supervision 0.2.4 → 0.2.6
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/depth-preview-probe-DG7McQEN.js +314 -0
- package/dist/depth-preview-probe-DG7McQEN.js.map +1 -0
- package/dist/depth-preview-track-BvgwNEE9.js +612 -0
- package/dist/depth-preview-track-BvgwNEE9.js.map +1 -0
- package/dist/detections/chunked-detection-frame-source.d.ts.map +1 -1
- package/dist/index.d.ts +4 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +9504 -4719
- package/dist/index.js.map +1 -1
- package/dist/mask-preparation.worker.js +658 -21
- package/dist/mask-preparation.worker.js.map +1 -1
- package/dist/media/decoder-deadline.d.ts +18 -0
- package/dist/media/decoder-deadline.d.ts.map +1 -0
- package/dist/media/depth-preview-probe.d.ts +65 -0
- package/dist/media/depth-preview-probe.d.ts.map +1 -0
- package/dist/media/depth-preview-track.d.ts +96 -0
- package/dist/media/depth-preview-track.d.ts.map +1 -0
- package/dist/media/media-normalization.d.ts.map +1 -1
- package/dist/media/media-source.d.ts +7 -0
- package/dist/media/media-source.d.ts.map +1 -1
- package/dist/media/mediabunny-frame-clock.d.ts +7 -0
- package/dist/media/mediabunny-frame-clock.d.ts.map +1 -0
- package/dist/media/mediabunny-media-source.d.ts +3 -0
- package/dist/media/mediabunny-media-source.d.ts.map +1 -1
- package/dist/media/track-frame-index.d.ts +14 -0
- package/dist/media/track-frame-index.d.ts.map +1 -0
- package/dist/media/video-engine-media-source.d.ts.map +1 -1
- package/dist/media/video-engine-media-source.js +1 -1
- package/dist/render-preparation/depth/clip-preview.d.ts +41 -0
- package/dist/render-preparation/depth/clip-preview.d.ts.map +1 -0
- package/dist/render-preparation/depth/clip-timing.d.ts +18 -0
- package/dist/render-preparation/depth/clip-timing.d.ts.map +1 -0
- package/dist/render-preparation/depth/clip.d.ts +14 -0
- package/dist/render-preparation/depth/clip.d.ts.map +1 -0
- package/dist/render-preparation/depth/exact-at-rest.d.ts +44 -0
- package/dist/render-preparation/depth/exact-at-rest.d.ts.map +1 -0
- package/dist/render-preparation/depth/exact-playback.d.ts +40 -0
- package/dist/render-preparation/depth/exact-playback.d.ts.map +1 -0
- package/dist/render-preparation/depth/files.d.ts +55 -0
- package/dist/render-preparation/depth/files.d.ts.map +1 -0
- package/dist/render-preparation/depth/frame-decode.d.ts +20 -0
- package/dist/render-preparation/depth/frame-decode.d.ts.map +1 -0
- package/dist/render-preparation/depth/frame-preparer.d.ts +51 -0
- package/dist/render-preparation/depth/frame-preparer.d.ts.map +1 -0
- package/dist/render-preparation/depth/frame-window.d.ts +146 -0
- package/dist/render-preparation/depth/frame-window.d.ts.map +1 -0
- package/dist/render-preparation/depth/options.d.ts +37 -0
- package/dist/render-preparation/depth/options.d.ts.map +1 -0
- package/dist/render-preparation/depth/png16.d.ts +40 -0
- package/dist/render-preparation/depth/png16.d.ts.map +1 -0
- package/dist/render-preparation/depth/preview-luma-copier.d.ts +12 -0
- package/dist/render-preparation/depth/preview-luma-copier.d.ts.map +1 -0
- package/dist/render-preparation/depth/preview-luma.d.ts +48 -0
- package/dist/render-preparation/depth/preview-luma.d.ts.map +1 -0
- package/dist/render-preparation/depth/source.d.ts +123 -0
- package/dist/render-preparation/depth/source.d.ts.map +1 -0
- package/dist/render-preparation/depth/worker-protocol.d.ts +76 -0
- package/dist/render-preparation/depth/worker-protocol.d.ts.map +1 -0
- package/dist/render-preparation/playhead-motion.d.ts +67 -0
- package/dist/render-preparation/playhead-motion.d.ts.map +1 -0
- package/dist/render-preparation/prepared-render-window.d.ts +12 -0
- package/dist/render-preparation/prepared-render-window.d.ts.map +1 -1
- package/dist/renderers/depth-textures.d.ts +79 -0
- package/dist/renderers/depth-textures.d.ts.map +1 -0
- package/dist/renderers/id-mask-stroke-coverage.d.ts +3 -0
- package/dist/renderers/id-mask-stroke-coverage.d.ts.map +1 -0
- package/dist/renderers/media-renderer-core.d.ts.map +1 -1
- package/dist/renderers/media-renderer-scene.d.ts +14 -1
- package/dist/renderers/media-renderer-scene.d.ts.map +1 -1
- package/dist/renderers/pixi-batch-texture-bindings.d.ts +9 -0
- package/dist/renderers/pixi-batch-texture-bindings.d.ts.map +1 -0
- package/dist/renderers/pixi-depth-layer.d.ts +76 -0
- package/dist/renderers/pixi-depth-layer.d.ts.map +1 -0
- package/dist/renderers/pixi-depth-shader.d.ts +76 -0
- package/dist/renderers/pixi-depth-shader.d.ts.map +1 -0
- package/dist/renderers/pixi-focus-layer.d.ts +1 -0
- package/dist/renderers/pixi-focus-layer.d.ts.map +1 -1
- package/dist/renderers/pixi-frame-present.d.ts +1 -0
- package/dist/renderers/pixi-frame-present.d.ts.map +1 -1
- package/dist/renderers/pixi-id-mask-shader.d.ts +1 -0
- package/dist/renderers/pixi-id-mask-shader.d.ts.map +1 -1
- package/dist/renderers/pixi-interaction-layer.d.ts +1 -1
- package/dist/renderers/pixi-interaction-layer.d.ts.map +1 -1
- package/dist/renderers/pixi-interaction-presentation-layer.d.ts +1 -0
- package/dist/renderers/pixi-interaction-presentation-layer.d.ts.map +1 -1
- package/dist/renderers/pixi-mask-halo.d.ts +1 -0
- package/dist/renderers/pixi-mask-halo.d.ts.map +1 -1
- package/dist/renderers/pixi-mask-layer.d.ts +4 -0
- package/dist/renderers/pixi-mask-layer.d.ts.map +1 -1
- package/dist/renderers/pixi-media-scene.d.ts.map +1 -1
- package/dist/renderers/pixi-region-coverage-mask.d.ts +1 -0
- package/dist/renderers/pixi-region-coverage-mask.d.ts.map +1 -1
- package/dist/renderers/pixi-region-layer.d.ts +1 -0
- package/dist/renderers/pixi-region-layer.d.ts.map +1 -1
- package/dist/renderers/pixi-scene-layer-slot.d.ts +1 -0
- package/dist/renderers/pixi-scene-layer-slot.d.ts.map +1 -1
- package/dist/renderers/prepared-annotation-window.d.ts +1 -0
- package/dist/renderers/prepared-annotation-window.d.ts.map +1 -1
- package/dist/renderers/render-preparation-report.d.ts +23 -0
- package/dist/renderers/render-preparation-report.d.ts.map +1 -0
- package/dist/sessions/media-session-defaults.d.ts.map +1 -1
- package/dist/sessions/media-session-state.d.ts.map +1 -1
- package/dist/sessions/media-session.d.ts.map +1 -1
- package/dist/tracking.worker.js +64 -0
- package/dist/tracking.worker.js.map +1 -1
- package/dist/types/media-depth.d.ts +55 -0
- package/dist/types/media-depth.d.ts.map +1 -0
- package/dist/types/media-renderer.d.ts +19 -0
- package/dist/types/media-renderer.d.ts.map +1 -1
- package/dist/types/media-session.d.ts +19 -0
- package/dist/types/media-session.d.ts.map +1 -1
- package/dist/types/render-preparation.d.ts +105 -3
- package/dist/types/render-preparation.d.ts.map +1 -1
- package/dist/{video-engine-media-source-DR-x1UfK.js → video-engine-media-source-DOFsuGS4.js} +35 -29
- package/dist/video-engine-media-source-DOFsuGS4.js.map +1 -0
- package/dist/web-video-engine/analysis.js +6 -43
- package/dist/web-video-engine/cache-budget.d.ts +4 -7
- package/dist/web-video-engine/constants.d.ts +3 -4
- package/dist/web-video-engine/create-scrub-cursor.d.ts +1 -0
- package/dist/web-video-engine/decode-resolution-fynpRIAG.js +109 -0
- package/dist/web-video-engine/decode-scheduler.d.ts +1 -0
- package/dist/web-video-engine/engine-core.d.ts +4 -2
- package/dist/web-video-engine/engine.js +8 -6
- package/dist/web-video-engine/engine.worker.js +164 -66
- package/dist/web-video-engine/frame-cache.d.ts +9 -0
- package/dist/web-video-engine/frame-index.d.ts +1 -0
- package/dist/web-video-engine/frame-index.js +97 -0
- package/dist/web-video-engine/{frame-timeline-DreEwsRY.js → frame-timeline-CYgzzmpA.js} +1 -109
- package/dist/web-video-engine/frame-timeline-reader.d.ts +21 -0
- package/dist/web-video-engine/video-engine.d.ts +9 -4
- package/node_modules/supervision-js-core/THIRD_PARTY_NOTICES.md +132 -0
- package/node_modules/supervision-js-core/dist/detections/buffered-detection-timeline.d.ts.map +1 -1
- package/node_modules/supervision-js-core/dist/index.d.ts +7 -2
- package/node_modules/supervision-js-core/dist/index.d.ts.map +1 -1
- package/node_modules/supervision-js-core/dist/index.js +1138 -62
- 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 +2 -2
- package/node_modules/supervision-js-core/dist/interactions/annotation-handles.d.ts.map +1 -1
- package/node_modules/supervision-js-core/dist/styles/annotation-renderer-presentation.d.ts.map +1 -1
- package/node_modules/supervision-js-core/dist/styles/annotation-renderer-registry.d.ts.map +1 -1
- package/node_modules/supervision-js-core/dist/types/annotation-renderer.d.ts +50 -3
- package/node_modules/supervision-js-core/dist/types/annotation-renderer.d.ts.map +1 -1
- package/node_modules/supervision-js-core/dist/types/depth-map.d.ts +203 -0
- package/node_modules/supervision-js-core/dist/types/depth-map.d.ts.map +1 -0
- package/node_modules/supervision-js-core/dist/types/editing.d.ts +4 -0
- package/node_modules/supervision-js-core/dist/types/editing.d.ts.map +1 -1
- package/node_modules/supervision-js-core/dist/types/media-rendering.d.ts +3 -0
- package/node_modules/supervision-js-core/dist/types/media-rendering.d.ts.map +1 -1
- package/node_modules/supervision-js-core/dist/utils/depth-color-mapping.d.ts +91 -0
- package/node_modules/supervision-js-core/dist/utils/depth-color-mapping.d.ts.map +1 -0
- package/node_modules/supervision-js-core/dist/utils/depth-colormap-tables.d.ts +30 -0
- package/node_modules/supervision-js-core/dist/utils/depth-colormap-tables.d.ts.map +1 -0
- package/node_modules/supervision-js-core/dist/utils/depth-colormaps.d.ts +16 -0
- package/node_modules/supervision-js-core/dist/utils/depth-colormaps.d.ts.map +1 -0
- package/node_modules/supervision-js-core/dist/utils/depth-manifest.d.ts +23 -0
- package/node_modules/supervision-js-core/dist/utils/depth-manifest.d.ts.map +1 -0
- package/node_modules/supervision-js-core/dist/utils/depth-readout.d.ts +14 -0
- package/node_modules/supervision-js-core/dist/utils/depth-readout.d.ts.map +1 -0
- package/node_modules/supervision-js-core/dist/utils/detection-frames.d.ts +21 -0
- package/node_modules/supervision-js-core/dist/utils/detection-frames.d.ts.map +1 -1
- package/node_modules/supervision-js-core/dist/utils/id-mask-frame.d.ts +3 -7
- package/node_modules/supervision-js-core/dist/utils/id-mask-frame.d.ts.map +1 -1
- package/node_modules/supervision-js-core/package.json +2 -1
- package/package.json +6 -1
- package/dist/video-engine-media-source-DR-x1UfK.js.map +0 -1
|
@@ -89,6 +89,19 @@ var DetectionFrameRetentionMode;
|
|
|
89
89
|
* function and not in a smaller number here.
|
|
90
90
|
*/
|
|
91
91
|
const PLAYHEAD_QUANTIZATION_TOLERANCE_SECONDS = 0.0005;
|
|
92
|
+
/**
|
|
93
|
+
* Validates and orders a window without copying it. The frames stay the
|
|
94
|
+
* caller's own objects, which is what a source wants: `DetectionFrame` and its
|
|
95
|
+
* `detections` are both `readonly`, nothing in the library writes to either,
|
|
96
|
+
* and the buffer that receives this window already keeps the objects it
|
|
97
|
+
* recognises and copies only the ones it does not
|
|
98
|
+
* (`reuseBufferedFrameSnapshots`). A source that copies first pays for every
|
|
99
|
+
* frame the buffer is about to discard.
|
|
100
|
+
*/
|
|
101
|
+
function sortedDetectionFrames(detectionFrames) {
|
|
102
|
+
validateDetectionFrames(detectionFrames ?? []);
|
|
103
|
+
return [...(detectionFrames ?? [])].sort((left, right) => left.mediaTime - right.mediaTime);
|
|
104
|
+
}
|
|
92
105
|
function copySortedDetectionFrames(detectionFrames) {
|
|
93
106
|
validateDetectionFrames(detectionFrames ?? []);
|
|
94
107
|
return (detectionFrames ?? [])
|
|
@@ -668,6 +681,9 @@ function createBufferedDetectionTimeline(options) {
|
|
|
668
681
|
let state = createIdleDetectionBufferState();
|
|
669
682
|
let destroyed = false;
|
|
670
683
|
let loadId = 0;
|
|
684
|
+
// Preserve snapshot reuse without retaining evicted geometry.
|
|
685
|
+
const snapshotByIdentity = new Map();
|
|
686
|
+
let snapshotSourceVersion = null;
|
|
671
687
|
let bufferedSourceVersion = null;
|
|
672
688
|
let bufferedVersionRange = null;
|
|
673
689
|
let timelineContext = {
|
|
@@ -693,6 +709,37 @@ function createBufferedDetectionTimeline(options) {
|
|
|
693
709
|
}
|
|
694
710
|
return ranges.reduce((version, range) => Math.max(version, options.source.getVersion?.(range) ?? 0), 0);
|
|
695
711
|
};
|
|
712
|
+
const synchronizeSnapshotMemo = () => {
|
|
713
|
+
const sourceVersion = getSourceVersion();
|
|
714
|
+
if (snapshotSourceVersion === sourceVersion) {
|
|
715
|
+
return;
|
|
716
|
+
}
|
|
717
|
+
const frames = [...snapshotByIdentity.values()].flatMap((reference) => {
|
|
718
|
+
const frame = reference.deref();
|
|
719
|
+
return frame ? [frame] : [];
|
|
720
|
+
});
|
|
721
|
+
const changes = snapshotSourceVersion === null || frames.length === 0
|
|
722
|
+
? undefined
|
|
723
|
+
: options.source.getChangesSince?.(snapshotSourceVersion, [
|
|
724
|
+
{
|
|
725
|
+
startTime: Math.min(...frames.map((frame) => frame.mediaTime)),
|
|
726
|
+
endTime: Math.max(...frames.map((frame) => frame.endTime ??
|
|
727
|
+
frame.mediaTime +
|
|
728
|
+
Number.EPSILON * Math.max(1, Math.abs(frame.mediaTime)))),
|
|
729
|
+
},
|
|
730
|
+
]);
|
|
731
|
+
if (!changes || changes.requiresReload) {
|
|
732
|
+
snapshotByIdentity.clear();
|
|
733
|
+
}
|
|
734
|
+
else {
|
|
735
|
+
for (const frame of frames) {
|
|
736
|
+
if (changes.ranges.some((range) => detectionFrameOverlapsRange(frame, range.startTime, range.endTime))) {
|
|
737
|
+
snapshotByIdentity.delete(getDetectionFrameIdentity(frame));
|
|
738
|
+
}
|
|
739
|
+
}
|
|
740
|
+
}
|
|
741
|
+
snapshotSourceVersion = sourceVersion;
|
|
742
|
+
};
|
|
696
743
|
const isLoadingEnabled = () => typeof options.enabled === "function"
|
|
697
744
|
? options.enabled()
|
|
698
745
|
: options.enabled !== false;
|
|
@@ -740,14 +787,19 @@ function createBufferedDetectionTimeline(options) {
|
|
|
740
787
|
return;
|
|
741
788
|
}
|
|
742
789
|
const committedSourceVersion = getSourceVersion(sourceRanges);
|
|
790
|
+
const sourceUnchanged = sourceVersion === committedSourceVersion;
|
|
743
791
|
const loadedFrames = frameRanges.flat();
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
792
|
+
synchronizeSnapshotMemo();
|
|
793
|
+
buffer = sourceUnchanged
|
|
794
|
+
? reuseBufferedFrameSnapshots(bufferedSourceVersion === sourceVersion ? buffer : [], loadedFrames, snapshotByIdentity)
|
|
795
|
+
: copySortedDetectionFrames(loadedFrames);
|
|
796
|
+
if (sourceUnchanged) {
|
|
797
|
+
for (const frame of buffer) {
|
|
798
|
+
rememberSnapshot(snapshotByIdentity, frame);
|
|
799
|
+
}
|
|
800
|
+
}
|
|
749
801
|
bufferedVersionRange = versionRange;
|
|
750
|
-
bufferedSourceVersion =
|
|
802
|
+
bufferedSourceVersion = sourceVersion;
|
|
751
803
|
state = {
|
|
752
804
|
bufferEndTime: endTime,
|
|
753
805
|
bufferStartTime: startTime,
|
|
@@ -1003,6 +1055,8 @@ function createBufferedDetectionTimeline(options) {
|
|
|
1003
1055
|
listeners.clear();
|
|
1004
1056
|
pendingPrefetch = undefined;
|
|
1005
1057
|
buffer = [];
|
|
1058
|
+
snapshotByIdentity.clear();
|
|
1059
|
+
snapshotSourceVersion = null;
|
|
1006
1060
|
bufferedSourceVersion = null;
|
|
1007
1061
|
bufferedVersionRange = null;
|
|
1008
1062
|
state = {
|
|
@@ -1086,7 +1140,14 @@ function createBufferedDetectionTimeline(options) {
|
|
|
1086
1140
|
bufferedVersionRange !== incrementalVersionRange) {
|
|
1087
1141
|
return;
|
|
1088
1142
|
}
|
|
1089
|
-
|
|
1143
|
+
const changedFrames = copySortedDetectionFrames(changedFrameRanges.flat());
|
|
1144
|
+
synchronizeSnapshotMemo();
|
|
1145
|
+
buffer = mergeIncrementalFrames(buffer, changedFrames, changedRanges);
|
|
1146
|
+
if (getSourceVersion(sourceRanges) === changes.version) {
|
|
1147
|
+
for (const frame of buffer) {
|
|
1148
|
+
rememberSnapshot(snapshotByIdentity, frame);
|
|
1149
|
+
}
|
|
1150
|
+
}
|
|
1090
1151
|
bufferedSourceVersion = changes.version;
|
|
1091
1152
|
state = {
|
|
1092
1153
|
...state,
|
|
@@ -1391,18 +1452,36 @@ function mergeIncrementalFrames(currentFrames, changedFrames, changedRanges) {
|
|
|
1391
1452
|
}
|
|
1392
1453
|
return Array.from(framesByIdentity.values()).sort(compareDetectionFrames$1);
|
|
1393
1454
|
}
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1455
|
+
const MAX_REMEMBERED_SNAPSHOTS = 8192;
|
|
1456
|
+
function rememberSnapshot(snapshots, frame) {
|
|
1457
|
+
const identity = getDetectionFrameIdentity(frame);
|
|
1458
|
+
if (snapshots.get(identity)?.deref() === frame) {
|
|
1459
|
+
return;
|
|
1460
|
+
}
|
|
1461
|
+
snapshots.delete(identity);
|
|
1462
|
+
snapshots.set(identity, new WeakRef(frame));
|
|
1463
|
+
while (snapshots.size > MAX_REMEMBERED_SNAPSHOTS) {
|
|
1464
|
+
const oldest = snapshots.keys().next().value;
|
|
1465
|
+
if (oldest === undefined) {
|
|
1466
|
+
break;
|
|
1467
|
+
}
|
|
1468
|
+
snapshots.delete(oldest);
|
|
1469
|
+
}
|
|
1470
|
+
}
|
|
1471
|
+
/** Reuse unchanged snapshots so moving the window copies only new frames. */
|
|
1472
|
+
function reuseBufferedFrameSnapshots(currentFrames, loadedFrames, snapshots) {
|
|
1402
1473
|
const currentFramesByIdentity = new Map(currentFrames.map((frame) => [getDetectionFrameIdentity(frame), frame]));
|
|
1403
1474
|
return loadedFrames
|
|
1404
|
-
.map((frame) =>
|
|
1405
|
-
|
|
1475
|
+
.map((frame) => {
|
|
1476
|
+
const identity = getDetectionFrameIdentity(frame);
|
|
1477
|
+
const existing = currentFramesByIdentity.get(identity) ??
|
|
1478
|
+
snapshots.get(identity)?.deref();
|
|
1479
|
+
if (existing) {
|
|
1480
|
+
return existing;
|
|
1481
|
+
}
|
|
1482
|
+
validateDetectionFrames([frame]);
|
|
1483
|
+
return copyDetectionFrame(frame);
|
|
1484
|
+
})
|
|
1406
1485
|
.sort(compareDetectionFrames$1);
|
|
1407
1486
|
}
|
|
1408
1487
|
function getDetectionFrameIdentity(frame) {
|
|
@@ -5311,9 +5390,9 @@ function pickAnnotationHandle(handles, point) {
|
|
|
5311
5390
|
}
|
|
5312
5391
|
return picked;
|
|
5313
5392
|
}
|
|
5314
|
-
function applyAnnotationHandleDrag(detection, handle, point) {
|
|
5393
|
+
function applyAnnotationHandleDrag(detection, handle, point, mediaDimensions) {
|
|
5315
5394
|
if (detection.rect && handle.kind === AnnotationHandleKind.Resize) {
|
|
5316
|
-
const rect = resizeRect(detection.rect, handle.id, point);
|
|
5395
|
+
const rect = resizeRect(detection.rect, handle.id, point, mediaDimensions);
|
|
5317
5396
|
const boxRelative = detection.keypoints?.boxRelative;
|
|
5318
5397
|
if (!detection.keypoints || !boxRelative?.some(Boolean)) {
|
|
5319
5398
|
return { ...detection, rect };
|
|
@@ -5477,26 +5556,50 @@ function getPathHandles(points, closed, radius, hitSize) {
|
|
|
5477
5556
|
}
|
|
5478
5557
|
return handles;
|
|
5479
5558
|
}
|
|
5480
|
-
function resizeRect(rect, handle, point) {
|
|
5559
|
+
function resizeRect(rect, handle, point, mediaDimensions) {
|
|
5481
5560
|
let left = rect.x - rect.width / 2;
|
|
5482
5561
|
let right = rect.x + rect.width / 2;
|
|
5483
5562
|
let top = rect.y - rect.height / 2;
|
|
5484
5563
|
let bottom = rect.y + rect.height / 2;
|
|
5564
|
+
if (mediaDimensions) {
|
|
5565
|
+
left = Math.max(0, Math.min(left, mediaDimensions.width));
|
|
5566
|
+
right = Math.max(0, Math.min(right, mediaDimensions.width));
|
|
5567
|
+
top = Math.max(0, Math.min(top, mediaDimensions.height));
|
|
5568
|
+
bottom = Math.max(0, Math.min(bottom, mediaDimensions.height));
|
|
5569
|
+
}
|
|
5485
5570
|
if (handle.includes("w"))
|
|
5486
|
-
left =
|
|
5571
|
+
left = resizeEdge(point.x, right, -1, mediaDimensions?.width);
|
|
5487
5572
|
if (handle.includes("e"))
|
|
5488
|
-
right =
|
|
5573
|
+
right = resizeEdge(point.x, left, 1, mediaDimensions?.width);
|
|
5489
5574
|
if (handle.includes("n"))
|
|
5490
|
-
top =
|
|
5575
|
+
top = resizeEdge(point.y, bottom, -1, mediaDimensions?.height);
|
|
5491
5576
|
if (handle.includes("s"))
|
|
5492
|
-
bottom =
|
|
5577
|
+
bottom = resizeEdge(point.y, top, 1, mediaDimensions?.height);
|
|
5493
5578
|
return {
|
|
5494
5579
|
x: (left + right) / 2,
|
|
5495
5580
|
y: (top + bottom) / 2,
|
|
5496
|
-
width: right - left,
|
|
5497
|
-
height: bottom - top,
|
|
5581
|
+
width: Math.abs(right - left),
|
|
5582
|
+
height: Math.abs(bottom - top),
|
|
5498
5583
|
};
|
|
5499
5584
|
}
|
|
5585
|
+
function resizeEdge(point, anchor, initialDirection, extent) {
|
|
5586
|
+
let direction = point === anchor ? initialDirection : point < anchor ? -1 : 1;
|
|
5587
|
+
let minimum = 5;
|
|
5588
|
+
if (extent !== undefined) {
|
|
5589
|
+
const available = direction === 1 ? extent - anchor : anchor;
|
|
5590
|
+
const opposite = direction === 1 ? anchor : extent - anchor;
|
|
5591
|
+
// Crossing cannot push the minimum-size box past the media edge. Use the
|
|
5592
|
+
// other side of the fixed anchor when it has more room.
|
|
5593
|
+
if (available < Math.min(minimum, extent) && opposite > available) {
|
|
5594
|
+
direction *= -1;
|
|
5595
|
+
}
|
|
5596
|
+
minimum = Math.min(minimum, direction === 1 ? extent - anchor : anchor);
|
|
5597
|
+
}
|
|
5598
|
+
const edge = direction === 1
|
|
5599
|
+
? Math.max(point, anchor + minimum)
|
|
5600
|
+
: Math.min(point, anchor - minimum);
|
|
5601
|
+
return extent === undefined ? edge : Math.max(0, Math.min(edge, extent));
|
|
5602
|
+
}
|
|
5500
5603
|
function replacePathPoint(detection, key, index, point) {
|
|
5501
5604
|
const geometry = detection[key];
|
|
5502
5605
|
const points = [...geometry.points];
|
|
@@ -5528,6 +5631,8 @@ function createAnnotationEditingEngine(options = {}) {
|
|
|
5528
5631
|
return {
|
|
5529
5632
|
getState: () => state,
|
|
5530
5633
|
setCreationTool(nextTool) {
|
|
5634
|
+
if (nextTool === tool)
|
|
5635
|
+
return;
|
|
5531
5636
|
if (gesture)
|
|
5532
5637
|
cancel();
|
|
5533
5638
|
tool = nextTool;
|
|
@@ -5551,12 +5656,10 @@ function createAnnotationEditingEngine(options = {}) {
|
|
|
5551
5656
|
setPreview(resolveBoxPreview(gesture));
|
|
5552
5657
|
break;
|
|
5553
5658
|
case "move": {
|
|
5554
|
-
const dx = input.point.x - gesture.start.point.x;
|
|
5555
|
-
const dy = input.point.y - gesture.start.point.y;
|
|
5556
5659
|
const moved = gesture.moved || exceedsMoveThreshold(gesture.start, input);
|
|
5557
5660
|
gesture = { ...gesture, current: input, moved };
|
|
5558
5661
|
if (moved) {
|
|
5559
|
-
const preview =
|
|
5662
|
+
const { preview, dx, dy } = resolveMovePreview(gesture, input);
|
|
5560
5663
|
setPreview(preview);
|
|
5561
5664
|
if (gesture.detection.id !== undefined) {
|
|
5562
5665
|
options.onFastTranslate?.(gesture.detection.id, dx, dy);
|
|
@@ -5574,7 +5677,7 @@ function createAnnotationEditingEngine(options = {}) {
|
|
|
5574
5677
|
const moved = gesture.moved || exceedsMoveThreshold(gesture.start, input);
|
|
5575
5678
|
gesture = { ...gesture, current: input, moved };
|
|
5576
5679
|
if (moved) {
|
|
5577
|
-
setPreview(applyAnnotationHandleDrag(gesture.detection, gesture.handle, input.point));
|
|
5680
|
+
setPreview(applyAnnotationHandleDrag(gesture.detection, gesture.handle, input.point, input.mediaDimensions ?? gesture.start.mediaDimensions));
|
|
5578
5681
|
}
|
|
5579
5682
|
break;
|
|
5580
5683
|
}
|
|
@@ -5611,11 +5714,11 @@ function createAnnotationEditingEngine(options = {}) {
|
|
|
5611
5714
|
rect.width < 1 ||
|
|
5612
5715
|
rect.height < 1 ||
|
|
5613
5716
|
(duration < CLICK_CANCEL_MS && diagonal <= CLICK_CANCEL_DIAGONAL)) {
|
|
5614
|
-
|
|
5717
|
+
rejectCreation();
|
|
5615
5718
|
return;
|
|
5616
5719
|
}
|
|
5617
5720
|
if (tool?.shouldCommit?.(rect) === false) {
|
|
5618
|
-
|
|
5721
|
+
rejectCreation();
|
|
5619
5722
|
return;
|
|
5620
5723
|
}
|
|
5621
5724
|
commit(preview, null);
|
|
@@ -5624,7 +5727,7 @@ function createAnnotationEditingEngine(options = {}) {
|
|
|
5624
5727
|
if (active.kind === "freehand") {
|
|
5625
5728
|
const points = [...active.points, input.point];
|
|
5626
5729
|
if (points.length < 2 || tool?.shouldCommit?.(points) === false) {
|
|
5627
|
-
|
|
5730
|
+
rejectCreation();
|
|
5628
5731
|
return;
|
|
5629
5732
|
}
|
|
5630
5733
|
commit(tool.createDetection(points), null);
|
|
@@ -5635,7 +5738,7 @@ function createAnnotationEditingEngine(options = {}) {
|
|
|
5635
5738
|
setState(idleState());
|
|
5636
5739
|
return;
|
|
5637
5740
|
}
|
|
5638
|
-
const preview =
|
|
5741
|
+
const { preview } = resolveMovePreview(active, input);
|
|
5639
5742
|
commit(preview, active.detection);
|
|
5640
5743
|
return;
|
|
5641
5744
|
}
|
|
@@ -5643,7 +5746,7 @@ function createAnnotationEditingEngine(options = {}) {
|
|
|
5643
5746
|
setState(idleState());
|
|
5644
5747
|
return;
|
|
5645
5748
|
}
|
|
5646
|
-
const preview = applyAnnotationHandleDrag(active.detection, active.handle, input.point);
|
|
5749
|
+
const preview = applyAnnotationHandleDrag(active.detection, active.handle, input.point, input.mediaDimensions ?? active.start.mediaDimensions);
|
|
5647
5750
|
commit(preview, active.detection);
|
|
5648
5751
|
},
|
|
5649
5752
|
keyDown(key) {
|
|
@@ -5775,10 +5878,13 @@ function createAnnotationEditingEngine(options = {}) {
|
|
|
5775
5878
|
});
|
|
5776
5879
|
}
|
|
5777
5880
|
function resolveBoxPreview(active) {
|
|
5778
|
-
const
|
|
5779
|
-
const
|
|
5780
|
-
const
|
|
5781
|
-
const
|
|
5881
|
+
const dimensions = active.current.mediaDimensions ?? active.start.mediaDimensions;
|
|
5882
|
+
const start = boundPoint(active.start.point, dimensions);
|
|
5883
|
+
const current = boundPoint(active.current.point, dimensions);
|
|
5884
|
+
const left = Math.min(start.x, current.x);
|
|
5885
|
+
const right = Math.max(start.x, current.x);
|
|
5886
|
+
const top = Math.min(start.y, current.y);
|
|
5887
|
+
const bottom = Math.max(start.y, current.y);
|
|
5782
5888
|
const rect = {
|
|
5783
5889
|
x: (left + right) / 2,
|
|
5784
5890
|
y: (top + bottom) / 2,
|
|
@@ -5787,6 +5893,35 @@ function createAnnotationEditingEngine(options = {}) {
|
|
|
5787
5893
|
};
|
|
5788
5894
|
return tool.createDetection(rect);
|
|
5789
5895
|
}
|
|
5896
|
+
function resolveMovePreview(active, input) {
|
|
5897
|
+
let dx = input.point.x - active.start.point.x;
|
|
5898
|
+
let dy = input.point.y - active.start.point.y;
|
|
5899
|
+
let rect = active.detection.rect;
|
|
5900
|
+
const dimensions = input.mediaDimensions ?? active.start.mediaDimensions;
|
|
5901
|
+
if (rect && dimensions) {
|
|
5902
|
+
const width = Math.min(rect.width, dimensions.width);
|
|
5903
|
+
const height = Math.min(rect.height, dimensions.height);
|
|
5904
|
+
const x = Math.max(width / 2, Math.min(rect.x + dx, dimensions.width - width / 2));
|
|
5905
|
+
const y = Math.max(height / 2, Math.min(rect.y + dy, dimensions.height - height / 2));
|
|
5906
|
+
dx = x - rect.x;
|
|
5907
|
+
dy = y - rect.y;
|
|
5908
|
+
rect = { ...rect, x, y, width, height };
|
|
5909
|
+
}
|
|
5910
|
+
const preview = offsetDetection(active.detection, dx, dy);
|
|
5911
|
+
return {
|
|
5912
|
+
preview: rect && dimensions ? { ...preview, rect } : preview,
|
|
5913
|
+
dx,
|
|
5914
|
+
dy,
|
|
5915
|
+
};
|
|
5916
|
+
}
|
|
5917
|
+
function boundPoint(point, dimensions) {
|
|
5918
|
+
return dimensions
|
|
5919
|
+
? {
|
|
5920
|
+
x: Math.max(0, Math.min(point.x, dimensions.width)),
|
|
5921
|
+
y: Math.max(0, Math.min(point.y, dimensions.height)),
|
|
5922
|
+
}
|
|
5923
|
+
: point;
|
|
5924
|
+
}
|
|
5790
5925
|
function resolvePathPreview(points) {
|
|
5791
5926
|
return tool.createDetection(points);
|
|
5792
5927
|
}
|
|
@@ -5828,6 +5963,11 @@ function createAnnotationEditingEngine(options = {}) {
|
|
|
5828
5963
|
preview,
|
|
5829
5964
|
});
|
|
5830
5965
|
}
|
|
5966
|
+
function rejectCreation() {
|
|
5967
|
+
const onRejected = tool?.onRejected;
|
|
5968
|
+
cancel();
|
|
5969
|
+
onRejected?.();
|
|
5970
|
+
}
|
|
5831
5971
|
function cancel() {
|
|
5832
5972
|
const pointerId = gesture && "start" in gesture
|
|
5833
5973
|
? gesture.start.pointerId
|
|
@@ -7268,6 +7408,7 @@ function resolveDefaultClassColor(detection, getClassColor) {
|
|
|
7268
7408
|
const annotationRendererKinds = [
|
|
7269
7409
|
"box",
|
|
7270
7410
|
"box-corners",
|
|
7411
|
+
"depth",
|
|
7271
7412
|
"ellipse",
|
|
7272
7413
|
"heatmap",
|
|
7273
7414
|
"keypoints",
|
|
@@ -7321,6 +7462,11 @@ const RegionRendererComposeMode = {
|
|
|
7321
7462
|
const annotationRenderers = {
|
|
7322
7463
|
box: (options) => createAnnotationRenderer("box", options),
|
|
7323
7464
|
boxCorners: (options) => createAnnotationRenderer("box-corners", options),
|
|
7465
|
+
depth: (options) => ({
|
|
7466
|
+
...options,
|
|
7467
|
+
id: options?.id ?? "depth",
|
|
7468
|
+
kind: "depth",
|
|
7469
|
+
}),
|
|
7324
7470
|
ellipse: (options) => createAnnotationRenderer("ellipse", options),
|
|
7325
7471
|
heatmap: (options) => ({
|
|
7326
7472
|
...options,
|
|
@@ -7356,6 +7502,7 @@ const annotationRendererRegistry = {
|
|
|
7356
7502
|
createCanonicalStyle: createDefaultBoxCornerStyle,
|
|
7357
7503
|
styleField: "boxCornerStyle",
|
|
7358
7504
|
},
|
|
7505
|
+
depth: { cardinality: "multiple" },
|
|
7359
7506
|
ellipse: {
|
|
7360
7507
|
cardinality: "singleton",
|
|
7361
7508
|
createCanonicalStyle: createDefaultEllipseStyle,
|
|
@@ -7420,6 +7567,241 @@ function resolveAnnotationRendererStyleFields(kinds) {
|
|
|
7420
7567
|
});
|
|
7421
7568
|
}
|
|
7422
7569
|
|
|
7570
|
+
/**
|
|
7571
|
+
* What the stored values of a depth map measure.
|
|
7572
|
+
*
|
|
7573
|
+
* Every kind stores 0 where there is no depth.
|
|
7574
|
+
*/
|
|
7575
|
+
const DepthMapKind = {
|
|
7576
|
+
/** Stereo disparity in pixels of the map. Larger is nearer. */
|
|
7577
|
+
DisparityPx: "disparity_px",
|
|
7578
|
+
/** Metric depth along the optical axis in metres. Smaller is nearer. */
|
|
7579
|
+
DepthM: "depth_m",
|
|
7580
|
+
/**
|
|
7581
|
+
* Unitless inverse depth up to an unknown scale and shift, as monocular
|
|
7582
|
+
* models produce it. Larger is nearer; it has no metric depth.
|
|
7583
|
+
*/
|
|
7584
|
+
RelativeInverse: "relative_inverse",
|
|
7585
|
+
};
|
|
7586
|
+
/**
|
|
7587
|
+
* The quantity a depth renderer colours.
|
|
7588
|
+
*
|
|
7589
|
+
* Disparity is inverse depth, so it spends colour on near detail the way
|
|
7590
|
+
* stereo measures it. Either way the nearest end of the range is the warm or
|
|
7591
|
+
* bright end of the colour table.
|
|
7592
|
+
*/
|
|
7593
|
+
const DepthQuantity = {
|
|
7594
|
+
Disparity: "disparity",
|
|
7595
|
+
Depth: "depth",
|
|
7596
|
+
};
|
|
7597
|
+
const DepthColormap = {
|
|
7598
|
+
Turbo: "turbo",
|
|
7599
|
+
Viridis: "viridis",
|
|
7600
|
+
Cividis: "cividis",
|
|
7601
|
+
Inferno: "inferno",
|
|
7602
|
+
Magma: "magma",
|
|
7603
|
+
Grayscale: "grayscale",
|
|
7604
|
+
};
|
|
7605
|
+
/** How a depth map is sampled where one map pixel does not cover one media pixel. */
|
|
7606
|
+
const DepthSampling = {
|
|
7607
|
+
/** Nearest when the map is at least media size, edge-aware otherwise. */
|
|
7608
|
+
Auto: "auto",
|
|
7609
|
+
Nearest: "nearest",
|
|
7610
|
+
/**
|
|
7611
|
+
* Bilinear over valid neighbours only, falling back to the nearest one
|
|
7612
|
+
* across a depth edge, so objects never bleed into the background.
|
|
7613
|
+
*/
|
|
7614
|
+
EdgeAware: "edge-aware",
|
|
7615
|
+
};
|
|
7616
|
+
/**
|
|
7617
|
+
* The luma range a depth preview video's codes are written in.
|
|
7618
|
+
*
|
|
7619
|
+
* TV range is what browsers' hardware decoders return as written; a decoder
|
|
7620
|
+
* may squeeze full range into TV range or move codes converting it to RGB.
|
|
7621
|
+
*/
|
|
7622
|
+
const DepthPreviewLevels = {
|
|
7623
|
+
/** Every code from 0 to 255; the top code is 255. */
|
|
7624
|
+
Full: "full",
|
|
7625
|
+
/** Limited range: black is 16 and the top code is 235. */
|
|
7626
|
+
Tv: "tv",
|
|
7627
|
+
};
|
|
7628
|
+
|
|
7629
|
+
// Generated by tools/depth-colormaps/generate-depth-colormap-tables.py
|
|
7630
|
+
// from matplotlib 3.11.2. Do not edit by hand.
|
|
7631
|
+
//
|
|
7632
|
+
// Each table is 256 sRGB entries as 6-digit hex, darkest or coldest
|
|
7633
|
+
// first, in the order the colour coordinate t runs from 0 to 1.
|
|
7634
|
+
/*!
|
|
7635
|
+
* Turbo, Anton Mikhailov's published table.
|
|
7636
|
+
* Copyright 2019 Google LLC.
|
|
7637
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
7638
|
+
* https://gist.github.com/mikhailov-work/ee72ba4191942acecc03fe6da94fc73f
|
|
7639
|
+
*/
|
|
7640
|
+
const turboTable = [
|
|
7641
|
+
"30123b32154333184a341b51351e5836215f37246638276d392a733a2d793b2f803c32863d358b3e38913f3b973f3e9c",
|
|
7642
|
+
"4040a24143a74146ac4249b1424bb5434eba4451bf4454c34456c74559cb455ccf455ed34661d64664da4666dd4669e0",
|
|
7643
|
+
"466be3476ee64771e94773eb4776ee4778f0477bf2467df44680f64682f84685fa4687fb458afc458cfd448ffe4391fe",
|
|
7644
|
+
"4294ff4196ff4099ff3e9bfe3d9efe3ba0fd3aa3fc38a5fb37a8fa35abf833adf731aff52fb2f42eb4f22cb7f02ab9ee",
|
|
7645
|
+
"28bceb27bee925c0e723c3e422c5e220c7df1fc9dd1ecbda1ccdd81bd0d51ad2d21ad4d019d5cd18d7ca18d9c818dbc5",
|
|
7646
|
+
"18ddc218dec018e0bd19e2bb19e3b91ae4b61ce6b41de7b21fe9af20eaac22ebaa25eca727eea42aefa12cf09e2ff19b",
|
|
7647
|
+
"32f29835f39438f4913cf58e3ff68a43f78746f8844af8804ef97d52fa7a55fa7659fb735dfc6f61fc6c65fd6969fd66",
|
|
7648
|
+
"6dfe6271fe5f75fe5c79fe597dff5680ff5384ff5188ff4e8bff4b8fff4992ff4796fe4499fe429cfe409ffd3fa1fd3d",
|
|
7649
|
+
"a4fc3ca7fc3aa9fb39acfb38affa37b1f936b4f836b7f735b9f635bcf534bef434c1f334c3f134c6f034c8ef34cbed34",
|
|
7650
|
+
"cdec34d0ea34d2e935d4e735d7e535d9e436dbe236dde037dfdf37e1dd37e3db38e5d938e7d739e9d539ebd339ecd13a",
|
|
7651
|
+
"eecf3aefcd3af1cb3af2c93af4c73af5c53af6c33af7c13af8be39f9bc39faba39fbb838fbb637fcb336fcb136fdae35",
|
|
7652
|
+
"fdac34fea933fea732fea431fea130fe9e2ffe9b2dfe992cfe962bfe932afe9029fd8d27fd8a26fc8725fc8423fb8122",
|
|
7653
|
+
"fb7e21fa7b1ff9781ef9751df8721cf76f1af66c19f56918f46617f36315f26014f15d13f05b12ef5811ed5510ec530f",
|
|
7654
|
+
"eb500eea4e0de84b0ce7490ce5470be4450ae2430ae14109df3f08dd3d08dc3b07da3907d83706d63506d43305d23105",
|
|
7655
|
+
"d02f05ce2d04cc2b04ca2a04c82803c52603c32503c12302be2102bc2002b91e02b71d02b41b01b21a01af1801ac1701",
|
|
7656
|
+
"a91601a71401a41301a112019e10019b0f01980e01950d01920b018e0a018b09028808028507028106027e05027a0403",
|
|
7657
|
+
].join("");
|
|
7658
|
+
/*!
|
|
7659
|
+
* Viridis, by Stéfan van der Walt, Nathaniel Smith and Eric Firing.
|
|
7660
|
+
* Released under CC0: https://github.com/BIDS/colormap
|
|
7661
|
+
*/
|
|
7662
|
+
const viridisTable = [
|
|
7663
|
+
"44015444025645045745055946075a46085c460a5d460b5e470d60470e61471063471164471365481467481668481769",
|
|
7664
|
+
"48186a481a6c481b6d481c6e481d6f481f70482071482173482374482475482576482677482878482979472a7a472c7a",
|
|
7665
|
+
"472d7b472e7c472f7d46307e46327e46337f463480453581453781453882443983443a83443b84433d84433e85423f85",
|
|
7666
|
+
"4240864241864142874144874045884046883f47883f48893e49893e4a893e4c8a3d4d8a3d4e8a3c4f8a3c508b3b518b",
|
|
7667
|
+
"3b528b3a538b3a548c39558c39568c38588c38598c375a8c375b8d365c8d365d8d355e8d355f8d34608d34618d33628d",
|
|
7668
|
+
"33638d32648e32658e31668e31678e31688e30698e306a8e2f6b8e2f6c8e2e6d8e2e6e8e2e6f8e2d708e2d718e2c718e",
|
|
7669
|
+
"2c728e2c738e2b748e2b758e2a768e2a778e2a788e29798e297a8e297b8e287c8e287d8e277e8e277f8e27808e26818e",
|
|
7670
|
+
"26828e26828e25838e25848e25858e24868e24878e23888e23898e238a8d228b8d228c8d228d8d218e8d218f8d21908d",
|
|
7671
|
+
"21918c20928c20928c20938c1f948c1f958b1f968b1f978b1f988b1f998a1f9a8a1e9b8a1e9c891e9d891f9e891f9f88",
|
|
7672
|
+
"1fa0881fa1881fa1871fa28720a38620a48621a58521a68522a78522a88423a98324aa8325ab8225ac8226ad8127ad81",
|
|
7673
|
+
"28ae8029af7f2ab07f2cb17e2db27d2eb37c2fb47c31b57b32b67a34b67935b77937b87838b9773aba763bbb753dbc74",
|
|
7674
|
+
"3fbc7340bd7242be7144bf7046c06f48c16e4ac16d4cc26c4ec36b50c46a52c56954c56856c66758c7655ac8645cc863",
|
|
7675
|
+
"5ec96260ca6063cb5f65cb5e67cc5c69cd5b6ccd5a6ece5870cf5773d05675d05477d1537ad1517cd2507fd34e81d34d",
|
|
7676
|
+
"84d44b86d54989d5488bd6468ed64590d74393d74195d84098d83e9bd93c9dd93ba0da39a2da37a5db36a8db34aadc32",
|
|
7677
|
+
"addc30b0dd2fb2dd2db5de2bb8de29bade28bddf26c0df25c2df23c5e021c8e020cae11fcde11dd0e11cd2e21bd5e21a",
|
|
7678
|
+
"d8e219dae319dde318dfe318e2e418e5e419e7e419eae51aece51befe51cf1e51df4e61ef6e620f8e621fbe723fde725",
|
|
7679
|
+
].join("");
|
|
7680
|
+
/*!
|
|
7681
|
+
* Cividis, Nuñez, Anderton and Renslow, PLOS ONE 13(7) e0199239 (2018),
|
|
7682
|
+
* from https://github.com/pnnl/cmaputil as shipped by matplotlib.
|
|
7683
|
+
* Copyright (c) 2017, Battelle Memorial Institute. Redistributed under
|
|
7684
|
+
* its BSD-style licence, which asks for this notice to be kept.
|
|
7685
|
+
*/
|
|
7686
|
+
const cividisTable = [
|
|
7687
|
+
"00224e00234f00245100255300255400265600275800285900285b00295d002a5f002a61002b62002c64002c66002d68",
|
|
7688
|
+
"002e6a002e6c002f6d00306f0030700031700031710132710533710833700c34700f357012357014367016377018376f",
|
|
7689
|
+
"1a386f1c396f1e3a6f203a6f213b6e233c6e243c6e263d6e273e6e293f6e2a3f6d2b406d2d416d2e416d2f426d31436d",
|
|
7690
|
+
"32436d33446d34456c35456c36466c38476c39486c3a486c3b496c3c4a6c3d4a6c3e4b6c3f4c6c404c6c414d6c424e6c",
|
|
7691
|
+
"434e6c444f6c45506c46516c47516c48526c49536c4a536c4b546c4c556c4d556c4e566c4f576c50576c51586d52596d",
|
|
7692
|
+
"535a6d545a6d555b6d555c6d565c6d575d6d585e6d595e6e5a5f6e5b606e5c616e5d616e5e626e5e636f5f636f60646f",
|
|
7693
|
+
"61656f62656f636670646770656870656870666970676a71686a71696b716a6c716b6d726c6d726c6e726d6f726e6f73",
|
|
7694
|
+
"6f70737071737172747272747273747374757474757575757676767777767777777878777979777a7a787b7a787c7b78",
|
|
7695
|
+
"7d7c787e7c787e7d787f7e78807f78817f788280798381798482798582798683798784788885788985788a86788b8778",
|
|
7696
|
+
"8c88788d88788e89788f8a78908b78918b78928c78928d78938e78948e77958f779690779791779892779992779a9376",
|
|
7697
|
+
"9b94769c95769d95769e96769f9775a09875a19975a29975a39a74a49b74a59c74a69c74a79d73a89e73a99f73aaa073",
|
|
7698
|
+
"aba072aca172ada272aea371afa471b0a571b1a570b3a670b4a76fb5a86fb6a96fb7a96eb8aa6eb9ab6dbaac6dbbad6d",
|
|
7699
|
+
"bcae6cbdae6cbeaf6bbfb06bc0b16ac1b26ac2b369c3b369c4b468c5b568c6b667c7b767c8b866c9b965cbb965ccba64",
|
|
7700
|
+
"cdbb63cebc63cfbd62d0be62d1bf61d2c060d3c05fd4c15fd5c25ed6c35dd7c45cd9c55cdac65bdbc75adcc859ddc858",
|
|
7701
|
+
"dec958dfca57e0cb56e1cc55e2cd54e4ce53e5cf52e6d051e7d150e8d24fe9d34eead34cebd44bedd54aeed649efd748",
|
|
7702
|
+
"f0d846f1d945f2da44f3db42f5dc41f6dd3ff7de3ef8df3cf9e03afbe138fce236fde334fee434fee535fee636fee838",
|
|
7703
|
+
].join("");
|
|
7704
|
+
/*!
|
|
7705
|
+
* Inferno, by Stéfan van der Walt and Nathaniel Smith.
|
|
7706
|
+
* Released under CC0: https://github.com/BIDS/colormap
|
|
7707
|
+
*/
|
|
7708
|
+
const infernoTable = [
|
|
7709
|
+
"00000401000501010601010802010a02020c02020e03021004031204031405041706041907051b08051d09061f0a0722",
|
|
7710
|
+
"0b07240c08260d08290e092b10092d110a30120a32140b34150b37160b39180c3c190c3e1b0c411c0c431e0c451f0c48",
|
|
7711
|
+
"210c4a230c4c240c4f260c51280b53290b552b0b572d0b592f0a5b310a5c320a5e340a5f3609613809623909633b0964",
|
|
7712
|
+
"3d09653e0966400a67420a68440a68450a69470b6a490b6a4a0c6b4c0c6b4d0d6c4f0d6c510e6c520e6d540f6d550f6d",
|
|
7713
|
+
"57106e59106e5a116e5c126e5d126e5f136e61136e62146e64156e65156e67166e69166e6a176e6c186e6d186e6f196e",
|
|
7714
|
+
"71196e721a6e741a6e751b6e771c6d781c6d7a1d6d7c1d6d7d1e6d7f1e6c801f6c82206c84206b85216b87216b88226a",
|
|
7715
|
+
"8a226a8c23698d23698f24699025689225689326679526679727669827669a28659b29649d29649f2a63a02a63a22b62",
|
|
7716
|
+
"a32c61a52c60a62d60a82e5fa92e5eab2f5ead305dae305cb0315bb1325ab3325ab43359b63458b73557b93556ba3655",
|
|
7717
|
+
"bc3754bd3853bf3952c03a51c13a50c33b4fc43c4ec63d4dc73e4cc83f4bca404acb4149cc4248ce4347cf4446d04545",
|
|
7718
|
+
"d24644d34743d44842d54a41d74b3fd84c3ed94d3dda4e3cdb503bdd513ade5238df5337e05536e15635e25734e35933",
|
|
7719
|
+
"e45a31e55c30e65d2fe75e2ee8602de9612bea632aeb6429eb6628ec6726ed6925ee6a24ef6c23ef6e21f06f20f1711f",
|
|
7720
|
+
"f1731df2741cf3761bf37819f47918f57b17f57d15f67e14f68013f78212f78410f8850ff8870ef8890cf98b0bf98c0a",
|
|
7721
|
+
"f98e09fa9008fa9207fa9407fb9606fb9706fb9906fb9b06fb9d07fc9f07fca108fca309fca50afca60cfca80dfcaa0f",
|
|
7722
|
+
"fcac11fcae12fcb014fcb216fcb418fbb61afbb81dfbba1ffbbc21fbbe23fac026fac228fac42afac62df9c72ff9c932",
|
|
7723
|
+
"f9cb35f8cd37f8cf3af7d13df7d340f6d543f6d746f5d949f5db4cf4dd4ff4df53f4e156f3e35af3e55df2e661f2e865",
|
|
7724
|
+
"f2ea69f1ec6df1ed71f1ef75f1f179f2f27df2f482f3f586f3f68af4f88ef5f992f6fa96f8fb9af9fc9dfafda1fcffa4",
|
|
7725
|
+
].join("");
|
|
7726
|
+
/*!
|
|
7727
|
+
* Magma, by Stéfan van der Walt and Nathaniel Smith.
|
|
7728
|
+
* Released under CC0: https://github.com/BIDS/colormap
|
|
7729
|
+
*/
|
|
7730
|
+
const magmaTable = [
|
|
7731
|
+
"00000401000501010601010802010902020b02020d03030f03031204041405041606051806051a07061c08071e090720",
|
|
7732
|
+
"0a08220b09240c09260d0a290e0b2b100b2d110c2f120d31130d34140e36150e38160f3b180f3d19103f1a10421c1044",
|
|
7733
|
+
"1d11471e114920114b21114e22115024125325125527125829115a2a115c2c115f2d11612f1163311165331067341069",
|
|
7734
|
+
"36106b38106c390f6e3b0f703d0f713f0f72400f74420f75440f764510774710784910784a10794c117a4e117b4f127b",
|
|
7735
|
+
"51127c52137c54137d56147d57157e59157e5a167e5c167f5d177f5f187f601880621980641a80651a80671b80681c81",
|
|
7736
|
+
"6a1c816b1d816d1d816e1e81701f81721f817320817521817621817822817922827b23827c23827e2482802582812581",
|
|
7737
|
+
"8326818426818627818827818928818b29818c29818e2a81902a81912b81932b80942c80962c80982d80992d809b2e7f",
|
|
7738
|
+
"9c2e7f9e2f7fa02f7fa1307ea3307ea5317ea6317da8327daa337dab337cad347cae347bb0357bb2357bb3367ab5367a",
|
|
7739
|
+
"b73779b83779ba3878bc3978bd3977bf3a77c03a76c23b75c43c75c53c74c73d73c83e73ca3e72cc3f71cd4071cf4070",
|
|
7740
|
+
"d0416fd2426fd3436ed5446dd6456cd8456cd9466bdb476adc4869de4968df4a68e04c67e24d66e34e65e44f64e55064",
|
|
7741
|
+
"e75263e85362e95462ea5661eb5760ec5860ed5a5fee5b5eef5d5ef05f5ef1605df2625df2645cf3655cf4675cf4695c",
|
|
7742
|
+
"f56b5cf66c5cf66e5cf7705cf7725cf8745cf8765cf9785df9795df97b5dfa7d5efa7f5efa815ffb835ffb8560fb8761",
|
|
7743
|
+
"fc8961fc8a62fc8c63fc8e64fc9065fd9266fd9467fd9668fd9869fd9a6afd9b6bfe9d6cfe9f6dfea16efea36ffea571",
|
|
7744
|
+
"fea772fea973feaa74feac76feae77feb078feb27afeb47bfeb67cfeb77efeb97ffebb81febd82febf84fec185fec287",
|
|
7745
|
+
"fec488fec68afec88cfeca8dfecc8ffecd90fecf92fed194fed395fed597fed799fed89afdda9cfddc9efddea0fde0a1",
|
|
7746
|
+
"fde2a3fde3a5fde5a7fde7a9fde9aafdebacfcecaefceeb0fcf0b2fcf2b4fcf4b6fcf6b8fcf7b9fcf9bbfcfbbdfcfdbf",
|
|
7747
|
+
].join("");
|
|
7748
|
+
|
|
7749
|
+
const DEPTH_COLORMAP_ENTRIES = 256;
|
|
7750
|
+
const colormapTables = {
|
|
7751
|
+
cividis: cividisTable,
|
|
7752
|
+
inferno: infernoTable,
|
|
7753
|
+
magma: magmaTable,
|
|
7754
|
+
turbo: turboTable,
|
|
7755
|
+
viridis: viridisTable,
|
|
7756
|
+
};
|
|
7757
|
+
const depthColormaps = new Set(Object.values(DepthColormap));
|
|
7758
|
+
function isDepthColormap(value) {
|
|
7759
|
+
return typeof value === "string" && depthColormaps.has(value);
|
|
7760
|
+
}
|
|
7761
|
+
/**
|
|
7762
|
+
* The colour table as 256 RGBA8 entries with opaque alpha, far or low values
|
|
7763
|
+
* first. Entry `i` is the colour of `t = i / 255`.
|
|
7764
|
+
*/
|
|
7765
|
+
function createDepthColormapLut(name) {
|
|
7766
|
+
if (!isDepthColormap(name)) {
|
|
7767
|
+
throw new RangeError(`Unknown depth colormap "${String(name)}".`);
|
|
7768
|
+
}
|
|
7769
|
+
const lut = new Uint8Array(DEPTH_COLORMAP_ENTRIES * 4);
|
|
7770
|
+
for (let index = 0; index < DEPTH_COLORMAP_ENTRIES; index += 1) {
|
|
7771
|
+
const offset = index * 4;
|
|
7772
|
+
if (name === DepthColormap.Grayscale) {
|
|
7773
|
+
lut[offset] = index;
|
|
7774
|
+
lut[offset + 1] = index;
|
|
7775
|
+
lut[offset + 2] = index;
|
|
7776
|
+
}
|
|
7777
|
+
else {
|
|
7778
|
+
const table = colormapTables[name];
|
|
7779
|
+
const hex = index * 6;
|
|
7780
|
+
lut[offset] = Number.parseInt(table.slice(hex, hex + 2), 16);
|
|
7781
|
+
lut[offset + 1] = Number.parseInt(table.slice(hex + 2, hex + 4), 16);
|
|
7782
|
+
lut[offset + 2] = Number.parseInt(table.slice(hex + 4, hex + 6), 16);
|
|
7783
|
+
}
|
|
7784
|
+
lut[offset + 3] = 255;
|
|
7785
|
+
}
|
|
7786
|
+
return lut;
|
|
7787
|
+
}
|
|
7788
|
+
/**
|
|
7789
|
+
* CSS colours of a depth colormap at `stops` evenly spaced points, far end
|
|
7790
|
+
* first and near end last, read from the same table the depth renderer draws
|
|
7791
|
+
* with. A legend that matches the picture is
|
|
7792
|
+
* `linear-gradient(to right, ${colors.join(", ")})`.
|
|
7793
|
+
*/
|
|
7794
|
+
function depthColormapColors(colormap, stops = 16) {
|
|
7795
|
+
if (!Number.isInteger(stops) || stops < 2 || stops > DEPTH_COLORMAP_ENTRIES) {
|
|
7796
|
+
throw new RangeError(`Depth colormap stops must be an integer from 2 to ${DEPTH_COLORMAP_ENTRIES}, got ${stops}.`);
|
|
7797
|
+
}
|
|
7798
|
+
const lut = createDepthColormapLut(colormap);
|
|
7799
|
+
return Array.from({ length: stops }, (_, stop) => {
|
|
7800
|
+
const offset = Math.round((stop * (DEPTH_COLORMAP_ENTRIES - 1)) / (stops - 1)) * 4;
|
|
7801
|
+
return `#${Array.from(lut.subarray(offset, offset + 3), (channel) => channel.toString(16).padStart(2, "0")).join("")}`;
|
|
7802
|
+
});
|
|
7803
|
+
}
|
|
7804
|
+
|
|
7423
7805
|
/**
|
|
7424
7806
|
* Resolves built-in renderer descriptors into the existing specialized style
|
|
7425
7807
|
* fields. The browser backend deliberately keeps ownership of its box, mask,
|
|
@@ -7451,6 +7833,9 @@ function resolveAnnotationRendererPresentation(presentation) {
|
|
|
7451
7833
|
if (renderer.kind === "heatmap") {
|
|
7452
7834
|
validateHeatmapRenderer(renderer);
|
|
7453
7835
|
}
|
|
7836
|
+
if (renderer.kind === "depth") {
|
|
7837
|
+
validateDepthRenderer(renderer);
|
|
7838
|
+
}
|
|
7454
7839
|
if (rendererIds.has(renderer.id)) {
|
|
7455
7840
|
throw new RangeError(`MediaRendererPresentation.renderers contains duplicate renderer id "${renderer.id}".`);
|
|
7456
7841
|
}
|
|
@@ -7493,6 +7878,32 @@ function validateHeatmapRenderer(renderer) {
|
|
|
7493
7878
|
throw new RangeError(`Invalid heatmap renderer settings for "${renderer.id}".`);
|
|
7494
7879
|
}
|
|
7495
7880
|
}
|
|
7881
|
+
const depthQuantities = new Set(Object.values(DepthQuantity));
|
|
7882
|
+
const depthSamplings = new Set(Object.values(DepthSampling));
|
|
7883
|
+
function validateDepthRenderer(renderer) {
|
|
7884
|
+
const { colormap, noDepthColor, opacity, quantity, range, sampling, wipe } = renderer;
|
|
7885
|
+
if ((colormap !== undefined && !isDepthColormap(colormap)) ||
|
|
7886
|
+
(quantity !== undefined && !depthQuantities.has(quantity)) ||
|
|
7887
|
+
(sampling !== undefined && !depthSamplings.has(sampling)) ||
|
|
7888
|
+
(range !== undefined &&
|
|
7889
|
+
range !== "clip" &&
|
|
7890
|
+
range !== "auto" &&
|
|
7891
|
+
(typeof range !== "object" ||
|
|
7892
|
+
range === null ||
|
|
7893
|
+
!Number.isFinite(range.min) ||
|
|
7894
|
+
!Number.isFinite(range.max) ||
|
|
7895
|
+
range.min >= range.max)) ||
|
|
7896
|
+
(opacity !== undefined && !Number.isFinite(opacity)) ||
|
|
7897
|
+
(wipe !== undefined &&
|
|
7898
|
+
!(Number.isFinite(wipe) && wipe >= 0 && wipe <= 1)) ||
|
|
7899
|
+
(noDepthColor !== undefined &&
|
|
7900
|
+
noDepthColor !== null &&
|
|
7901
|
+
!(Number.isInteger(noDepthColor) &&
|
|
7902
|
+
noDepthColor >= 0 &&
|
|
7903
|
+
noDepthColor <= 0xffffff))) {
|
|
7904
|
+
throw new RangeError(`Invalid depth renderer settings for "${renderer.id}".`);
|
|
7905
|
+
}
|
|
7906
|
+
}
|
|
7496
7907
|
function isStyleBackedAnnotationRenderer(renderer) {
|
|
7497
7908
|
return isStyleBackedAnnotationRendererKind(renderer.kind);
|
|
7498
7909
|
}
|
|
@@ -7948,8 +8359,10 @@ function createIdMaskFrame(instructions, options = {}) {
|
|
|
7948
8359
|
if (instructions.length === 0) {
|
|
7949
8360
|
return undefined;
|
|
7950
8361
|
}
|
|
7951
|
-
|
|
7952
|
-
|
|
8362
|
+
/* Pixel-grid dimensions use integer parts throughout allocation and RLE
|
|
8363
|
+
traversal. */
|
|
8364
|
+
const maskWidth = Math.floor(Math.max(...instructions.map(({ mask }) => mask.width)));
|
|
8365
|
+
const maskHeight = Math.floor(Math.max(...instructions.map(({ mask }) => mask.height)));
|
|
7953
8366
|
const width = resolveRasterWidth(maskWidth, options.maxWidth);
|
|
7954
8367
|
const height = width === maskWidth
|
|
7955
8368
|
? maskHeight
|
|
@@ -8001,8 +8414,8 @@ function createIdMaskFrame(instructions, options = {}) {
|
|
|
8001
8414
|
*/
|
|
8002
8415
|
function writeMaskRuns(data, frameWidth, mask, detectionMaskId) {
|
|
8003
8416
|
const counts = decodeCompressedRleCounts(mask.counts);
|
|
8004
|
-
const maskWidth = mask.width;
|
|
8005
|
-
const maskHeight = mask.height;
|
|
8417
|
+
const maskWidth = Math.floor(mask.width);
|
|
8418
|
+
const maskHeight = Math.floor(mask.height);
|
|
8006
8419
|
let maskOffset = 0;
|
|
8007
8420
|
for (let index = 0; index < counts.length; index += 1) {
|
|
8008
8421
|
const runLength = counts[index] ?? 0;
|
|
@@ -8036,9 +8449,10 @@ function writeMaskRuns(data, frameWidth, mask, detectionMaskId) {
|
|
|
8036
8449
|
*/
|
|
8037
8450
|
function writeScaledMaskRuns(data, axes, mask, detectionMaskId) {
|
|
8038
8451
|
const counts = decodeCompressedRleCounts(mask.counts);
|
|
8039
|
-
const maskWidth = mask.width;
|
|
8040
|
-
const maskHeight = mask.height;
|
|
8452
|
+
const maskWidth = Math.floor(mask.width);
|
|
8453
|
+
const maskHeight = Math.floor(mask.height);
|
|
8041
8454
|
let maskOffset = 0;
|
|
8455
|
+
const stride = axes.stride;
|
|
8042
8456
|
for (let index = 0; index < counts.length; index += 1) {
|
|
8043
8457
|
const runLength = counts[index] ?? 0;
|
|
8044
8458
|
if (index % 2 === 0 || runLength <= 0) {
|
|
@@ -8047,17 +8461,30 @@ function writeScaledMaskRuns(data, axes, mask, detectionMaskId) {
|
|
|
8047
8461
|
}
|
|
8048
8462
|
let x = Math.floor(maskOffset / maskHeight);
|
|
8049
8463
|
let y = maskOffset - x * maskHeight;
|
|
8050
|
-
let
|
|
8051
|
-
|
|
8052
|
-
|
|
8053
|
-
|
|
8054
|
-
|
|
8055
|
-
|
|
8056
|
-
|
|
8464
|
+
let remaining = runLength;
|
|
8465
|
+
/* A run walks down one column and wraps into the next, so it splits into
|
|
8466
|
+
column-sized segments. Within a segment the destination offset only ever
|
|
8467
|
+
steps by the row pitch or stays put: createMaskAxisMap builds
|
|
8468
|
+
`rows[i] = min(height - 1, floor(i * scale)) * width` with scale <= 1, so
|
|
8469
|
+
consecutive entries differ by exactly 0 or exactly `width`. Walking the
|
|
8470
|
+
destination directly therefore touches the same bytes the per-pixel loop
|
|
8471
|
+
touched, and drops the bounds test, the wrap test and one typed-array
|
|
8472
|
+
load from every source pixel. Where the scale collapses several source
|
|
8473
|
+
rows onto one destination row, this writes that byte once instead of
|
|
8474
|
+
once per source row. */
|
|
8475
|
+
while (remaining > 0 && x < maskWidth) {
|
|
8476
|
+
const span = Math.min(remaining, maskHeight - y);
|
|
8477
|
+
const column = axes.columns[x];
|
|
8478
|
+
const first = axes.rows[y];
|
|
8479
|
+
const last = axes.rows[y + span - 1];
|
|
8480
|
+
for (let offset = first; offset <= last; offset += stride) {
|
|
8481
|
+
data[offset + column] = detectionMaskId;
|
|
8482
|
+
}
|
|
8483
|
+
remaining -= span;
|
|
8484
|
+
y += span;
|
|
8057
8485
|
if (y === maskHeight) {
|
|
8058
8486
|
y = 0;
|
|
8059
8487
|
x += 1;
|
|
8060
|
-
column = axes.columns[x];
|
|
8061
8488
|
}
|
|
8062
8489
|
}
|
|
8063
8490
|
maskOffset += runLength;
|
|
@@ -8069,22 +8496,19 @@ function resolveRasterWidth(maskWidth, maxWidth) {
|
|
|
8069
8496
|
: maskWidth;
|
|
8070
8497
|
}
|
|
8071
8498
|
/**
|
|
8072
|
-
*
|
|
8073
|
-
*
|
|
8074
|
-
*
|
|
8075
|
-
* own width, which the shader draws as an inner boundary at any scale. The
|
|
8076
|
-
* ceiling is the widest neighbourhood the shaders scan, so a wider stroke would
|
|
8077
|
-
* be drawn at the ceiling anyway and every layer drawing the same annotation
|
|
8078
|
-
* has to arrive at the same width.
|
|
8499
|
+
* Converts a source-grid stroke width into raster texels. Fractional widths
|
|
8500
|
+
* let coverage shaders draw a border within a texel. The ceiling bounds the
|
|
8501
|
+
* neighbourhood the shaders scan.
|
|
8079
8502
|
*/
|
|
8080
8503
|
function resolveIdMaskStrokeTexels(strokeWidth, maskWidth, rasterWidth) {
|
|
8081
8504
|
const scale = maskWidth > 0 ? rasterWidth / maskWidth : 1;
|
|
8082
|
-
return Math.min(
|
|
8505
|
+
return Math.min(strokeWidth * scale, MAX_ID_MASK_STROKE_WIDTH);
|
|
8083
8506
|
}
|
|
8084
8507
|
function createScaledMaskAxes(frame) {
|
|
8085
8508
|
return {
|
|
8086
8509
|
columns: createMaskAxisMap(frame.maskWidth, frame.width, 1),
|
|
8087
8510
|
rows: createMaskAxisMap(frame.maskHeight, frame.height, frame.width),
|
|
8511
|
+
stride: frame.width,
|
|
8088
8512
|
};
|
|
8089
8513
|
}
|
|
8090
8514
|
/** Rows hold their destination offset, so writing a run is two lookups. */
|
|
@@ -8313,6 +8737,658 @@ function includes(values, value) {
|
|
|
8313
8737
|
: values.has(value);
|
|
8314
8738
|
}
|
|
8315
8739
|
|
|
8740
|
+
const DEPTH_MANIFEST_SCHEMA = "supervision.depth-manifest";
|
|
8741
|
+
const PREVIEW_TOP_CODES = {
|
|
8742
|
+
full: 255,
|
|
8743
|
+
tv: 235,
|
|
8744
|
+
};
|
|
8745
|
+
/**
|
|
8746
|
+
* TV black, 16, is written for no depth, and a decoder that converts to RGB
|
|
8747
|
+
* returns every code below it as black too, so the reserved band must cover it.
|
|
8748
|
+
*/
|
|
8749
|
+
const PREVIEW_MIN_RESERVED_CODES = { full: 0, tv: 16 };
|
|
8750
|
+
const depthPreviewLevels = new Set(Object.values(DepthPreviewLevels));
|
|
8751
|
+
const depthMapKinds = new Set(Object.values(DepthMapKind));
|
|
8752
|
+
const FRAME_INDEX_TOKEN = /\{index(?::0(\d{1,2}))?\}/g;
|
|
8753
|
+
/**
|
|
8754
|
+
* Reads the snake_case `depth.json` a producer writes into a
|
|
8755
|
+
* {@link DepthManifest}, and rejects anything a renderer could not honour.
|
|
8756
|
+
*
|
|
8757
|
+
* Messages name the offending wire field, such as
|
|
8758
|
+
* `depth.json: storage.scale must be a positive number`. Unknown fields are
|
|
8759
|
+
* ignored so newer producers stay readable.
|
|
8760
|
+
*/
|
|
8761
|
+
function parseDepthManifest(json) {
|
|
8762
|
+
const root = readObject(json, "the manifest");
|
|
8763
|
+
if (root.schema !== DEPTH_MANIFEST_SCHEMA) {
|
|
8764
|
+
fail(`schema must be "${DEPTH_MANIFEST_SCHEMA}"`);
|
|
8765
|
+
}
|
|
8766
|
+
if (root.version !== 1) {
|
|
8767
|
+
fail(`version ${String(root.version)} is not supported; expected 1`);
|
|
8768
|
+
}
|
|
8769
|
+
const kind = root.kind;
|
|
8770
|
+
if (typeof kind !== "string" || !depthMapKinds.has(kind)) {
|
|
8771
|
+
fail(`kind must be one of ${[...depthMapKinds].join(", ")}`);
|
|
8772
|
+
}
|
|
8773
|
+
const depthKind = kind;
|
|
8774
|
+
const width = readPositiveInteger(root.width, "width");
|
|
8775
|
+
const height = readPositiveInteger(root.height, "height");
|
|
8776
|
+
const view = readOptionalString(root.view, "view");
|
|
8777
|
+
const storage = readObject(root.storage, "storage");
|
|
8778
|
+
if (storage.format !== "png16") {
|
|
8779
|
+
fail('storage.format must be "png16"');
|
|
8780
|
+
}
|
|
8781
|
+
const scale = readPositiveNumber(storage.scale, "storage.scale");
|
|
8782
|
+
if (storage.no_depth !== 0) {
|
|
8783
|
+
fail("storage.no_depth must be 0");
|
|
8784
|
+
}
|
|
8785
|
+
const camera = root.camera === undefined || root.camera === null
|
|
8786
|
+
? undefined
|
|
8787
|
+
: readCamera(root.camera);
|
|
8788
|
+
const displayRange = readDisplayRange(root, depthKind);
|
|
8789
|
+
const hasImage = root.image !== undefined && root.image !== null;
|
|
8790
|
+
const hasFrames = root.frames !== undefined && root.frames !== null;
|
|
8791
|
+
if (hasImage === hasFrames) {
|
|
8792
|
+
fail("exactly one of image or frames must be present");
|
|
8793
|
+
}
|
|
8794
|
+
const image = hasImage ? readImage(root.image) : undefined;
|
|
8795
|
+
const frames = hasFrames ? readFrames(root.frames) : undefined;
|
|
8796
|
+
let preview;
|
|
8797
|
+
if (root.preview !== undefined && root.preview !== null) {
|
|
8798
|
+
if (!frames) {
|
|
8799
|
+
fail("preview is only valid next to frames");
|
|
8800
|
+
}
|
|
8801
|
+
if (depthKind !== DepthMapKind.DisparityPx) {
|
|
8802
|
+
fail(`preview is only supported for kind ${DepthMapKind.DisparityPx}`);
|
|
8803
|
+
}
|
|
8804
|
+
preview = readPreview(root.preview);
|
|
8805
|
+
}
|
|
8806
|
+
return {
|
|
8807
|
+
schema: DEPTH_MANIFEST_SCHEMA,
|
|
8808
|
+
version: 1,
|
|
8809
|
+
kind: depthKind,
|
|
8810
|
+
...(view === undefined ? {} : { view }),
|
|
8811
|
+
width,
|
|
8812
|
+
height,
|
|
8813
|
+
storage: { format: "png16", noDepth: 0, scale },
|
|
8814
|
+
...(camera === undefined ? {} : { camera }),
|
|
8815
|
+
...(displayRange === undefined ? {} : { displayRange }),
|
|
8816
|
+
...(image === undefined ? {} : { image }),
|
|
8817
|
+
...(frames === undefined ? {} : { frames }),
|
|
8818
|
+
...(preview === undefined ? {} : { preview }),
|
|
8819
|
+
};
|
|
8820
|
+
}
|
|
8821
|
+
/**
|
|
8822
|
+
* Expands a clip frame pattern for one frame index: `{index}` becomes the
|
|
8823
|
+
* plain number and `{index:06}` the number zero-padded to six digits.
|
|
8824
|
+
*/
|
|
8825
|
+
function resolveDepthFrameFile(pattern, index) {
|
|
8826
|
+
if (!Number.isInteger(index) || index < 0) {
|
|
8827
|
+
throw new RangeError(`Depth frame index must be a non-negative integer, got ${index}.`);
|
|
8828
|
+
}
|
|
8829
|
+
return pattern.replace(FRAME_INDEX_TOKEN, (_token, width) => width === undefined
|
|
8830
|
+
? String(index)
|
|
8831
|
+
: String(index).padStart(Number(width), "0"));
|
|
8832
|
+
}
|
|
8833
|
+
/**
|
|
8834
|
+
* Checks a host-supplied depth map's shape, without reading its sample values.
|
|
8835
|
+
*/
|
|
8836
|
+
function validateDepthMap(map) {
|
|
8837
|
+
const reject = (message) => {
|
|
8838
|
+
throw new RangeError(`DepthMap ${message}.`);
|
|
8839
|
+
};
|
|
8840
|
+
if (!depthMapKinds.has(map.kind)) {
|
|
8841
|
+
reject(`kind must be one of ${[...depthMapKinds].join(", ")}`);
|
|
8842
|
+
}
|
|
8843
|
+
if (!isPositiveInteger(map.width) || !isPositiveInteger(map.height)) {
|
|
8844
|
+
reject("width and height must be positive integers");
|
|
8845
|
+
}
|
|
8846
|
+
const pixelCount = map.width * map.height;
|
|
8847
|
+
const samples = map.samples;
|
|
8848
|
+
if (samples?.encoding === "scaled16") {
|
|
8849
|
+
if (!(samples.values instanceof Uint16Array)) {
|
|
8850
|
+
reject("samples.values must be a Uint16Array for scaled16 samples");
|
|
8851
|
+
}
|
|
8852
|
+
if (!isPositiveNumber(samples.scale)) {
|
|
8853
|
+
reject("samples.scale must be a positive number");
|
|
8854
|
+
}
|
|
8855
|
+
}
|
|
8856
|
+
else if (samples?.encoding === "preview8") {
|
|
8857
|
+
if (!(samples.values instanceof Uint8Array)) {
|
|
8858
|
+
reject("samples.values must be a Uint8Array for preview8 samples");
|
|
8859
|
+
}
|
|
8860
|
+
const levels = samples.levels ?? DepthPreviewLevels.Full;
|
|
8861
|
+
if (!depthPreviewLevels.has(levels)) {
|
|
8862
|
+
reject(`samples.levels must be one of ${[...depthPreviewLevels].join(", ")}`);
|
|
8863
|
+
}
|
|
8864
|
+
if (!isReservedPreviewCode(samples.reservedMax, levels)) {
|
|
8865
|
+
reject(`samples.reservedMax must be ${describeReservedCodes(levels)}`);
|
|
8866
|
+
}
|
|
8867
|
+
if (!isRange(samples.range)) {
|
|
8868
|
+
reject("samples.range must have finite bounds with min < max");
|
|
8869
|
+
}
|
|
8870
|
+
}
|
|
8871
|
+
else {
|
|
8872
|
+
reject('samples.encoding must be "scaled16" or "preview8"');
|
|
8873
|
+
}
|
|
8874
|
+
if (samples.values.length !== pixelCount) {
|
|
8875
|
+
reject(`samples.values has ${samples.values.length} values for ${map.width}x${map.height} pixels`);
|
|
8876
|
+
}
|
|
8877
|
+
if (map.camera !== undefined && !isCamera(map.camera)) {
|
|
8878
|
+
reject("camera needs finite fxPx and baselineM above 0, and finite optional offsets");
|
|
8879
|
+
}
|
|
8880
|
+
if (map.displayRange !== undefined && !isRange(map.displayRange)) {
|
|
8881
|
+
reject("displayRange must have finite bounds with min < max");
|
|
8882
|
+
}
|
|
8883
|
+
if (map.confidence !== undefined) {
|
|
8884
|
+
if (!(map.confidence instanceof Uint8Array)) {
|
|
8885
|
+
reject("confidence must be a Uint8Array");
|
|
8886
|
+
}
|
|
8887
|
+
if (map.confidence.length !== pixelCount) {
|
|
8888
|
+
reject(`confidence has ${map.confidence.length} values for ${map.width}x${map.height} pixels`);
|
|
8889
|
+
}
|
|
8890
|
+
}
|
|
8891
|
+
}
|
|
8892
|
+
function readCamera(value) {
|
|
8893
|
+
const camera = readObject(value, "camera");
|
|
8894
|
+
const fxPx = readPositiveNumber(camera.fx_px, "camera.fx_px");
|
|
8895
|
+
const baselineM = readPositiveNumber(camera.baseline_m, "camera.baseline_m");
|
|
8896
|
+
const doffsPx = readOptionalFinite(camera.doffs_px, "camera.doffs_px");
|
|
8897
|
+
const cxPx = readOptionalFinite(camera.cx_px, "camera.cx_px");
|
|
8898
|
+
const cyPx = readOptionalFinite(camera.cy_px, "camera.cy_px");
|
|
8899
|
+
return {
|
|
8900
|
+
fxPx,
|
|
8901
|
+
baselineM,
|
|
8902
|
+
...(doffsPx === undefined ? {} : { doffsPx }),
|
|
8903
|
+
...(cxPx === undefined ? {} : { cxPx }),
|
|
8904
|
+
...(cyPx === undefined ? {} : { cyPx }),
|
|
8905
|
+
};
|
|
8906
|
+
}
|
|
8907
|
+
/**
|
|
8908
|
+
* `display_range` is in the kind's unit; `display_range_px` is its name for
|
|
8909
|
+
* disparity, kept because disparity producers already write it.
|
|
8910
|
+
*/
|
|
8911
|
+
function readDisplayRange(root, kind) {
|
|
8912
|
+
const inKindUnit = readOptionalRange(root.display_range, "display_range");
|
|
8913
|
+
const inPixels = readOptionalRange(root.display_range_px, "display_range_px");
|
|
8914
|
+
if (inPixels !== undefined && kind !== DepthMapKind.DisparityPx) {
|
|
8915
|
+
fail(`display_range_px is only valid for kind ${DepthMapKind.DisparityPx}; use display_range`);
|
|
8916
|
+
}
|
|
8917
|
+
if (inKindUnit !== undefined &&
|
|
8918
|
+
inPixels !== undefined &&
|
|
8919
|
+
(inKindUnit.min !== inPixels.min || inKindUnit.max !== inPixels.max)) {
|
|
8920
|
+
fail("display_range and display_range_px disagree");
|
|
8921
|
+
}
|
|
8922
|
+
return inKindUnit ?? inPixels;
|
|
8923
|
+
}
|
|
8924
|
+
function readImage(value) {
|
|
8925
|
+
const image = readObject(value, "image");
|
|
8926
|
+
const file = readFileName(image.file, "image.file");
|
|
8927
|
+
const confidenceFile = readOptionalFileName(image.confidence_file, "image.confidence_file");
|
|
8928
|
+
return {
|
|
8929
|
+
file,
|
|
8930
|
+
...(confidenceFile === undefined ? {} : { confidenceFile }),
|
|
8931
|
+
};
|
|
8932
|
+
}
|
|
8933
|
+
function readFrames(value) {
|
|
8934
|
+
const frames = readObject(value, "frames");
|
|
8935
|
+
const count = readPositiveInteger(frames.count, "frames.count");
|
|
8936
|
+
const exact = readFramePattern(frames.exact, "frames.exact");
|
|
8937
|
+
const confidence = frames.confidence === undefined || frames.confidence === null
|
|
8938
|
+
? undefined
|
|
8939
|
+
: readFramePattern(frames.confidence, "frames.confidence");
|
|
8940
|
+
const timesS = frames.times_s === undefined || frames.times_s === null
|
|
8941
|
+
? undefined
|
|
8942
|
+
: readFrameTimes(frames.times_s, count);
|
|
8943
|
+
return {
|
|
8944
|
+
count,
|
|
8945
|
+
exact,
|
|
8946
|
+
...(confidence === undefined ? {} : { confidence }),
|
|
8947
|
+
...(timesS === undefined ? {} : { timesS }),
|
|
8948
|
+
};
|
|
8949
|
+
}
|
|
8950
|
+
function readFrameTimes(value, count) {
|
|
8951
|
+
if (!Array.isArray(value) || value.length !== count) {
|
|
8952
|
+
fail(`frames.times_s must be an array of frames.count (${count}) times`);
|
|
8953
|
+
}
|
|
8954
|
+
const times = value;
|
|
8955
|
+
times.forEach((time, index) => {
|
|
8956
|
+
if (typeof time !== "number" || !Number.isFinite(time) || time < 0) {
|
|
8957
|
+
fail(`frames.times_s[${index}] must be a finite number of seconds >= 0`);
|
|
8958
|
+
}
|
|
8959
|
+
if (index > 0 && time <= times[index - 1]) {
|
|
8960
|
+
fail(`frames.times_s must strictly increase at index ${index}`);
|
|
8961
|
+
}
|
|
8962
|
+
});
|
|
8963
|
+
return [...times];
|
|
8964
|
+
}
|
|
8965
|
+
/** A preview without `levels` predates TV range and is read as full range. */
|
|
8966
|
+
function readPreview(value) {
|
|
8967
|
+
const preview = readObject(value, "preview");
|
|
8968
|
+
const file = readFileName(preview.file, "preview.file");
|
|
8969
|
+
const codec = readOptionalString(preview.codec, "preview.codec");
|
|
8970
|
+
const levels = preview.levels ?? DepthPreviewLevels.Full;
|
|
8971
|
+
const reservedMax = preview.reserved_max;
|
|
8972
|
+
if (typeof levels !== "string" || !depthPreviewLevels.has(levels)) {
|
|
8973
|
+
fail(`preview.levels must be one of ${[...depthPreviewLevels].join(", ")}`);
|
|
8974
|
+
}
|
|
8975
|
+
const previewLevels = levels;
|
|
8976
|
+
if (!isReservedPreviewCode(reservedMax, previewLevels)) {
|
|
8977
|
+
fail(`preview.reserved_max must be ${describeReservedCodes(previewLevels)}`);
|
|
8978
|
+
}
|
|
8979
|
+
const range = readOptionalRange(preview.range_px, "preview.range_px");
|
|
8980
|
+
if (range === undefined) {
|
|
8981
|
+
fail("preview.range_px is required");
|
|
8982
|
+
}
|
|
8983
|
+
return {
|
|
8984
|
+
file,
|
|
8985
|
+
...(codec === undefined ? {} : { codec }),
|
|
8986
|
+
levels: previewLevels,
|
|
8987
|
+
reservedMax,
|
|
8988
|
+
range,
|
|
8989
|
+
};
|
|
8990
|
+
}
|
|
8991
|
+
/** The code that stands for the top of a preview's range: 255, or 235 in TV range. */
|
|
8992
|
+
function depthPreviewTopCode(levels = "full") {
|
|
8993
|
+
return PREVIEW_TOP_CODES[levels];
|
|
8994
|
+
}
|
|
8995
|
+
function readFramePattern(value, path) {
|
|
8996
|
+
const pattern = readFileName(value, path);
|
|
8997
|
+
if (!new RegExp(FRAME_INDEX_TOKEN.source).test(pattern)) {
|
|
8998
|
+
fail(`${path} must contain {index} or a padded {index:06}`);
|
|
8999
|
+
}
|
|
9000
|
+
return pattern;
|
|
9001
|
+
}
|
|
9002
|
+
function readOptionalRange(value, path) {
|
|
9003
|
+
if (value === undefined || value === null) {
|
|
9004
|
+
return undefined;
|
|
9005
|
+
}
|
|
9006
|
+
if (!Array.isArray(value) ||
|
|
9007
|
+
value.length !== 2 ||
|
|
9008
|
+
!value.every((bound) => typeof bound === "number" && Number.isFinite(bound))) {
|
|
9009
|
+
fail(`${path} must be two finite numbers [low, high]`);
|
|
9010
|
+
}
|
|
9011
|
+
const [min, max] = value;
|
|
9012
|
+
if (!(min < max)) {
|
|
9013
|
+
fail(`${path} must have low < high`);
|
|
9014
|
+
}
|
|
9015
|
+
return { min, max };
|
|
9016
|
+
}
|
|
9017
|
+
function readObject(value, path) {
|
|
9018
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) {
|
|
9019
|
+
fail(`${path} must be an object`);
|
|
9020
|
+
}
|
|
9021
|
+
return value;
|
|
9022
|
+
}
|
|
9023
|
+
function readPositiveInteger(value, path) {
|
|
9024
|
+
if (!isPositiveInteger(value)) {
|
|
9025
|
+
fail(`${path} must be a positive integer`);
|
|
9026
|
+
}
|
|
9027
|
+
return value;
|
|
9028
|
+
}
|
|
9029
|
+
function readPositiveNumber(value, path) {
|
|
9030
|
+
if (!isPositiveNumber(value)) {
|
|
9031
|
+
fail(`${path} must be a positive number`);
|
|
9032
|
+
}
|
|
9033
|
+
return value;
|
|
9034
|
+
}
|
|
9035
|
+
function readOptionalFinite(value, path) {
|
|
9036
|
+
if (value === undefined || value === null) {
|
|
9037
|
+
return undefined;
|
|
9038
|
+
}
|
|
9039
|
+
if (typeof value !== "number" || !Number.isFinite(value)) {
|
|
9040
|
+
fail(`${path} must be a finite number`);
|
|
9041
|
+
}
|
|
9042
|
+
return value;
|
|
9043
|
+
}
|
|
9044
|
+
function readOptionalString(value, path) {
|
|
9045
|
+
if (value === undefined || value === null) {
|
|
9046
|
+
return undefined;
|
|
9047
|
+
}
|
|
9048
|
+
if (typeof value !== "string") {
|
|
9049
|
+
fail(`${path} must be a string`);
|
|
9050
|
+
}
|
|
9051
|
+
return value;
|
|
9052
|
+
}
|
|
9053
|
+
function readFileName(value, path) {
|
|
9054
|
+
if (typeof value !== "string" || value.length === 0) {
|
|
9055
|
+
fail(`${path} must be a non-empty file name`);
|
|
9056
|
+
}
|
|
9057
|
+
return value;
|
|
9058
|
+
}
|
|
9059
|
+
function readOptionalFileName(value, path) {
|
|
9060
|
+
return value === undefined || value === null
|
|
9061
|
+
? undefined
|
|
9062
|
+
: readFileName(value, path);
|
|
9063
|
+
}
|
|
9064
|
+
function isPositiveInteger(value) {
|
|
9065
|
+
return typeof value === "number" && Number.isInteger(value) && value > 0;
|
|
9066
|
+
}
|
|
9067
|
+
function isPositiveNumber(value) {
|
|
9068
|
+
return typeof value === "number" && Number.isFinite(value) && value > 0;
|
|
9069
|
+
}
|
|
9070
|
+
/** At least two valid codes must remain, or the preview span is zero. */
|
|
9071
|
+
function isReservedPreviewCode(value, levels) {
|
|
9072
|
+
return (typeof value === "number" &&
|
|
9073
|
+
Number.isInteger(value) &&
|
|
9074
|
+
value >= PREVIEW_MIN_RESERVED_CODES[levels] &&
|
|
9075
|
+
value <= PREVIEW_TOP_CODES[levels] - 2);
|
|
9076
|
+
}
|
|
9077
|
+
function describeReservedCodes(levels) {
|
|
9078
|
+
return `an integer from ${PREVIEW_MIN_RESERVED_CODES[levels]} to ${PREVIEW_TOP_CODES[levels] - 2} at ${levels} levels`;
|
|
9079
|
+
}
|
|
9080
|
+
function isRange(range) {
|
|
9081
|
+
return (range !== undefined &&
|
|
9082
|
+
Number.isFinite(range.min) &&
|
|
9083
|
+
Number.isFinite(range.max) &&
|
|
9084
|
+
range.min < range.max);
|
|
9085
|
+
}
|
|
9086
|
+
function isCamera(camera) {
|
|
9087
|
+
return (isPositiveNumber(camera.fxPx) &&
|
|
9088
|
+
isPositiveNumber(camera.baselineM) &&
|
|
9089
|
+
[camera.doffsPx, camera.cxPx, camera.cyPx].every((value) => value === undefined || Number.isFinite(value)));
|
|
9090
|
+
}
|
|
9091
|
+
function fail(message) {
|
|
9092
|
+
throw new RangeError(`depth.json: ${message}`);
|
|
9093
|
+
}
|
|
9094
|
+
|
|
9095
|
+
/** Percentiles `"auto"` colours between: outliers and flying pixels fall outside. */
|
|
9096
|
+
const DEFAULT_DEPTH_PERCENTILE_LOW = 0.02;
|
|
9097
|
+
const DEFAULT_DEPTH_PERCENTILE_HIGH = 0.98;
|
|
9098
|
+
/** Below this share of valid samples a percentile says nothing about the scene. */
|
|
9099
|
+
const MIN_VALID_SAMPLE_SHARE = 0.01;
|
|
9100
|
+
const PERCENTILE_SAMPLE_STRIDE = 2;
|
|
9101
|
+
const FALLBACK_RANGE = { min: 0, max: 1 };
|
|
9102
|
+
/**
|
|
9103
|
+
* Metric depth needs a camera to come out of disparity, and relative inverse
|
|
9104
|
+
* depth has no metric depth at all, so both fall back to disparity.
|
|
9105
|
+
*/
|
|
9106
|
+
function resolveDepthQuantity(kind, quantity = DepthQuantity.Disparity, camera) {
|
|
9107
|
+
if (quantity === DepthQuantity.Disparity) {
|
|
9108
|
+
return { fellBack: false, quantity };
|
|
9109
|
+
}
|
|
9110
|
+
if (kind === DepthMapKind.DepthM ||
|
|
9111
|
+
(kind === DepthMapKind.DisparityPx && camera !== undefined)) {
|
|
9112
|
+
return { fellBack: false, quantity: DepthQuantity.Depth };
|
|
9113
|
+
}
|
|
9114
|
+
return { fellBack: true, quantity: DepthQuantity.Disparity };
|
|
9115
|
+
}
|
|
9116
|
+
function resolveDepthValueConversion(map, quantity) {
|
|
9117
|
+
const resolved = resolveDepthQuantity(map.kind, quantity, map.camera);
|
|
9118
|
+
const nearIsLow = resolved.quantity === DepthQuantity.Depth;
|
|
9119
|
+
const identity = {
|
|
9120
|
+
...resolved,
|
|
9121
|
+
innerOffset: 0,
|
|
9122
|
+
nearIsLow,
|
|
9123
|
+
numerator: 1,
|
|
9124
|
+
outerOffset: 0,
|
|
9125
|
+
reciprocal: false,
|
|
9126
|
+
};
|
|
9127
|
+
const camera = map.camera;
|
|
9128
|
+
const focalBaseline = camera ? camera.fxPx * camera.baselineM : 1;
|
|
9129
|
+
const doffs = camera?.doffsPx ?? 0;
|
|
9130
|
+
if (map.kind === DepthMapKind.DisparityPx &&
|
|
9131
|
+
resolved.quantity === DepthQuantity.Depth) {
|
|
9132
|
+
// Z = fx * B / (d + doffs)
|
|
9133
|
+
return {
|
|
9134
|
+
...identity,
|
|
9135
|
+
innerOffset: doffs,
|
|
9136
|
+
numerator: focalBaseline,
|
|
9137
|
+
reciprocal: true,
|
|
9138
|
+
};
|
|
9139
|
+
}
|
|
9140
|
+
if (map.kind === DepthMapKind.DepthM &&
|
|
9141
|
+
resolved.quantity === DepthQuantity.Disparity) {
|
|
9142
|
+
// d = fx * B / Z - doffs, or plain inverse depth without a camera.
|
|
9143
|
+
return {
|
|
9144
|
+
...identity,
|
|
9145
|
+
numerator: focalBaseline,
|
|
9146
|
+
outerOffset: doffs === 0 ? 0 : -doffs,
|
|
9147
|
+
reciprocal: true,
|
|
9148
|
+
};
|
|
9149
|
+
}
|
|
9150
|
+
return identity;
|
|
9151
|
+
}
|
|
9152
|
+
function convertDepthValue(value, conversion) {
|
|
9153
|
+
return conversion.reciprocal
|
|
9154
|
+
? conversion.numerator / (value + conversion.innerOffset) +
|
|
9155
|
+
conversion.outerOffset
|
|
9156
|
+
: value;
|
|
9157
|
+
}
|
|
9158
|
+
/**
|
|
9159
|
+
* Converts a range in the map kind's unit, swapping its bounds under a
|
|
9160
|
+
* reciprocal. Null when a bound has no image, such as disparity at or below
|
|
9161
|
+
* `-doffsPx` for depth, which would be infinite or negative.
|
|
9162
|
+
*/
|
|
9163
|
+
function convertDepthRange(range, conversion) {
|
|
9164
|
+
if (!hasConvertedValue(Math.min(range.min, range.max), conversion)) {
|
|
9165
|
+
return null;
|
|
9166
|
+
}
|
|
9167
|
+
const first = convertDepthValue(range.min, conversion);
|
|
9168
|
+
const second = convertDepthValue(range.max, conversion);
|
|
9169
|
+
const min = Math.min(first, second);
|
|
9170
|
+
const max = Math.max(first, second);
|
|
9171
|
+
return Number.isFinite(min) && Number.isFinite(max) && min < max
|
|
9172
|
+
? { max, min }
|
|
9173
|
+
: null;
|
|
9174
|
+
}
|
|
9175
|
+
/** The value in the map kind's unit, or null where there is no depth. */
|
|
9176
|
+
function decodeDepthSample(stored, samples) {
|
|
9177
|
+
if (samples.encoding === "scaled16") {
|
|
9178
|
+
return stored === 0 ? null : stored / samples.scale;
|
|
9179
|
+
}
|
|
9180
|
+
if (stored <= samples.reservedMax) {
|
|
9181
|
+
return null;
|
|
9182
|
+
}
|
|
9183
|
+
const span = depthPreviewSpan(samples);
|
|
9184
|
+
return (samples.range.min +
|
|
9185
|
+
(Math.min(stored - samples.reservedMax - 1, span) / span) *
|
|
9186
|
+
(samples.range.max - samples.range.min));
|
|
9187
|
+
}
|
|
9188
|
+
/**
|
|
9189
|
+
* Steps between the lowest valid preview code and the top one, so one step
|
|
9190
|
+
* is `(range.max - range.min) / span`.
|
|
9191
|
+
*/
|
|
9192
|
+
function depthPreviewSpan(samples) {
|
|
9193
|
+
return depthPreviewTopCode(samples.levels) - samples.reservedMax - 1;
|
|
9194
|
+
}
|
|
9195
|
+
/**
|
|
9196
|
+
* This map's own percentile range in the coloured quantity's unit, or null
|
|
9197
|
+
* when fewer than 1 % of the samples it reads hold depth.
|
|
9198
|
+
*
|
|
9199
|
+
* It depends on this map alone, so a frame always gets the same range. Pass
|
|
9200
|
+
* the result back as an explicit range to keep colours steady across frames.
|
|
9201
|
+
*/
|
|
9202
|
+
function computeDepthPercentileRange(map, options = {}) {
|
|
9203
|
+
const low = options.low ?? DEFAULT_DEPTH_PERCENTILE_LOW;
|
|
9204
|
+
const high = options.high ?? DEFAULT_DEPTH_PERCENTILE_HIGH;
|
|
9205
|
+
if (!Number.isFinite(low) ||
|
|
9206
|
+
!Number.isFinite(high) ||
|
|
9207
|
+
low < 0 ||
|
|
9208
|
+
high > 1 ||
|
|
9209
|
+
low >= high) {
|
|
9210
|
+
throw new RangeError("Depth percentiles need 0 <= low < high <= 1, as fractions.");
|
|
9211
|
+
}
|
|
9212
|
+
const conversion = resolveDepthValueConversion(map, options.quantity);
|
|
9213
|
+
const histogram = histogramDepthCodes(map, conversion);
|
|
9214
|
+
if (histogram.valid < histogram.sampled * MIN_VALID_SAMPLE_SHARE) {
|
|
9215
|
+
return null;
|
|
9216
|
+
}
|
|
9217
|
+
return rangeFromHistogram(map, histogram, low, high, conversion);
|
|
9218
|
+
}
|
|
9219
|
+
/**
|
|
9220
|
+
* The range a renderer colours between, in the coloured quantity's unit.
|
|
9221
|
+
*
|
|
9222
|
+
* `autoRange` lets a caller that already computed this map's percentile range
|
|
9223
|
+
* skip the histogram. A range that cannot be honoured falls back step by step:
|
|
9224
|
+
* the display range to the percentile range, and that to the full range of
|
|
9225
|
+
* the valid samples, so a sparse frame still colours what it has.
|
|
9226
|
+
*/
|
|
9227
|
+
function resolveDepthDisplayRange(map, options, conversion, autoRange) {
|
|
9228
|
+
const range = options.range ?? "clip";
|
|
9229
|
+
if (typeof range === "object") {
|
|
9230
|
+
return range;
|
|
9231
|
+
}
|
|
9232
|
+
if (range === "clip" && map.displayRange) {
|
|
9233
|
+
const clip = convertDepthRange(map.displayRange, conversion);
|
|
9234
|
+
if (clip) {
|
|
9235
|
+
return clip;
|
|
9236
|
+
}
|
|
9237
|
+
}
|
|
9238
|
+
const percentile = autoRange !== undefined
|
|
9239
|
+
? autoRange
|
|
9240
|
+
: computeDepthPercentileRange(map, { quantity: conversion.quantity });
|
|
9241
|
+
if (percentile) {
|
|
9242
|
+
return percentile;
|
|
9243
|
+
}
|
|
9244
|
+
const histogram = histogramDepthCodes(map, conversion);
|
|
9245
|
+
return histogram.valid > 0
|
|
9246
|
+
? rangeFromHistogram(map, histogram, 0, 1, conversion)
|
|
9247
|
+
: FALLBACK_RANGE;
|
|
9248
|
+
}
|
|
9249
|
+
/** Resolves a renderer's quantity and range for one map into shader terms. */
|
|
9250
|
+
function resolveDepthColorMapping(map, options = {}, autoRange) {
|
|
9251
|
+
const conversion = resolveDepthValueConversion(map, options.quantity);
|
|
9252
|
+
const range = resolveDepthDisplayRange(map, options, conversion, autoRange);
|
|
9253
|
+
return { ...conversion, hi: range.max, lo: range.min };
|
|
9254
|
+
}
|
|
9255
|
+
function histogramDepthCodes(map, conversion) {
|
|
9256
|
+
const { samples } = map;
|
|
9257
|
+
const values = samples.values;
|
|
9258
|
+
const counts = new Uint32Array(samples.encoding === "scaled16" ? 65_536 : 256);
|
|
9259
|
+
let firstValid = samples.encoding === "scaled16" ? 1 : samples.reservedMax + 1;
|
|
9260
|
+
// Decoded values never fall as codes rise, so the codes the conversion
|
|
9261
|
+
// gives a value for start at one code.
|
|
9262
|
+
while (firstValid < counts.length &&
|
|
9263
|
+
!hasConvertedValue(decodeDepthSample(firstValid, samples), conversion)) {
|
|
9264
|
+
firstValid += 1;
|
|
9265
|
+
}
|
|
9266
|
+
let sampled = 0;
|
|
9267
|
+
let valid = 0;
|
|
9268
|
+
for (let y = 0; y < map.height; y += PERCENTILE_SAMPLE_STRIDE) {
|
|
9269
|
+
const row = y * map.width;
|
|
9270
|
+
for (let x = 0; x < map.width; x += PERCENTILE_SAMPLE_STRIDE) {
|
|
9271
|
+
const code = values[row + x];
|
|
9272
|
+
sampled += 1;
|
|
9273
|
+
if (code >= firstValid) {
|
|
9274
|
+
counts[code] += 1;
|
|
9275
|
+
valid += 1;
|
|
9276
|
+
}
|
|
9277
|
+
}
|
|
9278
|
+
}
|
|
9279
|
+
return { counts, sampled, valid };
|
|
9280
|
+
}
|
|
9281
|
+
/**
|
|
9282
|
+
* Exact percentiles over the stored codes, taken by nearest rank and then
|
|
9283
|
+
* mapped through the monotonic conversion to the coloured quantity. A frame
|
|
9284
|
+
* whose chosen codes coincide, preview codes above the top one counting as
|
|
9285
|
+
* the top one, is widened by one code, so the range stays usable as an
|
|
9286
|
+
* explicit range.
|
|
9287
|
+
*/
|
|
9288
|
+
function rangeFromHistogram(map, histogram, low, high, conversion) {
|
|
9289
|
+
const topCode = map.samples.encoding === "scaled16"
|
|
9290
|
+
? histogram.counts.length - 1
|
|
9291
|
+
: depthPreviewTopCode(map.samples.levels);
|
|
9292
|
+
const lastRank = histogram.valid - 1;
|
|
9293
|
+
let lowCode = Math.min(topCode, codeAtRank(histogram.counts, Math.round(low * lastRank)));
|
|
9294
|
+
let highCode = Math.min(topCode, codeAtRank(histogram.counts, Math.round(high * lastRank)));
|
|
9295
|
+
if (lowCode === highCode) {
|
|
9296
|
+
if (highCode < topCode)
|
|
9297
|
+
highCode += 1;
|
|
9298
|
+
else
|
|
9299
|
+
lowCode -= 1;
|
|
9300
|
+
}
|
|
9301
|
+
const lowValue = decodeDepthSample(lowCode, map.samples) ?? 0;
|
|
9302
|
+
const highValue = decodeDepthSample(highCode, map.samples) ?? 0;
|
|
9303
|
+
return (convertDepthRange({ max: highValue, min: lowValue }, conversion) ??
|
|
9304
|
+
FALLBACK_RANGE);
|
|
9305
|
+
}
|
|
9306
|
+
/**
|
|
9307
|
+
* Whether `value` converts to the coloured quantity: depth from disparity at
|
|
9308
|
+
* or below `-doffsPx` would be infinite or negative, and `readDepthAt` gives
|
|
9309
|
+
* it none.
|
|
9310
|
+
*/
|
|
9311
|
+
function hasConvertedValue(value, conversion) {
|
|
9312
|
+
return !conversion.reciprocal || value + conversion.innerOffset > 0;
|
|
9313
|
+
}
|
|
9314
|
+
function codeAtRank(counts, rank) {
|
|
9315
|
+
let seen = 0;
|
|
9316
|
+
for (let code = 0; code < counts.length; code += 1) {
|
|
9317
|
+
seen += counts[code];
|
|
9318
|
+
if (seen > rank) {
|
|
9319
|
+
return code;
|
|
9320
|
+
}
|
|
9321
|
+
}
|
|
9322
|
+
return counts.length - 1;
|
|
9323
|
+
}
|
|
9324
|
+
|
|
9325
|
+
/**
|
|
9326
|
+
* Reads the depth map pixel under a point.
|
|
9327
|
+
*
|
|
9328
|
+
* `coordinateSpace` is the size `point` is measured in, normally the media's;
|
|
9329
|
+
* the map is stretched over it the way the renderer draws it. Without it the
|
|
9330
|
+
* point is in map pixels. Returns null outside the map.
|
|
9331
|
+
*/
|
|
9332
|
+
function readDepthAt(map, point, coordinateSpace) {
|
|
9333
|
+
const scaleX = coordinateSpace ? map.width / coordinateSpace.width : 1;
|
|
9334
|
+
const scaleY = coordinateSpace ? map.height / coordinateSpace.height : 1;
|
|
9335
|
+
const x = Math.floor(point.x * scaleX);
|
|
9336
|
+
const y = Math.floor(point.y * scaleY);
|
|
9337
|
+
if (!Number.isFinite(x) ||
|
|
9338
|
+
!Number.isFinite(y) ||
|
|
9339
|
+
x < 0 ||
|
|
9340
|
+
y < 0 ||
|
|
9341
|
+
x >= map.width ||
|
|
9342
|
+
y >= map.height) {
|
|
9343
|
+
return null;
|
|
9344
|
+
}
|
|
9345
|
+
const index = y * map.width + x;
|
|
9346
|
+
const { samples } = map;
|
|
9347
|
+
const stored = samples.values[index];
|
|
9348
|
+
const value = decodeDepthSample(stored, samples);
|
|
9349
|
+
const step = samples.encoding === "scaled16"
|
|
9350
|
+
? 1 / samples.scale
|
|
9351
|
+
: (samples.range.max - samples.range.min) / depthPreviewSpan(samples);
|
|
9352
|
+
const confidence = map.confidence?.[index];
|
|
9353
|
+
const readout = {
|
|
9354
|
+
precision: samples.encoding === "scaled16" ? "exact" : "preview",
|
|
9355
|
+
step,
|
|
9356
|
+
stored,
|
|
9357
|
+
valid: value !== null,
|
|
9358
|
+
x,
|
|
9359
|
+
y,
|
|
9360
|
+
...(confidence === undefined ? {} : { confidence: confidence / 255 }),
|
|
9361
|
+
};
|
|
9362
|
+
if (value === null) {
|
|
9363
|
+
return readout;
|
|
9364
|
+
}
|
|
9365
|
+
const camera = map.camera;
|
|
9366
|
+
const focalBaseline = camera ? camera.fxPx * camera.baselineM : undefined;
|
|
9367
|
+
const doffs = camera?.doffsPx ?? 0;
|
|
9368
|
+
switch (map.kind) {
|
|
9369
|
+
case DepthMapKind.DisparityPx: {
|
|
9370
|
+
const shifted = value + doffs;
|
|
9371
|
+
return {
|
|
9372
|
+
...readout,
|
|
9373
|
+
disparityPx: value,
|
|
9374
|
+
...(focalBaseline !== undefined && shifted > 0
|
|
9375
|
+
? { depthM: focalBaseline / shifted }
|
|
9376
|
+
: {}),
|
|
9377
|
+
};
|
|
9378
|
+
}
|
|
9379
|
+
case DepthMapKind.DepthM:
|
|
9380
|
+
return {
|
|
9381
|
+
...readout,
|
|
9382
|
+
depthM: value,
|
|
9383
|
+
...(focalBaseline !== undefined
|
|
9384
|
+
? { disparityPx: focalBaseline / value - doffs }
|
|
9385
|
+
: {}),
|
|
9386
|
+
};
|
|
9387
|
+
default:
|
|
9388
|
+
return { ...readout, relativeInverse: value };
|
|
9389
|
+
}
|
|
9390
|
+
}
|
|
9391
|
+
|
|
8316
9392
|
var MediaRendererFit;
|
|
8317
9393
|
(function (MediaRendererFit) {
|
|
8318
9394
|
/**
|
|
@@ -8474,5 +9550,5 @@ var MediaSessionActivityStatus;
|
|
|
8474
9550
|
MediaSessionActivityStatus["Waiting"] = "waiting";
|
|
8475
9551
|
})(MediaSessionActivityStatus || (MediaSessionActivityStatus = {}));
|
|
8476
9552
|
|
|
8477
|
-
export { AnnotationFrameMutationKind, AnnotationGeometryKind, AnnotationGestureStateKind, AnnotationHandleKind, BaseBoxCornerStyle, BaseBoxStyle, BaseFocusStyle, BaseInteractionStyle, BaseKeypointStyle, BaseLabelStyle, BaseMarkerStyle, BaseMaskStyle, BaseOrientedBoxStyle, BasePercentageBarStyle, BasePolygonStyle, BasePolylineStyle, BoxShape, StrokeAlignment as BoxStrokeAlignment, DEFAULT_DETECTION_CLASS_STYLES, DEFAULT_DETECTION_COLOR_SEQUENCE, DetectionBufferStatus, DetectionFrameRetentionMode, DetectionFrameSelectionMode, DetectionInteractionState, DetectionMaskEncoding, DetectionMaskPayloadFormat, DetectionPickTarget, FocusTargetMode, KeypointMarkerShape, KeypointVisibility, LabelPlacement, LabelVisibilityMode, MAX_ID_MASK_PALETTE_ENTRIES, MAX_ID_MASK_STROKE_WIDTH, MarkerShape, MarkerSizeSpace, MaskRenderMode, MediaErrorKind, MediaInteractionMode, MediaRendererFit, MediaRendererPlaybackState, MediaSessionActivityKind, MediaSessionActivityStatus, MediaSessionMode, MediaSessionStatus, MediaSourceStatus, PercentageBarPlacement, PlaybackGateReach, RegionRendererComposeMode, RegionRendererCoverageKind, RegionRendererMediaEffectKind, RegionRendererRegionKind, RegionRendererSizeSpace, RegionRendererSourceKind, SUPERVISION_ROBOFLOW_COLOR, ShapeInstructionKind, StrokeAlignment, TrackingGeometry, annotationRendererKinds, annotationRenderers, applyAnnotationHandleDrag, canReuseMaskStyleArtifacts, centerRectToTopLeftRect, computeDetectionMaskRect, computeMaskBounds, containsPoint, convertDetectionBoxToMask, convertDetectionBoxToPolygon, convertDetectionMaskToBox, convertDetectionMaskToPolygon, convertDetectionPolygonToBox, convertDetectionPolygonToMask, copySortedDetectionFrames, createAnnotationEditingEngine, createArrayDetectionFrameSource, createBufferedDetectionTimeline, createByteTrackTracker, createCBIoUTracker, createColdDetectionFrameSource, createCompositeDetectionFrameSource, createDefaultAnnotationPresentation, createDetectionPickKey, createEditableAnnotationFrameSession, createIdMaskFrame, createIdleDetectionBufferState, createMemoryColdDetectionFrameStore, createOCSortTracker, createProjectedDetectionFrameSource, createSortTracker, createSourceAwarePresentation, createViewportController, createWritableDetectionFrameSource, decodeCompressedRleCounts, decodeCompressedRleMask, decodeDetectionMaskPayload, deleteAnnotationVertex, detectMaskBorders, detectionFrameOverlapsRange, detectionPostProcessors, distanceToSegment, encodeBinaryMask, encodeBinaryMaskWithBounds, encodeCompressedRleCounts, encodeDetectionMaskPayload, extractDetectionMaskRectRuns, extractMaskContour, extractMaskRectRuns, filterDetectionFramesForRange, findClosestAnnotationSegment, followDetectionPickAcrossFrames, getAnnotationHandles, getBufferedDetectionTimelineFrameSnapshot, getDetectionRect, getPointsRect, haveSameDetectionPickIdentities, includeDefined, isDeflatedBase64DetectionMaskPayload, lightenColor, mediaToScreen, mergeDetectionMasks, mergeDetectionPolygonsByClass, normalizeDetectionClassName, offsetDetection, pickAnnotationHandle, pickDetectionAtPoint, pickDetectionByMaskId, pointInPolygon, polygonArea, polygonToRect, projectDetectionFrame, projectDetectionFrameForTracking, projectDetectionFrames, rasterizePolygonToMask, rasterizeRectToMask, rebaseDetectionPickToFrame, rectArea, rectToPolygon, resolveAnnotationRendererPresentation, resolveAnnotationRendererStyleFields, resolveAnnotationStyleState, resolveContrastTextColor, resolveDetectionClassColorStyle, resolveEllipseSegmentCount, resolveIdMaskPaletteId, resolveIdMaskStrokeTexels, resolveMarkerGeometry, resolveMaskStyleOpacity, resolveStyleValue, sampleEllipseArc, screenToMedia, selectDetectionFrame, topLeftRectToCenterRect, validateDetectionFrames, writeIdMaskPaletteEntry };
|
|
9553
|
+
export { AnnotationFrameMutationKind, AnnotationGeometryKind, AnnotationGestureStateKind, AnnotationHandleKind, BaseBoxCornerStyle, BaseBoxStyle, BaseFocusStyle, BaseInteractionStyle, BaseKeypointStyle, BaseLabelStyle, BaseMarkerStyle, BaseMaskStyle, BaseOrientedBoxStyle, BasePercentageBarStyle, BasePolygonStyle, BasePolylineStyle, BoxShape, StrokeAlignment as BoxStrokeAlignment, DEFAULT_DETECTION_CLASS_STYLES, DEFAULT_DETECTION_COLOR_SEQUENCE, DepthColormap, DepthMapKind, DepthPreviewLevels, DepthQuantity, DepthSampling, DetectionBufferStatus, DetectionFrameRetentionMode, DetectionFrameSelectionMode, DetectionInteractionState, DetectionMaskEncoding, DetectionMaskPayloadFormat, DetectionPickTarget, FocusTargetMode, KeypointMarkerShape, KeypointVisibility, LabelPlacement, LabelVisibilityMode, MAX_ID_MASK_PALETTE_ENTRIES, MAX_ID_MASK_STROKE_WIDTH, MarkerShape, MarkerSizeSpace, MaskRenderMode, MediaErrorKind, MediaInteractionMode, MediaRendererFit, MediaRendererPlaybackState, MediaSessionActivityKind, MediaSessionActivityStatus, MediaSessionMode, MediaSessionStatus, MediaSourceStatus, PLAYHEAD_QUANTIZATION_TOLERANCE_SECONDS, PercentageBarPlacement, PlaybackGateReach, RegionRendererComposeMode, RegionRendererCoverageKind, RegionRendererMediaEffectKind, RegionRendererRegionKind, RegionRendererSizeSpace, RegionRendererSourceKind, SUPERVISION_ROBOFLOW_COLOR, ShapeInstructionKind, StrokeAlignment, TrackingGeometry, annotationRendererKinds, annotationRenderers, applyAnnotationHandleDrag, canReuseMaskStyleArtifacts, centerRectToTopLeftRect, computeDepthPercentileRange, computeDetectionMaskRect, computeMaskBounds, containsPoint, convertDetectionBoxToMask, convertDetectionBoxToPolygon, convertDetectionMaskToBox, convertDetectionMaskToPolygon, convertDetectionPolygonToBox, convertDetectionPolygonToMask, copySortedDetectionFrames, createAnnotationEditingEngine, createArrayDetectionFrameSource, createBufferedDetectionTimeline, createByteTrackTracker, createCBIoUTracker, createColdDetectionFrameSource, createCompositeDetectionFrameSource, createDefaultAnnotationPresentation, createDepthColormapLut, createDetectionPickKey, createEditableAnnotationFrameSession, createIdMaskFrame, createIdleDetectionBufferState, createMemoryColdDetectionFrameStore, createOCSortTracker, createProjectedDetectionFrameSource, createSortTracker, createSourceAwarePresentation, createViewportController, createWritableDetectionFrameSource, decodeCompressedRleCounts, decodeCompressedRleMask, decodeDetectionMaskPayload, deleteAnnotationVertex, depthColormapColors, depthPreviewTopCode, detectMaskBorders, detectionFrameOverlapsRange, detectionPostProcessors, distanceToSegment, encodeBinaryMask, encodeBinaryMaskWithBounds, encodeCompressedRleCounts, encodeDetectionMaskPayload, extractDetectionMaskRectRuns, extractMaskContour, extractMaskRectRuns, filterDetectionFramesForRange, findClosestAnnotationSegment, followDetectionPickAcrossFrames, getAnnotationHandles, getBufferedDetectionTimelineFrameSnapshot, getDetectionRect, getPointsRect, haveSameDetectionPickIdentities, includeDefined, isDeflatedBase64DetectionMaskPayload, isDepthColormap, lightenColor, mediaToScreen, mergeDetectionMasks, mergeDetectionPolygonsByClass, normalizeDetectionClassName, offsetDetection, parseDepthManifest, pickAnnotationHandle, pickDetectionAtPoint, pickDetectionByMaskId, pointInPolygon, polygonArea, polygonToRect, projectDetectionFrame, projectDetectionFrameForTracking, projectDetectionFrames, rasterizePolygonToMask, rasterizeRectToMask, readDepthAt, rebaseDetectionPickToFrame, rectArea, rectToPolygon, resolveAnnotationRendererPresentation, resolveAnnotationRendererStyleFields, resolveAnnotationStyleState, resolveContrastTextColor, resolveDepthColorMapping, resolveDepthFrameFile, resolveDepthQuantity, resolveDetectionClassColorStyle, resolveEllipseSegmentCount, resolveIdMaskPaletteId, resolveIdMaskStrokeTexels, resolveMarkerGeometry, resolveMaskStyleOpacity, resolveStyleValue, sampleEllipseArc, screenToMedia, selectDetectionFrame, sortedDetectionFrames, topLeftRectToCenterRect, validateDepthMap, validateDetectionFrames, writeIdMaskPaletteEntry };
|
|
8478
9554
|
//# sourceMappingURL=index.js.map
|