mediversal-rn-image-intelligence 1.0.6 β 1.0.8
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/LICENSE +21 -0
- package/README.md +532 -131
- package/android/build.gradle +4 -6
- package/android/gradle.properties +9 -0
- package/android/src/main/java/com/mediversalrnimagintelligence/ImageIntelligencePackage.kt +1 -2
- package/lib/commonjs/index.js +2 -25
- package/lib/commonjs/index.js.map +1 -1
- package/lib/module/index.js +2 -25
- package/lib/module/index.js.map +1 -1
- package/lib/typescript/index.d.ts.map +1 -1
- package/lib/typescript/types.d.ts +0 -11
- package/lib/typescript/types.d.ts.map +1 -1
- package/mediversal-rn-image-intelligence.podspec +24 -0
- package/package.json +8 -5
- package/src/index.tsx +1 -38
- package/src/types.ts +0 -11
- package/android/src/main/java/com/mediversalrnimagintelligence/HandwritingRecognitionModule.kt +0 -74
- package/ios/HandwritingRecognitionModule.m +0 -14
- package/ios/HandwritingRecognitionModule.swift +0 -53
- package/lib/commonjs/NativeHandwritingRecognitionModule.js +0 -12
- package/lib/commonjs/NativeHandwritingRecognitionModule.js.map +0 -1
- package/lib/module/NativeHandwritingRecognitionModule.js +0 -8
- package/lib/module/NativeHandwritingRecognitionModule.js.map +0 -1
- package/lib/typescript/NativeHandwritingRecognitionModule.d.ts +0 -11
- package/lib/typescript/NativeHandwritingRecognitionModule.d.ts.map +0 -1
- package/src/NativeHandwritingRecognitionModule.ts +0 -16
package/README.md
CHANGED
|
@@ -3,68 +3,130 @@
|
|
|
3
3
|
[](https://badge.fury.io/js/mediversal-rn-image-intelligence)
|
|
4
4
|
[](https://opensource.org/licenses/MIT)
|
|
5
5
|
|
|
6
|
-
|
|
6
|
+
A production-ready React Native library for intelligent image analysis using Google ML Kit's on-device APIs. Analyze images to detect faces and extract text, with all processing happening locally on the device for maximum privacy and performance.
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
## Overview
|
|
9
9
|
|
|
10
|
-
|
|
10
|
+
This library provides a simple interface to Google ML Kit's powerful machine learning capabilities for mobile applications. You can detect human faces with detailed metadata including smile probability, eye state, and head rotation, as well as extract printed text from images with high accuracy using optical character recognition.
|
|
11
11
|
|
|
12
|
-
-
|
|
13
|
-
- **Text Recognition (OCR)**: Extract printed text from images with high accuracy
|
|
14
|
-
- **Handwriting Recognition**: Recognize handwritten text (requires stroke data)
|
|
15
|
-
- **π On-Device Processing**: All analysis happens locally using Google ML Kit
|
|
16
|
-
- **π Privacy-First**: No data sent to external servers
|
|
17
|
-
- **β‘ Fast & Efficient**: Optimized for mobile performance
|
|
18
|
-
- **π± Cross-Platform**: Works seamlessly on iOS and Android
|
|
19
|
-
- **π¨ TurboModule Architecture**: Built with React Native New Architecture support
|
|
20
|
-
- **πͺ TypeScript**: Fully typed API for excellent developer experience
|
|
12
|
+
Since all processing happens entirely on-device, your users' data never leaves their device. There's no need for an internet connection, no data is uploaded to external servers, and you get fast, reliable results even in offline scenarios.
|
|
21
13
|
|
|
22
|
-
##
|
|
14
|
+
## Key Features
|
|
15
|
+
|
|
16
|
+
**Face Detection**
|
|
17
|
+
Detect human faces in images with comprehensive metadata including smiling probability, eye open probability, and head rotation angles. Perfect for selfie validation, group photo analysis, and identity verification workflows.
|
|
18
|
+
|
|
19
|
+
**Text Recognition (OCR)**
|
|
20
|
+
Extract printed text from images with high accuracy. Ideal for document scanning, business card reading, receipt processing, and any scenario where you need to digitize text from photos.
|
|
21
|
+
|
|
22
|
+
**Privacy-First Architecture**
|
|
23
|
+
All image processing happens on-device using Google ML Kit. No images or extracted data are ever transmitted to external servers, ensuring complete privacy and GDPR compliance.
|
|
24
|
+
|
|
25
|
+
**Cross-Platform Support**
|
|
26
|
+
Works seamlessly on both iOS and Android with a unified API. Built with TurboModule specifications for the new React Native architecture.
|
|
27
|
+
|
|
28
|
+
**Performance Optimized**
|
|
29
|
+
Designed for mobile performance with efficient processing and the option to run face detection and text recognition in parallel.
|
|
30
|
+
|
|
31
|
+
**TypeScript Support**
|
|
32
|
+
Fully typed API provides excellent autocomplete and type safety in your development environment.
|
|
33
|
+
|
|
34
|
+
## Installation
|
|
35
|
+
|
|
36
|
+
Install the package using npm:
|
|
23
37
|
|
|
24
38
|
```bash
|
|
25
39
|
npm install mediversal-rn-image-intelligence
|
|
26
|
-
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
Or if you prefer yarn:
|
|
43
|
+
|
|
44
|
+
```bash
|
|
27
45
|
yarn add mediversal-rn-image-intelligence
|
|
28
46
|
```
|
|
29
47
|
|
|
30
|
-
|
|
48
|
+
## Platform-Specific Setup
|
|
49
|
+
|
|
50
|
+
### iOS Configuration
|
|
51
|
+
|
|
52
|
+
After installing the package, you'll need to install the CocoaPods dependencies. Navigate to your iOS directory:
|
|
31
53
|
|
|
32
54
|
```bash
|
|
33
|
-
cd ios
|
|
55
|
+
cd ios
|
|
56
|
+
pod install
|
|
57
|
+
cd ..
|
|
34
58
|
```
|
|
35
59
|
|
|
36
|
-
|
|
60
|
+
Add the required permissions to your `ios/YourApp/Info.plist` file:
|
|
37
61
|
|
|
38
62
|
```xml
|
|
39
63
|
<key>NSCameraUsageDescription</key>
|
|
40
|
-
<string>We need access to
|
|
64
|
+
<string>We need camera access to capture and analyze images</string>
|
|
41
65
|
<key>NSPhotoLibraryUsageDescription</key>
|
|
42
|
-
<string>We need access to
|
|
66
|
+
<string>We need photo library access to select and analyze images</string>
|
|
43
67
|
```
|
|
44
68
|
|
|
45
|
-
|
|
69
|
+
Requirements for iOS:
|
|
70
|
+
|
|
71
|
+
- iOS 12.0 or higher
|
|
72
|
+
- Xcode 12.0 or higher
|
|
73
|
+
- CocoaPods 1.10 or higher
|
|
46
74
|
|
|
47
|
-
|
|
75
|
+
### Android Configuration
|
|
76
|
+
|
|
77
|
+
First, make sure your minimum SDK version is set to 21 or higher in `android/build.gradle`:
|
|
48
78
|
|
|
49
79
|
```gradle
|
|
50
|
-
// android/build.gradle
|
|
51
80
|
buildscript {
|
|
52
81
|
ext {
|
|
53
82
|
minSdkVersion = 21
|
|
83
|
+
compileSdkVersion = 33
|
|
84
|
+
targetSdkVersion = 33
|
|
54
85
|
}
|
|
55
86
|
}
|
|
56
87
|
```
|
|
57
88
|
|
|
58
|
-
|
|
89
|
+
Update your `android/app/build.gradle` to use Java 17:
|
|
90
|
+
|
|
91
|
+
```gradle
|
|
92
|
+
android {
|
|
93
|
+
compileOptions {
|
|
94
|
+
sourceCompatibility JavaVersion.VERSION_17
|
|
95
|
+
targetCompatibility JavaVersion.VERSION_17
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
kotlinOptions {
|
|
99
|
+
jvmTarget = "17"
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
Add the necessary permissions to `android/app/src/main/AndroidManifest.xml`:
|
|
59
105
|
|
|
60
106
|
```xml
|
|
61
|
-
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
|
|
62
107
|
<uses-permission android:name="android.permission.CAMERA" />
|
|
108
|
+
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
|
|
109
|
+
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
|
63
110
|
```
|
|
64
111
|
|
|
65
|
-
|
|
112
|
+
Remember to request these permissions at runtime in your application code:
|
|
66
113
|
|
|
67
|
-
|
|
114
|
+
```typescript
|
|
115
|
+
import { PermissionsAndroid, Platform } from 'react-native';
|
|
116
|
+
|
|
117
|
+
async function requestPermissions() {
|
|
118
|
+
if (Platform.OS === 'android') {
|
|
119
|
+
await PermissionsAndroid.requestMultiple([
|
|
120
|
+
PermissionsAndroid.PERMISSIONS.CAMERA,
|
|
121
|
+
PermissionsAndroid.PERMISSIONS.READ_EXTERNAL_STORAGE,
|
|
122
|
+
]);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
## Basic Usage
|
|
128
|
+
|
|
129
|
+
The simplest way to use the library is with the `analyzeImage` function. Pass it an image URI and it will return the analysis results:
|
|
68
130
|
|
|
69
131
|
```typescript
|
|
70
132
|
import { analyzeImage } from 'mediversal-rn-image-intelligence';
|
|
@@ -74,21 +136,12 @@ async function analyzeMyImage() {
|
|
|
74
136
|
const result = await analyzeImage('file:///path/to/image.jpg');
|
|
75
137
|
|
|
76
138
|
console.log('Contains face:', result.containsFace);
|
|
77
|
-
console.log('Contains
|
|
78
|
-
console.log('Contains handwritten text:', result.containsHandwrittenText);
|
|
139
|
+
console.log('Contains text:', result.containsPrintedText);
|
|
79
140
|
|
|
80
|
-
// Access detected faces
|
|
81
141
|
if (result.faces) {
|
|
82
|
-
result.faces.
|
|
83
|
-
console.log(`Face ${index + 1}:`);
|
|
84
|
-
console.log(' Bounding box:', face.boundingBox);
|
|
85
|
-
console.log(' Smiling:', face.smilingProbability);
|
|
86
|
-
console.log(' Left eye open:', face.leftEyeOpenProbability);
|
|
87
|
-
console.log(' Right eye open:', face.rightEyeOpenProbability);
|
|
88
|
-
});
|
|
142
|
+
console.log('Number of faces:', result.faces.length);
|
|
89
143
|
}
|
|
90
144
|
|
|
91
|
-
// Access extracted text
|
|
92
145
|
if (result.printedText) {
|
|
93
146
|
console.log('Extracted text:', result.printedText);
|
|
94
147
|
}
|
|
@@ -98,7 +151,9 @@ async function analyzeMyImage() {
|
|
|
98
151
|
}
|
|
99
152
|
```
|
|
100
153
|
|
|
101
|
-
|
|
154
|
+
## Advanced Configuration
|
|
155
|
+
|
|
156
|
+
You can customize the analysis behavior by passing an options object:
|
|
102
157
|
|
|
103
158
|
```typescript
|
|
104
159
|
import {
|
|
@@ -109,15 +164,32 @@ import {
|
|
|
109
164
|
const options: AnalysisOptions = {
|
|
110
165
|
detectFaces: true,
|
|
111
166
|
detectPrintedText: true,
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
minFaceSize: 0.15, // Minimum face size (0.0 to 1.0)
|
|
167
|
+
faceDetectionMode: 'accurate',
|
|
168
|
+
minFaceSize: 0.15,
|
|
115
169
|
};
|
|
116
170
|
|
|
117
171
|
const result = await analyzeImage('file:///path/to/image.jpg', options);
|
|
118
172
|
```
|
|
119
173
|
|
|
120
|
-
###
|
|
174
|
+
### Available Options
|
|
175
|
+
|
|
176
|
+
**detectFaces** (boolean, default: true)
|
|
177
|
+
Enable or disable face detection. Set to false if you only need text recognition to improve performance.
|
|
178
|
+
|
|
179
|
+
**detectPrintedText** (boolean, default: true)
|
|
180
|
+
Enable or disable text recognition. Set to false if you only need face detection to improve performance.
|
|
181
|
+
|
|
182
|
+
**faceDetectionMode** ('fast' | 'accurate', default: 'fast')
|
|
183
|
+
Choose between fast processing for real-time scenarios or accurate mode for higher quality detection. Use 'fast' for camera previews and 'accurate' for analyzing stored images.
|
|
184
|
+
|
|
185
|
+
**minFaceSize** (number, default: 0.1)
|
|
186
|
+
The minimum face size to detect, expressed as a proportion of the image's smaller dimension. Valid range is 0.0 to 1.0. Increase this value to filter out small or distant faces.
|
|
187
|
+
|
|
188
|
+
## Integration with Image Picker
|
|
189
|
+
|
|
190
|
+
This library works seamlessly with React Native image picker libraries. Here are examples using react-native-image-picker:
|
|
191
|
+
|
|
192
|
+
### Selecting from Gallery
|
|
121
193
|
|
|
122
194
|
```typescript
|
|
123
195
|
import { launchImageLibrary } from 'react-native-image-picker';
|
|
@@ -136,11 +208,41 @@ async function pickAndAnalyze() {
|
|
|
136
208
|
}
|
|
137
209
|
```
|
|
138
210
|
|
|
139
|
-
###
|
|
211
|
+
### Capturing with Camera
|
|
212
|
+
|
|
213
|
+
```typescript
|
|
214
|
+
import { launchCamera } from 'react-native-image-picker';
|
|
215
|
+
import { analyzeImage } from 'mediversal-rn-image-intelligence';
|
|
216
|
+
|
|
217
|
+
async function captureAndAnalyze() {
|
|
218
|
+
const response = await launchCamera({
|
|
219
|
+
mediaType: 'photo',
|
|
220
|
+
quality: 1,
|
|
221
|
+
});
|
|
222
|
+
|
|
223
|
+
if (response.assets && response.assets[0].uri) {
|
|
224
|
+
const result = await analyzeImage(response.assets[0].uri);
|
|
225
|
+
console.log(result);
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
## Complete Example Component
|
|
231
|
+
|
|
232
|
+
Here's a full working example of a React component that lets users select an image and displays the analysis results:
|
|
140
233
|
|
|
141
234
|
```typescript
|
|
142
235
|
import React, { useState } from 'react';
|
|
143
|
-
import {
|
|
236
|
+
import {
|
|
237
|
+
View,
|
|
238
|
+
Button,
|
|
239
|
+
Text,
|
|
240
|
+
Image,
|
|
241
|
+
StyleSheet,
|
|
242
|
+
ScrollView,
|
|
243
|
+
ActivityIndicator,
|
|
244
|
+
Alert,
|
|
245
|
+
} from 'react-native';
|
|
144
246
|
import { launchImageLibrary } from 'react-native-image-picker';
|
|
145
247
|
import {
|
|
146
248
|
analyzeImage,
|
|
@@ -153,7 +255,10 @@ export default function ImageAnalyzer() {
|
|
|
153
255
|
const [loading, setLoading] = useState(false);
|
|
154
256
|
|
|
155
257
|
const selectAndAnalyze = async () => {
|
|
156
|
-
const response = await launchImageLibrary({
|
|
258
|
+
const response = await launchImageLibrary({
|
|
259
|
+
mediaType: 'photo',
|
|
260
|
+
quality: 1,
|
|
261
|
+
});
|
|
157
262
|
|
|
158
263
|
if (response.assets && response.assets[0].uri) {
|
|
159
264
|
const uri = response.assets[0].uri;
|
|
@@ -161,9 +266,14 @@ export default function ImageAnalyzer() {
|
|
|
161
266
|
setLoading(true);
|
|
162
267
|
|
|
163
268
|
try {
|
|
164
|
-
const analysis = await analyzeImage(uri
|
|
269
|
+
const analysis = await analyzeImage(uri, {
|
|
270
|
+
detectFaces: true,
|
|
271
|
+
detectPrintedText: true,
|
|
272
|
+
faceDetectionMode: 'accurate',
|
|
273
|
+
});
|
|
165
274
|
setResult(analysis);
|
|
166
275
|
} catch (error) {
|
|
276
|
+
Alert.alert('Error', 'Failed to analyze image');
|
|
167
277
|
console.error(error);
|
|
168
278
|
} finally {
|
|
169
279
|
setLoading(false);
|
|
@@ -172,86 +282,269 @@ export default function ImageAnalyzer() {
|
|
|
172
282
|
};
|
|
173
283
|
|
|
174
284
|
return (
|
|
175
|
-
<
|
|
285
|
+
<ScrollView contentContainerStyle={styles.container}>
|
|
286
|
+
<Text style={styles.title}>Image Intelligence Demo</Text>
|
|
287
|
+
|
|
176
288
|
<Button title="Select Image" onPress={selectAndAnalyze} />
|
|
177
289
|
|
|
178
290
|
{imageUri && (
|
|
179
|
-
<Image
|
|
291
|
+
<Image
|
|
292
|
+
source={{ uri: imageUri }}
|
|
293
|
+
style={styles.image}
|
|
294
|
+
resizeMode="contain"
|
|
295
|
+
/>
|
|
180
296
|
)}
|
|
181
297
|
|
|
182
|
-
{loading &&
|
|
298
|
+
{loading && (
|
|
299
|
+
<View style={styles.loadingContainer}>
|
|
300
|
+
<ActivityIndicator size="large" color="#007AFF" />
|
|
301
|
+
<Text style={styles.loadingText}>Analyzing...</Text>
|
|
302
|
+
</View>
|
|
303
|
+
)}
|
|
304
|
+
|
|
305
|
+
{result && !loading && (
|
|
306
|
+
<View style={styles.resultsContainer}>
|
|
307
|
+
<Text style={styles.resultTitle}>Results</Text>
|
|
308
|
+
|
|
309
|
+
<Text style={styles.resultItem}>
|
|
310
|
+
Face Detected: {result.containsFace ? 'Yes' : 'No'}
|
|
311
|
+
</Text>
|
|
183
312
|
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
<Text>Faces detected: {result.containsFace ? 'Yes' : 'No'}</Text>
|
|
187
|
-
<Text>
|
|
188
|
-
Text detected: {result.containsPrintedText ? 'Yes' : 'No'}
|
|
313
|
+
<Text style={styles.resultItem}>
|
|
314
|
+
Text Detected: {result.containsPrintedText ? 'Yes' : 'No'}
|
|
189
315
|
</Text>
|
|
190
|
-
|
|
316
|
+
|
|
317
|
+
{result.faces && result.faces.length > 0 && (
|
|
318
|
+
<View style={styles.section}>
|
|
319
|
+
<Text style={styles.sectionTitle}>
|
|
320
|
+
Faces Found: {result.faces.length}
|
|
321
|
+
</Text>
|
|
322
|
+
{result.faces.map((face, index) => (
|
|
323
|
+
<View key={index} style={styles.faceInfo}>
|
|
324
|
+
<Text>Face {index + 1}:</Text>
|
|
325
|
+
<Text>
|
|
326
|
+
Smiling: {(face.smilingProbability! * 100).toFixed(0)}%
|
|
327
|
+
</Text>
|
|
328
|
+
<Text>
|
|
329
|
+
Left Eye Open:{' '}
|
|
330
|
+
{(face.leftEyeOpenProbability! * 100).toFixed(0)}%
|
|
331
|
+
</Text>
|
|
332
|
+
<Text>
|
|
333
|
+
Right Eye Open:{' '}
|
|
334
|
+
{(face.rightEyeOpenProbability! * 100).toFixed(0)}%
|
|
335
|
+
</Text>
|
|
336
|
+
</View>
|
|
337
|
+
))}
|
|
338
|
+
</View>
|
|
339
|
+
)}
|
|
340
|
+
|
|
191
341
|
{result.printedText && (
|
|
192
|
-
<
|
|
342
|
+
<View style={styles.section}>
|
|
343
|
+
<Text style={styles.sectionTitle}>Extracted Text:</Text>
|
|
344
|
+
<Text style={styles.extractedText}>{result.printedText}</Text>
|
|
345
|
+
</View>
|
|
346
|
+
)}
|
|
347
|
+
|
|
348
|
+
{result.errors && (
|
|
349
|
+
<View style={styles.errorSection}>
|
|
350
|
+
<Text style={styles.errorTitle}>Errors:</Text>
|
|
351
|
+
{result.errors.faceDetection && (
|
|
352
|
+
<Text style={styles.errorText}>
|
|
353
|
+
Face Detection: {result.errors.faceDetection}
|
|
354
|
+
</Text>
|
|
355
|
+
)}
|
|
356
|
+
{result.errors.textRecognition && (
|
|
357
|
+
<Text style={styles.errorText}>
|
|
358
|
+
Text Recognition: {result.errors.textRecognition}
|
|
359
|
+
</Text>
|
|
360
|
+
)}
|
|
361
|
+
</View>
|
|
193
362
|
)}
|
|
194
363
|
</View>
|
|
195
364
|
)}
|
|
196
|
-
</
|
|
365
|
+
</ScrollView>
|
|
197
366
|
);
|
|
198
367
|
}
|
|
368
|
+
|
|
369
|
+
const styles = StyleSheet.create({
|
|
370
|
+
container: {
|
|
371
|
+
padding: 20,
|
|
372
|
+
alignItems: 'center',
|
|
373
|
+
},
|
|
374
|
+
title: {
|
|
375
|
+
fontSize: 24,
|
|
376
|
+
fontWeight: 'bold',
|
|
377
|
+
marginBottom: 20,
|
|
378
|
+
},
|
|
379
|
+
image: {
|
|
380
|
+
width: 300,
|
|
381
|
+
height: 300,
|
|
382
|
+
marginVertical: 20,
|
|
383
|
+
},
|
|
384
|
+
loadingContainer: {
|
|
385
|
+
alignItems: 'center',
|
|
386
|
+
marginVertical: 20,
|
|
387
|
+
},
|
|
388
|
+
loadingText: {
|
|
389
|
+
marginTop: 10,
|
|
390
|
+
fontSize: 16,
|
|
391
|
+
},
|
|
392
|
+
resultsContainer: {
|
|
393
|
+
marginTop: 20,
|
|
394
|
+
padding: 15,
|
|
395
|
+
backgroundColor: '#f5f5f5',
|
|
396
|
+
borderRadius: 10,
|
|
397
|
+
width: '100%',
|
|
398
|
+
},
|
|
399
|
+
resultTitle: {
|
|
400
|
+
fontSize: 20,
|
|
401
|
+
fontWeight: 'bold',
|
|
402
|
+
marginBottom: 10,
|
|
403
|
+
},
|
|
404
|
+
resultItem: {
|
|
405
|
+
fontSize: 16,
|
|
406
|
+
marginVertical: 5,
|
|
407
|
+
},
|
|
408
|
+
section: {
|
|
409
|
+
marginTop: 15,
|
|
410
|
+
},
|
|
411
|
+
sectionTitle: {
|
|
412
|
+
fontSize: 16,
|
|
413
|
+
fontWeight: 'bold',
|
|
414
|
+
marginBottom: 8,
|
|
415
|
+
},
|
|
416
|
+
faceInfo: {
|
|
417
|
+
marginLeft: 10,
|
|
418
|
+
marginVertical: 5,
|
|
419
|
+
},
|
|
420
|
+
extractedText: {
|
|
421
|
+
fontSize: 14,
|
|
422
|
+
lineHeight: 20,
|
|
423
|
+
},
|
|
424
|
+
errorSection: {
|
|
425
|
+
marginTop: 15,
|
|
426
|
+
padding: 10,
|
|
427
|
+
backgroundColor: '#ffebee',
|
|
428
|
+
borderRadius: 5,
|
|
429
|
+
},
|
|
430
|
+
errorTitle: {
|
|
431
|
+
fontSize: 16,
|
|
432
|
+
fontWeight: 'bold',
|
|
433
|
+
color: '#c62828',
|
|
434
|
+
marginBottom: 5,
|
|
435
|
+
},
|
|
436
|
+
errorText: {
|
|
437
|
+
fontSize: 14,
|
|
438
|
+
color: '#d32f2f',
|
|
439
|
+
},
|
|
440
|
+
});
|
|
199
441
|
```
|
|
200
442
|
|
|
201
|
-
##
|
|
443
|
+
## API Reference
|
|
444
|
+
|
|
445
|
+
### analyzeImage()
|
|
202
446
|
|
|
203
|
-
|
|
447
|
+
```typescript
|
|
448
|
+
analyzeImage(imageUri: string, options?: AnalysisOptions): Promise<AnalysisResult>
|
|
449
|
+
```
|
|
204
450
|
|
|
205
|
-
|
|
451
|
+
Analyzes an image and returns the detection results.
|
|
206
452
|
|
|
207
453
|
**Parameters:**
|
|
208
454
|
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
- iOS: `file:///path/to/image.jpg`, `ph://...`, `assets-library://...`, or absolute path
|
|
212
|
-
- `options` (AnalysisOptions, optional): Configuration options
|
|
455
|
+
**imageUri** (string, required)
|
|
456
|
+
The local file URI pointing to the image you want to analyze. Supported URI formats vary by platform:
|
|
213
457
|
|
|
214
|
-
|
|
458
|
+
- Android: file://, content://, or absolute filesystem paths
|
|
459
|
+
- iOS: file://, ph://, assets-library://, or absolute filesystem paths
|
|
215
460
|
|
|
216
|
-
|
|
461
|
+
**options** (AnalysisOptions, optional)
|
|
462
|
+
Configuration object to customize the analysis behavior. See the Advanced Configuration section for available options.
|
|
217
463
|
|
|
218
|
-
|
|
464
|
+
**Returns:**
|
|
465
|
+
A Promise that resolves to an AnalysisResult object containing the detection results.
|
|
219
466
|
|
|
220
|
-
|
|
467
|
+
**Throws:**
|
|
468
|
+
An error if the image URI is invalid or if both face detection and text recognition fail.
|
|
221
469
|
|
|
222
|
-
###
|
|
470
|
+
### isAvailable()
|
|
471
|
+
|
|
472
|
+
```typescript
|
|
473
|
+
isAvailable(): Promise<boolean>
|
|
474
|
+
```
|
|
475
|
+
|
|
476
|
+
Checks whether the native modules are properly linked and available for use.
|
|
477
|
+
|
|
478
|
+
**Returns:**
|
|
479
|
+
A Promise that resolves to true if the library is ready to use, false otherwise.
|
|
480
|
+
|
|
481
|
+
This is useful for verifying that the installation was successful before attempting to analyze images.
|
|
482
|
+
|
|
483
|
+
## TypeScript Type Definitions
|
|
484
|
+
|
|
485
|
+
### AnalysisResult
|
|
223
486
|
|
|
224
487
|
```typescript
|
|
225
488
|
interface AnalysisResult {
|
|
226
489
|
containsFace: boolean;
|
|
227
490
|
containsPrintedText: boolean;
|
|
228
|
-
containsHandwrittenText: boolean;
|
|
229
491
|
faces?: FaceData[];
|
|
230
492
|
printedText?: string;
|
|
231
|
-
handwrittenText?: string;
|
|
232
493
|
errors?: {
|
|
233
494
|
faceDetection?: string;
|
|
234
495
|
textRecognition?: string;
|
|
235
|
-
handwritingRecognition?: string;
|
|
236
496
|
};
|
|
237
497
|
}
|
|
238
498
|
```
|
|
239
499
|
|
|
240
|
-
|
|
500
|
+
**containsFace**
|
|
501
|
+
Boolean indicating whether at least one face was detected in the image.
|
|
502
|
+
|
|
503
|
+
**containsPrintedText**
|
|
504
|
+
Boolean indicating whether any text was detected in the image.
|
|
505
|
+
|
|
506
|
+
**faces**
|
|
507
|
+
Optional array of FaceData objects, one for each detected face. Only present if faces were detected.
|
|
508
|
+
|
|
509
|
+
**printedText**
|
|
510
|
+
Optional string containing all the text extracted from the image. Only present if text was detected.
|
|
511
|
+
|
|
512
|
+
**errors**
|
|
513
|
+
Optional object containing error messages if either detection method encountered issues. This allows partial results - for example, face detection might succeed while text recognition fails.
|
|
514
|
+
|
|
515
|
+
### FaceData
|
|
241
516
|
|
|
242
517
|
```typescript
|
|
243
518
|
interface FaceData {
|
|
244
519
|
boundingBox: BoundingBox;
|
|
245
|
-
smilingProbability?: number;
|
|
246
|
-
leftEyeOpenProbability?: number;
|
|
247
|
-
rightEyeOpenProbability?: number;
|
|
248
|
-
headEulerAngleY?: number;
|
|
249
|
-
headEulerAngleZ?: number;
|
|
520
|
+
smilingProbability?: number;
|
|
521
|
+
leftEyeOpenProbability?: number;
|
|
522
|
+
rightEyeOpenProbability?: number;
|
|
523
|
+
headEulerAngleY?: number;
|
|
524
|
+
headEulerAngleZ?: number;
|
|
250
525
|
trackingId?: number;
|
|
251
526
|
}
|
|
252
527
|
```
|
|
253
528
|
|
|
254
|
-
|
|
529
|
+
**boundingBox**
|
|
530
|
+
The location and size of the detected face within the image.
|
|
531
|
+
|
|
532
|
+
**smilingProbability**
|
|
533
|
+
A number between 0.0 and 1.0 indicating the likelihood that the person is smiling. 0.0 means definitely not smiling, 1.0 means definitely smiling, and 0.5 indicates uncertainty.
|
|
534
|
+
|
|
535
|
+
**leftEyeOpenProbability** and **rightEyeOpenProbability**
|
|
536
|
+
Numbers between 0.0 and 1.0 indicating the likelihood that each eye is open.
|
|
537
|
+
|
|
538
|
+
**headEulerAngleY** (yaw)
|
|
539
|
+
The rotation of the head from left to right in degrees. A value of 0 means the face is looking straight at the camera, positive values mean the head is turned to the right, and negative values mean turned to the left.
|
|
540
|
+
|
|
541
|
+
**headEulerAngleZ** (roll)
|
|
542
|
+
The tilt of the head in degrees. A value of 0 means the head is upright, positive values mean tilted clockwise, and negative values mean tilted counter-clockwise.
|
|
543
|
+
|
|
544
|
+
**trackingId**
|
|
545
|
+
A unique identifier for the face, useful for tracking the same face across multiple frames in video scenarios.
|
|
546
|
+
|
|
547
|
+
### BoundingBox
|
|
255
548
|
|
|
256
549
|
```typescript
|
|
257
550
|
interface BoundingBox {
|
|
@@ -262,100 +555,208 @@ interface BoundingBox {
|
|
|
262
555
|
}
|
|
263
556
|
```
|
|
264
557
|
|
|
265
|
-
|
|
558
|
+
Represents the rectangular region containing a detected face. The coordinate system has its origin (0, 0) at the top-left corner of the image, with x increasing to the right and y increasing downward. All values are in pixels.
|
|
559
|
+
|
|
560
|
+
### AnalysisOptions
|
|
266
561
|
|
|
267
562
|
```typescript
|
|
268
563
|
interface AnalysisOptions {
|
|
269
|
-
detectFaces?: boolean;
|
|
270
|
-
detectPrintedText?: boolean;
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
564
|
+
detectFaces?: boolean;
|
|
565
|
+
detectPrintedText?: boolean;
|
|
566
|
+
faceDetectionMode?: 'fast' | 'accurate';
|
|
567
|
+
minFaceSize?: number;
|
|
568
|
+
}
|
|
569
|
+
```
|
|
570
|
+
|
|
571
|
+
All fields are optional and have sensible defaults. See the Advanced Configuration section for detailed descriptions.
|
|
572
|
+
|
|
573
|
+
## Common Use Cases
|
|
574
|
+
|
|
575
|
+
### Counting People in Group Photos
|
|
576
|
+
|
|
577
|
+
```typescript
|
|
578
|
+
const result = await analyzeImage(photoUri, {
|
|
579
|
+
detectFaces: true,
|
|
580
|
+
detectPrintedText: false,
|
|
581
|
+
});
|
|
582
|
+
|
|
583
|
+
const faceCount = result.faces?.length || 0;
|
|
584
|
+
console.log(`Found ${faceCount} people in the photo`);
|
|
585
|
+
```
|
|
586
|
+
|
|
587
|
+
### Document Scanning and Text Extraction
|
|
588
|
+
|
|
589
|
+
```typescript
|
|
590
|
+
const result = await analyzeImage(documentUri, {
|
|
591
|
+
detectFaces: false,
|
|
592
|
+
detectPrintedText: true,
|
|
593
|
+
});
|
|
594
|
+
|
|
595
|
+
if (result.printedText) {
|
|
596
|
+
await saveToDatabase(result.printedText);
|
|
274
597
|
}
|
|
275
598
|
```
|
|
276
599
|
|
|
277
|
-
|
|
600
|
+
### Selfie Validation
|
|
601
|
+
|
|
602
|
+
```typescript
|
|
603
|
+
const result = await analyzeImage(selfieUri, {
|
|
604
|
+
detectFaces: true,
|
|
605
|
+
minFaceSize: 0.3,
|
|
606
|
+
faceDetectionMode: 'accurate',
|
|
607
|
+
});
|
|
608
|
+
|
|
609
|
+
const isValidSelfie =
|
|
610
|
+
result.faces?.length === 1 && result.faces[0].smilingProbability! > 0.5;
|
|
611
|
+
```
|
|
612
|
+
|
|
613
|
+
### ID Card and Document Verification
|
|
278
614
|
|
|
279
|
-
|
|
615
|
+
```typescript
|
|
616
|
+
const result = await analyzeImage(idCardUri, {
|
|
617
|
+
detectFaces: true,
|
|
618
|
+
detectPrintedText: true,
|
|
619
|
+
faceDetectionMode: 'accurate',
|
|
620
|
+
});
|
|
280
621
|
|
|
281
|
-
|
|
622
|
+
const hasRequiredElements = result.containsFace && result.containsPrintedText;
|
|
623
|
+
```
|
|
282
624
|
|
|
283
|
-
|
|
284
|
-
analyzeImage(uri, {
|
|
285
|
-
detectPrintedText: false,
|
|
286
|
-
detectHandwrittenText: false,
|
|
287
|
-
});
|
|
288
|
-
```
|
|
625
|
+
## Performance Considerations
|
|
289
626
|
|
|
290
|
-
|
|
627
|
+
**Choose the Right Detection Mode**
|
|
628
|
+
Use 'fast' mode for real-time scenarios like camera previews where speed is more important than perfect accuracy. Use 'accurate' mode when analyzing stored images where quality matters more than speed.
|
|
291
629
|
|
|
292
|
-
|
|
630
|
+
**Disable Unused Features**
|
|
631
|
+
If you only need face detection, set `detectPrintedText: false` to improve performance. Similarly, if you only need text recognition, disable face detection.
|
|
293
632
|
|
|
294
|
-
|
|
633
|
+
**Optimize Image Size**
|
|
634
|
+
Large images take longer to process. Consider resizing images before analysis if you're working with high-resolution photos. Libraries like react-native-image-resizer can help:
|
|
295
635
|
|
|
296
|
-
|
|
636
|
+
```typescript
|
|
637
|
+
import ImageResizer from 'react-native-image-resizer';
|
|
297
638
|
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
639
|
+
const resized = await ImageResizer.createResizedImage(
|
|
640
|
+
uri,
|
|
641
|
+
1024,
|
|
642
|
+
1024,
|
|
643
|
+
'JPEG',
|
|
644
|
+
80
|
|
645
|
+
);
|
|
303
646
|
|
|
304
|
-
|
|
647
|
+
const result = await analyzeImage(resized.uri);
|
|
648
|
+
```
|
|
305
649
|
|
|
306
|
-
|
|
650
|
+
**First Run Download**
|
|
651
|
+
The first time you use the library, Google ML Kit needs to download its models (approximately 10-15 MB). This only happens once per device, but the first analysis may take longer than subsequent ones.
|
|
307
652
|
|
|
308
|
-
|
|
653
|
+
**Cache Results When Appropriate**
|
|
654
|
+
If you're analyzing the same image multiple times, consider caching the results instead of re-processing the image.
|
|
655
|
+
|
|
656
|
+
## Troubleshooting
|
|
657
|
+
|
|
658
|
+
### iOS Module Not Found
|
|
659
|
+
|
|
660
|
+
If you see "Module not found" errors on iOS, make sure you've installed the CocoaPods dependencies:
|
|
309
661
|
|
|
310
662
|
```bash
|
|
311
|
-
cd ios
|
|
663
|
+
cd ios
|
|
664
|
+
pod install
|
|
665
|
+
cd ..
|
|
666
|
+
npx react-native run-ios
|
|
312
667
|
```
|
|
313
668
|
|
|
314
|
-
### Android
|
|
669
|
+
### Android Gradle Build Errors
|
|
315
670
|
|
|
316
|
-
|
|
671
|
+
For generic Gradle build errors, try cleaning the build:
|
|
672
|
+
|
|
673
|
+
```bash
|
|
674
|
+
cd android
|
|
675
|
+
./gradlew clean
|
|
676
|
+
cd ..
|
|
677
|
+
npx react-native run-android
|
|
678
|
+
```
|
|
679
|
+
|
|
680
|
+
### Java Version Compatibility Issues
|
|
681
|
+
|
|
682
|
+
If you encounter compilation errors related to Java compatibility, ensure your `android/app/build.gradle` specifies Java 17:
|
|
317
683
|
|
|
318
684
|
```gradle
|
|
319
685
|
android {
|
|
320
686
|
compileOptions {
|
|
321
|
-
sourceCompatibility JavaVersion.
|
|
322
|
-
targetCompatibility JavaVersion.
|
|
687
|
+
sourceCompatibility JavaVersion.VERSION_17
|
|
688
|
+
targetCompatibility JavaVersion.VERSION_17
|
|
689
|
+
}
|
|
690
|
+
kotlinOptions {
|
|
691
|
+
jvmTarget = "17"
|
|
323
692
|
}
|
|
324
693
|
}
|
|
325
694
|
```
|
|
326
695
|
|
|
327
|
-
###
|
|
696
|
+
### Permission Denied Errors
|
|
328
697
|
|
|
329
|
-
|
|
330
|
-
- **iOS**: Use `file://`, `ph://`, `assets-library://`, or absolute paths
|
|
698
|
+
Remember that on Android, you need to request permissions at runtime, not just declare them in the manifest:
|
|
331
699
|
|
|
332
|
-
|
|
700
|
+
```typescript
|
|
701
|
+
import { PermissionsAndroid, Platform } from 'react-native';
|
|
702
|
+
|
|
703
|
+
if (Platform.OS === 'android') {
|
|
704
|
+
await PermissionsAndroid.request(
|
|
705
|
+
PermissionsAndroid.PERMISSIONS.READ_EXTERNAL_STORAGE
|
|
706
|
+
);
|
|
707
|
+
}
|
|
708
|
+
```
|
|
709
|
+
|
|
710
|
+
### Image URI Format Issues
|
|
711
|
+
|
|
712
|
+
Make sure you're using the correct URI format for your platform:
|
|
713
|
+
|
|
714
|
+
- Android accepts: file://, content://, or absolute paths
|
|
715
|
+
- iOS accepts: file://, ph://, assets-library://, or absolute paths
|
|
716
|
+
|
|
717
|
+
## Privacy and Security
|
|
333
718
|
|
|
334
|
-
|
|
719
|
+
This library is designed with privacy as a core principle. All image processing happens entirely on-device using Google ML Kit. Your users' images and the data extracted from them never leave their device. No internet connection is required for the library to function, and no data is transmitted to external servers.
|
|
335
720
|
|
|
336
|
-
|
|
337
|
-
- Pre-processing images to extract stroke information
|
|
338
|
-
- Using alternative OCR solutions specifically designed for handwriting
|
|
721
|
+
This makes the library GDPR compliant by default, as user data stays under their control. The library doesn't collect any analytics or telemetry, and the source code is open for review to verify these privacy guarantees.
|
|
339
722
|
|
|
340
|
-
##
|
|
723
|
+
## Technical Details
|
|
341
724
|
|
|
342
|
-
|
|
725
|
+
The library includes the following Google ML Kit components:
|
|
343
726
|
|
|
344
|
-
|
|
727
|
+
- Face Detection (Android: version 16.1.5, iOS: version 4.0.0)
|
|
728
|
+
- Text Recognition v2 (Android: version 19.0.0, iOS: version 4.0.0)
|
|
345
729
|
|
|
346
|
-
|
|
730
|
+
It's built with TurboModule specifications to be compatible with React Native's new architecture. The package includes full TypeScript type definitions and integrates with iOS via CocoaPods and Android via Gradle.
|
|
347
731
|
|
|
348
|
-
##
|
|
732
|
+
## Contributing
|
|
349
733
|
|
|
350
|
-
|
|
734
|
+
Contributions are welcome. Please read the CONTRIBUTING.md file in the repository for guidelines on how to submit issues and pull requests.
|
|
351
735
|
|
|
352
|
-
|
|
353
|
-
- [React Native](https://reactnative.dev/)
|
|
736
|
+
## License
|
|
354
737
|
|
|
355
|
-
|
|
738
|
+
This project is licensed under the MIT License. See the LICENSE file for complete terms.
|
|
356
739
|
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
740
|
+
## Author
|
|
741
|
+
|
|
742
|
+
Sushant Singh
|
|
743
|
+
|
|
744
|
+
Email: sushantbibhu@gmail.com
|
|
745
|
+
|
|
746
|
+
GitHub: @thisissushant
|
|
747
|
+
|
|
748
|
+
## Support and Community
|
|
749
|
+
|
|
750
|
+
If you need help or want to discuss the library:
|
|
751
|
+
|
|
752
|
+
- Report bugs or request features via GitHub Issues
|
|
753
|
+
- Join discussions on GitHub Discussions
|
|
754
|
+
- Contact the author directly at sushantbibhu@gmail.com
|
|
755
|
+
|
|
756
|
+
## Acknowledgments
|
|
757
|
+
|
|
758
|
+
This library is built on top of Google ML Kit and React Native. Thanks to both teams for their excellent work that makes projects like this possible.
|
|
360
759
|
|
|
361
760
|
---
|
|
761
|
+
|
|
762
|
+
If this library has been helpful for your project, consider giving it a star on GitHub to help others discover it.
|