supervision 0.1.3 → 0.1.5

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.
Files changed (121) hide show
  1. package/dist/index.d.ts +16 -4
  2. package/dist/index.d.ts.map +1 -1
  3. package/dist/index.js +1587 -177
  4. package/dist/index.js.map +1 -1
  5. package/dist/mask-preparation.worker.js +110 -68
  6. package/dist/mask-preparation.worker.js.map +1 -1
  7. package/dist/post-processing/default-tracking-worker.d.ts +3 -0
  8. package/dist/post-processing/default-tracking-worker.d.ts.map +1 -0
  9. package/dist/post-processing/detection-post-processing-pipeline.d.ts +7 -0
  10. package/dist/post-processing/detection-post-processing-pipeline.d.ts.map +1 -0
  11. package/dist/post-processing/embedded-tracking-worker.d.ts +2 -0
  12. package/dist/post-processing/embedded-tracking-worker.d.ts.map +1 -0
  13. package/dist/post-processing/tracking-worker-protocol.d.ts +29 -0
  14. package/dist/post-processing/tracking-worker-protocol.d.ts.map +1 -0
  15. package/dist/post-processing/tracking.worker.d.ts +2 -0
  16. package/dist/post-processing/tracking.worker.d.ts.map +1 -0
  17. package/dist/render-preparation/mask-frame-artifact.d.ts +6 -0
  18. package/dist/render-preparation/mask-frame-artifact.d.ts.map +1 -1
  19. package/dist/render-preparation/mask-frame-compositor.d.ts +1 -0
  20. package/dist/render-preparation/mask-frame-compositor.d.ts.map +1 -1
  21. package/dist/render-preparation/mask-frame-preparer.d.ts.map +1 -1
  22. package/dist/render-preparation/mask-preparation-worker-protocol.d.ts +1 -0
  23. package/dist/render-preparation/mask-preparation-worker-protocol.d.ts.map +1 -1
  24. package/dist/renderers/annotation-shape-styles.d.ts +17 -0
  25. package/dist/renderers/annotation-shape-styles.d.ts.map +1 -0
  26. package/dist/renderers/media-renderer-core.d.ts.map +1 -1
  27. package/dist/renderers/media-renderer-scene.d.ts +17 -2
  28. package/dist/renderers/media-renderer-scene.d.ts.map +1 -1
  29. package/dist/renderers/pixi-annotation-overlay-layer.d.ts.map +1 -1
  30. package/dist/renderers/pixi-focus-layer.d.ts +1 -0
  31. package/dist/renderers/pixi-focus-layer.d.ts.map +1 -1
  32. package/dist/renderers/pixi-interaction-layer.d.ts +2 -0
  33. package/dist/renderers/pixi-interaction-layer.d.ts.map +1 -1
  34. package/dist/renderers/pixi-interaction-presentation-layer.d.ts +2 -1
  35. package/dist/renderers/pixi-interaction-presentation-layer.d.ts.map +1 -1
  36. package/dist/renderers/pixi-mask-halo.d.ts +94 -0
  37. package/dist/renderers/pixi-mask-halo.d.ts.map +1 -0
  38. package/dist/renderers/pixi-mask-layer.d.ts +10 -1
  39. package/dist/renderers/pixi-mask-layer.d.ts.map +1 -1
  40. package/dist/renderers/pixi-media-scene.d.ts.map +1 -1
  41. package/dist/renderers/pixi-path.d.ts +13 -7
  42. package/dist/renderers/pixi-path.d.ts.map +1 -1
  43. package/dist/renderers/pixi-polygon-layer.d.ts.map +1 -1
  44. package/dist/renderers/pixi-region-layer.d.ts +42 -0
  45. package/dist/renderers/pixi-region-layer.d.ts.map +1 -0
  46. package/dist/renderers/pixi-scene-layer-slot.d.ts +1 -0
  47. package/dist/renderers/pixi-scene-layer-slot.d.ts.map +1 -1
  48. package/dist/renderers/pixi-vector-layer.d.ts +3 -1
  49. package/dist/renderers/pixi-vector-layer.d.ts.map +1 -1
  50. package/dist/sessions/media-session.d.ts.map +1 -1
  51. package/dist/tracking.worker.js +1485 -0
  52. package/dist/tracking.worker.js.map +1 -0
  53. package/dist/types/detection-post-processing.d.ts +62 -0
  54. package/dist/types/detection-post-processing.d.ts.map +1 -0
  55. package/dist/types/media-renderer.d.ts +1 -1
  56. package/dist/types/media-renderer.d.ts.map +1 -1
  57. package/dist/workers/worker-rpc-client.d.ts +2 -1
  58. package/dist/workers/worker-rpc-client.d.ts.map +1 -1
  59. package/node_modules/supervision-js-core/dist/index.d.ts +23 -3
  60. package/node_modules/supervision-js-core/dist/index.d.ts.map +1 -1
  61. package/node_modules/supervision-js-core/dist/index.js +2545 -566
  62. package/node_modules/supervision-js-core/dist/index.js.map +1 -1
  63. package/node_modules/supervision-js-core/dist/interactions/detection-picker.d.ts +18 -0
  64. package/node_modules/supervision-js-core/dist/interactions/detection-picker.d.ts.map +1 -1
  65. package/node_modules/supervision-js-core/dist/post-processing/byte-track-tracker.d.ts +4 -0
  66. package/node_modules/supervision-js-core/dist/post-processing/byte-track-tracker.d.ts.map +1 -0
  67. package/node_modules/supervision-js-core/dist/post-processing/cbiou-tracker.d.ts +4 -0
  68. package/node_modules/supervision-js-core/dist/post-processing/cbiou-tracker.d.ts.map +1 -0
  69. package/node_modules/supervision-js-core/dist/post-processing/oc-sort-tracker.d.ts +4 -0
  70. package/node_modules/supervision-js-core/dist/post-processing/oc-sort-tracker.d.ts.map +1 -0
  71. package/node_modules/supervision-js-core/dist/post-processing/sort-tracker.d.ts +4 -0
  72. package/node_modules/supervision-js-core/dist/post-processing/sort-tracker.d.ts.map +1 -0
  73. package/node_modules/supervision-js-core/dist/post-processing/tracking.d.ts +9 -0
  74. package/node_modules/supervision-js-core/dist/post-processing/tracking.d.ts.map +1 -0
  75. package/node_modules/supervision-js-core/dist/styles/annotation-renderer-presentation.d.ts.map +1 -1
  76. package/node_modules/supervision-js-core/dist/styles/annotation-renderer-registry.d.ts +13 -6
  77. package/node_modules/supervision-js-core/dist/styles/annotation-renderer-registry.d.ts.map +1 -1
  78. package/node_modules/supervision-js-core/dist/styles/box-corner-style.d.ts +14 -0
  79. package/node_modules/supervision-js-core/dist/styles/box-corner-style.d.ts.map +1 -0
  80. package/node_modules/supervision-js-core/dist/styles/box-style.d.ts.map +1 -1
  81. package/node_modules/supervision-js-core/dist/styles/default-annotation-presentation.d.ts +19 -0
  82. package/node_modules/supervision-js-core/dist/styles/default-annotation-presentation.d.ts.map +1 -1
  83. package/node_modules/supervision-js-core/dist/styles/interaction-style.d.ts.map +1 -1
  84. package/node_modules/supervision-js-core/dist/styles/keypoint-style.d.ts.map +1 -1
  85. package/node_modules/supervision-js-core/dist/styles/marker-style.d.ts +14 -0
  86. package/node_modules/supervision-js-core/dist/styles/marker-style.d.ts.map +1 -0
  87. package/node_modules/supervision-js-core/dist/styles/polygon-style.d.ts.map +1 -1
  88. package/node_modules/supervision-js-core/dist/styles/polyline-style.d.ts.map +1 -1
  89. package/node_modules/supervision-js-core/dist/styles/source-presentation.d.ts +8 -0
  90. package/node_modules/supervision-js-core/dist/styles/source-presentation.d.ts.map +1 -1
  91. package/node_modules/supervision-js-core/dist/styles/stroke-style.d.ts +12 -0
  92. package/node_modules/supervision-js-core/dist/styles/stroke-style.d.ts.map +1 -0
  93. package/node_modules/supervision-js-core/dist/types/annotation-renderer.d.ts +124 -4
  94. package/node_modules/supervision-js-core/dist/types/annotation-renderer.d.ts.map +1 -1
  95. package/node_modules/supervision-js-core/dist/types/box-corner-style.d.ts +31 -0
  96. package/node_modules/supervision-js-core/dist/types/box-corner-style.d.ts.map +1 -0
  97. package/node_modules/supervision-js-core/dist/types/box-style.d.ts +4 -17
  98. package/node_modules/supervision-js-core/dist/types/box-style.d.ts.map +1 -1
  99. package/node_modules/supervision-js-core/dist/types/detections.d.ts +11 -3
  100. package/node_modules/supervision-js-core/dist/types/detections.d.ts.map +1 -1
  101. package/node_modules/supervision-js-core/dist/types/ellipse-style.d.ts +30 -0
  102. package/node_modules/supervision-js-core/dist/types/ellipse-style.d.ts.map +1 -0
  103. package/node_modules/supervision-js-core/dist/types/interaction.d.ts +12 -0
  104. package/node_modules/supervision-js-core/dist/types/interaction.d.ts.map +1 -1
  105. package/node_modules/supervision-js-core/dist/types/marker-style.d.ts +46 -0
  106. package/node_modules/supervision-js-core/dist/types/marker-style.d.ts.map +1 -0
  107. package/node_modules/supervision-js-core/dist/types/mask-halo-style.d.ts +29 -0
  108. package/node_modules/supervision-js-core/dist/types/mask-halo-style.d.ts.map +1 -0
  109. package/node_modules/supervision-js-core/dist/types/media-rendering.d.ts +18 -2
  110. package/node_modules/supervision-js-core/dist/types/media-rendering.d.ts.map +1 -1
  111. package/node_modules/supervision-js-core/dist/types/paint-style.d.ts +36 -0
  112. package/node_modules/supervision-js-core/dist/types/paint-style.d.ts.map +1 -0
  113. package/node_modules/supervision-js-core/dist/types/post-processing.d.ts +148 -0
  114. package/node_modules/supervision-js-core/dist/types/post-processing.d.ts.map +1 -0
  115. package/node_modules/supervision-js-core/dist/types/shape-style.d.ts +112 -0
  116. package/node_modules/supervision-js-core/dist/types/shape-style.d.ts.map +1 -0
  117. package/node_modules/supervision-js-core/dist/utils/detection-frames.d.ts.map +1 -1
  118. package/node_modules/supervision-js-core/dist/utils/shape-geometry.d.ts +50 -0
  119. package/node_modules/supervision-js-core/dist/utils/shape-geometry.d.ts.map +1 -0
  120. package/node_modules/supervision-js-core/package.json +12 -5
  121. package/package.json +7 -2
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
- import { copySortedDetectionFrames, detectionFrameOverlapsRange, includeDefined, MediaSourceStatus, MediaRendererPlaybackState, MediaRendererFit, createDefaultAnnotationPresentation, createArrayDetectionFrameSource, createBufferedDetectionTimeline, resolveAnnotationRendererPresentation, createIdleDetectionBufferState, MediaInteractionMode, BoxStrokeAlignment, BaseBoxStyle, centerRectToTopLeftRect, BoxShape, BaseFocusStyle, decodeCompressedRleMask, extractMaskRectRuns, AnnotationGestureStateKind, AnnotationHandleKind, getDetectionRect, DetectionPickTarget, createDetectionPickKey, rebaseDetectionPickToFrame, pickDetectionAtPoint, pickAnnotationHandle, getAnnotationHandles, createIdMaskFrame, encodeBinaryMask, rasterizePolygonToMask, LabelPlacement, BasePolygonStyle, BasePolylineStyle, BaseKeypointStyle, KeypointMarkerShape, BaseInteractionStyle, DetectionInteractionState, MAX_ID_MASK_PALETTE_ENTRIES, DetectionBufferStatus, canReuseMaskStyleArtifacts, getBufferedDetectionTimelineFrameSnapshot, MAX_ID_MASK_STROKE_WIDTH, resolveMaskStyleOpacity, pickDetectionByMaskId, resolveStyleValue, lightenColor, createViewportController, resolveAnnotationStyleState, MediaSessionMode, DetectionFrameRetentionMode, createMemoryColdDetectionFrameStore, createWritableDetectionFrameSource, createCompositeDetectionFrameSource, MediaSessionActivityStatus, MediaSessionActivityKind, MediaSessionStatus, createSourceAwarePresentation } from 'supervision-js-core';
2
- export { BaseBoxStyle, BaseFocusStyle, BaseInteractionStyle, BaseKeypointStyle, BaseLabelStyle, BaseMaskStyle, BasePolygonStyle, BasePolylineStyle, BoxShape, BoxStrokeAlignment, DEFAULT_DETECTION_CLASS_STYLES, DEFAULT_DETECTION_COLOR_SEQUENCE, DetectionBufferStatus, DetectionFrameRetentionMode, DetectionFrameSelectionMode, DetectionInteractionState, DetectionMaskEncoding, DetectionPickTarget, FocusTargetMode, KeypointMarkerShape, KeypointVisibility, LabelPlacement, LabelVisibilityMode, MaskRenderMode, MediaInteractionMode, MediaRendererFit, MediaRendererPlaybackState, MediaSessionActivityKind, MediaSessionActivityStatus, MediaSessionMode, MediaSessionStatus, MediaSourceStatus, SUPERVISION_ROBOFLOW_COLOR, annotationRendererKinds, annotationRenderers, createArrayDetectionFrameSource, createBufferedDetectionTimeline, createColdDetectionFrameSource, createCompositeDetectionFrameSource, createDefaultAnnotationPresentation, createMemoryColdDetectionFrameStore, createWritableDetectionFrameSource, normalizeDetectionClassName, pickDetectionAtPoint, resolveDetectionClassColorStyle } from 'supervision-js-core';
1
+ import { copySortedDetectionFrames, detectionFrameOverlapsRange, projectDetectionFrameForTracking, createSortTracker, createOCSortTracker, createCBIoUTracker, createByteTrackTracker, includeDefined, MediaSourceStatus, MediaRendererPlaybackState, MediaRendererFit, createDefaultAnnotationPresentation, createArrayDetectionFrameSource, createBufferedDetectionTimeline, resolveAnnotationRendererPresentation, createIdleDetectionBufferState, MediaInteractionMode, StrokeAlignment, BaseBoxStyle, centerRectToTopLeftRect, BoxShape, BaseFocusStyle, decodeCompressedRleMask, extractMaskRectRuns, AnnotationGestureStateKind, AnnotationHandleKind, getDetectionRect, DetectionPickTarget, createDetectionPickKey, rebaseDetectionPickToFrame, pickDetectionAtPoint, followDetectionPickAcrossFrames, haveSameDetectionPickIdentities, pickAnnotationHandle, getAnnotationHandles, createIdMaskFrame, encodeBinaryMask, rasterizePolygonToMask, LabelPlacement, BasePolygonStyle, BasePolylineStyle, BaseKeypointStyle, KeypointMarkerShape, MarkerSizeSpace, MarkerShape, ShapeInstructionKind, sampleEllipseArc, resolveEllipseSegmentCount, resolveMarkerGeometry, BaseInteractionStyle, DetectionInteractionState, MAX_ID_MASK_PALETTE_ENTRIES, DetectionBufferStatus, canReuseMaskStyleArtifacts, getBufferedDetectionTimelineFrameSnapshot, MAX_ID_MASK_STROKE_WIDTH, resolveMaskStyleOpacity, pickDetectionByMaskId, BoxStrokeAlignment, resolveStyleValue, resolveAnnotationStyleState, lightenColor, createViewportController, MediaSessionMode, DetectionFrameRetentionMode, createMemoryColdDetectionFrameStore, createWritableDetectionFrameSource, createCompositeDetectionFrameSource, MediaSessionActivityStatus, MediaSessionActivityKind, MediaSessionStatus, createSourceAwarePresentation } from 'supervision-js-core';
2
+ export { BaseBoxCornerStyle, BaseBoxStyle, BaseFocusStyle, BaseInteractionStyle, BaseKeypointStyle, BaseLabelStyle, BaseMarkerStyle, BaseMaskStyle, BasePolygonStyle, BasePolylineStyle, BoxShape, BoxStrokeAlignment, DEFAULT_DETECTION_CLASS_STYLES, DEFAULT_DETECTION_COLOR_SEQUENCE, DetectionBufferStatus, DetectionFrameRetentionMode, DetectionFrameSelectionMode, DetectionInteractionState, DetectionMaskEncoding, DetectionPickTarget, FocusTargetMode, KeypointMarkerShape, KeypointVisibility, LabelPlacement, LabelVisibilityMode, MarkerShape, MarkerSizeSpace, MaskRenderMode, MediaInteractionMode, MediaRendererFit, MediaRendererPlaybackState, MediaSessionActivityKind, MediaSessionActivityStatus, MediaSessionMode, MediaSessionStatus, MediaSourceStatus, RegionRendererComposeMode, RegionRendererRegionKind, RegionRendererSourceKind, SUPERVISION_ROBOFLOW_COLOR, TrackingGeometry, annotationRendererKinds, annotationRenderers, createArrayDetectionFrameSource, createBufferedDetectionTimeline, createByteTrackTracker, createCBIoUTracker, createColdDetectionFrameSource, createCompositeDetectionFrameSource, createDefaultAnnotationPresentation, createMemoryColdDetectionFrameStore, createOCSortTracker, createSortTracker, createWritableDetectionFrameSource, detectionPostProcessors, normalizeDetectionClassName, pickDetectionAtPoint, projectDetectionFrameForTracking, resolveDetectionClassColorStyle } from 'supervision-js-core';
3
3
 
4
4
  const DEFAULT_DATABASE_NAME = "supervision-js-detection-frames";
5
5
  const DEFAULT_CHUNK_DURATION_SECONDS = 1;
@@ -458,6 +458,434 @@ function getDetectionFrameDedupeKey(frame) {
458
458
  : `index:${frame.frameIndex}`;
459
459
  }
460
460
 
461
+ const EMBEDDED_TRACKING_WORKER_SOURCE = "(function () {\n 'use strict';\n\n /** COCO-compatible keypoint visibility values. */\n var KeypointVisibility;\n (function (KeypointVisibility) {\n KeypointVisibility[KeypointVisibility[\"NotLabeled\"] = 0] = \"NotLabeled\";\n KeypointVisibility[KeypointVisibility[\"Occluded\"] = 1] = \"Occluded\";\n KeypointVisibility[KeypointVisibility[\"Visible\"] = 2] = \"Visible\";\n })(KeypointVisibility || (KeypointVisibility = {}));\n var DetectionMaskEncoding;\n (function (DetectionMaskEncoding) {\n DetectionMaskEncoding[\"CompressedRle\"] = \"compressedRle\";\n })(DetectionMaskEncoding || (DetectionMaskEncoding = {}));\n\n var DetectionBufferStatus;\n (function (DetectionBufferStatus) {\n DetectionBufferStatus[\"Idle\"] = \"idle\";\n DetectionBufferStatus[\"Loading\"] = \"loading\";\n DetectionBufferStatus[\"Ready\"] = \"ready\";\n DetectionBufferStatus[\"Error\"] = \"error\";\n DetectionBufferStatus[\"Destroyed\"] = \"destroyed\";\n })(DetectionBufferStatus || (DetectionBufferStatus = {}));\n /**\n * How the renderer selects the active detection frame for a media timestamp.\n */\n var DetectionFrameSelectionMode;\n (function (DetectionFrameSelectionMode) {\n /**\n * Select the frame whose `[mediaTime, endTime)` interval contains the media\n * time. This is the default for interval annotations and timestamped sources.\n */\n DetectionFrameSelectionMode[\"Interval\"] = \"interval\";\n /**\n * Select from a known inference frame grid using `frameIndex`, `frameRate`,\n * and optional `frameIndexOriginTime`. This is useful when inference was run\n * on normalized frames and playback should snap detections to that grid.\n */\n DetectionFrameSelectionMode[\"NearestFrameIndex\"] = \"nearestFrameIndex\";\n })(DetectionFrameSelectionMode || (DetectionFrameSelectionMode = {}));\n var DetectionFrameRetentionMode;\n (function (DetectionFrameRetentionMode) {\n /**\n * Keep writable detections only in an in-memory store. Useful for ephemeral\n * live streams where old predictions should disappear when evicted.\n */\n DetectionFrameRetentionMode[\"MemoryOnly\"] = \"memoryOnly\";\n /**\n * Persist every written detection frame. Useful for finite media where seek\n * and replay should not require recomputing detections.\n */\n DetectionFrameRetentionMode[\"PersistAll\"] = \"persistAll\";\n /**\n * Persist only the most recent retention window. Useful for long-running\n * streams where replay is bounded to a recent time horizon.\n */\n DetectionFrameRetentionMode[\"PersistWindow\"] = \"persistWindow\";\n })(DetectionFrameRetentionMode || (DetectionFrameRetentionMode = {}));\n\n var AnnotationFrameMutationKind;\n (function (AnnotationFrameMutationKind) {\n AnnotationFrameMutationKind[\"Add\"] = \"add\";\n AnnotationFrameMutationKind[\"Remove\"] = \"remove\";\n AnnotationFrameMutationKind[\"Replace\"] = \"replace\";\n AnnotationFrameMutationKind[\"Transact\"] = \"transact\";\n AnnotationFrameMutationKind[\"Update\"] = \"update\";\n })(AnnotationFrameMutationKind || (AnnotationFrameMutationKind = {}));\n\n /**\n * Creates one stateful SORT tracker for a single ordered media sequence.\n *\n * Defaults, lifecycle semantics, and observation-only output mirror\n * roboflow/trackers SORT. Motion predictions remain internal to association.\n */\n function createSortTracker$1(options = {}) {\n const lostTrackBuffer = normalizeNonNegativeInteger$1(options.lostTrackBuffer ?? 30, \"lostTrackBuffer\");\n const frameRate = options.frameRate ?? 30;\n const trackActivationThreshold = options.trackActivationThreshold ?? 0.25;\n const minimumConsecutiveFrames = normalizePositiveInteger$1(options.minimumConsecutiveFrames ?? 3, \"minimumConsecutiveFrames\");\n const minimumIouThreshold = options.minimumIouThreshold ?? 0.3;\n if (!Number.isFinite(frameRate) || frameRate <= 0) {\n throw new Error(\"frameRate must be a finite positive value.\");\n }\n normalizeUnitInterval$1(trackActivationThreshold, \"trackActivationThreshold\");\n normalizeUnitInterval$1(minimumIouThreshold, \"minimumIouThreshold\");\n const scaledLostTrackBuffer = (frameRate / 30) * lostTrackBuffer;\n if (!Number.isFinite(scaledLostTrackBuffer)) {\n throw new Error(\"Scaled lostTrackBuffer overflows: frameRate / 30 * lostTrackBuffer must be finite.\");\n }\n const maximumFramesWithoutUpdate = lostTrackBuffer === 0 ? 0 : Math.max(1, Math.ceil(scaledLostTrackBuffer));\n let tracks = [];\n let nextTrackerId = 0;\n let previousFrameIndex;\n return {\n reset() {\n tracks = [];\n nextTrackerId = 0;\n previousFrameIndex = undefined;\n },\n update(detections, frameIndex) {\n const frameStep = resolveFrameStep(frameIndex, previousFrameIndex);\n previousFrameIndex = frameIndex ?? previousFrameIndex;\n const predicted = tracks.map((track) => track.predict(frameStep, frameRate));\n const { matches, unmatchedDetections } = associateDetectionsToTracks(detections, predicted, minimumIouThreshold);\n const trackerIds = new Map();\n for (const match of matches) {\n const track = tracks[match.trackIndex];\n const detection = detections[match.detectionIndex];\n track.update(detection);\n if (track.trackerId === undefined &&\n track.successfulUpdates >= minimumConsecutiveFrames) {\n track.trackerId = nextTrackerId;\n nextTrackerId += 1;\n }\n if (track.trackerId !== undefined) {\n trackerIds.set(detection.detectionIndex, track.trackerId);\n }\n }\n for (const detectionIndex of unmatchedDetections) {\n const detection = detections[detectionIndex];\n if ((detection.confidence ?? 1) >= trackActivationThreshold) {\n tracks.push(new KalmanBoxTrack(detection));\n }\n }\n tracks = tracks.filter((track) => track.timeSinceUpdate <= maximumFramesWithoutUpdate &&\n (track.successfulUpdates >= minimumConsecutiveFrames ||\n track.timeSinceUpdate === 0));\n return {\n activeTrackCount: tracks.length,\n assignments: detections.flatMap((detection) => {\n const trackerId = trackerIds.get(detection.detectionIndex);\n return trackerId === undefined\n ? []\n : [{ detectionIndex: detection.detectionIndex, trackerId }];\n }),\n confirmedTrackCount: tracks.filter((track) => track.trackerId !== undefined).length,\n };\n },\n };\n }\n class KalmanBoxTrack {\n consecutiveUpdates;\n trackerId;\n successfulUpdates = 1;\n timeSinceUpdate = 0;\n state;\n covariance = identity(8);\n constructor(detection, consecutiveUpdates = false) {\n this.consecutiveUpdates = consecutiveUpdates;\n this.state = [...rectToXyxy(detection.rect), 0, 0, 0, 0].map((value) => [\n value,\n ]);\n }\n predict(frameStep, frameRate) {\n if (this.consecutiveUpdates && this.timeSinceUpdate > 0) {\n this.successfulUpdates = 0;\n }\n const transition = identity(8);\n for (let index = 0; index < 4; index += 1) {\n transition[index][index + 4] = frameStep;\n }\n this.state = multiply(transition, this.state);\n this.covariance = add(multiply(multiply(transition, this.covariance), transpose(transition)), createProcessNoise(frameStep, frameRate));\n // Python SORT counts update calls for the fixed-rate lost-track budget.\n this.timeSinceUpdate += 1;\n return stateToRect(this.state);\n }\n update(detection) {\n const measurement = rectToXyxy(detection.rect).map((value) => [value]);\n const observation = [\n [1, 0, 0, 0, 0, 0, 0, 0],\n [0, 1, 0, 0, 0, 0, 0, 0],\n [0, 0, 1, 0, 0, 0, 0, 0],\n [0, 0, 0, 1, 0, 0, 0, 0],\n ];\n const measurementNoise = scale(identity(4), 0.1);\n const innovation = subtract(measurement, multiply(observation, this.state));\n const covarianceObservationTranspose = multiply(this.covariance, transpose(observation));\n const innovationCovariance = add(multiply(observation, covarianceObservationTranspose), measurementNoise);\n const gain = multiply(covarianceObservationTranspose, inverse(innovationCovariance));\n this.state = add(this.state, multiply(gain, innovation));\n const identityMinusGainObservation = subtract(identity(8), multiply(gain, observation));\n // Joseph form matches the Python implementation and is more stable.\n this.covariance = add(multiply(multiply(identityMinusGainObservation, this.covariance), transpose(identityMinusGainObservation)), multiply(multiply(gain, measurementNoise), transpose(gain)));\n this.timeSinceUpdate = 0;\n this.successfulUpdates += 1;\n }\n getStateRect() {\n return stateToRect(this.state);\n }\n }\n function associateDetectionsToTracks(detections, predicted, minimumIouThreshold) {\n if (predicted.length === 0 || detections.length === 0) {\n return {\n matches: [],\n unmatchedDetections: detections.map((_, index) => index),\n };\n }\n // Python SORT associates all detections class-agnostically using standard IoU.\n const scores = predicted.map((rect) => detections.map((detection) => intersectionOverUnion(rect, detection.rect)));\n const candidateMatches = maximizeAssignment(scores);\n const matches = [];\n const matchedDetections = new Set();\n for (const [trackIndex, detectionIndex] of candidateMatches) {\n if (scores[trackIndex][detectionIndex] < minimumIouThreshold)\n continue;\n matches.push({ detectionIndex, trackIndex });\n matchedDetections.add(detectionIndex);\n }\n matches.sort((left, right) => left.trackIndex - right.trackIndex);\n return {\n matches,\n unmatchedDetections: detections.flatMap((_, index) => matchedDetections.has(index) ? [] : [index]),\n };\n }\n /** Hungarian assignment for a rectangular score matrix. */\n function maximizeAssignment(scores) {\n if (scores.length === 0 || scores[0]?.length === 0) {\n return [];\n }\n const rowCount = scores.length;\n const columnCount = scores[0].length;\n const transposed = rowCount > columnCount;\n const costs = transposed\n ? Array.from({ length: columnCount }, (_, row) => Array.from({ length: rowCount }, (_, column) => 1 - scores[column][row]))\n : scores.map((row) => row.map((score) => 1 - score));\n const rows = costs.length;\n const columns = costs[0].length;\n const u = new Array(rows + 1).fill(0);\n const v = new Array(columns + 1).fill(0);\n const p = new Array(columns + 1).fill(0);\n const way = new Array(columns + 1).fill(0);\n for (let row = 1; row <= rows; row += 1) {\n p[0] = row;\n let column0 = 0;\n const minValue = new Array(columns + 1).fill(Number.POSITIVE_INFINITY);\n const used = new Array(columns + 1).fill(false);\n do {\n used[column0] = true;\n const row0 = p[column0];\n let delta = Number.POSITIVE_INFINITY;\n let column1 = 0;\n for (let column = 1; column <= columns; column += 1) {\n if (used[column])\n continue;\n const current = costs[row0 - 1][column - 1] - u[row0] - v[column];\n if (current < minValue[column]) {\n minValue[column] = current;\n way[column] = column0;\n }\n if (minValue[column] < delta) {\n delta = minValue[column];\n column1 = column;\n }\n }\n for (let column = 0; column <= columns; column += 1) {\n if (used[column]) {\n u[p[column]] += delta;\n v[column] -= delta;\n }\n else {\n minValue[column] -= delta;\n }\n }\n column0 = column1;\n } while (p[column0] !== 0);\n do {\n const column1 = way[column0];\n p[column0] = p[column1];\n column0 = column1;\n } while (column0 !== 0);\n }\n const assignments = [];\n for (let column = 1; column <= columns; column += 1) {\n if (p[column] === 0)\n continue;\n const row = p[column] - 1;\n assignments.push(transposed ? [column - 1, row] : [row, column - 1]);\n }\n return assignments;\n }\n function rectToXyxy(rect) {\n return [\n rect.x - rect.width / 2,\n rect.y - rect.height / 2,\n rect.x + rect.width / 2,\n rect.y + rect.height / 2,\n ];\n }\n function stateToRect(state) {\n // The Python XYXY estimator intentionally leaves corner velocities\n // unconstrained. Normalize only at the browser Rect boundary so a crossing\n // prediction cannot violate the positive-width/height storage contract.\n const x1 = Math.min(state[0][0], state[2][0]);\n const y1 = Math.min(state[1][0], state[3][0]);\n const x2 = Math.max(state[0][0], state[2][0]);\n const y2 = Math.max(state[1][0], state[3][0]);\n return {\n height: Math.max(Number.EPSILON, y2 - y1),\n width: Math.max(Number.EPSILON, x2 - x1),\n x: (x1 + x2) / 2,\n y: (y1 + y2) / 2,\n };\n }\n function createProcessNoise(frameStep, frameRate) {\n if (Math.abs(frameStep - 1) <= 0.004 * frameRate) {\n return scale(identity(8), 0.01);\n }\n const result = Array.from({ length: 8 }, () => new Array(8).fill(0));\n const dt2 = frameStep * frameStep;\n const dt3 = dt2 * frameStep;\n const dt4 = dt2 * dt2;\n for (let index = 0; index < 4; index += 1) {\n const velocityIndex = index + 4;\n result[index][index] = (0.01 * dt4) / 4;\n result[index][velocityIndex] = (0.01 * dt3) / 2;\n result[velocityIndex][index] = (0.01 * dt3) / 2;\n result[velocityIndex][velocityIndex] = 0.01 * dt2;\n }\n return result;\n }\n function intersectionOverUnion(left, right) {\n const leftX = left.x - left.width / 2;\n const leftY = left.y - left.height / 2;\n const rightX = right.x - right.width / 2;\n const rightY = right.y - right.height / 2;\n const intersectionWidth = Math.max(0, Math.min(leftX + left.width, rightX + right.width) -\n Math.max(leftX, rightX));\n const intersectionHeight = Math.max(0, Math.min(leftY + left.height, rightY + right.height) -\n Math.max(leftY, rightY));\n const intersection = intersectionWidth * intersectionHeight;\n const union = left.width * left.height + right.width * right.height - intersection;\n return union <= 0 ? 0 : intersection / union;\n }\n function resolveFrameStep(current, previous) {\n if (current === undefined || previous === undefined)\n return 1;\n return Math.max(1, current - previous);\n }\n function normalizePositiveInteger$1(value, label) {\n if (!Number.isInteger(value) || value < 1) {\n throw new Error(`${label} must be a positive integer.`);\n }\n return value;\n }\n function normalizeNonNegativeInteger$1(value, label) {\n if (!Number.isInteger(value) || value < 0) {\n throw new Error(`${label} must be a non-negative integer.`);\n }\n return value;\n }\n function normalizeUnitInterval$1(value, label) {\n if (!Number.isFinite(value) || value < 0 || value > 1) {\n throw new Error(`${label} must be between 0 and 1.`);\n }\n }\n function identity(size) {\n return Array.from({ length: size }, (_, row) => Array.from({ length: size }, (_, column) => (row === column ? 1 : 0)));\n }\n function scale(matrix, factor) {\n return matrix.map((row) => row.map((value) => value * factor));\n }\n function transpose(matrix) {\n return matrix[0].map((_, column) => matrix.map((row) => row[column]));\n }\n function add(left, right) {\n return left.map((row, rowIndex) => row.map((value, columnIndex) => value + right[rowIndex][columnIndex]));\n }\n function subtract(left, right) {\n return left.map((row, rowIndex) => row.map((value, columnIndex) => value - right[rowIndex][columnIndex]));\n }\n function multiply(left, right) {\n return left.map((row) => right[0].map((_, column) => row.reduce((sum, value, index) => sum + value * right[index][column], 0)));\n }\n function inverse(matrix) {\n const size = matrix.length;\n const augmented = matrix.map((row, index) => [\n ...row,\n ...identity(size)[index],\n ]);\n for (let column = 0; column < size; column += 1) {\n let pivot = column;\n for (let row = column + 1; row < size; row += 1) {\n if (Math.abs(augmented[row][column]) >\n Math.abs(augmented[pivot][column])) {\n pivot = row;\n }\n }\n if (Math.abs(augmented[pivot][column]) < 1e-12) {\n throw new Error(\"SORT Kalman covariance is singular.\");\n }\n [augmented[column], augmented[pivot]] = [\n augmented[pivot],\n augmented[column],\n ];\n const divisor = augmented[column][column];\n augmented[column] = augmented[column].map((value) => value / divisor);\n for (let row = 0; row < size; row += 1) {\n if (row === column)\n continue;\n const factor = augmented[row][column];\n augmented[row] = augmented[row].map((value, index) => value - factor * augmented[column][index]);\n }\n }\n return augmented.map((row) => row.slice(size));\n }\n\n /**\n * Creates one stateful ByteTrack tracker for a single ordered media sequence.\n *\n * Defaults and two-stage association mirror roboflow/trackers ByteTrack at\n * source commit 60b21c8a48676784085fbee455559f16b75a7c9a.\n * Motion predictions remain internal; only observed detections receive IDs.\n */\n function createByteTrackTracker$1(options = {}) {\n const lostTrackBuffer = normalizeNonNegativeInteger$1(options.lostTrackBuffer ?? 30, \"lostTrackBuffer\");\n const frameRate = options.frameRate ?? 30;\n const trackActivationThreshold = options.trackActivationThreshold ?? 0.7;\n const minimumConsecutiveFrames = normalizePositiveInteger$1(options.minimumConsecutiveFrames ?? 2, \"minimumConsecutiveFrames\");\n const minimumIouThreshold = options.minimumIouThreshold ?? 0.1;\n const highConfidenceDetectionThreshold = options.highConfidenceDetectionThreshold ?? 0.6;\n if (!Number.isFinite(frameRate) || frameRate <= 0) {\n throw new Error(\"frameRate must be a finite positive value.\");\n }\n normalizeUnitInterval$1(trackActivationThreshold, \"trackActivationThreshold\");\n normalizeUnitInterval$1(minimumIouThreshold, \"minimumIouThreshold\");\n normalizeUnitInterval$1(highConfidenceDetectionThreshold, \"highConfidenceDetectionThreshold\");\n const scaledLostTrackBuffer = (frameRate / 30) * lostTrackBuffer;\n if (!Number.isFinite(scaledLostTrackBuffer)) {\n throw new Error(\"Scaled lostTrackBuffer overflows: frameRate / 30 * lostTrackBuffer must be finite.\");\n }\n const maximumFramesWithoutUpdate = lostTrackBuffer === 0 ? 0 : Math.max(1, Math.ceil(scaledLostTrackBuffer));\n let tracks = [];\n let nextTrackerId = 0;\n let previousFrameIndex;\n return {\n reset() {\n tracks = [];\n nextTrackerId = 0;\n previousFrameIndex = undefined;\n },\n update(detections, frameIndex) {\n const frameStep = resolveFrameStep(frameIndex, previousFrameIndex);\n previousFrameIndex = frameIndex ?? previousFrameIndex;\n tracks.forEach((track) => track.predict(frameStep, frameRate));\n const highDetectionIndexes = [];\n const lowDetectionIndexes = [];\n detections.forEach((detection, index) => {\n if ((detection.confidence ?? 1) >= highConfidenceDetectionThreshold) {\n highDetectionIndexes.push(index);\n }\n else {\n lowDetectionIndexes.push(index);\n }\n });\n const assignments = new Map();\n const firstStage = associate(tracks, detections, highDetectionIndexes, minimumIouThreshold);\n for (const match of firstStage.matches) {\n updateMatchedTrack(tracks[match.trackIndex], detections[match.detectionIndex], assignments);\n }\n const remainingTracks = firstStage.unmatchedTrackIndexes.map((index) => tracks[index]);\n const secondStage = associate(remainingTracks, detections, lowDetectionIndexes, minimumIouThreshold);\n for (const match of secondStage.matches) {\n updateMatchedTrack(remainingTracks[match.trackIndex], detections[match.detectionIndex], assignments);\n }\n // Only unmatched high-confidence observations can start a track.\n for (const detectionIndex of firstStage.unmatchedDetectionIndexes) {\n const detection = detections[detectionIndex];\n if ((detection.confidence ?? 1) >= trackActivationThreshold) {\n tracks.push(new KalmanBoxTrack(detection, true));\n }\n }\n // Confirmation is sticky once an ID has been allocated. An unmatched\n // unconfirmed track is discarded immediately, as in Python ByteTrack.\n tracks = tracks.filter((track) => track.timeSinceUpdate <= maximumFramesWithoutUpdate &&\n (track.trackerId !== undefined ||\n track.successfulUpdates >= minimumConsecutiveFrames ||\n track.timeSinceUpdate === 0));\n return {\n activeTrackCount: tracks.length,\n assignments: detections.flatMap((detection) => {\n const trackerId = assignments.get(detection.detectionIndex);\n return trackerId === undefined\n ? []\n : [{ detectionIndex: detection.detectionIndex, trackerId }];\n }),\n confirmedTrackCount: tracks.filter((track) => track.trackerId !== undefined).length,\n };\n },\n };\n function updateMatchedTrack(track, detection, assignments) {\n track.update(detection);\n if (track.trackerId === undefined &&\n track.successfulUpdates >= minimumConsecutiveFrames) {\n track.trackerId = nextTrackerId;\n nextTrackerId += 1;\n }\n if (track.trackerId !== undefined) {\n assignments.set(detection.detectionIndex, track.trackerId);\n }\n }\n }\n function associate(tracks, detections, detectionIndexes, minimumIouThreshold) {\n if (tracks.length === 0 || detectionIndexes.length === 0) {\n return {\n matches: [],\n unmatchedDetectionIndexes: [...detectionIndexes],\n unmatchedTrackIndexes: tracks.map((_, index) => index),\n };\n }\n const scores = tracks.map((track) => {\n const predictedRect = track.getStateRect();\n return detectionIndexes.map((detectionIndex) => intersectionOverUnion(predictedRect, detections[detectionIndex].rect));\n });\n const matches = [];\n const matchedTracks = new Set();\n const matchedDetections = new Set();\n for (const [trackIndex, localDetectionIndex] of maximizeAssignment(scores)) {\n if (scores[trackIndex][localDetectionIndex] < minimumIouThreshold) {\n continue;\n }\n const detectionIndex = detectionIndexes[localDetectionIndex];\n matches.push({ detectionIndex, trackIndex });\n matchedTracks.add(trackIndex);\n matchedDetections.add(detectionIndex);\n }\n matches.sort((left, right) => left.trackIndex - right.trackIndex);\n return {\n matches,\n unmatchedDetectionIndexes: detectionIndexes.filter((index) => !matchedDetections.has(index)),\n unmatchedTrackIndexes: tracks.flatMap((_, index) => matchedTracks.has(index) ? [] : [index]),\n };\n }\n\n function associateTrackingScores(scores, trackCount, detectionCount, minimumScore, acceptanceScores = scores) {\n const matches = [];\n const matchedTracks = new Set();\n const matchedDetections = new Set();\n for (const [trackIndex, detectionIndex] of maximizeAssignment(scores)) {\n if (acceptanceScores[trackIndex][detectionIndex] < minimumScore)\n continue;\n matches.push({ detectionIndex, trackIndex });\n matchedTracks.add(trackIndex);\n matchedDetections.add(detectionIndex);\n }\n matches.sort((left, right) => left.trackIndex - right.trackIndex);\n return {\n matches,\n unmatchedDetectionIndexes: Array.from({ length: detectionCount }, (_, index) => index).filter((index) => !matchedDetections.has(index)),\n unmatchedTrackIndexes: Array.from({ length: trackCount }, (_, index) => index).filter((index) => !matchedTracks.has(index)),\n };\n }\n function pairwiseIou(tracks, detections, bufferRatio = 0) {\n return tracks.map((track) => detections.map((detection) => bufferedIntersectionOverUnion(track, detection, bufferRatio)));\n }\n function bufferedIntersectionOverUnion(left, right, bufferRatio) {\n const leftWidth = left.width * (1 + 2 * bufferRatio);\n const leftHeight = left.height * (1 + 2 * bufferRatio);\n const rightWidth = right.width * (1 + 2 * bufferRatio);\n const rightHeight = right.height * (1 + 2 * bufferRatio);\n const leftX = left.x - leftWidth / 2;\n const leftY = left.y - leftHeight / 2;\n const rightX = right.x - rightWidth / 2;\n const rightY = right.y - rightHeight / 2;\n const intersectionWidth = Math.max(0, Math.min(leftX + leftWidth, rightX + rightWidth) - Math.max(leftX, rightX));\n const intersectionHeight = Math.max(0, Math.min(leftY + leftHeight, rightY + rightHeight) -\n Math.max(leftY, rightY));\n const intersection = intersectionWidth * intersectionHeight;\n const union = leftWidth * leftHeight + rightWidth * rightHeight - intersection;\n return union <= 0 ? 0 : intersection / union;\n }\n\n /**\n * Kalman state estimator shared by the browser C-BIoU and OC-SORT ports.\n * Its layouts, covariance update, and gap-scaled constant-velocity model\n * mirror roboflow/trackers at 60b21c8.\n */\n class TrackingKalmanEstimator {\n representation;\n dimension;\n measurementDimension = 4;\n covariance;\n measurementNoise = identity(4);\n processNoise;\n state;\n baselineProcessNoise;\n positionIndexes;\n velocityIndexes;\n constructor(initialRect, representation) {\n this.representation = representation;\n const measurement = rectToMeasurement(initialRect, representation);\n this.dimension = representation === \"xcycsr\" ? 7 : 8;\n this.positionIndexes =\n representation === \"xcycsr\" ? [0, 1, 2] : [0, 1, 2, 3];\n this.velocityIndexes =\n representation === \"xcycsr\" ? [4, 5, 6] : [4, 5, 6, 7];\n this.state = Array.from({ length: this.dimension }, (_, index) => [\n measurement[index] ?? 0,\n ]);\n this.covariance = identity(this.dimension);\n this.processNoise = identity(this.dimension);\n this.baselineProcessNoise = identity(this.dimension);\n }\n predict(frameStep, frameRate) {\n if (this.representation === \"xcycsr\" &&\n this.state[2][0] + frameStep * this.state[6][0] <= 0) {\n this.state[6][0] = 0;\n }\n const transition = identity(this.dimension);\n this.positionIndexes.forEach((positionIndex, index) => {\n transition[positionIndex][this.velocityIndexes[index]] = frameStep;\n });\n this.processNoise = this.createProcessNoise(frameStep, frameRate);\n this.state = multiply(transition, this.state);\n this.covariance = add(multiply(multiply(transition, this.covariance), transpose(transition)), this.processNoise);\n }\n update(rect) {\n this.updateMeasurement(rectToMeasurement(rect, this.representation));\n }\n updateMeasurement(measurement) {\n const observation = Array.from({ length: 4 }, (_, row) => Array.from({ length: this.dimension }, (_, column) => row === column ? 1 : 0));\n const measurementColumn = measurement.map((value) => [value]);\n const innovation = subtract(measurementColumn, multiply(observation, this.state));\n const covarianceObservationTranspose = multiply(this.covariance, transpose(observation));\n const innovationCovariance = add(multiply(observation, covarianceObservationTranspose), this.measurementNoise);\n const gain = multiply(covarianceObservationTranspose, inverse(innovationCovariance));\n this.state = add(this.state, multiply(gain, innovation));\n const identityMinusGainObservation = subtract(identity(this.dimension), multiply(gain, observation));\n this.covariance = add(multiply(multiply(identityMinusGainObservation, this.covariance), transpose(identityMinusGainObservation)), multiply(multiply(gain, this.measurementNoise), transpose(gain)));\n }\n getRect() {\n return measurementToRect(this.state.slice(0, 4).map((row) => row[0]), this.representation);\n }\n setCovariances(options) {\n if (options.covariance)\n this.covariance = clone(options.covariance);\n if (options.measurementNoise) {\n this.measurementNoise = clone(options.measurementNoise);\n }\n if (options.processNoise) {\n this.processNoise = clone(options.processNoise);\n this.baselineProcessNoise = clone(options.processNoise);\n }\n }\n snapshot() {\n return {\n covariance: clone(this.covariance),\n measurementNoise: clone(this.measurementNoise),\n processNoise: clone(this.processNoise),\n state: clone(this.state),\n };\n }\n restore(snapshot) {\n this.covariance = clone(snapshot.covariance);\n this.measurementNoise = clone(snapshot.measurementNoise);\n this.processNoise = clone(snapshot.processNoise);\n this.state = clone(snapshot.state);\n }\n createProcessNoise(frameStep, frameRate) {\n if (Math.abs(frameStep - 1) <= 0.004 * frameRate) {\n return clone(this.baselineProcessNoise);\n }\n const result = Array.from({ length: this.dimension }, () => new Array(this.dimension).fill(0));\n const dt2 = frameStep * frameStep;\n const dt3 = dt2 * frameStep;\n const dt4 = dt2 * dt2;\n const kinematicIndexes = new Set([\n ...this.positionIndexes,\n ...this.velocityIndexes,\n ]);\n this.positionIndexes.forEach((positionIndex, index) => {\n const velocityIndex = this.velocityIndexes[index];\n const accelerationVariance = this.baselineProcessNoise[velocityIndex][velocityIndex];\n result[positionIndex][positionIndex] = (accelerationVariance * dt4) / 4;\n result[positionIndex][velocityIndex] = (accelerationVariance * dt3) / 2;\n result[velocityIndex][positionIndex] = (accelerationVariance * dt3) / 2;\n result[velocityIndex][velocityIndex] = accelerationVariance * dt2;\n });\n for (let index = 0; index < this.dimension; index += 1) {\n if (!kinematicIndexes.has(index)) {\n result[index][index] = this.baselineProcessNoise[index][index];\n }\n }\n return result;\n }\n }\n function rectToMeasurement(rect, representation) {\n if (representation === \"xcycwh\") {\n return [rect.x, rect.y, rect.width, rect.height];\n }\n return [\n rect.x,\n rect.y,\n rect.width * rect.height,\n rect.width / (rect.height + 1e-6),\n ];\n }\n function measurementToRect(measurement, representation) {\n const [x, y, third, fourth] = measurement;\n if (representation === \"xcycwh\") {\n return {\n height: Math.max(1e-3, fourth),\n width: Math.max(1e-3, third),\n x,\n y,\n };\n }\n const width = Math.sqrt(third * fourth);\n const height = width === 0 ? 0 : third / width;\n return {\n height: Math.max(Number.EPSILON, height),\n width: Math.max(Number.EPSILON, width),\n x,\n y,\n };\n }\n function diagonal(values) {\n return values.map((value, row) => values.map((_, column) => (row === column ? value : 0)));\n }\n function scaleMatrix(matrix, factor) {\n return scale(matrix, factor);\n }\n function clone(matrix) {\n return matrix.map((row) => [...row]);\n }\n\n const MINIMUM_DETECTION_CONFIDENCE = 0.1;\n class CBIoUTrack {\n trackerId;\n successfulUpdates = 1;\n timeSinceUpdate = 0;\n estimator;\n constructor(initial) {\n this.estimator = new TrackingKalmanEstimator(initial.rect, \"xcycwh\");\n this.setInitialNoise(initial.rect.width, initial.rect.height);\n }\n predict(frameStep, frameRate) {\n const current = this.estimator.getRect();\n this.estimator.setCovariances({\n processNoise: this.buildProcessNoise(Math.max(current.width, 1e-3), Math.max(current.height, 1e-3)),\n });\n this.estimator.predict(frameStep, frameRate);\n this.clampState();\n this.timeSinceUpdate += 1;\n }\n update(detection) {\n const current = this.estimator.getRect();\n this.estimator.setCovariances({\n measurementNoise: this.buildMeasurementNoise(Math.max(current.width, 1e-3), Math.max(current.height, 1e-3)),\n });\n this.estimator.update(detection.rect);\n this.clampState();\n this.timeSinceUpdate = 0;\n this.successfulUpdates += 1;\n }\n getRect() {\n return this.estimator.getRect();\n }\n setInitialNoise(width, height) {\n const sigmaPosition = 0.05;\n const sigmaVelocity = 0.00625;\n this.estimator.setCovariances({\n covariance: diagonal([\n (2 * sigmaPosition * width) ** 2,\n (2 * sigmaPosition * height) ** 2,\n (2 * sigmaPosition * width) ** 2,\n (2 * sigmaPosition * height) ** 2,\n (10 * sigmaVelocity * width) ** 2,\n (10 * sigmaVelocity * height) ** 2,\n (10 * sigmaVelocity * width) ** 2,\n (10 * sigmaVelocity * height) ** 2,\n ]),\n measurementNoise: this.buildMeasurementNoise(width, height),\n processNoise: this.buildProcessNoise(width, height),\n });\n }\n buildProcessNoise(width, height) {\n const sigmaPosition = 0.05;\n const sigmaVelocity = 0.00625;\n return diagonal([\n (sigmaPosition * width) ** 2,\n (sigmaPosition * height) ** 2,\n (sigmaPosition * width) ** 2,\n (sigmaPosition * height) ** 2,\n (sigmaVelocity * width) ** 2,\n (sigmaVelocity * height) ** 2,\n (sigmaVelocity * width) ** 2,\n (sigmaVelocity * height) ** 2,\n ]);\n }\n buildMeasurementNoise(width, height) {\n const sigmaMeasurement = 0.05;\n return diagonal([\n (sigmaMeasurement * width) ** 2,\n (sigmaMeasurement * height) ** 2,\n (sigmaMeasurement * width) ** 2,\n (sigmaMeasurement * height) ** 2,\n ]);\n }\n clampState() {\n this.estimator.state[2][0] = Math.max(this.estimator.state[2][0], 1e-3);\n this.estimator.state[3][0] = Math.max(this.estimator.state[3][0], 1e-3);\n }\n }\n /** Creates the detection-only C-BIoU implementation from roboflow/trackers. */\n function createCBIoUTracker$1(options = {}) {\n const lostTrackBuffer = normalizeNonNegativeInteger$1(options.lostTrackBuffer ?? 30, \"lostTrackBuffer\");\n const frameRate = options.frameRate ?? 30;\n const trackActivationThreshold = options.trackActivationThreshold ?? 0.7;\n const minimumConsecutiveFrames = normalizePositiveInteger$1(options.minimumConsecutiveFrames ?? 2, \"minimumConsecutiveFrames\");\n const minimumIouThresholdFirstAssociation = options.minimumIouThresholdFirstAssociation ?? 0.2;\n const minimumIouThresholdSecondAssociation = options.minimumIouThresholdSecondAssociation ?? 0.5;\n const minimumIouThresholdUnconfirmedAssociation = options.minimumIouThresholdUnconfirmedAssociation ?? 0.3;\n const highConfidenceDetectionThreshold = options.highConfidenceDetectionThreshold ?? 0.6;\n const instantFirstFrameActivation = options.instantFirstFrameActivation ?? true;\n const bufferRatioFirst = options.bufferRatioFirst ?? 0.3;\n const bufferRatioSecond = options.bufferRatioSecond ?? 0.5;\n if (!Number.isFinite(frameRate) || frameRate <= 0) {\n throw new Error(\"frameRate must be a finite positive value.\");\n }\n normalizeUnitInterval$1(trackActivationThreshold, \"trackActivationThreshold\");\n normalizeUnitInterval$1(minimumIouThresholdFirstAssociation, \"minimumIouThresholdFirstAssociation\");\n normalizeUnitInterval$1(minimumIouThresholdSecondAssociation, \"minimumIouThresholdSecondAssociation\");\n normalizeUnitInterval$1(minimumIouThresholdUnconfirmedAssociation, \"minimumIouThresholdUnconfirmedAssociation\");\n normalizeUnitInterval$1(highConfidenceDetectionThreshold, \"highConfidenceDetectionThreshold\");\n if (!Number.isFinite(bufferRatioFirst) || bufferRatioFirst < 0) {\n throw new Error(\"bufferRatioFirst must be a finite non-negative value.\");\n }\n if (!Number.isFinite(bufferRatioSecond) || bufferRatioSecond < 0) {\n throw new Error(\"bufferRatioSecond must be a finite non-negative value.\");\n }\n const scaledLostTrackBuffer = (frameRate / 30) * lostTrackBuffer;\n if (!Number.isFinite(scaledLostTrackBuffer)) {\n throw new Error(\"Scaled lostTrackBuffer overflows: frameRate / 30 * lostTrackBuffer must be finite.\");\n }\n const maximumFramesWithoutUpdate = lostTrackBuffer === 0 ? 0 : Math.max(1, Math.ceil(scaledLostTrackBuffer));\n let tracks = [];\n let nextTrackerId = 0;\n let frameId = 0;\n let previousFrameIndex;\n return {\n reset() {\n tracks = [];\n nextTrackerId = 0;\n frameId = 0;\n previousFrameIndex = undefined;\n },\n update(detections, frameIndex) {\n const frameStep = resolveFrameStep(frameIndex, previousFrameIndex);\n previousFrameIndex = frameIndex ?? previousFrameIndex;\n frameId += 1;\n tracks.forEach((track) => track.predict(frameStep, frameRate));\n const highDetectionIndexes = [];\n const lowDetectionIndexes = [];\n detections.forEach((detection, index) => {\n const confidence = detection.confidence ?? 1;\n if (confidence >= highConfidenceDetectionThreshold) {\n highDetectionIndexes.push(index);\n }\n else if (confidence > MINIMUM_DETECTION_CONFIDENCE) {\n lowDetectionIndexes.push(index);\n }\n });\n const confirmed = [];\n const unconfirmed = [];\n const lost = [];\n for (const track of tracks) {\n if (track.timeSinceUpdate > 1)\n lost.push(track);\n else if (track.trackerId !== undefined ||\n track.successfulUpdates >= minimumConsecutiveFrames) {\n confirmed.push(track);\n }\n else\n unconfirmed.push(track);\n }\n const assignments = new Map();\n const pool = [...confirmed, ...lost];\n const highDetections = highDetectionIndexes.map((index) => detections[index]);\n const firstScores = pairwiseIou(pool.map((track) => track.getRect()), highDetections.map((detection) => detection.rect), bufferRatioFirst).map((row) => row.map((score, index) => score * (highDetections[index].confidence ?? 1)));\n const firstAssociation = associateTrackingScores(firstScores, pool.length, highDetections.length, minimumIouThresholdFirstAssociation);\n for (const match of firstAssociation.matches) {\n updateMatchedTrack(pool[match.trackIndex], highDetections[match.detectionIndex], assignments);\n }\n const remainingTracked = firstAssociation.unmatchedTrackIndexes\n .map((index) => pool[index])\n .filter((track) => track.timeSinceUpdate === 1);\n const lowDetections = lowDetectionIndexes.map((index) => detections[index]);\n const secondScores = pairwiseIou(remainingTracked.map((track) => track.getRect()), lowDetections.map((detection) => detection.rect), bufferRatioSecond);\n const secondAssociation = associateTrackingScores(secondScores, remainingTracked.length, lowDetections.length, minimumIouThresholdSecondAssociation);\n for (const match of secondAssociation.matches) {\n updateMatchedTrack(remainingTracked[match.trackIndex], lowDetections[match.detectionIndex], assignments);\n }\n let unmatchedHighLocal = [...firstAssociation.unmatchedDetectionIndexes];\n let unmatchedUnconfirmed = unconfirmed.map((_, index) => index);\n if (unconfirmed.length > 0 && unmatchedHighLocal.length > 0) {\n const remainingHigh = unmatchedHighLocal.map((index) => highDetections[index]);\n const unconfirmedScores = pairwiseIou(unconfirmed.map((track) => track.getRect()), remainingHigh.map((detection) => detection.rect), bufferRatioFirst).map((row) => row.map((score, index) => score * (remainingHigh[index].confidence ?? 1)));\n const unconfirmedAssociation = associateTrackingScores(unconfirmedScores, unconfirmed.length, remainingHigh.length, minimumIouThresholdUnconfirmedAssociation);\n unmatchedUnconfirmed = unconfirmedAssociation.unmatchedTrackIndexes;\n for (const match of unconfirmedAssociation.matches) {\n updateMatchedTrack(unconfirmed[match.trackIndex], remainingHigh[match.detectionIndex], assignments);\n }\n unmatchedHighLocal =\n unconfirmedAssociation.unmatchedDetectionIndexes.map((index) => unmatchedHighLocal[index]);\n }\n const unmatchedUnconfirmedTracks = new Set(unmatchedUnconfirmed.map((index) => unconfirmed[index]));\n tracks = tracks.filter((track) => !unmatchedUnconfirmedTracks.has(track));\n for (const localIndex of unmatchedHighLocal) {\n const detection = highDetections[localIndex];\n if ((detection.confidence ?? 1) < trackActivationThreshold)\n continue;\n const track = new CBIoUTrack(detection);\n if (frameId === 1 && instantFirstFrameActivation) {\n track.trackerId = nextTrackerId++;\n assignments.set(detection.detectionIndex, track.trackerId);\n }\n tracks.push(track);\n }\n tracks = tracks.filter((track) => track.timeSinceUpdate <= maximumFramesWithoutUpdate &&\n (track.timeSinceUpdate === 0 ||\n track.trackerId !== undefined ||\n track.successfulUpdates >= minimumConsecutiveFrames));\n return createUpdate(detections, assignments, tracks);\n },\n };\n function updateMatchedTrack(track, detection, assignments) {\n track.update(detection);\n if (track.trackerId === undefined &&\n track.successfulUpdates >= minimumConsecutiveFrames) {\n track.trackerId = nextTrackerId++;\n }\n if (track.trackerId !== undefined) {\n assignments.set(detection.detectionIndex, track.trackerId);\n }\n }\n }\n function createUpdate(detections, assignments, tracks) {\n const resolvedAssignments = detections.flatMap((detection) => {\n const trackerId = assignments.get(detection.detectionIndex);\n return trackerId === undefined\n ? []\n : [{ detectionIndex: detection.detectionIndex, trackerId }];\n });\n return {\n activeTrackCount: tracks.length,\n assignments: resolvedAssignments,\n confirmedTrackCount: tracks.filter((track) => track.trackerId !== undefined)\n .length,\n };\n }\n\n class OCSortTrack {\n deltaT;\n age = 0;\n lastObservation;\n trackerId;\n successfulConsecutiveUpdates = 0;\n timeSinceUpdate = 0;\n velocity;\n frozenState;\n observed = true;\n observations = new Map();\n estimator;\n constructor(initial, deltaT) {\n this.deltaT = deltaT;\n this.lastObservation = initial;\n this.estimator = new TrackingKalmanEstimator(initial.rect, \"xcycsr\");\n this.configureNoise();\n }\n predict(frameStep, frameRate) {\n if (this.observed && this.timeSinceUpdate > 0) {\n this.frozenState = this.estimator.snapshot();\n this.observed = false;\n }\n this.estimator.predict(frameStep, frameRate);\n if (this.timeSinceUpdate > 0) {\n this.successfulConsecutiveUpdates = 0;\n }\n this.timeSinceUpdate += 1;\n this.age += 1;\n }\n update(detection, frameStep, frameRate) {\n const previous = this.getPreviousObservation();\n if (previous) {\n this.velocity = computeVelocity(previous, detection);\n }\n if (!this.observed && this.frozenState) {\n this.unfreeze(detection, frameStep, frameRate);\n }\n this.estimator.update(detection.rect);\n this.observed = true;\n this.timeSinceUpdate = 0;\n this.successfulConsecutiveUpdates += 1;\n this.lastObservation = detection;\n this.observations.set(this.age, detection);\n const cutoff = this.age - this.deltaT;\n for (const age of this.observations.keys()) {\n if (age < cutoff)\n this.observations.delete(age);\n }\n }\n getRect() {\n return this.estimator.getRect();\n }\n getPreviousObservation() {\n if (this.observations.size === 0)\n return undefined;\n for (let index = 0; index < this.deltaT; index += 1) {\n const delta = this.deltaT - index;\n const observation = this.observations.get(this.age - delta);\n if (observation)\n return observation;\n }\n const latestAge = Math.max(...this.observations.keys());\n return this.observations.get(latestAge);\n }\n configureNoise() {\n const measurementNoise = identity(4);\n for (let index = 2; index < 4; index += 1) {\n measurementNoise[index][index] *= 10;\n }\n const covariance = scaleMatrix(identity(7), 10);\n for (let index = 4; index < 7; index += 1) {\n covariance[index][index] *= 1000;\n }\n const processNoise = identity(7);\n processNoise[6][6] *= 0.01;\n for (let index = 4; index < 7; index += 1) {\n processNoise[index][index] *= 0.01;\n }\n this.estimator.setCovariances({\n covariance,\n measurementNoise,\n processNoise,\n });\n }\n unfreeze(detection, frameStep, frameRate) {\n if (!this.frozenState || this.timeSinceUpdate === 0)\n return;\n this.estimator.restore(this.frozenState);\n const timeGap = this.timeSinceUpdate;\n const start = rectToMeasurement(this.lastObservation.rect, \"xcycsr\");\n const end = rectToMeasurement(detection.rect, \"xcycsr\");\n const startWidth = Math.sqrt(start[2] * start[3]);\n const startHeight = start[3] === 0 ? 0 : Math.sqrt(start[2] / start[3]);\n const endWidth = Math.sqrt(end[2] * end[3]);\n const endHeight = end[3] === 0 ? 0 : Math.sqrt(end[2] / end[3]);\n for (let index = 0; index < timeGap; index += 1) {\n const progress = (index + 1) / timeGap;\n const x = start[0] + progress * (end[0] - start[0]);\n const y = start[1] + progress * (end[1] - start[1]);\n const width = startWidth + progress * (endWidth - startWidth);\n const height = startHeight + progress * (endHeight - startHeight);\n this.estimator.updateMeasurement([x, y, width * height, width / height]);\n if (index < timeGap - 1) {\n this.estimator.predict(frameStep, frameRate);\n }\n }\n this.frozenState = undefined;\n }\n }\n /** Creates the observation-centric SORT implementation from roboflow/trackers. */\n function createOCSortTracker$1(options = {}) {\n const lostTrackBuffer = normalizeNonNegativeInteger$1(options.lostTrackBuffer ?? 30, \"lostTrackBuffer\");\n const frameRate = options.frameRate ?? 30;\n const minimumConsecutiveFrames = normalizePositiveInteger$1(options.minimumConsecutiveFrames ?? 3, \"minimumConsecutiveFrames\");\n const minimumIouThreshold = options.minimumIouThreshold ?? 0.3;\n const directionConsistencyWeight = options.directionConsistencyWeight ?? 0.2;\n const highConfidenceDetectionThreshold = options.highConfidenceDetectionThreshold ?? 0.6;\n const deltaT = normalizePositiveInteger$1(options.deltaT ?? 3, \"deltaT\");\n if (!Number.isFinite(frameRate) || frameRate <= 0) {\n throw new Error(\"frameRate must be a finite positive value.\");\n }\n normalizeUnitInterval$1(minimumIouThreshold, \"minimumIouThreshold\");\n normalizeUnitInterval$1(directionConsistencyWeight, \"directionConsistencyWeight\");\n normalizeUnitInterval$1(highConfidenceDetectionThreshold, \"highConfidenceDetectionThreshold\");\n const scaledLostTrackBuffer = (frameRate / 30) * lostTrackBuffer;\n if (!Number.isFinite(scaledLostTrackBuffer)) {\n throw new Error(\"Scaled lostTrackBuffer overflows: frameRate / 30 * lostTrackBuffer must be finite.\");\n }\n const maximumFramesWithoutUpdate = lostTrackBuffer === 0 ? 0 : Math.max(1, Math.ceil(scaledLostTrackBuffer));\n let tracks = [];\n let frameCount = 0;\n let nextTrackerId = 0;\n let previousFrameIndex;\n return {\n reset() {\n tracks = [];\n frameCount = 0;\n nextTrackerId = 0;\n previousFrameIndex = undefined;\n },\n update(detections, frameIndex) {\n const frameStep = resolveFrameStep(frameIndex, previousFrameIndex);\n previousFrameIndex = frameIndex ?? previousFrameIndex;\n // Match roboflow/trackers: an empty stream before any track exists is\n // not part of OC-SORT's early-sequence activation window.\n if (tracks.length === 0 && detections.length === 0) {\n return {\n activeTrackCount: 0,\n assignments: [],\n confirmedTrackCount: 0,\n };\n }\n tracks.forEach((track) => track.predict(frameStep, frameRate));\n const highDetectionIndexes = detections.flatMap((detection, index) => detection.confidence === undefined ||\n detection.confidence >= highConfidenceDetectionThreshold\n ? [index]\n : []);\n const highDetections = highDetectionIndexes.map((index) => detections[index]);\n const iouScores = pairwiseIou(tracks.map((track) => track.getRect()), highDetections.map((detection) => detection.rect));\n const combinedScores = iouScores.map((row, trackIndex) => row.map((iou, detectionIndex) => {\n if (directionConsistencyWeight === 0)\n return iou;\n return (iou +\n directionConsistencyWeight *\n directionConsistency(tracks[trackIndex], highDetections[detectionIndex]) *\n (highDetections[detectionIndex].confidence ?? 1));\n }));\n const primary = associateTrackingScores(combinedScores, tracks.length, highDetections.length, minimumIouThreshold, iouScores);\n const assignments = new Map();\n for (const match of primary.matches) {\n updateMatchedTrack(tracks[match.trackIndex], highDetections[match.detectionIndex], assignments, frameStep);\n }\n let remainingHigh = [...primary.unmatchedDetectionIndexes];\n if (primary.unmatchedTrackIndexes.length > 0 &&\n remainingHigh.length > 0) {\n const unmatchedTracks = primary.unmatchedTrackIndexes.map((index) => tracks[index]);\n const unmatchedDetections = remainingHigh.map((index) => highDetections[index]);\n const recoveryScores = pairwiseIou(unmatchedTracks.map((track) => track.lastObservation.rect), unmatchedDetections.map((detection) => detection.rect));\n const recovery = associateTrackingScores(recoveryScores, unmatchedTracks.length, unmatchedDetections.length, minimumIouThreshold);\n for (const match of recovery.matches) {\n updateMatchedTrack(unmatchedTracks[match.trackIndex], unmatchedDetections[match.detectionIndex], assignments, frameStep);\n }\n remainingHigh = recovery.unmatchedDetectionIndexes.map((index) => remainingHigh[index]);\n }\n for (const localIndex of remainingHigh) {\n tracks.push(new OCSortTrack(highDetections[localIndex], deltaT));\n }\n tracks = tracks.filter((track) => track.timeSinceUpdate <= maximumFramesWithoutUpdate);\n frameCount += 1;\n const resolvedAssignments = detections.flatMap((detection) => {\n const trackerId = assignments.get(detection.detectionIndex);\n return trackerId === undefined\n ? []\n : [{ detectionIndex: detection.detectionIndex, trackerId }];\n });\n return {\n activeTrackCount: tracks.length,\n assignments: resolvedAssignments,\n confirmedTrackCount: tracks.filter((track) => track.trackerId !== undefined).length,\n };\n },\n };\n function updateMatchedTrack(track, detection, assignments, frameStep) {\n track.update(detection, frameStep, frameRate);\n const earlySequence = frameCount <= minimumConsecutiveFrames;\n const shouldEmit = (earlySequence && track.timeSinceUpdate === 0) ||\n track.successfulConsecutiveUpdates >= minimumConsecutiveFrames;\n if (shouldEmit) {\n if (track.trackerId === undefined) {\n track.trackerId = nextTrackerId++;\n }\n assignments.set(detection.detectionIndex, track.trackerId);\n }\n }\n }\n function computeVelocity(previous, current) {\n const deltaX = current.rect.x - previous.rect.x;\n const deltaY = current.rect.y - previous.rect.y;\n const norm = Math.sqrt(deltaX * deltaX + deltaY * deltaY) + 1e-6;\n return { x: deltaX / norm, y: deltaY / norm };\n }\n function directionConsistency(track, detection) {\n if (!track.velocity)\n return 0;\n const reference = track.getPreviousObservation() ?? track.lastObservation;\n const deltaX = detection.rect.x - reference.rect.x;\n const deltaY = detection.rect.y - reference.rect.y;\n const norm = Math.sqrt(deltaX * deltaX + deltaY * deltaY) + 1e-6;\n const cosine = Math.max(-1, Math.min(1, track.velocity.x * (deltaX / norm) + track.velocity.y * (deltaY / norm)));\n const angle = Math.acos(cosine);\n return (Math.PI / 2 - Math.abs(angle)) / Math.PI;\n }\n\n /** Public core facade for the internal tracker engine workspace. */\n function createSortTracker(options = {}) {\n return createSortTracker$1(options);\n }\n\n /** Public core facade for the internal tracker engine workspace. */\n function createByteTrackTracker(options = {}) {\n return createByteTrackTracker$1(options);\n }\n\n /** Public core facade for the internal tracker engine workspace. */\n function createCBIoUTracker(options = {}) {\n return createCBIoUTracker$1(options);\n }\n\n /** Public core facade for the internal tracker engine workspace. */\n function createOCSortTracker(options = {}) {\n return createOCSortTracker$1(options);\n }\n\n /** Geometry projected into a tracker. The original detection payload is retained. */\n var TrackingGeometry;\n (function (TrackingGeometry) {\n TrackingGeometry[\"Box\"] = \"box\";\n TrackingGeometry[\"Mask\"] = \"mask\";\n TrackingGeometry[\"Keypoints\"] = \"keypoints\";\n })(TrackingGeometry || (TrackingGeometry = {}));\n\n var DetectionMaskPayloadFormat;\n (function (DetectionMaskPayloadFormat) {\n DetectionMaskPayloadFormat[\"RawCocoRle\"] = \"rawCocoRle\";\n DetectionMaskPayloadFormat[\"DeflatedBase64\"] = \"deflatedBase64\";\n })(DetectionMaskPayloadFormat || (DetectionMaskPayloadFormat = {}));\n\n var DetectionPickTarget;\n (function (DetectionPickTarget) {\n DetectionPickTarget[\"Box\"] = \"box\";\n DetectionPickTarget[\"Edge\"] = \"edge\";\n DetectionPickTarget[\"Keypoint\"] = \"keypoint\";\n DetectionPickTarget[\"Label\"] = \"label\";\n DetectionPickTarget[\"Mask\"] = \"mask\";\n DetectionPickTarget[\"Polygon\"] = \"polygon\";\n DetectionPickTarget[\"Polyline\"] = \"polyline\";\n })(DetectionPickTarget || (DetectionPickTarget = {}));\n var MediaInteractionMode;\n (function (MediaInteractionMode) {\n MediaInteractionMode[\"Always\"] = \"always\";\n MediaInteractionMode[\"Disabled\"] = \"disabled\";\n MediaInteractionMode[\"PausedOnly\"] = \"pausedOnly\";\n })(MediaInteractionMode || (MediaInteractionMode = {}));\n\n var AnnotationGeometryKind;\n (function (AnnotationGeometryKind) {\n AnnotationGeometryKind[\"Box\"] = \"box\";\n AnnotationGeometryKind[\"Polygon\"] = \"polygon\";\n AnnotationGeometryKind[\"Polyline\"] = \"polyline\";\n AnnotationGeometryKind[\"Keypoints\"] = \"keypoints\";\n AnnotationGeometryKind[\"Mask\"] = \"mask\";\n })(AnnotationGeometryKind || (AnnotationGeometryKind = {}));\n var AnnotationGestureStateKind;\n (function (AnnotationGestureStateKind) {\n AnnotationGestureStateKind[\"Idle\"] = \"idle\";\n AnnotationGestureStateKind[\"Creating\"] = \"creating\";\n AnnotationGestureStateKind[\"Moving\"] = \"moving\";\n AnnotationGestureStateKind[\"Resizing\"] = \"resizing\";\n AnnotationGestureStateKind[\"DragSelecting\"] = \"dragSelecting\";\n })(AnnotationGestureStateKind || (AnnotationGestureStateKind = {}));\n var AnnotationHandleKind;\n (function (AnnotationHandleKind) {\n AnnotationHandleKind[\"Resize\"] = \"resize\";\n AnnotationHandleKind[\"Vertex\"] = \"vertex\";\n AnnotationHandleKind[\"AddVertex\"] = \"addVertex\";\n AnnotationHandleKind[\"Keypoint\"] = \"keypoint\";\n })(AnnotationHandleKind || (AnnotationHandleKind = {}));\n\n /** Alignment for strokes applied to closed rendered geometry. */\n var StrokeAlignment;\n (function (StrokeAlignment) {\n StrokeAlignment[\"Inside\"] = \"inside\";\n StrokeAlignment[\"Center\"] = \"center\";\n StrokeAlignment[\"Outside\"] = \"outside\";\n })(StrokeAlignment || (StrokeAlignment = {}));\n\n var BoxShape;\n (function (BoxShape) {\n BoxShape[\"Rect\"] = \"rect\";\n BoxShape[\"RoundedRect\"] = \"roundedRect\";\n })(BoxShape || (BoxShape = {}));\n\n var FocusTargetMode;\n (function (FocusTargetMode) {\n FocusTargetMode[\"Hovered\"] = \"hovered\";\n FocusTargetMode[\"Selected\"] = \"selected\";\n FocusTargetMode[\"HoveredAndSelected\"] = \"hoveredAndSelected\";\n FocusTargetMode[\"Ambient\"] = \"ambient\";\n })(FocusTargetMode || (FocusTargetMode = {}));\n\n var DetectionInteractionState;\n (function (DetectionInteractionState) {\n DetectionInteractionState[\"Hovered\"] = \"hovered\";\n DetectionInteractionState[\"Selected\"] = \"selected\";\n })(DetectionInteractionState || (DetectionInteractionState = {}));\n\n var LabelPlacement;\n (function (LabelPlacement) {\n LabelPlacement[\"Top\"] = \"top\";\n LabelPlacement[\"Bottom\"] = \"bottom\";\n LabelPlacement[\"InsideTop\"] = \"insideTop\";\n LabelPlacement[\"InsideBottom\"] = \"insideBottom\";\n LabelPlacement[\"Center\"] = \"center\";\n })(LabelPlacement || (LabelPlacement = {}));\n var LabelVisibilityMode;\n (function (LabelVisibilityMode) {\n LabelVisibilityMode[\"Always\"] = \"always\";\n LabelVisibilityMode[\"HoveredOnly\"] = \"hoveredOnly\";\n })(LabelVisibilityMode || (LabelVisibilityMode = {}));\n\n var MaskRenderMode;\n (function (MaskRenderMode) {\n MaskRenderMode[\"FillAndStroke\"] = \"fillAndStroke\";\n MaskRenderMode[\"FillOnly\"] = \"fillOnly\";\n MaskRenderMode[\"StrokeOnly\"] = \"strokeOnly\";\n })(MaskRenderMode || (MaskRenderMode = {}));\n\n /**\n * Discriminator for renderer-neutral shape draw instructions.\n */\n var ShapeInstructionKind;\n (function (ShapeInstructionKind) {\n ShapeInstructionKind[\"Ellipse\"] = \"ellipse\";\n ShapeInstructionKind[\"Marker\"] = \"marker\";\n ShapeInstructionKind[\"Path\"] = \"path\";\n })(ShapeInstructionKind || (ShapeInstructionKind = {}));\n var MarkerShape;\n (function (MarkerShape) {\n MarkerShape[\"Circle\"] = \"circle\";\n MarkerShape[\"Cross\"] = \"cross\";\n MarkerShape[\"Square\"] = \"square\";\n MarkerShape[\"Triangle\"] = \"triangle\";\n })(MarkerShape || (MarkerShape = {}));\n /**\n * Coordinate space for marker sizing. Media sizes scale with the viewport;\n * screen sizes stay constant on screen, matching stroke-width semantics.\n */\n var MarkerSizeSpace;\n (function (MarkerSizeSpace) {\n MarkerSizeSpace[\"Media\"] = \"media\";\n MarkerSizeSpace[\"Screen\"] = \"screen\";\n })(MarkerSizeSpace || (MarkerSizeSpace = {}));\n\n var KeypointMarkerShape;\n (function (KeypointMarkerShape) {\n KeypointMarkerShape[\"Circle\"] = \"circle\";\n KeypointMarkerShape[\"Cross\"] = \"cross\";\n })(KeypointMarkerShape || (KeypointMarkerShape = {}));\n\n var MediaRendererFit;\n (function (MediaRendererFit) {\n /**\n * Preserve media aspect ratio and fit the full frame inside the canvas.\n */\n MediaRendererFit[\"Contain\"] = \"contain\";\n /**\n * Preserve media aspect ratio and fill the canvas, cropping if necessary.\n */\n MediaRendererFit[\"Cover\"] = \"cover\";\n })(MediaRendererFit || (MediaRendererFit = {}));\n /**\n * Playback lifecycle state reported by a platform renderer.\n */\n var MediaRendererPlaybackState;\n (function (MediaRendererPlaybackState) {\n MediaRendererPlaybackState[\"Loading\"] = \"loading\";\n MediaRendererPlaybackState[\"Ready\"] = \"ready\";\n MediaRendererPlaybackState[\"Playing\"] = \"playing\";\n MediaRendererPlaybackState[\"Buffering\"] = \"buffering\";\n MediaRendererPlaybackState[\"Paused\"] = \"paused\";\n MediaRendererPlaybackState[\"Error\"] = \"error\";\n MediaRendererPlaybackState[\"Destroyed\"] = \"destroyed\";\n })(MediaRendererPlaybackState || (MediaRendererPlaybackState = {}));\n /**\n * Lower-level media source readiness.\n */\n var MediaSourceStatus;\n (function (MediaSourceStatus) {\n MediaSourceStatus[\"Loading\"] = \"loading\";\n MediaSourceStatus[\"Ready\"] = \"ready\";\n MediaSourceStatus[\"Error\"] = \"error\";\n MediaSourceStatus[\"Destroyed\"] = \"destroyed\";\n })(MediaSourceStatus || (MediaSourceStatus = {}));\n\n /**\n * Media-session operating mode.\n *\n * Core owns the names because file-like and stream-like lifecycle choices are\n * platform-neutral. Platform packages decide how these modes tune media,\n * storage, and renderer defaults.\n */\n var MediaSessionMode;\n (function (MediaSessionMode) {\n /**\n * Finite media. Defaults usually favor seek/replay and persistent detection\n * storage.\n */\n MediaSessionMode[\"File\"] = \"file\";\n /**\n * Live or append-only media. Defaults usually favor rolling windows and\n * bounded retention.\n */\n MediaSessionMode[\"Stream\"] = \"stream\";\n })(MediaSessionMode || (MediaSessionMode = {}));\n /**\n * Aggregate lifecycle state for a media session.\n */\n var MediaSessionStatus;\n (function (MediaSessionStatus) {\n MediaSessionStatus[\"Buffering\"] = \"buffering\";\n MediaSessionStatus[\"Destroyed\"] = \"destroyed\";\n MediaSessionStatus[\"Error\"] = \"error\";\n MediaSessionStatus[\"Loading\"] = \"loading\";\n MediaSessionStatus[\"Paused\"] = \"paused\";\n MediaSessionStatus[\"Playing\"] = \"playing\";\n MediaSessionStatus[\"Processing\"] = \"processing\";\n MediaSessionStatus[\"Ready\"] = \"ready\";\n })(MediaSessionStatus || (MediaSessionStatus = {}));\n /**\n * Subsystem currently affecting session readiness or presentation.\n */\n var MediaSessionActivityKind;\n (function (MediaSessionActivityKind) {\n MediaSessionActivityKind[\"DetectionsBuffering\"] = \"detectionsBuffering\";\n MediaSessionActivityKind[\"DetectionsLoading\"] = \"detectionsLoading\";\n MediaSessionActivityKind[\"Error\"] = \"error\";\n MediaSessionActivityKind[\"MediaNormalizing\"] = \"mediaNormalizing\";\n MediaSessionActivityKind[\"MediaOpening\"] = \"mediaOpening\";\n MediaSessionActivityKind[\"PlaybackBuffering\"] = \"playbackBuffering\";\n MediaSessionActivityKind[\"RenderPreparing\"] = \"renderPreparing\";\n })(MediaSessionActivityKind || (MediaSessionActivityKind = {}));\n /**\n * State of one session activity.\n */\n var MediaSessionActivityStatus;\n (function (MediaSessionActivityStatus) {\n MediaSessionActivityStatus[\"Error\"] = \"error\";\n MediaSessionActivityStatus[\"Running\"] = \"running\";\n MediaSessionActivityStatus[\"Waiting\"] = \"waiting\";\n })(MediaSessionActivityStatus || (MediaSessionActivityStatus = {}));\n\n let tracker;\n self.addEventListener(\"message\", (event) => {\n const request = event.data;\n try {\n let response;\n if (request.type === \"configure\") {\n switch (request.processor.algorithm) {\n case \"bytetrack\":\n tracker = createByteTrackTracker(request.processor.options);\n break;\n case \"cbiou\":\n tracker = createCBIoUTracker(request.processor.options);\n break;\n case \"ocsort\":\n tracker = createOCSortTracker(request.processor.options);\n break;\n default:\n tracker = createSortTracker(request.processor.options);\n }\n response = { requestId: request.requestId, type: \"success\" };\n }\n else if (request.type === \"reset\") {\n tracker?.reset();\n response = { requestId: request.requestId, type: \"success\" };\n }\n else {\n if (!tracker) {\n throw new Error(\"Tracking worker is not configured.\");\n }\n const startedAt = performance.now();\n const update = tracker.update(request.detections, request.frameIndex);\n response = {\n ...update,\n durationMs: performance.now() - startedAt,\n requestId: request.requestId,\n type: \"success\",\n };\n }\n self.postMessage(response);\n }\n catch (error) {\n self.postMessage({\n message: error instanceof Error ? error.message : \"Tracking worker failed.\",\n requestId: request.requestId,\n type: \"error\",\n });\n }\n });\n\n})();";
462
+
463
+ const DEFAULT_WORKER_NAME$1 = "supervision-detection-post-processing";
464
+ let defaultWorkerUrl$1;
465
+ function createDefaultDetectionPostProcessingWorkerFactory() {
466
+ return {
467
+ createWorker() {
468
+ defaultWorkerUrl$1 ??= URL.createObjectURL(new Blob([EMBEDDED_TRACKING_WORKER_SOURCE], {
469
+ type: "text/javascript",
470
+ }));
471
+ return new Worker(defaultWorkerUrl$1, { name: DEFAULT_WORKER_NAME$1 });
472
+ },
473
+ };
474
+ }
475
+
476
+ function isTrackingWorkerResponse(value) {
477
+ if (!value || typeof value !== "object")
478
+ return false;
479
+ const candidate = value;
480
+ return (typeof candidate.requestId === "number" &&
481
+ (candidate.type === "success" ||
482
+ (candidate.type === "error" && typeof candidate.message === "string")));
483
+ }
484
+
485
+ var DetectionPostProcessingMode;
486
+ (function (DetectionPostProcessingMode) {
487
+ DetectionPostProcessingMode["Auto"] = "auto";
488
+ DetectionPostProcessingMode["Worker"] = "worker";
489
+ DetectionPostProcessingMode["MainThread"] = "mainThread";
490
+ })(DetectionPostProcessingMode || (DetectionPostProcessingMode = {}));
491
+
492
+ function createWorkerRpcClient(options) {
493
+ let failureMessage = null;
494
+ let isDestroyed = false;
495
+ let nextRequestId = 1;
496
+ const pendingRequests = new Map();
497
+ options.worker.addEventListener("message", (event) => {
498
+ handleWorkerMessage(event.data);
499
+ });
500
+ options.worker.addEventListener("error", (event) => {
501
+ rejectPendingRequests(event.message || options.defaultErrorMessage);
502
+ });
503
+ options.worker.addEventListener("messageerror", () => {
504
+ rejectPendingRequests(`${options.defaultErrorMessage}: message error.`);
505
+ });
506
+ return {
507
+ destroy() {
508
+ if (isDestroyed) {
509
+ return;
510
+ }
511
+ isDestroyed = true;
512
+ rejectPendingRequests("Worker RPC client has been destroyed.", {
513
+ markFailure: false,
514
+ });
515
+ options.worker.terminate();
516
+ },
517
+ getFailureMessage() {
518
+ return failureMessage;
519
+ },
520
+ request(message) {
521
+ if (isDestroyed) {
522
+ return Promise.reject(new Error("Worker RPC client has been destroyed."));
523
+ }
524
+ if (failureMessage) {
525
+ return Promise.reject(new Error(failureMessage));
526
+ }
527
+ const request = {
528
+ ...message,
529
+ requestId: nextRequestId,
530
+ };
531
+ nextRequestId += 1;
532
+ return new Promise((resolve, reject) => {
533
+ pendingRequests.set(request.requestId, { reject, resolve });
534
+ try {
535
+ options.worker.postMessage(request);
536
+ }
537
+ catch (error) {
538
+ pendingRequests.delete(request.requestId);
539
+ failureMessage = getErrorMessage$2(error, options.defaultErrorMessage);
540
+ reject(new Error(failureMessage));
541
+ }
542
+ });
543
+ },
544
+ };
545
+ function handleWorkerMessage(message) {
546
+ if (!options.isResponse(message)) {
547
+ return;
548
+ }
549
+ const pendingRequest = pendingRequests.get(message.requestId);
550
+ if (!pendingRequest) {
551
+ options.onOrphanedResponse?.(message);
552
+ return;
553
+ }
554
+ pendingRequests.delete(message.requestId);
555
+ pendingRequest.resolve(message);
556
+ }
557
+ function rejectPendingRequests(message, options = { markFailure: true }) {
558
+ if (options.markFailure) {
559
+ failureMessage = message;
560
+ }
561
+ for (const pendingRequest of pendingRequests.values()) {
562
+ pendingRequest.reject(new Error(message));
563
+ }
564
+ pendingRequests.clear();
565
+ }
566
+ }
567
+ function getErrorMessage$2(error, fallback) {
568
+ return error instanceof Error ? error.message : fallback;
569
+ }
570
+
571
+ const DEFAULT_MAX_PENDING_FRAMES = 90;
572
+ /**
573
+ * Creates an ordered, bounded stream processor for detections arriving out of
574
+ * order. Stateful processors run serially; the renderer remains independent.
575
+ */
576
+ function createDetectionPostProcessingPipeline(options) {
577
+ const processor = validateProcessors(options.processors);
578
+ const maxPendingFrames = normalizePositiveInteger(options.maxPendingFrames ?? DEFAULT_MAX_PENDING_FRAMES, "maxPendingFrames");
579
+ let nextFrameIndex = normalizeFrameIndex(options.startFrameIndex ?? 0, "startFrameIndex");
580
+ let execution;
581
+ let destroyed = false;
582
+ let failedError;
583
+ let inFlightFrameIndex;
584
+ let processing = Promise.resolve({
585
+ acceptedFrameCount: 0,
586
+ processedFrameCount: 0,
587
+ processedFrames: [],
588
+ });
589
+ const pendingFrames = new Map();
590
+ let diagnostics = {
591
+ activeTrackCount: 0,
592
+ confirmedTrackCount: 0,
593
+ errorMessage: null,
594
+ executionMode: null,
595
+ lastFrameDurationMs: null,
596
+ nextFrameIndex,
597
+ pendingFrameCount: 0,
598
+ processedFrameCount: 0,
599
+ trackedDetectionCount: 0,
600
+ };
601
+ publishDiagnostics();
602
+ return {
603
+ async appendFrames(frames) {
604
+ assertActive();
605
+ assertHealthy();
606
+ const acceptedFrameCount = enqueueFrames(frames);
607
+ processing = processing.then(() => drainPendingFrames(acceptedFrameCount));
608
+ return processing;
609
+ },
610
+ destroy() {
611
+ if (destroyed)
612
+ return;
613
+ destroyed = true;
614
+ pendingFrames.clear();
615
+ execution?.destroy();
616
+ execution = undefined;
617
+ },
618
+ getDiagnostics() {
619
+ return { ...diagnostics };
620
+ },
621
+ async reset(resetOptions = {}) {
622
+ assertActive();
623
+ await processing.catch(() => undefined);
624
+ pendingFrames.clear();
625
+ inFlightFrameIndex = undefined;
626
+ nextFrameIndex = normalizeFrameIndex(resetOptions.startFrameIndex ?? options.startFrameIndex ?? 0, "startFrameIndex");
627
+ await execution?.reset();
628
+ failedError = undefined;
629
+ processing = Promise.resolve({
630
+ acceptedFrameCount: 0,
631
+ processedFrameCount: 0,
632
+ processedFrames: [],
633
+ });
634
+ diagnostics = {
635
+ ...diagnostics,
636
+ activeTrackCount: 0,
637
+ confirmedTrackCount: 0,
638
+ errorMessage: null,
639
+ lastFrameDurationMs: null,
640
+ nextFrameIndex,
641
+ pendingFrameCount: 0,
642
+ processedFrameCount: 0,
643
+ trackedDetectionCount: 0,
644
+ };
645
+ publishDiagnostics();
646
+ },
647
+ };
648
+ function enqueueFrames(frames) {
649
+ let acceptedFrameCount = 0;
650
+ const additions = new Map();
651
+ for (const frame of frames) {
652
+ const frameIndex = normalizeFrameIndex(frame.frameIndex, "frameIndex");
653
+ if (frameIndex < nextFrameIndex) {
654
+ throw new Error(`Frame ${frameIndex} is behind the processed frontier ${nextFrameIndex}. Reset before revising causal history.`);
655
+ }
656
+ if (frameIndex === inFlightFrameIndex) {
657
+ throw new Error(`Frame ${frameIndex} is already being processed. Reset before revising causal history.`);
658
+ }
659
+ if (!pendingFrames.has(frameIndex) && !additions.has(frameIndex)) {
660
+ acceptedFrameCount += 1;
661
+ }
662
+ additions.set(frameIndex, frame);
663
+ }
664
+ const nextPendingCount = pendingFrames.size + acceptedFrameCount;
665
+ if (nextPendingCount > maxPendingFrames) {
666
+ throw new Error(`Out-of-order buffer limit exceeded (${nextPendingCount}/${maxPendingFrames}). Apply upstream backpressure or increase maxPendingFrames.`);
667
+ }
668
+ for (const [frameIndex, frame] of additions) {
669
+ pendingFrames.set(frameIndex, frame);
670
+ }
671
+ diagnostics = {
672
+ ...diagnostics,
673
+ nextFrameIndex,
674
+ pendingFrameCount: pendingFrames.size,
675
+ };
676
+ publishDiagnostics();
677
+ return acceptedFrameCount;
678
+ }
679
+ async function drainPendingFrames(acceptedFrameCount) {
680
+ const processedFrames = [];
681
+ try {
682
+ execution ??= await createTrackingExecution(processor, options);
683
+ diagnostics = { ...diagnostics, executionMode: execution.mode };
684
+ while (!destroyed) {
685
+ const frame = pendingFrames.get(nextFrameIndex);
686
+ if (!frame)
687
+ break;
688
+ pendingFrames.delete(nextFrameIndex);
689
+ inFlightFrameIndex = nextFrameIndex;
690
+ const projections = projectDetectionFrameForTracking(frame, processor.geometry);
691
+ const result = await execution.process(projections, nextFrameIndex);
692
+ const processedFrame = attachTrackerIds(frame, result.assignments, options.mutateInput ?? true);
693
+ if (destroyed)
694
+ break;
695
+ await options.output?.appendFrames([processedFrame]);
696
+ options.onFrame?.(processedFrame);
697
+ processedFrames.push(processedFrame);
698
+ nextFrameIndex += 1;
699
+ inFlightFrameIndex = undefined;
700
+ diagnostics = {
701
+ ...diagnostics,
702
+ activeTrackCount: result.activeTrackCount,
703
+ confirmedTrackCount: result.confirmedTrackCount,
704
+ errorMessage: null,
705
+ lastFrameDurationMs: result.durationMs,
706
+ nextFrameIndex,
707
+ pendingFrameCount: pendingFrames.size,
708
+ processedFrameCount: diagnostics.processedFrameCount + 1,
709
+ trackedDetectionCount: diagnostics.trackedDetectionCount + result.assignments.length,
710
+ };
711
+ publishDiagnostics();
712
+ }
713
+ return {
714
+ acceptedFrameCount,
715
+ processedFrameCount: processedFrames.length,
716
+ processedFrames,
717
+ };
718
+ }
719
+ catch (error) {
720
+ failedError =
721
+ error instanceof Error ? error : new Error("Post-processing failed.");
722
+ diagnostics = {
723
+ ...diagnostics,
724
+ errorMessage: failedError.message,
725
+ pendingFrameCount: pendingFrames.size,
726
+ };
727
+ publishDiagnostics();
728
+ throw failedError;
729
+ }
730
+ }
731
+ function publishDiagnostics() {
732
+ options.onDiagnostics?.({ ...diagnostics });
733
+ }
734
+ function assertActive() {
735
+ if (destroyed) {
736
+ throw new Error("Detection post-processing pipeline has been destroyed.");
737
+ }
738
+ }
739
+ function assertHealthy() {
740
+ if (failedError) {
741
+ throw new Error(`Detection post-processing pipeline failed and must be reset: ${failedError.message}`);
742
+ }
743
+ }
744
+ }
745
+ async function createTrackingExecution(processor, options) {
746
+ const mode = options.mode ?? DetectionPostProcessingMode.Auto;
747
+ if (mode === DetectionPostProcessingMode.MainThread) {
748
+ return createMainThreadExecution(processor);
749
+ }
750
+ let workerExecution;
751
+ try {
752
+ const workerFactory = options.workerFactory ??
753
+ createDefaultDetectionPostProcessingWorkerFactory();
754
+ workerExecution = createWorkerExecution(workerFactory.createWorker(), processor);
755
+ await workerExecution.reset();
756
+ return workerExecution;
757
+ }
758
+ catch (error) {
759
+ workerExecution?.destroy();
760
+ if (mode === DetectionPostProcessingMode.Worker)
761
+ throw error;
762
+ return createMainThreadExecution(processor);
763
+ }
764
+ }
765
+ function createMainThreadExecution(processor) {
766
+ let tracker;
767
+ switch (processor.algorithm) {
768
+ case "bytetrack":
769
+ tracker = createByteTrackTracker(processor.options);
770
+ break;
771
+ case "cbiou":
772
+ tracker = createCBIoUTracker(processor.options);
773
+ break;
774
+ case "ocsort":
775
+ tracker = createOCSortTracker(processor.options);
776
+ break;
777
+ default:
778
+ tracker = createSortTracker(processor.options);
779
+ }
780
+ return {
781
+ destroy() { },
782
+ mode: "mainThread",
783
+ async process(detections, frameIndex) {
784
+ const startedAt = performance.now();
785
+ const update = tracker.update(detections, frameIndex);
786
+ return { ...update, durationMs: performance.now() - startedAt };
787
+ },
788
+ async reset() {
789
+ tracker.reset();
790
+ },
791
+ };
792
+ }
793
+ function createWorkerExecution(worker, processor) {
794
+ const client = createWorkerRpcClient({
795
+ defaultErrorMessage: "Detection post-processing worker failed.",
796
+ isResponse: isTrackingWorkerResponse,
797
+ worker,
798
+ });
799
+ let configured = false;
800
+ const request = async (message) => {
801
+ const response = await client.request(message);
802
+ if (response.type === "error")
803
+ throw new Error(response.message);
804
+ return response;
805
+ };
806
+ return {
807
+ destroy() {
808
+ client.destroy();
809
+ },
810
+ mode: "worker",
811
+ async process(detections, frameIndex) {
812
+ if (!configured) {
813
+ await request({ processor, type: "configure" });
814
+ configured = true;
815
+ }
816
+ const response = await request({
817
+ detections,
818
+ frameIndex,
819
+ type: "process",
820
+ });
821
+ return {
822
+ activeTrackCount: response.activeTrackCount ?? 0,
823
+ assignments: response.assignments ?? [],
824
+ confirmedTrackCount: response.confirmedTrackCount ?? 0,
825
+ durationMs: response.durationMs ?? 0,
826
+ };
827
+ },
828
+ async reset() {
829
+ if (!configured) {
830
+ await request({ processor, type: "configure" });
831
+ configured = true;
832
+ }
833
+ else {
834
+ await request({ type: "reset" });
835
+ }
836
+ },
837
+ };
838
+ }
839
+ function attachTrackerIds(frame, assignments, mutateInput) {
840
+ const trackerIds = new Map(assignments.map(({ detectionIndex, trackerId }) => [
841
+ detectionIndex,
842
+ trackerId,
843
+ ]));
844
+ for (const [detectionIndex, detection] of frame.detections.entries()) {
845
+ if (mutateInput) {
846
+ const trackerId = trackerIds.get(detectionIndex);
847
+ if (trackerId === undefined) {
848
+ Reflect.deleteProperty(detection, "trackerId");
849
+ }
850
+ else {
851
+ detection.trackerId = trackerId;
852
+ }
853
+ }
854
+ }
855
+ if (mutateInput) {
856
+ return frame;
857
+ }
858
+ return {
859
+ ...frame,
860
+ detections: frame.detections.map((detection, detectionIndex) => {
861
+ const semanticDetection = { ...detection };
862
+ Reflect.deleteProperty(semanticDetection, "trackerId");
863
+ const trackerId = trackerIds.get(detectionIndex);
864
+ return trackerId === undefined
865
+ ? semanticDetection
866
+ : { ...semanticDetection, trackerId };
867
+ }),
868
+ };
869
+ }
870
+ function validateProcessors(processors) {
871
+ if (processors.length !== 1 || processors[0]?.kind !== "tracking") {
872
+ throw new Error("The first post-processing release requires exactly one tracking processor.");
873
+ }
874
+ return processors[0];
875
+ }
876
+ function normalizePositiveInteger(value, label) {
877
+ if (!Number.isInteger(value) || value < 1) {
878
+ throw new Error(`${label} must be a positive integer.`);
879
+ }
880
+ return value;
881
+ }
882
+ function normalizeFrameIndex(value, label) {
883
+ if (!Number.isInteger(value) || value === undefined || value < 0) {
884
+ throw new Error(`${label} must be a non-negative integer.`);
885
+ }
886
+ return value;
887
+ }
888
+
461
889
  async function collectInputMetadata(input, source) {
462
890
  const [format, detectedMimeType, duration, primaryVideoTrack] = await Promise.all([
463
891
  input.getFormat(),
@@ -2239,11 +2667,15 @@ async function createMediaRendererCore(options, providers) {
2239
2667
  annotationOverlayStyle: options.annotationOverlayStyle,
2240
2668
  backgroundColor: options.backgroundColor,
2241
2669
  boxStyle: options.boxStyle,
2670
+ boxCornerStyle: options.boxCornerStyle,
2671
+ ellipseStyle: options.ellipseStyle,
2242
2672
  focusStyle: options.focusStyle,
2243
2673
  interactionStyle: options.interactionStyle,
2244
2674
  keypointStyle: options.keypointStyle,
2245
2675
  labelStyle: options.labelStyle,
2676
+ maskHaloStyle: options.maskHaloStyle,
2246
2677
  maskStyle: options.maskStyle,
2678
+ markerStyle: options.markerStyle,
2247
2679
  polygonStyle: options.polygonStyle,
2248
2680
  polylineStyle: options.polylineStyle,
2249
2681
  renderers: options.renderers,
@@ -2538,6 +2970,8 @@ async function createMediaRendererCore(options, providers) {
2538
2970
  annotationOverlayStyle: currentPresentation.annotationOverlayStyle,
2539
2971
  backgroundColor: currentPresentation.backgroundColor,
2540
2972
  boxStyle: currentPresentation.boxStyle,
2973
+ boxCornerStyle: currentPresentation.boxCornerStyle,
2974
+ ellipseStyle: currentPresentation.ellipseStyle,
2541
2975
  canInteract: () => canInteract(options, runtimeState.isPlaybackActive()),
2542
2976
  container: options.container,
2543
2977
  detectionTimeline,
@@ -2550,10 +2984,18 @@ async function createMediaRendererCore(options, providers) {
2550
2984
  keypointStyle: currentPresentation.keypointStyle,
2551
2985
  labelStyle: currentPresentation.labelStyle,
2552
2986
  maskBrush: options.createMaskBrush?.(mediaDimensions) ?? options.maskBrush,
2987
+ maskHaloStyle: currentPresentation.maskHaloStyle,
2553
2988
  maskStyle: currentPresentation.maskStyle,
2989
+ markerStyle: currentPresentation.markerStyle,
2554
2990
  maxDevicePixelRatio: options.maxDevicePixelRatio,
2991
+ onPresentationUpdate(presentedSample) {
2992
+ if (!runtimeState.isDestroyed()) {
2993
+ runtimeState.recordPresentationUpdate(presentedSample);
2994
+ }
2995
+ },
2555
2996
  polygonStyle: currentPresentation.polygonStyle,
2556
2997
  polylineStyle: currentPresentation.polylineStyle,
2998
+ regionRenderers: resolveRegionRenderers(currentPresentation),
2557
2999
  previewOverlay: options.previewOverlay,
2558
3000
  renderPreparation: options.renderPreparation
2559
3001
  ? {
@@ -2699,6 +3141,10 @@ async function createMediaRendererCore(options, providers) {
2699
3141
  }
2700
3142
  }
2701
3143
  }
3144
+ function resolveRegionRenderers(presentation) {
3145
+ return (presentation.renderers?.filter((renderer) => renderer.kind === "region") ??
3146
+ []);
3147
+ }
2702
3148
  async function openRendererMediaSource(options, providers) {
2703
3149
  if (options.src !== undefined && options.source !== undefined) {
2704
3150
  throw new Error("Provide either src or source, not both.");
@@ -2792,10 +3238,10 @@ function drawPixiPath(graphics, points, closed, stroke, viewportScale) {
2792
3238
  if (points.length < 2) {
2793
3239
  return;
2794
3240
  }
2795
- if (stroke.dash?.length) {
2796
- appendDashedPath(graphics, points, closed, stroke.dash, viewportScale);
2797
- }
2798
- else {
3241
+ const appendedDash = stroke.dash?.length
3242
+ ? appendDashedPath(graphics, points, closed, stroke.dash, viewportScale)
3243
+ : false;
3244
+ if (!appendedDash) {
2799
3245
  graphics.moveTo(points[0].x, points[0].y);
2800
3246
  for (let index = 1; index < points.length; index += 1) {
2801
3247
  graphics.lineTo(points[index].x, points[index].y);
@@ -2809,16 +3255,21 @@ function drawPixiPath(graphics, points, closed, stroke, viewportScale) {
2809
3255
  function resolvePixiStroke(stroke, viewportScale) {
2810
3256
  const pixiStroke = {
2811
3257
  alpha: stroke.alpha,
3258
+ ...(stroke.cap === undefined ? {} : { cap: stroke.cap }),
2812
3259
  color: stroke.color,
3260
+ ...(stroke.join === undefined ? {} : { join: stroke.join }),
3261
+ ...(stroke.miterLimit === undefined
3262
+ ? {}
3263
+ : { miterLimit: stroke.miterLimit }),
2813
3264
  width: resolveScreenLength(stroke.width, viewportScale),
2814
3265
  };
2815
3266
  if (stroke.alignment === undefined)
2816
3267
  return pixiStroke;
2817
3268
  return {
2818
3269
  ...pixiStroke,
2819
- alignment: stroke.alignment === BoxStrokeAlignment.Inside
3270
+ alignment: stroke.alignment === StrokeAlignment.Inside
2820
3271
  ? 1
2821
- : stroke.alignment === BoxStrokeAlignment.Outside
3272
+ : stroke.alignment === StrokeAlignment.Outside
2822
3273
  ? 0
2823
3274
  : 0.5,
2824
3275
  };
@@ -2828,7 +3279,7 @@ function appendDashedPath(graphics, points, closed, dashPattern, viewportScale)
2828
3279
  .filter((value) => Number.isFinite(value) && value > 0)
2829
3280
  .map((value) => resolveScreenLength(value, viewportScale));
2830
3281
  if (pattern.length === 0) {
2831
- return;
3282
+ return false;
2832
3283
  }
2833
3284
  if (pattern.length % 2 === 1) {
2834
3285
  pattern.push(...pattern);
@@ -2836,6 +3287,7 @@ function appendDashedPath(graphics, points, closed, dashPattern, viewportScale)
2836
3287
  let patternIndex = 0;
2837
3288
  let patternRemaining = pattern[0];
2838
3289
  let drawing = true;
3290
+ let penDown = false;
2839
3291
  const segmentCount = closed ? points.length : points.length - 1;
2840
3292
  for (let segmentIndex = 0; segmentIndex < segmentCount; segmentIndex += 1) {
2841
3293
  const start = points[segmentIndex];
@@ -2856,8 +3308,14 @@ function appendDashedPath(graphics, points, closed, dashPattern, viewportScale)
2856
3308
  const x2 = start.x + dx * endRatio;
2857
3309
  const y2 = start.y + dy * endRatio;
2858
3310
  if (drawing) {
2859
- graphics.moveTo(x1, y1);
3311
+ if (!penDown) {
3312
+ graphics.moveTo(x1, y1);
3313
+ }
2860
3314
  graphics.lineTo(x2, y2);
3315
+ penDown = true;
3316
+ }
3317
+ else {
3318
+ penDown = false;
2861
3319
  }
2862
3320
  offset += step;
2863
3321
  patternRemaining -= step;
@@ -2865,9 +3323,13 @@ function appendDashedPath(graphics, points, closed, dashPattern, viewportScale)
2865
3323
  patternIndex = (patternIndex + 1) % pattern.length;
2866
3324
  patternRemaining = pattern[patternIndex];
2867
3325
  drawing = !drawing;
3326
+ if (!drawing) {
3327
+ penDown = false;
3328
+ }
2868
3329
  }
2869
3330
  }
2870
3331
  }
3332
+ return true;
2871
3333
  }
2872
3334
 
2873
3335
  function createPixiBoxLayer(options) {
@@ -3104,13 +3566,19 @@ function createPixiFocusLayer(options) {
3104
3566
  hide();
3105
3567
  return;
3106
3568
  }
3107
- const instruction = focusStyle.resolve({
3569
+ const resolvedInstruction = focusStyle.resolve({
3108
3570
  frame: context.frame,
3109
3571
  hoveredPick: context.hoveredPick,
3110
3572
  mediaTime: context.mediaTime,
3111
3573
  selectedPick: context.selectedPick,
3112
3574
  viewportScale: context.viewportScale,
3113
3575
  });
3576
+ const instruction = resolvedInstruction
3577
+ ? {
3578
+ ...resolvedInstruction,
3579
+ targets: resolvedInstruction.targets.filter(({ detection }) => options.isDetectionVisible?.(detection) ?? true),
3580
+ }
3581
+ : undefined;
3114
3582
  if (!instruction || instruction.targets.length === 0) {
3115
3583
  transitionToHidden();
3116
3584
  return;
@@ -3322,7 +3790,7 @@ function createFocusIdMaskRenderer(options) {
3322
3790
  autoGenerateMipmaps: false,
3323
3791
  dynamic: false,
3324
3792
  height: 1,
3325
- resource: createPlaceholderCanvas$2(),
3793
+ resource: createPlaceholderCanvas$3(),
3326
3794
  scaleMode: "nearest",
3327
3795
  width: 1,
3328
3796
  });
@@ -3414,7 +3882,7 @@ function createPremultipliedColor(fill) {
3414
3882
  alpha,
3415
3883
  ]);
3416
3884
  }
3417
- function createPlaceholderCanvas$2() {
3885
+ function createPlaceholderCanvas$3() {
3418
3886
  const canvas = document.createElement("canvas");
3419
3887
  canvas.height = 1;
3420
3888
  canvas.width = 1;
@@ -3536,15 +4004,21 @@ function createPixiInteractionLayer(options) {
3536
4004
  drawFrame(mediaTime) {
3537
4005
  currentMediaTime = mediaTime;
3538
4006
  activeFrame = options.detectionTimeline.selectFrame(mediaTime);
3539
- if (!canHandleInteraction()) {
3540
- pointerPoint = null;
3541
- marqueeRect = null;
3542
- setHoveredPick(null);
3543
- setSelectedPick(null);
3544
- }
3545
- else {
4007
+ if (canHandleInteraction()) {
3546
4008
  clearStalePicks(activeFrame);
4009
+ return;
4010
+ }
4011
+ pointerPoint = null;
4012
+ marqueeRect = null;
4013
+ setHoveredPick(null);
4014
+ if (isDestroyed || mode === MediaInteractionMode.Disabled) {
4015
+ setSelectedPick(null);
4016
+ return;
3547
4017
  }
4018
+ // Paused-only interaction gates new picks during playback, but an
4019
+ // existing selection keeps following its detection so selection-driven
4020
+ // presentation such as focus survives frame advances.
4021
+ followSelectedPicks(activeFrame);
3548
4022
  },
3549
4023
  setSelectedDetection(selection) {
3550
4024
  if (selection === null) {
@@ -3563,14 +4037,15 @@ function createPixiInteractionLayer(options) {
3563
4037
  return createState();
3564
4038
  },
3565
4039
  cycleSelection(direction = 1) {
3566
- if (!activeFrame?.detections.length)
4040
+ const visibleDetections = activeFrame?.detections.flatMap((detection, detectionIndex) => canPickDetection(detection) ? [{ detection, detectionIndex }] : []);
4041
+ if (!activeFrame || !visibleDetections?.length)
3567
4042
  return null;
3568
- const currentIndex = selectedPick?.detectionIndex ?? (direction > 0 ? -1 : 0);
3569
- const nextIndex = (currentIndex + direction + activeFrame.detections.length) %
3570
- activeFrame.detections.length;
3571
- const detection = activeFrame.detections[nextIndex];
4043
+ const currentVisibleIndex = visibleDetections.findIndex(({ detectionIndex }) => detectionIndex === selectedPick?.detectionIndex);
4044
+ const nextVisibleIndex = (currentVisibleIndex + direction + visibleDetections.length) %
4045
+ visibleDetections.length;
4046
+ const { detection, detectionIndex } = visibleDetections[nextVisibleIndex];
3572
4047
  const next = createPickFromSelection({
3573
- detectionIndex: nextIndex,
4048
+ detectionIndex,
3574
4049
  point: getDetectionCenter(detection),
3575
4050
  });
3576
4051
  setSelectedPick(next);
@@ -3699,6 +4174,8 @@ function createPixiInteractionLayer(options) {
3699
4174
  return;
3700
4175
  if (didDragMarquee && marqueeRect && activeFrame) {
3701
4176
  selectedPicks = activeFrame.detections.flatMap((detection, detectionIndex) => {
4177
+ if (!canPickDetection(detection))
4178
+ return [];
3702
4179
  const rect = getDetectionRect(detection);
3703
4180
  if (!rect || !intersects(rect, marqueeRect))
3704
4181
  return [];
@@ -3735,10 +4212,13 @@ function createPixiInteractionLayer(options) {
3735
4212
  const pickPoint = { x: point.x, y: point.y };
3736
4213
  const labelPick = options.pickLabelDetectionAtPoint?.(pickPoint, currentMediaTime);
3737
4214
  const activeLabelPick = rebaseDetectionPickToFrame(labelPick ?? null, activeFrame);
3738
- if (activeLabelPick)
4215
+ if (activeLabelPick && canPickDetection(activeLabelPick.detection)) {
3739
4216
  return activeLabelPick;
4217
+ }
3740
4218
  const geometryPick = pickDetectionAtPoint(activeFrame, pickPoint, {
3741
4219
  ...options.interaction,
4220
+ filter: (detection, detectionIndex) => canPickDetection(detection) &&
4221
+ (options.interaction.filter?.(detection, detectionIndex) ?? true),
3742
4222
  includeMasks: options.pickMaskDetectionAtPoint === undefined,
3743
4223
  maskMediaDimensions: options.getMediaDimensions?.(),
3744
4224
  padding: pickPadding,
@@ -3748,7 +4228,9 @@ function createPixiInteractionLayer(options) {
3748
4228
  }
3749
4229
  const maskPick = options.pickMaskDetectionAtPoint?.(pickPoint, currentMediaTime);
3750
4230
  const activeMaskPick = rebaseDetectionPickToFrame(maskPick ?? null, activeFrame);
3751
- return activeMaskPick ?? geometryPick;
4231
+ return activeMaskPick && canPickDetection(activeMaskPick.detection)
4232
+ ? activeMaskPick
4233
+ : geometryPick;
3752
4234
  }
3753
4235
  function createPickFromSelection(selection) {
3754
4236
  const frame = selection.mediaTime === undefined
@@ -3760,7 +4242,10 @@ function createPixiInteractionLayer(options) {
3760
4242
  const detection = detectionIndex === undefined || detectionIndex < 0
3761
4243
  ? undefined
3762
4244
  : frame?.detections[detectionIndex];
3763
- if (!frame || detectionIndex === undefined || !detection) {
4245
+ if (!frame ||
4246
+ detectionIndex === undefined ||
4247
+ !detection ||
4248
+ !canPickDetection(detection)) {
3764
4249
  return null;
3765
4250
  }
3766
4251
  return {
@@ -3774,19 +4259,44 @@ function createPixiInteractionLayer(options) {
3774
4259
  };
3775
4260
  }
3776
4261
  function clearStalePicks(frame) {
3777
- const nextHoveredPick = rebaseDetectionPickToFrame(hoveredPick, frame);
3778
- const nextSelectedPick = rebaseDetectionPickToFrame(selectedPick, frame);
4262
+ // Hover stays bound to the pointer, so it keeps a per-frame lifetime.
4263
+ const nextHoveredPick = filterPick(rebaseDetectionPickToFrame(hoveredPick, frame));
3779
4264
  if (hoveredPick && !nextHoveredPick) {
3780
4265
  setHoveredPick(null);
3781
4266
  }
3782
4267
  else if (nextHoveredPick && nextHoveredPick !== hoveredPick) {
3783
- setHoveredPick(nextHoveredPick);
4268
+ // Hover remains pointer-owned. Refresh its frame snapshot without
4269
+ // emitting a second event-driven redraw from the normal frame path.
4270
+ hoveredPick = nextHoveredPick;
4271
+ hoveredPickKey = createDetectionPickKey(nextHoveredPick);
3784
4272
  }
3785
- if (selectedPick && !nextSelectedPick) {
3786
- setSelectedPick(null);
4273
+ followSelectedPicks(frame);
4274
+ }
4275
+ function followSelectedPicks(frame) {
4276
+ if (selectedPicks.length === 0) {
4277
+ return;
4278
+ }
4279
+ const nextSelectedPicks = selectedPicks.flatMap((pick) => {
4280
+ const nextPick = filterPick(followDetectionPickAcrossFrames(pick, frame));
4281
+ return nextPick ? [nextPick] : [];
4282
+ });
4283
+ const currentKeys = selectedPicks.map(createDetectionPickKey).join("|");
4284
+ const nextKeys = nextSelectedPicks.map(createDetectionPickKey).join("|");
4285
+ const identityChanged = !haveSameDetectionPickIdentities(selectedPicks, nextSelectedPicks);
4286
+ selectedPicks = nextSelectedPicks;
4287
+ selectedPick = nextSelectedPicks.at(-1) ?? null;
4288
+ if (nextKeys === currentKeys) {
4289
+ return;
3787
4290
  }
3788
- else if (nextSelectedPick && nextSelectedPick !== selectedPick) {
3789
- setSelectedPick(nextSelectedPick);
4291
+ selectedPickKey = createDetectionPickKey(selectedPick);
4292
+ // A followed pick re-bases onto every new frame, so its full pick key
4293
+ // changes at playback rate. The scene's normal frame path consumes this
4294
+ // refreshed state immediately; emitting onStateChange here would redraw
4295
+ // focus and interaction a second time. Public callbacks only report
4296
+ // identity or membership changes.
4297
+ if (identityChanged) {
4298
+ options.interaction.onSelect?.(selectedPick);
4299
+ options.interaction.onSelectionChange?.(selectedPicks);
3790
4300
  }
3791
4301
  }
3792
4302
  function setHoveredPick(nextPick) {
@@ -3895,6 +4405,12 @@ function createPixiInteractionLayer(options) {
3895
4405
  }
3896
4406
  return options.canInteract();
3897
4407
  }
4408
+ function canPickDetection(detection) {
4409
+ return options.canPickDetection?.(detection) ?? true;
4410
+ }
4411
+ function filterPick(pick) {
4412
+ return pick && canPickDetection(pick.detection) ? pick : null;
4413
+ }
3898
4414
  }
3899
4415
  function getDetectionCenter(detection) {
3900
4416
  const rect = getDetectionRect(detection);
@@ -3940,8 +4456,11 @@ function compositeMaskFrame(instructions) {
3940
4456
  }
3941
4457
  return { data, height, width };
3942
4458
  }
4459
+ function createMaskIdFrame(instructions) {
4460
+ return createIdMaskFrame(materializeMaskInstructions(instructions));
4461
+ }
3943
4462
  async function createPngIdMaskFrame(instructions) {
3944
- const frame = createIdMaskFrame(materializeMaskInstructions(instructions));
4463
+ const frame = createMaskIdFrame(instructions);
3945
4464
  if (!frame) {
3946
4465
  return undefined;
3947
4466
  }
@@ -4480,6 +4999,9 @@ function createPixiVectorLayer(options) {
4480
4999
  let keypointStyle = options.keypointStyle === undefined
4481
5000
  ? new BaseKeypointStyle()
4482
5001
  : options.keypointStyle;
5002
+ // Shape decorations are opt-in; no default style exists so configuring
5003
+ // nothing keeps the semantic-geometry skip untouched.
5004
+ let shapeStyle = options.shapeStyle ?? null;
4483
5005
  let styleVersion = 0;
4484
5006
  let drawnStyleVersion = -1;
4485
5007
  // A versioned source can replace a frame without changing its timeline key.
@@ -4516,7 +5038,8 @@ function createPixiVectorLayer(options) {
4516
5038
  const resolvedDetections = frame.detections.flatMap((detection, detectionIndex) => {
4517
5039
  if (!detection.polygon &&
4518
5040
  !detection.polyline &&
4519
- !detection.keypoints) {
5041
+ !detection.keypoints &&
5042
+ !shapeStyle) {
4520
5043
  return [];
4521
5044
  }
4522
5045
  const resolved = resolveDetection(detection, detectionIndex, frame, mediaTime, viewportScale);
@@ -4551,6 +5074,8 @@ function createPixiVectorLayer(options) {
4551
5074
  polylineStyle = styles.polylineStyle;
4552
5075
  if (styles.keypointStyle !== undefined)
4553
5076
  keypointStyle = styles.keypointStyle;
5077
+ if (styles.shapeStyle !== undefined)
5078
+ shapeStyle = styles.shapeStyle;
4554
5079
  styleVersion += 1;
4555
5080
  },
4556
5081
  destroy() {
@@ -4595,13 +5120,16 @@ function createPixiVectorLayer(options) {
4595
5120
  const polygon = polygonStyle?.resolve(detection, context);
4596
5121
  const polyline = polylineStyle?.resolve(detection, context);
4597
5122
  const keypoints = keypointStyle?.resolve(detection, context);
4598
- if (!polygon && !polyline && !keypoints)
5123
+ const shapes = shapeStyle?.resolve(detection, context);
5124
+ if (!polygon && !polyline && !keypoints && !shapes?.length) {
4599
5125
  return undefined;
5126
+ }
4600
5127
  return {
4601
5128
  key: detectionKey(detection, detectionIndex),
4602
5129
  keypoints,
4603
5130
  polygon,
4604
5131
  polyline,
5132
+ shapes,
4605
5133
  zIndex: detection.zIndex ?? detectionIndex,
4606
5134
  };
4607
5135
  }
@@ -4610,7 +5138,10 @@ function createPixiVectorLayer(options) {
4610
5138
  if (!entry.cleared)
4611
5139
  graphics.clear();
4612
5140
  entry.cleared = false;
4613
- const { keypoints, polygon, polyline } = detection;
5141
+ const { keypoints, polygon, polyline, shapes } = detection;
5142
+ for (const shape of shapes ?? []) {
5143
+ drawShapeInstruction(graphics, shape, viewportScale);
5144
+ }
4614
5145
  if (polygon) {
4615
5146
  graphics.poly(polygon.points.flatMap(({ x, y }) => [x, y]), true);
4616
5147
  if (polygon.fill)
@@ -4628,31 +5159,102 @@ function createPixiVectorLayer(options) {
4628
5159
  drawPixiPath(graphics, [edge.from, edge.to], false, edge.stroke, viewportScale);
4629
5160
  }
4630
5161
  for (const marker of keypoints.markers) {
4631
- const radius = resolveScreenLength(marker.radius, viewportScale);
4632
5162
  if (marker.shape === KeypointMarkerShape.Cross) {
4633
- drawPixiPath(graphics, [
4634
- { x: marker.point.x - radius, y: marker.point.y - radius },
4635
- { x: marker.point.x + radius, y: marker.point.y + radius },
4636
- ], false, marker.stroke ?? { alpha: 1, color: 0xffffff, width: 2 }, viewportScale);
4637
- drawPixiPath(graphics, [
4638
- { x: marker.point.x + radius, y: marker.point.y - radius },
4639
- { x: marker.point.x - radius, y: marker.point.y + radius },
4640
- ], false, marker.stroke ?? { alpha: 1, color: 0xffffff, width: 2 }, viewportScale);
5163
+ drawMarkerInstruction(graphics, {
5164
+ center: marker.point,
5165
+ kind: ShapeInstructionKind.Marker,
5166
+ shape: MarkerShape.Cross,
5167
+ size: marker.radius * 2,
5168
+ sizeSpace: MarkerSizeSpace.Screen,
5169
+ stroke: asOpenStroke(marker.stroke ?? { alpha: 1, color: 0xffffff, width: 2 }),
5170
+ }, viewportScale);
4641
5171
  }
4642
5172
  else {
4643
- graphics.circle(marker.point.x, marker.point.y, radius);
4644
- if (marker.fill)
4645
- graphics.fill(marker.fill);
4646
- if (marker.stroke)
4647
- graphics.stroke({
4648
- alpha: marker.stroke.alpha,
4649
- color: marker.stroke.color,
4650
- width: resolveScreenLength(marker.stroke.width, viewportScale),
4651
- });
5173
+ drawMarkerInstruction(graphics, {
5174
+ center: marker.point,
5175
+ fill: marker.fill,
5176
+ kind: ShapeInstructionKind.Marker,
5177
+ shape: MarkerShape.Circle,
5178
+ size: marker.radius * 2,
5179
+ sizeSpace: MarkerSizeSpace.Screen,
5180
+ stroke: marker.stroke,
5181
+ }, viewportScale);
5182
+ }
5183
+ }
5184
+ }
5185
+ }
5186
+ function drawShapeInstruction(graphics, instruction, viewportScale) {
5187
+ if (instruction.kind === ShapeInstructionKind.Ellipse) {
5188
+ const { closed, points } = sampleEllipseArc(instruction, resolveEllipseSegmentCount(instruction, viewportScale));
5189
+ if (closed && instruction.fill) {
5190
+ graphics.poly(points.flatMap(({ x, y }) => [x, y]), true);
5191
+ graphics.fill(instruction.fill);
5192
+ }
5193
+ if (instruction.stroke) {
5194
+ drawPixiPath(graphics, points, closed, instruction.stroke, viewportScale);
5195
+ }
5196
+ return;
5197
+ }
5198
+ if (instruction.kind === ShapeInstructionKind.Marker) {
5199
+ drawMarkerInstruction(graphics, instruction, viewportScale);
5200
+ return;
5201
+ }
5202
+ for (const segment of instruction.segments) {
5203
+ if (instruction.closed && instruction.fill) {
5204
+ graphics.poly(segment.flatMap(({ x, y }) => [x, y]), true);
5205
+ graphics.fill(instruction.fill);
5206
+ }
5207
+ drawPixiPath(graphics, segment, instruction.closed, instruction.stroke, viewportScale);
5208
+ }
5209
+ }
5210
+ function drawMarkerInstruction(graphics, instruction, viewportScale) {
5211
+ const geometry = resolveMarkerGeometry(instruction, viewportScale);
5212
+ if (geometry.kind === "circle") {
5213
+ const dashed = Boolean(instruction.stroke?.dash?.length);
5214
+ if (instruction.fill || !dashed) {
5215
+ graphics.circle(geometry.center.x, geometry.center.y, geometry.radius);
5216
+ if (instruction.fill)
5217
+ graphics.fill(instruction.fill);
5218
+ }
5219
+ if (instruction.stroke) {
5220
+ if (dashed) {
5221
+ const ellipse = {
5222
+ center: geometry.center,
5223
+ radiusX: geometry.radius,
5224
+ radiusY: geometry.radius,
5225
+ };
5226
+ const { points } = sampleEllipseArc(ellipse, resolveEllipseSegmentCount(ellipse, viewportScale));
5227
+ drawPixiPath(graphics, points, true, instruction.stroke, viewportScale);
5228
+ }
5229
+ else {
5230
+ graphics.stroke(resolvePixiStroke(instruction.stroke, viewportScale));
4652
5231
  }
4653
5232
  }
5233
+ return;
5234
+ }
5235
+ for (const subpath of geometry.subpaths) {
5236
+ if (geometry.closed && instruction.fill) {
5237
+ graphics.poly(subpath.flatMap(({ x, y }) => [x, y]), true);
5238
+ graphics.fill(instruction.fill);
5239
+ }
5240
+ if (instruction.stroke) {
5241
+ drawPixiPath(graphics, subpath, geometry.closed, instruction.stroke, viewportScale);
5242
+ }
4654
5243
  }
4655
5244
  }
5245
+ function asOpenStroke(stroke) {
5246
+ if (!stroke)
5247
+ return undefined;
5248
+ return {
5249
+ alpha: stroke.alpha,
5250
+ cap: stroke.cap,
5251
+ color: stroke.color,
5252
+ dash: stroke.dash,
5253
+ join: stroke.join,
5254
+ miterLimit: stroke.miterLimit,
5255
+ width: stroke.width,
5256
+ };
5257
+ }
4656
5258
  function detectionKey(detection, detectionIndex) {
4657
5259
  return detection.id === undefined
4658
5260
  ? `index:${detectionIndex}`
@@ -4782,7 +5384,8 @@ function createPixiInteractionPresentationLayer(options) {
4782
5384
  }
4783
5385
  function createActivePick(pick, frame, mediaTime, state) {
4784
5386
  const activePick = rebaseDetectionPickToFrame(pick, frame);
4785
- if (!activePick) {
5387
+ if (!activePick ||
5388
+ options.isDetectionVisible?.(activePick.detection) === false) {
4786
5389
  return null;
4787
5390
  }
4788
5391
  const context = {
@@ -5039,7 +5642,7 @@ function createInteractionMaskRenderer(options) {
5039
5642
  autoGenerateMipmaps: false,
5040
5643
  dynamic: false,
5041
5644
  height: 1,
5042
- resource: createPlaceholderCanvas$1(),
5645
+ resource: createPlaceholderCanvas$2(),
5043
5646
  scaleMode: "nearest",
5044
5647
  width: 1,
5045
5648
  });
@@ -5150,7 +5753,7 @@ function writePaletteEntry(palette, maskId, color, alpha) {
5150
5753
  palette[offset + 2] = (color & 0xff) / 255;
5151
5754
  palette[offset + 3] = clampedAlpha;
5152
5755
  }
5153
- function createPlaceholderCanvas$1() {
5756
+ function createPlaceholderCanvas$2() {
5154
5757
  const canvas = document.createElement("canvas");
5155
5758
  canvas.height = 1;
5156
5759
  canvas.width = 1;
@@ -5298,7 +5901,7 @@ void main(void) {
5298
5901
  }
5299
5902
  `;
5300
5903
 
5301
- const EMBEDDED_MASK_PREPARATION_WORKER_SOURCE = "(function () {\n 'use strict';\n\n /** COCO-compatible keypoint visibility values. */\n var KeypointVisibility;\n (function (KeypointVisibility) {\n KeypointVisibility[KeypointVisibility[\"NotLabeled\"] = 0] = \"NotLabeled\";\n KeypointVisibility[KeypointVisibility[\"Occluded\"] = 1] = \"Occluded\";\n KeypointVisibility[KeypointVisibility[\"Visible\"] = 2] = \"Visible\";\n })(KeypointVisibility || (KeypointVisibility = {}));\n var DetectionMaskEncoding;\n (function (DetectionMaskEncoding) {\n DetectionMaskEncoding[\"CompressedRle\"] = \"compressedRle\";\n })(DetectionMaskEncoding || (DetectionMaskEncoding = {}));\n\n var DetectionBufferStatus;\n (function (DetectionBufferStatus) {\n DetectionBufferStatus[\"Idle\"] = \"idle\";\n DetectionBufferStatus[\"Loading\"] = \"loading\";\n DetectionBufferStatus[\"Ready\"] = \"ready\";\n DetectionBufferStatus[\"Error\"] = \"error\";\n DetectionBufferStatus[\"Destroyed\"] = \"destroyed\";\n })(DetectionBufferStatus || (DetectionBufferStatus = {}));\n /**\n * How the renderer selects the active detection frame for a media timestamp.\n */\n var DetectionFrameSelectionMode;\n (function (DetectionFrameSelectionMode) {\n /**\n * Select the frame whose `[mediaTime, endTime)` interval contains the media\n * time. This is the default for interval annotations and timestamped sources.\n */\n DetectionFrameSelectionMode[\"Interval\"] = \"interval\";\n /**\n * Select from a known inference frame grid using `frameIndex`, `frameRate`,\n * and optional `frameIndexOriginTime`. This is useful when inference was run\n * on normalized frames and playback should snap detections to that grid.\n */\n DetectionFrameSelectionMode[\"NearestFrameIndex\"] = \"nearestFrameIndex\";\n })(DetectionFrameSelectionMode || (DetectionFrameSelectionMode = {}));\n var DetectionFrameRetentionMode;\n (function (DetectionFrameRetentionMode) {\n /**\n * Keep writable detections only in an in-memory store. Useful for ephemeral\n * live streams where old predictions should disappear when evicted.\n */\n DetectionFrameRetentionMode[\"MemoryOnly\"] = \"memoryOnly\";\n /**\n * Persist every written detection frame. Useful for finite media where seek\n * and replay should not require recomputing detections.\n */\n DetectionFrameRetentionMode[\"PersistAll\"] = \"persistAll\";\n /**\n * Persist only the most recent retention window. Useful for long-running\n * streams where replay is bounded to a recent time horizon.\n */\n DetectionFrameRetentionMode[\"PersistWindow\"] = \"persistWindow\";\n })(DetectionFrameRetentionMode || (DetectionFrameRetentionMode = {}));\n function decodeCompressedRleMask(mask) {\n if (mask.encoding !== DetectionMaskEncoding.CompressedRle) {\n throw new Error(`Unsupported detection mask encoding: ${mask.encoding}`);\n }\n const data = new Uint8Array(mask.width * mask.height);\n const counts = decodeCompressedRleCounts(mask.counts);\n let offset = 0;\n for (let index = 0; index < counts.length; index += 1) {\n const runLength = counts[index] ?? 0;\n const isForeground = index % 2 === 1;\n if (isForeground) {\n for (let runOffset = 0; runOffset < runLength; runOffset += 1) {\n const maskOffset = offset + runOffset;\n const x = Math.floor(maskOffset / mask.height);\n const y = maskOffset % mask.height;\n const rowMajorOffset = y * mask.width + x;\n if (rowMajorOffset < data.length) {\n data[rowMajorOffset] = 1;\n }\n }\n }\n offset += runLength;\n }\n return {\n data,\n height: mask.height,\n width: mask.width,\n };\n }\n function decodeCompressedRleCounts(counts) {\n const decoded = [];\n let index = 0;\n while (index < counts.length) {\n let value = 0;\n let shift = 0;\n let charCode;\n do {\n charCode = counts.charCodeAt(index) - 48;\n index += 1;\n value |= (charCode & 0x1f) << shift;\n shift += 5;\n } while (charCode & 0x20);\n if (charCode & 0x10) {\n value |= -1 << shift;\n }\n if (decoded.length > 2) {\n value += decoded[decoded.length - 2] ?? 0;\n }\n decoded.push(value);\n }\n return decoded;\n }\n function encodeCompressedRleCounts(counts) {\n return counts\n .map((count, index) => {\n let value = index > 2 ? count - counts[index - 2] : count;\n let encoded = \"\";\n let more = true;\n while (more) {\n let charCode = value & 0x1f;\n value >>= 5;\n more = !((value === 0 && (charCode & 0x10) === 0) ||\n (value === -1 && (charCode & 0x10) !== 0));\n if (more) {\n charCode |= 0x20;\n }\n encoded += String.fromCharCode(charCode + 48);\n }\n return encoded;\n })\n .join(\"\");\n }\n\n var AnnotationFrameMutationKind;\n (function (AnnotationFrameMutationKind) {\n AnnotationFrameMutationKind[\"Add\"] = \"add\";\n AnnotationFrameMutationKind[\"Remove\"] = \"remove\";\n AnnotationFrameMutationKind[\"Replace\"] = \"replace\";\n AnnotationFrameMutationKind[\"Transact\"] = \"transact\";\n AnnotationFrameMutationKind[\"Update\"] = \"update\";\n })(AnnotationFrameMutationKind || (AnnotationFrameMutationKind = {}));\n\n var DetectionPickTarget;\n (function (DetectionPickTarget) {\n DetectionPickTarget[\"Box\"] = \"box\";\n DetectionPickTarget[\"Edge\"] = \"edge\";\n DetectionPickTarget[\"Keypoint\"] = \"keypoint\";\n DetectionPickTarget[\"Label\"] = \"label\";\n DetectionPickTarget[\"Mask\"] = \"mask\";\n DetectionPickTarget[\"Polygon\"] = \"polygon\";\n DetectionPickTarget[\"Polyline\"] = \"polyline\";\n })(DetectionPickTarget || (DetectionPickTarget = {}));\n var MediaInteractionMode;\n (function (MediaInteractionMode) {\n MediaInteractionMode[\"Always\"] = \"always\";\n MediaInteractionMode[\"Disabled\"] = \"disabled\";\n MediaInteractionMode[\"PausedOnly\"] = \"pausedOnly\";\n })(MediaInteractionMode || (MediaInteractionMode = {}));\n\n function centerRectToTopLeftRect(rect) {\n return {\n height: rect.height,\n width: rect.width,\n x: rect.x - rect.width / 2,\n y: rect.y - rect.height / 2,\n };\n }\n function getPointsRect(points) {\n if (points.length === 0) {\n return undefined;\n }\n let minX = Number.POSITIVE_INFINITY;\n let minY = Number.POSITIVE_INFINITY;\n let maxX = Number.NEGATIVE_INFINITY;\n let maxY = Number.NEGATIVE_INFINITY;\n for (const point of points) {\n minX = Math.min(minX, point.x);\n minY = Math.min(minY, point.y);\n maxX = Math.max(maxX, point.x);\n maxY = Math.max(maxY, point.y);\n }\n return {\n height: maxY - minY,\n width: maxX - minX,\n x: (minX + maxX) / 2,\n y: (minY + maxY) / 2,\n };\n }\n\n var AnnotationGeometryKind;\n (function (AnnotationGeometryKind) {\n AnnotationGeometryKind[\"Box\"] = \"box\";\n AnnotationGeometryKind[\"Polygon\"] = \"polygon\";\n AnnotationGeometryKind[\"Polyline\"] = \"polyline\";\n AnnotationGeometryKind[\"Keypoints\"] = \"keypoints\";\n AnnotationGeometryKind[\"Mask\"] = \"mask\";\n })(AnnotationGeometryKind || (AnnotationGeometryKind = {}));\n var AnnotationGestureStateKind;\n (function (AnnotationGestureStateKind) {\n AnnotationGestureStateKind[\"Idle\"] = \"idle\";\n AnnotationGestureStateKind[\"Creating\"] = \"creating\";\n AnnotationGestureStateKind[\"Moving\"] = \"moving\";\n AnnotationGestureStateKind[\"Resizing\"] = \"resizing\";\n AnnotationGestureStateKind[\"DragSelecting\"] = \"dragSelecting\";\n })(AnnotationGestureStateKind || (AnnotationGestureStateKind = {}));\n var AnnotationHandleKind;\n (function (AnnotationHandleKind) {\n AnnotationHandleKind[\"Resize\"] = \"resize\";\n AnnotationHandleKind[\"Vertex\"] = \"vertex\";\n AnnotationHandleKind[\"AddVertex\"] = \"addVertex\";\n AnnotationHandleKind[\"Keypoint\"] = \"keypoint\";\n })(AnnotationHandleKind || (AnnotationHandleKind = {}));\n\n var BoxShape;\n (function (BoxShape) {\n BoxShape[\"Rect\"] = \"rect\";\n BoxShape[\"RoundedRect\"] = \"roundedRect\";\n })(BoxShape || (BoxShape = {}));\n var BoxStrokeAlignment;\n (function (BoxStrokeAlignment) {\n BoxStrokeAlignment[\"Inside\"] = \"inside\";\n BoxStrokeAlignment[\"Center\"] = \"center\";\n BoxStrokeAlignment[\"Outside\"] = \"outside\";\n })(BoxStrokeAlignment || (BoxStrokeAlignment = {}));\n\n var FocusTargetMode;\n (function (FocusTargetMode) {\n FocusTargetMode[\"Hovered\"] = \"hovered\";\n FocusTargetMode[\"Selected\"] = \"selected\";\n FocusTargetMode[\"HoveredAndSelected\"] = \"hoveredAndSelected\";\n FocusTargetMode[\"Ambient\"] = \"ambient\";\n })(FocusTargetMode || (FocusTargetMode = {}));\n\n var DetectionInteractionState;\n (function (DetectionInteractionState) {\n DetectionInteractionState[\"Hovered\"] = \"hovered\";\n DetectionInteractionState[\"Selected\"] = \"selected\";\n })(DetectionInteractionState || (DetectionInteractionState = {}));\n\n var LabelPlacement;\n (function (LabelPlacement) {\n LabelPlacement[\"Top\"] = \"top\";\n LabelPlacement[\"Bottom\"] = \"bottom\";\n LabelPlacement[\"InsideTop\"] = \"insideTop\";\n LabelPlacement[\"InsideBottom\"] = \"insideBottom\";\n LabelPlacement[\"Center\"] = \"center\";\n })(LabelPlacement || (LabelPlacement = {}));\n var LabelVisibilityMode;\n (function (LabelVisibilityMode) {\n LabelVisibilityMode[\"Always\"] = \"always\";\n LabelVisibilityMode[\"HoveredOnly\"] = \"hoveredOnly\";\n })(LabelVisibilityMode || (LabelVisibilityMode = {}));\n\n var MaskRenderMode;\n (function (MaskRenderMode) {\n MaskRenderMode[\"FillAndStroke\"] = \"fillAndStroke\";\n MaskRenderMode[\"FillOnly\"] = \"fillOnly\";\n MaskRenderMode[\"StrokeOnly\"] = \"strokeOnly\";\n })(MaskRenderMode || (MaskRenderMode = {}));\n\n var KeypointMarkerShape;\n (function (KeypointMarkerShape) {\n KeypointMarkerShape[\"Circle\"] = \"circle\";\n KeypointMarkerShape[\"Cross\"] = \"cross\";\n })(KeypointMarkerShape || (KeypointMarkerShape = {}));\n\n var DetectionMaskPayloadFormat;\n (function (DetectionMaskPayloadFormat) {\n DetectionMaskPayloadFormat[\"RawCocoRle\"] = \"rawCocoRle\";\n DetectionMaskPayloadFormat[\"DeflatedBase64\"] = \"deflatedBase64\";\n })(DetectionMaskPayloadFormat || (DetectionMaskPayloadFormat = {}));\n function encodeBinaryMask(data, width, height) {\n assertMaskDimensions(data, width, height);\n const runs = [];\n let currentValue = 0;\n let runLength = 0;\n for (let x = 0; x < width; x += 1) {\n for (let y = 0; y < height; y += 1) {\n const value = data[y * width + x] ? 1 : 0;\n if (value === currentValue) {\n runLength += 1;\n }\n else {\n runs.push(runLength);\n currentValue = value;\n runLength = 1;\n }\n }\n }\n runs.push(runLength);\n return {\n counts: encodeCompressedRleCounts(runs),\n encoding: DetectionMaskEncoding.CompressedRle,\n height,\n width,\n };\n }\n function assertMaskDimensions(data, width, height) {\n if (!Number.isInteger(width) || width <= 0) {\n throw new Error(\"Mask width must be a positive integer.\");\n }\n if (!Number.isInteger(height) || height <= 0) {\n throw new Error(\"Mask height must be a positive integer.\");\n }\n if (data.length !== width * height) {\n throw new Error(\"Mask data length must equal width * height.\");\n }\n }\n function rasterizePolygonToMask(points, dimensions) {\n const data = createEmptyMask(dimensions);\n if (points.length < 3) {\n return data;\n }\n const bounds = centerRectToTopLeftRect(getPointsRect(points));\n const startY = Math.max(0, Math.floor(bounds.y));\n const endY = Math.min(dimensions.height - 1, Math.ceil(bounds.y + bounds.height));\n for (let y = startY; y <= endY; y += 1) {\n const scanY = y + 0.5;\n const intersections = [];\n for (let index = 0; index < points.length; index += 1) {\n const current = points[index];\n const next = points[(index + 1) % points.length];\n if ((current.y <= scanY && next.y > scanY) ||\n (next.y <= scanY && current.y > scanY)) {\n const ratio = (scanY - current.y) / (next.y - current.y);\n intersections.push(current.x + ratio * (next.x - current.x));\n }\n }\n intersections.sort((left, right) => left - right);\n for (let index = 0; index < intersections.length - 1; index += 2) {\n const left = Math.max(0, Math.ceil(intersections[index]));\n const right = Math.min(dimensions.width - 1, Math.floor(intersections[index + 1]));\n for (let x = left; x <= right; x += 1) {\n data[y * dimensions.width + x] = 1;\n }\n }\n }\n return data;\n }\n function createEmptyMask(dimensions) {\n if (!Number.isInteger(dimensions.width) ||\n dimensions.width <= 0 ||\n !Number.isInteger(dimensions.height) ||\n dimensions.height <= 0) {\n throw new Error(\"Media dimensions must be positive integers.\");\n }\n return new Uint8Array(dimensions.width * dimensions.height);\n }\n\n const MAX_ID_MASK_PALETTE_ENTRIES = 64;\n const MAX_ID_MASK_STROKE_WIDTH = 16;\n function createIdMaskFrame(instructions) {\n if (instructions.length === 0) {\n return undefined;\n }\n const width = Math.max(...instructions.map(({ mask }) => mask.width));\n const height = Math.max(...instructions.map(({ mask }) => mask.height));\n const data = new Uint8Array(new ArrayBuffer(width * height));\n const fillPalette = new Float32Array(new ArrayBuffer(MAX_ID_MASK_PALETTE_ENTRIES * 4 * 4));\n const strokePalette = new Float32Array(new ArrayBuffer(MAX_ID_MASK_PALETTE_ENTRIES * 4 * 4));\n const strokeWidths = new Float32Array(new ArrayBuffer(MAX_ID_MASK_PALETTE_ENTRIES * 4));\n let hasStroke = false;\n let maxStrokeWidth = 0;\n for (const instruction of instructions) {\n const detectionMaskId = instruction.detectionIndex + 1;\n if (detectionMaskId <= 0 ||\n detectionMaskId >= MAX_ID_MASK_PALETTE_ENTRIES) {\n return undefined;\n }\n writePaletteEntry(fillPalette, detectionMaskId, instruction.color, instruction.alpha);\n if (instruction.stroke && instruction.stroke.width > 0) {\n const strokeWidth = Math.min(Math.max(0, instruction.stroke.width), MAX_ID_MASK_STROKE_WIDTH);\n hasStroke = true;\n strokeWidths[detectionMaskId] = strokeWidth;\n maxStrokeWidth = Math.max(maxStrokeWidth, strokeWidth);\n writePaletteEntry(strokePalette, detectionMaskId, instruction.stroke.color, instruction.stroke.alpha);\n }\n const decodedMask = decodeCompressedRleMask(instruction.mask);\n for (let y = 0; y < decodedMask.height; y += 1) {\n for (let x = 0; x < decodedMask.width; x += 1) {\n const maskOffset = y * decodedMask.width + x;\n if (decodedMask.data[maskOffset]) {\n data[y * width + x] = detectionMaskId;\n }\n }\n }\n }\n return {\n data,\n fillPalette,\n hasStroke,\n height,\n maxStrokeWidth,\n strokePalette,\n strokeWidths,\n width,\n };\n }\n function writePaletteEntry(palette, id, color, alpha) {\n const offset = id * 4;\n palette[offset] = ((color >> 16) & 0xff) / 255;\n palette[offset + 1] = ((color >> 8) & 0xff) / 255;\n palette[offset + 2] = (color & 0xff) / 255;\n palette[offset + 3] = Math.max(0, Math.min(alpha, 1));\n }\n\n var MediaRendererFit;\n (function (MediaRendererFit) {\n /**\n * Preserve media aspect ratio and fit the full frame inside the canvas.\n */\n MediaRendererFit[\"Contain\"] = \"contain\";\n /**\n * Preserve media aspect ratio and fill the canvas, cropping if necessary.\n */\n MediaRendererFit[\"Cover\"] = \"cover\";\n })(MediaRendererFit || (MediaRendererFit = {}));\n /**\n * Playback lifecycle state reported by a platform renderer.\n */\n var MediaRendererPlaybackState;\n (function (MediaRendererPlaybackState) {\n MediaRendererPlaybackState[\"Loading\"] = \"loading\";\n MediaRendererPlaybackState[\"Ready\"] = \"ready\";\n MediaRendererPlaybackState[\"Playing\"] = \"playing\";\n MediaRendererPlaybackState[\"Buffering\"] = \"buffering\";\n MediaRendererPlaybackState[\"Paused\"] = \"paused\";\n MediaRendererPlaybackState[\"Error\"] = \"error\";\n MediaRendererPlaybackState[\"Destroyed\"] = \"destroyed\";\n })(MediaRendererPlaybackState || (MediaRendererPlaybackState = {}));\n /**\n * Lower-level media source readiness.\n */\n var MediaSourceStatus;\n (function (MediaSourceStatus) {\n MediaSourceStatus[\"Loading\"] = \"loading\";\n MediaSourceStatus[\"Ready\"] = \"ready\";\n MediaSourceStatus[\"Error\"] = \"error\";\n MediaSourceStatus[\"Destroyed\"] = \"destroyed\";\n })(MediaSourceStatus || (MediaSourceStatus = {}));\n\n /**\n * Media-session operating mode.\n *\n * Core owns the names because file-like and stream-like lifecycle choices are\n * platform-neutral. Platform packages decide how these modes tune media,\n * storage, and renderer defaults.\n */\n var MediaSessionMode;\n (function (MediaSessionMode) {\n /**\n * Finite media. Defaults usually favor seek/replay and persistent detection\n * storage.\n */\n MediaSessionMode[\"File\"] = \"file\";\n /**\n * Live or append-only media. Defaults usually favor rolling windows and\n * bounded retention.\n */\n MediaSessionMode[\"Stream\"] = \"stream\";\n })(MediaSessionMode || (MediaSessionMode = {}));\n /**\n * Aggregate lifecycle state for a media session.\n */\n var MediaSessionStatus;\n (function (MediaSessionStatus) {\n MediaSessionStatus[\"Buffering\"] = \"buffering\";\n MediaSessionStatus[\"Destroyed\"] = \"destroyed\";\n MediaSessionStatus[\"Error\"] = \"error\";\n MediaSessionStatus[\"Loading\"] = \"loading\";\n MediaSessionStatus[\"Paused\"] = \"paused\";\n MediaSessionStatus[\"Playing\"] = \"playing\";\n MediaSessionStatus[\"Processing\"] = \"processing\";\n MediaSessionStatus[\"Ready\"] = \"ready\";\n })(MediaSessionStatus || (MediaSessionStatus = {}));\n /**\n * Subsystem currently affecting session readiness or presentation.\n */\n var MediaSessionActivityKind;\n (function (MediaSessionActivityKind) {\n MediaSessionActivityKind[\"DetectionsBuffering\"] = \"detectionsBuffering\";\n MediaSessionActivityKind[\"DetectionsLoading\"] = \"detectionsLoading\";\n MediaSessionActivityKind[\"Error\"] = \"error\";\n MediaSessionActivityKind[\"MediaNormalizing\"] = \"mediaNormalizing\";\n MediaSessionActivityKind[\"MediaOpening\"] = \"mediaOpening\";\n MediaSessionActivityKind[\"PlaybackBuffering\"] = \"playbackBuffering\";\n MediaSessionActivityKind[\"RenderPreparing\"] = \"renderPreparing\";\n })(MediaSessionActivityKind || (MediaSessionActivityKind = {}));\n /**\n * State of one session activity.\n */\n var MediaSessionActivityStatus;\n (function (MediaSessionActivityStatus) {\n MediaSessionActivityStatus[\"Error\"] = \"error\";\n MediaSessionActivityStatus[\"Running\"] = \"running\";\n MediaSessionActivityStatus[\"Waiting\"] = \"waiting\";\n })(MediaSessionActivityStatus || (MediaSessionActivityStatus = {}));\n\n const PNG_SIGNATURE = new Uint8Array([\n 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a,\n ]);\n function compositeMaskFrame(instructions) {\n const maskInstructions = materializeMaskInstructions(instructions);\n if (maskInstructions.length === 0) {\n return undefined;\n }\n const width = Math.max(...maskInstructions.map(({ mask }) => mask.width));\n const height = Math.max(...maskInstructions.map(({ mask }) => mask.height));\n const data = new Uint8ClampedArray(new ArrayBuffer(width * height * 4));\n for (const instruction of maskInstructions) {\n compositeInstruction(data, width, instruction);\n }\n return { data, height, width };\n }\n async function createPngIdMaskFrame(instructions) {\n const frame = createIdMaskFrame(materializeMaskInstructions(instructions));\n if (!frame) {\n return undefined;\n }\n return {\n ...frame,\n png: await encodeGrayscalePng({\n height: frame.height,\n pixels: frame.data,\n width: frame.width,\n }),\n };\n }\n function compositeInstruction(rgba, canvasWidth, instruction) {\n const decodedMask = decodeCompressedRleMask(instruction.mask);\n const fill = resolveRgbaColor(instruction.color, instruction.alpha);\n compositeMaskFill(rgba, canvasWidth, decodedMask, fill);\n if (instruction.stroke) {\n compositeMaskStroke(rgba, canvasWidth, decodedMask, instruction.stroke);\n }\n }\n function materializeMaskInstructions(instructions) {\n return instructions.map((instruction) => {\n if (instruction.mask) {\n return instruction;\n }\n const { height, points, width } = instruction.polygon;\n return {\n alpha: instruction.alpha,\n color: instruction.color,\n detectionIndex: instruction.detectionIndex,\n mask: encodeBinaryMask(rasterizePolygonToMask(points, { height, width }), width, height),\n stroke: instruction.stroke,\n };\n });\n }\n function compositeMaskFill(rgba, canvasWidth, decodedMask, fill) {\n for (let y = 0; y < decodedMask.height; y += 1) {\n for (let x = 0; x < decodedMask.width; x += 1) {\n const maskOffset = y * decodedMask.width + x;\n if (!decodedMask.data[maskOffset]) {\n continue;\n }\n writePixel(rgba, canvasWidth, x, y, fill);\n }\n }\n }\n function compositeMaskStroke(rgba, canvasWidth, decodedMask, stroke) {\n const width = Math.round(stroke.width);\n if (width <= 0) {\n return;\n }\n const strokeColor = resolveRgbaColor(stroke.color, stroke.alpha);\n for (let y = 0; y < decodedMask.height; y += 1) {\n for (let x = 0; x < decodedMask.width; x += 1) {\n if (!isMaskPixel(decodedMask, x, y) ||\n !isBoundaryPixel(decodedMask, x, y)) {\n continue;\n }\n for (let offsetY = -width; offsetY <= width; offsetY += 1) {\n for (let offsetX = -width; offsetX <= width; offsetX += 1) {\n const strokeX = x + offsetX;\n const strokeY = y + offsetY;\n if (isOutsideMaskBounds(decodedMask, strokeX, strokeY) ||\n isMaskPixel(decodedMask, strokeX, strokeY)) {\n continue;\n }\n writePixel(rgba, canvasWidth, strokeX, strokeY, strokeColor);\n }\n }\n }\n }\n }\n function isBoundaryPixel(mask, x, y) {\n for (let offsetY = -1; offsetY <= 1; offsetY += 1) {\n for (let offsetX = -1; offsetX <= 1; offsetX += 1) {\n if (offsetX === 0 && offsetY === 0) {\n continue;\n }\n const neighborX = x + offsetX;\n const neighborY = y + offsetY;\n if (isOutsideMaskBounds(mask, neighborX, neighborY) ||\n !isMaskPixel(mask, neighborX, neighborY)) {\n return true;\n }\n }\n }\n return false;\n }\n function isMaskPixel(mask, x, y) {\n return mask.data[y * mask.width + x] === 1;\n }\n function isOutsideMaskBounds(mask, x, y) {\n return x < 0 || y < 0 || x >= mask.width || y >= mask.height;\n }\n function resolveRgbaColor(color, alpha) {\n return {\n alpha: Math.round(Math.max(0, Math.min(alpha, 1)) * 255),\n blue: color & 0xff,\n green: (color >> 8) & 0xff,\n red: (color >> 16) & 0xff,\n };\n }\n function writePixel(rgba, canvasWidth, x, y, color) {\n const rgbaOffset = (y * canvasWidth + x) * 4;\n rgba[rgbaOffset] = color.red;\n rgba[rgbaOffset + 1] = color.green;\n rgba[rgbaOffset + 2] = color.blue;\n rgba[rgbaOffset + 3] = color.alpha;\n }\n async function encodeGrayscalePng(options) {\n if (typeof CompressionStream === \"undefined\") {\n throw new Error(\"CompressionStream is required to encode PNG ID masks.\");\n }\n const rawScanlines = createFilterlessPngScanlines(options);\n const ihdr = new Uint8Array(13);\n const ihdrView = new DataView(ihdr.buffer);\n ihdrView.setUint32(0, options.width);\n ihdrView.setUint32(4, options.height);\n ihdr[8] = 8;\n ihdr[9] = 0;\n ihdr[10] = 0;\n ihdr[11] = 0;\n ihdr[12] = 0;\n const compressed = new Uint8Array(await new Response(new Blob([rawScanlines])\n .stream()\n .pipeThrough(new CompressionStream(\"deflate\"))).arrayBuffer());\n return concatUint8Arrays([\n PNG_SIGNATURE,\n createPngChunk(\"IHDR\", ihdr),\n createPngChunk(\"IDAT\", compressed),\n createPngChunk(\"IEND\", new Uint8Array(0)),\n ]);\n }\n function createFilterlessPngScanlines(options) {\n const rowStride = options.width + 1;\n const scanlines = new Uint8Array(rowStride * options.height);\n for (let y = 0; y < options.height; y += 1) {\n const sourceOffset = y * options.width;\n const targetOffset = y * rowStride;\n scanlines[targetOffset] = 0;\n scanlines.set(options.pixels.subarray(sourceOffset, sourceOffset + options.width), targetOffset + 1);\n }\n return scanlines;\n }\n function createPngChunk(type, data) {\n const typeBytes = new TextEncoder().encode(type);\n const chunk = new Uint8Array(12 + data.length);\n const view = new DataView(chunk.buffer);\n view.setUint32(0, data.length);\n chunk.set(typeBytes, 4);\n chunk.set(data, 8);\n view.setUint32(8 + data.length, crc32(concatUint8Arrays([typeBytes, data])));\n return chunk;\n }\n const crc32Table = createCrc32Table();\n function createCrc32Table() {\n const table = new Uint32Array(256);\n for (let index = 0; index < table.length; index += 1) {\n let value = index;\n for (let bit = 0; bit < 8; bit += 1) {\n value = value & 1 ? 0xedb88320 ^ (value >>> 1) : value >>> 1;\n }\n table[index] = value >>> 0;\n }\n return table;\n }\n function crc32(bytes) {\n let crc = 0xffffffff;\n for (const byte of bytes) {\n crc = crc32Table[(crc ^ byte) & 0xff] ^ (crc >>> 8);\n }\n return (crc ^ 0xffffffff) >>> 0;\n }\n function concatUint8Arrays(chunks) {\n const totalLength = chunks.reduce((total, chunk) => total + chunk.length, 0);\n const result = new Uint8Array(totalLength);\n let offset = 0;\n for (const chunk of chunks) {\n result.set(chunk, offset);\n offset += chunk.length;\n }\n return result;\n }\n\n var PreparedMaskFrameKind;\n (function (PreparedMaskFrameKind) {\n PreparedMaskFrameKind[\"PngIdMask\"] = \"pngIdMask\";\n PreparedMaskFrameKind[\"RgbaImage\"] = \"rgbaImage\";\n })(PreparedMaskFrameKind || (PreparedMaskFrameKind = {}));\n\n var MaskPreparationWorkerMessageType;\n (function (MaskPreparationWorkerMessageType) {\n MaskPreparationWorkerMessageType[\"Complete\"] = \"complete\";\n MaskPreparationWorkerMessageType[\"Empty\"] = \"empty\";\n MaskPreparationWorkerMessageType[\"Error\"] = \"error\";\n MaskPreparationWorkerMessageType[\"Prepare\"] = \"prepare\";\n })(MaskPreparationWorkerMessageType || (MaskPreparationWorkerMessageType = {}));\n\n const workerScope = globalThis;\n workerScope.addEventListener(\"message\", (event) => {\n const message = event.data;\n if (message.type !== MaskPreparationWorkerMessageType.Prepare) {\n return;\n }\n void prepareMaskFrame(message);\n });\n async function prepareMaskFrame(message) {\n try {\n const pngIdMaskFrame = await createPngIdMaskWorkerResponse(message);\n if (pngIdMaskFrame) {\n workerScope.postMessage(pngIdMaskFrame, [\n pngIdMaskFrame.imageBitmap,\n pngIdMaskFrame.png.buffer,\n pngIdMaskFrame.fillPalette.buffer,\n pngIdMaskFrame.strokePalette.buffer,\n pngIdMaskFrame.strokeWidths.buffer,\n ]);\n return;\n }\n const compositedFrame = compositeMaskFrame(message.job.instructions);\n if (!compositedFrame) {\n workerScope.postMessage({\n key: message.job.key,\n requestId: message.requestId,\n type: MaskPreparationWorkerMessageType.Empty,\n });\n return;\n }\n const imageData = new ImageData(compositedFrame.data, compositedFrame.width, compositedFrame.height);\n const imageBitmap = createImageBitmapFromImageData(imageData);\n if (imageBitmap) {\n workerScope.postMessage({\n imageBitmap,\n key: message.job.key,\n requestId: message.requestId,\n type: MaskPreparationWorkerMessageType.Complete,\n }, [imageBitmap]);\n return;\n }\n workerScope.postMessage({\n imageData,\n key: message.job.key,\n requestId: message.requestId,\n type: MaskPreparationWorkerMessageType.Complete,\n }, [imageData.data.buffer]);\n }\n catch (error) {\n workerScope.postMessage({\n error: error instanceof Error\n ? error.message\n : \"Unable to prepare mask frame.\",\n key: message.job.key,\n requestId: message.requestId,\n type: MaskPreparationWorkerMessageType.Error,\n });\n }\n }\n async function createPngIdMaskWorkerResponse(message) {\n if (typeof Blob === \"undefined\" ||\n typeof createImageBitmap === \"undefined\" ||\n typeof CompressionStream === \"undefined\") {\n return undefined;\n }\n let frame;\n try {\n frame = await createPngIdMaskFrame(message.job.instructions);\n }\n catch {\n return undefined;\n }\n if (!frame) {\n return undefined;\n }\n let imageBitmap;\n try {\n imageBitmap = await createImageBitmap(new Blob([frame.png], { type: \"image/png\" }));\n }\n catch {\n return undefined;\n }\n return {\n artifactKind: PreparedMaskFrameKind.PngIdMask,\n fillPalette: frame.fillPalette,\n hasStroke: frame.hasStroke,\n imageBitmap,\n key: message.job.key,\n maxStrokeWidth: frame.maxStrokeWidth,\n png: frame.png,\n requestId: message.requestId,\n strokePalette: frame.strokePalette,\n strokeWidths: frame.strokeWidths,\n type: MaskPreparationWorkerMessageType.Complete,\n };\n }\n function createImageBitmapFromImageData(imageData) {\n if (typeof OffscreenCanvas === \"undefined\") {\n return null;\n }\n const canvas = new OffscreenCanvas(imageData.width, imageData.height);\n const context = canvas.getContext(\"2d\");\n if (!context) {\n return null;\n }\n context.putImageData(imageData, 0, 0);\n return canvas.transferToImageBitmap();\n }\n\n})();";
5904
+ const EMBEDDED_MASK_PREPARATION_WORKER_SOURCE = "(function () {\n 'use strict';\n\n /** COCO-compatible keypoint visibility values. */\n var KeypointVisibility;\n (function (KeypointVisibility) {\n KeypointVisibility[KeypointVisibility[\"NotLabeled\"] = 0] = \"NotLabeled\";\n KeypointVisibility[KeypointVisibility[\"Occluded\"] = 1] = \"Occluded\";\n KeypointVisibility[KeypointVisibility[\"Visible\"] = 2] = \"Visible\";\n })(KeypointVisibility || (KeypointVisibility = {}));\n var DetectionMaskEncoding;\n (function (DetectionMaskEncoding) {\n DetectionMaskEncoding[\"CompressedRle\"] = \"compressedRle\";\n })(DetectionMaskEncoding || (DetectionMaskEncoding = {}));\n\n var DetectionBufferStatus;\n (function (DetectionBufferStatus) {\n DetectionBufferStatus[\"Idle\"] = \"idle\";\n DetectionBufferStatus[\"Loading\"] = \"loading\";\n DetectionBufferStatus[\"Ready\"] = \"ready\";\n DetectionBufferStatus[\"Error\"] = \"error\";\n DetectionBufferStatus[\"Destroyed\"] = \"destroyed\";\n })(DetectionBufferStatus || (DetectionBufferStatus = {}));\n /**\n * How the renderer selects the active detection frame for a media timestamp.\n */\n var DetectionFrameSelectionMode;\n (function (DetectionFrameSelectionMode) {\n /**\n * Select the frame whose `[mediaTime, endTime)` interval contains the media\n * time. This is the default for interval annotations and timestamped sources.\n */\n DetectionFrameSelectionMode[\"Interval\"] = \"interval\";\n /**\n * Select from a known inference frame grid using `frameIndex`, `frameRate`,\n * and optional `frameIndexOriginTime`. This is useful when inference was run\n * on normalized frames and playback should snap detections to that grid.\n */\n DetectionFrameSelectionMode[\"NearestFrameIndex\"] = \"nearestFrameIndex\";\n })(DetectionFrameSelectionMode || (DetectionFrameSelectionMode = {}));\n var DetectionFrameRetentionMode;\n (function (DetectionFrameRetentionMode) {\n /**\n * Keep writable detections only in an in-memory store. Useful for ephemeral\n * live streams where old predictions should disappear when evicted.\n */\n DetectionFrameRetentionMode[\"MemoryOnly\"] = \"memoryOnly\";\n /**\n * Persist every written detection frame. Useful for finite media where seek\n * and replay should not require recomputing detections.\n */\n DetectionFrameRetentionMode[\"PersistAll\"] = \"persistAll\";\n /**\n * Persist only the most recent retention window. Useful for long-running\n * streams where replay is bounded to a recent time horizon.\n */\n DetectionFrameRetentionMode[\"PersistWindow\"] = \"persistWindow\";\n })(DetectionFrameRetentionMode || (DetectionFrameRetentionMode = {}));\n function decodeCompressedRleMask(mask) {\n if (mask.encoding !== DetectionMaskEncoding.CompressedRle) {\n throw new Error(`Unsupported detection mask encoding: ${mask.encoding}`);\n }\n const data = new Uint8Array(mask.width * mask.height);\n const counts = decodeCompressedRleCounts(mask.counts);\n let offset = 0;\n for (let index = 0; index < counts.length; index += 1) {\n const runLength = counts[index] ?? 0;\n const isForeground = index % 2 === 1;\n if (isForeground) {\n for (let runOffset = 0; runOffset < runLength; runOffset += 1) {\n const maskOffset = offset + runOffset;\n const x = Math.floor(maskOffset / mask.height);\n const y = maskOffset % mask.height;\n const rowMajorOffset = y * mask.width + x;\n if (rowMajorOffset < data.length) {\n data[rowMajorOffset] = 1;\n }\n }\n }\n offset += runLength;\n }\n return {\n data,\n height: mask.height,\n width: mask.width,\n };\n }\n function decodeCompressedRleCounts(counts) {\n const decoded = [];\n let index = 0;\n while (index < counts.length) {\n let value = 0;\n let shift = 0;\n let charCode;\n do {\n charCode = counts.charCodeAt(index) - 48;\n index += 1;\n value |= (charCode & 0x1f) << shift;\n shift += 5;\n } while (charCode & 0x20);\n if (charCode & 0x10) {\n value |= -1 << shift;\n }\n if (decoded.length > 2) {\n value += decoded[decoded.length - 2] ?? 0;\n }\n decoded.push(value);\n }\n return decoded;\n }\n function encodeCompressedRleCounts(counts) {\n return counts\n .map((count, index) => {\n let value = index > 2 ? count - counts[index - 2] : count;\n let encoded = \"\";\n let more = true;\n while (more) {\n let charCode = value & 0x1f;\n value >>= 5;\n more = !((value === 0 && (charCode & 0x10) === 0) ||\n (value === -1 && (charCode & 0x10) !== 0));\n if (more) {\n charCode |= 0x20;\n }\n encoded += String.fromCharCode(charCode + 48);\n }\n return encoded;\n })\n .join(\"\");\n }\n\n var AnnotationFrameMutationKind;\n (function (AnnotationFrameMutationKind) {\n AnnotationFrameMutationKind[\"Add\"] = \"add\";\n AnnotationFrameMutationKind[\"Remove\"] = \"remove\";\n AnnotationFrameMutationKind[\"Replace\"] = \"replace\";\n AnnotationFrameMutationKind[\"Transact\"] = \"transact\";\n AnnotationFrameMutationKind[\"Update\"] = \"update\";\n })(AnnotationFrameMutationKind || (AnnotationFrameMutationKind = {}));\n\n /** Geometry projected into a tracker. The original detection payload is retained. */\n var TrackingGeometry;\n (function (TrackingGeometry) {\n TrackingGeometry[\"Box\"] = \"box\";\n TrackingGeometry[\"Mask\"] = \"mask\";\n TrackingGeometry[\"Keypoints\"] = \"keypoints\";\n })(TrackingGeometry || (TrackingGeometry = {}));\n\n var DetectionMaskPayloadFormat;\n (function (DetectionMaskPayloadFormat) {\n DetectionMaskPayloadFormat[\"RawCocoRle\"] = \"rawCocoRle\";\n DetectionMaskPayloadFormat[\"DeflatedBase64\"] = \"deflatedBase64\";\n })(DetectionMaskPayloadFormat || (DetectionMaskPayloadFormat = {}));\n function encodeBinaryMask(data, width, height) {\n assertMaskDimensions(data, width, height);\n const runs = [];\n let currentValue = 0;\n let runLength = 0;\n for (let x = 0; x < width; x += 1) {\n for (let y = 0; y < height; y += 1) {\n const value = data[y * width + x] ? 1 : 0;\n if (value === currentValue) {\n runLength += 1;\n }\n else {\n runs.push(runLength);\n currentValue = value;\n runLength = 1;\n }\n }\n }\n runs.push(runLength);\n return {\n counts: encodeCompressedRleCounts(runs),\n encoding: DetectionMaskEncoding.CompressedRle,\n height,\n width,\n };\n }\n function assertMaskDimensions(data, width, height) {\n if (!Number.isInteger(width) || width <= 0) {\n throw new Error(\"Mask width must be a positive integer.\");\n }\n if (!Number.isInteger(height) || height <= 0) {\n throw new Error(\"Mask height must be a positive integer.\");\n }\n if (data.length !== width * height) {\n throw new Error(\"Mask data length must equal width * height.\");\n }\n }\n\n function centerRectToTopLeftRect(rect) {\n return {\n height: rect.height,\n width: rect.width,\n x: rect.x - rect.width / 2,\n y: rect.y - rect.height / 2,\n };\n }\n function getPointsRect(points) {\n if (points.length === 0) {\n return undefined;\n }\n let minX = Number.POSITIVE_INFINITY;\n let minY = Number.POSITIVE_INFINITY;\n let maxX = Number.NEGATIVE_INFINITY;\n let maxY = Number.NEGATIVE_INFINITY;\n for (const point of points) {\n minX = Math.min(minX, point.x);\n minY = Math.min(minY, point.y);\n maxX = Math.max(maxX, point.x);\n maxY = Math.max(maxY, point.y);\n }\n return {\n height: maxY - minY,\n width: maxX - minX,\n x: (minX + maxX) / 2,\n y: (minY + maxY) / 2,\n };\n }\n\n var DetectionPickTarget;\n (function (DetectionPickTarget) {\n DetectionPickTarget[\"Box\"] = \"box\";\n DetectionPickTarget[\"Edge\"] = \"edge\";\n DetectionPickTarget[\"Keypoint\"] = \"keypoint\";\n DetectionPickTarget[\"Label\"] = \"label\";\n DetectionPickTarget[\"Mask\"] = \"mask\";\n DetectionPickTarget[\"Polygon\"] = \"polygon\";\n DetectionPickTarget[\"Polyline\"] = \"polyline\";\n })(DetectionPickTarget || (DetectionPickTarget = {}));\n var MediaInteractionMode;\n (function (MediaInteractionMode) {\n MediaInteractionMode[\"Always\"] = \"always\";\n MediaInteractionMode[\"Disabled\"] = \"disabled\";\n MediaInteractionMode[\"PausedOnly\"] = \"pausedOnly\";\n })(MediaInteractionMode || (MediaInteractionMode = {}));\n\n var AnnotationGeometryKind;\n (function (AnnotationGeometryKind) {\n AnnotationGeometryKind[\"Box\"] = \"box\";\n AnnotationGeometryKind[\"Polygon\"] = \"polygon\";\n AnnotationGeometryKind[\"Polyline\"] = \"polyline\";\n AnnotationGeometryKind[\"Keypoints\"] = \"keypoints\";\n AnnotationGeometryKind[\"Mask\"] = \"mask\";\n })(AnnotationGeometryKind || (AnnotationGeometryKind = {}));\n var AnnotationGestureStateKind;\n (function (AnnotationGestureStateKind) {\n AnnotationGestureStateKind[\"Idle\"] = \"idle\";\n AnnotationGestureStateKind[\"Creating\"] = \"creating\";\n AnnotationGestureStateKind[\"Moving\"] = \"moving\";\n AnnotationGestureStateKind[\"Resizing\"] = \"resizing\";\n AnnotationGestureStateKind[\"DragSelecting\"] = \"dragSelecting\";\n })(AnnotationGestureStateKind || (AnnotationGestureStateKind = {}));\n var AnnotationHandleKind;\n (function (AnnotationHandleKind) {\n AnnotationHandleKind[\"Resize\"] = \"resize\";\n AnnotationHandleKind[\"Vertex\"] = \"vertex\";\n AnnotationHandleKind[\"AddVertex\"] = \"addVertex\";\n AnnotationHandleKind[\"Keypoint\"] = \"keypoint\";\n })(AnnotationHandleKind || (AnnotationHandleKind = {}));\n\n /** Alignment for strokes applied to closed rendered geometry. */\n var StrokeAlignment;\n (function (StrokeAlignment) {\n StrokeAlignment[\"Inside\"] = \"inside\";\n StrokeAlignment[\"Center\"] = \"center\";\n StrokeAlignment[\"Outside\"] = \"outside\";\n })(StrokeAlignment || (StrokeAlignment = {}));\n\n var BoxShape;\n (function (BoxShape) {\n BoxShape[\"Rect\"] = \"rect\";\n BoxShape[\"RoundedRect\"] = \"roundedRect\";\n })(BoxShape || (BoxShape = {}));\n\n var FocusTargetMode;\n (function (FocusTargetMode) {\n FocusTargetMode[\"Hovered\"] = \"hovered\";\n FocusTargetMode[\"Selected\"] = \"selected\";\n FocusTargetMode[\"HoveredAndSelected\"] = \"hoveredAndSelected\";\n FocusTargetMode[\"Ambient\"] = \"ambient\";\n })(FocusTargetMode || (FocusTargetMode = {}));\n\n var DetectionInteractionState;\n (function (DetectionInteractionState) {\n DetectionInteractionState[\"Hovered\"] = \"hovered\";\n DetectionInteractionState[\"Selected\"] = \"selected\";\n })(DetectionInteractionState || (DetectionInteractionState = {}));\n\n var LabelPlacement;\n (function (LabelPlacement) {\n LabelPlacement[\"Top\"] = \"top\";\n LabelPlacement[\"Bottom\"] = \"bottom\";\n LabelPlacement[\"InsideTop\"] = \"insideTop\";\n LabelPlacement[\"InsideBottom\"] = \"insideBottom\";\n LabelPlacement[\"Center\"] = \"center\";\n })(LabelPlacement || (LabelPlacement = {}));\n var LabelVisibilityMode;\n (function (LabelVisibilityMode) {\n LabelVisibilityMode[\"Always\"] = \"always\";\n LabelVisibilityMode[\"HoveredOnly\"] = \"hoveredOnly\";\n })(LabelVisibilityMode || (LabelVisibilityMode = {}));\n\n var MaskRenderMode;\n (function (MaskRenderMode) {\n MaskRenderMode[\"FillAndStroke\"] = \"fillAndStroke\";\n MaskRenderMode[\"FillOnly\"] = \"fillOnly\";\n MaskRenderMode[\"StrokeOnly\"] = \"strokeOnly\";\n })(MaskRenderMode || (MaskRenderMode = {}));\n\n /**\n * Discriminator for renderer-neutral shape draw instructions.\n */\n var ShapeInstructionKind;\n (function (ShapeInstructionKind) {\n ShapeInstructionKind[\"Ellipse\"] = \"ellipse\";\n ShapeInstructionKind[\"Marker\"] = \"marker\";\n ShapeInstructionKind[\"Path\"] = \"path\";\n })(ShapeInstructionKind || (ShapeInstructionKind = {}));\n var MarkerShape;\n (function (MarkerShape) {\n MarkerShape[\"Circle\"] = \"circle\";\n MarkerShape[\"Cross\"] = \"cross\";\n MarkerShape[\"Square\"] = \"square\";\n MarkerShape[\"Triangle\"] = \"triangle\";\n })(MarkerShape || (MarkerShape = {}));\n /**\n * Coordinate space for marker sizing. Media sizes scale with the viewport;\n * screen sizes stay constant on screen, matching stroke-width semantics.\n */\n var MarkerSizeSpace;\n (function (MarkerSizeSpace) {\n MarkerSizeSpace[\"Media\"] = \"media\";\n MarkerSizeSpace[\"Screen\"] = \"screen\";\n })(MarkerSizeSpace || (MarkerSizeSpace = {}));\n\n var KeypointMarkerShape;\n (function (KeypointMarkerShape) {\n KeypointMarkerShape[\"Circle\"] = \"circle\";\n KeypointMarkerShape[\"Cross\"] = \"cross\";\n })(KeypointMarkerShape || (KeypointMarkerShape = {}));\n function rasterizePolygonToMask(points, dimensions) {\n const data = createEmptyMask(dimensions);\n if (points.length < 3) {\n return data;\n }\n const bounds = centerRectToTopLeftRect(getPointsRect(points));\n const startY = Math.max(0, Math.floor(bounds.y));\n const endY = Math.min(dimensions.height - 1, Math.ceil(bounds.y + bounds.height));\n for (let y = startY; y <= endY; y += 1) {\n const scanY = y + 0.5;\n const intersections = [];\n for (let index = 0; index < points.length; index += 1) {\n const current = points[index];\n const next = points[(index + 1) % points.length];\n if ((current.y <= scanY && next.y > scanY) ||\n (next.y <= scanY && current.y > scanY)) {\n const ratio = (scanY - current.y) / (next.y - current.y);\n intersections.push(current.x + ratio * (next.x - current.x));\n }\n }\n intersections.sort((left, right) => left - right);\n for (let index = 0; index < intersections.length - 1; index += 2) {\n const left = Math.max(0, Math.ceil(intersections[index]));\n const right = Math.min(dimensions.width - 1, Math.floor(intersections[index + 1]));\n for (let x = left; x <= right; x += 1) {\n data[y * dimensions.width + x] = 1;\n }\n }\n }\n return data;\n }\n function createEmptyMask(dimensions) {\n if (!Number.isInteger(dimensions.width) ||\n dimensions.width <= 0 ||\n !Number.isInteger(dimensions.height) ||\n dimensions.height <= 0) {\n throw new Error(\"Media dimensions must be positive integers.\");\n }\n return new Uint8Array(dimensions.width * dimensions.height);\n }\n\n const MAX_ID_MASK_PALETTE_ENTRIES = 64;\n const MAX_ID_MASK_STROKE_WIDTH = 16;\n function createIdMaskFrame(instructions) {\n if (instructions.length === 0) {\n return undefined;\n }\n const width = Math.max(...instructions.map(({ mask }) => mask.width));\n const height = Math.max(...instructions.map(({ mask }) => mask.height));\n const data = new Uint8Array(new ArrayBuffer(width * height));\n const fillPalette = new Float32Array(new ArrayBuffer(MAX_ID_MASK_PALETTE_ENTRIES * 4 * 4));\n const strokePalette = new Float32Array(new ArrayBuffer(MAX_ID_MASK_PALETTE_ENTRIES * 4 * 4));\n const strokeWidths = new Float32Array(new ArrayBuffer(MAX_ID_MASK_PALETTE_ENTRIES * 4));\n let hasStroke = false;\n let maxStrokeWidth = 0;\n for (const instruction of instructions) {\n const detectionMaskId = instruction.detectionIndex + 1;\n if (detectionMaskId <= 0 ||\n detectionMaskId >= MAX_ID_MASK_PALETTE_ENTRIES) {\n return undefined;\n }\n writePaletteEntry(fillPalette, detectionMaskId, instruction.color, instruction.alpha);\n if (instruction.stroke && instruction.stroke.width > 0) {\n const strokeWidth = Math.min(Math.max(0, instruction.stroke.width), MAX_ID_MASK_STROKE_WIDTH);\n hasStroke = true;\n strokeWidths[detectionMaskId] = strokeWidth;\n maxStrokeWidth = Math.max(maxStrokeWidth, strokeWidth);\n writePaletteEntry(strokePalette, detectionMaskId, instruction.stroke.color, instruction.stroke.alpha);\n }\n const decodedMask = decodeCompressedRleMask(instruction.mask);\n for (let y = 0; y < decodedMask.height; y += 1) {\n for (let x = 0; x < decodedMask.width; x += 1) {\n const maskOffset = y * decodedMask.width + x;\n if (decodedMask.data[maskOffset]) {\n data[y * width + x] = detectionMaskId;\n }\n }\n }\n }\n return {\n data,\n fillPalette,\n hasStroke,\n height,\n maxStrokeWidth,\n strokePalette,\n strokeWidths,\n width,\n };\n }\n function writePaletteEntry(palette, id, color, alpha) {\n const offset = id * 4;\n palette[offset] = ((color >> 16) & 0xff) / 255;\n palette[offset + 1] = ((color >> 8) & 0xff) / 255;\n palette[offset + 2] = (color & 0xff) / 255;\n palette[offset + 3] = Math.max(0, Math.min(alpha, 1));\n }\n\n var MediaRendererFit;\n (function (MediaRendererFit) {\n /**\n * Preserve media aspect ratio and fit the full frame inside the canvas.\n */\n MediaRendererFit[\"Contain\"] = \"contain\";\n /**\n * Preserve media aspect ratio and fill the canvas, cropping if necessary.\n */\n MediaRendererFit[\"Cover\"] = \"cover\";\n })(MediaRendererFit || (MediaRendererFit = {}));\n /**\n * Playback lifecycle state reported by a platform renderer.\n */\n var MediaRendererPlaybackState;\n (function (MediaRendererPlaybackState) {\n MediaRendererPlaybackState[\"Loading\"] = \"loading\";\n MediaRendererPlaybackState[\"Ready\"] = \"ready\";\n MediaRendererPlaybackState[\"Playing\"] = \"playing\";\n MediaRendererPlaybackState[\"Buffering\"] = \"buffering\";\n MediaRendererPlaybackState[\"Paused\"] = \"paused\";\n MediaRendererPlaybackState[\"Error\"] = \"error\";\n MediaRendererPlaybackState[\"Destroyed\"] = \"destroyed\";\n })(MediaRendererPlaybackState || (MediaRendererPlaybackState = {}));\n /**\n * Lower-level media source readiness.\n */\n var MediaSourceStatus;\n (function (MediaSourceStatus) {\n MediaSourceStatus[\"Loading\"] = \"loading\";\n MediaSourceStatus[\"Ready\"] = \"ready\";\n MediaSourceStatus[\"Error\"] = \"error\";\n MediaSourceStatus[\"Destroyed\"] = \"destroyed\";\n })(MediaSourceStatus || (MediaSourceStatus = {}));\n\n /**\n * Media-session operating mode.\n *\n * Core owns the names because file-like and stream-like lifecycle choices are\n * platform-neutral. Platform packages decide how these modes tune media,\n * storage, and renderer defaults.\n */\n var MediaSessionMode;\n (function (MediaSessionMode) {\n /**\n * Finite media. Defaults usually favor seek/replay and persistent detection\n * storage.\n */\n MediaSessionMode[\"File\"] = \"file\";\n /**\n * Live or append-only media. Defaults usually favor rolling windows and\n * bounded retention.\n */\n MediaSessionMode[\"Stream\"] = \"stream\";\n })(MediaSessionMode || (MediaSessionMode = {}));\n /**\n * Aggregate lifecycle state for a media session.\n */\n var MediaSessionStatus;\n (function (MediaSessionStatus) {\n MediaSessionStatus[\"Buffering\"] = \"buffering\";\n MediaSessionStatus[\"Destroyed\"] = \"destroyed\";\n MediaSessionStatus[\"Error\"] = \"error\";\n MediaSessionStatus[\"Loading\"] = \"loading\";\n MediaSessionStatus[\"Paused\"] = \"paused\";\n MediaSessionStatus[\"Playing\"] = \"playing\";\n MediaSessionStatus[\"Processing\"] = \"processing\";\n MediaSessionStatus[\"Ready\"] = \"ready\";\n })(MediaSessionStatus || (MediaSessionStatus = {}));\n /**\n * Subsystem currently affecting session readiness or presentation.\n */\n var MediaSessionActivityKind;\n (function (MediaSessionActivityKind) {\n MediaSessionActivityKind[\"DetectionsBuffering\"] = \"detectionsBuffering\";\n MediaSessionActivityKind[\"DetectionsLoading\"] = \"detectionsLoading\";\n MediaSessionActivityKind[\"Error\"] = \"error\";\n MediaSessionActivityKind[\"MediaNormalizing\"] = \"mediaNormalizing\";\n MediaSessionActivityKind[\"MediaOpening\"] = \"mediaOpening\";\n MediaSessionActivityKind[\"PlaybackBuffering\"] = \"playbackBuffering\";\n MediaSessionActivityKind[\"RenderPreparing\"] = \"renderPreparing\";\n })(MediaSessionActivityKind || (MediaSessionActivityKind = {}));\n /**\n * State of one session activity.\n */\n var MediaSessionActivityStatus;\n (function (MediaSessionActivityStatus) {\n MediaSessionActivityStatus[\"Error\"] = \"error\";\n MediaSessionActivityStatus[\"Running\"] = \"running\";\n MediaSessionActivityStatus[\"Waiting\"] = \"waiting\";\n })(MediaSessionActivityStatus || (MediaSessionActivityStatus = {}));\n\n const PNG_SIGNATURE = new Uint8Array([\n 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a,\n ]);\n function compositeMaskFrame(instructions) {\n const maskInstructions = materializeMaskInstructions(instructions);\n if (maskInstructions.length === 0) {\n return undefined;\n }\n const width = Math.max(...maskInstructions.map(({ mask }) => mask.width));\n const height = Math.max(...maskInstructions.map(({ mask }) => mask.height));\n const data = new Uint8ClampedArray(new ArrayBuffer(width * height * 4));\n for (const instruction of maskInstructions) {\n compositeInstruction(data, width, instruction);\n }\n return { data, height, width };\n }\n function createMaskIdFrame(instructions) {\n return createIdMaskFrame(materializeMaskInstructions(instructions));\n }\n async function createPngIdMaskFrame(instructions) {\n const frame = createMaskIdFrame(instructions);\n if (!frame) {\n return undefined;\n }\n return {\n ...frame,\n png: await encodeGrayscalePng({\n height: frame.height,\n pixels: frame.data,\n width: frame.width,\n }),\n };\n }\n function compositeInstruction(rgba, canvasWidth, instruction) {\n const decodedMask = decodeCompressedRleMask(instruction.mask);\n const fill = resolveRgbaColor(instruction.color, instruction.alpha);\n compositeMaskFill(rgba, canvasWidth, decodedMask, fill);\n if (instruction.stroke) {\n compositeMaskStroke(rgba, canvasWidth, decodedMask, instruction.stroke);\n }\n }\n function materializeMaskInstructions(instructions) {\n return instructions.map((instruction) => {\n if (instruction.mask) {\n return instruction;\n }\n const { height, points, width } = instruction.polygon;\n return {\n alpha: instruction.alpha,\n color: instruction.color,\n detectionIndex: instruction.detectionIndex,\n mask: encodeBinaryMask(rasterizePolygonToMask(points, { height, width }), width, height),\n stroke: instruction.stroke,\n };\n });\n }\n function compositeMaskFill(rgba, canvasWidth, decodedMask, fill) {\n for (let y = 0; y < decodedMask.height; y += 1) {\n for (let x = 0; x < decodedMask.width; x += 1) {\n const maskOffset = y * decodedMask.width + x;\n if (!decodedMask.data[maskOffset]) {\n continue;\n }\n writePixel(rgba, canvasWidth, x, y, fill);\n }\n }\n }\n function compositeMaskStroke(rgba, canvasWidth, decodedMask, stroke) {\n const width = Math.round(stroke.width);\n if (width <= 0) {\n return;\n }\n const strokeColor = resolveRgbaColor(stroke.color, stroke.alpha);\n for (let y = 0; y < decodedMask.height; y += 1) {\n for (let x = 0; x < decodedMask.width; x += 1) {\n if (!isMaskPixel(decodedMask, x, y) ||\n !isBoundaryPixel(decodedMask, x, y)) {\n continue;\n }\n for (let offsetY = -width; offsetY <= width; offsetY += 1) {\n for (let offsetX = -width; offsetX <= width; offsetX += 1) {\n const strokeX = x + offsetX;\n const strokeY = y + offsetY;\n if (isOutsideMaskBounds(decodedMask, strokeX, strokeY) ||\n isMaskPixel(decodedMask, strokeX, strokeY)) {\n continue;\n }\n writePixel(rgba, canvasWidth, strokeX, strokeY, strokeColor);\n }\n }\n }\n }\n }\n function isBoundaryPixel(mask, x, y) {\n for (let offsetY = -1; offsetY <= 1; offsetY += 1) {\n for (let offsetX = -1; offsetX <= 1; offsetX += 1) {\n if (offsetX === 0 && offsetY === 0) {\n continue;\n }\n const neighborX = x + offsetX;\n const neighborY = y + offsetY;\n if (isOutsideMaskBounds(mask, neighborX, neighborY) ||\n !isMaskPixel(mask, neighborX, neighborY)) {\n return true;\n }\n }\n }\n return false;\n }\n function isMaskPixel(mask, x, y) {\n return mask.data[y * mask.width + x] === 1;\n }\n function isOutsideMaskBounds(mask, x, y) {\n return x < 0 || y < 0 || x >= mask.width || y >= mask.height;\n }\n function resolveRgbaColor(color, alpha) {\n return {\n alpha: Math.round(Math.max(0, Math.min(alpha, 1)) * 255),\n blue: color & 0xff,\n green: (color >> 8) & 0xff,\n red: (color >> 16) & 0xff,\n };\n }\n function writePixel(rgba, canvasWidth, x, y, color) {\n const rgbaOffset = (y * canvasWidth + x) * 4;\n rgba[rgbaOffset] = color.red;\n rgba[rgbaOffset + 1] = color.green;\n rgba[rgbaOffset + 2] = color.blue;\n rgba[rgbaOffset + 3] = color.alpha;\n }\n async function encodeGrayscalePng(options) {\n if (typeof CompressionStream === \"undefined\") {\n throw new Error(\"CompressionStream is required to encode PNG ID masks.\");\n }\n const rawScanlines = createFilterlessPngScanlines(options);\n const ihdr = new Uint8Array(13);\n const ihdrView = new DataView(ihdr.buffer);\n ihdrView.setUint32(0, options.width);\n ihdrView.setUint32(4, options.height);\n ihdr[8] = 8;\n ihdr[9] = 0;\n ihdr[10] = 0;\n ihdr[11] = 0;\n ihdr[12] = 0;\n const compressed = new Uint8Array(await new Response(new Blob([rawScanlines])\n .stream()\n .pipeThrough(new CompressionStream(\"deflate\"))).arrayBuffer());\n return concatUint8Arrays([\n PNG_SIGNATURE,\n createPngChunk(\"IHDR\", ihdr),\n createPngChunk(\"IDAT\", compressed),\n createPngChunk(\"IEND\", new Uint8Array(0)),\n ]);\n }\n function createFilterlessPngScanlines(options) {\n const rowStride = options.width + 1;\n const scanlines = new Uint8Array(rowStride * options.height);\n for (let y = 0; y < options.height; y += 1) {\n const sourceOffset = y * options.width;\n const targetOffset = y * rowStride;\n scanlines[targetOffset] = 0;\n scanlines.set(options.pixels.subarray(sourceOffset, sourceOffset + options.width), targetOffset + 1);\n }\n return scanlines;\n }\n function createPngChunk(type, data) {\n const typeBytes = new TextEncoder().encode(type);\n const chunk = new Uint8Array(12 + data.length);\n const view = new DataView(chunk.buffer);\n view.setUint32(0, data.length);\n chunk.set(typeBytes, 4);\n chunk.set(data, 8);\n view.setUint32(8 + data.length, crc32(concatUint8Arrays([typeBytes, data])));\n return chunk;\n }\n const crc32Table = createCrc32Table();\n function createCrc32Table() {\n const table = new Uint32Array(256);\n for (let index = 0; index < table.length; index += 1) {\n let value = index;\n for (let bit = 0; bit < 8; bit += 1) {\n value = value & 1 ? 0xedb88320 ^ (value >>> 1) : value >>> 1;\n }\n table[index] = value >>> 0;\n }\n return table;\n }\n function crc32(bytes) {\n let crc = 0xffffffff;\n for (const byte of bytes) {\n crc = crc32Table[(crc ^ byte) & 0xff] ^ (crc >>> 8);\n }\n return (crc ^ 0xffffffff) >>> 0;\n }\n function concatUint8Arrays(chunks) {\n const totalLength = chunks.reduce((total, chunk) => total + chunk.length, 0);\n const result = new Uint8Array(totalLength);\n let offset = 0;\n for (const chunk of chunks) {\n result.set(chunk, offset);\n offset += chunk.length;\n }\n return result;\n }\n\n var PreparedMaskFrameKind;\n (function (PreparedMaskFrameKind) {\n PreparedMaskFrameKind[\"PngIdMask\"] = \"pngIdMask\";\n PreparedMaskFrameKind[\"RgbaImage\"] = \"rgbaImage\";\n })(PreparedMaskFrameKind || (PreparedMaskFrameKind = {}));\n\n var MaskPreparationWorkerMessageType;\n (function (MaskPreparationWorkerMessageType) {\n MaskPreparationWorkerMessageType[\"Complete\"] = \"complete\";\n MaskPreparationWorkerMessageType[\"Empty\"] = \"empty\";\n MaskPreparationWorkerMessageType[\"Error\"] = \"error\";\n MaskPreparationWorkerMessageType[\"Prepare\"] = \"prepare\";\n })(MaskPreparationWorkerMessageType || (MaskPreparationWorkerMessageType = {}));\n\n const workerScope = globalThis;\n workerScope.addEventListener(\"message\", (event) => {\n const message = event.data;\n if (message.type !== MaskPreparationWorkerMessageType.Prepare) {\n return;\n }\n void prepareMaskFrame(message);\n });\n async function prepareMaskFrame(message) {\n try {\n const pngIdMaskFrame = await createPngIdMaskWorkerResponse(message);\n if (pngIdMaskFrame) {\n workerScope.postMessage(pngIdMaskFrame, [\n pngIdMaskFrame.imageBitmap,\n pngIdMaskFrame.png.buffer,\n pngIdMaskFrame.fillPalette.buffer,\n pngIdMaskFrame.strokePalette.buffer,\n pngIdMaskFrame.strokeWidths.buffer,\n ]);\n return;\n }\n const compositedFrame = compositeMaskFrame(message.job.instructions);\n if (!compositedFrame) {\n workerScope.postMessage({\n key: message.job.key,\n requestId: message.requestId,\n type: MaskPreparationWorkerMessageType.Empty,\n });\n return;\n }\n const imageData = new ImageData(compositedFrame.data, compositedFrame.width, compositedFrame.height);\n const imageBitmap = createImageBitmapFromImageData(imageData);\n const idMaskData = createMaskIdFrame(message.job.instructions)?.data;\n if (imageBitmap) {\n workerScope.postMessage({\n imageBitmap,\n idMaskData,\n key: message.job.key,\n requestId: message.requestId,\n type: MaskPreparationWorkerMessageType.Complete,\n }, [...(idMaskData ? [idMaskData.buffer] : []), imageBitmap]);\n return;\n }\n workerScope.postMessage({\n idMaskData,\n imageData,\n key: message.job.key,\n requestId: message.requestId,\n type: MaskPreparationWorkerMessageType.Complete,\n }, [...(idMaskData ? [idMaskData.buffer] : []), imageData.data.buffer]);\n }\n catch (error) {\n workerScope.postMessage({\n error: error instanceof Error\n ? error.message\n : \"Unable to prepare mask frame.\",\n key: message.job.key,\n requestId: message.requestId,\n type: MaskPreparationWorkerMessageType.Error,\n });\n }\n }\n async function createPngIdMaskWorkerResponse(message) {\n if (typeof Blob === \"undefined\" ||\n typeof createImageBitmap === \"undefined\" ||\n typeof CompressionStream === \"undefined\") {\n return undefined;\n }\n let frame;\n try {\n frame = await createPngIdMaskFrame(message.job.instructions);\n }\n catch {\n return undefined;\n }\n if (!frame) {\n return undefined;\n }\n let imageBitmap;\n try {\n imageBitmap = await createImageBitmap(new Blob([frame.png], { type: \"image/png\" }));\n }\n catch {\n return undefined;\n }\n return {\n artifactKind: PreparedMaskFrameKind.PngIdMask,\n fillPalette: frame.fillPalette,\n hasStroke: frame.hasStroke,\n imageBitmap,\n key: message.job.key,\n maxStrokeWidth: frame.maxStrokeWidth,\n png: frame.png,\n requestId: message.requestId,\n strokePalette: frame.strokePalette,\n strokeWidths: frame.strokeWidths,\n type: MaskPreparationWorkerMessageType.Complete,\n };\n }\n function createImageBitmapFromImageData(imageData) {\n if (typeof OffscreenCanvas === \"undefined\") {\n return null;\n }\n const canvas = new OffscreenCanvas(imageData.width, imageData.height);\n const context = canvas.getContext(\"2d\");\n if (!context) {\n return null;\n }\n context.putImageData(imageData, 0, 0);\n return canvas.transferToImageBitmap();\n }\n\n})();";
5302
5905
 
5303
5906
  const DEFAULT_WORKER_NAME = "supervision-js-render-preparation";
5304
5907
  let defaultWorkerUrl;
@@ -5361,85 +5964,6 @@ var MaskPreparationWorkerMessageType;
5361
5964
  MaskPreparationWorkerMessageType["Prepare"] = "prepare";
5362
5965
  })(MaskPreparationWorkerMessageType || (MaskPreparationWorkerMessageType = {}));
5363
5966
 
5364
- function createWorkerRpcClient(options) {
5365
- let failureMessage = null;
5366
- let isDestroyed = false;
5367
- let nextRequestId = 1;
5368
- const pendingRequests = new Map();
5369
- options.worker.addEventListener("message", (event) => {
5370
- handleWorkerMessage(event.data);
5371
- });
5372
- options.worker.addEventListener("error", (event) => {
5373
- rejectPendingRequests(event.message || options.defaultErrorMessage);
5374
- });
5375
- options.worker.addEventListener("messageerror", () => {
5376
- rejectPendingRequests(`${options.defaultErrorMessage}: message error.`);
5377
- });
5378
- return {
5379
- destroy() {
5380
- if (isDestroyed) {
5381
- return;
5382
- }
5383
- isDestroyed = true;
5384
- rejectPendingRequests("Worker RPC client has been destroyed.", {
5385
- markFailure: false,
5386
- });
5387
- options.worker.terminate();
5388
- },
5389
- getFailureMessage() {
5390
- return failureMessage;
5391
- },
5392
- request(message) {
5393
- if (isDestroyed) {
5394
- return Promise.reject(new Error("Worker RPC client has been destroyed."));
5395
- }
5396
- if (failureMessage) {
5397
- return Promise.reject(new Error(failureMessage));
5398
- }
5399
- const request = {
5400
- ...message,
5401
- requestId: nextRequestId,
5402
- };
5403
- nextRequestId += 1;
5404
- return new Promise((resolve, reject) => {
5405
- pendingRequests.set(request.requestId, { reject, resolve });
5406
- try {
5407
- options.worker.postMessage(request);
5408
- }
5409
- catch (error) {
5410
- pendingRequests.delete(request.requestId);
5411
- failureMessage = getErrorMessage$2(error, options.defaultErrorMessage);
5412
- reject(new Error(failureMessage));
5413
- }
5414
- });
5415
- },
5416
- };
5417
- function handleWorkerMessage(message) {
5418
- if (!options.isResponse(message)) {
5419
- return;
5420
- }
5421
- const pendingRequest = pendingRequests.get(message.requestId);
5422
- if (!pendingRequest) {
5423
- options.onOrphanedResponse?.(message);
5424
- return;
5425
- }
5426
- pendingRequests.delete(message.requestId);
5427
- pendingRequest.resolve(message);
5428
- }
5429
- function rejectPendingRequests(message, options = { markFailure: true }) {
5430
- if (options.markFailure) {
5431
- failureMessage = message;
5432
- }
5433
- for (const pendingRequest of pendingRequests.values()) {
5434
- pendingRequest.reject(new Error(message));
5435
- }
5436
- pendingRequests.clear();
5437
- }
5438
- }
5439
- function getErrorMessage$2(error, fallback) {
5440
- return error instanceof Error ? error.message : fallback;
5441
- }
5442
-
5443
5967
  function createMaskFramePreparer(options = {}) {
5444
5968
  const mainThreadPreparer = createMainThreadMaskFramePreparer(RenderPreparationWorkerStatus.Disabled);
5445
5969
  const mode = options.renderPreparation?.mode ?? RenderPreparationMode.Auto;
@@ -5573,6 +6097,7 @@ function createMainThreadMaskFramePreparer(workerStatus, message = null) {
5573
6097
  canvas.height = 0;
5574
6098
  },
5575
6099
  height: compositedFrame.height,
6100
+ idMaskData: createMaskIdFrame(job.instructions)?.data,
5576
6101
  key: job.key,
5577
6102
  kind: PreparedMaskFrameKind.RgbaImage,
5578
6103
  source: canvas,
@@ -5747,6 +6272,7 @@ function createPreparedFrameFromWorkerResponse(message) {
5747
6272
  message.imageBitmap?.close();
5748
6273
  },
5749
6274
  height: message.imageBitmap.height,
6275
+ idMaskData: message.idMaskData,
5750
6276
  key: message.key,
5751
6277
  kind: PreparedMaskFrameKind.RgbaImage,
5752
6278
  source: message.imageBitmap,
@@ -5770,6 +6296,7 @@ function createPreparedFrameFromWorkerResponse(message) {
5770
6296
  canvas.height = 0;
5771
6297
  },
5772
6298
  height: message.imageData.height,
6299
+ idMaskData: message.idMaskData,
5773
6300
  key: message.key,
5774
6301
  kind: PreparedMaskFrameKind.RgbaImage,
5775
6302
  source: canvas,
@@ -6638,7 +7165,7 @@ function createPixiIdMaskShaderRenderer(options) {
6638
7165
  autoGenerateMipmaps: false,
6639
7166
  dynamic: false,
6640
7167
  height: 1,
6641
- resource: createPlaceholderCanvas(),
7168
+ resource: createPlaceholderCanvas$1(),
6642
7169
  scaleMode: "nearest",
6643
7170
  width: 1,
6644
7171
  });
@@ -6728,7 +7255,7 @@ function createPixiIdMaskShaderRenderer(options) {
6728
7255
  mesh.shader = shader;
6729
7256
  }
6730
7257
  }
6731
- function createPlaceholderCanvas() {
7258
+ function createPlaceholderCanvas$1() {
6732
7259
  const canvas = document.createElement("canvas");
6733
7260
  canvas.height = 1;
6734
7261
  canvas.width = 1;
@@ -6878,15 +7405,219 @@ void main(void) {
6878
7405
  }
6879
7406
  `;
6880
7407
 
7408
+ /**
7409
+ * Renders blurred, per-detection-colored copies of the prepared id mask
7410
+ * beneath the mask layer. Each pass maps mask ids through a halo palette and
7411
+ * a GPU blur filter turns that coverage into a smooth glow following the
7412
+ * exact mask silhouette, reusing the already-prepared mask texture. Passes
7413
+ * are pooled and one runs per distinct spread.
7414
+ */
7415
+ function createPixiMaskHaloRenderer(options) {
7416
+ const display = new options.Container();
7417
+ const placeholderSource = new options.ImageSource({
7418
+ autoGenerateMipmaps: false,
7419
+ dynamic: false,
7420
+ height: 1,
7421
+ resource: createPlaceholderCanvas(),
7422
+ scaleMode: "nearest",
7423
+ width: 1,
7424
+ });
7425
+ const geometry = new options.MeshGeometry({
7426
+ indices: new Uint32Array([0, 1, 2, 0, 2, 3]),
7427
+ positions: new Float32Array([
7428
+ 0,
7429
+ 0,
7430
+ options.mediaWidth,
7431
+ 0,
7432
+ options.mediaWidth,
7433
+ options.mediaHeight,
7434
+ 0,
7435
+ options.mediaHeight,
7436
+ ]),
7437
+ shrinkBuffersToFit: true,
7438
+ topology: "triangle-list",
7439
+ uvs: new Float32Array([0, 0, 1, 0, 1, 1, 0, 1]),
7440
+ });
7441
+ const passes = [];
7442
+ display.visible = false;
7443
+ return {
7444
+ destroy() {
7445
+ for (const pass of passes) {
7446
+ pass.mesh.destroy();
7447
+ pass.shader.destroy(true);
7448
+ }
7449
+ passes.length = 0;
7450
+ display.destroy();
7451
+ geometry.destroy();
7452
+ placeholderSource.destroy();
7453
+ },
7454
+ display,
7455
+ hide() {
7456
+ display.visible = false;
7457
+ },
7458
+ render(frame, texture, groups) {
7459
+ if (groups.length === 0) {
7460
+ display.visible = false;
7461
+ return;
7462
+ }
7463
+ for (const [index, group] of groups.entries()) {
7464
+ const pass = ensurePass(index);
7465
+ bindTexture(pass, texture.source);
7466
+ pass.uniforms.uniforms.uHaloPalette = group.palette;
7467
+ pass.uniforms.update();
7468
+ pass.blurFilter.strength = Math.max(group.spread, 0);
7469
+ pass.mesh.visible = true;
7470
+ }
7471
+ for (const pass of passes.slice(groups.length)) {
7472
+ pass.mesh.visible = false;
7473
+ }
7474
+ display.visible = true;
7475
+ },
7476
+ setOpacity(opacity) {
7477
+ display.alpha = opacity;
7478
+ },
7479
+ };
7480
+ function ensurePass(index) {
7481
+ const existing = passes[index];
7482
+ if (existing) {
7483
+ return existing;
7484
+ }
7485
+ const uniforms = new options.UniformGroup({
7486
+ uHaloPalette: {
7487
+ size: MAX_ID_MASK_PALETTE_ENTRIES,
7488
+ type: "vec4<f32>",
7489
+ value: new Float32Array(MAX_ID_MASK_PALETTE_ENTRIES * 4),
7490
+ },
7491
+ });
7492
+ const shader = createShader(uniforms);
7493
+ const mesh = new options.Mesh({ geometry, shader });
7494
+ const blurFilter = new options.BlurFilter({ quality: 4, strength: 8 });
7495
+ // Filters size their working texture from measured bounds, and a
7496
+ // custom-geometry mesh measures empty; an explicit bounds area keeps the
7497
+ // blur pass covering the full media rect.
7498
+ mesh.boundsArea = new options.Rectangle(0, 0, options.mediaWidth, options.mediaHeight);
7499
+ mesh.filters = [blurFilter];
7500
+ display.addChild(mesh);
7501
+ const pass = { blurFilter, mesh, shader, uniforms };
7502
+ passes.push(pass);
7503
+ return pass;
7504
+ }
7505
+ function bindTexture(pass, source) {
7506
+ try {
7507
+ pass.shader.resources.uTexture = source;
7508
+ pass.shader.resources.uSampler = source.style;
7509
+ }
7510
+ catch {
7511
+ try {
7512
+ pass.shader.destroy(true);
7513
+ }
7514
+ catch {
7515
+ // Pixi has already invalidated this shader's resource group.
7516
+ }
7517
+ pass.shader = createShader(pass.uniforms);
7518
+ pass.mesh.shader = pass.shader;
7519
+ pass.shader.resources.uTexture = source;
7520
+ pass.shader.resources.uSampler = source.style;
7521
+ }
7522
+ }
7523
+ function createShader(uniforms) {
7524
+ return options.Shader.from({
7525
+ gl: {
7526
+ fragment: maskHaloFragmentShader,
7527
+ vertex: maskHaloVertexShader,
7528
+ },
7529
+ resources: {
7530
+ haloUniforms: uniforms,
7531
+ uSampler: placeholderSource.style,
7532
+ uTexture: placeholderSource,
7533
+ },
7534
+ });
7535
+ }
7536
+ }
7537
+ /**
7538
+ * Builds one halo palette uniform: premultiplied halo color and alpha per
7539
+ * mask id, transparent for ids outside the group.
7540
+ */
7541
+ function buildMaskHaloPalette(halos) {
7542
+ const palette = new Float32Array(MAX_ID_MASK_PALETTE_ENTRIES * 4);
7543
+ for (const [maskId, halo] of halos) {
7544
+ if (maskId <= 0 || maskId >= MAX_ID_MASK_PALETTE_ENTRIES) {
7545
+ continue;
7546
+ }
7547
+ const alpha = Math.max(0, Math.min(halo.alpha, 1));
7548
+ const offset = maskId * 4;
7549
+ palette[offset] = (((halo.color >> 16) & 0xff) / 255) * alpha;
7550
+ palette[offset + 1] = (((halo.color >> 8) & 0xff) / 255) * alpha;
7551
+ palette[offset + 2] = ((halo.color & 0xff) / 255) * alpha;
7552
+ palette[offset + 3] = alpha;
7553
+ }
7554
+ return palette;
7555
+ }
7556
+ function createPlaceholderCanvas() {
7557
+ const canvas = document.createElement("canvas");
7558
+ canvas.height = 1;
7559
+ canvas.width = 1;
7560
+ return canvas;
7561
+ }
7562
+ const maskHaloVertexShader = `#version 300 es
7563
+ precision highp float;
7564
+
7565
+ in vec2 aPosition;
7566
+ in vec2 aUV;
7567
+
7568
+ uniform mat3 uProjectionMatrix;
7569
+ uniform mat3 uWorldTransformMatrix;
7570
+ uniform vec4 uWorldColorAlpha;
7571
+ uniform mat3 uTransformMatrix;
7572
+ uniform vec4 uColor;
7573
+
7574
+ out vec2 vUV;
7575
+ out vec4 vColor;
7576
+
7577
+ void main(void) {
7578
+ mat3 modelViewProjectionMatrix =
7579
+ uProjectionMatrix * uWorldTransformMatrix * uTransformMatrix;
7580
+
7581
+ gl_Position =
7582
+ vec4((modelViewProjectionMatrix * vec3(aPosition, 1.0)).xy, 0.0, 1.0);
7583
+ vUV = aUV;
7584
+ vColor = uWorldColorAlpha * uColor;
7585
+ }
7586
+ `;
7587
+ const maskHaloFragmentShader = `#version 300 es
7588
+ precision highp float;
7589
+ precision highp int;
7590
+
7591
+ in vec2 vUV;
7592
+ in vec4 vColor;
7593
+
7594
+ uniform sampler2D uTexture;
7595
+ uniform vec4 uHaloPalette[${MAX_ID_MASK_PALETTE_ENTRIES}];
7596
+
7597
+ out vec4 finalColor;
7598
+
7599
+ void main(void) {
7600
+ float maskId = floor(texture(uTexture, vUV).r * 255.0 + 0.5);
7601
+ int paletteIndex =
7602
+ int(clamp(maskId, 0.0, float(${MAX_ID_MASK_PALETTE_ENTRIES - 1})));
7603
+
7604
+ finalColor = uHaloPalette[paletteIndex] * vColor;
7605
+ }
7606
+ `;
7607
+
6881
7608
  const MAX_PENDING_MASK_HOLD_SECONDS = 0.05;
6882
7609
  function createPixiMaskLayer(options) {
6883
7610
  let mediaHeight = 0;
6884
7611
  let mediaWidth = 0;
6885
7612
  let idMaskRenderer;
7613
+ let haloRenderer;
7614
+ let currentMaskHaloStyle = options.maskHaloStyle ?? null;
7615
+ options.maskStyle;
6886
7616
  let maskSprite;
6887
7617
  let activeFrameKey = null;
6888
7618
  let activeFrameMediaTime = null;
6889
7619
  let activeIdMaskFrame = null;
7620
+ let activeRgbaMaskFrame = null;
6890
7621
  let maskOpacity = resolveMaskStyleOpacity(options.maskStyle);
6891
7622
  let maskPickCanvas;
6892
7623
  let maskPickContext;
@@ -6894,6 +7625,7 @@ function createPixiMaskLayer(options) {
6894
7625
  let visibleMaskMediaTime = null;
6895
7626
  let isDestroyed = false;
6896
7627
  const maskTextures = new Map();
7628
+ const haloTextures = new Map();
6897
7629
  const preparedRenderWindow = createPreparedRenderWindow({
6898
7630
  artifactKind: options.artifactKind,
6899
7631
  detectionTimeline: options.detectionTimeline,
@@ -6939,10 +7671,19 @@ function createPixiMaskLayer(options) {
6939
7671
  maskSprite.height = mediaHeight;
6940
7672
  idMaskRenderer = createIdMaskRenderer();
6941
7673
  idMaskRenderer?.setOpacity(maskOpacity);
7674
+ haloRenderer = createHaloRenderer();
7675
+ // Halo alpha is resolved from MaskHaloStyle per detection. It must not
7676
+ // inherit the independent mask renderer's global opacity.
7677
+ haloRenderer?.setOpacity(1);
6942
7678
  if (!idMaskRenderer || !options.Container) {
6943
7679
  return maskSprite;
6944
7680
  }
6945
7681
  const maskContainer = new options.Container();
7682
+ // The halo draws beneath the mask so the glow bleeds outward from the
7683
+ // silhouette while fills and borders stay crisp on top.
7684
+ if (haloRenderer) {
7685
+ maskContainer.addChild(haloRenderer.display);
7686
+ }
6946
7687
  maskContainer.addChild(maskSprite, idMaskRenderer.mesh);
6947
7688
  return maskContainer;
6948
7689
  },
@@ -6952,6 +7693,7 @@ function createPixiMaskLayer(options) {
6952
7693
  activeFrameKey = preparedFrame?.key ?? null;
6953
7694
  activeFrameMediaTime = preparedFrame?.detectionFrame.mediaTime ?? null;
6954
7695
  activeIdMaskFrame = null;
7696
+ activeRgbaMaskFrame = null;
6955
7697
  hideSprite();
6956
7698
  return;
6957
7699
  }
@@ -6964,6 +7706,7 @@ function createPixiMaskLayer(options) {
6964
7706
  if (preparedFrame.maskStatus === PreparedRenderFrameMaskStatus.Empty ||
6965
7707
  preparedFrame.maskStatus === PreparedRenderFrameMaskStatus.Disabled) {
6966
7708
  activeIdMaskFrame = null;
7709
+ activeRgbaMaskFrame = null;
6967
7710
  hideSprite();
6968
7711
  return;
6969
7712
  }
@@ -7016,6 +7759,14 @@ function createPixiMaskLayer(options) {
7016
7759
  applyMaskOpacity();
7017
7760
  }
7018
7761
  preparedRenderWindow.setMaskStyle(nextMaskStyle);
7762
+ refreshHalo();
7763
+ },
7764
+ setMaskHaloStyle(nextMaskHaloStyle) {
7765
+ if (nextMaskHaloStyle === undefined) {
7766
+ return;
7767
+ }
7768
+ currentMaskHaloStyle = nextMaskHaloStyle;
7769
+ refreshHalo();
7019
7770
  },
7020
7771
  destroy() {
7021
7772
  if (isDestroyed) {
@@ -7026,17 +7777,22 @@ function createPixiMaskLayer(options) {
7026
7777
  destroyTextures();
7027
7778
  resetMaskPickCanvas();
7028
7779
  idMaskRenderer?.destroy();
7780
+ haloRenderer?.destroy();
7029
7781
  },
7030
7782
  };
7031
7783
  function showMaskFrame(maskFrame, mediaTime) {
7032
7784
  visibleMaskMediaTime = mediaTime;
7033
7785
  activeIdMaskFrame =
7034
7786
  maskFrame.kind === PreparedMaskFrameKind.PngIdMask ? maskFrame : null;
7787
+ activeRgbaMaskFrame =
7788
+ maskFrame.kind === PreparedMaskFrameKind.RgbaImage ? maskFrame : null;
7035
7789
  if (maskFrame.kind === PreparedMaskFrameKind.PngIdMask && idMaskRenderer) {
7036
7790
  showIdMaskFrame(maskFrame);
7037
7791
  return;
7038
7792
  }
7039
- showRgbaMaskFrame(maskFrame);
7793
+ if (maskFrame.kind === PreparedMaskFrameKind.RgbaImage) {
7794
+ showRgbaMaskFrame(maskFrame);
7795
+ }
7040
7796
  }
7041
7797
  function getTexture(maskFrame) {
7042
7798
  const existingTexture = maskTextures.get(maskFrame.key);
@@ -7060,6 +7816,40 @@ function createPixiMaskLayer(options) {
7060
7816
  maskTextures.set(maskFrame.key, texture);
7061
7817
  return texture;
7062
7818
  }
7819
+ function getHaloTexture(maskFrame) {
7820
+ const existingTexture = haloTextures.get(maskFrame.key);
7821
+ if (existingTexture) {
7822
+ return existingTexture;
7823
+ }
7824
+ if (!maskFrame.idMaskData || typeof document === "undefined") {
7825
+ return undefined;
7826
+ }
7827
+ const canvas = document.createElement("canvas");
7828
+ const context = canvas.getContext("2d");
7829
+ if (!context || typeof context.createImageData !== "function") {
7830
+ return undefined;
7831
+ }
7832
+ canvas.width = maskFrame.width;
7833
+ canvas.height = maskFrame.height;
7834
+ const imageData = context.createImageData(maskFrame.width, maskFrame.height);
7835
+ for (let index = 0; index < maskFrame.idMaskData.length; index += 1) {
7836
+ const offset = index * 4;
7837
+ imageData.data[offset] = maskFrame.idMaskData[index];
7838
+ imageData.data[offset + 3] = 255;
7839
+ }
7840
+ context.putImageData(imageData, 0, 0);
7841
+ const source = new options.ImageSource({
7842
+ autoGenerateMipmaps: false,
7843
+ dynamic: false,
7844
+ height: maskFrame.height,
7845
+ resource: canvas,
7846
+ scaleMode: "nearest",
7847
+ width: maskFrame.width,
7848
+ });
7849
+ const texture = new options.Texture({ dynamic: false, source });
7850
+ haloTextures.set(maskFrame.key, texture);
7851
+ return texture;
7852
+ }
7063
7853
  function showRgbaMaskFrame(maskFrame) {
7064
7854
  if (!maskSprite) {
7065
7855
  return;
@@ -7071,6 +7861,13 @@ function createPixiMaskLayer(options) {
7071
7861
  maskSprite.height = mediaHeight;
7072
7862
  maskSprite.visible = true;
7073
7863
  idMaskRenderer?.hide();
7864
+ const haloTexture = getHaloTexture(maskFrame);
7865
+ if (haloTexture) {
7866
+ renderHalo(maskFrame, haloTexture);
7867
+ }
7868
+ else {
7869
+ haloRenderer?.hide();
7870
+ }
7074
7871
  }
7075
7872
  function showIdMaskFrame(maskFrame) {
7076
7873
  if (!idMaskRenderer || !maskSprite) {
@@ -7078,14 +7875,82 @@ function createPixiMaskLayer(options) {
7078
7875
  }
7079
7876
  maskSprite.visible = false;
7080
7877
  idMaskRenderer.render(maskFrame, getTexture(maskFrame));
7878
+ renderHalo(maskFrame, getTexture(maskFrame));
7879
+ }
7880
+ function renderHalo(maskFrame, texture) {
7881
+ if (!haloRenderer) {
7882
+ return;
7883
+ }
7884
+ const groups = resolveFrameHaloGroups();
7885
+ if (groups.length === 0) {
7886
+ haloRenderer.hide();
7887
+ return;
7888
+ }
7889
+ haloRenderer.render(maskFrame, texture, groups);
7890
+ }
7891
+ function refreshHalo() {
7892
+ if (activeIdMaskFrame) {
7893
+ renderHalo(activeIdMaskFrame, getTexture(activeIdMaskFrame));
7894
+ return;
7895
+ }
7896
+ if (activeRgbaMaskFrame) {
7897
+ const texture = getHaloTexture(activeRgbaMaskFrame);
7898
+ if (texture) {
7899
+ renderHalo(activeRgbaMaskFrame, texture);
7900
+ }
7901
+ }
7902
+ }
7903
+ /**
7904
+ * Halo instructions resolve per frame from the live halo style rather than
7905
+ * being baked into the prepared artifact, so halo edits never invalidate
7906
+ * prepared mask pixels. Detections are grouped by their requested spread
7907
+ * and every group renders in its own blur pass, preserving each
7908
+ * detection's public spread contract.
7909
+ */
7910
+ function resolveFrameHaloGroups() {
7911
+ if (visibleMaskMediaTime === null || !currentMaskHaloStyle) {
7912
+ return [];
7913
+ }
7914
+ const frame = options.detectionTimeline.selectFrame(visibleMaskMediaTime);
7915
+ if (!frame) {
7916
+ return [];
7917
+ }
7918
+ const bySpread = new Map();
7919
+ for (const [detectionIndex, detection] of frame.detections.entries()) {
7920
+ if (!detection.mask) {
7921
+ continue;
7922
+ }
7923
+ const halo = currentMaskHaloStyle.resolve(detection, {
7924
+ detectionIndex,
7925
+ frame,
7926
+ mediaTime: visibleMaskMediaTime,
7927
+ });
7928
+ if (!halo || halo.alpha <= 0 || halo.spread <= 0) {
7929
+ continue;
7930
+ }
7931
+ let group = bySpread.get(halo.spread);
7932
+ if (!group) {
7933
+ group = new Map();
7934
+ bySpread.set(halo.spread, group);
7935
+ }
7936
+ group.set(detectionIndex + 1, { alpha: halo.alpha, color: halo.color });
7937
+ }
7938
+ return [...bySpread.entries()]
7939
+ .sort(([left], [right]) => left - right)
7940
+ .map(([spread, halos]) => ({
7941
+ palette: buildMaskHaloPalette(halos),
7942
+ spread,
7943
+ }));
7081
7944
  }
7082
7945
  function hideSprite() {
7083
7946
  visibleMaskMediaTime = null;
7084
7947
  activeIdMaskFrame = null;
7948
+ activeRgbaMaskFrame = null;
7085
7949
  if (maskSprite) {
7086
7950
  maskSprite.visible = false;
7087
7951
  }
7088
7952
  idMaskRenderer?.hide();
7953
+ haloRenderer?.hide();
7089
7954
  }
7090
7955
  function canHoldVisibleMaskFor(mediaTime) {
7091
7956
  return (visibleMaskMediaTime !== null &&
@@ -7101,11 +7966,21 @@ function createPixiMaskLayer(options) {
7101
7966
  function destroyTexture(key) {
7102
7967
  const texture = maskTextures.get(key);
7103
7968
  if (!texture) {
7969
+ destroyHaloTexture(key);
7104
7970
  return;
7105
7971
  }
7106
7972
  releaseTextureBindings(key, texture);
7107
7973
  maskTextures.delete(key);
7108
7974
  texture.destroy(true);
7975
+ destroyHaloTexture(key);
7976
+ }
7977
+ function destroyHaloTexture(key) {
7978
+ const texture = haloTextures.get(key);
7979
+ if (!texture) {
7980
+ return;
7981
+ }
7982
+ haloTextures.delete(key);
7983
+ texture.destroy(true);
7109
7984
  }
7110
7985
  function destroyTextures() {
7111
7986
  releaseTextureBindings();
@@ -7113,6 +7988,10 @@ function createPixiMaskLayer(options) {
7113
7988
  texture.destroy(true);
7114
7989
  }
7115
7990
  maskTextures.clear();
7991
+ for (const texture of haloTextures.values()) {
7992
+ texture.destroy(true);
7993
+ }
7994
+ haloTextures.clear();
7116
7995
  }
7117
7996
  function releaseTextureBindings(key, texture) {
7118
7997
  if (!texture || maskSprite?.texture === texture) {
@@ -7124,6 +8003,31 @@ function createPixiMaskLayer(options) {
7124
8003
  idMaskRenderer?.clearTexture();
7125
8004
  }
7126
8005
  }
8006
+ function createHaloRenderer() {
8007
+ if (!options.BlurFilter ||
8008
+ !options.Container ||
8009
+ !options.Rectangle ||
8010
+ !options.Mesh ||
8011
+ !options.MeshGeometry ||
8012
+ !options.Shader ||
8013
+ !options.UniformGroup ||
8014
+ mediaWidth <= 0 ||
8015
+ mediaHeight <= 0) {
8016
+ return undefined;
8017
+ }
8018
+ return createPixiMaskHaloRenderer({
8019
+ BlurFilter: options.BlurFilter,
8020
+ Container: options.Container,
8021
+ Rectangle: options.Rectangle,
8022
+ ImageSource: options.ImageSource,
8023
+ Mesh: options.Mesh,
8024
+ MeshGeometry: options.MeshGeometry,
8025
+ Shader: options.Shader,
8026
+ UniformGroup: options.UniformGroup,
8027
+ mediaHeight,
8028
+ mediaWidth,
8029
+ });
8030
+ }
7127
8031
  function createIdMaskRenderer() {
7128
8032
  if (!options.Mesh ||
7129
8033
  !options.MeshGeometry ||
@@ -7324,6 +8228,9 @@ function canPreparePolygonInstruction(instruction) {
7324
8228
  return (instruction.points.length >= 3 &&
7325
8229
  Boolean(instruction.fill || stroke) &&
7326
8230
  !stroke?.dash?.length &&
8231
+ stroke?.cap === undefined &&
8232
+ stroke?.join === undefined &&
8233
+ stroke?.miterLimit === undefined &&
7327
8234
  (stroke?.alignment === undefined ||
7328
8235
  stroke.alignment === BoxStrokeAlignment.Center));
7329
8236
  }
@@ -7368,6 +8275,367 @@ function resolvePreparedPolygonInstructions(options) {
7368
8275
  return instructions;
7369
8276
  }
7370
8277
 
8278
+ const sharedAssets = new WeakMap();
8279
+ /** Browser implementation for asset-backed region renderer descriptors. */
8280
+ function createPixiRegionLayer(options) {
8281
+ let container;
8282
+ let currentFrame;
8283
+ let currentMediaTime = 0;
8284
+ let currentViewportScale = 1;
8285
+ let destroyed = false;
8286
+ let renderers = [...options.regionRenderers];
8287
+ const assets = new Map();
8288
+ const entries = new Map();
8289
+ const pools = new Map();
8290
+ syncAssets();
8291
+ return {
8292
+ createContainer() {
8293
+ if (!container) {
8294
+ container = new options.Container();
8295
+ container.sortableChildren = true;
8296
+ }
8297
+ return container;
8298
+ },
8299
+ drawFrame(mediaTime, viewportScale = 1) {
8300
+ currentMediaTime = mediaTime;
8301
+ currentViewportScale = viewportScale;
8302
+ currentFrame = options.detectionTimeline.selectFrame(mediaTime);
8303
+ const activeKeys = new Set();
8304
+ const activeDetectionIndexes = new Set();
8305
+ for (const entry of entries.values())
8306
+ entry.active = false;
8307
+ if (currentFrame) {
8308
+ for (const [rendererIndex, renderer] of renderers.entries()) {
8309
+ const asset = assets.get(renderer.id);
8310
+ if (!asset?.asset)
8311
+ continue;
8312
+ for (const [detectionIndex, detection,] of currentFrame.detections.entries()) {
8313
+ const context = {
8314
+ detectionIndex,
8315
+ frame: currentFrame,
8316
+ mediaTime,
8317
+ viewportScale,
8318
+ ...options.resolveContextState?.(detection),
8319
+ };
8320
+ if (context.hidden ||
8321
+ !matchesTarget(renderer, detection, context)) {
8322
+ continue;
8323
+ }
8324
+ const region = resolveRegion(renderer, detection);
8325
+ if (!region)
8326
+ continue;
8327
+ const key = resolveSpriteKey(renderer.id, detection, detectionIndex);
8328
+ const entry = ensureEntry(key, renderer.id, asset.src, asset.asset, detection.id);
8329
+ const position = positionSprite(entry.display, renderer, region, asset.asset);
8330
+ entry.baseX = position.x;
8331
+ entry.baseY = position.y;
8332
+ entry.detectionId = detection.id;
8333
+ entry.display.zIndex =
8334
+ (renderer.compose?.zIndex ?? 0) * 1_000_000 +
8335
+ rendererIndex * 10_000 +
8336
+ (detection.zIndex ?? detectionIndex);
8337
+ entry.display.visible = true;
8338
+ entry.active = true;
8339
+ activeKeys.add(key);
8340
+ activeDetectionIndexes.add(detectionIndex);
8341
+ }
8342
+ }
8343
+ }
8344
+ for (const [key, entry] of entries) {
8345
+ if (activeKeys.has(key))
8346
+ continue;
8347
+ entry.display.visible = false;
8348
+ pauseAnimatedDisplay(entry.display);
8349
+ entries.delete(key);
8350
+ const poolKey = resolvePoolKey(entry.rendererId, entry.src);
8351
+ const pool = pools.get(poolKey) ?? [];
8352
+ pool.push(entry);
8353
+ pools.set(poolKey, pool);
8354
+ }
8355
+ return { activeDetectionIndexes: [...activeDetectionIndexes] };
8356
+ },
8357
+ setRenderers(nextRenderers) {
8358
+ renderers = [...nextRenderers];
8359
+ syncAssets();
8360
+ this.drawFrame(currentMediaTime, currentViewportScale);
8361
+ },
8362
+ translateDetection(id, x, y) {
8363
+ let translated = false;
8364
+ for (const entry of entries.values()) {
8365
+ if (!entry.active || entry.detectionId !== id)
8366
+ continue;
8367
+ entry.display.position.set(entry.baseX + x, entry.baseY + y);
8368
+ translated = true;
8369
+ }
8370
+ return translated;
8371
+ },
8372
+ destroy() {
8373
+ destroyed = true;
8374
+ destroyAllDisplays();
8375
+ for (const asset of assets.values())
8376
+ asset.lease.release();
8377
+ assets.clear();
8378
+ container = undefined;
8379
+ currentFrame = undefined;
8380
+ },
8381
+ };
8382
+ function syncAssets() {
8383
+ const desired = new Map(renderers.map((renderer) => [renderer.id, renderer.source.asset.src]));
8384
+ for (const [id, asset] of assets) {
8385
+ if (desired.get(id) === asset.src)
8386
+ continue;
8387
+ destroyRendererDisplays(id);
8388
+ asset.lease.release();
8389
+ assets.delete(id);
8390
+ }
8391
+ for (const [id, src] of desired) {
8392
+ if (assets.has(id))
8393
+ continue;
8394
+ const lease = acquireAsset(options.Assets, src);
8395
+ const asset = { lease, src };
8396
+ assets.set(id, asset);
8397
+ void lease.asset.then((loadedAsset) => {
8398
+ if (destroyed || assets.get(id) !== asset)
8399
+ return;
8400
+ asset.asset = loadedAsset;
8401
+ options.onInvalidate?.();
8402
+ }, (error) => {
8403
+ // A failed asset is omitted. Replacing the descriptor retries it.
8404
+ options.onAssetError?.({ error, rendererId: id, src });
8405
+ });
8406
+ }
8407
+ }
8408
+ function ensureEntry(key, rendererId, src, asset, detectionId) {
8409
+ let entry = entries.get(key);
8410
+ if (entry)
8411
+ return entry;
8412
+ const poolKey = resolvePoolKey(rendererId, src);
8413
+ const pool = pools.get(poolKey);
8414
+ entry = pool?.pop();
8415
+ if (pool?.length === 0)
8416
+ pools.delete(poolKey);
8417
+ if (entry) {
8418
+ entry.detectionId = detectionId;
8419
+ resumeAnimatedDisplay(entry.display);
8420
+ }
8421
+ else {
8422
+ const display = isGifSource(asset)
8423
+ ? new options.GifSprite({ autoPlay: true, loop: true, source: asset })
8424
+ : new options.Sprite({ texture: asset });
8425
+ display.anchor.set(0.5);
8426
+ container?.addChild(display);
8427
+ entry = {
8428
+ active: false,
8429
+ baseX: 0,
8430
+ baseY: 0,
8431
+ detectionId,
8432
+ display,
8433
+ rendererId,
8434
+ src,
8435
+ };
8436
+ }
8437
+ entries.set(key, entry);
8438
+ return entry;
8439
+ }
8440
+ function destroyRendererDisplays(rendererId) {
8441
+ for (const [key, entry] of entries) {
8442
+ if (entry.rendererId !== rendererId)
8443
+ continue;
8444
+ destroyDisplay(entry.display);
8445
+ entries.delete(key);
8446
+ }
8447
+ for (const [key, pool] of pools) {
8448
+ const retained = pool.filter((entry) => {
8449
+ if (entry.rendererId !== rendererId)
8450
+ return true;
8451
+ destroyDisplay(entry.display);
8452
+ return false;
8453
+ });
8454
+ if (retained.length > 0)
8455
+ pools.set(key, retained);
8456
+ else
8457
+ pools.delete(key);
8458
+ }
8459
+ }
8460
+ function destroyAllDisplays() {
8461
+ for (const entry of entries.values())
8462
+ destroyDisplay(entry.display);
8463
+ for (const pool of pools.values()) {
8464
+ for (const entry of pool)
8465
+ destroyDisplay(entry.display);
8466
+ }
8467
+ entries.clear();
8468
+ pools.clear();
8469
+ }
8470
+ }
8471
+ function destroyDisplay(display) {
8472
+ display.removeFromParent?.();
8473
+ // GifSprite sources are shared and released through Assets.unload().
8474
+ display.destroy();
8475
+ }
8476
+ function pauseAnimatedDisplay(display) {
8477
+ if ("stop" in display && typeof display.stop === "function")
8478
+ display.stop();
8479
+ }
8480
+ function resumeAnimatedDisplay(display) {
8481
+ if ("play" in display && typeof display.play === "function")
8482
+ display.play();
8483
+ }
8484
+ function matchesTarget(renderer, detection, context) {
8485
+ const { target } = renderer;
8486
+ return (matchesValue(target.id, detection.id) &&
8487
+ matchesValue(target.className, detection.className) &&
8488
+ matchesValue(target.sourceId, detection.sourceId) &&
8489
+ (target.resolve?.(detection, context) ?? true));
8490
+ }
8491
+ function matchesValue(configured, actual) {
8492
+ if (configured === undefined)
8493
+ return true;
8494
+ if (actual === undefined)
8495
+ return false;
8496
+ return Array.isArray(configured)
8497
+ ? configured.includes(actual)
8498
+ : configured === actual;
8499
+ }
8500
+ function resolveRegion(renderer, detection) {
8501
+ if (renderer.region.kind === "bounds")
8502
+ return detection.rect;
8503
+ const fallbackSize = detection.rect
8504
+ ? Math.max(1, detection.rect.width * 0.4)
8505
+ : undefined;
8506
+ if (renderer.region.anchor === "head") {
8507
+ const facePoints = detection.keypoints?.points
8508
+ .slice(0, 5)
8509
+ .filter((_, index) => (detection.keypoints?.visibility?.[index] ?? 2) > 0);
8510
+ if (facePoints && facePoints.length > 0) {
8511
+ const xs = facePoints.map(({ x }) => x);
8512
+ const ys = facePoints.map(({ y }) => y);
8513
+ const width = Math.max(fallbackSize ?? 1, (Math.max(...xs) - Math.min(...xs)) * 2);
8514
+ return {
8515
+ height: width,
8516
+ width,
8517
+ x: xs.reduce((sum, x) => sum + x, 0) / xs.length,
8518
+ y: ys.reduce((sum, y) => sum + y, 0) / ys.length,
8519
+ };
8520
+ }
8521
+ if (!detection.rect || fallbackSize === undefined)
8522
+ return undefined;
8523
+ return {
8524
+ height: fallbackSize,
8525
+ width: fallbackSize,
8526
+ x: detection.rect.x,
8527
+ y: detection.rect.y - detection.rect.height / 2,
8528
+ };
8529
+ }
8530
+ const point = detection.keypoints?.points[renderer.region.anchor];
8531
+ const visibility = detection.keypoints?.visibility?.[renderer.region.anchor] ?? 2;
8532
+ if (!point || visibility <= 0 || fallbackSize === undefined)
8533
+ return undefined;
8534
+ return { height: fallbackSize, width: fallbackSize, x: point.x, y: point.y };
8535
+ }
8536
+ function positionSprite(sprite, renderer, region, asset) {
8537
+ const scale = finiteOr(renderer.transform?.scale, 1);
8538
+ const opacity = Math.min(1, Math.max(0, finiteOr(renderer.transform?.opacity, 1)));
8539
+ const sourceWidth = Math.max(1, asset.width);
8540
+ const sourceHeight = Math.max(1, asset.height);
8541
+ const containScale = Math.min(region.width / sourceWidth, region.height / sourceHeight);
8542
+ const offset = renderer.transform?.offset;
8543
+ sprite.alpha = opacity;
8544
+ sprite.width = sourceWidth * containScale * scale;
8545
+ sprite.height = sourceHeight * containScale * scale;
8546
+ const position = {
8547
+ x: region.x + finiteOr(offset?.x, 0) * region.width,
8548
+ y: region.y + finiteOr(offset?.y, 0) * region.height,
8549
+ };
8550
+ sprite.position.set(position.x, position.y);
8551
+ sprite.rotation = finiteOr(renderer.transform?.rotation, 0);
8552
+ return position;
8553
+ }
8554
+ function isGifSource(asset) {
8555
+ return ("totalFrames" in asset && "frames" in asset && Array.isArray(asset.frames));
8556
+ }
8557
+ function resolvePoolKey(rendererId, src) {
8558
+ return JSON.stringify([rendererId, src]);
8559
+ }
8560
+ function resolveSpriteKey(rendererId, detection, detectionIndex) {
8561
+ return JSON.stringify([rendererId, detection.id ?? null, detectionIndex]);
8562
+ }
8563
+ function finiteOr(value, fallback) {
8564
+ return value !== undefined && Number.isFinite(value) ? value : fallback;
8565
+ }
8566
+ function acquireAsset(loader, src) {
8567
+ let cache = sharedAssets.get(loader);
8568
+ if (!cache) {
8569
+ cache = new Map();
8570
+ sharedAssets.set(loader, cache);
8571
+ }
8572
+ let entry = cache.get(src);
8573
+ if (!entry) {
8574
+ entry = { asset: loader.load(src), references: 0 };
8575
+ cache.set(src, entry);
8576
+ }
8577
+ entry.references += 1;
8578
+ let released = false;
8579
+ return {
8580
+ release() {
8581
+ if (released)
8582
+ return;
8583
+ released = true;
8584
+ entry.references -= 1;
8585
+ if (entry.references > 0)
8586
+ return;
8587
+ cache.delete(src);
8588
+ void entry.asset.then(() => loader.unload(src), () => undefined);
8589
+ },
8590
+ asset: entry.asset,
8591
+ };
8592
+ }
8593
+
8594
+ /**
8595
+ * Bridges public annotation renderer kinds onto the internal vector-layer
8596
+ * shape pipeline.
8597
+ *
8598
+ * Each shape-backed renderer kind resolves its own public instruction per
8599
+ * detection; this adapter lowers those instructions into the internal
8600
+ * renderer-neutral shape vocabulary and concatenates them into the single
8601
+ * shape style the vector layer consumes. Kinds stay independent: a kind that
8602
+ * resolves nothing for a detection simply contributes no instructions.
8603
+ */
8604
+ function resolveAnnotationShapeStyle(styles) {
8605
+ const boxCornerStyle = styles.boxCornerStyle ?? null;
8606
+ const ellipseStyle = styles.ellipseStyle ?? null;
8607
+ const markerStyle = styles.markerStyle ?? null;
8608
+ if (!boxCornerStyle && !ellipseStyle && !markerStyle) {
8609
+ return null;
8610
+ }
8611
+ return {
8612
+ resolve(detection, context) {
8613
+ const instructions = [];
8614
+ const boxCorners = boxCornerStyle?.resolve(detection, context);
8615
+ if (boxCorners) {
8616
+ instructions.push({
8617
+ closed: false,
8618
+ kind: ShapeInstructionKind.Path,
8619
+ segments: boxCorners.segments,
8620
+ stroke: boxCorners.stroke,
8621
+ });
8622
+ }
8623
+ const marker = markerStyle?.resolve(detection, context);
8624
+ if (marker) {
8625
+ instructions.push({ kind: ShapeInstructionKind.Marker, ...marker });
8626
+ }
8627
+ const ellipse = ellipseStyle?.resolve(detection, context);
8628
+ if (ellipse) {
8629
+ instructions.push(lowerEllipseInstruction(ellipse));
8630
+ }
8631
+ return instructions.length > 0 ? instructions : undefined;
8632
+ },
8633
+ };
8634
+ }
8635
+ function lowerEllipseInstruction(instruction) {
8636
+ return { ...instruction, kind: ShapeInstructionKind.Ellipse };
8637
+ }
8638
+
7371
8639
  function createPixiAnnotationOverlayLayer(editingEngine, initialStyle) {
7372
8640
  let graphics;
7373
8641
  let style = resolveAnnotationOverlayStyle(initialStyle);
@@ -7600,8 +8868,11 @@ function drawLoading(graphics, context, style) {
7600
8868
  const loading = values instanceof Set ? values : new Set(values);
7601
8869
  const angle = (context.now / 500) % (Math.PI * 2);
7602
8870
  for (const detection of context.frame?.detections ?? []) {
7603
- if (detection.id === undefined || !loading.has(detection.id))
8871
+ if (detection.id === undefined ||
8872
+ !loading.has(detection.id) ||
8873
+ resolveAnnotationStyleState(detection, context.visibility).hidden) {
7604
8874
  continue;
8875
+ }
7605
8876
  const rect = getDetectionRect(detection);
7606
8877
  if (!rect)
7607
8878
  continue;
@@ -7676,6 +8947,7 @@ var PixiSceneLayerKind;
7676
8947
  PixiSceneLayerKind["Mask"] = "mask";
7677
8948
  PixiSceneLayerKind["Box"] = "box";
7678
8949
  PixiSceneLayerKind["Vector"] = "vector";
8950
+ PixiSceneLayerKind["Region"] = "region";
7679
8951
  PixiSceneLayerKind["Focus"] = "focus";
7680
8952
  PixiSceneLayerKind["Preview"] = "preview";
7681
8953
  PixiSceneLayerKind["Guide"] = "guide";
@@ -7720,6 +8992,8 @@ function defaultLayerOrder(kind) {
7720
8992
  return 200;
7721
8993
  case PixiSceneLayerKind.Vector:
7722
8994
  return 250;
8995
+ case PixiSceneLayerKind.Region:
8996
+ return 275;
7723
8997
  case PixiSceneLayerKind.Focus:
7724
8998
  return 300;
7725
8999
  case PixiSceneLayerKind.Preview:
@@ -7775,14 +9049,42 @@ function observePixiContainerResize(container, resize) {
7775
9049
  return () => observer.disconnect();
7776
9050
  }
7777
9051
  async function createPixiMediaScene(options) {
7778
- const { Application, CanvasSource, Container, Graphics, ImageSource, Mesh, MeshGeometry, Rectangle, Sprite, Shader, Text, Texture, UniformGroup, } = await import('pixi.js');
9052
+ const { Application, Assets, BlurFilter, CanvasSource, Container, Graphics, ImageSource, Mesh, MeshGeometry, Rectangle, Sprite, Shader, Text, Texture, UniformGroup, } = await import('pixi.js');
9053
+ const { GifSprite } = await import('pixi.js/gif');
7779
9054
  const app = new Application();
9055
+ let currentEllipseStyle = options.ellipseStyle ?? null;
9056
+ let currentBoxCornerStyle = options.boxCornerStyle ?? null;
7780
9057
  let currentLabelStyle = options.labelStyle ?? null;
7781
9058
  let currentMaskStyle = options.maskStyle ?? null;
9059
+ let currentMarkerStyle = options.markerStyle ?? null;
9060
+ let currentMaskHaloStyle = options.maskHaloStyle ?? null;
9061
+ // Halo-only presentations still need prepared id-mask artifacts; this
9062
+ // invisible style drives preparation without drawing any fill or border.
9063
+ const haloOnlyMaskStyle = {
9064
+ artifactKey: "mask-halo-only:v1",
9065
+ resolve: (detection) => detection.mask
9066
+ ? { alpha: 0, color: 0x000000, mask: detection.mask }
9067
+ : undefined,
9068
+ };
9069
+ const createVisibilityMaskHaloStyle = (style) => ({
9070
+ resolve(detection, context) {
9071
+ const state = resolveContextState(detection);
9072
+ if (state.hidden) {
9073
+ return undefined;
9074
+ }
9075
+ return style.resolve(detection, { ...context, ...state });
9076
+ },
9077
+ });
9078
+ const resolveArtifactMaskStyle = () => currentMaskStyle ?? (currentMaskHaloStyle ? haloOnlyMaskStyle : null);
7782
9079
  let currentPolygonStyle = options.polygonStyle === undefined
7783
9080
  ? new BasePolygonStyle()
7784
9081
  : options.polygonStyle;
7785
9082
  let currentVisibility = options.visibility;
9083
+ let currentMediaTime = 0;
9084
+ let viewportScale = 1;
9085
+ let hasPresentedSample = false;
9086
+ let mediaHeight = 0;
9087
+ let mediaWidth = 0;
7786
9088
  let visibilityVersion = 0;
7787
9089
  let visibilityMaskStyleCache = null;
7788
9090
  const resolveContextState = (detection) => resolveAnnotationStyleState(detection, currentVisibility);
@@ -7791,6 +9093,28 @@ async function createPixiMediaScene(options) {
7791
9093
  hidden: currentVisibility?.labelsHidden === true ||
7792
9094
  resolveContextState(detection).hidden,
7793
9095
  });
9096
+ // The vector layer consumes one shape style; renderer kinds that lower to
9097
+ // shape instructions compose with the internal hook here.
9098
+ function resolveVectorShapeStyle() {
9099
+ const kindShapeStyle = resolveAnnotationShapeStyle({
9100
+ boxCornerStyle: currentBoxCornerStyle,
9101
+ ellipseStyle: currentEllipseStyle,
9102
+ markerStyle: currentMarkerStyle,
9103
+ });
9104
+ const baseShapeStyle = options.shapeStyle ?? null;
9105
+ if (!kindShapeStyle || !baseShapeStyle) {
9106
+ return kindShapeStyle ?? baseShapeStyle;
9107
+ }
9108
+ return {
9109
+ resolve(detection, context) {
9110
+ const combined = [
9111
+ ...(baseShapeStyle.resolve(detection, context) ?? []),
9112
+ ...(kindShapeStyle.resolve(detection, context) ?? []),
9113
+ ];
9114
+ return combined.length > 0 ? combined : undefined;
9115
+ },
9116
+ };
9117
+ }
7794
9118
  const boxLayer = createPixiBoxLayer({
7795
9119
  boxStyle: options.boxStyle,
7796
9120
  Container: options.editingEngine ? Container : undefined,
@@ -7823,6 +9147,24 @@ async function createPixiMediaScene(options) {
7823
9147
  polygonStyle: polygonLayer?.getVectorFallbackStyle() ?? currentPolygonStyle,
7824
9148
  polylineStyle: options.polylineStyle,
7825
9149
  keypointStyle: options.keypointStyle,
9150
+ shapeStyle: resolveVectorShapeStyle(),
9151
+ resolveContextState,
9152
+ });
9153
+ const regionLayer = createPixiRegionLayer({
9154
+ Assets,
9155
+ Container,
9156
+ GifSprite,
9157
+ Sprite,
9158
+ detectionTimeline: options.detectionTimeline,
9159
+ onInvalidate: () => {
9160
+ if (!hasPresentedSample || mediaWidth <= 0 || mediaHeight <= 0)
9161
+ return;
9162
+ const boxState = boxLayer.drawFrame(currentMediaTime, viewportScale);
9163
+ const regionState = regionLayer.drawFrame(currentMediaTime, viewportScale);
9164
+ options.onPresentationUpdate?.(createPresentedSampleState(currentMediaTime, boxState, regionState));
9165
+ },
9166
+ onAssetError: options.diagnostics?.onAssetError,
9167
+ regionRenderers: options.regionRenderers,
7826
9168
  resolveContextState,
7827
9169
  });
7828
9170
  const annotationOverlayLayer = createPixiAnnotationOverlayLayer(options.editingEngine, options.annotationOverlayStyle);
@@ -7836,9 +9178,11 @@ async function createPixiMediaScene(options) {
7836
9178
  preview: options.maskBrush,
7837
9179
  })
7838
9180
  : undefined;
7839
- let maskLayer = options.maskStyle
9181
+ let maskLayer = resolveArtifactMaskStyle()
7840
9182
  ? createPixiMaskLayer({
9183
+ BlurFilter,
7841
9184
  Container,
9185
+ Rectangle,
7842
9186
  ImageSource,
7843
9187
  Mesh,
7844
9188
  MeshGeometry,
@@ -7847,7 +9191,10 @@ async function createPixiMediaScene(options) {
7847
9191
  Texture,
7848
9192
  UniformGroup,
7849
9193
  detectionTimeline: options.detectionTimeline,
7850
- maskStyle: createVisibilityMaskStyle(options.maskStyle),
9194
+ maskHaloStyle: currentMaskHaloStyle
9195
+ ? createVisibilityMaskHaloStyle(currentMaskHaloStyle)
9196
+ : null,
9197
+ maskStyle: createVisibilityMaskStyle(resolveArtifactMaskStyle()),
7851
9198
  onActiveIdMaskFramePresented: handleActiveIdMaskFramePresented,
7852
9199
  renderPreparation: options.renderPreparation,
7853
9200
  })
@@ -7879,8 +9226,6 @@ async function createPixiMediaScene(options) {
7879
9226
  if (!stagingContext) {
7880
9227
  throw new Error("Unable to create staging canvas context.");
7881
9228
  }
7882
- let mediaHeight = 0;
7883
- let mediaWidth = 0;
7884
9229
  let mediaScene;
7885
9230
  let vectorDisplay;
7886
9231
  let polygonDisplay;
@@ -7889,6 +9234,7 @@ async function createPixiMediaScene(options) {
7889
9234
  const maskSlot = createPixiSceneLayerSlot(PixiSceneLayerKind.Mask);
7890
9235
  const boxSlot = createPixiSceneLayerSlot(PixiSceneLayerKind.Box);
7891
9236
  const vectorSlot = createPixiSceneLayerSlot(PixiSceneLayerKind.Vector);
9237
+ const regionSlot = createPixiSceneLayerSlot(PixiSceneLayerKind.Region);
7892
9238
  const focusSlot = createPixiSceneLayerSlot(PixiSceneLayerKind.Focus);
7893
9239
  const previewSlot = createPixiSceneLayerSlot(PixiSceneLayerKind.Preview);
7894
9240
  const interactionSlot = createPixiSceneLayerSlot(PixiSceneLayerKind.Interaction);
@@ -7899,16 +9245,14 @@ async function createPixiMediaScene(options) {
7899
9245
  maskSlot,
7900
9246
  boxSlot,
7901
9247
  vectorSlot,
9248
+ regionSlot,
7902
9249
  focusSlot,
7903
9250
  previewSlot,
7904
9251
  handleSlot,
7905
9252
  interactionSlot,
7906
9253
  labelSlot,
7907
9254
  ];
7908
- let currentMediaTime = 0;
7909
- let viewportScale = 1;
7910
9255
  const viewport = createViewportController({ scale: 1 });
7911
- let hasPresentedSample = false;
7912
9256
  /**
7913
9257
  * Timestamp of the sample whose pixels are on the staging canvas. Unlike
7914
9258
  * `currentMediaTime`, presentation and selection updates never move it.
@@ -7924,6 +9268,7 @@ async function createPixiMediaScene(options) {
7924
9268
  interaction: options.interaction ?? {
7925
9269
  mode: MediaInteractionMode.Always,
7926
9270
  },
9271
+ canPickDetection: (detection) => !resolveAnnotationStyleState(detection, currentVisibility).hidden,
7927
9272
  editingEngine: options.editingEngine,
7928
9273
  capturePointer: (pointerId) => {
7929
9274
  if (!rendererCanvas.hasPointerCapture?.(pointerId)) {
@@ -7960,6 +9305,7 @@ async function createPixiMediaScene(options) {
7960
9305
  Text,
7961
9306
  UniformGroup,
7962
9307
  interactionStyle: options.interactionStyle,
9308
+ isDetectionVisible: (detection) => !resolveAnnotationStyleState(detection, currentVisibility).hidden,
7963
9309
  })
7964
9310
  : undefined;
7965
9311
  let fastTranslatedDetectionId = null;
@@ -7967,6 +9313,7 @@ async function createPixiMediaScene(options) {
7967
9313
  fastTranslatedDetectionId = id;
7968
9314
  boxLayer.translateDetection(id, dx, dy);
7969
9315
  vectorLayer.translateDetection(id, dx, dy);
9316
+ regionLayer.translateDetection(id, dx, dy);
7970
9317
  labelLayer?.translateDetection(id, dx, dy);
7971
9318
  });
7972
9319
  const unsubscribeEditingState = options.editingEngine?.subscribe((state) => {
@@ -7978,11 +9325,13 @@ async function createPixiMediaScene(options) {
7978
9325
  fastTranslatedDetectionId = null;
7979
9326
  boxLayer.translateDetection(id, 0, 0);
7980
9327
  vectorLayer.translateDetection(id, 0, 0);
9328
+ regionLayer.translateDetection(id, 0, 0);
7981
9329
  labelLayer?.translateDetection(id, 0, 0);
7982
9330
  boxLayer.invalidateDetection(id);
7983
9331
  vectorLayer.invalidateDetection(id);
7984
9332
  boxLayer.drawFrame(currentMediaTime, viewportScale);
7985
9333
  vectorLayer.drawFrame(currentMediaTime, viewportScale);
9334
+ regionLayer.drawFrame(currentMediaTime, viewportScale);
7986
9335
  labelLayer?.drawFrame(currentMediaTime, viewportScale);
7987
9336
  });
7988
9337
  const handleKeyDown = (event) => {
@@ -8017,6 +9366,7 @@ async function createPixiMediaScene(options) {
8017
9366
  Shader,
8018
9367
  UniformGroup,
8019
9368
  focusStyle: options.focusStyle,
9369
+ isDetectionVisible: (detection) => !resolveAnnotationStyleState(detection, currentVisibility).hidden,
8020
9370
  })
8021
9371
  : undefined;
8022
9372
  let mediaSprite;
@@ -8126,6 +9476,7 @@ async function createPixiMediaScene(options) {
8126
9476
  attachPolygonLayerDisplay();
8127
9477
  vectorDisplay.addChild(vectorLayer.createContainer());
8128
9478
  vectorSlot.setDisplay(vectorDisplay);
9479
+ regionSlot.setDisplay(regionLayer.createContainer());
8129
9480
  previewSlot.setDisplay(maskBrushPreview?.display);
8130
9481
  if (annotationOverlay) {
8131
9482
  annotationOverlayLayer.attachGraphics(annotationOverlay);
@@ -8179,13 +9530,14 @@ async function createPixiMediaScene(options) {
8179
9530
  const boxState = boxLayer.drawFrame(sample.timestamp, viewportScale);
8180
9531
  polygonLayer?.drawFrame(sample.timestamp, viewportScale);
8181
9532
  vectorLayer.drawFrame(sample.timestamp, viewportScale);
9533
+ const regionState = regionLayer.drawFrame(sample.timestamp, viewportScale);
8182
9534
  interactionLayer?.drawFrame(sample.timestamp);
8183
9535
  drawFocusLayer(sample.timestamp);
8184
9536
  drawInteractionPresentationLayer(sample.timestamp);
8185
9537
  labelLayer?.drawFrame(sample.timestamp, viewportScale);
8186
9538
  updateMediaSceneFit();
8187
9539
  return {
8188
- ...createPresentedSampleState(sample.timestamp, boxState),
9540
+ ...createPresentedSampleState(sample.timestamp, boxState, regionState),
8189
9541
  duration: sample.duration,
8190
9542
  };
8191
9543
  }
@@ -8207,10 +9559,12 @@ async function createPixiMediaScene(options) {
8207
9559
  maskLayer?.drawFrame(sample.timestamp);
8208
9560
  });
8209
9561
  let boxState;
9562
+ let regionState;
8210
9563
  boxMs = measure(() => {
8211
9564
  boxState = boxLayer.drawFrame(sample.timestamp, viewportScale);
8212
9565
  polygonLayer?.drawFrame(sample.timestamp, viewportScale);
8213
9566
  vectorLayer.drawFrame(sample.timestamp, viewportScale);
9567
+ regionState = regionLayer.drawFrame(sample.timestamp, viewportScale);
8214
9568
  });
8215
9569
  interactionMs = measure(() => {
8216
9570
  interactionLayer?.drawFrame(sample.timestamp);
@@ -8237,7 +9591,7 @@ async function createPixiMediaScene(options) {
8237
9591
  totalMs: elapsedSince(totalStart),
8238
9592
  };
8239
9593
  return {
8240
- ...createPresentedSampleState(sample.timestamp, boxState),
9594
+ ...createPresentedSampleState(sample.timestamp, boxState, regionState),
8241
9595
  duration: sample.duration,
8242
9596
  renderTimings,
8243
9597
  };
@@ -8386,6 +9740,7 @@ async function createPixiMediaScene(options) {
8386
9740
  visibilityVersion += 1;
8387
9741
  maskLayer?.setMaskStyle(createVisibilityMaskStyle(currentMaskStyle));
8388
9742
  }
9743
+ interactionLayer?.drawFrame(mediaTime);
8389
9744
  }
8390
9745
  boxLayer.setBoxStyle(presentation.boxStyle);
8391
9746
  if (presentation.polygonStyle !== undefined) {
@@ -8406,15 +9761,38 @@ async function createPixiMediaScene(options) {
8406
9761
  polylineStyle: presentation.polylineStyle,
8407
9762
  keypointStyle: presentation.keypointStyle,
8408
9763
  });
8409
- if (presentation.maskStyle !== undefined) {
8410
- currentMaskStyle = presentation.maskStyle;
8411
- const nextMaskLayer = presentation.maskStyle
8412
- ? ensureMaskLayer(presentation.maskStyle)
9764
+ regionLayer.setRenderers(presentation.renderers?.filter((renderer) => renderer.kind === "region") ?? []);
9765
+ if (presentation.boxCornerStyle !== undefined ||
9766
+ presentation.ellipseStyle !== undefined ||
9767
+ presentation.markerStyle !== undefined) {
9768
+ if (presentation.boxCornerStyle !== undefined) {
9769
+ currentBoxCornerStyle = presentation.boxCornerStyle;
9770
+ }
9771
+ if (presentation.ellipseStyle !== undefined) {
9772
+ currentEllipseStyle = presentation.ellipseStyle;
9773
+ }
9774
+ if (presentation.markerStyle !== undefined) {
9775
+ currentMarkerStyle = presentation.markerStyle;
9776
+ }
9777
+ vectorLayer.setStyles({ shapeStyle: resolveVectorShapeStyle() });
9778
+ }
9779
+ if (presentation.maskStyle !== undefined ||
9780
+ presentation.maskHaloStyle !== undefined) {
9781
+ if (presentation.maskStyle !== undefined) {
9782
+ currentMaskStyle = presentation.maskStyle;
9783
+ }
9784
+ if (presentation.maskHaloStyle !== undefined) {
9785
+ currentMaskHaloStyle = presentation.maskHaloStyle;
9786
+ }
9787
+ const artifactStyle = resolveArtifactMaskStyle();
9788
+ const nextMaskLayer = artifactStyle
9789
+ ? ensureMaskLayer(artifactStyle)
8413
9790
  : maskLayer;
8414
- nextMaskLayer?.setMaskStyle(presentation.maskStyle
8415
- ? createVisibilityMaskStyle(presentation.maskStyle)
8416
- : presentation.maskStyle);
8417
- if (presentation.maskStyle) {
9791
+ nextMaskLayer?.setMaskStyle(artifactStyle ? createVisibilityMaskStyle(artifactStyle) : null);
9792
+ nextMaskLayer?.setMaskHaloStyle(currentMaskHaloStyle
9793
+ ? createVisibilityMaskHaloStyle(currentMaskHaloStyle)
9794
+ : null);
9795
+ if (artifactStyle) {
8418
9796
  attachMaskLayerDisplay();
8419
9797
  syncSceneChildren();
8420
9798
  }
@@ -8448,11 +9826,12 @@ async function createPixiMediaScene(options) {
8448
9826
  const boxState = boxLayer.drawFrame(mediaTime, viewportScale);
8449
9827
  polygonLayer?.drawFrame(mediaTime, viewportScale);
8450
9828
  vectorLayer.drawFrame(mediaTime, viewportScale);
9829
+ const regionState = regionLayer.drawFrame(mediaTime, viewportScale);
8451
9830
  interactionLayer?.drawFrame(mediaTime);
8452
9831
  drawFocusLayer(mediaTime);
8453
9832
  drawInteractionPresentationLayer(mediaTime);
8454
9833
  labelLayer?.drawFrame(mediaTime, viewportScale);
8455
- return createPresentedSampleState(mediaTime, boxState);
9834
+ return createPresentedSampleState(mediaTime, boxState, regionState);
8456
9835
  },
8457
9836
  setSelectedDetection(selection, mediaTime) {
8458
9837
  currentMediaTime = selection?.mediaTime ?? mediaTime;
@@ -8479,6 +9858,7 @@ async function createPixiMediaScene(options) {
8479
9858
  polygonLayer?.destroy();
8480
9859
  labelLayer?.destroy();
8481
9860
  vectorLayer.destroy();
9861
+ regionLayer.destroy();
8482
9862
  maskBrushPreview?.destroy();
8483
9863
  unsubscribeFastTranslate?.();
8484
9864
  unsubscribeEditingState?.();
@@ -8495,21 +9875,24 @@ async function createPixiMediaScene(options) {
8495
9875
  });
8496
9876
  },
8497
9877
  };
8498
- function createPresentedSampleState(mediaTime, boxState) {
9878
+ function createPresentedSampleState(mediaTime, boxState, regionState) {
8499
9879
  const detectionFrame = options.detectionTimeline.selectFrame(mediaTime);
8500
9880
  return {
8501
- activeDetectionCount: countPresentedDetections(detectionFrame, mediaTime, boxState),
9881
+ activeDetectionCount: countPresentedDetections(detectionFrame, mediaTime, boxState, regionState),
8502
9882
  activeDetectionFrameIndex: detectionFrame?.frameIndex ?? null,
8503
9883
  activeDetectionFrameTime: detectionFrame?.mediaTime ?? null,
8504
9884
  detectionBuffer: options.detectionTimeline.getState(),
8505
9885
  mediaTime,
8506
9886
  };
8507
9887
  }
8508
- function countPresentedDetections(frame, mediaTime, boxState) {
9888
+ function countPresentedDetections(frame, mediaTime, boxState, regionState) {
8509
9889
  if (!frame) {
8510
9890
  return 0;
8511
9891
  }
8512
9892
  const visibleDetectionIndexes = new Set(boxState.activeDetectionIndexes);
9893
+ for (const detectionIndex of regionState?.activeDetectionIndexes ?? []) {
9894
+ visibleDetectionIndexes.add(detectionIndex);
9895
+ }
8513
9896
  for (const [detectionIndex, detection] of frame.detections.entries()) {
8514
9897
  if (visibleDetectionIndexes.has(detectionIndex)) {
8515
9898
  continue;
@@ -8536,7 +9919,9 @@ async function createPixiMediaScene(options) {
8536
9919
  function ensureMaskLayer(maskStyle) {
8537
9920
  if (!maskLayer) {
8538
9921
  maskLayer = createPixiMaskLayer({
9922
+ BlurFilter,
8539
9923
  Container,
9924
+ Rectangle,
8540
9925
  ImageSource,
8541
9926
  Mesh,
8542
9927
  MeshGeometry,
@@ -8545,6 +9930,9 @@ async function createPixiMediaScene(options) {
8545
9930
  Texture,
8546
9931
  UniformGroup,
8547
9932
  detectionTimeline: options.detectionTimeline,
9933
+ maskHaloStyle: currentMaskHaloStyle
9934
+ ? createVisibilityMaskHaloStyle(currentMaskHaloStyle)
9935
+ : null,
8548
9936
  maskStyle: createVisibilityMaskStyle(maskStyle),
8549
9937
  onActiveIdMaskFramePresented: handleActiveIdMaskFramePresented,
8550
9938
  renderPreparation: options.renderPreparation,
@@ -8622,6 +10010,7 @@ async function createPixiMediaScene(options) {
8622
10010
  Shader,
8623
10011
  UniformGroup,
8624
10012
  focusStyle,
10013
+ isDetectionVisible: (detection) => !resolveAnnotationStyleState(detection, currentVisibility).hidden,
8625
10014
  });
8626
10015
  }
8627
10016
  return focusLayer;
@@ -8657,10 +10046,10 @@ async function createPixiMediaScene(options) {
8657
10046
  const interactionState = interactionLayer?.getState();
8658
10047
  focusLayer.drawFrame({
8659
10048
  frame,
8660
- hoveredPick: interactionState?.hoveredPick ?? null,
10049
+ hoveredPick: filterVisiblePick(interactionState?.hoveredPick ?? null),
8661
10050
  idMaskArtifact: maskLayer?.getActiveIdMaskFrameTexture() ?? null,
8662
10051
  mediaTime,
8663
- selectedPick: interactionState?.selectedPick ?? null,
10052
+ selectedPick: filterVisiblePick(interactionState?.selectedPick ?? null),
8664
10053
  viewportScale,
8665
10054
  });
8666
10055
  }
@@ -8672,11 +10061,11 @@ async function createPixiMediaScene(options) {
8672
10061
  const interactionState = interactionLayer?.getState();
8673
10062
  interactionPresentationLayer.drawFrame({
8674
10063
  frame,
8675
- hoveredPick: interactionState?.hoveredPick ?? null,
10064
+ hoveredPick: filterVisiblePick(interactionState?.hoveredPick ?? null),
8676
10065
  idMaskArtifact: maskLayer?.getActiveIdMaskFrameTexture() ?? null,
8677
10066
  mediaTime,
8678
- selectedPick: interactionState?.selectedPick ?? null,
8679
- selectedPicks: interactionState?.selectedPicks,
10067
+ selectedPick: filterVisiblePick(interactionState?.selectedPick ?? null),
10068
+ selectedPicks: interactionState?.selectedPicks.filter(({ detection }) => !resolveAnnotationStyleState(detection, currentVisibility).hidden),
8680
10069
  viewportScale,
8681
10070
  });
8682
10071
  }
@@ -8747,6 +10136,12 @@ async function createPixiMediaScene(options) {
8747
10136
  };
8748
10137
  return visibilityMaskStyle;
8749
10138
  }
10139
+ function filterVisiblePick(pick) {
10140
+ return pick &&
10141
+ !resolveAnnotationStyleState(pick.detection, currentVisibility).hidden
10142
+ ? pick
10143
+ : null;
10144
+ }
8750
10145
  }
8751
10146
  function canReuseMaskVisibilityArtifacts(previous, next) {
8752
10147
  return ((previous?.annotationsHidden === true) ===
@@ -9393,10 +10788,20 @@ async function createMediaSession(options) {
9393
10788
  ? undefined
9394
10789
  : { enabledRendererKinds: renderers.map(({ kind }) => kind) });
9395
10790
  };
9396
- const resolveRendererPresentation = (presentation) => ({
9397
- ...resolvePresentation(presentation),
9398
- renderers: undefined,
9399
- });
10791
+ const resolveRendererPresentation = (presentation) => {
10792
+ const resolved = resolvePresentation(presentation);
10793
+ return {
10794
+ ...resolved,
10795
+ // Style-backed renderers have already been lowered into their
10796
+ // specialized fields, including source overrides. Keep their kinds in
10797
+ // the authoritative list so the renderer core does not clear those
10798
+ // fields when direct renderers are also present, but let the resolved
10799
+ // fields win over the descriptors' original global styles.
10800
+ renderers: resolved.renderers?.map((renderer) => renderer.kind === "region"
10801
+ ? renderer
10802
+ : { ...renderer, style: undefined }),
10803
+ };
10804
+ };
9400
10805
  const initialPresentation = resolveRendererPresentation(currentPresentation);
9401
10806
  const renderer = await createMediaRenderer({
9402
10807
  ...options.renderer,
@@ -9404,6 +10809,8 @@ async function createMediaSession(options) {
9404
10809
  backgroundColor: initialPresentation.backgroundColor,
9405
10810
  annotationOverlayStyle: initialPresentation.annotationOverlayStyle,
9406
10811
  boxStyle: initialPresentation.boxStyle,
10812
+ boxCornerStyle: initialPresentation.boxCornerStyle,
10813
+ ellipseStyle: initialPresentation.ellipseStyle,
9407
10814
  container: options.container,
9408
10815
  detectionBuffer: sessionDefaults.detectionBuffer,
9409
10816
  detectionFrames: sessionDetections.detectionFrames,
@@ -9412,9 +10819,12 @@ async function createMediaSession(options) {
9412
10819
  focusStyle: initialPresentation.focusStyle,
9413
10820
  interactionStyle: initialPresentation.interactionStyle,
9414
10821
  labelStyle: initialPresentation.labelStyle,
10822
+ maskHaloStyle: initialPresentation.maskHaloStyle,
9415
10823
  maskStyle: initialPresentation.maskStyle,
10824
+ markerStyle: initialPresentation.markerStyle,
9416
10825
  polygonStyle: initialPresentation.polygonStyle,
9417
10826
  polylineStyle: initialPresentation.polylineStyle,
10827
+ renderers: initialPresentation.renderers,
9418
10828
  keypointStyle: initialPresentation.keypointStyle,
9419
10829
  visibility: initialPresentation.visibility,
9420
10830
  onState(state) {
@@ -9569,5 +10979,5 @@ function getErrorMessage(error, fallback) {
9569
10979
  return error instanceof Error ? error.message : fallback;
9570
10980
  }
9571
10981
 
9572
- export { DetectionTimelineOrigin, MediaNormalizationAudioCodec, MediaNormalizationContainer, MediaNormalizationFit, MediaNormalizationVideoCodec, MediaPreparationError, MediaProbeIssueCode, MediaProbeStatus, RenderPreparationArtifactFrameStatus, RenderPreparationArtifactKind, RenderPreparationExecutionMode, RenderPreparationMode, RenderPreparationWorkerStatus, createBrowserColdDetectionFrameStore, createChunkedDetectionFrameSource, createImageUrlMediaSource, createMediaRenderer, createMediaSession, createMediaStreamRendererSource, createStaticImageMediaSource, normalizeMedia, normalizeMediaProgressively, prepareMedia, prepareMediaProgressively, probeMedia };
10982
+ export { DetectionPostProcessingMode, DetectionTimelineOrigin, MediaNormalizationAudioCodec, MediaNormalizationContainer, MediaNormalizationFit, MediaNormalizationVideoCodec, MediaPreparationError, MediaProbeIssueCode, MediaProbeStatus, RenderPreparationArtifactFrameStatus, RenderPreparationArtifactKind, RenderPreparationExecutionMode, RenderPreparationMode, RenderPreparationWorkerStatus, createBrowserColdDetectionFrameStore, createChunkedDetectionFrameSource, createDefaultDetectionPostProcessingWorkerFactory, createDetectionPostProcessingPipeline, createImageUrlMediaSource, createMediaRenderer, createMediaSession, createMediaStreamRendererSource, createStaticImageMediaSource, normalizeMedia, normalizeMediaProgressively, prepareMedia, prepareMediaProgressively, probeMedia };
9573
10983
  //# sourceMappingURL=index.js.map