@souscheflabs/ml-vision 0.1.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.
Files changed (48) hide show
  1. package/README.md +274 -0
  2. package/dist/components/DetectionOverlay.d.ts +57 -0
  3. package/dist/components/DetectionOverlay.js +133 -0
  4. package/dist/components/index.d.ts +4 -0
  5. package/dist/components/index.js +9 -0
  6. package/dist/core/CacheManager.d.ts +168 -0
  7. package/dist/core/CacheManager.js +331 -0
  8. package/dist/core/MLVisionProvider.d.ts +90 -0
  9. package/dist/core/MLVisionProvider.js +188 -0
  10. package/dist/core/ServerClient.d.ts +131 -0
  11. package/dist/core/ServerClient.js +291 -0
  12. package/dist/core/index.d.ts +6 -0
  13. package/dist/core/index.js +18 -0
  14. package/dist/hooks/classLabels.d.ts +35 -0
  15. package/dist/hooks/classLabels.js +439 -0
  16. package/dist/hooks/classLabelsCoco.d.ts +43 -0
  17. package/dist/hooks/classLabelsCoco.js +103 -0
  18. package/dist/hooks/index.d.ts +8 -0
  19. package/dist/hooks/index.js +27 -0
  20. package/dist/hooks/useMultiBarcodeScanner.d.ts +34 -0
  21. package/dist/hooks/useMultiBarcodeScanner.js +290 -0
  22. package/dist/hooks/useProductDetector.d.ts +38 -0
  23. package/dist/hooks/useProductDetector.js +679 -0
  24. package/dist/hooks/useReceiptScanner.d.ts +37 -0
  25. package/dist/hooks/useReceiptScanner.js +405 -0
  26. package/dist/hooks/useVideoScanner.d.ts +118 -0
  27. package/dist/hooks/useVideoScanner.js +383 -0
  28. package/dist/index.d.ts +58 -0
  29. package/dist/index.js +130 -0
  30. package/dist/processors/detectionProcessor.d.ts +86 -0
  31. package/dist/processors/detectionProcessor.js +124 -0
  32. package/dist/processors/index.d.ts +5 -0
  33. package/dist/processors/index.js +16 -0
  34. package/dist/processors/tfliteFrameProcessor.d.ts +90 -0
  35. package/dist/processors/tfliteFrameProcessor.js +213 -0
  36. package/dist/types/barcode.d.ts +91 -0
  37. package/dist/types/barcode.js +19 -0
  38. package/dist/types/detection.d.ts +166 -0
  39. package/dist/types/detection.js +8 -0
  40. package/dist/types/index.d.ts +126 -0
  41. package/dist/types/index.js +25 -0
  42. package/dist/types/ocr.d.ts +202 -0
  43. package/dist/types/ocr.js +8 -0
  44. package/dist/utils/imagePreprocessor.d.ts +85 -0
  45. package/dist/utils/imagePreprocessor.js +304 -0
  46. package/dist/utils/yoloProcessor.d.ts +40 -0
  47. package/dist/utils/yoloProcessor.js +154 -0
  48. package/package.json +78 -0
@@ -0,0 +1,131 @@
1
+ /**
2
+ * ServerClient - HTTP client for ML inference server communication
3
+ *
4
+ * Handles:
5
+ * - Health checks
6
+ * - Product detection requests
7
+ * - OCR requests
8
+ * - Error handling and retries
9
+ */
10
+ import type { ServerHealthResponse, ServerErrorResponse, ProductDetectionResult, ReceiptScanResult } from '../types';
11
+ /**
12
+ * Server request options
13
+ */
14
+ export interface ServerRequestOptions {
15
+ /** Request timeout in milliseconds */
16
+ timeout?: number;
17
+ /** Abort signal for cancellation */
18
+ signal?: AbortSignal;
19
+ }
20
+ /**
21
+ * Image upload data
22
+ */
23
+ export interface ImageUploadData {
24
+ /** Base64 encoded image or file URI */
25
+ image: string;
26
+ /** Image MIME type */
27
+ mimeType?: string;
28
+ /** Whether the image is base64 encoded */
29
+ isBase64?: boolean;
30
+ }
31
+ /**
32
+ * Detection request options
33
+ */
34
+ export interface DetectionRequestOptions extends ServerRequestOptions {
35
+ /** Maximum detections to return */
36
+ maxDetections?: number;
37
+ /** Minimum confidence threshold */
38
+ minConfidence?: number;
39
+ /** Specific categories to detect */
40
+ categories?: string[];
41
+ }
42
+ /**
43
+ * OCR request options
44
+ */
45
+ export interface OCRRequestOptions extends ServerRequestOptions {
46
+ /** Known store format for optimized parsing */
47
+ storeHint?: string;
48
+ /** Language hint */
49
+ languageHint?: string;
50
+ }
51
+ /**
52
+ * Custom error for server communication failures
53
+ */
54
+ export declare class ServerError extends Error {
55
+ code: string;
56
+ statusCode?: number;
57
+ details?: Record<string, unknown>;
58
+ constructor(message: string, code: string, statusCode?: number, details?: Record<string, unknown>);
59
+ static fromResponse(response: ServerErrorResponse, statusCode?: number): ServerError;
60
+ static networkError(originalError?: Error): ServerError;
61
+ static timeoutError(): ServerError;
62
+ }
63
+ /**
64
+ * Client for communicating with the ML inference server
65
+ *
66
+ * @example
67
+ * ```typescript
68
+ * const client = new ServerClient('http://192.168.1.100:8000');
69
+ *
70
+ * // Check health
71
+ * const health = await client.checkHealth();
72
+ *
73
+ * // Detect products
74
+ * const detections = await client.detectProducts({
75
+ * image: base64Image,
76
+ * isBase64: true,
77
+ * });
78
+ * ```
79
+ */
80
+ export declare class ServerClient {
81
+ private baseUrl;
82
+ private defaultTimeout;
83
+ constructor(baseUrl: string, defaultTimeout?: number);
84
+ /**
85
+ * Check server health status
86
+ */
87
+ checkHealth(options?: ServerRequestOptions): Promise<ServerHealthResponse>;
88
+ /**
89
+ * Quick connectivity check (returns boolean)
90
+ */
91
+ isAvailable(): Promise<boolean>;
92
+ /**
93
+ * Detect products in an image
94
+ */
95
+ detectProducts(imageData: ImageUploadData, options?: DetectionRequestOptions): Promise<ProductDetectionResult[]>;
96
+ /**
97
+ * Detect fresh produce with quality assessment
98
+ */
99
+ detectProduce(imageData: ImageUploadData, options?: DetectionRequestOptions): Promise<ProductDetectionResult[]>;
100
+ /**
101
+ * Detect spice jars (requires larger model on server)
102
+ */
103
+ detectSpices(imageData: ImageUploadData, options?: DetectionRequestOptions): Promise<ProductDetectionResult[]>;
104
+ /**
105
+ * Extract text and items from a receipt image
106
+ */
107
+ scanReceipt(imageData: ImageUploadData, options?: OCRRequestOptions): Promise<ReceiptScanResult>;
108
+ /**
109
+ * Read text from a product label
110
+ */
111
+ readLabel(imageData: ImageUploadData, options?: OCRRequestOptions): Promise<{
112
+ text: string;
113
+ lines: string[];
114
+ }>;
115
+ /**
116
+ * Create FormData with image
117
+ */
118
+ private createImageFormData;
119
+ /**
120
+ * Make a JSON request
121
+ */
122
+ private request;
123
+ /**
124
+ * Make a multipart form request
125
+ */
126
+ private requestWithForm;
127
+ }
128
+ /**
129
+ * Create a ServerClient instance
130
+ */
131
+ export declare function createServerClient(baseUrl: string, timeout?: number): ServerClient;
@@ -0,0 +1,291 @@
1
+ "use strict";
2
+ /**
3
+ * ServerClient - HTTP client for ML inference server communication
4
+ *
5
+ * Handles:
6
+ * - Health checks
7
+ * - Product detection requests
8
+ * - OCR requests
9
+ * - Error handling and retries
10
+ */
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.ServerClient = exports.ServerError = void 0;
13
+ exports.createServerClient = createServerClient;
14
+ // ============================================================================
15
+ // Server Error
16
+ // ============================================================================
17
+ /**
18
+ * Custom error for server communication failures
19
+ */
20
+ class ServerError extends Error {
21
+ constructor(message, code, statusCode, details) {
22
+ super(message);
23
+ this.name = 'ServerError';
24
+ this.code = code;
25
+ this.statusCode = statusCode;
26
+ this.details = details;
27
+ }
28
+ static fromResponse(response, statusCode) {
29
+ return new ServerError(response.error, response.code, statusCode, response.details);
30
+ }
31
+ static networkError(originalError) {
32
+ return new ServerError(originalError?.message || 'Network request failed', 'NETWORK_ERROR');
33
+ }
34
+ static timeoutError() {
35
+ return new ServerError('Request timed out', 'TIMEOUT_ERROR');
36
+ }
37
+ }
38
+ exports.ServerError = ServerError;
39
+ // ============================================================================
40
+ // ServerClient Class
41
+ // ============================================================================
42
+ /**
43
+ * Client for communicating with the ML inference server
44
+ *
45
+ * @example
46
+ * ```typescript
47
+ * const client = new ServerClient('http://192.168.1.100:8000');
48
+ *
49
+ * // Check health
50
+ * const health = await client.checkHealth();
51
+ *
52
+ * // Detect products
53
+ * const detections = await client.detectProducts({
54
+ * image: base64Image,
55
+ * isBase64: true,
56
+ * });
57
+ * ```
58
+ */
59
+ class ServerClient {
60
+ constructor(baseUrl, defaultTimeout = 5000) {
61
+ // Remove trailing slash
62
+ this.baseUrl = baseUrl.replace(/\/$/, '');
63
+ this.defaultTimeout = defaultTimeout;
64
+ }
65
+ // ==========================================================================
66
+ // Health Check
67
+ // ==========================================================================
68
+ /**
69
+ * Check server health status
70
+ */
71
+ async checkHealth(options) {
72
+ const response = await this.request('/health', 'GET', undefined, options);
73
+ return response;
74
+ }
75
+ /**
76
+ * Quick connectivity check (returns boolean)
77
+ */
78
+ async isAvailable() {
79
+ try {
80
+ const health = await this.checkHealth({ timeout: 2000 });
81
+ return health.status === 'healthy';
82
+ }
83
+ catch {
84
+ return false;
85
+ }
86
+ }
87
+ // ==========================================================================
88
+ // Detection Endpoints
89
+ // ==========================================================================
90
+ /**
91
+ * Detect products in an image
92
+ */
93
+ async detectProducts(imageData, options) {
94
+ console.log('[ServerClient] detectProducts called');
95
+ console.log('[ServerClient] baseUrl:', this.baseUrl);
96
+ console.log('[ServerClient] imageData.image:', imageData.image?.substring(0, 100));
97
+ console.log('[ServerClient] imageData.isBase64:', imageData.isBase64);
98
+ const formData = this.createImageFormData(imageData);
99
+ if (options?.maxDetections) {
100
+ formData.append('max_detections', String(options.maxDetections));
101
+ }
102
+ if (options?.minConfidence) {
103
+ formData.append('min_confidence', String(options.minConfidence));
104
+ }
105
+ if (options?.categories) {
106
+ formData.append('categories', options.categories.join(','));
107
+ }
108
+ console.log('[ServerClient] Sending request to /api/v1/detect/products...');
109
+ try {
110
+ const response = await this.requestWithForm('/api/v1/detect/products', formData, options);
111
+ console.log('[ServerClient] Response received:', JSON.stringify(response));
112
+ return response.detections;
113
+ }
114
+ catch (err) {
115
+ console.error('[ServerClient] Request failed:', err);
116
+ throw err;
117
+ }
118
+ }
119
+ /**
120
+ * Detect fresh produce with quality assessment
121
+ */
122
+ async detectProduce(imageData, options) {
123
+ const formData = this.createImageFormData(imageData);
124
+ const response = await this.requestWithForm('/api/v1/detect/produce', formData, options);
125
+ return response.detections;
126
+ }
127
+ /**
128
+ * Detect spice jars (requires larger model on server)
129
+ */
130
+ async detectSpices(imageData, options) {
131
+ const formData = this.createImageFormData(imageData);
132
+ const response = await this.requestWithForm('/api/v1/detect/spices', formData, options);
133
+ return response.detections;
134
+ }
135
+ // ==========================================================================
136
+ // OCR Endpoints
137
+ // ==========================================================================
138
+ /**
139
+ * Extract text and items from a receipt image
140
+ */
141
+ async scanReceipt(imageData, options) {
142
+ const formData = this.createImageFormData(imageData);
143
+ if (options?.storeHint) {
144
+ formData.append('store_hint', options.storeHint);
145
+ }
146
+ if (options?.languageHint) {
147
+ formData.append('language_hint', options.languageHint);
148
+ }
149
+ const response = await this.requestWithForm('/api/v1/ocr/receipt', formData, options);
150
+ return response;
151
+ }
152
+ /**
153
+ * Read text from a product label
154
+ */
155
+ async readLabel(imageData, options) {
156
+ const formData = this.createImageFormData(imageData);
157
+ const response = await this.requestWithForm('/api/v1/ocr/label', formData, options);
158
+ return response;
159
+ }
160
+ // ==========================================================================
161
+ // Private Methods
162
+ // ==========================================================================
163
+ /**
164
+ * Create FormData with image
165
+ */
166
+ createImageFormData(imageData) {
167
+ const formData = new FormData();
168
+ if (imageData.isBase64) {
169
+ // For base64, send as a field
170
+ formData.append('image_base64', imageData.image);
171
+ if (imageData.mimeType) {
172
+ formData.append('mime_type', imageData.mimeType);
173
+ }
174
+ }
175
+ else {
176
+ // For file URI, create a file object
177
+ // React Native's FormData handles file:// URIs
178
+ const uri = imageData.image;
179
+ const filename = uri.split('/').pop() || 'image.jpg';
180
+ const type = imageData.mimeType || 'image/jpeg';
181
+ formData.append('image', {
182
+ uri,
183
+ name: filename,
184
+ type,
185
+ });
186
+ }
187
+ return formData;
188
+ }
189
+ /**
190
+ * Make a JSON request
191
+ */
192
+ async request(endpoint, method, body, options) {
193
+ const url = `${this.baseUrl}${endpoint}`;
194
+ const timeout = options?.timeout ?? this.defaultTimeout;
195
+ // Create abort controller for timeout
196
+ const controller = new AbortController();
197
+ const timeoutId = setTimeout(() => controller.abort(), timeout);
198
+ // Combine with external signal if provided
199
+ if (options?.signal) {
200
+ options.signal.addEventListener('abort', () => controller.abort());
201
+ }
202
+ try {
203
+ const response = await fetch(url, {
204
+ method,
205
+ headers: {
206
+ 'Content-Type': 'application/json',
207
+ Accept: 'application/json',
208
+ },
209
+ body: body ? JSON.stringify(body) : undefined,
210
+ signal: controller.signal,
211
+ });
212
+ clearTimeout(timeoutId);
213
+ if (!response.ok) {
214
+ const errorBody = await response.json().catch(() => ({
215
+ error: 'Unknown error',
216
+ code: 'UNKNOWN_ERROR',
217
+ }));
218
+ throw ServerError.fromResponse(errorBody, response.status);
219
+ }
220
+ return (await response.json());
221
+ }
222
+ catch (error) {
223
+ clearTimeout(timeoutId);
224
+ if (error instanceof ServerError) {
225
+ throw error;
226
+ }
227
+ if (error instanceof Error) {
228
+ if (error.name === 'AbortError') {
229
+ throw ServerError.timeoutError();
230
+ }
231
+ throw ServerError.networkError(error);
232
+ }
233
+ throw ServerError.networkError();
234
+ }
235
+ }
236
+ /**
237
+ * Make a multipart form request
238
+ */
239
+ async requestWithForm(endpoint, formData, options) {
240
+ const url = `${this.baseUrl}${endpoint}`;
241
+ const timeout = options?.timeout ?? this.defaultTimeout;
242
+ const controller = new AbortController();
243
+ const timeoutId = setTimeout(() => controller.abort(), timeout);
244
+ if (options?.signal) {
245
+ options.signal.addEventListener('abort', () => controller.abort());
246
+ }
247
+ try {
248
+ const response = await fetch(url, {
249
+ method: 'POST',
250
+ headers: {
251
+ Accept: 'application/json',
252
+ // Don't set Content-Type for FormData - fetch will set it with boundary
253
+ },
254
+ body: formData,
255
+ signal: controller.signal,
256
+ });
257
+ clearTimeout(timeoutId);
258
+ if (!response.ok) {
259
+ const errorBody = await response.json().catch(() => ({
260
+ error: 'Unknown error',
261
+ code: 'UNKNOWN_ERROR',
262
+ }));
263
+ throw ServerError.fromResponse(errorBody, response.status);
264
+ }
265
+ return (await response.json());
266
+ }
267
+ catch (error) {
268
+ clearTimeout(timeoutId);
269
+ if (error instanceof ServerError) {
270
+ throw error;
271
+ }
272
+ if (error instanceof Error) {
273
+ if (error.name === 'AbortError') {
274
+ throw ServerError.timeoutError();
275
+ }
276
+ throw ServerError.networkError(error);
277
+ }
278
+ throw ServerError.networkError();
279
+ }
280
+ }
281
+ }
282
+ exports.ServerClient = ServerClient;
283
+ // ============================================================================
284
+ // Factory Function
285
+ // ============================================================================
286
+ /**
287
+ * Create a ServerClient instance
288
+ */
289
+ function createServerClient(baseUrl, timeout) {
290
+ return new ServerClient(baseUrl, timeout);
291
+ }
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Core module exports
3
+ */
4
+ export { MLVisionProvider, useMLVisionContext, useMLVisionReady, type MLVisionProviderProps, type MLVisionContextValue, } from './MLVisionProvider';
5
+ export { CacheManager, createCacheManager, CACHE_TTL, type CachedBarcodeLookup, type CachedProductRecognition, } from './CacheManager';
6
+ export { ServerClient, createServerClient, ServerError, type ServerRequestOptions, type ImageUploadData, type DetectionRequestOptions, type OCRRequestOptions, } from './ServerClient';
@@ -0,0 +1,18 @@
1
+ "use strict";
2
+ /**
3
+ * Core module exports
4
+ */
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.ServerError = exports.createServerClient = exports.ServerClient = exports.CACHE_TTL = exports.createCacheManager = exports.CacheManager = exports.useMLVisionReady = exports.useMLVisionContext = exports.MLVisionProvider = void 0;
7
+ var MLVisionProvider_1 = require("./MLVisionProvider");
8
+ Object.defineProperty(exports, "MLVisionProvider", { enumerable: true, get: function () { return MLVisionProvider_1.MLVisionProvider; } });
9
+ Object.defineProperty(exports, "useMLVisionContext", { enumerable: true, get: function () { return MLVisionProvider_1.useMLVisionContext; } });
10
+ Object.defineProperty(exports, "useMLVisionReady", { enumerable: true, get: function () { return MLVisionProvider_1.useMLVisionReady; } });
11
+ var CacheManager_1 = require("./CacheManager");
12
+ Object.defineProperty(exports, "CacheManager", { enumerable: true, get: function () { return CacheManager_1.CacheManager; } });
13
+ Object.defineProperty(exports, "createCacheManager", { enumerable: true, get: function () { return CacheManager_1.createCacheManager; } });
14
+ Object.defineProperty(exports, "CACHE_TTL", { enumerable: true, get: function () { return CacheManager_1.CACHE_TTL; } });
15
+ var ServerClient_1 = require("./ServerClient");
16
+ Object.defineProperty(exports, "ServerClient", { enumerable: true, get: function () { return ServerClient_1.ServerClient; } });
17
+ Object.defineProperty(exports, "createServerClient", { enumerable: true, get: function () { return ServerClient_1.createServerClient; } });
18
+ Object.defineProperty(exports, "ServerError", { enumerable: true, get: function () { return ServerClient_1.ServerError; } });
@@ -0,0 +1,35 @@
1
+ /**
2
+ * Class label mappings for the trained SousChef YOLOv8 model
3
+ *
4
+ * These labels correspond to the 233 classes in the souschef_coco_finetuned_v1 model.
5
+ * The model was fine-tuned on a dataset of food and kitchen items.
6
+ */
7
+ import type { ProductCategory, ClassLabelMap } from '../types';
8
+ /**
9
+ * Categorize a food item label
10
+ */
11
+ declare function categorizeLabel(label: string): ProductCategory;
12
+ /**
13
+ * Map class index to label and category (233 classes)
14
+ */
15
+ export declare const CLASS_LABELS: ClassLabelMap;
16
+ /**
17
+ * Number of classes in the model
18
+ */
19
+ export declare const NUM_CLASSES = 233;
20
+ /**
21
+ * Get label info for a class index
22
+ */
23
+ export declare function getClassInfo(classIndex: number): {
24
+ label: string;
25
+ category: ProductCategory;
26
+ };
27
+ /**
28
+ * Get all labels as an array (ordered by class index)
29
+ */
30
+ export declare function getLabelsArray(): string[];
31
+ /**
32
+ * Category to class indices mapping
33
+ */
34
+ export declare const CATEGORY_CLASSES: Record<ProductCategory, number[]>;
35
+ export { categorizeLabel };