supervision 0.1.6 → 0.1.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +205 -83
- package/dist/index.js.map +1 -1
- package/dist/mask-preparation.worker.js.map +1 -1
- package/dist/renderers/pixi-annotation-overlay-layer.d.ts +3 -2
- package/dist/renderers/pixi-annotation-overlay-layer.d.ts.map +1 -1
- package/dist/renderers/pixi-interaction-layer.d.ts.map +1 -1
- package/dist/renderers/pixi-interaction-presentation-layer.d.ts.map +1 -1
- package/dist/renderers/pixi-media-scene.d.ts.map +1 -1
- package/dist/renderers/pixi-vector-layer.d.ts +2 -1
- package/dist/renderers/pixi-vector-layer.d.ts.map +1 -1
- package/dist/tracking.worker.js.map +1 -1
- package/node_modules/supervision-js-core/dist/index.js +124 -28
- package/node_modules/supervision-js-core/dist/index.js.map +1 -1
- package/node_modules/supervision-js-core/dist/interactions/annotation-editing-engine.d.ts.map +1 -1
- package/node_modules/supervision-js-core/dist/interactions/annotation-handles.d.ts +1 -1
- package/node_modules/supervision-js-core/dist/interactions/annotation-handles.d.ts.map +1 -1
- package/node_modules/supervision-js-core/dist/interactions/detection-picker.d.ts.map +1 -1
- package/node_modules/supervision-js-core/dist/types/detections.d.ts +7 -0
- package/node_modules/supervision-js-core/dist/types/detections.d.ts.map +1 -1
- package/node_modules/supervision-js-core/dist/types/editing.d.ts +6 -0
- package/node_modules/supervision-js-core/dist/types/editing.d.ts.map +1 -1
- package/node_modules/supervision-js-core/dist/types/interaction-style.d.ts +2 -0
- package/node_modules/supervision-js-core/dist/types/interaction-style.d.ts.map +1 -1
- package/node_modules/supervision-js-core/dist/types/interaction.d.ts +9 -1
- package/node_modules/supervision-js-core/dist/types/interaction.d.ts.map +1 -1
- package/node_modules/supervision-js-core/dist/utils/detection-frames.d.ts.map +1 -1
- package/package.json +1 -1
|
@@ -65,6 +65,9 @@ function copySortedDetectionFrames(detectionFrames) {
|
|
|
65
65
|
: undefined,
|
|
66
66
|
keypoints: detection.keypoints
|
|
67
67
|
? {
|
|
68
|
+
boxRelative: detection.keypoints.boxRelative
|
|
69
|
+
? [...detection.keypoints.boxRelative]
|
|
70
|
+
: undefined,
|
|
68
71
|
edges: detection.keypoints.edges.map((edge) => [edge[0], edge[1]]),
|
|
69
72
|
points: detection.keypoints.points.map((point) => ({ ...point })),
|
|
70
73
|
visibility: detection.keypoints.visibility
|
|
@@ -3950,10 +3953,18 @@ function pickDetectionAtPoint(frame, point, options = {}) {
|
|
|
3950
3953
|
if (!frame) {
|
|
3951
3954
|
return null;
|
|
3952
3955
|
}
|
|
3953
|
-
|
|
3954
|
-
|
|
3955
|
-
|
|
3956
|
-
const
|
|
3956
|
+
// Tolerances are screen-space sizes; a zoomed-out viewport maps more media
|
|
3957
|
+
// units per screen pixel, so a fixed media-space padding would shrink the
|
|
3958
|
+
// hit target below the drawn marker.
|
|
3959
|
+
const viewportScale = options.viewportScale !== undefined &&
|
|
3960
|
+
Number.isFinite(options.viewportScale) &&
|
|
3961
|
+
options.viewportScale > 0
|
|
3962
|
+
? options.viewportScale
|
|
3963
|
+
: 1;
|
|
3964
|
+
const padding = Math.max(0, options.padding ?? 0) / viewportScale;
|
|
3965
|
+
const polylinePadding = Math.max(0, options.polylinePadding ?? 6) / viewportScale;
|
|
3966
|
+
const keypointPadding = Math.max(0, options.keypointPadding ?? 10) / viewportScale;
|
|
3967
|
+
const edgePadding = Math.max(0, options.edgePadding ?? 8) / viewportScale;
|
|
3957
3968
|
const candidates = [];
|
|
3958
3969
|
for (let detectionIndex = 0; detectionIndex < frame.detections.length; detectionIndex += 1) {
|
|
3959
3970
|
const detection = frame.detections[detectionIndex];
|
|
@@ -3992,10 +4003,22 @@ function pickDetectionAtPoint(frame, point, options = {}) {
|
|
|
3992
4003
|
}
|
|
3993
4004
|
}
|
|
3994
4005
|
if (detection.keypoints) {
|
|
3995
|
-
|
|
3996
|
-
|
|
3997
|
-
|
|
3998
|
-
|
|
4006
|
+
// Clustered keypoints share the pick tolerance; take the nearest so the
|
|
4007
|
+
// hover label, the menu, and the handle drag all name the same point.
|
|
4008
|
+
// Later points draw on top, so they win exact ties.
|
|
4009
|
+
let keypointIndex = -1;
|
|
4010
|
+
let keypointDistance = Number.POSITIVE_INFINITY;
|
|
4011
|
+
detection.keypoints.points.forEach((keypoint, index) => {
|
|
4012
|
+
if (detection.keypoints?.visibility?.[index] ===
|
|
4013
|
+
KeypointVisibility.NotLabeled) {
|
|
4014
|
+
return;
|
|
4015
|
+
}
|
|
4016
|
+
const distance = Math.hypot(point.x - keypoint.x, point.y - keypoint.y);
|
|
4017
|
+
if (distance <= keypointPadding && distance <= keypointDistance) {
|
|
4018
|
+
keypointIndex = index;
|
|
4019
|
+
keypointDistance = distance;
|
|
4020
|
+
}
|
|
4021
|
+
});
|
|
3999
4022
|
if (keypointIndex !== -1) {
|
|
4000
4023
|
pushCandidate(DetectionPickTarget.Keypoint, Math.PI * keypointPadding * keypointPadding, keypointIndex);
|
|
4001
4024
|
}
|
|
@@ -4368,29 +4391,77 @@ function getAnnotationHandles(detection, viewportScale = 1) {
|
|
|
4368
4391
|
if (detection.polyline)
|
|
4369
4392
|
return getPathHandles(detection.polyline.points, false, radius, hitSize);
|
|
4370
4393
|
if (detection.keypoints) {
|
|
4371
|
-
|
|
4372
|
-
|
|
4373
|
-
|
|
4374
|
-
|
|
4375
|
-
|
|
4376
|
-
|
|
4377
|
-
|
|
4378
|
-
|
|
4379
|
-
|
|
4394
|
+
const keypointHandles = detection.keypoints.points.flatMap((point, geometryIndex) => detection.keypoints?.visibility?.[geometryIndex] ===
|
|
4395
|
+
KeypointVisibility.NotLabeled
|
|
4396
|
+
? []
|
|
4397
|
+
: [
|
|
4398
|
+
{
|
|
4399
|
+
cursor: "pointer",
|
|
4400
|
+
geometryIndex,
|
|
4401
|
+
hitSize,
|
|
4402
|
+
id: `kp-${geometryIndex}`,
|
|
4403
|
+
kind: AnnotationHandleKind.Keypoint,
|
|
4404
|
+
point,
|
|
4405
|
+
radius,
|
|
4406
|
+
},
|
|
4407
|
+
]);
|
|
4408
|
+
if (!detection.rect)
|
|
4409
|
+
return keypointHandles;
|
|
4410
|
+
// The skeleton's box resizes like any box; keypoints come last so a point
|
|
4411
|
+
// sitting on the box edge still wins the hit test.
|
|
4412
|
+
return [
|
|
4413
|
+
...getBoxHandles(detection.rect, radius, hitSize),
|
|
4414
|
+
...keypointHandles,
|
|
4415
|
+
];
|
|
4380
4416
|
}
|
|
4381
4417
|
if (detection.rect)
|
|
4382
4418
|
return getBoxHandles(detection.rect, radius, hitSize);
|
|
4383
4419
|
return [];
|
|
4384
4420
|
}
|
|
4385
4421
|
function pickAnnotationHandle(handles, point) {
|
|
4386
|
-
|
|
4387
|
-
|
|
4388
|
-
|
|
4389
|
-
Math.abs(point.
|
|
4422
|
+
let picked;
|
|
4423
|
+
let pickedDistance = Number.POSITIVE_INFINITY;
|
|
4424
|
+
for (const handle of handles) {
|
|
4425
|
+
const dx = Math.abs(point.x - handle.point.x);
|
|
4426
|
+
const dy = Math.abs(point.y - handle.point.y);
|
|
4427
|
+
if (dx > handle.hitSize / 2 || dy > handle.hitSize / 2)
|
|
4428
|
+
continue;
|
|
4429
|
+
// Clustered handles (face keypoints, a tiny box's corners) share hit
|
|
4430
|
+
// areas; the nearest one is what the pointer is on. Later handles draw on
|
|
4431
|
+
// top, so they win exact ties.
|
|
4432
|
+
const distance = Math.hypot(dx, dy);
|
|
4433
|
+
if (distance <= pickedDistance) {
|
|
4434
|
+
picked = handle;
|
|
4435
|
+
pickedDistance = distance;
|
|
4436
|
+
}
|
|
4437
|
+
}
|
|
4438
|
+
return picked;
|
|
4390
4439
|
}
|
|
4391
4440
|
function applyAnnotationHandleDrag(detection, handle, point) {
|
|
4392
4441
|
if (detection.rect && handle.kind === AnnotationHandleKind.Resize) {
|
|
4393
|
-
|
|
4442
|
+
const rect = resizeRect(detection.rect, handle.id, point);
|
|
4443
|
+
const boxRelative = detection.keypoints?.boxRelative;
|
|
4444
|
+
if (!detection.keypoints || !boxRelative?.some(Boolean)) {
|
|
4445
|
+
return { ...detection, rect };
|
|
4446
|
+
}
|
|
4447
|
+
// Box-relative keypoints (unplaced template points) follow the rect;
|
|
4448
|
+
// points the user positioned keep their coordinates.
|
|
4449
|
+
const from = detection.rect;
|
|
4450
|
+
const scaleX = from.width > 0 ? rect.width / from.width : 1;
|
|
4451
|
+
const scaleY = from.height > 0 ? rect.height / from.height : 1;
|
|
4452
|
+
return {
|
|
4453
|
+
...detection,
|
|
4454
|
+
keypoints: {
|
|
4455
|
+
...detection.keypoints,
|
|
4456
|
+
points: detection.keypoints.points.map((keypoint, index) => boxRelative[index]
|
|
4457
|
+
? {
|
|
4458
|
+
x: rect.x + (keypoint.x - from.x) * scaleX,
|
|
4459
|
+
y: rect.y + (keypoint.y - from.y) * scaleY,
|
|
4460
|
+
}
|
|
4461
|
+
: keypoint),
|
|
4462
|
+
},
|
|
4463
|
+
rect,
|
|
4464
|
+
};
|
|
4394
4465
|
}
|
|
4395
4466
|
if (handle.kind === AnnotationHandleKind.AddVertex &&
|
|
4396
4467
|
handle.edgeIndex !== undefined) {
|
|
@@ -4411,7 +4482,16 @@ function applyAnnotationHandleDrag(detection, handle, point) {
|
|
|
4411
4482
|
if (detection.keypoints) {
|
|
4412
4483
|
const points = [...detection.keypoints.points];
|
|
4413
4484
|
points[handle.geometryIndex] = point;
|
|
4414
|
-
|
|
4485
|
+
// A positioned point no longer follows the box.
|
|
4486
|
+
const boxRelative = detection.keypoints.boxRelative?.map((relative, index) => relative && index !== handle.geometryIndex);
|
|
4487
|
+
return {
|
|
4488
|
+
...detection,
|
|
4489
|
+
keypoints: {
|
|
4490
|
+
...detection.keypoints,
|
|
4491
|
+
points,
|
|
4492
|
+
...(boxRelative ? { boxRelative } : {}),
|
|
4493
|
+
},
|
|
4494
|
+
};
|
|
4415
4495
|
}
|
|
4416
4496
|
return detection;
|
|
4417
4497
|
}
|
|
@@ -4580,7 +4660,7 @@ function createAnnotationEditingEngine(options = {}) {
|
|
|
4580
4660
|
case "move": {
|
|
4581
4661
|
const dx = input.point.x - gesture.start.point.x;
|
|
4582
4662
|
const dy = input.point.y - gesture.start.point.y;
|
|
4583
|
-
const moved = gesture.moved ||
|
|
4663
|
+
const moved = gesture.moved || exceedsMoveThreshold(gesture.start, input);
|
|
4584
4664
|
gesture = { ...gesture, current: input, moved };
|
|
4585
4665
|
if (moved) {
|
|
4586
4666
|
const preview = offsetDetection(gesture.detection, dx, dy);
|
|
@@ -4594,10 +4674,17 @@ function createAnnotationEditingEngine(options = {}) {
|
|
|
4594
4674
|
}
|
|
4595
4675
|
break;
|
|
4596
4676
|
}
|
|
4597
|
-
case "resize":
|
|
4598
|
-
|
|
4599
|
-
|
|
4677
|
+
case "resize": {
|
|
4678
|
+
// A click on a handle is not a drag: a keypoint tapped to open its
|
|
4679
|
+
// menu must not snap to the pointer, so nothing previews or commits
|
|
4680
|
+
// until the pointer has travelled like a move gesture.
|
|
4681
|
+
const moved = gesture.moved || exceedsMoveThreshold(gesture.start, input);
|
|
4682
|
+
gesture = { ...gesture, current: input, moved };
|
|
4683
|
+
if (moved) {
|
|
4684
|
+
setPreview(applyAnnotationHandleDrag(gesture.detection, gesture.handle, input.point));
|
|
4685
|
+
}
|
|
4600
4686
|
break;
|
|
4687
|
+
}
|
|
4601
4688
|
case "path":
|
|
4602
4689
|
setPreview(resolvePathPreview([...gesture.points, input.point]));
|
|
4603
4690
|
break;
|
|
@@ -4659,6 +4746,10 @@ function createAnnotationEditingEngine(options = {}) {
|
|
|
4659
4746
|
commit(preview, active.detection);
|
|
4660
4747
|
return;
|
|
4661
4748
|
}
|
|
4749
|
+
if (!active.moved) {
|
|
4750
|
+
setState(idleState());
|
|
4751
|
+
return;
|
|
4752
|
+
}
|
|
4662
4753
|
const preview = applyAnnotationHandleDrag(active.detection, active.handle, input.point);
|
|
4663
4754
|
commit(preview, active.detection);
|
|
4664
4755
|
},
|
|
@@ -4679,6 +4770,7 @@ function createAnnotationEditingEngine(options = {}) {
|
|
|
4679
4770
|
detection,
|
|
4680
4771
|
handle,
|
|
4681
4772
|
kind: "resize",
|
|
4773
|
+
moved: false,
|
|
4682
4774
|
start: input,
|
|
4683
4775
|
};
|
|
4684
4776
|
setState({
|
|
@@ -4686,7 +4778,7 @@ function createAnnotationEditingEngine(options = {}) {
|
|
|
4686
4778
|
activeHandleId: handle.id,
|
|
4687
4779
|
kind: AnnotationGestureStateKind.Resizing,
|
|
4688
4780
|
pointerId: input.pointerId ?? null,
|
|
4689
|
-
preview:
|
|
4781
|
+
preview: null,
|
|
4690
4782
|
});
|
|
4691
4783
|
},
|
|
4692
4784
|
deleteVertex(detection, vertexIndex) {
|
|
@@ -4864,6 +4956,10 @@ function createAnnotationEditingEngine(options = {}) {
|
|
|
4864
4956
|
function scale() {
|
|
4865
4957
|
return Math.max(options.viewportScale?.() ?? 1, Number.EPSILON);
|
|
4866
4958
|
}
|
|
4959
|
+
function exceedsMoveThreshold(start, input) {
|
|
4960
|
+
return (Math.hypot(input.point.x - start.point.x, input.point.y - start.point.y) >=
|
|
4961
|
+
MOVE_THRESHOLD / scale());
|
|
4962
|
+
}
|
|
4867
4963
|
function capture(pointerId) {
|
|
4868
4964
|
if (pointerId !== undefined)
|
|
4869
4965
|
options.capturePointer?.(pointerId);
|