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.
Files changed (60) hide show
  1. package/LICENSE +0 -0
  2. package/README.md +361 -0
  3. package/android/.gradle/8.9/checksums/checksums.lock +0 -0
  4. package/android/.gradle/8.9/dependencies-accessors/gc.properties +0 -0
  5. package/android/.gradle/8.9/executionHistory/executionHistory.lock +0 -0
  6. package/android/.gradle/8.9/fileChanges/last-build.bin +0 -0
  7. package/android/.gradle/8.9/fileHashes/fileHashes.lock +0 -0
  8. package/android/.gradle/8.9/gc.properties +0 -0
  9. package/android/.gradle/buildOutputCleanup/buildOutputCleanup.lock +0 -0
  10. package/android/.gradle/buildOutputCleanup/cache.properties +2 -0
  11. package/android/.gradle/vcs-1/gc.properties +0 -0
  12. package/android/build.gradle +71 -0
  13. package/android/src/main/AndroidManifest.xml +10 -0
  14. package/android/src/main/java/com/mediversalrnimagintelligence/FaceDetectionModule.kt +147 -0
  15. package/android/src/main/java/com/mediversalrnimagintelligence/HandwritingRecognitionModule.kt +74 -0
  16. package/android/src/main/java/com/mediversalrnimagintelligence/ImageIntelligencePackage.kt +20 -0
  17. package/android/src/main/java/com/mediversalrnimagintelligence/TextRecognitionModule.kt +86 -0
  18. package/ios/FaceDetectionModule.m +16 -0
  19. package/ios/FaceDetectionModule.swift +164 -0
  20. package/ios/HandwritingRecognitionModule.m +14 -0
  21. package/ios/HandwritingRecognitionModule.swift +53 -0
  22. package/ios/TextRecognitionModule.m +14 -0
  23. package/ios/TextRecognitionModule.swift +102 -0
  24. package/lib/commonjs/NativeFaceDetectionModule.js +12 -0
  25. package/lib/commonjs/NativeFaceDetectionModule.js.map +1 -0
  26. package/lib/commonjs/NativeHandwritingRecognitionModule.js +12 -0
  27. package/lib/commonjs/NativeHandwritingRecognitionModule.js.map +1 -0
  28. package/lib/commonjs/NativeTextRecognitionModule.js +12 -0
  29. package/lib/commonjs/NativeTextRecognitionModule.js.map +1 -0
  30. package/lib/commonjs/index.js +194 -0
  31. package/lib/commonjs/index.js.map +1 -0
  32. package/lib/commonjs/types.js +2 -0
  33. package/lib/commonjs/types.js.map +1 -0
  34. package/lib/module/NativeFaceDetectionModule.js +8 -0
  35. package/lib/module/NativeFaceDetectionModule.js.map +1 -0
  36. package/lib/module/NativeHandwritingRecognitionModule.js +8 -0
  37. package/lib/module/NativeHandwritingRecognitionModule.js.map +1 -0
  38. package/lib/module/NativeTextRecognitionModule.js +8 -0
  39. package/lib/module/NativeTextRecognitionModule.js.map +1 -0
  40. package/lib/module/index.js +186 -0
  41. package/lib/module/index.js.map +1 -0
  42. package/lib/module/types.js +2 -0
  43. package/lib/module/types.js.map +1 -0
  44. package/lib/typescript/NativeFaceDetectionModule.d.ts +11 -0
  45. package/lib/typescript/NativeFaceDetectionModule.d.ts.map +1 -0
  46. package/lib/typescript/NativeHandwritingRecognitionModule.d.ts +11 -0
  47. package/lib/typescript/NativeHandwritingRecognitionModule.d.ts.map +1 -0
  48. package/lib/typescript/NativeTextRecognitionModule.d.ts +11 -0
  49. package/lib/typescript/NativeTextRecognitionModule.d.ts.map +1 -0
  50. package/lib/typescript/index.d.ts +44 -0
  51. package/lib/typescript/index.d.ts.map +1 -0
  52. package/lib/typescript/types.d.ts +91 -0
  53. package/lib/typescript/types.d.ts.map +1 -0
  54. package/mediversal-rn-image-intelligence.podspec +0 -0
  55. package/package.json +157 -0
  56. package/src/NativeFaceDetectionModule.ts +18 -0
  57. package/src/NativeHandwritingRecognitionModule.ts +16 -0
  58. package/src/NativeTextRecognitionModule.ts +14 -0
  59. package/src/index.tsx +243 -0
  60. package/src/types.ts +96 -0
package/package.json ADDED
@@ -0,0 +1,157 @@
1
+ {
2
+ "name": "mediversal-rn-image-intelligence",
3
+ "version": "1.0.6",
4
+ "description": "Production-ready React Native library for intelligent image analysis using Google ML Kit (on-device)",
5
+ "main": "lib/commonjs/index.js",
6
+ "module": "lib/module/index.js",
7
+ "types": "lib/typescript/index.d.ts",
8
+ "react-native": "src/index.tsx",
9
+ "source": "src/index.tsx",
10
+ "files": [
11
+ "src",
12
+ "lib",
13
+ "android",
14
+ "ios",
15
+ "cpp",
16
+ "mediversal-rn-image-intelligence.podspec",
17
+ "!lib/typescript/example",
18
+ "!android/build",
19
+ "!ios/build",
20
+ "!**/__tests__",
21
+ "!**/__fixtures__",
22
+ "!**/__mocks__"
23
+ ],
24
+ "scripts": {
25
+ "test": "jest",
26
+ "typescript": "tsc --noEmit",
27
+ "lint": "eslint \"**/*.{js,ts,tsx}\"",
28
+ "prepare": "bob build",
29
+ "release": "release-it",
30
+ "example": "yarn --cwd example",
31
+ "pods": "cd example && pod-install --quiet",
32
+ "bootstrap": "yarn example && yarn && yarn pods"
33
+ },
34
+ "keywords": [
35
+ "react-native",
36
+ "ios",
37
+ "android",
38
+ "ml-kit",
39
+ "google-ml-kit",
40
+ "face-detection",
41
+ "text-recognition",
42
+ "ocr",
43
+ "handwriting-recognition",
44
+ "image-analysis",
45
+ "computer-vision",
46
+ "turbomodule"
47
+ ],
48
+ "repository": "https://github.com/mediversal/mediversal-rn-image-intelligence",
49
+ "author": "Mediversal <dev@mediversal.com> (https://mediversal.com)",
50
+ "license": "MIT",
51
+ "bugs": {
52
+ "url": "https://github.com/mediversal/mediversal-rn-image-intelligence/issues"
53
+ },
54
+ "homepage": "https://github.com/mediversal/mediversal-rn-image-intelligence#readme",
55
+ "publishConfig": {
56
+ "registry": "https://registry.npmjs.org/"
57
+ },
58
+ "devDependencies": {
59
+ "@babel/preset-typescript": "^7.28.5",
60
+ "@react-native-community/eslint-config": "^3.0.0",
61
+ "@types/jest": "^28.1.2",
62
+ "@types/react": "~17.0.21",
63
+ "@types/react-native": "0.70.0",
64
+ "eslint": "^8.4.1",
65
+ "eslint-config-prettier": "^8.5.0",
66
+ "eslint-plugin-prettier": "^4.0.0",
67
+ "jest": "^28.1.1",
68
+ "pod-install": "^0.1.0",
69
+ "prettier": "^2.0.5",
70
+ "react": "18.2.0",
71
+ "react-native": "0.72.0",
72
+ "react-native-builder-bob": "^0.20.0",
73
+ "release-it": "^15.0.0",
74
+ "typescript": "^4.5.2"
75
+ },
76
+ "peerDependencies": {
77
+ "react": "*",
78
+ "react-native": "*"
79
+ },
80
+ "jest": {
81
+ "preset": "react-native",
82
+ "moduleFileExtensions": [
83
+ "ts",
84
+ "tsx",
85
+ "js",
86
+ "jsx",
87
+ "json",
88
+ "node"
89
+ ],
90
+ "transformIgnorePatterns": [
91
+ "node_modules/(?!((jest-)?react-native|@react-native|@react-native-community|@react-native-js-polyfills)/)"
92
+ ],
93
+ "setupFiles": [
94
+ "<rootDir>/jest.setup.js"
95
+ ]
96
+ },
97
+ "eslintConfig": {
98
+ "root": true,
99
+ "parser": "@typescript-eslint/parser",
100
+ "parserOptions": {
101
+ "ecmaVersion": 2020,
102
+ "sourceType": "module",
103
+ "ecmaFeatures": {
104
+ "jsx": true
105
+ }
106
+ },
107
+ "plugins": [
108
+ "@typescript-eslint"
109
+ ],
110
+ "extends": [
111
+ "@react-native-community",
112
+ "prettier",
113
+ "plugin:@typescript-eslint/recommended"
114
+ ],
115
+ "rules": {
116
+ "prettier/prettier": [
117
+ "error",
118
+ {
119
+ "quoteProps": "consistent",
120
+ "singleQuote": true,
121
+ "tabWidth": 2,
122
+ "trailingComma": "es5",
123
+ "useTabs": false
124
+ }
125
+ ]
126
+ },
127
+ "ignorePatterns": [
128
+ "node_modules/",
129
+ "lib/"
130
+ ]
131
+ },
132
+ "eslintIgnore": [
133
+ "node_modules/",
134
+ "lib/"
135
+ ],
136
+ "prettier": {
137
+ "quoteProps": "consistent",
138
+ "singleQuote": true,
139
+ "tabWidth": 2,
140
+ "trailingComma": "es5",
141
+ "useTabs": false
142
+ },
143
+ "react-native-builder-bob": {
144
+ "source": "src",
145
+ "output": "lib",
146
+ "targets": [
147
+ "commonjs",
148
+ "module",
149
+ [
150
+ "typescript",
151
+ {
152
+ "project": "tsconfig.build.json"
153
+ }
154
+ ]
155
+ ]
156
+ }
157
+ }
@@ -0,0 +1,18 @@
1
+ import type { TurboModule } from 'react-native';
2
+ import { TurboModuleRegistry } from 'react-native';
3
+ import type { NativeFaceDetectionResult } from './types';
4
+
5
+ /**
6
+ * Face Detection TurboModule Specification
7
+ */
8
+ export interface Spec extends TurboModule {
9
+ detectFaces(
10
+ imageUri: string,
11
+ mode: string,
12
+ minFaceSize: number
13
+ ): Promise<NativeFaceDetectionResult>;
14
+ }
15
+
16
+ export default TurboModuleRegistry.getEnforcing<Spec>(
17
+ 'FaceDetectionModule'
18
+ ) as Spec;
@@ -0,0 +1,16 @@
1
+ import type { TurboModule } from 'react-native';
2
+ import { TurboModuleRegistry } from 'react-native';
3
+ import type { NativeHandwritingRecognitionResult } from './types';
4
+
5
+ /**
6
+ * Handwriting Recognition TurboModule Specification
7
+ */
8
+ export interface Spec extends TurboModule {
9
+ recognizeHandwriting(
10
+ imageUri: string
11
+ ): Promise<NativeHandwritingRecognitionResult>;
12
+ }
13
+
14
+ export default TurboModuleRegistry.getEnforcing<Spec>(
15
+ 'HandwritingRecognitionModule'
16
+ ) as Spec;
@@ -0,0 +1,14 @@
1
+ import type { TurboModule } from 'react-native';
2
+ import { TurboModuleRegistry } from 'react-native';
3
+ import type { NativeTextRecognitionResult } from './types';
4
+
5
+ /**
6
+ * Text Recognition TurboModule Specification
7
+ */
8
+ export interface Spec extends TurboModule {
9
+ recognizeText(imageUri: string): Promise<NativeTextRecognitionResult>;
10
+ }
11
+
12
+ export default TurboModuleRegistry.getEnforcing<Spec>(
13
+ 'TextRecognitionModule'
14
+ ) as Spec;
package/src/index.tsx ADDED
@@ -0,0 +1,243 @@
1
+ import { Platform } from 'react-native';
2
+ import FaceDetectionModule from './NativeFaceDetectionModule';
3
+ import TextRecognitionModule from './NativeTextRecognitionModule';
4
+ import HandwritingRecognitionModule from './NativeHandwritingRecognitionModule';
5
+ import type { AnalysisResult, AnalysisOptions } from './types';
6
+
7
+ // Export types for consumers
8
+ export type {
9
+ AnalysisResult,
10
+ AnalysisOptions,
11
+ FaceData,
12
+ BoundingBox,
13
+ } from './types';
14
+
15
+ /**
16
+ * Default analysis options
17
+ */
18
+ const DEFAULT_OPTIONS: Required<AnalysisOptions> = {
19
+ detectFaces: true,
20
+ detectPrintedText: true,
21
+ detectHandwrittenText: true,
22
+ faceDetectionMode: 'fast',
23
+ minFaceSize: 0.1,
24
+ };
25
+
26
+ /**
27
+ * Validates and normalizes the image URI
28
+ * @param imageUri - The image URI to validate
29
+ * @returns Normalized URI
30
+ * @throws Error if URI is invalid
31
+ */
32
+ function validateImageUri(imageUri: string): string {
33
+ if (!imageUri || typeof imageUri !== 'string') {
34
+ throw new Error('Invalid image URI: must be a non-empty string');
35
+ }
36
+
37
+ const trimmedUri = imageUri.trim();
38
+
39
+ if (trimmedUri.length === 0) {
40
+ throw new Error('Invalid image URI: must be a non-empty string');
41
+ }
42
+
43
+ // Validate URI format
44
+ if (Platform.OS === 'android') {
45
+ // Android accepts: file://, content://, or absolute paths
46
+ if (
47
+ !trimmedUri.startsWith('file://') &&
48
+ !trimmedUri.startsWith('content://') &&
49
+ !trimmedUri.startsWith('/')
50
+ ) {
51
+ throw new Error(
52
+ 'Invalid Android image URI: must start with file://, content://, or be an absolute path'
53
+ );
54
+ }
55
+ } else if (Platform.OS === 'ios') {
56
+ // iOS accepts: file://, ph://, assets-library://, or absolute paths
57
+ if (
58
+ !trimmedUri.startsWith('file://') &&
59
+ !trimmedUri.startsWith('ph://') &&
60
+ !trimmedUri.startsWith('assets-library://') &&
61
+ !trimmedUri.startsWith('/')
62
+ ) {
63
+ throw new Error(
64
+ 'Invalid iOS image URI: must start with file://, ph://, assets-library://, or be an absolute path'
65
+ );
66
+ }
67
+ }
68
+
69
+ return trimmedUri;
70
+ }
71
+
72
+ /**
73
+ * Analyzes an image using Google ML Kit on-device APIs
74
+ *
75
+ * This function performs parallel analysis using three ML Kit models:
76
+ * - Face Detection: Detects human faces and facial attributes
77
+ * - Text Recognition: Extracts printed text (OCR)
78
+ * - Digital Ink Recognition: Extracts handwritten text
79
+ *
80
+ * @param imageUri - Local file URI (e.g., "file:///path/to/image.jpg", "content://...", or absolute path)
81
+ * @param options - Configuration options for the analysis
82
+ * @returns Promise resolving to the analysis result
83
+ *
84
+ * @example
85
+ * ```typescript
86
+ * const result = await analyzeImage('file:///path/to/image.jpg');
87
+ *
88
+ * if (result.containsFace) {
89
+ * console.log(`Found ${result.faces?.length} face(s)`);
90
+ * result.faces?.forEach(face => {
91
+ * console.log('Smiling:', face.smilingProbability);
92
+ * });
93
+ * }
94
+ *
95
+ * if (result.containsPrintedText) {
96
+ * console.log('Text:', result.printedText);
97
+ * }
98
+ * ```
99
+ *
100
+ * @throws Error if imageUri is invalid or if all analyses fail
101
+ */
102
+ export async function analyzeImage(
103
+ imageUri: string,
104
+ options: AnalysisOptions = {}
105
+ ): Promise<AnalysisResult> {
106
+ // Validate input
107
+ const validatedUri = validateImageUri(imageUri);
108
+
109
+ // Merge with default options
110
+ const opts: Required<AnalysisOptions> = {
111
+ ...DEFAULT_OPTIONS,
112
+ ...options,
113
+ };
114
+
115
+ // Initialize result
116
+ const result: AnalysisResult = {
117
+ containsFace: false,
118
+ containsPrintedText: false,
119
+ containsHandwrittenText: false,
120
+ };
121
+
122
+ // Create promises array for parallel execution
123
+ const promises: Promise<void>[] = [];
124
+
125
+ // Face Detection
126
+ if (opts.detectFaces) {
127
+ promises.push(
128
+ (async () => {
129
+ try {
130
+ const faceResult = await FaceDetectionModule.detectFaces(
131
+ validatedUri,
132
+ opts.faceDetectionMode,
133
+ opts.minFaceSize
134
+ );
135
+
136
+ if (faceResult.error) {
137
+ result.errors = result.errors || {};
138
+ result.errors.faceDetection = faceResult.error;
139
+ } else if (faceResult.faces && faceResult.faces.length > 0) {
140
+ result.containsFace = true;
141
+ result.faces = faceResult.faces;
142
+ }
143
+ } catch (error) {
144
+ result.errors = result.errors || {};
145
+ result.errors.faceDetection =
146
+ error instanceof Error ? error.message : 'Unknown error';
147
+ }
148
+ })()
149
+ );
150
+ }
151
+
152
+ // Text Recognition (Printed)
153
+ if (opts.detectPrintedText) {
154
+ promises.push(
155
+ (async () => {
156
+ try {
157
+ const textResult = await TextRecognitionModule.recognizeText(
158
+ validatedUri
159
+ );
160
+
161
+ if (textResult.error) {
162
+ result.errors = result.errors || {};
163
+ result.errors.textRecognition = textResult.error;
164
+ } else if (textResult.text && textResult.text.trim().length > 0) {
165
+ result.containsPrintedText = true;
166
+ result.printedText = textResult.text;
167
+ }
168
+ } catch (error) {
169
+ result.errors = result.errors || {};
170
+ result.errors.textRecognition =
171
+ error instanceof Error ? error.message : 'Unknown error';
172
+ }
173
+ })()
174
+ );
175
+ }
176
+
177
+ // Handwriting Recognition
178
+ if (opts.detectHandwrittenText) {
179
+ promises.push(
180
+ (async () => {
181
+ try {
182
+ const handwritingResult =
183
+ await HandwritingRecognitionModule.recognizeHandwriting(
184
+ validatedUri
185
+ );
186
+
187
+ if (handwritingResult.error) {
188
+ result.errors = result.errors || {};
189
+ result.errors.handwritingRecognition = handwritingResult.error;
190
+ } else if (
191
+ handwritingResult.text &&
192
+ handwritingResult.text.trim().length > 0
193
+ ) {
194
+ result.containsHandwrittenText = true;
195
+ result.handwrittenText = handwritingResult.text;
196
+ }
197
+ } catch (error) {
198
+ result.errors = result.errors || {};
199
+ result.errors.handwritingRecognition =
200
+ error instanceof Error ? error.message : 'Unknown error';
201
+ }
202
+ })()
203
+ );
204
+ }
205
+
206
+ // Wait for all analyses to complete
207
+ await Promise.all(promises);
208
+
209
+ // If all enabled analyses failed, throw an error
210
+ const enabledCount = promises.length;
211
+ const errorCount = result.errors ? Object.keys(result.errors).length : 0;
212
+
213
+ if (enabledCount > 0 && errorCount === enabledCount) {
214
+ throw new Error(
215
+ `All image analyses failed: ${JSON.stringify(result.errors)}`
216
+ );
217
+ }
218
+
219
+ return result;
220
+ }
221
+
222
+ /**
223
+ * Check if the library is properly installed and native modules are available
224
+ * @returns Promise resolving to true if all modules are available
225
+ */
226
+ export async function isAvailable(): Promise<boolean> {
227
+ try {
228
+ // Simple check - all modules should be available
229
+ return !!(
230
+ FaceDetectionModule &&
231
+ TextRecognitionModule &&
232
+ HandwritingRecognitionModule
233
+ );
234
+ } catch {
235
+ return false;
236
+ }
237
+ }
238
+
239
+ // Default export for convenience
240
+ export default {
241
+ analyzeImage,
242
+ isAvailable,
243
+ };
package/src/types.ts ADDED
@@ -0,0 +1,96 @@
1
+ /**
2
+ * Bounding box coordinates and dimensions
3
+ */
4
+ export interface BoundingBox {
5
+ x: number;
6
+ y: number;
7
+ width: number;
8
+ height: number;
9
+ }
10
+
11
+ /**
12
+ * Face detection result with metadata
13
+ */
14
+ export interface FaceData {
15
+ /** Bounding box of the detected face */
16
+ boundingBox: BoundingBox;
17
+ /** Probability that the face is smiling (0.0 to 1.0) */
18
+ smilingProbability?: number;
19
+ /** Probability that the left eye is open (0.0 to 1.0) */
20
+ leftEyeOpenProbability?: number;
21
+ /** Probability that the right eye is open (0.0 to 1.0) */
22
+ rightEyeOpenProbability?: number;
23
+ /** Head rotation around Y-axis (yaw) in degrees */
24
+ headEulerAngleY?: number;
25
+ /** Head rotation around Z-axis (roll) in degrees */
26
+ headEulerAngleZ?: number;
27
+ /** Tracking ID for video sequences */
28
+ trackingId?: number;
29
+ }
30
+
31
+ /**
32
+ * Complete analysis result from image intelligence
33
+ */
34
+ export interface AnalysisResult {
35
+ /** True if at least one human face was detected */
36
+ containsFace: boolean;
37
+ /** True if printed text was detected */
38
+ containsPrintedText: boolean;
39
+ /** True if handwritten text was detected */
40
+ containsHandwrittenText: boolean;
41
+ /** Array of detected faces with metadata */
42
+ faces?: FaceData[];
43
+ /** Extracted printed text content */
44
+ printedText?: string;
45
+ /** Extracted handwritten text content */
46
+ handwrittenText?: string;
47
+ /** Error messages if any module failed (won't throw, for graceful degradation) */
48
+ errors?: {
49
+ faceDetection?: string;
50
+ textRecognition?: string;
51
+ handwritingRecognition?: string;
52
+ };
53
+ }
54
+
55
+ /**
56
+ * Configuration options for image analysis
57
+ */
58
+ export interface AnalysisOptions {
59
+ /** Enable face detection (default: true) */
60
+ detectFaces?: boolean;
61
+ /** Enable printed text recognition (default: true) */
62
+ detectPrintedText?: boolean;
63
+ /** Enable handwritten text recognition (default: true) */
64
+ detectHandwrittenText?: boolean;
65
+ /** Face detection performance mode: 'fast' or 'accurate' (default: 'fast') */
66
+ faceDetectionMode?: 'fast' | 'accurate';
67
+ /** Minimum face size relative to image (0.0 to 1.0, default: 0.1) */
68
+ minFaceSize?: number;
69
+ }
70
+
71
+ /**
72
+ * Internal face detection result from native module
73
+ * @internal
74
+ */
75
+ export interface NativeFaceDetectionResult {
76
+ faces: FaceData[];
77
+ error?: string;
78
+ }
79
+
80
+ /**
81
+ * Internal text recognition result from native module
82
+ * @internal
83
+ */
84
+ export interface NativeTextRecognitionResult {
85
+ text: string;
86
+ error?: string;
87
+ }
88
+
89
+ /**
90
+ * Internal handwriting recognition result from native module
91
+ * @internal
92
+ */
93
+ export interface NativeHandwritingRecognitionResult {
94
+ text: string;
95
+ error?: string;
96
+ }