@arfuhad/react-native-smart-camera 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/ARCHITECTURE.md +341 -0
- package/README.md +154 -0
- package/android/build.gradle +89 -0
- package/android/src/main/AndroidManifest.xml +2 -0
- package/android/src/main/java/expo/modules/smartcamera/ImageLoader.kt +106 -0
- package/android/src/main/java/expo/modules/smartcamera/MLKitFaceDetector.kt +273 -0
- package/android/src/main/java/expo/modules/smartcamera/SmartCameraModule.kt +205 -0
- package/android/src/main/java/expo/modules/smartcamera/SmartCameraView.kt +153 -0
- package/android/src/main/java/expo/modules/smartcamera/WebRTCFrameBridge.kt +184 -0
- package/app.plugin.js +17 -0
- package/build/SmartCamera.d.ts +17 -0
- package/build/SmartCamera.d.ts.map +1 -0
- package/build/SmartCamera.js +270 -0
- package/build/SmartCamera.js.map +1 -0
- package/build/SmartCameraModule.d.ts +112 -0
- package/build/SmartCameraModule.d.ts.map +1 -0
- package/build/SmartCameraModule.js +121 -0
- package/build/SmartCameraModule.js.map +1 -0
- package/build/SmartCameraView.d.ts +8 -0
- package/build/SmartCameraView.d.ts.map +1 -0
- package/build/SmartCameraView.js +7 -0
- package/build/SmartCameraView.js.map +1 -0
- package/build/detection/blinkProcessor.d.ts +23 -0
- package/build/detection/blinkProcessor.d.ts.map +1 -0
- package/build/detection/blinkProcessor.js +90 -0
- package/build/detection/blinkProcessor.js.map +1 -0
- package/build/detection/faceDetector.d.ts +16 -0
- package/build/detection/faceDetector.d.ts.map +1 -0
- package/build/detection/faceDetector.js +46 -0
- package/build/detection/faceDetector.js.map +1 -0
- package/build/detection/index.d.ts +4 -0
- package/build/detection/index.d.ts.map +1 -0
- package/build/detection/index.js +4 -0
- package/build/detection/index.js.map +1 -0
- package/build/detection/staticImageDetector.d.ts +25 -0
- package/build/detection/staticImageDetector.d.ts.map +1 -0
- package/build/detection/staticImageDetector.js +48 -0
- package/build/detection/staticImageDetector.js.map +1 -0
- package/build/hooks/index.d.ts +5 -0
- package/build/hooks/index.d.ts.map +1 -0
- package/build/hooks/index.js +5 -0
- package/build/hooks/index.js.map +1 -0
- package/build/hooks/useBlinkDetection.d.ts +39 -0
- package/build/hooks/useBlinkDetection.d.ts.map +1 -0
- package/build/hooks/useBlinkDetection.js +67 -0
- package/build/hooks/useBlinkDetection.js.map +1 -0
- package/build/hooks/useFaceDetection.d.ts +46 -0
- package/build/hooks/useFaceDetection.d.ts.map +1 -0
- package/build/hooks/useFaceDetection.js +80 -0
- package/build/hooks/useFaceDetection.js.map +1 -0
- package/build/hooks/useSmartCamera.d.ts +31 -0
- package/build/hooks/useSmartCamera.d.ts.map +1 -0
- package/build/hooks/useSmartCamera.js +75 -0
- package/build/hooks/useSmartCamera.js.map +1 -0
- package/build/hooks/useSmartCameraWebRTC.d.ts +58 -0
- package/build/hooks/useSmartCameraWebRTC.d.ts.map +1 -0
- package/build/hooks/useSmartCameraWebRTC.js +160 -0
- package/build/hooks/useSmartCameraWebRTC.js.map +1 -0
- package/build/index.d.ts +14 -0
- package/build/index.d.ts.map +1 -0
- package/build/index.js +20 -0
- package/build/index.js.map +1 -0
- package/build/types.d.ts +478 -0
- package/build/types.d.ts.map +1 -0
- package/build/types.js +2 -0
- package/build/types.js.map +1 -0
- package/build/utils/index.d.ts +98 -0
- package/build/utils/index.d.ts.map +1 -0
- package/build/utils/index.js +276 -0
- package/build/utils/index.js.map +1 -0
- package/build/webrtc/WebRTCBridge.d.ts +55 -0
- package/build/webrtc/WebRTCBridge.d.ts.map +1 -0
- package/build/webrtc/WebRTCBridge.js +113 -0
- package/build/webrtc/WebRTCBridge.js.map +1 -0
- package/build/webrtc/index.d.ts +3 -0
- package/build/webrtc/index.d.ts.map +1 -0
- package/build/webrtc/index.js +2 -0
- package/build/webrtc/index.js.map +1 -0
- package/build/webrtc/types.d.ts +64 -0
- package/build/webrtc/types.d.ts.map +1 -0
- package/build/webrtc/types.js +5 -0
- package/build/webrtc/types.js.map +1 -0
- package/expo-module.config.json +9 -0
- package/ios/MLKitFaceDetector.swift +310 -0
- package/ios/SmartCamera.podspec +33 -0
- package/ios/SmartCameraModule.swift +225 -0
- package/ios/SmartCameraView.swift +146 -0
- package/ios/WebRTCFrameBridge.swift +150 -0
- package/package.json +91 -0
- package/plugin/build/index.d.ts +28 -0
- package/plugin/build/index.js +33 -0
- package/plugin/build/withSmartCameraAndroid.d.ts +9 -0
- package/plugin/build/withSmartCameraAndroid.js +108 -0
- package/plugin/build/withSmartCameraIOS.d.ts +11 -0
- package/plugin/build/withSmartCameraIOS.js +92 -0
package/ARCHITECTURE.md
ADDED
|
@@ -0,0 +1,341 @@
|
|
|
1
|
+
# react-native-smart-camera
|
|
2
|
+
|
|
3
|
+
Expo-compatible native module for camera preview, face detection, blink detection, and WebRTC streaming.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
Build a production-ready Expo-compatible native module that provides:
|
|
8
|
+
|
|
9
|
+
- Camera preview via VisionCamera
|
|
10
|
+
- Real-time face and blink detection using Google ML Kit
|
|
11
|
+
- Static image face detection
|
|
12
|
+
- Native WebRTC bridge for video calling and streaming
|
|
13
|
+
- Expo config plugin for zero-config setup
|
|
14
|
+
|
|
15
|
+
## Expo Compatibility
|
|
16
|
+
|
|
17
|
+
| Expo Workflow | Supported | Why |
|
|
18
|
+
|---------------|-----------|-----|
|
|
19
|
+
| Expo Managed | ❌ No | WebRTC + VisionCamera need native code |
|
|
20
|
+
| Expo Dev Client | ✅ Yes | Allows custom native modules |
|
|
21
|
+
| Expo Bare | ✅ Yes | Full native control |
|
|
22
|
+
|
|
23
|
+
**Requirements:**
|
|
24
|
+
- Expo SDK ≥ 49
|
|
25
|
+
- Expo Dev Client
|
|
26
|
+
- EAS Build
|
|
27
|
+
|
|
28
|
+
## Architecture
|
|
29
|
+
|
|
30
|
+
```
|
|
31
|
+
Expo App
|
|
32
|
+
├── SmartCamera (JS Component)
|
|
33
|
+
├── VisionCamera (native)
|
|
34
|
+
├── Frame Processor (worklet)
|
|
35
|
+
├── Native WebRTC Bridge
|
|
36
|
+
│ ├── iOS (Swift)
|
|
37
|
+
│ └── Android (Kotlin)
|
|
38
|
+
└── react-native-webrtc
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
## Installation
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
expo install react-native-smart-camera
|
|
45
|
+
expo prebuild
|
|
46
|
+
expo run:ios
|
|
47
|
+
expo run:android
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
Or with EAS:
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
eas build --profile development
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
## Usage
|
|
57
|
+
|
|
58
|
+
```tsx
|
|
59
|
+
import { SmartCamera } from 'react-native-smart-camera';
|
|
60
|
+
|
|
61
|
+
<SmartCamera
|
|
62
|
+
camera="front"
|
|
63
|
+
faceDetection={{
|
|
64
|
+
enabled: true,
|
|
65
|
+
performanceMode: 'fast',
|
|
66
|
+
landmarkMode: 'all',
|
|
67
|
+
classificationMode: 'all',
|
|
68
|
+
}}
|
|
69
|
+
blinkDetection
|
|
70
|
+
onBlinkDetected={handleBlink}
|
|
71
|
+
onFaceDetected={handleFaces}
|
|
72
|
+
webrtc={{
|
|
73
|
+
enabled: true,
|
|
74
|
+
peerConnection,
|
|
75
|
+
mode: 'call',
|
|
76
|
+
}}
|
|
77
|
+
/>
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
---
|
|
81
|
+
|
|
82
|
+
## API Reference
|
|
83
|
+
|
|
84
|
+
### SmartCamera Component Props
|
|
85
|
+
|
|
86
|
+
```typescript
|
|
87
|
+
interface SmartCameraProps {
|
|
88
|
+
// Camera settings
|
|
89
|
+
camera?: 'front' | 'back';
|
|
90
|
+
fps?: number;
|
|
91
|
+
style?: ViewStyle;
|
|
92
|
+
|
|
93
|
+
// Face Detection Options
|
|
94
|
+
faceDetection?: FaceDetectionConfig;
|
|
95
|
+
|
|
96
|
+
// Blink Detection
|
|
97
|
+
blinkDetection?: boolean;
|
|
98
|
+
onBlinkDetected?: (event: BlinkEvent) => void;
|
|
99
|
+
|
|
100
|
+
// Face Detection Callback
|
|
101
|
+
onFaceDetected?: (faces: Face[]) => void;
|
|
102
|
+
|
|
103
|
+
// WebRTC Configuration
|
|
104
|
+
webrtc?: WebRTCConfig;
|
|
105
|
+
}
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
---
|
|
109
|
+
|
|
110
|
+
## Face Detection Options
|
|
111
|
+
|
|
112
|
+
### Common Options (Frame Processor and Static Images)
|
|
113
|
+
|
|
114
|
+
| Option | Description | Default | Options |
|
|
115
|
+
|--------|-------------|---------|---------|
|
|
116
|
+
| `performanceMode` | Favor speed or accuracy when detecting faces | `'fast'` | `'fast'`, `'accurate'` |
|
|
117
|
+
| `landmarkMode` | Whether to identify facial landmarks: eyes, ears, nose, cheeks, mouth | `'none'` | `'none'`, `'all'` |
|
|
118
|
+
| `contourMode` | Whether to detect contours of facial features. Contours are detected for only the most prominent face in an image | `'none'` | `'none'`, `'all'` |
|
|
119
|
+
| `classificationMode` | Whether to classify faces into categories such as 'smiling' and 'eyes open' | `'none'` | `'none'`, `'all'` |
|
|
120
|
+
| `minFaceSize` | Sets the smallest desired face size, expressed as the ratio of the width of the head to width of the image | `0.15` | `number` |
|
|
121
|
+
| `trackingEnabled` | Whether to assign faces an ID to track faces across images. Note: When contour detection is enabled, only one face is detected, so face tracking doesn't produce useful results. Don't enable both contour detection and face tracking | `false` | `boolean` |
|
|
122
|
+
|
|
123
|
+
### Frame Processor Options
|
|
124
|
+
|
|
125
|
+
| Option | Description | Default | Options |
|
|
126
|
+
|--------|-------------|---------|---------|
|
|
127
|
+
| `cameraFacing` | Current active camera | `'front'` | `'front'`, `'back'` |
|
|
128
|
+
| `autoMode` | Should handle auto scale (face bounds, contour and landmarks) and rotation on native side? If disabled, all detection results will be relative to frame coordinates, not to screen/preview. You shouldn't use this option if you want to draw on screen using Skia Frame Processor | `false` | `boolean` |
|
|
129
|
+
| `windowWidth` | Required if you want to use `autoMode`. You must handle your own logic to get screen sizes, with or without statusbar size, etc. | `1.0` | `number` |
|
|
130
|
+
| `windowHeight` | Required if you want to use `autoMode`. You must handle your own logic to get screen sizes, with or without statusbar size, etc. | `1.0` | `number` |
|
|
131
|
+
|
|
132
|
+
### Static Images Options
|
|
133
|
+
|
|
134
|
+
| Option | Description | Default | Options |
|
|
135
|
+
|--------|-------------|---------|---------|
|
|
136
|
+
| `image` | Image source for static face detection | - | `number`, `string`, `{ uri: string }` |
|
|
137
|
+
|
|
138
|
+
---
|
|
139
|
+
|
|
140
|
+
## Types
|
|
141
|
+
|
|
142
|
+
```typescript
|
|
143
|
+
// Face Detection Options
|
|
144
|
+
interface FaceDetectionOptions {
|
|
145
|
+
performanceMode?: 'fast' | 'accurate';
|
|
146
|
+
landmarkMode?: 'none' | 'all';
|
|
147
|
+
contourMode?: 'none' | 'all';
|
|
148
|
+
classificationMode?: 'none' | 'all';
|
|
149
|
+
minFaceSize?: number;
|
|
150
|
+
trackingEnabled?: boolean;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// Frame Processor Options
|
|
154
|
+
interface FrameProcessorOptions extends FaceDetectionOptions {
|
|
155
|
+
cameraFacing?: 'front' | 'back';
|
|
156
|
+
autoMode?: boolean;
|
|
157
|
+
windowWidth?: number;
|
|
158
|
+
windowHeight?: number;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
// Static Image Options
|
|
162
|
+
interface StaticImageOptions extends FaceDetectionOptions {
|
|
163
|
+
image: number | string | { uri: string };
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
// Face Detection Result
|
|
167
|
+
interface Face {
|
|
168
|
+
bounds: { x: number; y: number; width: number; height: number };
|
|
169
|
+
landmarks?: FaceLandmarks;
|
|
170
|
+
contours?: FaceContours;
|
|
171
|
+
smilingProbability?: number;
|
|
172
|
+
leftEyeOpenProbability?: number;
|
|
173
|
+
rightEyeOpenProbability?: number;
|
|
174
|
+
trackingId?: number;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
// Blink Event
|
|
178
|
+
interface BlinkEvent {
|
|
179
|
+
timestamp: number;
|
|
180
|
+
leftEyeOpen: number; // 0.0 - 1.0
|
|
181
|
+
rightEyeOpen: number; // 0.0 - 1.0
|
|
182
|
+
isBlink: boolean;
|
|
183
|
+
faceId?: number;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
// WebRTC Configuration
|
|
187
|
+
interface WebRTCConfig {
|
|
188
|
+
enabled: boolean;
|
|
189
|
+
peerConnection?: RTCPeerConnection;
|
|
190
|
+
mode?: 'call' | 'stream';
|
|
191
|
+
videoConstraints?: {
|
|
192
|
+
width?: number;
|
|
193
|
+
height?: number;
|
|
194
|
+
frameRate?: number;
|
|
195
|
+
};
|
|
196
|
+
}
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
---
|
|
200
|
+
|
|
201
|
+
## Frame Processor Functions
|
|
202
|
+
|
|
203
|
+
```typescript
|
|
204
|
+
import { detectFaces, detectBlink } from 'react-native-smart-camera';
|
|
205
|
+
|
|
206
|
+
// Detect faces in a frame
|
|
207
|
+
const faces = detectFaces(frame, {
|
|
208
|
+
performanceMode: 'fast',
|
|
209
|
+
landmarkMode: 'all',
|
|
210
|
+
});
|
|
211
|
+
|
|
212
|
+
// Detect blink in a frame
|
|
213
|
+
const blinkEvent = detectBlink(frame, {
|
|
214
|
+
classificationMode: 'all',
|
|
215
|
+
});
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
---
|
|
219
|
+
|
|
220
|
+
## Static Image Detection
|
|
221
|
+
|
|
222
|
+
```typescript
|
|
223
|
+
import { detectFacesInImage } from 'react-native-smart-camera';
|
|
224
|
+
|
|
225
|
+
const faces = await detectFacesInImage({
|
|
226
|
+
image: require('./photo.jpg'),
|
|
227
|
+
performanceMode: 'accurate',
|
|
228
|
+
landmarkMode: 'all',
|
|
229
|
+
contourMode: 'all',
|
|
230
|
+
});
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
---
|
|
234
|
+
|
|
235
|
+
## WebRTC Hooks
|
|
236
|
+
|
|
237
|
+
```typescript
|
|
238
|
+
import { useSmartCameraWebRTC } from 'react-native-smart-camera';
|
|
239
|
+
|
|
240
|
+
const { videoTrack, startStreaming, stopStreaming, switchCamera } = useSmartCameraWebRTC({
|
|
241
|
+
enabled: true,
|
|
242
|
+
peerConnection,
|
|
243
|
+
mode: 'call',
|
|
244
|
+
});
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
---
|
|
248
|
+
|
|
249
|
+
## Expo Config Plugin
|
|
250
|
+
|
|
251
|
+
Add to your `app.json`:
|
|
252
|
+
|
|
253
|
+
```json
|
|
254
|
+
{
|
|
255
|
+
"plugins": ["react-native-smart-camera"]
|
|
256
|
+
}
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
The plugin automatically:
|
|
260
|
+
- Adds camera permissions
|
|
261
|
+
- Adds microphone permissions
|
|
262
|
+
- Adds required iOS frameworks
|
|
263
|
+
- Configures Android Proguard rules
|
|
264
|
+
|
|
265
|
+
---
|
|
266
|
+
|
|
267
|
+
## Project Structure
|
|
268
|
+
|
|
269
|
+
```
|
|
270
|
+
react-native-smart-camera/
|
|
271
|
+
├── src/
|
|
272
|
+
│ ├── index.ts
|
|
273
|
+
│ ├── SmartCamera.tsx
|
|
274
|
+
│ ├── types.ts
|
|
275
|
+
│ ├── hooks/
|
|
276
|
+
│ │ ├── useSmartCamera.ts
|
|
277
|
+
│ │ ├── useBlinkDetection.ts
|
|
278
|
+
│ │ ├── useFaceDetection.ts
|
|
279
|
+
│ │ └── useSmartCameraWebRTC.ts
|
|
280
|
+
│ ├── detection/
|
|
281
|
+
│ │ ├── blinkProcessor.ts
|
|
282
|
+
│ │ ├── faceDetector.ts
|
|
283
|
+
│ │ └── staticImageDetector.ts
|
|
284
|
+
│ └── webrtc/
|
|
285
|
+
│ ├── WebRTCBridge.ts
|
|
286
|
+
│ └── types.ts
|
|
287
|
+
├── android/
|
|
288
|
+
│ ├── src/main/java/expo/modules/smartcamera/
|
|
289
|
+
│ │ ├── SmartCameraModule.kt
|
|
290
|
+
│ │ ├── SmartCameraView.kt
|
|
291
|
+
│ │ ├── MLKitBridge.kt
|
|
292
|
+
│ │ └── WebRTCFrameBridge.kt
|
|
293
|
+
│ └── build.gradle
|
|
294
|
+
├── ios/
|
|
295
|
+
│ ├── SmartCameraModule.swift
|
|
296
|
+
│ ├── SmartCameraView.swift
|
|
297
|
+
│ ├── MLKitBridge.swift
|
|
298
|
+
│ ├── WebRTCFrameBridge.swift
|
|
299
|
+
│ └── SmartCamera.podspec
|
|
300
|
+
├── plugin/
|
|
301
|
+
│ ├── index.ts
|
|
302
|
+
│ ├── withSmartCameraIOS.ts
|
|
303
|
+
│ └── withSmartCameraAndroid.ts
|
|
304
|
+
├── example/
|
|
305
|
+
│ ├── App.tsx
|
|
306
|
+
│ ├── app.json
|
|
307
|
+
│ └── package.json
|
|
308
|
+
├── app.plugin.js
|
|
309
|
+
├── package.json
|
|
310
|
+
├── tsconfig.json
|
|
311
|
+
└── README.md
|
|
312
|
+
```
|
|
313
|
+
|
|
314
|
+
---
|
|
315
|
+
|
|
316
|
+
## Limitations
|
|
317
|
+
|
|
318
|
+
- ❌ Not supported in Expo Go
|
|
319
|
+
- ❌ Requires Dev Client or Bare Workflow
|
|
320
|
+
- ❌ Native build required (via EAS or local)
|
|
321
|
+
|
|
322
|
+
---
|
|
323
|
+
|
|
324
|
+
## Peer Dependencies
|
|
325
|
+
|
|
326
|
+
```json
|
|
327
|
+
{
|
|
328
|
+
"peerDependencies": {
|
|
329
|
+
"react-native-vision-camera": ">=3.0.0",
|
|
330
|
+
"react-native-webrtc": ">=118.0.0",
|
|
331
|
+
"react-native-worklets-core": ">=1.0.0"
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
```
|
|
335
|
+
|
|
336
|
+
---
|
|
337
|
+
|
|
338
|
+
## License
|
|
339
|
+
|
|
340
|
+
MIT
|
|
341
|
+
|
package/README.md
ADDED
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
# react-native-smart-camera
|
|
2
|
+
|
|
3
|
+
Expo-compatible native module for camera preview, face detection, blink detection, and WebRTC streaming.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- 📷 Camera preview via VisionCamera
|
|
8
|
+
- 👁️ Real-time face detection with Google ML Kit
|
|
9
|
+
- 😉 Blink detection with configurable sensitivity
|
|
10
|
+
- 📡 Native WebRTC bridge for video calling and streaming
|
|
11
|
+
- 🔌 Expo config plugin for zero-config setup
|
|
12
|
+
- 📱 Static image face detection
|
|
13
|
+
|
|
14
|
+
## Expo Compatibility
|
|
15
|
+
|
|
16
|
+
| Expo Workflow | Supported | Notes |
|
|
17
|
+
| --------------- | --------- | ---------------------------------- |
|
|
18
|
+
| Expo Managed | ❌ No | Requires native code |
|
|
19
|
+
| Expo Dev Client | ✅ Yes | Recommended for development |
|
|
20
|
+
| Expo Bare | ✅ Yes | Full native control |
|
|
21
|
+
|
|
22
|
+
**Requirements:**
|
|
23
|
+
- Expo SDK ≥ 49
|
|
24
|
+
- Expo Dev Client or Bare Workflow
|
|
25
|
+
- EAS Build or local native builds
|
|
26
|
+
|
|
27
|
+
## Installation
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
# Install the package
|
|
31
|
+
npx expo install react-native-smart-camera
|
|
32
|
+
|
|
33
|
+
# Install peer dependencies
|
|
34
|
+
npx expo install react-native-vision-camera react-native-webrtc react-native-worklets-core
|
|
35
|
+
|
|
36
|
+
# Rebuild native code
|
|
37
|
+
npx expo prebuild
|
|
38
|
+
npx expo run:ios
|
|
39
|
+
# or
|
|
40
|
+
npx expo run:android
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
## Expo Config Plugin
|
|
44
|
+
|
|
45
|
+
Add to your `app.json` or `app.config.js`:
|
|
46
|
+
|
|
47
|
+
```json
|
|
48
|
+
{
|
|
49
|
+
"plugins": [
|
|
50
|
+
[
|
|
51
|
+
"react-native-smart-camera",
|
|
52
|
+
{
|
|
53
|
+
"cameraPermissionText": "Allow camera access for face detection",
|
|
54
|
+
"microphonePermissionText": "Allow microphone for audio streaming"
|
|
55
|
+
}
|
|
56
|
+
]
|
|
57
|
+
]
|
|
58
|
+
}
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
## Usage
|
|
62
|
+
|
|
63
|
+
### Basic Camera Preview
|
|
64
|
+
|
|
65
|
+
```tsx
|
|
66
|
+
import { SmartCamera } from 'react-native-smart-camera';
|
|
67
|
+
|
|
68
|
+
export default function App() {
|
|
69
|
+
return (
|
|
70
|
+
<SmartCamera
|
|
71
|
+
camera="front"
|
|
72
|
+
style={{ flex: 1 }}
|
|
73
|
+
/>
|
|
74
|
+
);
|
|
75
|
+
}
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
### Face Detection
|
|
79
|
+
|
|
80
|
+
```tsx
|
|
81
|
+
import { SmartCamera, Face } from 'react-native-smart-camera';
|
|
82
|
+
|
|
83
|
+
export default function App() {
|
|
84
|
+
const handleFaces = (faces: Face[]) => {
|
|
85
|
+
console.log(`Detected ${faces.length} faces`);
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
return (
|
|
89
|
+
<SmartCamera
|
|
90
|
+
camera="front"
|
|
91
|
+
faceDetection={{
|
|
92
|
+
enabled: true,
|
|
93
|
+
performanceMode: 'fast',
|
|
94
|
+
landmarkMode: 'all',
|
|
95
|
+
classificationMode: 'all',
|
|
96
|
+
}}
|
|
97
|
+
onFaceDetected={handleFaces}
|
|
98
|
+
style={{ flex: 1 }}
|
|
99
|
+
/>
|
|
100
|
+
);
|
|
101
|
+
}
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
### Blink Detection
|
|
105
|
+
|
|
106
|
+
```tsx
|
|
107
|
+
import { SmartCamera, BlinkEvent } from 'react-native-smart-camera';
|
|
108
|
+
|
|
109
|
+
export default function App() {
|
|
110
|
+
const handleBlink = (event: BlinkEvent) => {
|
|
111
|
+
console.log('Blink detected!', event);
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
return (
|
|
115
|
+
<SmartCamera
|
|
116
|
+
camera="front"
|
|
117
|
+
blinkDetection
|
|
118
|
+
onBlinkDetected={handleBlink}
|
|
119
|
+
style={{ flex: 1 }}
|
|
120
|
+
/>
|
|
121
|
+
);
|
|
122
|
+
}
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
### Static Image Detection
|
|
126
|
+
|
|
127
|
+
```tsx
|
|
128
|
+
import { detectFacesInImage } from 'react-native-smart-camera';
|
|
129
|
+
|
|
130
|
+
const faces = await detectFacesInImage({
|
|
131
|
+
image: require('./photo.jpg'),
|
|
132
|
+
performanceMode: 'accurate',
|
|
133
|
+
landmarkMode: 'all',
|
|
134
|
+
});
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
## Face Detection Options
|
|
138
|
+
|
|
139
|
+
| Option | Description | Default | Options |
|
|
140
|
+
| ------------------ | ----------------------------------------------- | --------- | --------------------- |
|
|
141
|
+
| `performanceMode` | Speed vs accuracy trade-off | `'fast'` | `'fast'`, `'accurate'`|
|
|
142
|
+
| `landmarkMode` | Detect facial landmarks | `'none'` | `'none'`, `'all'` |
|
|
143
|
+
| `contourMode` | Detect facial contours | `'none'` | `'none'`, `'all'` |
|
|
144
|
+
| `classificationMode` | Classify smiling/eyes open | `'none'` | `'none'`, `'all'` |
|
|
145
|
+
| `minFaceSize` | Minimum face size ratio | `0.15` | `number` |
|
|
146
|
+
| `trackingEnabled` | Track faces across frames | `false` | `boolean` |
|
|
147
|
+
|
|
148
|
+
## API Reference
|
|
149
|
+
|
|
150
|
+
See [ARCHITECTURE.md](./ARCHITECTURE.md) for full API documentation.
|
|
151
|
+
|
|
152
|
+
## License
|
|
153
|
+
|
|
154
|
+
MIT
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
apply plugin: 'com.android.library'
|
|
2
|
+
apply plugin: 'kotlin-android'
|
|
3
|
+
apply plugin: 'maven-publish'
|
|
4
|
+
|
|
5
|
+
group = 'expo.modules.smartcamera'
|
|
6
|
+
version = '0.1.0'
|
|
7
|
+
|
|
8
|
+
buildscript {
|
|
9
|
+
def expoModulesCorePlugin = new File(project(":expo-modules-core").projectDir.absolutePath, "ExpoModulesCorePlugin.gradle")
|
|
10
|
+
if (expoModulesCorePlugin.exists()) {
|
|
11
|
+
apply from: expoModulesCorePlugin
|
|
12
|
+
applyKotlinExpoModulesCorePlugin()
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
// Simple fallback that allows building without expo-modules-core for lint
|
|
16
|
+
repositories {
|
|
17
|
+
mavenCentral()
|
|
18
|
+
google()
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
dependencies {
|
|
22
|
+
classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:1.9.22")
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
afterEvaluate {
|
|
27
|
+
publishing {
|
|
28
|
+
publications {
|
|
29
|
+
release(MavenPublication) {
|
|
30
|
+
from components.release
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
repositories {
|
|
34
|
+
maven {
|
|
35
|
+
url = mavenLocal().url
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
android {
|
|
42
|
+
namespace "expo.modules.smartcamera"
|
|
43
|
+
compileSdkVersion safeExtGet("compileSdkVersion", 34)
|
|
44
|
+
|
|
45
|
+
defaultConfig {
|
|
46
|
+
minSdkVersion safeExtGet("minSdkVersion", 23)
|
|
47
|
+
targetSdkVersion safeExtGet("targetSdkVersion", 34)
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
publishing {
|
|
51
|
+
singleVariant("release") {
|
|
52
|
+
withSourcesJar()
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
lintOptions {
|
|
57
|
+
abortOnError false
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
compileOptions {
|
|
61
|
+
sourceCompatibility JavaVersion.VERSION_17
|
|
62
|
+
targetCompatibility JavaVersion.VERSION_17
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
kotlinOptions {
|
|
66
|
+
jvmTarget = JavaVersion.VERSION_17.majorVersion
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
repositories {
|
|
71
|
+
mavenCentral()
|
|
72
|
+
google()
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
dependencies {
|
|
76
|
+
implementation project(':expo-modules-core')
|
|
77
|
+
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.9.22"
|
|
78
|
+
|
|
79
|
+
// Kotlin Coroutines
|
|
80
|
+
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.3"
|
|
81
|
+
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.7.3"
|
|
82
|
+
|
|
83
|
+
// Google ML Kit Face Detection
|
|
84
|
+
implementation 'com.google.mlkit:face-detection:16.1.6'
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
def safeExtGet(prop, fallback) {
|
|
88
|
+
rootProject.ext.has(prop) ? rootProject.ext.get(prop) : fallback
|
|
89
|
+
}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
package expo.modules.smartcamera
|
|
2
|
+
|
|
3
|
+
import android.graphics.Bitmap
|
|
4
|
+
import android.graphics.BitmapFactory
|
|
5
|
+
import android.net.Uri
|
|
6
|
+
import android.util.Base64
|
|
7
|
+
import android.util.Log
|
|
8
|
+
import kotlinx.coroutines.Dispatchers
|
|
9
|
+
import kotlinx.coroutines.withContext
|
|
10
|
+
import java.io.File
|
|
11
|
+
import java.net.URL
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Utility class for loading images from various sources
|
|
15
|
+
*/
|
|
16
|
+
class ImageLoader {
|
|
17
|
+
companion object {
|
|
18
|
+
private const val TAG = "ImageLoader"
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Load an image from various sources (suspending function for async loading)
|
|
23
|
+
*
|
|
24
|
+
* @param source Image source - can be String URI, Map with "uri" key, or resource ID
|
|
25
|
+
* @return Bitmap
|
|
26
|
+
* @throws IllegalArgumentException if the source is invalid
|
|
27
|
+
*/
|
|
28
|
+
suspend fun loadImage(source: Any): Bitmap = withContext(Dispatchers.IO) {
|
|
29
|
+
when (source) {
|
|
30
|
+
is String -> loadFromString(source)
|
|
31
|
+
is Map<*, *> -> {
|
|
32
|
+
val uri = source["uri"] as? String
|
|
33
|
+
val base64 = source["base64"] as? String
|
|
34
|
+
|
|
35
|
+
when {
|
|
36
|
+
uri != null -> loadFromString(uri)
|
|
37
|
+
base64 != null -> loadFromBase64(base64)
|
|
38
|
+
else -> throw IllegalArgumentException("Invalid image source map - must contain 'uri' or 'base64'")
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
is Number -> throw IllegalArgumentException("Loading from resource ID not supported. Use a URI instead.")
|
|
42
|
+
else -> throw IllegalArgumentException("Unsupported image source type: ${source::class.java.simpleName}")
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
private fun loadFromString(source: String): Bitmap {
|
|
47
|
+
return when {
|
|
48
|
+
source.startsWith("data:image") -> {
|
|
49
|
+
// Base64 data URI
|
|
50
|
+
val base64Data = source.substringAfter("base64,")
|
|
51
|
+
loadFromBase64(base64Data)
|
|
52
|
+
}
|
|
53
|
+
source.startsWith("http://") || source.startsWith("https://") -> {
|
|
54
|
+
loadFromUrl(source)
|
|
55
|
+
}
|
|
56
|
+
source.startsWith("file://") -> {
|
|
57
|
+
loadFromFile(source.removePrefix("file://"))
|
|
58
|
+
}
|
|
59
|
+
else -> {
|
|
60
|
+
// Try as file path
|
|
61
|
+
loadFromFile(source)
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
private fun loadFromBase64(base64: String): Bitmap {
|
|
67
|
+
return try {
|
|
68
|
+
val decodedBytes = Base64.decode(base64, Base64.DEFAULT)
|
|
69
|
+
BitmapFactory.decodeByteArray(decodedBytes, 0, decodedBytes.size)
|
|
70
|
+
?: throw IllegalStateException("Failed to decode base64 image data")
|
|
71
|
+
} catch (e: Exception) {
|
|
72
|
+
Log.e(TAG, "Failed to decode base64 image", e)
|
|
73
|
+
throw IllegalArgumentException("Failed to decode base64 image: ${e.message}", e)
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
private fun loadFromUrl(url: String): Bitmap {
|
|
78
|
+
return try {
|
|
79
|
+
val connection = URL(url).openConnection()
|
|
80
|
+
connection.connectTimeout = 10000
|
|
81
|
+
connection.readTimeout = 30000
|
|
82
|
+
connection.doInput = true
|
|
83
|
+
connection.connect()
|
|
84
|
+
val inputStream = connection.getInputStream()
|
|
85
|
+
BitmapFactory.decodeStream(inputStream)
|
|
86
|
+
?: throw IllegalStateException("Failed to decode image from URL")
|
|
87
|
+
} catch (e: Exception) {
|
|
88
|
+
Log.e(TAG, "Failed to load image from URL: $url", e)
|
|
89
|
+
throw IllegalArgumentException("Failed to load image from URL: ${e.message}", e)
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
private fun loadFromFile(path: String): Bitmap {
|
|
94
|
+
return try {
|
|
95
|
+
val file = File(path)
|
|
96
|
+
if (!file.exists()) {
|
|
97
|
+
throw IllegalArgumentException("File not found: $path")
|
|
98
|
+
}
|
|
99
|
+
BitmapFactory.decodeFile(path)
|
|
100
|
+
?: throw IllegalStateException("Failed to decode image from file")
|
|
101
|
+
} catch (e: Exception) {
|
|
102
|
+
Log.e(TAG, "Failed to load image from file: $path", e)
|
|
103
|
+
throw IllegalArgumentException("Failed to load image from file: ${e.message}", e)
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|