@ume-group/contracts 0.2.1

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 (57) hide show
  1. package/README.md +37 -0
  2. package/dist/adserving.d.ts +150 -0
  3. package/dist/adserving.d.ts.map +1 -0
  4. package/dist/adserving.js +8 -0
  5. package/dist/campaigns.d.ts +37 -0
  6. package/dist/campaigns.d.ts.map +1 -0
  7. package/dist/campaigns.js +8 -0
  8. package/dist/gausst.d.ts +236 -0
  9. package/dist/gausst.d.ts.map +1 -0
  10. package/dist/gausst.js +307 -0
  11. package/dist/gausst.test.d.ts +2 -0
  12. package/dist/gausst.test.d.ts.map +1 -0
  13. package/dist/gausst.test.js +71 -0
  14. package/dist/index.d.ts +1531 -0
  15. package/dist/index.d.ts.map +1 -0
  16. package/dist/index.js +1112 -0
  17. package/dist/layer2/index.d.ts +9 -0
  18. package/dist/layer2/index.d.ts.map +1 -0
  19. package/dist/layer2/index.js +10 -0
  20. package/dist/layer2/shaders.d.ts +185 -0
  21. package/dist/layer2/shaders.d.ts.map +1 -0
  22. package/dist/layer2/shaders.js +604 -0
  23. package/dist/layer2/webcam-utils.d.ts +113 -0
  24. package/dist/layer2/webcam-utils.d.ts.map +1 -0
  25. package/dist/layer2/webcam-utils.js +147 -0
  26. package/dist/layer2/webcam-utils.test.d.ts +2 -0
  27. package/dist/layer2/webcam-utils.test.d.ts.map +1 -0
  28. package/dist/layer2/webcam-utils.test.js +18 -0
  29. package/dist/layer2.d.ts +558 -0
  30. package/dist/layer2.d.ts.map +1 -0
  31. package/dist/layer2.js +376 -0
  32. package/dist/layer2.test.d.ts +2 -0
  33. package/dist/layer2.test.d.ts.map +1 -0
  34. package/dist/layer2.test.js +65 -0
  35. package/dist/perspective.d.ts +28 -0
  36. package/dist/perspective.d.ts.map +1 -0
  37. package/dist/perspective.js +157 -0
  38. package/dist/segmentation/MediaPipeSegmenter.d.ts +201 -0
  39. package/dist/segmentation/MediaPipeSegmenter.d.ts.map +1 -0
  40. package/dist/segmentation/MediaPipeSegmenter.js +434 -0
  41. package/dist/segmentation/index.d.ts +5 -0
  42. package/dist/segmentation/index.d.ts.map +1 -0
  43. package/dist/segmentation/index.js +4 -0
  44. package/dist/webcam/GarbageMatteDragManager.d.ts +63 -0
  45. package/dist/webcam/GarbageMatteDragManager.d.ts.map +1 -0
  46. package/dist/webcam/GarbageMatteDragManager.js +183 -0
  47. package/dist/webcam/WebcamStreamManager.d.ts +103 -0
  48. package/dist/webcam/WebcamStreamManager.d.ts.map +1 -0
  49. package/dist/webcam/WebcamStreamManager.js +356 -0
  50. package/dist/webcam/index.d.ts +5 -0
  51. package/dist/webcam/index.d.ts.map +1 -0
  52. package/dist/webcam/index.js +2 -0
  53. package/openapi/admetise.yaml +632 -0
  54. package/openapi/includu.yaml +621 -0
  55. package/openapi/integration.yaml +372 -0
  56. package/openapi/shared/schemas.yaml +227 -0
  57. package/package.json +53 -0
@@ -0,0 +1,201 @@
1
+ /**
2
+ * MediaPipeSegmenter.ts - AI-based background segmentation using MediaPipe
3
+ *
4
+ * Wraps MediaPipe Image Segmenter to process webcam frames and produce
5
+ * segmentation masks for background removal.
6
+ *
7
+ * Key features:
8
+ * - Async initialization (model loading)
9
+ * - Frame-by-frame processing with mask output
10
+ * - Mobile-optimized resolution scaling
11
+ * - Temporal smoothing with anti-flicker measures
12
+ * - Proper resource cleanup
13
+ *
14
+ * ArchiMate: Application Component (AI Segmentation Engine)
15
+ */
16
+ /**
17
+ * AI model quality mode
18
+ * - 'fast': selfie_segmenter (float16, 256x256) — quick, good for close-up
19
+ * - 'quality': DeepLabV3 (float32, PASCAL VOC 21-class) — better at distance, slower
20
+ */
21
+ export type AIModelType = 'fast' | 'quality';
22
+ /**
23
+ * Asset URLs for MediaPipe loading
24
+ * Can be configured to use self-hosted assets for better reliability at scale
25
+ */
26
+ export interface MediaPipeAssetUrls {
27
+ /** WASM files URL (default: jsdelivr CDN) */
28
+ wasmUrl: string;
29
+ /** Model file URL (default: Google Storage) */
30
+ modelUrl: string;
31
+ }
32
+ /**
33
+ * Default asset URLs - external CDNs
34
+ * For production at scale, self-host these on Cloudflare R2
35
+ */
36
+ export declare const DEFAULT_ASSET_URLS: MediaPipeAssetUrls;
37
+ /**
38
+ * DeepLabV3 model URL — PASCAL VOC 21-class segmentation
39
+ * Person = class index 15. Float32, heavier but better at distance.
40
+ */
41
+ export declare const DEEPLAB_MODEL_URL = "https://storage.googleapis.com/mediapipe-models/image_segmenter/deeplab_v3/float32/latest/deeplab_v3.tflite";
42
+ /**
43
+ * Self-hosted asset URLs on Includu R2 (for enterprise scale)
44
+ * TODO: Upload MediaPipe assets to R2 and enable these URLs
45
+ */
46
+ export declare const SELF_HOSTED_ASSET_URLS: MediaPipeAssetUrls;
47
+ /**
48
+ * Configuration for the segmenter
49
+ */
50
+ export interface SegmenterConfig {
51
+ /** Processing resolution width (default: 640) */
52
+ width: number;
53
+ /** Processing resolution height (default: 480) */
54
+ height: number;
55
+ /** Mask threshold 0-1 (default: 0.7) */
56
+ threshold: number;
57
+ /** Edge blur radius in pixels (default: 3) */
58
+ edgeBlur: number;
59
+ /** Asset URLs for MediaPipe (default: external CDNs) */
60
+ assetUrls?: MediaPipeAssetUrls;
61
+ /** Model type: 'fast' (selfie_segmenter) or 'quality' (DeepLabV3) */
62
+ modelType?: AIModelType;
63
+ }
64
+ /**
65
+ * Default configuration optimized for balance of quality and performance
66
+ */
67
+ export declare const DEFAULT_SEGMENTER_CONFIG: SegmenterConfig;
68
+ /**
69
+ * Result from segmentation processing
70
+ */
71
+ export interface SegmentationResult {
72
+ /** The mask as ImageData (grayscale, white = person) */
73
+ maskData: ImageData;
74
+ /** The mask canvas element (can be used as texture source) */
75
+ maskCanvas: HTMLCanvasElement;
76
+ /** Processing time in milliseconds */
77
+ processingTimeMs: number;
78
+ }
79
+ /**
80
+ * MediaPipe-based background segmentation
81
+ *
82
+ * Usage:
83
+ * ```typescript
84
+ * const segmenter = new MediaPipeSegmenter();
85
+ * await segmenter.initialize();
86
+ *
87
+ * // In render loop
88
+ * const result = segmenter.process(videoElement);
89
+ * // Use result.maskCanvas as texture source
90
+ *
91
+ * // Cleanup
92
+ * segmenter.dispose();
93
+ * ```
94
+ */
95
+ export declare class MediaPipeSegmenter {
96
+ private segmenter;
97
+ private workCanvas;
98
+ private workCtx;
99
+ private outputCanvas;
100
+ private outputCtx;
101
+ private tempCanvas;
102
+ private tempCtx;
103
+ private config;
104
+ private isInitialized;
105
+ private isInitializing;
106
+ private _modelType;
107
+ private static readonly DEEPLAB_PERSON_INDEX;
108
+ private previousAlpha;
109
+ private backgroundPersistence;
110
+ private readonly persistenceThreshold;
111
+ private readonly persistenceThresholdLowLatency;
112
+ private readonly temporalBlendNormal;
113
+ private readonly temporalBlendLowLatency;
114
+ private readonly hysteresisOffset;
115
+ private lowLatencyMode;
116
+ constructor(config?: Partial<SegmenterConfig>);
117
+ /**
118
+ * Initialize MediaPipe model
119
+ */
120
+ initialize(): Promise<void>;
121
+ /**
122
+ * Process a video frame and return segmentation mask
123
+ */
124
+ process(video: HTMLVideoElement): SegmentationResult | null;
125
+ /**
126
+ * Process MediaPipe result into a usable mask
127
+ */
128
+ private processMask;
129
+ /**
130
+ * Copy work canvas to output canvas
131
+ */
132
+ private copyWorkToOutput;
133
+ /**
134
+ * Apply morphological dilation to fill small holes in the mask.
135
+ * Uses a 4-connected max-filter: draws the mask shifted 1px in each
136
+ * cardinal direction with 'lighten' blend (pixel max). This expands
137
+ * bright regions by 1px, filling 1-2px holes common at distance.
138
+ */
139
+ private applyDilation;
140
+ /**
141
+ * Apply Gaussian blur to soften mask edges
142
+ */
143
+ private applyEdgeBlur;
144
+ /**
145
+ * Reset temporal smoothing buffers.
146
+ * Call when the video source changes (camera switch) to avoid
147
+ * stale mask data from the previous camera bleeding into the new one.
148
+ */
149
+ reset(): void;
150
+ /**
151
+ * Update segmentation threshold
152
+ */
153
+ setThreshold(threshold: number): void;
154
+ /**
155
+ * Update edge blur radius
156
+ */
157
+ setEdgeBlur(edgeBlur: number): void;
158
+ /**
159
+ * Enable/disable low latency mode
160
+ */
161
+ setLowLatency(enabled: boolean): void;
162
+ /**
163
+ * Switch between fast (selfie_segmenter) and quality (DeepLabV3) models.
164
+ * Disposes the current model and re-initializes with the new one.
165
+ * Returns a promise that resolves when the new model is ready.
166
+ */
167
+ switchModel(modelType: AIModelType): Promise<void>;
168
+ /**
169
+ * Get the current model type
170
+ */
171
+ get currentModelType(): AIModelType;
172
+ /**
173
+ * Get current configuration
174
+ */
175
+ getConfig(): SegmenterConfig;
176
+ /**
177
+ * Check if segmenter is ready
178
+ */
179
+ get ready(): boolean;
180
+ /**
181
+ * Check if segmenter is currently loading
182
+ */
183
+ get loading(): boolean;
184
+ /**
185
+ * Get the output mask canvas for use as texture source
186
+ */
187
+ getMaskCanvas(): HTMLCanvasElement;
188
+ /**
189
+ * Clean up resources
190
+ */
191
+ dispose(): void;
192
+ }
193
+ /**
194
+ * Detect if running on mobile device
195
+ */
196
+ export declare function isMobileDevice(): boolean;
197
+ /**
198
+ * Get optimal processing configuration based on device
199
+ */
200
+ export declare function getOptimalConfig(): Partial<SegmenterConfig>;
201
+ //# sourceMappingURL=MediaPipeSegmenter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"MediaPipeSegmenter.d.ts","sourceRoot":"","sources":["../../src/segmentation/MediaPipeSegmenter.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,434 @@
1
+ /**
2
+ * MediaPipeSegmenter.ts - AI-based background segmentation using MediaPipe
3
+ *
4
+ * Wraps MediaPipe Image Segmenter to process webcam frames and produce
5
+ * segmentation masks for background removal.
6
+ *
7
+ * Key features:
8
+ * - Async initialization (model loading)
9
+ * - Frame-by-frame processing with mask output
10
+ * - Mobile-optimized resolution scaling
11
+ * - Temporal smoothing with anti-flicker measures
12
+ * - Proper resource cleanup
13
+ *
14
+ * ArchiMate: Application Component (AI Segmentation Engine)
15
+ */
16
+ import { DEBUG } from '../index';
17
+ import { ImageSegmenter, FilesetResolver } from '@mediapipe/tasks-vision';
18
+ /**
19
+ * Default asset URLs - external CDNs
20
+ * For production at scale, self-host these on Cloudflare R2
21
+ */
22
+ export const DEFAULT_ASSET_URLS = {
23
+ wasmUrl: 'https://cdn.jsdelivr.net/npm/@mediapipe/tasks-vision@0.10.32/wasm',
24
+ modelUrl: 'https://storage.googleapis.com/mediapipe-models/image_segmenter/selfie_segmenter/float16/latest/selfie_segmenter.tflite'
25
+ };
26
+ /**
27
+ * DeepLabV3 model URL — PASCAL VOC 21-class segmentation
28
+ * Person = class index 15. Float32, heavier but better at distance.
29
+ */
30
+ export const DEEPLAB_MODEL_URL = 'https://storage.googleapis.com/mediapipe-models/image_segmenter/deeplab_v3/float32/latest/deeplab_v3.tflite';
31
+ /**
32
+ * Self-hosted asset URLs on Includu R2 (for enterprise scale)
33
+ * TODO: Upload MediaPipe assets to R2 and enable these URLs
34
+ */
35
+ export const SELF_HOSTED_ASSET_URLS = {
36
+ wasmUrl: 'https://pub-55570832bee446f4a0bb149a53ad7050.r2.dev/mediapipe/wasm',
37
+ modelUrl: 'https://pub-55570832bee446f4a0bb149a53ad7050.r2.dev/mediapipe/models/selfie_segmenter.tflite'
38
+ };
39
+ /**
40
+ * Default configuration optimized for balance of quality and performance
41
+ */
42
+ export const DEFAULT_SEGMENTER_CONFIG = {
43
+ width: 640,
44
+ height: 480,
45
+ threshold: 0.7,
46
+ edgeBlur: 3,
47
+ assetUrls: DEFAULT_ASSET_URLS
48
+ };
49
+ /**
50
+ * MediaPipe-based background segmentation
51
+ *
52
+ * Usage:
53
+ * ```typescript
54
+ * const segmenter = new MediaPipeSegmenter();
55
+ * await segmenter.initialize();
56
+ *
57
+ * // In render loop
58
+ * const result = segmenter.process(videoElement);
59
+ * // Use result.maskCanvas as texture source
60
+ *
61
+ * // Cleanup
62
+ * segmenter.dispose();
63
+ * ```
64
+ */
65
+ export class MediaPipeSegmenter {
66
+ segmenter = null;
67
+ // Double-buffered mask canvases to prevent flickering
68
+ workCanvas;
69
+ workCtx;
70
+ outputCanvas;
71
+ outputCtx;
72
+ tempCanvas;
73
+ tempCtx;
74
+ config;
75
+ isInitialized = false;
76
+ isInitializing = false;
77
+ _modelType = 'fast';
78
+ // DeepLabV3 PASCAL VOC person class index
79
+ static DEEPLAB_PERSON_INDEX = 15;
80
+ // Temporal smoothing buffers
81
+ previousAlpha = null;
82
+ backgroundPersistence = null;
83
+ // Anti-flicker settings — tuned to prevent "big chunk" flicker at ~1m distance
84
+ // where the model's confidence oscillates on large body regions.
85
+ // Persistence: how many consecutive low-confidence frames before converting to background
86
+ // Higher values = more stable but slower to remove background when person moves out
87
+ persistenceThreshold = 8;
88
+ persistenceThresholdLowLatency = 4;
89
+ // Temporal blend: how much of current frame vs previous (1.0 = all current, 0.0 = all previous)
90
+ // Lower values = smoother but more lag; critical for noisy confidence at distance
91
+ temporalBlendNormal = 0.40;
92
+ temporalBlendLowLatency = 0.65;
93
+ // Hysteresis: once a pixel is "person", it needs to drop further to become background
94
+ // Higher values = more stable at edges where confidence fluctuates
95
+ hysteresisOffset = 0.25;
96
+ lowLatencyMode = true;
97
+ constructor(config = {}) {
98
+ this.config = { ...DEFAULT_SEGMENTER_CONFIG, ...config };
99
+ this._modelType = this.config.modelType ?? 'fast';
100
+ // Create work canvas
101
+ this.workCanvas = document.createElement('canvas');
102
+ this.workCanvas.width = this.config.width;
103
+ this.workCanvas.height = this.config.height;
104
+ this.workCtx = this.workCanvas.getContext('2d', { willReadFrequently: true });
105
+ // Create output canvas
106
+ this.outputCanvas = document.createElement('canvas');
107
+ this.outputCanvas.width = this.config.width;
108
+ this.outputCanvas.height = this.config.height;
109
+ this.outputCtx = this.outputCanvas.getContext('2d', { willReadFrequently: true });
110
+ // Create temp canvas
111
+ this.tempCanvas = document.createElement('canvas');
112
+ this.tempCanvas.width = this.config.width;
113
+ this.tempCanvas.height = this.config.height;
114
+ this.tempCtx = this.tempCanvas.getContext('2d', { willReadFrequently: true });
115
+ // Initialize buffers
116
+ this.previousAlpha = new Uint8Array(this.config.width * this.config.height);
117
+ this.backgroundPersistence = new Uint8Array(this.config.width * this.config.height);
118
+ }
119
+ /**
120
+ * Initialize MediaPipe model
121
+ */
122
+ async initialize() {
123
+ if (this.isInitialized || this.isInitializing) {
124
+ return;
125
+ }
126
+ this.isInitializing = true;
127
+ try {
128
+ const assetUrls = this.config.assetUrls ?? DEFAULT_ASSET_URLS;
129
+ if (DEBUG)
130
+ console.log('[MediaPipeSegmenter] Loading MediaPipe vision WASM from:', assetUrls.wasmUrl);
131
+ const vision = await FilesetResolver.forVisionTasks(assetUrls.wasmUrl);
132
+ // Select model URL based on modelType
133
+ const modelUrl = this._modelType === 'quality'
134
+ ? DEEPLAB_MODEL_URL
135
+ : assetUrls.modelUrl;
136
+ if (DEBUG)
137
+ console.log(`[MediaPipeSegmenter] Creating image segmenter (${this._modelType}) with model:`, modelUrl);
138
+ // CPU delegate avoids WebGL context conflicts with Three.js.
139
+ // GPU delegate creates its own WebGL context which can produce
140
+ // all-zero inference results when sharing the GPU with Three.js.
141
+ this.segmenter = await ImageSegmenter.createFromOptions(vision, {
142
+ baseOptions: {
143
+ modelAssetPath: modelUrl,
144
+ delegate: 'CPU'
145
+ },
146
+ runningMode: 'VIDEO',
147
+ outputCategoryMask: false,
148
+ outputConfidenceMasks: true
149
+ });
150
+ this.isInitialized = true;
151
+ if (DEBUG)
152
+ console.log(`[MediaPipeSegmenter] Initialization complete (${this._modelType})`);
153
+ }
154
+ catch (error) {
155
+ console.error('[MediaPipeSegmenter] Failed to initialize:', error);
156
+ throw error;
157
+ }
158
+ finally {
159
+ this.isInitializing = false;
160
+ }
161
+ }
162
+ /**
163
+ * Process a video frame and return segmentation mask
164
+ */
165
+ process(video) {
166
+ if (!this.isInitialized || !this.segmenter) {
167
+ console.warn('[MediaPipeSegmenter] Not initialized, skipping frame');
168
+ return null;
169
+ }
170
+ if (video.readyState < 2) {
171
+ return null;
172
+ }
173
+ const startTime = performance.now();
174
+ try {
175
+ // Contrast enhancement: mild boost helps the model distinguish
176
+ // person from background, especially at distance or in flat lighting.
177
+ // Applied before inference so the model sees a clearer input.
178
+ this.tempCtx.filter = 'contrast(1.15) saturate(1.1)';
179
+ this.tempCtx.drawImage(video, 0, 0, this.config.width, this.config.height);
180
+ this.tempCtx.filter = 'none';
181
+ const result = this.segmenter.segmentForVideo(this.tempCanvas, performance.now());
182
+ this.processMask(result);
183
+ const processingTimeMs = performance.now() - startTime;
184
+ return {
185
+ maskData: this.outputCtx.getImageData(0, 0, this.config.width, this.config.height),
186
+ maskCanvas: this.outputCanvas,
187
+ processingTimeMs
188
+ };
189
+ }
190
+ catch (error) {
191
+ console.error('[MediaPipeSegmenter] Processing error:', error);
192
+ return null;
193
+ }
194
+ }
195
+ /**
196
+ * Process MediaPipe result into a usable mask
197
+ */
198
+ processMask(result) {
199
+ const confidenceMasks = result.confidenceMasks;
200
+ if (!confidenceMasks || confidenceMasks.length === 0) {
201
+ this.workCtx.clearRect(0, 0, this.config.width, this.config.height);
202
+ this.copyWorkToOutput();
203
+ return;
204
+ }
205
+ // selfie_segmenter: person = confidenceMasks[0]
206
+ // DeepLabV3 PASCAL VOC: person = confidenceMasks[15] (21 classes)
207
+ const personIndex = this._modelType === 'quality'
208
+ ? MediaPipeSegmenter.DEEPLAB_PERSON_INDEX
209
+ : 0;
210
+ if (personIndex >= confidenceMasks.length) {
211
+ this.workCtx.clearRect(0, 0, this.config.width, this.config.height);
212
+ this.copyWorkToOutput();
213
+ return;
214
+ }
215
+ const personMask = confidenceMasks[personIndex];
216
+ const maskData = personMask.getAsFloat32Array();
217
+ const imageData = this.workCtx.createImageData(this.config.width, this.config.height);
218
+ const data = imageData.data;
219
+ const threshold = this.config.threshold;
220
+ const blendFactor = this.lowLatencyMode ? this.temporalBlendLowLatency : this.temporalBlendNormal;
221
+ const prevAlpha = this.previousAlpha;
222
+ const persistence = this.backgroundPersistence;
223
+ const persistThreshold = this.lowLatencyMode ? this.persistenceThresholdLowLatency : this.persistenceThreshold;
224
+ const hysteresis = this.hysteresisOffset;
225
+ for (let i = 0; i < maskData.length; i++) {
226
+ const confidence = maskData[i];
227
+ const wasPerson = prevAlpha && prevAlpha[i] > 127;
228
+ const effectiveThreshold = wasPerson ? threshold - hysteresis : threshold;
229
+ const isPersonThisFrame = confidence >= effectiveThreshold;
230
+ let alpha;
231
+ if (isPersonThisFrame) {
232
+ if (persistence) {
233
+ persistence[i] = 0;
234
+ }
235
+ if (confidence >= effectiveThreshold + 0.1) {
236
+ alpha = 255;
237
+ }
238
+ else {
239
+ alpha = Math.round(((confidence - effectiveThreshold) / 0.1) * 127.5 + 127.5);
240
+ }
241
+ }
242
+ else {
243
+ if (persistence) {
244
+ persistence[i] = Math.min(255, persistence[i] + 1);
245
+ if (persistence[i] < persistThreshold) {
246
+ alpha = prevAlpha ? prevAlpha[i] : 0;
247
+ }
248
+ else {
249
+ if (confidence <= effectiveThreshold - 0.1) {
250
+ alpha = 0;
251
+ }
252
+ else {
253
+ alpha = Math.round(((confidence - (effectiveThreshold - 0.1)) / 0.1) * 127.5);
254
+ }
255
+ }
256
+ }
257
+ else {
258
+ alpha = confidence <= effectiveThreshold - 0.1 ? 0 :
259
+ Math.round(((confidence - (effectiveThreshold - 0.1)) / 0.2) * 255);
260
+ }
261
+ }
262
+ if (prevAlpha) {
263
+ alpha = Math.round(alpha * blendFactor + prevAlpha[i] * (1 - blendFactor));
264
+ prevAlpha[i] = alpha;
265
+ }
266
+ data[i * 4] = alpha;
267
+ data[i * 4 + 1] = alpha;
268
+ data[i * 4 + 2] = alpha;
269
+ data[i * 4 + 3] = 255;
270
+ }
271
+ this.workCtx.putImageData(imageData, 0, 0);
272
+ // Dilation pass: fills small 1-2px holes common when subject is far from camera
273
+ this.applyDilation();
274
+ if (this.config.edgeBlur > 0) {
275
+ this.applyEdgeBlur();
276
+ }
277
+ this.copyWorkToOutput();
278
+ // Close all returned masks to free memory.
279
+ // DeepLabV3 returns 21 masks (PASCAL VOC classes), selfie_segmenter returns 1.
280
+ for (const mask of confidenceMasks) {
281
+ mask.close();
282
+ }
283
+ }
284
+ /**
285
+ * Copy work canvas to output canvas
286
+ */
287
+ copyWorkToOutput() {
288
+ this.outputCtx.drawImage(this.workCanvas, 0, 0);
289
+ }
290
+ /**
291
+ * Apply morphological dilation to fill small holes in the mask.
292
+ * Uses a 4-connected max-filter: draws the mask shifted 1px in each
293
+ * cardinal direction with 'lighten' blend (pixel max). This expands
294
+ * bright regions by 1px, filling 1-2px holes common at distance.
295
+ */
296
+ applyDilation() {
297
+ // Copy current mask to temp canvas as read-only source
298
+ // (tempCanvas is safe to reuse — video frame processing is complete)
299
+ this.tempCtx.clearRect(0, 0, this.config.width, this.config.height);
300
+ this.tempCtx.drawImage(this.workCanvas, 0, 0);
301
+ // Draw 4 shifted copies using 'lighten' (takes max per-pixel)
302
+ this.workCtx.save();
303
+ this.workCtx.globalCompositeOperation = 'lighten';
304
+ this.workCtx.drawImage(this.tempCanvas, -1, 0);
305
+ this.workCtx.drawImage(this.tempCanvas, 1, 0);
306
+ this.workCtx.drawImage(this.tempCanvas, 0, -1);
307
+ this.workCtx.drawImage(this.tempCanvas, 0, 1);
308
+ this.workCtx.restore();
309
+ }
310
+ /**
311
+ * Apply Gaussian blur to soften mask edges
312
+ */
313
+ applyEdgeBlur() {
314
+ this.workCtx.filter = `blur(${this.config.edgeBlur}px)`;
315
+ this.workCtx.drawImage(this.workCanvas, 0, 0);
316
+ this.workCtx.filter = 'none';
317
+ }
318
+ /**
319
+ * Reset temporal smoothing buffers.
320
+ * Call when the video source changes (camera switch) to avoid
321
+ * stale mask data from the previous camera bleeding into the new one.
322
+ */
323
+ reset() {
324
+ if (this.previousAlpha)
325
+ this.previousAlpha.fill(0);
326
+ if (this.backgroundPersistence)
327
+ this.backgroundPersistence.fill(0);
328
+ }
329
+ /**
330
+ * Update segmentation threshold
331
+ */
332
+ setThreshold(threshold) {
333
+ this.config.threshold = Math.max(0, Math.min(1, threshold));
334
+ }
335
+ /**
336
+ * Update edge blur radius
337
+ */
338
+ setEdgeBlur(edgeBlur) {
339
+ this.config.edgeBlur = Math.max(0, Math.min(20, edgeBlur));
340
+ }
341
+ /**
342
+ * Enable/disable low latency mode
343
+ */
344
+ setLowLatency(enabled) {
345
+ this.lowLatencyMode = enabled;
346
+ }
347
+ /**
348
+ * Switch between fast (selfie_segmenter) and quality (DeepLabV3) models.
349
+ * Disposes the current model and re-initializes with the new one.
350
+ * Returns a promise that resolves when the new model is ready.
351
+ */
352
+ async switchModel(modelType) {
353
+ if (modelType === this._modelType && this.isInitialized) {
354
+ return;
355
+ }
356
+ this._modelType = modelType;
357
+ // Dispose current model and re-initialize
358
+ if (this.segmenter) {
359
+ this.segmenter.close();
360
+ this.segmenter = null;
361
+ }
362
+ this.isInitialized = false;
363
+ this.reset();
364
+ await this.initialize();
365
+ }
366
+ /**
367
+ * Get the current model type
368
+ */
369
+ get currentModelType() {
370
+ return this._modelType;
371
+ }
372
+ /**
373
+ * Get current configuration
374
+ */
375
+ getConfig() {
376
+ return { ...this.config };
377
+ }
378
+ /**
379
+ * Check if segmenter is ready
380
+ */
381
+ get ready() {
382
+ return this.isInitialized;
383
+ }
384
+ /**
385
+ * Check if segmenter is currently loading
386
+ */
387
+ get loading() {
388
+ return this.isInitializing;
389
+ }
390
+ /**
391
+ * Get the output mask canvas for use as texture source
392
+ */
393
+ getMaskCanvas() {
394
+ return this.outputCanvas;
395
+ }
396
+ /**
397
+ * Clean up resources
398
+ */
399
+ dispose() {
400
+ if (this.segmenter) {
401
+ this.segmenter.close();
402
+ this.segmenter = null;
403
+ }
404
+ this.isInitialized = false;
405
+ if (DEBUG)
406
+ console.log('[MediaPipeSegmenter] Disposed');
407
+ }
408
+ }
409
+ /**
410
+ * Detect if running on mobile device
411
+ */
412
+ export function isMobileDevice() {
413
+ if (typeof navigator === 'undefined')
414
+ return false;
415
+ return (/iPhone|iPad|Android/i.test(navigator.userAgent) ||
416
+ (typeof window !== 'undefined' && window.innerWidth < 768));
417
+ }
418
+ /**
419
+ * Get optimal processing configuration based on device
420
+ */
421
+ export function getOptimalConfig() {
422
+ if (isMobileDevice()) {
423
+ return {
424
+ width: 320,
425
+ height: 240,
426
+ edgeBlur: 2
427
+ };
428
+ }
429
+ return {
430
+ width: 640,
431
+ height: 480,
432
+ edgeBlur: 3
433
+ };
434
+ }
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Segmentation module exports
3
+ */
4
+ export { MediaPipeSegmenter, type SegmenterConfig, type SegmentationResult, type MediaPipeAssetUrls, type AIModelType, DEFAULT_SEGMENTER_CONFIG, DEFAULT_ASSET_URLS, DEEPLAB_MODEL_URL, SELF_HOSTED_ASSET_URLS, isMobileDevice, getOptimalConfig } from './MediaPipeSegmenter';
5
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/segmentation/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EACN,kBAAkB,EAClB,KAAK,eAAe,EACpB,KAAK,kBAAkB,EACvB,KAAK,kBAAkB,EACvB,KAAK,WAAW,EAChB,wBAAwB,EACxB,kBAAkB,EAClB,iBAAiB,EACjB,sBAAsB,EACtB,cAAc,EACd,gBAAgB,EAChB,MAAM,sBAAsB,CAAC"}
@@ -0,0 +1,4 @@
1
+ /**
2
+ * Segmentation module exports
3
+ */
4
+ export { MediaPipeSegmenter, DEFAULT_SEGMENTER_CONFIG, DEFAULT_ASSET_URLS, DEEPLAB_MODEL_URL, SELF_HOSTED_ASSET_URLS, isMobileDevice, getOptimalConfig } from './MediaPipeSegmenter';