vision-camera-face-detection 1.2.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.
Files changed (46) hide show
  1. package/LICENSE +20 -0
  2. package/README.md +33 -0
  3. package/VisionCameraFaceDetection.podspec +45 -0
  4. package/android/build.gradle +106 -0
  5. package/android/gradle.properties +6 -0
  6. package/android/src/main/AndroidManifest.xml +3 -0
  7. package/android/src/main/AndroidManifestNew.xml +2 -0
  8. package/android/src/main/java/com/visioncamerafacedetection/FaceHelper.kt +112 -0
  9. package/android/src/main/java/com/visioncamerafacedetection/VisionCameraFaceDetectionModule.kt +118 -0
  10. package/android/src/main/java/com/visioncamerafacedetection/VisionCameraFaceDetectionPackage.kt +25 -0
  11. package/android/src/main/java/com/visioncamerafacedetection/VisionCameraFaceDetectionPlugin.kt +359 -0
  12. package/ios/FaceHelper.swift +238 -0
  13. package/ios/VisionCameraFaceDetection-Bridging-Header.h +6 -0
  14. package/ios/VisionCameraFaceDetectionModule.mm +19 -0
  15. package/ios/VisionCameraFaceDetectionModule.swift +105 -0
  16. package/ios/VisionCameraFaceDetectionPlugin.mm +22 -0
  17. package/ios/VisionCameraFaceDetectionPlugin.swift +341 -0
  18. package/lib/commonjs/Camera.cjs +161 -0
  19. package/lib/commonjs/Camera.cjs.map +1 -0
  20. package/lib/commonjs/FaceDetector.cjs +42 -0
  21. package/lib/commonjs/FaceDetector.cjs.map +1 -0
  22. package/lib/commonjs/Tensor.cjs +24 -0
  23. package/lib/commonjs/Tensor.cjs.map +1 -0
  24. package/lib/commonjs/index.cjs +39 -0
  25. package/lib/commonjs/index.cjs.map +1 -0
  26. package/lib/module/Camera.mjs +158 -0
  27. package/lib/module/Camera.mjs.map +1 -0
  28. package/lib/module/FaceDetector.mjs +36 -0
  29. package/lib/module/FaceDetector.mjs.map +1 -0
  30. package/lib/module/Tensor.mjs +17 -0
  31. package/lib/module/Tensor.mjs.map +1 -0
  32. package/lib/module/index.mjs +4 -0
  33. package/lib/module/index.mjs.map +1 -0
  34. package/lib/typescript/src/Camera.d.ts +17 -0
  35. package/lib/typescript/src/Camera.d.ts.map +1 -0
  36. package/lib/typescript/src/FaceDetector.d.ts +118 -0
  37. package/lib/typescript/src/FaceDetector.d.ts.map +1 -0
  38. package/lib/typescript/src/Tensor.d.ts +3 -0
  39. package/lib/typescript/src/Tensor.d.ts.map +1 -0
  40. package/lib/typescript/src/index.d.ts +4 -0
  41. package/lib/typescript/src/index.d.ts.map +1 -0
  42. package/package.json +186 -0
  43. package/src/Camera.tsx +192 -0
  44. package/src/FaceDetector.ts +161 -0
  45. package/src/Tensor.ts +27 -0
  46. package/src/index.tsx +3 -0
package/src/Camera.tsx ADDED
@@ -0,0 +1,192 @@
1
+ import React from 'react';
2
+ import {
3
+ Camera as VisionCamera,
4
+ // runAsync,
5
+ useFrameProcessor,
6
+ } from 'react-native-vision-camera';
7
+ import {
8
+ Worklets,
9
+ // useRunOnJS,
10
+ useSharedValue,
11
+ } from 'react-native-worklets-core';
12
+ import { useFaceDetector } from './FaceDetector';
13
+
14
+ // types
15
+ import type { DependencyList, ForwardedRef } from 'react';
16
+ import type {
17
+ CameraProps,
18
+ Frame,
19
+ FrameInternal,
20
+ } from 'react-native-vision-camera';
21
+ import type { Face, FaceDetectionOptions } from './FaceDetector';
22
+
23
+ type UseWorkletType = (frame: FrameInternal) => Promise<void>;
24
+
25
+ type UseRunInJSType = (
26
+ faces: Face[],
27
+ frame: Frame
28
+ ) => Promise<void | Promise<void>>;
29
+
30
+ type CallbackType = (faces: Face[], frame: Frame) => void | Promise<void>;
31
+
32
+ type ComponentType = {
33
+ faceDetectionOptions?: FaceDetectionOptions;
34
+ faceDetectionCallback: CallbackType;
35
+ } & CameraProps;
36
+
37
+ /**
38
+ * Create a Worklet function that persists between re-renders.
39
+ * The returned function can be called from both a Worklet context and the JS context, but will execute on a Worklet context.
40
+ *
41
+ * @param {function} func The Worklet. Must be marked with the `'worklet'` directive.
42
+ * @param {DependencyList} dependencyList The React dependencies of this Worklet.
43
+ * @returns {UseWorkletType} A memoized Worklet
44
+ */
45
+ function useWorklet(
46
+ func: (frame: FrameInternal) => void,
47
+ dependencyList: DependencyList
48
+ ): UseWorkletType {
49
+ const worklet = React.useMemo(() => {
50
+ const context = Worklets.defaultContext;
51
+ return context.createRunAsync(func);
52
+ // eslint-disable-next-line react-hooks/exhaustive-deps
53
+ }, dependencyList);
54
+
55
+ return worklet;
56
+ }
57
+
58
+ /**
59
+ * Create a Worklet function that runs the giver function on JS context.
60
+ * The returned function can be called from a Worklet to hop back to the JS thread.
61
+ *
62
+ * @param {function} func The Worklet. Must be marked with the `'worklet'` directive.
63
+ * @param {DependencyList} dependencyList The React dependencies of this Worklet.
64
+ * @returns {UseRunInJSType} a memoized Worklet
65
+ */
66
+ function useRunInJS(
67
+ func: CallbackType,
68
+ dependencyList: DependencyList
69
+ ): UseRunInJSType {
70
+ // eslint-disable-next-line react-hooks/exhaustive-deps
71
+ return React.useMemo(() => Worklets.createRunOnJS(func), dependencyList);
72
+ }
73
+
74
+ /**
75
+ * Vision camera wrapper
76
+ *
77
+ * @param {ComponentType} props Camera + face detection props
78
+ * @returns
79
+ */
80
+ export const Camera = React.forwardRef(
81
+ (
82
+ { faceDetectionOptions, faceDetectionCallback, ...props }: ComponentType,
83
+ ref: ForwardedRef<VisionCamera>
84
+ ) => {
85
+ const { detectFaces } = useFaceDetector(faceDetectionOptions);
86
+ /**
87
+ * Is there an async task already running?
88
+ */
89
+ const isAsyncContextBusy = useSharedValue(false);
90
+
91
+ /**
92
+ * Throws logs/errors back on js thread
93
+ */
94
+ const logOnJs = Worklets.createRunOnJS((log: string, error?: Error) => {
95
+ if (error) {
96
+ console.error(log, error.message ?? JSON.stringify(error));
97
+ } else {
98
+ console.log(log);
99
+ }
100
+ });
101
+
102
+ /**
103
+ * Runs on detection callback on js thread
104
+ */
105
+ const runOnJs = useRunInJS(faceDetectionCallback, [faceDetectionCallback]);
106
+
107
+ /**
108
+ * Async context that will handle face detection
109
+ */
110
+ const runOnAsyncContext = useWorklet(
111
+ (frame: FrameInternal) => {
112
+ 'worklet';
113
+ try {
114
+ const faces = detectFaces(frame);
115
+ // increment frame count so we can use frame on
116
+ // js side without frame processor getting stuck
117
+ frame.incrementRefCount();
118
+ runOnJs(faces, frame).finally(() => {
119
+ 'worklet';
120
+ // finally decrement frame count so it can be dropped
121
+ frame.decrementRefCount();
122
+ });
123
+ } catch (error: any) {
124
+ logOnJs('Execution error:', error);
125
+ } finally {
126
+ frame.decrementRefCount();
127
+ isAsyncContextBusy.value = false;
128
+ }
129
+ },
130
+ [detectFaces, runOnJs]
131
+ );
132
+
133
+ /**
134
+ * Detect faces on frame on an async context without blocking camera preview
135
+ *
136
+ * @param {Frame} frame Current frame
137
+ */
138
+ function runAsync(frame: Frame) {
139
+ 'worklet';
140
+ if (isAsyncContextBusy.value) return;
141
+ // set async context as busy
142
+ isAsyncContextBusy.value = true;
143
+ // cast to internal frame and increment ref count
144
+ const internal = frame as FrameInternal;
145
+ internal.incrementRefCount();
146
+ // detect faces in async context
147
+ runOnAsyncContext(internal);
148
+ }
149
+
150
+ /**
151
+ * Camera frame processor
152
+ */
153
+ const cameraFrameProcessor = useFrameProcessor(
154
+ (frame) => {
155
+ 'worklet';
156
+ runAsync(frame);
157
+ },
158
+ [runOnAsyncContext]
159
+ );
160
+
161
+ //
162
+ // use bellow when vision-camera's
163
+ // context creation issue is solved
164
+ //
165
+ // /**
166
+ // * Runs on detection callback on js thread
167
+ // */
168
+ // const runOnJs = useRunOnJS( faceDetectionCallback, [
169
+ // faceDetectionCallback
170
+ // ] )
171
+
172
+ // const cameraFrameProcessor = useFrameProcessor( ( frame ) => {
173
+ // 'worklet'
174
+ // runAsync( frame, () => {
175
+ // 'worklet'
176
+ // runOnJs(
177
+ // detectFaces( frame ),
178
+ // frame
179
+ // )
180
+ // } )
181
+ // }, [ runOnJs ] )
182
+
183
+ return (
184
+ <VisionCamera
185
+ {...props}
186
+ ref={ref}
187
+ frameProcessor={cameraFrameProcessor}
188
+ pixelFormat="yuv"
189
+ />
190
+ );
191
+ }
192
+ );
@@ -0,0 +1,161 @@
1
+ import { useMemo } from 'react';
2
+ import { VisionCameraProxy, type Frame } from 'react-native-vision-camera';
3
+
4
+ type FaceDetectorPlugin = {
5
+ /**
6
+ * Detect faces on frame
7
+ *
8
+ * @param {Frame} frame Frame to detect faces
9
+ */
10
+ detectFaces: (frame: Frame) => Face[];
11
+ };
12
+
13
+ type Point = {
14
+ x: number;
15
+ y: number;
16
+ };
17
+
18
+ export interface Face {
19
+ pitchAngle: number;
20
+ rollAngle: number;
21
+ yawAngle: number;
22
+ bounds: Bounds;
23
+ leftEyeOpenProbability: number;
24
+ rightEyeOpenProbability: number;
25
+ smilingProbability: number;
26
+ contours: Contours;
27
+ landmarks: Landmarks;
28
+ data: number[];
29
+ }
30
+
31
+ export interface Bounds {
32
+ width: number;
33
+ height: number;
34
+ x: number;
35
+ y: number;
36
+ }
37
+
38
+ export interface Contours {
39
+ FACE: Point[];
40
+ LEFT_EYEBROW_TOP: Point[];
41
+ LEFT_EYEBROW_BOTTOM: Point[];
42
+ RIGHT_EYEBROW_TOP: Point[];
43
+ RIGHT_EYEBROW_BOTTOM: Point[];
44
+ LEFT_EYE: Point[];
45
+ RIGHT_EYE: Point[];
46
+ UPPER_LIP_TOP: Point[];
47
+ UPPER_LIP_BOTTOM: Point[];
48
+ LOWER_LIP_TOP: Point[];
49
+ LOWER_LIP_BOTTOM: Point[];
50
+ NOSE_BRIDGE: Point[];
51
+ NOSE_BOTTOM: Point[];
52
+ LEFT_CHEEK: Point[];
53
+ RIGHT_CHEEK: Point[];
54
+ }
55
+
56
+ export interface Landmarks {
57
+ LEFT_CHEEK: Point;
58
+ LEFT_EAR: Point;
59
+ LEFT_EYE: Point;
60
+ MOUTH_BOTTOM: Point;
61
+ MOUTH_LEFT: Point;
62
+ MOUTH_RIGHT: Point;
63
+ NOSE_BASE: Point;
64
+ RIGHT_CHEEK: Point;
65
+ RIGHT_EAR: Point;
66
+ RIGHT_EYE: Point;
67
+ }
68
+
69
+ export interface FaceDetectionOptions {
70
+ /**
71
+ * Favor speed or accuracy when detecting faces.
72
+ *
73
+ * @default 'fast'
74
+ */
75
+ performanceMode?: 'fast' | 'accurate';
76
+
77
+ /**
78
+ * Whether to attempt to identify facial 'landmarks': eyes, ears, nose, cheeks, mouth, and so on.
79
+ *
80
+ * @default 'none'
81
+ */
82
+ landmarkMode?: 'none' | 'all';
83
+
84
+ /**
85
+ * Whether to detect the contours of facial features. Contours are detected for only the most prominent face in an image.
86
+ *
87
+ * @default 'none'
88
+ */
89
+ contourMode?: 'none' | 'all';
90
+
91
+ /**
92
+ * Whether or not to classify faces into categories such as 'smiling', and 'eyes open'.
93
+ *
94
+ * @default 'none'
95
+ */
96
+ classificationMode?: 'none' | 'all';
97
+
98
+ /**
99
+ * Sets the smallest desired face size, expressed as the ratio of the width of the head to width of the image.
100
+ *
101
+ * @default 0.15
102
+ */
103
+ minFaceSize?: number;
104
+
105
+ /**
106
+ * Whether or not to assign faces an ID, which can be used to track faces across images.
107
+ *
108
+ * Note that when contour detection is enabled, only one face is detected, so face tracking doesn't produce useful results. For this reason, and to improve detection speed, don't enable both contour detection and face tracking.
109
+ *
110
+ * @default false
111
+ */
112
+ trackingEnabled?: boolean;
113
+
114
+ /**
115
+ * Should auto scale face bounds, contour and landmarks on native side?
116
+ * This option should be disabled if you want to draw on frame using `Skia Frame Processor`.
117
+ * See [this](https://github.com/luicfrr/react-native-vision-camera-face-detector/issues/30#issuecomment-2058805546) and [this](https://github.com/luicfrr/react-native-vision-camera-face-detector/issues/35) for more details.
118
+ *
119
+ * @default false
120
+ */
121
+ autoScale?: boolean;
122
+ }
123
+
124
+ /**
125
+ * Create a new instance of face detector plugin
126
+ *
127
+ * @param {FaceDetectionOptions | undefined} options Detection options
128
+ * @returns {FaceDetectorPlugin} Plugin instance
129
+ */
130
+ function createFaceDetectorPlugin(
131
+ options?: FaceDetectionOptions
132
+ ): FaceDetectorPlugin {
133
+ const plugin = VisionCameraProxy.initFrameProcessorPlugin('detectFaces', {
134
+ ...options,
135
+ });
136
+
137
+ if (!plugin) {
138
+ throw new Error('Failed to load Frame Processor Plugin "detectFaces"!');
139
+ }
140
+
141
+ return {
142
+ detectFaces: (frame: Frame): Face[] => {
143
+ 'worklet';
144
+ // @ts-ignore
145
+ return plugin.call(frame) as Face[];
146
+ },
147
+ };
148
+ }
149
+
150
+ /**
151
+ * Use an instance of face detector plugin.
152
+ *
153
+ * @param {FaceDetectionOptions | undefined} options Detection options
154
+ * @returns {FaceDetectorPlugin} Memoized plugin instance that will be
155
+ * destroyed once the component using `useFaceDetector()` unmounts.
156
+ */
157
+ export function useFaceDetector(
158
+ options?: FaceDetectionOptions
159
+ ): FaceDetectorPlugin {
160
+ return useMemo(() => createFaceDetectorPlugin(options), [options]);
161
+ }
package/src/Tensor.ts ADDED
@@ -0,0 +1,27 @@
1
+ import { NativeModules, Platform } from 'react-native';
2
+
3
+ const LINKING_ERROR =
4
+ `The package 'vision-camera-face-detection' doesn't seem to be linked. Make sure: \n\n` +
5
+ Platform.select({ ios: "- You have run 'pod install'\n", default: '' }) +
6
+ '- You rebuilt the app after installing the package\n' +
7
+ '- You are not using Expo Go\n';
8
+
9
+ const VisionCameraFaceDetectionModule =
10
+ NativeModules.VisionCameraFaceDetectionModule
11
+ ? NativeModules.VisionCameraFaceDetectionModule
12
+ : new Proxy(
13
+ {},
14
+ {
15
+ get() {
16
+ throw new Error(LINKING_ERROR);
17
+ },
18
+ }
19
+ );
20
+
21
+ export function initTensor(modelPath: string, count?: number): Promise<string> {
22
+ return VisionCameraFaceDetectionModule.initTensor(modelPath, count);
23
+ }
24
+
25
+ export function detectFromBase64(imageString: string): Promise<any> {
26
+ return VisionCameraFaceDetectionModule.detectFromBase64(imageString);
27
+ }
package/src/index.tsx ADDED
@@ -0,0 +1,3 @@
1
+ export * from './Camera';
2
+ export * from './FaceDetector';
3
+ export * from './Tensor';