seg-cam 0.4.8 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  An easy-to-use drop-in library that wraps around TensorFlow.js BodyPix. Using it is as simple as passing a video reference to the `JerseyDetector` component.
4
4
 
5
- `seg-cam` was create for real-time video processing, for example to detect the shirts of users in a video stream.
5
+ `seg-cam` was created for real-time video processing—optimized for high-performance jersey and person segmentation in browser environments.
6
6
 
7
7
  ## Features
8
8
 
@@ -82,10 +82,10 @@ export function CameraApp() {
82
82
  | `threshold` | `number` | `0.5` | Confidence threshold for detection (0 to 1). |
83
83
  | `targetPartId` | `number` | `12` | BodyPix Part ID to isolate (12 = Torso). |
84
84
  | `imagesToReturn` | `'mask' \| 'mask_with_image' \| 'none'` | `'mask'` | Controls output: 'mask' (overlay only), 'mask_with_image' (overlay + frame), 'none' (data only). |
85
- | `onStatsUpdate`| `(stats: DetectionStats) => void` | - | Callback triggered on every processed frame with stats. |
85
+ | `onStatsUpdate`| `(stats: DetectionStats) => void` | - | Throttled (500ms). Callback triggered with detection metrics. |
86
86
  | `onWorkerReady`| `(ready: boolean) => void` | - | Callback triggered when the TFJS model is loaded. |
87
87
  | `onWorkerError`| `(error: string \| null) => void` | - | Callback triggered if the worker fails to initialize. |
88
- | `onSegmentedImage`| `(images: ImageBitmap[]) => void`| - | Callback returning individual bitmaps of detected objects. |
88
+ | `onSegmentedImage`| `(images: ImageBitmap[], bboxes?: BoundingBox[]) => void`| - | Throttled (500ms). Returns individual bitmaps and bounding boxes of detected objects (tlbr format). |
89
89
  | `bodyPixArchitecture`| `'MobileNetV1' \| 'ResNet50'` | `'MobileNetV1'` | Performance vs Accuracy trade-off. |
90
90
  | `verbose` | `boolean` | `false` | Enable detailed console logging. |
91
91
 
@@ -136,6 +136,16 @@ const currentStats = detectorRef.current?.stats;
136
136
  - `'none'`: Skips bitmap generation entirely. Use this if you only need `onSegmentedImage` or stats, improving performance.
137
137
  - **`threshold`**: (`number`, 0.0-1.0, Default `0.5`) The minimum confidence required for a person pixel to be considered valid.
138
138
 
139
+ ## Performance Tips
140
+
141
+ To get the most out of `seg-cam` v1.0.0:
142
+
143
+ 1. **Use `imagesToReturn="none"`** if you only need the data (stats, segmented images, or bboxes). This bypasses the creation of the full-screen overlay bitmap.
144
+ 2. **Optimize your `videoRef` dimensions**: Processing smaller video streams (e.g., 640x480) is significantly faster than 1080p.
145
+ 3. **Architecture Choice**: `MobileNetV1` with a `multiplier` of `0.75` or `0.5` is the sweet spot for mobile devices.
146
+ 4. **Quantization**: Stick to `bodyPixQuantBytes={2}` for a good balance between model size and accuracy.
147
+ 5. **Notification Throttling**: By default, `onStatsUpdate` and `onSegmentedImage` are throttled to **500ms** to ensure the main thread remains responsive. They will still fire immediately if the number of detected jerseys changes.
148
+
139
149
  ## Acknowledgements
140
150
 
141
151
  This project is powered by **TensorFlow.js** and the **BodyPix** model.
package/dist/index.d.mts CHANGED
@@ -33,6 +33,7 @@ declare const DEFAULT_CONFIG: {
33
33
  TARGET_PART_ID: number;
34
34
  IMAGES_TO_RETURN: ImagesToReturn;
35
35
  VERBOSE: boolean;
36
+ MIN_DETECTION_AREA: number;
36
37
  };
37
38
 
38
39
  /**
@@ -49,36 +50,6 @@ interface DetectionStats {
49
50
  fps: number;
50
51
  }
51
52
 
52
- interface JerseyDetectorHandle {
53
- isReady: boolean;
54
- stats: DetectionStats;
55
- lastError: string | null;
56
- capture: (format?: "image/jpeg" | "image/png", quality?: number) => string;
57
- }
58
- interface JerseyDetectorProps {
59
- videoRef: React.RefObject<HTMLVideoElement>;
60
- onWorkerReady?: (ready: boolean) => void;
61
- onWorkerError?: (error: string | null) => void;
62
- onStatsUpdate?: (stats: DetectionStats) => void;
63
- onSegmentedImage?: (segmentedImages: ImageBitmap[]) => void;
64
- task?: TaskType;
65
- model?: ModelType;
66
- verbose?: boolean;
67
- bodyPixArchitecture?: "MobileNetV1" | "ResNet50";
68
- bodyPixMultiplier?: number;
69
- bodyPixQuantBytes?: number;
70
- bodyPixStride?: number;
71
- multiSegmentation?: boolean;
72
- segmentBodyParts?: boolean;
73
- backgroundShade?: number;
74
- shirtShade?: number;
75
- threshold?: number;
76
- targetPartId?: number;
77
- imagesToReturn?: ImagesToReturn;
78
- workerUrl?: string;
79
- }
80
- declare const JerseyDetector: react.ForwardRefExoticComponent<JerseyDetectorProps & react.RefAttributes<JerseyDetectorHandle>>;
81
-
82
53
  type WorkerConfig = {
83
54
  task: TaskType;
84
55
  model: ModelType;
@@ -94,24 +65,36 @@ type WorkerConfig = {
94
65
  threshold?: number;
95
66
  targetPartId?: number;
96
67
  imagesToReturn?: ImagesToReturn;
68
+ minDetectionArea?: number;
69
+ };
70
+ type BoundingBox = {
71
+ top: number;
72
+ left: number;
73
+ bottom: number;
74
+ right: number;
97
75
  };
98
- type ImageDimensions$1 = {
76
+ type ImageDimensions = {
99
77
  width: number;
100
78
  height: number;
101
79
  };
102
80
  type WorkerInitMessage = {
103
81
  type: "init";
82
+ requestId?: string;
104
83
  isProduction: boolean;
105
84
  config: WorkerConfig;
106
85
  };
107
86
  type WorkerDetectMessage = {
87
+ requestId: string;
108
88
  type: "detect";
109
- dimensions: ImageDimensions$1;
89
+ dimensions: ImageDimensions;
110
90
  threshold?: number;
111
91
  bitmap: ImageBitmap;
112
92
  };
113
93
  type WorkerReadyMessage = {
114
94
  type: "ready";
95
+ config: WorkerConfig;
96
+ device: string;
97
+ dtype: string;
115
98
  };
116
99
  type WorkerProgressMessage = {
117
100
  type: "loading_progress";
@@ -127,6 +110,7 @@ type WorkerDetectResponseMessage = {
127
110
  bitmap?: ImageBitmap;
128
111
  stats: DetectionStats;
129
112
  segmentedImages?: ImageBitmap[];
113
+ bboxes?: BoundingBox[];
130
114
  };
131
115
  type WorkerDebugMessage = {
132
116
  type: "debug";
@@ -135,10 +119,37 @@ type WorkerDebugMessage = {
135
119
  type WorkerPostMessage = WorkerInitMessage | WorkerDetectMessage;
136
120
  type WorkerResponseMessage = WorkerReadyMessage | WorkerProgressMessage | WorkerErrorMessage | WorkerDetectResponseMessage | WorkerDetectMessage;
137
121
 
138
- interface ImageDimensions {
139
- width: number;
140
- height: number;
122
+ interface JerseyDetectorHandle {
123
+ isReady: boolean;
124
+ stats: DetectionStats;
125
+ lastError: string | null;
126
+ capture: (format?: "image/jpeg" | "image/png", quality?: number) => string;
127
+ }
128
+ interface JerseyDetectorProps {
129
+ videoRef: React.RefObject<HTMLVideoElement>;
130
+ onWorkerReady?: (ready: boolean) => void;
131
+ onWorkerError?: (error: string | null) => void;
132
+ onStatsUpdate?: (stats: DetectionStats) => void;
133
+ onSegmentedImage?: (segmentedImages: ImageBitmap[], bboxes?: BoundingBox[]) => void;
134
+ task?: TaskType;
135
+ model?: ModelType;
136
+ verbose?: boolean;
137
+ bodyPixArchitecture?: "MobileNetV1" | "ResNet50";
138
+ bodyPixMultiplier?: number;
139
+ bodyPixQuantBytes?: number;
140
+ bodyPixStride?: number;
141
+ multiSegmentation?: boolean;
142
+ segmentBodyParts?: boolean;
143
+ backgroundShade?: number;
144
+ shirtShade?: number;
145
+ threshold?: number;
146
+ targetPartId?: number;
147
+ imagesToReturn?: ImagesToReturn;
148
+ minDetectionArea?: number;
149
+ workerUrl?: string;
141
150
  }
151
+ declare const JerseyDetector: react.ForwardRefExoticComponent<JerseyDetectorProps & react.RefAttributes<JerseyDetectorHandle>>;
152
+
142
153
  /**
143
154
  * Simplified hook for jersey detection worker
144
155
  * Manages worker lifecycle, state, and detection requests
@@ -150,4 +161,4 @@ declare function useJerseyWorker(config: WorkerConfig, workerUrlProp?: string):
150
161
  detect: (bitmap: ImageBitmap, dimensions: ImageDimensions, threshold?: number) => Promise<any>;
151
162
  };
152
163
 
153
- export { DEFAULT_CONFIG, type DetectionStats, type ImageDimensions$1 as ImageDimensions, type ImageSegmentationType, type ImagesToReturn, JerseyDetector, type JerseyDetectorHandle, type JerseyDetectorProps, type ModelType, type ObjectDetectionType, type TaskType, type WorkerConfig, type WorkerDebugMessage, type WorkerDetectMessage, type WorkerDetectResponseMessage, type WorkerErrorMessage, type WorkerInitMessage, type WorkerPostMessage, type WorkerProgressMessage, type WorkerReadyMessage, type WorkerResponseMessage, useJerseyWorker };
164
+ export { type BoundingBox, DEFAULT_CONFIG, type DetectionStats, type ImageDimensions, type ImageSegmentationType, type ImagesToReturn, JerseyDetector, type JerseyDetectorHandle, type JerseyDetectorProps, type ModelType, type ObjectDetectionType, type TaskType, type WorkerConfig, type WorkerDebugMessage, type WorkerDetectMessage, type WorkerDetectResponseMessage, type WorkerErrorMessage, type WorkerInitMessage, type WorkerPostMessage, type WorkerProgressMessage, type WorkerReadyMessage, type WorkerResponseMessage, useJerseyWorker };
package/dist/index.d.ts CHANGED
@@ -33,6 +33,7 @@ declare const DEFAULT_CONFIG: {
33
33
  TARGET_PART_ID: number;
34
34
  IMAGES_TO_RETURN: ImagesToReturn;
35
35
  VERBOSE: boolean;
36
+ MIN_DETECTION_AREA: number;
36
37
  };
37
38
 
38
39
  /**
@@ -49,36 +50,6 @@ interface DetectionStats {
49
50
  fps: number;
50
51
  }
51
52
 
52
- interface JerseyDetectorHandle {
53
- isReady: boolean;
54
- stats: DetectionStats;
55
- lastError: string | null;
56
- capture: (format?: "image/jpeg" | "image/png", quality?: number) => string;
57
- }
58
- interface JerseyDetectorProps {
59
- videoRef: React.RefObject<HTMLVideoElement>;
60
- onWorkerReady?: (ready: boolean) => void;
61
- onWorkerError?: (error: string | null) => void;
62
- onStatsUpdate?: (stats: DetectionStats) => void;
63
- onSegmentedImage?: (segmentedImages: ImageBitmap[]) => void;
64
- task?: TaskType;
65
- model?: ModelType;
66
- verbose?: boolean;
67
- bodyPixArchitecture?: "MobileNetV1" | "ResNet50";
68
- bodyPixMultiplier?: number;
69
- bodyPixQuantBytes?: number;
70
- bodyPixStride?: number;
71
- multiSegmentation?: boolean;
72
- segmentBodyParts?: boolean;
73
- backgroundShade?: number;
74
- shirtShade?: number;
75
- threshold?: number;
76
- targetPartId?: number;
77
- imagesToReturn?: ImagesToReturn;
78
- workerUrl?: string;
79
- }
80
- declare const JerseyDetector: react.ForwardRefExoticComponent<JerseyDetectorProps & react.RefAttributes<JerseyDetectorHandle>>;
81
-
82
53
  type WorkerConfig = {
83
54
  task: TaskType;
84
55
  model: ModelType;
@@ -94,24 +65,36 @@ type WorkerConfig = {
94
65
  threshold?: number;
95
66
  targetPartId?: number;
96
67
  imagesToReturn?: ImagesToReturn;
68
+ minDetectionArea?: number;
69
+ };
70
+ type BoundingBox = {
71
+ top: number;
72
+ left: number;
73
+ bottom: number;
74
+ right: number;
97
75
  };
98
- type ImageDimensions$1 = {
76
+ type ImageDimensions = {
99
77
  width: number;
100
78
  height: number;
101
79
  };
102
80
  type WorkerInitMessage = {
103
81
  type: "init";
82
+ requestId?: string;
104
83
  isProduction: boolean;
105
84
  config: WorkerConfig;
106
85
  };
107
86
  type WorkerDetectMessage = {
87
+ requestId: string;
108
88
  type: "detect";
109
- dimensions: ImageDimensions$1;
89
+ dimensions: ImageDimensions;
110
90
  threshold?: number;
111
91
  bitmap: ImageBitmap;
112
92
  };
113
93
  type WorkerReadyMessage = {
114
94
  type: "ready";
95
+ config: WorkerConfig;
96
+ device: string;
97
+ dtype: string;
115
98
  };
116
99
  type WorkerProgressMessage = {
117
100
  type: "loading_progress";
@@ -127,6 +110,7 @@ type WorkerDetectResponseMessage = {
127
110
  bitmap?: ImageBitmap;
128
111
  stats: DetectionStats;
129
112
  segmentedImages?: ImageBitmap[];
113
+ bboxes?: BoundingBox[];
130
114
  };
131
115
  type WorkerDebugMessage = {
132
116
  type: "debug";
@@ -135,10 +119,37 @@ type WorkerDebugMessage = {
135
119
  type WorkerPostMessage = WorkerInitMessage | WorkerDetectMessage;
136
120
  type WorkerResponseMessage = WorkerReadyMessage | WorkerProgressMessage | WorkerErrorMessage | WorkerDetectResponseMessage | WorkerDetectMessage;
137
121
 
138
- interface ImageDimensions {
139
- width: number;
140
- height: number;
122
+ interface JerseyDetectorHandle {
123
+ isReady: boolean;
124
+ stats: DetectionStats;
125
+ lastError: string | null;
126
+ capture: (format?: "image/jpeg" | "image/png", quality?: number) => string;
127
+ }
128
+ interface JerseyDetectorProps {
129
+ videoRef: React.RefObject<HTMLVideoElement>;
130
+ onWorkerReady?: (ready: boolean) => void;
131
+ onWorkerError?: (error: string | null) => void;
132
+ onStatsUpdate?: (stats: DetectionStats) => void;
133
+ onSegmentedImage?: (segmentedImages: ImageBitmap[], bboxes?: BoundingBox[]) => void;
134
+ task?: TaskType;
135
+ model?: ModelType;
136
+ verbose?: boolean;
137
+ bodyPixArchitecture?: "MobileNetV1" | "ResNet50";
138
+ bodyPixMultiplier?: number;
139
+ bodyPixQuantBytes?: number;
140
+ bodyPixStride?: number;
141
+ multiSegmentation?: boolean;
142
+ segmentBodyParts?: boolean;
143
+ backgroundShade?: number;
144
+ shirtShade?: number;
145
+ threshold?: number;
146
+ targetPartId?: number;
147
+ imagesToReturn?: ImagesToReturn;
148
+ minDetectionArea?: number;
149
+ workerUrl?: string;
141
150
  }
151
+ declare const JerseyDetector: react.ForwardRefExoticComponent<JerseyDetectorProps & react.RefAttributes<JerseyDetectorHandle>>;
152
+
142
153
  /**
143
154
  * Simplified hook for jersey detection worker
144
155
  * Manages worker lifecycle, state, and detection requests
@@ -150,4 +161,4 @@ declare function useJerseyWorker(config: WorkerConfig, workerUrlProp?: string):
150
161
  detect: (bitmap: ImageBitmap, dimensions: ImageDimensions, threshold?: number) => Promise<any>;
151
162
  };
152
163
 
153
- export { DEFAULT_CONFIG, type DetectionStats, type ImageDimensions$1 as ImageDimensions, type ImageSegmentationType, type ImagesToReturn, JerseyDetector, type JerseyDetectorHandle, type JerseyDetectorProps, type ModelType, type ObjectDetectionType, type TaskType, type WorkerConfig, type WorkerDebugMessage, type WorkerDetectMessage, type WorkerDetectResponseMessage, type WorkerErrorMessage, type WorkerInitMessage, type WorkerPostMessage, type WorkerProgressMessage, type WorkerReadyMessage, type WorkerResponseMessage, useJerseyWorker };
164
+ export { type BoundingBox, DEFAULT_CONFIG, type DetectionStats, type ImageDimensions, type ImageSegmentationType, type ImagesToReturn, JerseyDetector, type JerseyDetectorHandle, type JerseyDetectorProps, type ModelType, type ObjectDetectionType, type TaskType, type WorkerConfig, type WorkerDebugMessage, type WorkerDetectMessage, type WorkerDetectResponseMessage, type WorkerErrorMessage, type WorkerInitMessage, type WorkerPostMessage, type WorkerProgressMessage, type WorkerReadyMessage, type WorkerResponseMessage, useJerseyWorker };
package/dist/index.js CHANGED
@@ -1 +1 @@
1
- "use strict";var Q=Object.defineProperty;var he=Object.getOwnPropertyDescriptor;var Te=Object.getOwnPropertyNames;var Ee=Object.prototype.hasOwnProperty;var De=(e,n)=>{for(var u in n)Q(e,u,{get:n[u],enumerable:!0})},be=(e,n,u,T)=>{if(n&&typeof n=="object"||typeof n=="function")for(let s of Te(n))!Ee.call(e,s)&&s!==u&&Q(e,s,{get:()=>n[s],enumerable:!(T=he(n,s))||T.enumerable});return e};var ke=e=>be(Q({},"__esModule",{value:!0}),e);var O=(e,n,u)=>new Promise((T,s)=>{var x=l=>{try{I(u.next(l))}catch(w){s(w)}},b=l=>{try{I(u.throw(l))}catch(w){s(w)}},I=l=>l.done?T(l.value):Promise.resolve(l.value).then(x,b);I((u=u.apply(e,n)).next())});var Re={};De(Re,{DEFAULT_CONFIG:()=>c,JerseyDetector:()=>re,useJerseyWorker:()=>z});module.exports=ke(Re);var t=require("react");var g=require("react");var f=null,S=!1,G=null,Z=0,ee=null,D=new Map,Ie=0,$=new Map;function z(e,n){let u=(0,g.useRef)(!1),[T,s]=(0,g.useState)(!1),[x,b]=(0,g.useState)(null),I=(0,g.useRef)(!0),l=(0,g.useRef)(!1),w=(0,g.useRef)(`instance_${Math.random().toString(36).substr(2,9)}`),A=(0,g.useRef)(0);(0,g.useEffect)(()=>{var _;if(!e)return;I.current=!0,A.current++;let v=A.current,C=w.current,H=(_=$.get(C))!=null?_:0;return v>H&&(Z++,$.set(C,v)),O(null,null,function*(){var p;if(!f||!we(ee,e)){f&&(f.terminate(),f=null,S=!1);try{l.current=(p=e.verbose)!=null?p:l.current;let a=n||"/jersey-detector-worker.js";f=new Worker(a),ee=e,S=!1,G=null,s(!1),b(null),f.onmessage=y=>{let i=y.data;switch(i.type){case"ready":S=!0,s(!0);break;case"loading_progress":l.current&&console.log(`[v0 useJerseyWorker] Loaded ${i.progress}% of ${i.file}`);break;case"debug":l.current&&console.log(i.debug_message);break;case"error":let M=i.error_message||"Unknown worker error";console.error("[v0 useJerseyWorker] Worker sent error message:",M),S=!1,G=M,s(!1),b(M);break;case"detect_response":if(i.requestId&&D.has(i.requestId)){let E=D.get(i.requestId);E&&(E.resolve(i),D.delete(i.requestId))}else if(D.size>0){let E=D.entries().next();if(!E.done){let[J,N]=E.value;N.resolve(i),D.delete(J)}}break}},f.onerror=y=>{let i=y.message||"Worker initialization error";console.error("[useJerseyWorker] Worker error:",i),S=!1,G=i,s(!1),b(i)},f.postMessage({type:"init",config:e})}catch(a){let y=(a==null?void 0:a.message)||String(a);console.error("[useJerseyWorker] Failed to create worker:",y),s(!1),b(y)}}else s(S),b(G)}),()=>{I.current=!1;let p=w.current,a=$.get(p),y=A.current;a===y&&(Z--,$.delete(p),Z<=0&&f&&(f.terminate(),f=null,S=!1,G=null,ee=null,D.clear()))}},[]);let V=(0,g.useCallback)((v,C,H=.5)=>O(null,null,function*(){if(!f||!S)throw new Error("Worker not ready");if(u.current)return null;let R=`req_${++Ie}_${Date.now()}`;return new Promise((_,p)=>{D.set(R,{resolve:_,reject:p});try{u.current=!0,f.postMessage({type:"detect",requestId:R,dimensions:C,bitmap:v,threshold:H},[v])}catch(a){D.delete(R);let y=`Failed to send detect: ${a==null?void 0:a.message}`;p(new Error(y))}finally{u.current=!1}setTimeout(()=>{D.has(R)&&(D.delete(R),p(new Error("Detection timeout")))},1e4)})}),[T]);return{isReady:T,workerReady:T,lastError:x,detect:V}}function we(e,n){return e?JSON.stringify(e)===JSON.stringify(n):!1}var c={TASK:"image-segmentation",MODEL:"BodyPix",BODYPIX_ARCHITECTURE:"MobileNetV1",BODYPIX_MULTIPLIER:.75,BODYPIX_QUANT_BYTES:2,BODYPIX_STRIDE:16,MULTI_SEGMENTATION:!0,SEGMENT_BODY_PARTS:!0,BACKGROUND_SHADE:0,SHIRT_SHADE:170,DETECTION_THRESHOLD:.5,TARGET_PART_ID:12,IMAGES_TO_RETURN:"mask",VERBOSE:!1};var ue=require("react/jsx-runtime"),re=(0,t.forwardRef)(({videoRef:e,onWorkerReady:n,onWorkerError:u,onStatsUpdate:T,onSegmentedImage:s,task:x=c.TASK,model:b=c.MODEL,verbose:I=c.VERBOSE,bodyPixArchitecture:l=c.BODYPIX_ARCHITECTURE,bodyPixMultiplier:w=c.BODYPIX_MULTIPLIER,bodyPixQuantBytes:A=c.BODYPIX_QUANT_BYTES,bodyPixStride:V=c.BODYPIX_STRIDE,multiSegmentation:v=c.MULTI_SEGMENTATION,segmentBodyParts:C=c.SEGMENT_BODY_PARTS,backgroundShade:H=c.BACKGROUND_SHADE,shirtShade:R=c.SHIRT_SHADE,threshold:_=c.DETECTION_THRESHOLD,targetPartId:p=c.TARGET_PART_ID,imagesToReturn:a=c.IMAGES_TO_RETURN,workerUrl:y},i)=>{var ne,se;let M=(0,t.useRef)(null),E=(0,t.useRef)(null),J=(0,t.useRef)(!1),N=(0,t.useRef)([]),le=(0,t.useRef)(null),L=(0,t.useRef)(!1),Y=(0,t.useRef)(void 0);Y.current=u;let K=(0,t.useRef)({jerseyCount:0,confidence:0,fps:0,processingTime:0}),te=(0,t.useRef)(0),me=500,de=(0,t.useMemo)(()=>({task:x,model:b,verbose:I,bodyPixArchitecture:l,bodyPixMultiplier:w,bodyPixQuantBytes:A,bodyPixStride:V,multiSegmentation:v,segmentBodyParts:C,backgroundShade:H,shirtShade:R,threshold:_,targetPartId:p,imagesToReturn:a}),[x,b,I,l,w,A,V,v,C,H,R,_,p,a]),fe=(0,t.useMemo)(()=>{var r,o;return{width:((r=e.current)==null?void 0:r.videoWidth)||0,height:((o=e.current)==null?void 0:o.videoHeight)||0}},[(ne=e.current)==null?void 0:ne.videoWidth,(se=e.current)==null?void 0:se.videoHeight]),{isReady:P,workerReady:W,lastError:U,detect:ge}=z(de,y);(0,t.useEffect)(()=>{n==null||n(W)},[W,n]),(0,t.useEffect)(()=>{var r;U&&(L.current=!0,(r=Y.current)==null||r.call(Y,U))},[U]);let pe=()=>O(null,null,function*(){var B,oe,ie;let r=e.current,o=M.current;if(L.current||!P||!W||!r||!o||J.current||r.readyState<2||r.paused||r.ended||r.videoWidth===0||r.videoHeight===0)return;let d=o.getContext("2d");if(!d||o.width<=0||o.height<=0)return;let h=null,m=null;try{J.current=!0;let F=r.videoWidth,j=r.videoHeight;le.current={width:F,height:j},h=yield createImageBitmap(r);let k=yield ge(h,fe,_);if((k==null?void 0:k.type)==="detect_response"&&k.bitmap){m=k.bitmap,(o.width!==F||o.height!==j)&&(o.width=F,o.height=j),a==="mask"&&d.clearRect(0,0,F,j),m&&d.drawImage(m,0,0),N.current.push(Date.now()),N.current=N.current.filter(X=>Date.now()-X<1e3);let q={jerseyCount:((B=k.stats)==null?void 0:B.detectionCount)||0,confidence:((oe=k.stats)==null?void 0:oe.averageConfidence)||0,fps:N.current.length,processingTime:((ie=k.stats)==null?void 0:ie.processingTime)||0},ae=K.current;K.current=q;let ce=Date.now();if((ce-te.current>me||q.jerseyCount!==ae.jerseyCount||q.confidence!==ae.confidence)&&T&&(T(q),te.current=ce),k.segmentedImages){let X=k.segmentedImages;O(null,null,function*(){try{yield s==null?void 0:s(X)}finally{X.forEach(ye=>ye.close())}})}}}catch(F){L.current=!0}finally{h==null||h.close(),m==null||m.close(),J.current=!1}});return(0,t.useImperativeHandle)(i,()=>({get isReady(){return P},get lastError(){return U},get stats(){return K.current},capture:(r="image/jpeg",o=.92)=>{let d=e.current,h=M.current;if(!d||d.videoWidth<=0||d.videoHeight<=0)throw new Error("Video not ready for capture");let m=document.createElement("canvas");m.width=d.videoWidth,m.height=d.videoHeight;let B=m.getContext("2d");if(!B)throw new Error("Failed to get context for capture canvas");return B.drawImage(d,0,0,m.width,m.height),h&&h.width>0&&h.height>0?B.drawImage(h,0,0,m.width,m.height):console.warn("[capture] Mask canvas not available \u2014 returning raw video frame"),m.toDataURL(r,o)}}),[P,U,W]),(0,t.useEffect)(()=>{if(!W||!P||L.current)return;let r=e.current,o=M.current;if(!r||!o||r.videoWidth===0||r.videoHeight===0){let h=setTimeout(()=>{E.current=requestAnimationFrame(()=>{})},100);return()=>clearTimeout(h)}(o.width!==r.videoWidth||o.height!==r.videoHeight)&&(o.width=r.videoWidth,o.height=r.videoHeight);let d=()=>O(null,null,function*(){!W||!P||L.current||(yield pe(),E.current=requestAnimationFrame(d))});return E.current=requestAnimationFrame(d),()=>{E.current&&cancelAnimationFrame(E.current)}},[W,P]),(0,ue.jsx)("canvas",{ref:M,className:"absolute inset-0 pointer-events-none",style:{zIndex:10,width:"100%",height:"100%"}})});re.displayName="JerseyDetector";0&&(module.exports={DEFAULT_CONFIG,JerseyDetector,useJerseyWorker});
1
+ "use strict";var ke=Object.create;var $=Object.defineProperty;var be=Object.getOwnPropertyDescriptor;var we=Object.getOwnPropertyNames;var Re=Object.getPrototypeOf,ve=Object.prototype.hasOwnProperty;var _e=(e,o)=>{for(var c in o)$(e,c,{get:o[c],enumerable:!0})},de=(e,o,c,T)=>{if(o&&typeof o=="object"||typeof o=="function")for(let s of we(o))!ve.call(e,s)&&s!==c&&$(e,s,{get:()=>o[s],enumerable:!(T=be(o,s))||T.enumerable});return e};var Me=(e,o,c)=>(c=e!=null?ke(Re(e)):{},de(o||!e||!e.__esModule?$(c,"default",{value:e,enumerable:!0}):c,e)),Ce=e=>de($({},"__esModule",{value:!0}),e);var S=(e,o,c)=>new Promise((T,s)=>{var O=f=>{try{b(c.next(f))}catch(w){s(w)}},I=f=>{try{b(c.throw(f))}catch(w){s(w)}},b=f=>f.done?T(f.value):Promise.resolve(f.value).then(O,I);b((c=c.apply(e,o)).next())});var Se={};_e(Se,{DEFAULT_CONFIG:()=>d,JerseyDetector:()=>te,useJerseyWorker:()=>K});module.exports=Ce(Se);var n=require("react");var p=require("react");var y=null,C=!1,F=null,ee=0,re=null,D=new Map,We=0,z=new Map;function K(e,o){let c=(0,p.useRef)(!1),[T,s]=(0,p.useState)(!1),[O,I]=(0,p.useState)(null),b=(0,p.useRef)(!0),f=(0,p.useRef)(!1),w=(0,p.useRef)(`instance_${Math.random().toString(36).substr(2,9)}`),A=(0,p.useRef)(0);(0,p.useEffect)(()=>{var _;if(!e)return;b.current=!0,A.current++;let v=A.current,W=w.current,N=(_=z.get(W))!=null?_:0;return v>N&&(ee++,z.set(W,v)),S(null,null,function*(){var h;if(!y||!xe(re,e)){y&&(y.terminate(),y=null,C=!1,console.log("[v0 useJerseyWorker] Worker terminated because of change in config"));try{f.current=(h=e.verbose)!=null?h:f.current;let m=o||"/jersey-detector-worker.js";y=new Worker(m),re=e,C=!1,F=null,s(!1),I(null),y.onmessage=g=>{let a=g.data;switch(a.type){case"ready":C=!0,s(!0),f.current&&console.log("[v0 useJerseyWorker] Worker Ready",{device:a.device,dtype:a.dtype,config:a.config});break;case"loading_progress":f.current&&console.log(`[v0 useJerseyWorker] Loaded ${a.progress}% of ${a.file}`);break;case"debug":f.current&&console.log(a.debug_message);break;case"error":let J=a.error_message||"Unknown worker error";console.error("[v0 useJerseyWorker] Worker sent error message:",J),C=!1,F=J,s(!1),I(J);break;case"detect_response":if(a.requestId&&D.has(a.requestId)){let k=D.get(a.requestId);k&&(k.resolve(a),D.delete(a.requestId))}else if(D.size>0){let k=D.entries().next();if(!k.done){let[x,P]=k.value;P.resolve(a),D.delete(x)}}break}},y.onerror=g=>{let a=g.message||"Worker initialization error";console.error("[useJerseyWorker] Worker error:",a),C=!1,F=a,s(!1),I(a)},y.postMessage({type:"init",config:e})}catch(m){let g=(m==null?void 0:m.message)||String(m);console.error("[useJerseyWorker] Failed to create worker:",g),s(!1),I(g)}}else s(C),I(F)}),()=>{b.current=!1;let h=w.current,m=z.get(h),g=A.current;m===g&&(ee--,z.delete(h),ee<=0&&y&&(y.terminate(),y=null,C=!1,F=null,re=null,D.clear()))}},[]);let V=(0,p.useCallback)((v,W,N=.5)=>S(null,null,function*(){if(!y||!C)throw new Error("Worker not ready");if(c.current)return null;let R=`req_${++We}_${Date.now()}`;return new Promise((_,h)=>{D.set(R,{resolve:_,reject:h});try{c.current=!0,y.postMessage({type:"detect",requestId:R,dimensions:W,bitmap:v,threshold:N},[v])}catch(m){D.delete(R);let g=`Failed to send detect: ${m==null?void 0:m.message}`;h(new Error(g))}finally{c.current=!1}setTimeout(()=>{D.has(R)&&(D.delete(R),h(new Error("Detection timeout")))},1e4)})}),[T]);return{isReady:T,workerReady:T,lastError:O,detect:V}}function xe(e,o){return e?JSON.stringify(e)===JSON.stringify(o):!1}var d={TASK:"image-segmentation",MODEL:"BodyPix",BODYPIX_ARCHITECTURE:"MobileNetV1",BODYPIX_MULTIPLIER:.75,BODYPIX_QUANT_BYTES:2,BODYPIX_STRIDE:16,MULTI_SEGMENTATION:!0,SEGMENT_BODY_PARTS:!0,BACKGROUND_SHADE:0,SHIRT_SHADE:170,DETECTION_THRESHOLD:.5,TARGET_PART_ID:12,IMAGES_TO_RETURN:"mask",VERBOSE:!1,MIN_DETECTION_AREA:1e4};var fe=Me(require("p-limit"));var me=require("react/jsx-runtime"),te=(0,n.forwardRef)(({videoRef:e,onWorkerReady:o,onWorkerError:c,onStatsUpdate:T,onSegmentedImage:s,task:O=d.TASK,model:I=d.MODEL,verbose:b=d.VERBOSE,bodyPixArchitecture:f=d.BODYPIX_ARCHITECTURE,bodyPixMultiplier:w=d.BODYPIX_MULTIPLIER,bodyPixQuantBytes:A=d.BODYPIX_QUANT_BYTES,bodyPixStride:V=d.BODYPIX_STRIDE,multiSegmentation:v=d.MULTI_SEGMENTATION,segmentBodyParts:W=d.SEGMENT_BODY_PARTS,backgroundShade:N=d.BACKGROUND_SHADE,shirtShade:R=d.SHIRT_SHADE,threshold:_=d.DETECTION_THRESHOLD,targetPartId:h=d.TARGET_PART_ID,imagesToReturn:m=d.IMAGES_TO_RETURN,minDetectionArea:g=d.MIN_DETECTION_AREA,workerUrl:a},J)=>{let k=(0,n.useRef)(null),x=(0,n.useRef)(null),P=(0,n.useRef)(!1),j=(0,n.useRef)([]),ge=(0,n.useRef)(null),L=(0,n.useRef)(!1),Y=(0,n.useRef)(void 0);Y.current=c;let Q=(0,n.useRef)({jerseyCount:0,confidence:0,fps:0,processingTime:0}),ne=(0,n.useRef)(0),ye=500,oe=5,se=(0,n.useMemo)(()=>(0,fe.default)(oe),[]),Z=(0,n.useMemo)(()=>({task:O,model:I,verbose:b,bodyPixArchitecture:f,bodyPixMultiplier:w,bodyPixQuantBytes:A,bodyPixStride:V,multiSegmentation:v,segmentBodyParts:W,backgroundShade:N,shirtShade:R,threshold:_,targetPartId:h,imagesToReturn:m,minDetectionArea:g}),[O,I,b,f,w,A,V,v,W,N,R,_,h,m,g]),pe=()=>{var r,i;return{width:((r=e.current)==null?void 0:r.videoWidth)||0,height:((i=e.current)==null?void 0:i.videoHeight)||0}},{isReady:B,workerReady:M,lastError:U,detect:he}=K(Z,a);(0,n.useEffect)(()=>{M&&console.log("[JerseyDetector] Worker Ready with config:",Z),o==null||o(M)},[M,o,Z]),(0,n.useEffect)(()=>{var r;U&&(L.current=!0,(r=Y.current)==null||r.call(Y,U))},[U]);let Te=(0,n.useCallback)((r,i)=>{if(se.activeCount>=oe){r.forEach(t=>{var u;return(u=t==null?void 0:t.close)==null?void 0:u.call(t)}),console.warn("[JerseyDetector] Dropped new segmented images \u2014 max 5 classifications running");return}se(()=>S(null,null,function*(){try{s==null||s(r,i)}catch(t){console.error("[JerseyDetector] Classification error:",t)}finally{r.forEach(t=>{var u;return(u=t==null?void 0:t.close)==null?void 0:u.call(t)})}}))},[s]),Ee=()=>S(null,null,function*(){var H,ie,ae;let r=e.current,i=k.current;if(L.current||!B||!M||!r||!i||P.current||r.readyState<2||r.paused||r.ended||r.videoWidth===0||r.videoHeight===0)return;let t=i.getContext("2d");if(!t||i.width<=0||i.height<=0)return;let u=null,l=null;try{P.current=!0;let G=r.videoWidth,q=r.videoHeight;ge.current={width:G,height:q},u=yield createImageBitmap(r);let De=pe(),E=yield he(u,De,_);if((E==null?void 0:E.type)==="detect_response"){E.bitmap&&m!=="none"&&(l=E.bitmap,(i.width!==G||i.height!==q)&&(i.width=G,i.height=q),t.clearRect(0,0,G,q),t.drawImage(l,0,0),console.log(`[JerseyDetector] Bitmap created with dims ${l.width}x${l.height}`),l.close()),j.current.push(Date.now()),j.current=j.current.filter(Ie=>Date.now()-Ie<1e3);let X={jerseyCount:((H=E.stats)==null?void 0:H.jerseyCount)||0,confidence:((ie=E.stats)==null?void 0:ie.confidence)||0,fps:j.current.length,processingTime:((ae=E.stats)==null?void 0:ae.processingTime)||0},ce=Q.current;Q.current=X;let ue=Date.now(),le=ue-ne.current>ye||X.jerseyCount!==ce.jerseyCount||X.confidence!==ce.confidence;le&&T&&(T(X),ne.current=ue),le&&E.segmentedImages&&s&&Te(E.segmentedImages,E.bboxes)}}catch(G){L.current=!0}finally{u==null||u.close(),l==null||l.close(),P.current=!1}});return(0,n.useImperativeHandle)(J,()=>({get isReady(){return B},get lastError(){return U},get stats(){return Q.current},capture:(r="image/jpeg",i=.92)=>{let t=e.current,u=k.current;if(!t||t.videoWidth<=0||t.videoHeight<=0)throw new Error("Video not ready for capture");let l=document.createElement("canvas");l.width=t.videoWidth,l.height=t.videoHeight;let H=l.getContext("2d");if(!H)throw new Error("Failed to get context for capture canvas");return H.drawImage(t,0,0,l.width,l.height),u&&u.width>0&&u.height>0?H.drawImage(u,0,0,l.width,l.height):console.warn("[capture] Mask canvas not available \u2014 returning raw video frame"),l.toDataURL(r,i)}}),[B,U,M]),(0,n.useEffect)(()=>{if(!M||!B||L.current)return;let r=e.current,i=k.current;if(!r||!i||r.videoWidth===0||r.videoHeight===0){let u=setTimeout(()=>{x.current=requestAnimationFrame(()=>{})},100);return()=>clearTimeout(u)}(i.width!==r.videoWidth||i.height!==r.videoHeight)&&(i.width=r.videoWidth,i.height=r.videoHeight);let t=()=>S(null,null,function*(){!M||!B||L.current||(yield Ee(),x.current=requestAnimationFrame(t))});return x.current=requestAnimationFrame(t),()=>{x.current&&cancelAnimationFrame(x.current)}},[M,B]),(0,me.jsx)("canvas",{ref:k,className:"absolute inset-0 pointer-events-none",style:{zIndex:10,width:"100%",height:"100%"}})});te.displayName="JerseyDetector";0&&(module.exports={DEFAULT_CONFIG,JerseyDetector,useJerseyWorker});
package/dist/index.mjs CHANGED
@@ -1 +1 @@
1
- var W=(t,y,h)=>new Promise((w,c)=>{var O=o=>{try{D(h.next(o))}catch(b){c(b)}},T=o=>{try{D(h.throw(o))}catch(b){c(b)}},D=o=>o.done?w(o.value):Promise.resolve(o.value).then(O,T);D((h=h.apply(t,y)).next())});import{forwardRef as ke,useImperativeHandle as Ie,useRef as I,useEffect as ee,useMemo as ue}from"react";import{useEffect as Te,useState as ce,useCallback as Ee,useRef as F}from"react";var l=null,M=!1,G=null,K=0,Q=null,p=new Map,De=0,$=new Map;function Z(t,y){let h=F(!1),[w,c]=ce(!1),[O,T]=ce(null),D=F(!0),o=F(!1),b=F(`instance_${Math.random().toString(36).substr(2,9)}`),x=F(0);Te(()=>{var v;if(!t)return;D.current=!0,x.current++;let R=x.current,S=b.current,A=(v=$.get(S))!=null?v:0;return R>A&&(K++,$.set(S,R)),W(null,null,function*(){var m;if(!l||!be(Q,t)){l&&(l.terminate(),l=null,M=!1);try{o.current=(m=t.verbose)!=null?m:o.current;let s=y||"/jersey-detector-worker.js";l=new Worker(s),Q=t,M=!1,G=null,c(!1),T(null),l.onmessage=d=>{let n=d.data;switch(n.type){case"ready":M=!0,c(!0);break;case"loading_progress":o.current&&console.log(`[v0 useJerseyWorker] Loaded ${n.progress}% of ${n.file}`);break;case"debug":o.current&&console.log(n.debug_message);break;case"error":let _=n.error_message||"Unknown worker error";console.error("[v0 useJerseyWorker] Worker sent error message:",_),M=!1,G=_,c(!1),T(_);break;case"detect_response":if(n.requestId&&p.has(n.requestId)){let g=p.get(n.requestId);g&&(g.resolve(n),p.delete(n.requestId))}else if(p.size>0){let g=p.entries().next();if(!g.done){let[B,H]=g.value;H.resolve(n),p.delete(B)}}break}},l.onerror=d=>{let n=d.message||"Worker initialization error";console.error("[useJerseyWorker] Worker error:",n),M=!1,G=n,c(!1),T(n)},l.postMessage({type:"init",config:t})}catch(s){let d=(s==null?void 0:s.message)||String(s);console.error("[useJerseyWorker] Failed to create worker:",d),c(!1),T(d)}}else c(M),T(G)}),()=>{D.current=!1;let m=b.current,s=$.get(m),d=x.current;s===d&&(K--,$.delete(m),K<=0&&l&&(l.terminate(),l=null,M=!1,G=null,Q=null,p.clear()))}},[]);let V=Ee((R,S,A=.5)=>W(null,null,function*(){if(!l||!M)throw new Error("Worker not ready");if(h.current)return null;let k=`req_${++De}_${Date.now()}`;return new Promise((v,m)=>{p.set(k,{resolve:v,reject:m});try{h.current=!0,l.postMessage({type:"detect",requestId:k,dimensions:S,bitmap:R,threshold:A},[R])}catch(s){p.delete(k);let d=`Failed to send detect: ${s==null?void 0:s.message}`;m(new Error(d))}finally{h.current=!1}setTimeout(()=>{p.has(k)&&(p.delete(k),m(new Error("Detection timeout")))},1e4)})}),[w]);return{isReady:w,workerReady:w,lastError:O,detect:V}}function be(t,y){return t?JSON.stringify(t)===JSON.stringify(y):!1}var a={TASK:"image-segmentation",MODEL:"BodyPix",BODYPIX_ARCHITECTURE:"MobileNetV1",BODYPIX_MULTIPLIER:.75,BODYPIX_QUANT_BYTES:2,BODYPIX_STRIDE:16,MULTI_SEGMENTATION:!0,SEGMENT_BODY_PARTS:!0,BACKGROUND_SHADE:0,SHIRT_SHADE:170,DETECTION_THRESHOLD:.5,TARGET_PART_ID:12,IMAGES_TO_RETURN:"mask",VERBOSE:!1};import{jsx as we}from"react/jsx-runtime";var le=ke(({videoRef:t,onWorkerReady:y,onWorkerError:h,onStatsUpdate:w,onSegmentedImage:c,task:O=a.TASK,model:T=a.MODEL,verbose:D=a.VERBOSE,bodyPixArchitecture:o=a.BODYPIX_ARCHITECTURE,bodyPixMultiplier:b=a.BODYPIX_MULTIPLIER,bodyPixQuantBytes:x=a.BODYPIX_QUANT_BYTES,bodyPixStride:V=a.BODYPIX_STRIDE,multiSegmentation:R=a.MULTI_SEGMENTATION,segmentBodyParts:S=a.SEGMENT_BODY_PARTS,backgroundShade:A=a.BACKGROUND_SHADE,shirtShade:k=a.SHIRT_SHADE,threshold:v=a.DETECTION_THRESHOLD,targetPartId:m=a.TARGET_PART_ID,imagesToReturn:s=a.IMAGES_TO_RETURN,workerUrl:d},n)=>{var te,ne;let _=I(null),g=I(null),B=I(!1),H=I([]),me=I(null),J=I(!1),Y=I(void 0);Y.current=h;let z=I({jerseyCount:0,confidence:0,fps:0,processingTime:0}),re=I(0),de=500,fe=ue(()=>({task:O,model:T,verbose:D,bodyPixArchitecture:o,bodyPixMultiplier:b,bodyPixQuantBytes:x,bodyPixStride:V,multiSegmentation:R,segmentBodyParts:S,backgroundShade:A,shirtShade:k,threshold:v,targetPartId:m,imagesToReturn:s}),[O,T,D,o,b,x,V,R,S,A,k,v,m,s]),ge=ue(()=>{var e,r;return{width:((e=t.current)==null?void 0:e.videoWidth)||0,height:((r=t.current)==null?void 0:r.videoHeight)||0}},[(te=t.current)==null?void 0:te.videoWidth,(ne=t.current)==null?void 0:ne.videoHeight]),{isReady:N,workerReady:C,lastError:L,detect:pe}=Z(fe,d);ee(()=>{y==null||y(C)},[C,y]),ee(()=>{var e;L&&(J.current=!0,(e=Y.current)==null||e.call(Y,L))},[L]);let ye=()=>W(null,null,function*(){var P,se,oe;let e=t.current,r=_.current;if(J.current||!N||!C||!e||!r||B.current||e.readyState<2||e.paused||e.ended||e.videoWidth===0||e.videoHeight===0)return;let u=r.getContext("2d");if(!u||r.width<=0||r.height<=0)return;let f=null,i=null;try{B.current=!0;let U=e.videoWidth,j=e.videoHeight;me.current={width:U,height:j},f=yield createImageBitmap(e);let E=yield pe(f,ge,v);if((E==null?void 0:E.type)==="detect_response"&&E.bitmap){i=E.bitmap,(r.width!==U||r.height!==j)&&(r.width=U,r.height=j),s==="mask"&&u.clearRect(0,0,U,j),i&&u.drawImage(i,0,0),H.current.push(Date.now()),H.current=H.current.filter(X=>Date.now()-X<1e3);let q={jerseyCount:((P=E.stats)==null?void 0:P.detectionCount)||0,confidence:((se=E.stats)==null?void 0:se.averageConfidence)||0,fps:H.current.length,processingTime:((oe=E.stats)==null?void 0:oe.processingTime)||0},ie=z.current;z.current=q;let ae=Date.now();if((ae-re.current>de||q.jerseyCount!==ie.jerseyCount||q.confidence!==ie.confidence)&&w&&(w(q),re.current=ae),E.segmentedImages){let X=E.segmentedImages;W(null,null,function*(){try{yield c==null?void 0:c(X)}finally{X.forEach(he=>he.close())}})}}}catch(U){J.current=!0}finally{f==null||f.close(),i==null||i.close(),B.current=!1}});return Ie(n,()=>({get isReady(){return N},get lastError(){return L},get stats(){return z.current},capture:(e="image/jpeg",r=.92)=>{let u=t.current,f=_.current;if(!u||u.videoWidth<=0||u.videoHeight<=0)throw new Error("Video not ready for capture");let i=document.createElement("canvas");i.width=u.videoWidth,i.height=u.videoHeight;let P=i.getContext("2d");if(!P)throw new Error("Failed to get context for capture canvas");return P.drawImage(u,0,0,i.width,i.height),f&&f.width>0&&f.height>0?P.drawImage(f,0,0,i.width,i.height):console.warn("[capture] Mask canvas not available \u2014 returning raw video frame"),i.toDataURL(e,r)}}),[N,L,C]),ee(()=>{if(!C||!N||J.current)return;let e=t.current,r=_.current;if(!e||!r||e.videoWidth===0||e.videoHeight===0){let f=setTimeout(()=>{g.current=requestAnimationFrame(()=>{})},100);return()=>clearTimeout(f)}(r.width!==e.videoWidth||r.height!==e.videoHeight)&&(r.width=e.videoWidth,r.height=e.videoHeight);let u=()=>W(null,null,function*(){!C||!N||J.current||(yield ye(),g.current=requestAnimationFrame(u))});return g.current=requestAnimationFrame(u),()=>{g.current&&cancelAnimationFrame(g.current)}},[C,N]),we("canvas",{ref:_,className:"absolute inset-0 pointer-events-none",style:{zIndex:10,width:"100%",height:"100%"}})});le.displayName="JerseyDetector";export{a as DEFAULT_CONFIG,le as JerseyDetector,Z as useJerseyWorker};
1
+ var x=(i,p,h)=>new Promise((w,l)=>{var S=a=>{try{D(h.next(a))}catch(I){l(I)}},T=a=>{try{D(h.throw(a))}catch(I){l(I)}},D=a=>a.done?w(a.value):Promise.resolve(a.value).then(S,T);D((h=h.apply(i,p)).next())});import{forwardRef as Re,useImperativeHandle as ve,useRef as b,useEffect as re,useMemo as de,useCallback as _e}from"react";import{useEffect as Ie,useState as le,useCallback as ke,useRef as G}from"react";var f=null,M=!1,F=null,Q=0,Z=null,y=new Map,be=0,$=new Map;function ee(i,p){let h=G(!1),[w,l]=le(!1),[S,T]=le(null),D=G(!0),a=G(!1),I=G(`instance_${Math.random().toString(36).substr(2,9)}`),O=G(0);Ie(()=>{var v;if(!i)return;D.current=!0,O.current++;let R=O.current,C=I.current,A=(v=$.get(C))!=null?v:0;return R>A&&(Q++,$.set(C,R)),x(null,null,function*(){var m;if(!f||!we(Z,i)){f&&(f.terminate(),f=null,M=!1,console.log("[v0 useJerseyWorker] Worker terminated because of change in config"));try{a.current=(m=i.verbose)!=null?m:a.current;let c=p||"/jersey-detector-worker.js";f=new Worker(c),Z=i,M=!1,F=null,l(!1),T(null),f.onmessage=d=>{let n=d.data;switch(n.type){case"ready":M=!0,l(!0),a.current&&console.log("[v0 useJerseyWorker] Worker Ready",{device:n.device,dtype:n.dtype,config:n.config});break;case"loading_progress":a.current&&console.log(`[v0 useJerseyWorker] Loaded ${n.progress}% of ${n.file}`);break;case"debug":a.current&&console.log(n.debug_message);break;case"error":let H=n.error_message||"Unknown worker error";console.error("[v0 useJerseyWorker] Worker sent error message:",H),M=!1,F=H,l(!1),T(H);break;case"detect_response":if(n.requestId&&y.has(n.requestId)){let E=y.get(n.requestId);E&&(E.resolve(n),y.delete(n.requestId))}else if(y.size>0){let E=y.entries().next();if(!E.done){let[W,J]=E.value;J.resolve(n),y.delete(W)}}break}},f.onerror=d=>{let n=d.message||"Worker initialization error";console.error("[useJerseyWorker] Worker error:",n),M=!1,F=n,l(!1),T(n)},f.postMessage({type:"init",config:i})}catch(c){let d=(c==null?void 0:c.message)||String(c);console.error("[useJerseyWorker] Failed to create worker:",d),l(!1),T(d)}}else l(M),T(F)}),()=>{D.current=!1;let m=I.current,c=$.get(m),d=O.current;c===d&&(Q--,$.delete(m),Q<=0&&f&&(f.terminate(),f=null,M=!1,F=null,Z=null,y.clear()))}},[]);let V=ke((R,C,A=.5)=>x(null,null,function*(){if(!f||!M)throw new Error("Worker not ready");if(h.current)return null;let k=`req_${++be}_${Date.now()}`;return new Promise((v,m)=>{y.set(k,{resolve:v,reject:m});try{h.current=!0,f.postMessage({type:"detect",requestId:k,dimensions:C,bitmap:R,threshold:A},[R])}catch(c){y.delete(k);let d=`Failed to send detect: ${c==null?void 0:c.message}`;m(new Error(d))}finally{h.current=!1}setTimeout(()=>{y.has(k)&&(y.delete(k),m(new Error("Detection timeout")))},1e4)})}),[w]);return{isReady:w,workerReady:w,lastError:S,detect:V}}function we(i,p){return i?JSON.stringify(i)===JSON.stringify(p):!1}var u={TASK:"image-segmentation",MODEL:"BodyPix",BODYPIX_ARCHITECTURE:"MobileNetV1",BODYPIX_MULTIPLIER:.75,BODYPIX_QUANT_BYTES:2,BODYPIX_STRIDE:16,MULTI_SEGMENTATION:!0,SEGMENT_BODY_PARTS:!0,BACKGROUND_SHADE:0,SHIRT_SHADE:170,DETECTION_THRESHOLD:.5,TARGET_PART_ID:12,IMAGES_TO_RETURN:"mask",VERBOSE:!1,MIN_DETECTION_AREA:1e4};import Me from"p-limit";import{jsx as Ce}from"react/jsx-runtime";var fe=Re(({videoRef:i,onWorkerReady:p,onWorkerError:h,onStatsUpdate:w,onSegmentedImage:l,task:S=u.TASK,model:T=u.MODEL,verbose:D=u.VERBOSE,bodyPixArchitecture:a=u.BODYPIX_ARCHITECTURE,bodyPixMultiplier:I=u.BODYPIX_MULTIPLIER,bodyPixQuantBytes:O=u.BODYPIX_QUANT_BYTES,bodyPixStride:V=u.BODYPIX_STRIDE,multiSegmentation:R=u.MULTI_SEGMENTATION,segmentBodyParts:C=u.SEGMENT_BODY_PARTS,backgroundShade:A=u.BACKGROUND_SHADE,shirtShade:k=u.SHIRT_SHADE,threshold:v=u.DETECTION_THRESHOLD,targetPartId:m=u.TARGET_PART_ID,imagesToReturn:c=u.IMAGES_TO_RETURN,minDetectionArea:d=u.MIN_DETECTION_AREA,workerUrl:n},H)=>{let E=b(null),W=b(null),J=b(!1),j=b([]),me=b(null),P=b(!1),Y=b(void 0);Y.current=h;let z=b({jerseyCount:0,confidence:0,fps:0,processingTime:0}),te=b(0),ge=500,ne=5,oe=de(()=>Me(ne),[]),K=de(()=>({task:S,model:T,verbose:D,bodyPixArchitecture:a,bodyPixMultiplier:I,bodyPixQuantBytes:O,bodyPixStride:V,multiSegmentation:R,segmentBodyParts:C,backgroundShade:A,shirtShade:k,threshold:v,targetPartId:m,imagesToReturn:c,minDetectionArea:d}),[S,T,D,a,I,O,V,R,C,A,k,v,m,c,d]),ye=()=>{var e,t;return{width:((e=i.current)==null?void 0:e.videoWidth)||0,height:((t=i.current)==null?void 0:t.videoHeight)||0}},{isReady:N,workerReady:_,lastError:L,detect:pe}=ee(K,n);re(()=>{_&&console.log("[JerseyDetector] Worker Ready with config:",K),p==null||p(_)},[_,p,K]),re(()=>{var e;L&&(P.current=!0,(e=Y.current)==null||e.call(Y,L))},[L]);let he=_e((e,t)=>{if(oe.activeCount>=ne){e.forEach(r=>{var o;return(o=r==null?void 0:r.close)==null?void 0:o.call(r)}),console.warn("[JerseyDetector] Dropped new segmented images \u2014 max 5 classifications running");return}oe(()=>x(null,null,function*(){try{l==null||l(e,t)}catch(r){console.error("[JerseyDetector] Classification error:",r)}finally{e.forEach(r=>{var o;return(o=r==null?void 0:r.close)==null?void 0:o.call(r)})}}))},[l]),Te=()=>x(null,null,function*(){var B,se,ie;let e=i.current,t=E.current;if(P.current||!N||!_||!e||!t||J.current||e.readyState<2||e.paused||e.ended||e.videoWidth===0||e.videoHeight===0)return;let r=t.getContext("2d");if(!r||t.width<=0||t.height<=0)return;let o=null,s=null;try{J.current=!0;let U=e.videoWidth,q=e.videoHeight;me.current={width:U,height:q},o=yield createImageBitmap(e);let Ee=ye(),g=yield pe(o,Ee,v);if((g==null?void 0:g.type)==="detect_response"){g.bitmap&&c!=="none"&&(s=g.bitmap,(t.width!==U||t.height!==q)&&(t.width=U,t.height=q),r.clearRect(0,0,U,q),r.drawImage(s,0,0),console.log(`[JerseyDetector] Bitmap created with dims ${s.width}x${s.height}`),s.close()),j.current.push(Date.now()),j.current=j.current.filter(De=>Date.now()-De<1e3);let X={jerseyCount:((B=g.stats)==null?void 0:B.jerseyCount)||0,confidence:((se=g.stats)==null?void 0:se.confidence)||0,fps:j.current.length,processingTime:((ie=g.stats)==null?void 0:ie.processingTime)||0},ae=z.current;z.current=X;let ce=Date.now(),ue=ce-te.current>ge||X.jerseyCount!==ae.jerseyCount||X.confidence!==ae.confidence;ue&&w&&(w(X),te.current=ce),ue&&g.segmentedImages&&l&&he(g.segmentedImages,g.bboxes)}}catch(U){P.current=!0}finally{o==null||o.close(),s==null||s.close(),J.current=!1}});return ve(H,()=>({get isReady(){return N},get lastError(){return L},get stats(){return z.current},capture:(e="image/jpeg",t=.92)=>{let r=i.current,o=E.current;if(!r||r.videoWidth<=0||r.videoHeight<=0)throw new Error("Video not ready for capture");let s=document.createElement("canvas");s.width=r.videoWidth,s.height=r.videoHeight;let B=s.getContext("2d");if(!B)throw new Error("Failed to get context for capture canvas");return B.drawImage(r,0,0,s.width,s.height),o&&o.width>0&&o.height>0?B.drawImage(o,0,0,s.width,s.height):console.warn("[capture] Mask canvas not available \u2014 returning raw video frame"),s.toDataURL(e,t)}}),[N,L,_]),re(()=>{if(!_||!N||P.current)return;let e=i.current,t=E.current;if(!e||!t||e.videoWidth===0||e.videoHeight===0){let o=setTimeout(()=>{W.current=requestAnimationFrame(()=>{})},100);return()=>clearTimeout(o)}(t.width!==e.videoWidth||t.height!==e.videoHeight)&&(t.width=e.videoWidth,t.height=e.videoHeight);let r=()=>x(null,null,function*(){!_||!N||P.current||(yield Te(),W.current=requestAnimationFrame(r))});return W.current=requestAnimationFrame(r),()=>{W.current&&cancelAnimationFrame(W.current)}},[_,N]),Ce("canvas",{ref:E,className:"absolute inset-0 pointer-events-none",style:{zIndex:10,width:"100%",height:"100%"}})});fe.displayName="JerseyDetector";export{u as DEFAULT_CONFIG,fe as JerseyDetector,ee as useJerseyWorker};