@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.
- package/README.md +274 -0
- package/dist/components/DetectionOverlay.d.ts +57 -0
- package/dist/components/DetectionOverlay.js +133 -0
- package/dist/components/index.d.ts +4 -0
- package/dist/components/index.js +9 -0
- package/dist/core/CacheManager.d.ts +168 -0
- package/dist/core/CacheManager.js +331 -0
- package/dist/core/MLVisionProvider.d.ts +90 -0
- package/dist/core/MLVisionProvider.js +188 -0
- package/dist/core/ServerClient.d.ts +131 -0
- package/dist/core/ServerClient.js +291 -0
- package/dist/core/index.d.ts +6 -0
- package/dist/core/index.js +18 -0
- package/dist/hooks/classLabels.d.ts +35 -0
- package/dist/hooks/classLabels.js +439 -0
- package/dist/hooks/classLabelsCoco.d.ts +43 -0
- package/dist/hooks/classLabelsCoco.js +103 -0
- package/dist/hooks/index.d.ts +8 -0
- package/dist/hooks/index.js +27 -0
- package/dist/hooks/useMultiBarcodeScanner.d.ts +34 -0
- package/dist/hooks/useMultiBarcodeScanner.js +290 -0
- package/dist/hooks/useProductDetector.d.ts +38 -0
- package/dist/hooks/useProductDetector.js +679 -0
- package/dist/hooks/useReceiptScanner.d.ts +37 -0
- package/dist/hooks/useReceiptScanner.js +405 -0
- package/dist/hooks/useVideoScanner.d.ts +118 -0
- package/dist/hooks/useVideoScanner.js +383 -0
- package/dist/index.d.ts +58 -0
- package/dist/index.js +130 -0
- package/dist/processors/detectionProcessor.d.ts +86 -0
- package/dist/processors/detectionProcessor.js +124 -0
- package/dist/processors/index.d.ts +5 -0
- package/dist/processors/index.js +16 -0
- package/dist/processors/tfliteFrameProcessor.d.ts +90 -0
- package/dist/processors/tfliteFrameProcessor.js +213 -0
- package/dist/types/barcode.d.ts +91 -0
- package/dist/types/barcode.js +19 -0
- package/dist/types/detection.d.ts +166 -0
- package/dist/types/detection.js +8 -0
- package/dist/types/index.d.ts +126 -0
- package/dist/types/index.js +25 -0
- package/dist/types/ocr.d.ts +202 -0
- package/dist/types/ocr.js +8 -0
- package/dist/utils/imagePreprocessor.d.ts +85 -0
- package/dist/utils/imagePreprocessor.js +304 -0
- package/dist/utils/yoloProcessor.d.ts +40 -0
- package/dist/utils/yoloProcessor.js +154 -0
- package/package.json +78 -0
package/README.md
ADDED
|
@@ -0,0 +1,274 @@
|
|
|
1
|
+
# @souschef/ml-vision
|
|
2
|
+
|
|
3
|
+
ML-powered product detection for React Native. Speed up kitchen inventory onboarding with multi-barcode scanning, receipt OCR, and visual product recognition.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- **Multi-Barcode Scanning** - Scan multiple barcodes from a single photo
|
|
8
|
+
- **Receipt OCR** - Photograph grocery receipts to auto-extract items
|
|
9
|
+
- **Visual Product Recognition** - Recognize fridge/pantry contents by appearance
|
|
10
|
+
- **Video Scanning** - Real-time product detection from camera feed
|
|
11
|
+
- **On-Device ML** - Fast inference using TFLite with GPU acceleration
|
|
12
|
+
- **Server Fallback** - Optional server-side processing for better accuracy
|
|
13
|
+
|
|
14
|
+
## Installation
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
npm install @souschef/ml-vision
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
### Peer Dependencies
|
|
21
|
+
|
|
22
|
+
This package requires the following peer dependencies:
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
npm install react-native-vision-camera react-native-mmkv
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
For on-device ML inference (Phase 3+):
|
|
29
|
+
```bash
|
|
30
|
+
npm install react-native-fast-tflite
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
### iOS Setup
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
cd ios && pod install
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
Add camera permissions to `Info.plist`:
|
|
40
|
+
```xml
|
|
41
|
+
<key>NSCameraUsageDescription</key>
|
|
42
|
+
<string>Camera is used to scan products and receipts</string>
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
### Android Setup
|
|
46
|
+
|
|
47
|
+
Add camera permission to `AndroidManifest.xml`:
|
|
48
|
+
```xml
|
|
49
|
+
<uses-permission android:name="android.permission.CAMERA" />
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
## Quick Start
|
|
53
|
+
|
|
54
|
+
### 1. Wrap your app with the provider
|
|
55
|
+
|
|
56
|
+
```typescript
|
|
57
|
+
import { MLVisionProvider } from '@souschef/ml-vision';
|
|
58
|
+
import { MMKV } from 'react-native-mmkv';
|
|
59
|
+
|
|
60
|
+
// Create MMKV instance for caching
|
|
61
|
+
const storage = new MMKV({ id: 'ml-vision-cache' });
|
|
62
|
+
|
|
63
|
+
function App() {
|
|
64
|
+
return (
|
|
65
|
+
<MLVisionProvider
|
|
66
|
+
config={{
|
|
67
|
+
serverUrl: 'http://192.168.1.100:8000', // Optional: your ML server
|
|
68
|
+
cacheEnabled: true,
|
|
69
|
+
}}
|
|
70
|
+
storage={storage}
|
|
71
|
+
>
|
|
72
|
+
<YourApp />
|
|
73
|
+
</MLVisionProvider>
|
|
74
|
+
);
|
|
75
|
+
}
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
### 2. Use the hooks in your components
|
|
79
|
+
|
|
80
|
+
```typescript
|
|
81
|
+
import { useMultiBarcodeScanner } from '@souschef/ml-vision';
|
|
82
|
+
import { Camera } from 'react-native-vision-camera';
|
|
83
|
+
|
|
84
|
+
function BarcodeScanScreen() {
|
|
85
|
+
const {
|
|
86
|
+
results,
|
|
87
|
+
isScanning,
|
|
88
|
+
startScanning,
|
|
89
|
+
stopScanning,
|
|
90
|
+
frameProcessor,
|
|
91
|
+
} = useMultiBarcodeScanner({
|
|
92
|
+
formats: ['ean-13', 'upc-a', 'qr'],
|
|
93
|
+
maxBarcodes: 20,
|
|
94
|
+
onDetected: (barcodes) => {
|
|
95
|
+
console.log('Found barcodes:', barcodes);
|
|
96
|
+
},
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
return (
|
|
100
|
+
<Camera
|
|
101
|
+
device={device}
|
|
102
|
+
isActive={isScanning}
|
|
103
|
+
frameProcessor={frameProcessor}
|
|
104
|
+
/>
|
|
105
|
+
);
|
|
106
|
+
}
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
## API Reference
|
|
110
|
+
|
|
111
|
+
### MLVisionProvider
|
|
112
|
+
|
|
113
|
+
The context provider that must wrap your app.
|
|
114
|
+
|
|
115
|
+
```typescript
|
|
116
|
+
<MLVisionProvider
|
|
117
|
+
config={{
|
|
118
|
+
serverUrl?: string; // ML server URL for fallback
|
|
119
|
+
serverTimeout?: number; // Request timeout (default: 5000ms)
|
|
120
|
+
cacheEnabled?: boolean; // Enable result caching (default: true)
|
|
121
|
+
cacheTTL?: number; // Cache TTL in ms (default: 24 hours)
|
|
122
|
+
enableGPUDelegate?: boolean; // Use GPU for inference (default: true)
|
|
123
|
+
}}
|
|
124
|
+
storage={mmkvInstance} // MMKV instance for caching
|
|
125
|
+
onInitialized={() => {}} // Called when ready
|
|
126
|
+
onError={(error) => {}} // Called on init error
|
|
127
|
+
>
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
### useMultiBarcodeScanner
|
|
131
|
+
|
|
132
|
+
Scan multiple barcodes from camera frames or photos.
|
|
133
|
+
|
|
134
|
+
```typescript
|
|
135
|
+
const {
|
|
136
|
+
// State
|
|
137
|
+
isReady: boolean;
|
|
138
|
+
isScanning: boolean;
|
|
139
|
+
results: BarcodeDetectionResult[];
|
|
140
|
+
error: Error | null;
|
|
141
|
+
|
|
142
|
+
// Actions
|
|
143
|
+
startScanning: () => void;
|
|
144
|
+
stopScanning: () => void;
|
|
145
|
+
scanPhoto: (uri: string) => Promise<BarcodeDetectionResult[]>;
|
|
146
|
+
clearResults: () => void;
|
|
147
|
+
|
|
148
|
+
// For VisionCamera
|
|
149
|
+
frameProcessor: FrameProcessor;
|
|
150
|
+
} = useMultiBarcodeScanner(options);
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
### useProductDetector
|
|
154
|
+
|
|
155
|
+
Detect products in images using trained ML models.
|
|
156
|
+
|
|
157
|
+
```typescript
|
|
158
|
+
const {
|
|
159
|
+
// State
|
|
160
|
+
isModelLoaded: boolean;
|
|
161
|
+
isDetecting: boolean;
|
|
162
|
+
detections: ProductDetectionResult[];
|
|
163
|
+
modelVersion: string;
|
|
164
|
+
|
|
165
|
+
// Actions
|
|
166
|
+
detectProducts: (uri: string) => Promise<ProductDetectionResult[]>;
|
|
167
|
+
updateModel: () => Promise<void>;
|
|
168
|
+
|
|
169
|
+
// For VisionCamera
|
|
170
|
+
frameProcessor: FrameProcessor;
|
|
171
|
+
} = useProductDetector({
|
|
172
|
+
model: 'fast' | 'accurate';
|
|
173
|
+
minConfidence?: number;
|
|
174
|
+
serverFallback?: boolean;
|
|
175
|
+
});
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
### useReceiptScanner
|
|
179
|
+
|
|
180
|
+
Extract items from receipt photos.
|
|
181
|
+
|
|
182
|
+
```typescript
|
|
183
|
+
const {
|
|
184
|
+
// State
|
|
185
|
+
isProcessing: boolean;
|
|
186
|
+
items: ReceiptItem[];
|
|
187
|
+
confidence: number;
|
|
188
|
+
|
|
189
|
+
// Actions
|
|
190
|
+
scanReceipt: (uri: string) => Promise<ReceiptScanResult>;
|
|
191
|
+
confirmItem: (item: ReceiptItem) => void;
|
|
192
|
+
rejectItem: (item: ReceiptItem) => void;
|
|
193
|
+
} = useReceiptScanner({
|
|
194
|
+
serverFallback?: boolean;
|
|
195
|
+
minConfidence?: number;
|
|
196
|
+
});
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
## Architecture
|
|
200
|
+
|
|
201
|
+
```
|
|
202
|
+
┌─────────────────────────────────────────────────────────────────┐
|
|
203
|
+
│ Your React Native App │
|
|
204
|
+
├─────────────────────────────────────────────────────────────────┤
|
|
205
|
+
│ @souschef/ml-vision │
|
|
206
|
+
│ ├── Hooks (useMultiBarcodeScanner, useProductDetector, etc.) │
|
|
207
|
+
│ ├── Frame Processors (barcode, OCR, TFLite detection) │
|
|
208
|
+
│ └── CacheManager (MMKV-based caching) │
|
|
209
|
+
├─────────────────────────────────────────────────────────────────┤
|
|
210
|
+
│ On-Device Processing (fast, offline) │
|
|
211
|
+
│ ├── MLKit (barcodes, text recognition) │
|
|
212
|
+
│ ├── react-native-fast-tflite (custom models) │
|
|
213
|
+
│ └── CoreML/NNAPI delegates (GPU acceleration) │
|
|
214
|
+
├─────────────────────────────────────────────────────────────────┤
|
|
215
|
+
│ Server Fallback (accurate, requires network) │
|
|
216
|
+
│ ├── FastAPI inference server │
|
|
217
|
+
│ ├── YOLOv8 full models │
|
|
218
|
+
│ └── PaddleOCR for complex receipts │
|
|
219
|
+
└─────────────────────────────────────────────────────────────────┘
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
## Training Your Own Models
|
|
223
|
+
|
|
224
|
+
See the [Training Guide](../../docker/README.md) for instructions on:
|
|
225
|
+
- Setting up the Docker training environment
|
|
226
|
+
- Preparing your dataset
|
|
227
|
+
- Training YOLOv8 models
|
|
228
|
+
- Exporting to TFLite for mobile
|
|
229
|
+
|
|
230
|
+
## Project Structure
|
|
231
|
+
|
|
232
|
+
```
|
|
233
|
+
packages/ml-vision/
|
|
234
|
+
├── src/
|
|
235
|
+
│ ├── index.ts # Main exports
|
|
236
|
+
│ ├── types/ # TypeScript definitions
|
|
237
|
+
│ ├── hooks/ # React hooks
|
|
238
|
+
│ ├── core/ # Provider, cache, server client
|
|
239
|
+
│ ├── processors/ # Frame processors
|
|
240
|
+
│ └── utils/ # Utilities
|
|
241
|
+
├── models/ # Bundled TFLite models
|
|
242
|
+
├── docs/ # Documentation
|
|
243
|
+
└── dist/ # Compiled output
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
## Development
|
|
247
|
+
|
|
248
|
+
```bash
|
|
249
|
+
# Build the package
|
|
250
|
+
npm run build
|
|
251
|
+
|
|
252
|
+
# Watch for changes
|
|
253
|
+
npm run watch
|
|
254
|
+
|
|
255
|
+
# Type check
|
|
256
|
+
npm run typecheck
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
## License
|
|
260
|
+
|
|
261
|
+
ISC
|
|
262
|
+
|
|
263
|
+
## Contributing
|
|
264
|
+
|
|
265
|
+
1. Fork the repository
|
|
266
|
+
2. Create your feature branch
|
|
267
|
+
3. Make your changes
|
|
268
|
+
4. Run tests and type checks
|
|
269
|
+
5. Submit a pull request
|
|
270
|
+
|
|
271
|
+
## Support
|
|
272
|
+
|
|
273
|
+
- [GitHub Issues](https://github.com/SousChefLabs/ml-vision/issues)
|
|
274
|
+
- [Documentation](./docs/)
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DetectionOverlay Component
|
|
3
|
+
*
|
|
4
|
+
* Renders bounding boxes over detected products.
|
|
5
|
+
* Overlay this on top of your camera view or image.
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* ```typescript
|
|
9
|
+
* import { DetectionOverlay } from '@souschef/ml-vision';
|
|
10
|
+
*
|
|
11
|
+
* function CameraScreen() {
|
|
12
|
+
* const { detections } = useProductDetector();
|
|
13
|
+
*
|
|
14
|
+
* return (
|
|
15
|
+
* <View style={styles.container}>
|
|
16
|
+
* <Camera style={StyleSheet.absoluteFill} />
|
|
17
|
+
* <DetectionOverlay
|
|
18
|
+
* detections={detections}
|
|
19
|
+
* imageWidth={screenWidth}
|
|
20
|
+
* imageHeight={screenHeight}
|
|
21
|
+
* />
|
|
22
|
+
* </View>
|
|
23
|
+
* );
|
|
24
|
+
* }
|
|
25
|
+
* ```
|
|
26
|
+
*/
|
|
27
|
+
import React from 'react';
|
|
28
|
+
import { type ViewStyle, type TextStyle } from 'react-native';
|
|
29
|
+
import type { ProductDetectionResult, ProductCategory } from '../types';
|
|
30
|
+
export interface DetectionOverlayProps {
|
|
31
|
+
/** Array of detection results to display */
|
|
32
|
+
detections: ProductDetectionResult[];
|
|
33
|
+
/** Width of the image/camera view */
|
|
34
|
+
imageWidth: number;
|
|
35
|
+
/** Height of the image/camera view */
|
|
36
|
+
imageHeight: number;
|
|
37
|
+
/** Show confidence score (default: true) */
|
|
38
|
+
showConfidence?: boolean;
|
|
39
|
+
/** Show class label (default: true) */
|
|
40
|
+
showLabel?: boolean;
|
|
41
|
+
/** Minimum confidence to display (default: 0) */
|
|
42
|
+
minConfidence?: number;
|
|
43
|
+
/** Custom colors per category */
|
|
44
|
+
categoryColors?: Partial<Record<ProductCategory, string>>;
|
|
45
|
+
/** Custom box style override */
|
|
46
|
+
boxStyle?: ViewStyle;
|
|
47
|
+
/** Custom label style override */
|
|
48
|
+
labelStyle?: TextStyle;
|
|
49
|
+
/** Callback when a detection is tapped */
|
|
50
|
+
onDetectionPress?: (detection: ProductDetectionResult) => void;
|
|
51
|
+
}
|
|
52
|
+
export declare function DetectionOverlay({ detections, imageWidth: _imageWidth, imageHeight: _imageHeight, showConfidence, showLabel, minConfidence, categoryColors, boxStyle, labelStyle, onDetectionPress, }: DetectionOverlayProps): React.ReactElement;
|
|
53
|
+
/**
|
|
54
|
+
* Simple loading indicator for when model is loading
|
|
55
|
+
*/
|
|
56
|
+
export declare function DetectionLoadingOverlay(): React.ReactElement;
|
|
57
|
+
export default DetectionOverlay;
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.DetectionOverlay = DetectionOverlay;
|
|
4
|
+
exports.DetectionLoadingOverlay = DetectionLoadingOverlay;
|
|
5
|
+
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
6
|
+
/**
|
|
7
|
+
* DetectionOverlay Component
|
|
8
|
+
*
|
|
9
|
+
* Renders bounding boxes over detected products.
|
|
10
|
+
* Overlay this on top of your camera view or image.
|
|
11
|
+
*
|
|
12
|
+
* @example
|
|
13
|
+
* ```typescript
|
|
14
|
+
* import { DetectionOverlay } from '@souschef/ml-vision';
|
|
15
|
+
*
|
|
16
|
+
* function CameraScreen() {
|
|
17
|
+
* const { detections } = useProductDetector();
|
|
18
|
+
*
|
|
19
|
+
* return (
|
|
20
|
+
* <View style={styles.container}>
|
|
21
|
+
* <Camera style={StyleSheet.absoluteFill} />
|
|
22
|
+
* <DetectionOverlay
|
|
23
|
+
* detections={detections}
|
|
24
|
+
* imageWidth={screenWidth}
|
|
25
|
+
* imageHeight={screenHeight}
|
|
26
|
+
* />
|
|
27
|
+
* </View>
|
|
28
|
+
* );
|
|
29
|
+
* }
|
|
30
|
+
* ```
|
|
31
|
+
*/
|
|
32
|
+
const react_1 = require("react");
|
|
33
|
+
const react_native_1 = require("react-native");
|
|
34
|
+
// ============================================================================
|
|
35
|
+
// Default Colors
|
|
36
|
+
// ============================================================================
|
|
37
|
+
const DEFAULT_CATEGORY_COLORS = {
|
|
38
|
+
produce: '#4CAF50', // Green
|
|
39
|
+
meat: '#F44336', // Red
|
|
40
|
+
dairy: '#2196F3', // Blue
|
|
41
|
+
packaged: '#FF9800', // Orange
|
|
42
|
+
beverage: '#9C27B0', // Purple
|
|
43
|
+
condiment: '#FFEB3B', // Yellow
|
|
44
|
+
spice: '#795548', // Brown
|
|
45
|
+
bakery: '#FFC107', // Amber
|
|
46
|
+
frozen: '#00BCD4', // Cyan
|
|
47
|
+
snack: '#E91E63', // Pink
|
|
48
|
+
unknown: '#9E9E9E', // Gray
|
|
49
|
+
};
|
|
50
|
+
// ============================================================================
|
|
51
|
+
// Component
|
|
52
|
+
// ============================================================================
|
|
53
|
+
function DetectionOverlay({ detections, imageWidth: _imageWidth, imageHeight: _imageHeight, showConfidence = true, showLabel = true, minConfidence = 0, categoryColors = {}, boxStyle, labelStyle, onDetectionPress, }) {
|
|
54
|
+
// Merge custom colors with defaults
|
|
55
|
+
const colors = (0, react_1.useMemo)(() => ({ ...DEFAULT_CATEGORY_COLORS, ...categoryColors }), [categoryColors]);
|
|
56
|
+
// Filter detections by confidence
|
|
57
|
+
const visibleDetections = (0, react_1.useMemo)(() => detections.filter((d) => d.confidence >= minConfidence), [detections, minConfidence]);
|
|
58
|
+
return ((0, jsx_runtime_1.jsx)(react_native_1.View, { style: styles.container, pointerEvents: "box-none", children: visibleDetections.map((detection) => {
|
|
59
|
+
if (!detection.boundingBox)
|
|
60
|
+
return null;
|
|
61
|
+
const { x, y, width, height } = detection.boundingBox;
|
|
62
|
+
const color = colors[detection.data.category];
|
|
63
|
+
const label = detection.data.name || detection.data.classLabel;
|
|
64
|
+
const confidence = Math.round(detection.confidence * 100);
|
|
65
|
+
return ((0, jsx_runtime_1.jsx)(react_native_1.View, { style: [
|
|
66
|
+
styles.box,
|
|
67
|
+
{
|
|
68
|
+
left: x,
|
|
69
|
+
top: y,
|
|
70
|
+
width: width,
|
|
71
|
+
height: height,
|
|
72
|
+
borderColor: color,
|
|
73
|
+
},
|
|
74
|
+
boxStyle,
|
|
75
|
+
], onTouchEnd: () => onDetectionPress?.(detection), children: (showLabel || showConfidence) && ((0, jsx_runtime_1.jsx)(react_native_1.View, { style: [styles.labelContainer, { backgroundColor: color }], children: (0, jsx_runtime_1.jsxs)(react_native_1.Text, { style: [styles.labelText, labelStyle], numberOfLines: 1, ellipsizeMode: "tail", children: [showLabel && label, showLabel && showConfidence && ' ', showConfidence && `${confidence}%`] }) })) }, detection.id));
|
|
76
|
+
}) }));
|
|
77
|
+
}
|
|
78
|
+
// ============================================================================
|
|
79
|
+
// Styles
|
|
80
|
+
// ============================================================================
|
|
81
|
+
const styles = react_native_1.StyleSheet.create({
|
|
82
|
+
container: {
|
|
83
|
+
...react_native_1.StyleSheet.absoluteFillObject,
|
|
84
|
+
},
|
|
85
|
+
box: {
|
|
86
|
+
position: 'absolute',
|
|
87
|
+
borderWidth: 2,
|
|
88
|
+
borderRadius: 4,
|
|
89
|
+
},
|
|
90
|
+
labelContainer: {
|
|
91
|
+
position: 'absolute',
|
|
92
|
+
top: -24,
|
|
93
|
+
left: -2,
|
|
94
|
+
paddingHorizontal: 6,
|
|
95
|
+
paddingVertical: 2,
|
|
96
|
+
borderRadius: 4,
|
|
97
|
+
minWidth: 40,
|
|
98
|
+
},
|
|
99
|
+
labelText: {
|
|
100
|
+
color: '#FFFFFF',
|
|
101
|
+
fontSize: 12,
|
|
102
|
+
fontWeight: '600',
|
|
103
|
+
},
|
|
104
|
+
});
|
|
105
|
+
// ============================================================================
|
|
106
|
+
// Utility Components
|
|
107
|
+
// ============================================================================
|
|
108
|
+
/**
|
|
109
|
+
* Simple loading indicator for when model is loading
|
|
110
|
+
*/
|
|
111
|
+
function DetectionLoadingOverlay() {
|
|
112
|
+
return ((0, jsx_runtime_1.jsx)(react_native_1.View, { style: loadingStyles.container, children: (0, jsx_runtime_1.jsx)(react_native_1.View, { style: loadingStyles.box, children: (0, jsx_runtime_1.jsx)(react_native_1.Text, { style: loadingStyles.text, children: "Loading model..." }) }) }));
|
|
113
|
+
}
|
|
114
|
+
const loadingStyles = react_native_1.StyleSheet.create({
|
|
115
|
+
container: {
|
|
116
|
+
...react_native_1.StyleSheet.absoluteFillObject,
|
|
117
|
+
justifyContent: 'center',
|
|
118
|
+
alignItems: 'center',
|
|
119
|
+
backgroundColor: 'rgba(0, 0, 0, 0.3)',
|
|
120
|
+
},
|
|
121
|
+
box: {
|
|
122
|
+
backgroundColor: 'rgba(0, 0, 0, 0.7)',
|
|
123
|
+
paddingHorizontal: 20,
|
|
124
|
+
paddingVertical: 12,
|
|
125
|
+
borderRadius: 8,
|
|
126
|
+
},
|
|
127
|
+
text: {
|
|
128
|
+
color: '#FFFFFF',
|
|
129
|
+
fontSize: 16,
|
|
130
|
+
fontWeight: '500',
|
|
131
|
+
},
|
|
132
|
+
});
|
|
133
|
+
exports.default = DetectionOverlay;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Components exports
|
|
4
|
+
*/
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.DetectionLoadingOverlay = exports.DetectionOverlay = void 0;
|
|
7
|
+
var DetectionOverlay_1 = require("./DetectionOverlay");
|
|
8
|
+
Object.defineProperty(exports, "DetectionOverlay", { enumerable: true, get: function () { return DetectionOverlay_1.DetectionOverlay; } });
|
|
9
|
+
Object.defineProperty(exports, "DetectionLoadingOverlay", { enumerable: true, get: function () { return DetectionOverlay_1.DetectionLoadingOverlay; } });
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CacheManager - MMKV-based caching for ML Vision results
|
|
3
|
+
*
|
|
4
|
+
* Provides fast, synchronous caching for:
|
|
5
|
+
* - Barcode to product lookups
|
|
6
|
+
* - Product recognition results
|
|
7
|
+
* - Receipt parsing results
|
|
8
|
+
* - Model metadata
|
|
9
|
+
*
|
|
10
|
+
* Uses react-native-mmkv for ~30x faster access than AsyncStorage.
|
|
11
|
+
*/
|
|
12
|
+
import type { MMKV } from 'react-native-mmkv';
|
|
13
|
+
import type { CacheStats, BarcodeFormat } from '../types';
|
|
14
|
+
/**
|
|
15
|
+
* Default TTL values (in milliseconds)
|
|
16
|
+
*/
|
|
17
|
+
export declare const CACHE_TTL: {
|
|
18
|
+
/** Barcode lookups - 7 days */
|
|
19
|
+
readonly BARCODE: number;
|
|
20
|
+
/** Product recognition - 24 hours */
|
|
21
|
+
readonly PRODUCT: number;
|
|
22
|
+
/** Receipt parsing - 1 hour */
|
|
23
|
+
readonly RECEIPT: number;
|
|
24
|
+
/** Model metadata - until manually cleared */
|
|
25
|
+
readonly MODEL: number;
|
|
26
|
+
/** Server responses - 5 minutes */
|
|
27
|
+
readonly SERVER: number;
|
|
28
|
+
};
|
|
29
|
+
/**
|
|
30
|
+
* Cache key prefixes for namespacing
|
|
31
|
+
*/
|
|
32
|
+
declare const CACHE_PREFIX: {
|
|
33
|
+
readonly BARCODE: "mlv_barcode_";
|
|
34
|
+
readonly PRODUCT: "mlv_product_";
|
|
35
|
+
readonly RECEIPT: "mlv_receipt_";
|
|
36
|
+
readonly MODEL: "mlv_model_";
|
|
37
|
+
readonly CONFIG: "mlv_config_";
|
|
38
|
+
};
|
|
39
|
+
/**
|
|
40
|
+
* Cached barcode lookup
|
|
41
|
+
*/
|
|
42
|
+
export interface CachedBarcodeLookup {
|
|
43
|
+
barcode: string;
|
|
44
|
+
format: BarcodeFormat;
|
|
45
|
+
productId?: string;
|
|
46
|
+
productName?: string;
|
|
47
|
+
imageUrl?: string;
|
|
48
|
+
found: boolean;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Cached product recognition
|
|
52
|
+
*/
|
|
53
|
+
export interface CachedProductRecognition {
|
|
54
|
+
imageHash: string;
|
|
55
|
+
detections: Array<{
|
|
56
|
+
classLabel: string;
|
|
57
|
+
confidence: number;
|
|
58
|
+
boundingBox?: {
|
|
59
|
+
x: number;
|
|
60
|
+
y: number;
|
|
61
|
+
width: number;
|
|
62
|
+
height: number;
|
|
63
|
+
};
|
|
64
|
+
}>;
|
|
65
|
+
modelVersion: string;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* CacheManager provides fast key-value caching using MMKV
|
|
69
|
+
*
|
|
70
|
+
* @example
|
|
71
|
+
* ```typescript
|
|
72
|
+
* const cache = new CacheManager(mmkvInstance);
|
|
73
|
+
*
|
|
74
|
+
* // Cache a barcode lookup
|
|
75
|
+
* cache.setBarcode('0123456789012', {
|
|
76
|
+
* barcode: '0123456789012',
|
|
77
|
+
* format: 'ean-13',
|
|
78
|
+
* productId: 'prod_123',
|
|
79
|
+
* productName: 'Example Product',
|
|
80
|
+
* found: true,
|
|
81
|
+
* });
|
|
82
|
+
*
|
|
83
|
+
* // Retrieve later
|
|
84
|
+
* const product = cache.getBarcode('0123456789012');
|
|
85
|
+
* ```
|
|
86
|
+
*/
|
|
87
|
+
export declare class CacheManager {
|
|
88
|
+
private storage;
|
|
89
|
+
private defaultTTL;
|
|
90
|
+
constructor(storage: MMKV, customTTL?: Partial<typeof CACHE_TTL>);
|
|
91
|
+
/**
|
|
92
|
+
* Cache a barcode lookup result
|
|
93
|
+
*/
|
|
94
|
+
setBarcode(barcode: string, data: CachedBarcodeLookup, ttl?: number): void;
|
|
95
|
+
/**
|
|
96
|
+
* Get a cached barcode lookup
|
|
97
|
+
* @returns The cached data or null if not found/expired
|
|
98
|
+
*/
|
|
99
|
+
getBarcode(barcode: string): CachedBarcodeLookup | null;
|
|
100
|
+
/**
|
|
101
|
+
* Check if a barcode is cached
|
|
102
|
+
*/
|
|
103
|
+
hasBarcode(barcode: string): boolean;
|
|
104
|
+
/**
|
|
105
|
+
* Delete a cached barcode
|
|
106
|
+
*/
|
|
107
|
+
deleteBarcode(barcode: string): void;
|
|
108
|
+
/**
|
|
109
|
+
* Cache product recognition results for an image
|
|
110
|
+
* @param imageHash - Hash of the image (e.g., MD5 or perceptual hash)
|
|
111
|
+
*/
|
|
112
|
+
setProductRecognition(imageHash: string, data: CachedProductRecognition, ttl?: number): void;
|
|
113
|
+
/**
|
|
114
|
+
* Get cached product recognition for an image
|
|
115
|
+
*/
|
|
116
|
+
getProductRecognition(imageHash: string): CachedProductRecognition | null;
|
|
117
|
+
/**
|
|
118
|
+
* Store model metadata
|
|
119
|
+
*/
|
|
120
|
+
setModelMetadata(modelId: string, metadata: Record<string, unknown>): void;
|
|
121
|
+
/**
|
|
122
|
+
* Get model metadata
|
|
123
|
+
*/
|
|
124
|
+
getModelMetadata<T = Record<string, unknown>>(modelId: string): T | null;
|
|
125
|
+
/**
|
|
126
|
+
* Store a configuration value
|
|
127
|
+
*/
|
|
128
|
+
setConfig<T>(key: string, value: T): void;
|
|
129
|
+
/**
|
|
130
|
+
* Get a configuration value
|
|
131
|
+
*/
|
|
132
|
+
getConfig<T>(key: string, defaultValue: T): T;
|
|
133
|
+
/**
|
|
134
|
+
* Clear all expired entries from the cache
|
|
135
|
+
* Call this periodically (e.g., on app start)
|
|
136
|
+
*/
|
|
137
|
+
cleanupExpired(): number;
|
|
138
|
+
/**
|
|
139
|
+
* Clear all ML Vision cache entries
|
|
140
|
+
*/
|
|
141
|
+
clearAll(): void;
|
|
142
|
+
/**
|
|
143
|
+
* Clear cache by type
|
|
144
|
+
*/
|
|
145
|
+
clearByType(type: keyof typeof CACHE_PREFIX): void;
|
|
146
|
+
/**
|
|
147
|
+
* Get cache statistics
|
|
148
|
+
*/
|
|
149
|
+
getStats(): CacheStats;
|
|
150
|
+
/**
|
|
151
|
+
* Check if a cache entry has expired
|
|
152
|
+
*/
|
|
153
|
+
private isExpired;
|
|
154
|
+
}
|
|
155
|
+
/**
|
|
156
|
+
* Create a CacheManager instance
|
|
157
|
+
* This is the recommended way to create a CacheManager
|
|
158
|
+
*
|
|
159
|
+
* @example
|
|
160
|
+
* ```typescript
|
|
161
|
+
* import { MMKV } from 'react-native-mmkv';
|
|
162
|
+
*
|
|
163
|
+
* const storage = new MMKV({ id: 'ml-vision-cache' });
|
|
164
|
+
* const cache = createCacheManager(storage);
|
|
165
|
+
* ```
|
|
166
|
+
*/
|
|
167
|
+
export declare function createCacheManager(storage: MMKV, customTTL?: Partial<typeof CACHE_TTL>): CacheManager;
|
|
168
|
+
export {};
|