@ashleysmart/react-native-vision-camera-face-detector
Version:
Frame Processor Plugin to detect faces using MLKit Vision Face Detector for React Native Vision Camera!
91 lines (90 loc) • 2.96 kB
JavaScript
import React from 'react';
import { Camera as VisionCamera, useFrameProcessor } from '@ashleysmart/react-native-vision-camera';
import { useSharedValue } from 'react-native-worklets-core';
import { detectFaces } from './FaceDetector';
/**
* Create a Worklet function that persists between re-renders.
* The returned function can be called from both a Worklet context and the JS context, but will execute on a Worklet context.
*
* @param {function} func The Worklet. Must be marked with the `'worklet'` directive.
* @param {DependencyList} dependencyList The React dependencies of this Worklet.
* @returns {WorkletType} A memoized Worklet
*/
function useWorklet(func, dependencyList) {
const worklet = React.useMemo(() => {
const context = 'VisionCamera.async';
return Worklets.createRunInContextFn(func, context);
}, dependencyList);
return worklet;
}
/**
* Vision camera wrapper
*
* @param {ComponentType} props Camera + face detection props
* @returns
*/
export const Camera = React.forwardRef(({ faceDetectionOptions, faceDetectionCallback, ...props }, ref) => {
/**
* Is there an async task already running?
*/
const isAsyncContextBusy = useSharedValue(false);
/**
* Throws logs/errors back on js thread
*/
const logOnJs = Worklets.createRunInJsFn((log, error) => {
if (error) {
console.error(log, error.message ?? JSON.stringify(error));
}
else {
console.log(log);
}
});
/**
* Async context that will handle face detection
*/
const runOnAsyncContext = useWorklet((frame) => {
'worklet';
try {
detectFaces({
frame,
callback: faceDetectionCallback,
options: faceDetectionOptions
});
}
catch (error) {
logOnJs('Execution error:', error);
}
finally {
frame.decrementRefCount();
isAsyncContextBusy.value = false;
}
}, [
faceDetectionOptions,
faceDetectionCallback
]);
/**
* Detect faces on frame on an async context without blocking camera preview
*
* @param {Frame} frame Current frame
*/
function runAsync(frame) {
'worklet';
if (isAsyncContextBusy.value)
return;
// set async context as busy
isAsyncContextBusy.value = true;
// cast to internal frame and increment ref count
const internal = frame;
internal.incrementRefCount();
// detect faces in async context
runOnAsyncContext(internal);
}
/**
* Camera frame processor
*/
const cameraFrameProcessor = useFrameProcessor((frame) => {
'worklet';
runAsync(frame);
}, [runOnAsyncContext]);
return React.createElement(VisionCamera, { ...props, ref: ref, frameProcessor: cameraFrameProcessor, pixelFormat: 'yuv' });
});