mediversal-rn-image-intelligence 1.0.6
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 +0 -0
- package/README.md +361 -0
- package/android/.gradle/8.9/checksums/checksums.lock +0 -0
- package/android/.gradle/8.9/dependencies-accessors/gc.properties +0 -0
- package/android/.gradle/8.9/executionHistory/executionHistory.lock +0 -0
- package/android/.gradle/8.9/fileChanges/last-build.bin +0 -0
- package/android/.gradle/8.9/fileHashes/fileHashes.lock +0 -0
- package/android/.gradle/8.9/gc.properties +0 -0
- package/android/.gradle/buildOutputCleanup/buildOutputCleanup.lock +0 -0
- package/android/.gradle/buildOutputCleanup/cache.properties +2 -0
- package/android/.gradle/vcs-1/gc.properties +0 -0
- package/android/build.gradle +71 -0
- package/android/src/main/AndroidManifest.xml +10 -0
- package/android/src/main/java/com/mediversalrnimagintelligence/FaceDetectionModule.kt +147 -0
- package/android/src/main/java/com/mediversalrnimagintelligence/HandwritingRecognitionModule.kt +74 -0
- package/android/src/main/java/com/mediversalrnimagintelligence/ImageIntelligencePackage.kt +20 -0
- package/android/src/main/java/com/mediversalrnimagintelligence/TextRecognitionModule.kt +86 -0
- package/ios/FaceDetectionModule.m +16 -0
- package/ios/FaceDetectionModule.swift +164 -0
- package/ios/HandwritingRecognitionModule.m +14 -0
- package/ios/HandwritingRecognitionModule.swift +53 -0
- package/ios/TextRecognitionModule.m +14 -0
- package/ios/TextRecognitionModule.swift +102 -0
- package/lib/commonjs/NativeFaceDetectionModule.js +12 -0
- package/lib/commonjs/NativeFaceDetectionModule.js.map +1 -0
- package/lib/commonjs/NativeHandwritingRecognitionModule.js +12 -0
- package/lib/commonjs/NativeHandwritingRecognitionModule.js.map +1 -0
- package/lib/commonjs/NativeTextRecognitionModule.js +12 -0
- package/lib/commonjs/NativeTextRecognitionModule.js.map +1 -0
- package/lib/commonjs/index.js +194 -0
- package/lib/commonjs/index.js.map +1 -0
- package/lib/commonjs/types.js +2 -0
- package/lib/commonjs/types.js.map +1 -0
- package/lib/module/NativeFaceDetectionModule.js +8 -0
- package/lib/module/NativeFaceDetectionModule.js.map +1 -0
- package/lib/module/NativeHandwritingRecognitionModule.js +8 -0
- package/lib/module/NativeHandwritingRecognitionModule.js.map +1 -0
- package/lib/module/NativeTextRecognitionModule.js +8 -0
- package/lib/module/NativeTextRecognitionModule.js.map +1 -0
- package/lib/module/index.js +186 -0
- package/lib/module/index.js.map +1 -0
- package/lib/module/types.js +2 -0
- package/lib/module/types.js.map +1 -0
- package/lib/typescript/NativeFaceDetectionModule.d.ts +11 -0
- package/lib/typescript/NativeFaceDetectionModule.d.ts.map +1 -0
- package/lib/typescript/NativeHandwritingRecognitionModule.d.ts +11 -0
- package/lib/typescript/NativeHandwritingRecognitionModule.d.ts.map +1 -0
- package/lib/typescript/NativeTextRecognitionModule.d.ts +11 -0
- package/lib/typescript/NativeTextRecognitionModule.d.ts.map +1 -0
- package/lib/typescript/index.d.ts +44 -0
- package/lib/typescript/index.d.ts.map +1 -0
- package/lib/typescript/types.d.ts +91 -0
- package/lib/typescript/types.d.ts.map +1 -0
- package/mediversal-rn-image-intelligence.podspec +0 -0
- package/package.json +157 -0
- package/src/NativeFaceDetectionModule.ts +18 -0
- package/src/NativeHandwritingRecognitionModule.ts +16 -0
- package/src/NativeTextRecognitionModule.ts +14 -0
- package/src/index.tsx +243 -0
- package/src/types.ts +96 -0
package/LICENSE
ADDED
|
File without changes
|
package/README.md
ADDED
|
@@ -0,0 +1,361 @@
|
|
|
1
|
+
# mediversal-rn-image-intelligence
|
|
2
|
+
|
|
3
|
+
[](https://badge.fury.io/js/mediversal-rn-image-intelligence)
|
|
4
|
+
[](https://opensource.org/licenses/MIT)
|
|
5
|
+
|
|
6
|
+
π― **Production-ready React Native library for intelligent image analysis using Google ML Kit (on-device)**
|
|
7
|
+
|
|
8
|
+
Analyze images to detect faces, extract printed text, and recognize handwritten contentβall processed locally on the device for maximum privacy and performance.
|
|
9
|
+
|
|
10
|
+
## π Features
|
|
11
|
+
|
|
12
|
+
- **Face Detection**: Detect human faces with detailed metadata (smiling probability, eye open probability, head rotation, etc.)
|
|
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
|
|
21
|
+
|
|
22
|
+
## π¦ Installation
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
npm install mediversal-rn-image-intelligence
|
|
26
|
+
# or
|
|
27
|
+
yarn add mediversal-rn-image-intelligence
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
### iOS Setup
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
cd ios && pod install
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
**Required**: Add the following to your `Info.plist` if you need camera or photo library access:
|
|
37
|
+
|
|
38
|
+
```xml
|
|
39
|
+
<key>NSCameraUsageDescription</key>
|
|
40
|
+
<string>We need access to your camera to capture images for analysis</string>
|
|
41
|
+
<key>NSPhotoLibraryUsageDescription</key>
|
|
42
|
+
<string>We need access to your photo library to analyze images</string>
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
### Android Setup
|
|
46
|
+
|
|
47
|
+
The library will automatically configure the required Google ML Kit dependencies. Ensure your `minSdkVersion` is at least 21:
|
|
48
|
+
|
|
49
|
+
```gradle
|
|
50
|
+
// android/build.gradle
|
|
51
|
+
buildscript {
|
|
52
|
+
ext {
|
|
53
|
+
minSdkVersion = 21
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
**Required**: Add permissions to your `AndroidManifest.xml` if needed:
|
|
59
|
+
|
|
60
|
+
```xml
|
|
61
|
+
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
|
|
62
|
+
<uses-permission android:name="android.permission.CAMERA" />
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
## π Usage
|
|
66
|
+
|
|
67
|
+
### Basic Example
|
|
68
|
+
|
|
69
|
+
```typescript
|
|
70
|
+
import { analyzeImage } from 'mediversal-rn-image-intelligence';
|
|
71
|
+
|
|
72
|
+
async function analyzeMyImage() {
|
|
73
|
+
try {
|
|
74
|
+
const result = await analyzeImage('file:///path/to/image.jpg');
|
|
75
|
+
|
|
76
|
+
console.log('Contains face:', result.containsFace);
|
|
77
|
+
console.log('Contains printed text:', result.containsPrintedText);
|
|
78
|
+
console.log('Contains handwritten text:', result.containsHandwrittenText);
|
|
79
|
+
|
|
80
|
+
// Access detected faces
|
|
81
|
+
if (result.faces) {
|
|
82
|
+
result.faces.forEach((face, index) => {
|
|
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
|
+
});
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// Access extracted text
|
|
92
|
+
if (result.printedText) {
|
|
93
|
+
console.log('Extracted text:', result.printedText);
|
|
94
|
+
}
|
|
95
|
+
} catch (error) {
|
|
96
|
+
console.error('Analysis failed:', error);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
### Advanced Example with Options
|
|
102
|
+
|
|
103
|
+
```typescript
|
|
104
|
+
import {
|
|
105
|
+
analyzeImage,
|
|
106
|
+
type AnalysisOptions,
|
|
107
|
+
} from 'mediversal-rn-image-intelligence';
|
|
108
|
+
|
|
109
|
+
const options: AnalysisOptions = {
|
|
110
|
+
detectFaces: true,
|
|
111
|
+
detectPrintedText: true,
|
|
112
|
+
detectHandwrittenText: false, // Skip handwriting for performance
|
|
113
|
+
faceDetectionMode: 'accurate', // 'fast' or 'accurate'
|
|
114
|
+
minFaceSize: 0.15, // Minimum face size (0.0 to 1.0)
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
const result = await analyzeImage('file:///path/to/image.jpg', options);
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
### With Image Picker
|
|
121
|
+
|
|
122
|
+
```typescript
|
|
123
|
+
import { launchImageLibrary } from 'react-native-image-picker';
|
|
124
|
+
import { analyzeImage } from 'mediversal-rn-image-intelligence';
|
|
125
|
+
|
|
126
|
+
async function pickAndAnalyze() {
|
|
127
|
+
const response = await launchImageLibrary({
|
|
128
|
+
mediaType: 'photo',
|
|
129
|
+
quality: 1,
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
if (response.assets && response.assets[0].uri) {
|
|
133
|
+
const result = await analyzeImage(response.assets[0].uri);
|
|
134
|
+
console.log(result);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
### React Component Example
|
|
140
|
+
|
|
141
|
+
```typescript
|
|
142
|
+
import React, { useState } from 'react';
|
|
143
|
+
import { View, Button, Text, Image } from 'react-native';
|
|
144
|
+
import { launchImageLibrary } from 'react-native-image-picker';
|
|
145
|
+
import {
|
|
146
|
+
analyzeImage,
|
|
147
|
+
type AnalysisResult,
|
|
148
|
+
} from 'mediversal-rn-image-intelligence';
|
|
149
|
+
|
|
150
|
+
export default function ImageAnalyzer() {
|
|
151
|
+
const [imageUri, setImageUri] = useState<string | null>(null);
|
|
152
|
+
const [result, setResult] = useState<AnalysisResult | null>(null);
|
|
153
|
+
const [loading, setLoading] = useState(false);
|
|
154
|
+
|
|
155
|
+
const selectAndAnalyze = async () => {
|
|
156
|
+
const response = await launchImageLibrary({ mediaType: 'photo' });
|
|
157
|
+
|
|
158
|
+
if (response.assets && response.assets[0].uri) {
|
|
159
|
+
const uri = response.assets[0].uri;
|
|
160
|
+
setImageUri(uri);
|
|
161
|
+
setLoading(true);
|
|
162
|
+
|
|
163
|
+
try {
|
|
164
|
+
const analysis = await analyzeImage(uri);
|
|
165
|
+
setResult(analysis);
|
|
166
|
+
} catch (error) {
|
|
167
|
+
console.error(error);
|
|
168
|
+
} finally {
|
|
169
|
+
setLoading(false);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
};
|
|
173
|
+
|
|
174
|
+
return (
|
|
175
|
+
<View style={{ padding: 20 }}>
|
|
176
|
+
<Button title="Select Image" onPress={selectAndAnalyze} />
|
|
177
|
+
|
|
178
|
+
{imageUri && (
|
|
179
|
+
<Image source={{ uri: imageUri }} style={{ width: 300, height: 300 }} />
|
|
180
|
+
)}
|
|
181
|
+
|
|
182
|
+
{loading && <Text>Analyzing...</Text>}
|
|
183
|
+
|
|
184
|
+
{result && (
|
|
185
|
+
<View style={{ marginTop: 20 }}>
|
|
186
|
+
<Text>Faces detected: {result.containsFace ? 'Yes' : 'No'}</Text>
|
|
187
|
+
<Text>
|
|
188
|
+
Text detected: {result.containsPrintedText ? 'Yes' : 'No'}
|
|
189
|
+
</Text>
|
|
190
|
+
{result.faces && <Text>Number of faces: {result.faces.length}</Text>}
|
|
191
|
+
{result.printedText && (
|
|
192
|
+
<Text>Extracted text: {result.printedText}</Text>
|
|
193
|
+
)}
|
|
194
|
+
</View>
|
|
195
|
+
)}
|
|
196
|
+
</View>
|
|
197
|
+
);
|
|
198
|
+
}
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
## π API Reference
|
|
202
|
+
|
|
203
|
+
### `analyzeImage(imageUri: string, options?: AnalysisOptions): Promise<AnalysisResult>`
|
|
204
|
+
|
|
205
|
+
Main function to analyze an image.
|
|
206
|
+
|
|
207
|
+
**Parameters:**
|
|
208
|
+
|
|
209
|
+
- `imageUri` (string): Local file URI. Supported formats:
|
|
210
|
+
- Android: `file:///path/to/image.jpg`, `content://...`, or absolute path
|
|
211
|
+
- iOS: `file:///path/to/image.jpg`, `ph://...`, `assets-library://...`, or absolute path
|
|
212
|
+
- `options` (AnalysisOptions, optional): Configuration options
|
|
213
|
+
|
|
214
|
+
**Returns:** Promise<AnalysisResult>
|
|
215
|
+
|
|
216
|
+
### `isAvailable(): Promise<boolean>`
|
|
217
|
+
|
|
218
|
+
Check if the library is properly installed and native modules are available.
|
|
219
|
+
|
|
220
|
+
## π Types
|
|
221
|
+
|
|
222
|
+
### `AnalysisResult`
|
|
223
|
+
|
|
224
|
+
```typescript
|
|
225
|
+
interface AnalysisResult {
|
|
226
|
+
containsFace: boolean;
|
|
227
|
+
containsPrintedText: boolean;
|
|
228
|
+
containsHandwrittenText: boolean;
|
|
229
|
+
faces?: FaceData[];
|
|
230
|
+
printedText?: string;
|
|
231
|
+
handwrittenText?: string;
|
|
232
|
+
errors?: {
|
|
233
|
+
faceDetection?: string;
|
|
234
|
+
textRecognition?: string;
|
|
235
|
+
handwritingRecognition?: string;
|
|
236
|
+
};
|
|
237
|
+
}
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
### `FaceData`
|
|
241
|
+
|
|
242
|
+
```typescript
|
|
243
|
+
interface FaceData {
|
|
244
|
+
boundingBox: BoundingBox;
|
|
245
|
+
smilingProbability?: number; // 0.0 to 1.0
|
|
246
|
+
leftEyeOpenProbability?: number; // 0.0 to 1.0
|
|
247
|
+
rightEyeOpenProbability?: number; // 0.0 to 1.0
|
|
248
|
+
headEulerAngleY?: number; // Yaw in degrees
|
|
249
|
+
headEulerAngleZ?: number; // Roll in degrees
|
|
250
|
+
trackingId?: number;
|
|
251
|
+
}
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
### `BoundingBox`
|
|
255
|
+
|
|
256
|
+
```typescript
|
|
257
|
+
interface BoundingBox {
|
|
258
|
+
x: number;
|
|
259
|
+
y: number;
|
|
260
|
+
width: number;
|
|
261
|
+
height: number;
|
|
262
|
+
}
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
### `AnalysisOptions`
|
|
266
|
+
|
|
267
|
+
```typescript
|
|
268
|
+
interface AnalysisOptions {
|
|
269
|
+
detectFaces?: boolean; // default: true
|
|
270
|
+
detectPrintedText?: boolean; // default: true
|
|
271
|
+
detectHandwrittenText?: boolean; // default: true
|
|
272
|
+
faceDetectionMode?: 'fast' | 'accurate'; // default: 'fast'
|
|
273
|
+
minFaceSize?: number; // 0.0 to 1.0, default: 0.1
|
|
274
|
+
}
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
## β‘ Performance Tips
|
|
278
|
+
|
|
279
|
+
1. **Use 'fast' mode for real-time applications**: The `faceDetectionMode: 'fast'` option provides better performance for live camera feeds.
|
|
280
|
+
|
|
281
|
+
2. **Disable unused features**: If you only need face detection, disable text recognition:
|
|
282
|
+
|
|
283
|
+
```typescript
|
|
284
|
+
analyzeImage(uri, {
|
|
285
|
+
detectPrintedText: false,
|
|
286
|
+
detectHandwrittenText: false,
|
|
287
|
+
});
|
|
288
|
+
```
|
|
289
|
+
|
|
290
|
+
3. **Optimize image size**: Resize large images before analysis to improve processing time.
|
|
291
|
+
|
|
292
|
+
4. **Parallel processing**: The library automatically runs all enabled analyses in parallel for maximum efficiency.
|
|
293
|
+
|
|
294
|
+
## π Privacy & Security
|
|
295
|
+
|
|
296
|
+
All image processing happens **locally on the device** using Google ML Kit's on-device APIs. No image data is ever sent to external servers, ensuring:
|
|
297
|
+
|
|
298
|
+
- β
Complete user privacy
|
|
299
|
+
- β
Offline functionality
|
|
300
|
+
- β
Faster processing
|
|
301
|
+
- β
No data usage costs
|
|
302
|
+
- β
GDPR & CCPA compliance
|
|
303
|
+
|
|
304
|
+
## π Troubleshooting
|
|
305
|
+
|
|
306
|
+
### iOS: "Module not found"
|
|
307
|
+
|
|
308
|
+
Make sure you've run `pod install`:
|
|
309
|
+
|
|
310
|
+
```bash
|
|
311
|
+
cd ios && pod install
|
|
312
|
+
```
|
|
313
|
+
|
|
314
|
+
### Android: Gradle build errors
|
|
315
|
+
|
|
316
|
+
Ensure your `minSdkVersion` is at least 21 and you're using Java 11:
|
|
317
|
+
|
|
318
|
+
```gradle
|
|
319
|
+
android {
|
|
320
|
+
compileOptions {
|
|
321
|
+
sourceCompatibility JavaVersion.VERSION_11
|
|
322
|
+
targetCompatibility JavaVersion.VERSION_11
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
```
|
|
326
|
+
|
|
327
|
+
### Image URI format errors
|
|
328
|
+
|
|
329
|
+
- **Android**: Use `file://`, `content://`, or absolute paths
|
|
330
|
+
- **iOS**: Use `file://`, `ph://`, `assets-library://`, or absolute paths
|
|
331
|
+
|
|
332
|
+
### Digital Ink Recognition limitation
|
|
333
|
+
|
|
334
|
+
The Digital Ink Recognition API is designed for real-time stroke data (from drawing apps), not static images. For handwriting recognition in static images, consider:
|
|
335
|
+
|
|
336
|
+
- Using Google Cloud Vision API (cloud-based)
|
|
337
|
+
- Pre-processing images to extract stroke information
|
|
338
|
+
- Using alternative OCR solutions specifically designed for handwriting
|
|
339
|
+
|
|
340
|
+
## π€ Contributing
|
|
341
|
+
|
|
342
|
+
Contributions are welcome! Please feel free to submit a Pull Request.
|
|
343
|
+
|
|
344
|
+
## π License
|
|
345
|
+
|
|
346
|
+
MIT Β© Mediversal
|
|
347
|
+
|
|
348
|
+
## π Acknowledgments
|
|
349
|
+
|
|
350
|
+
Built with:
|
|
351
|
+
|
|
352
|
+
- [Google ML Kit](https://developers.google.com/ml-kit)
|
|
353
|
+
- [React Native](https://reactnative.dev/)
|
|
354
|
+
|
|
355
|
+
## π Support
|
|
356
|
+
|
|
357
|
+
- π§ Email: sushantbibhu@gmail.com
|
|
358
|
+
- π Issues: [GitHub Issues](https://github.com/thisissushant/mediversal-rn-image-intelligence)
|
|
359
|
+
- π Documentation: [GitHub Wiki](https://github.com/thisissushant/mediversal-rn-image-intelligence)
|
|
360
|
+
|
|
361
|
+
---
|
|
Binary file
|
|
File without changes
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
File without changes
|
|
Binary file
|
|
File without changes
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
buildscript {
|
|
2
|
+
repositories {
|
|
3
|
+
google()
|
|
4
|
+
mavenCentral()
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
dependencies {
|
|
8
|
+
classpath 'com.android.tools.build:gradle:7.4.2'
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
apply plugin: 'com.android.library'
|
|
13
|
+
apply plugin: 'kotlin-android'
|
|
14
|
+
|
|
15
|
+
def safeExtGet(prop, fallback) {
|
|
16
|
+
rootProject.ext.has(prop) ? rootProject.ext.get(prop) : fallback
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
android {
|
|
20
|
+
compileSdkVersion safeExtGet('compileSdkVersion', 33)
|
|
21
|
+
|
|
22
|
+
namespace "com.mediversalrnimagintelligence"
|
|
23
|
+
|
|
24
|
+
defaultConfig {
|
|
25
|
+
minSdkVersion safeExtGet('minSdkVersion', 21)
|
|
26
|
+
targetSdkVersion safeExtGet('targetSdkVersion', 33)
|
|
27
|
+
versionCode 1
|
|
28
|
+
versionName "1.0.0"
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
buildTypes {
|
|
32
|
+
release {
|
|
33
|
+
minifyEnabled false
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
compileOptions {
|
|
38
|
+
sourceCompatibility JavaVersion.VERSION_11
|
|
39
|
+
targetCompatibility JavaVersion.VERSION_11
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
kotlinOptions {
|
|
43
|
+
jvmTarget = '11'
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
repositories {
|
|
48
|
+
google()
|
|
49
|
+
mavenCentral()
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
dependencies {
|
|
53
|
+
// React Native
|
|
54
|
+
implementation 'com.facebook.react:react-native:+'
|
|
55
|
+
|
|
56
|
+
// Kotlin
|
|
57
|
+
implementation "org.jetbrains.kotlin:kotlin-stdlib:1.8.0"
|
|
58
|
+
|
|
59
|
+
// Google ML Kit - Face Detection
|
|
60
|
+
implementation 'com.google.mlkit:face-detection:16.1.5'
|
|
61
|
+
|
|
62
|
+
// Google ML Kit - Text Recognition v2 (newer API)
|
|
63
|
+
implementation 'com.google.android.gms:play-services-mlkit-text-recognition:19.0.0'
|
|
64
|
+
|
|
65
|
+
// Google ML Kit - Digital Ink Recognition
|
|
66
|
+
implementation 'com.google.mlkit:digital-ink-recognition:18.1.0'
|
|
67
|
+
|
|
68
|
+
// Coroutines for async operations
|
|
69
|
+
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.4'
|
|
70
|
+
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.4'
|
|
71
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
|
2
|
+
package="com.mediversalrnimagintelligence">
|
|
3
|
+
|
|
4
|
+
<!-- Permissions for reading images -->
|
|
5
|
+
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
|
|
6
|
+
|
|
7
|
+
<!-- Optional: for camera access if needed -->
|
|
8
|
+
<uses-permission android:name="android.permission.CAMERA" />
|
|
9
|
+
|
|
10
|
+
</manifest>
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
package com.mediversalrnimagintelligence
|
|
2
|
+
|
|
3
|
+
import android.graphics.Rect
|
|
4
|
+
import android.net.Uri
|
|
5
|
+
import com.facebook.react.bridge.Arguments
|
|
6
|
+
import com.facebook.react.bridge.Promise
|
|
7
|
+
import com.facebook.react.bridge.ReactApplicationContext
|
|
8
|
+
import com.facebook.react.bridge.ReactContextBaseJavaModule
|
|
9
|
+
import com.facebook.react.bridge.ReactMethod
|
|
10
|
+
import com.facebook.react.bridge.WritableMap
|
|
11
|
+
import com.facebook.react.bridge.WritableArray
|
|
12
|
+
import com.google.mlkit.vision.common.InputImage
|
|
13
|
+
import com.google.mlkit.vision.face.Face
|
|
14
|
+
import com.google.mlkit.vision.face.FaceDetection
|
|
15
|
+
import com.google.mlkit.vision.face.FaceDetectorOptions
|
|
16
|
+
import java.io.IOException
|
|
17
|
+
|
|
18
|
+
class FaceDetectionModule(reactContext: ReactApplicationContext) :
|
|
19
|
+
ReactContextBaseJavaModule(reactContext) {
|
|
20
|
+
|
|
21
|
+
override fun getName(): String {
|
|
22
|
+
return "FaceDetectionModule"
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
@ReactMethod
|
|
26
|
+
fun detectFaces(imageUri: String, mode: String, minFaceSize: Double, promise: Promise) {
|
|
27
|
+
try {
|
|
28
|
+
// Parse the image from URI
|
|
29
|
+
val image = parseImageFromUri(imageUri)
|
|
30
|
+
|
|
31
|
+
// Configure face detector
|
|
32
|
+
val detectorOptions = when (mode) {
|
|
33
|
+
"accurate" -> FaceDetectorOptions.Builder()
|
|
34
|
+
.setPerformanceMode(FaceDetectorOptions.PERFORMANCE_MODE_ACCURATE)
|
|
35
|
+
.setLandmarkMode(FaceDetectorOptions.LANDMARK_MODE_ALL)
|
|
36
|
+
.setClassificationMode(FaceDetectorOptions.CLASSIFICATION_MODE_ALL)
|
|
37
|
+
.setMinFaceSize(minFaceSize.toFloat())
|
|
38
|
+
.enableTracking()
|
|
39
|
+
.build()
|
|
40
|
+
else -> FaceDetectorOptions.Builder()
|
|
41
|
+
.setPerformanceMode(FaceDetectorOptions.PERFORMANCE_MODE_FAST)
|
|
42
|
+
.setClassificationMode(FaceDetectorOptions.CLASSIFICATION_MODE_ALL)
|
|
43
|
+
.setMinFaceSize(minFaceSize.toFloat())
|
|
44
|
+
.build()
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
val detector = FaceDetection.getClient(detectorOptions)
|
|
48
|
+
|
|
49
|
+
// Process the image
|
|
50
|
+
detector.process(image)
|
|
51
|
+
.addOnSuccessListener { faces ->
|
|
52
|
+
val result = Arguments.createMap()
|
|
53
|
+
val facesArray = Arguments.createArray()
|
|
54
|
+
|
|
55
|
+
for (face in faces) {
|
|
56
|
+
facesArray.pushMap(convertFaceToMap(face))
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
result.putArray("faces", facesArray)
|
|
60
|
+
promise.resolve(result)
|
|
61
|
+
}
|
|
62
|
+
.addOnFailureListener { e ->
|
|
63
|
+
val result = Arguments.createMap()
|
|
64
|
+
result.putArray("faces", Arguments.createArray())
|
|
65
|
+
result.putString("error", e.message ?: "Face detection failed")
|
|
66
|
+
promise.resolve(result)
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
} catch (e: Exception) {
|
|
70
|
+
val result = Arguments.createMap()
|
|
71
|
+
result.putArray("faces", Arguments.createArray())
|
|
72
|
+
result.putString("error", e.message ?: "Failed to process image")
|
|
73
|
+
promise.resolve(result)
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
private fun parseImageFromUri(uriString: String): InputImage {
|
|
78
|
+
val context = reactApplicationContext
|
|
79
|
+
|
|
80
|
+
return try {
|
|
81
|
+
when {
|
|
82
|
+
uriString.startsWith("file://") -> {
|
|
83
|
+
val uri = Uri.parse(uriString)
|
|
84
|
+
InputImage.fromFilePath(context, uri)
|
|
85
|
+
}
|
|
86
|
+
uriString.startsWith("content://") -> {
|
|
87
|
+
val uri = Uri.parse(uriString)
|
|
88
|
+
InputImage.fromFilePath(context, uri)
|
|
89
|
+
}
|
|
90
|
+
uriString.startsWith("/") -> {
|
|
91
|
+
// Absolute file path
|
|
92
|
+
val uri = Uri.parse("file://$uriString")
|
|
93
|
+
InputImage.fromFilePath(context, uri)
|
|
94
|
+
}
|
|
95
|
+
else -> {
|
|
96
|
+
throw IOException("Unsupported URI format: $uriString")
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
} catch (e: IOException) {
|
|
100
|
+
throw IOException("Failed to load image from URI: ${e.message}")
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
private fun convertFaceToMap(face: Face): WritableMap {
|
|
105
|
+
val faceMap = Arguments.createMap()
|
|
106
|
+
|
|
107
|
+
// Bounding box
|
|
108
|
+
val boundingBox = Arguments.createMap()
|
|
109
|
+
val bounds: Rect = face.boundingBox
|
|
110
|
+
boundingBox.putInt("x", bounds.left)
|
|
111
|
+
boundingBox.putInt("y", bounds.top)
|
|
112
|
+
boundingBox.putInt("width", bounds.width())
|
|
113
|
+
boundingBox.putInt("height", bounds.height())
|
|
114
|
+
faceMap.putMap("boundingBox", boundingBox)
|
|
115
|
+
|
|
116
|
+
// Smiling probability
|
|
117
|
+
if (face.smilingProbability != null) {
|
|
118
|
+
faceMap.putDouble("smilingProbability", face.smilingProbability!!.toDouble())
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// Left eye open probability
|
|
122
|
+
if (face.leftEyeOpenProbability != null) {
|
|
123
|
+
faceMap.putDouble("leftEyeOpenProbability", face.leftEyeOpenProbability!!.toDouble())
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// Right eye open probability
|
|
127
|
+
if (face.rightEyeOpenProbability != null) {
|
|
128
|
+
faceMap.putDouble("rightEyeOpenProbability", face.rightEyeOpenProbability!!.toDouble())
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// Head Euler angles
|
|
132
|
+
if (face.headEulerAngleY != null) {
|
|
133
|
+
faceMap.putDouble("headEulerAngleY", face.headEulerAngleY!!.toDouble())
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
if (face.headEulerAngleZ != null) {
|
|
137
|
+
faceMap.putDouble("headEulerAngleZ", face.headEulerAngleZ!!.toDouble())
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// Tracking ID
|
|
141
|
+
if (face.trackingId != null) {
|
|
142
|
+
faceMap.putInt("trackingId", face.trackingId!!)
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
return faceMap
|
|
146
|
+
}
|
|
147
|
+
}
|
package/android/src/main/java/com/mediversalrnimagintelligence/HandwritingRecognitionModule.kt
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
package com.mediversalrnimagintelligence
|
|
2
|
+
|
|
3
|
+
import android.graphics.Bitmap
|
|
4
|
+
import android.graphics.BitmapFactory
|
|
5
|
+
import android.net.Uri
|
|
6
|
+
import com.facebook.react.bridge.Arguments
|
|
7
|
+
import com.facebook.react.bridge.Promise
|
|
8
|
+
import com.facebook.react.bridge.ReactApplicationContext
|
|
9
|
+
import com.facebook.react.bridge.ReactContextBaseJavaModule
|
|
10
|
+
import com.facebook.react.bridge.ReactMethod
|
|
11
|
+
import com.facebook.react.bridge.WritableMap
|
|
12
|
+
import com.google.mlkit.vision.digitalink.DigitalInkRecognition
|
|
13
|
+
import com.google.mlkit.vision.digitalink.DigitalInkRecognitionModel
|
|
14
|
+
import com.google.mlkit.vision.digitalink.DigitalInkRecognitionModelIdentifier
|
|
15
|
+
import com.google.mlkit.vision.digitalink.DigitalInkRecognizer
|
|
16
|
+
import com.google.mlkit.vision.digitalink.DigitalInkRecognizerOptions
|
|
17
|
+
import com.google.mlkit.vision.digitalink.Ink
|
|
18
|
+
import java.io.IOException
|
|
19
|
+
import java.io.InputStream
|
|
20
|
+
|
|
21
|
+
class HandwritingRecognitionModule(reactContext: ReactApplicationContext) :
|
|
22
|
+
ReactContextBaseJavaModule(reactContext) {
|
|
23
|
+
|
|
24
|
+
override fun getName(): String {
|
|
25
|
+
return "HandwritingRecognitionModule"
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
@ReactMethod
|
|
29
|
+
fun recognizeHandwriting(imageUri: String, promise: Promise) {
|
|
30
|
+
try {
|
|
31
|
+
// Note: Digital Ink Recognition in ML Kit is designed for real-time stroke data,
|
|
32
|
+
// not static images. For production use with static images, you would need to:
|
|
33
|
+
// 1. Use a different API (like Cloud Vision API's handwriting detection)
|
|
34
|
+
// 2. Or pre-process the image to extract strokes
|
|
35
|
+
//
|
|
36
|
+
// For this implementation, we'll provide a graceful fallback indicating
|
|
37
|
+
// that handwriting recognition from static images is not fully supported
|
|
38
|
+
// by the on-device Digital Ink API.
|
|
39
|
+
|
|
40
|
+
val result = Arguments.createMap()
|
|
41
|
+
result.putString("text", "")
|
|
42
|
+
result.putString(
|
|
43
|
+
"error",
|
|
44
|
+
"Digital Ink Recognition requires stroke data, not static images. " +
|
|
45
|
+
"For handwriting in static images, consider using Cloud Vision API or " +
|
|
46
|
+
"preprocessing to extract stroke information."
|
|
47
|
+
)
|
|
48
|
+
promise.resolve(result)
|
|
49
|
+
|
|
50
|
+
// Alternative implementation note:
|
|
51
|
+
// If you have stroke data (from a drawing canvas), the proper implementation would be:
|
|
52
|
+
// 1. Create an Ink object from strokes
|
|
53
|
+
// 2. Get recognizer with appropriate model
|
|
54
|
+
// 3. Call recognizer.recognize(ink)
|
|
55
|
+
//
|
|
56
|
+
// Example (commented out as it requires stroke data):
|
|
57
|
+
// val modelIdentifier = DigitalInkRecognitionModelIdentifier.fromLanguageTag("en-US")
|
|
58
|
+
// val model = DigitalInkRecognitionModel.builder(modelIdentifier!!).build()
|
|
59
|
+
// val recognizer = DigitalInkRecognition.getClient(
|
|
60
|
+
// DigitalInkRecognizerOptions.builder(model).build()
|
|
61
|
+
// )
|
|
62
|
+
// recognizer.recognize(ink)
|
|
63
|
+
// .addOnSuccessListener { result ->
|
|
64
|
+
// // Process candidates
|
|
65
|
+
// }
|
|
66
|
+
|
|
67
|
+
} catch (e: Exception) {
|
|
68
|
+
val result = Arguments.createMap()
|
|
69
|
+
result.putString("text", "")
|
|
70
|
+
result.putString("error", e.message ?: "Handwriting recognition failed")
|
|
71
|
+
promise.resolve(result)
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|