@rick427/react-native-liveness 0.1.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.
- package/LICENSE +20 -0
- package/LivenessCamera.podspec +26 -0
- package/README.md +167 -0
- package/android/build.gradle +80 -0
- package/android/src/main/AndroidManifest.xml +2 -0
- package/android/src/main/java/com/livenesscamera/LivenessCameraPackage.kt +28 -0
- package/android/src/main/java/com/livenesscamera/LivenessCameraPlugin.kt +63 -0
- package/ios/LivenessCameraPlugin-Bridging-Header.h +3 -0
- package/ios/LivenessCameraPlugin.m +8 -0
- package/ios/LivenessCameraPlugin.swift +69 -0
- package/lib/module/LivenessCamera.js +283 -0
- package/lib/module/LivenessCamera.js.map +1 -0
- package/lib/module/LivenessDetector.js +23 -0
- package/lib/module/LivenessDetector.js.map +1 -0
- package/lib/module/index.js +5 -0
- package/lib/module/index.js.map +1 -0
- package/lib/module/livenessScoring.js +58 -0
- package/lib/module/livenessScoring.js.map +1 -0
- package/lib/module/package.json +1 -0
- package/lib/module/types.js +4 -0
- package/lib/module/types.js.map +1 -0
- package/lib/module/useLivenessCamera.js +167 -0
- package/lib/module/useLivenessCamera.js.map +1 -0
- package/lib/typescript/package.json +1 -0
- package/lib/typescript/src/LivenessCamera.d.ts +3 -0
- package/lib/typescript/src/LivenessCamera.d.ts.map +1 -0
- package/lib/typescript/src/LivenessDetector.d.ts +8 -0
- package/lib/typescript/src/LivenessDetector.d.ts.map +1 -0
- package/lib/typescript/src/index.d.ts +4 -0
- package/lib/typescript/src/index.d.ts.map +1 -0
- package/lib/typescript/src/livenessScoring.d.ts +11 -0
- package/lib/typescript/src/livenessScoring.d.ts.map +1 -0
- package/lib/typescript/src/types.d.ts +61 -0
- package/lib/typescript/src/types.d.ts.map +1 -0
- package/lib/typescript/src/useLivenessCamera.d.ts +21 -0
- package/lib/typescript/src/useLivenessCamera.d.ts.map +1 -0
- package/package.json +120 -0
- package/src/LivenessCamera.tsx +284 -0
- package/src/LivenessDetector.ts +34 -0
- package/src/index.ts +9 -0
- package/src/livenessScoring.ts +81 -0
- package/src/types.ts +88 -0
- package/src/useLivenessCamera.ts +206 -0
|
@@ -0,0 +1,283 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
import { useCallback, useEffect, useRef, useState } from 'react';
|
|
4
|
+
import { Animated, StyleSheet, Text, View } from 'react-native';
|
|
5
|
+
import { Camera, useCameraDevice, useCameraPermission } from 'react-native-vision-camera';
|
|
6
|
+
import { Circle, Ellipse, Path, Svg } from 'react-native-svg';
|
|
7
|
+
import { useLivenessCamera } from "./useLivenessCamera.js";
|
|
8
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
9
|
+
const OVAL_H_RATIO = 0.55;
|
|
10
|
+
const OVAL_V_RATIO = 0.72;
|
|
11
|
+
const STROKE_WIDTH = 3;
|
|
12
|
+
// Cubic bezier approximation constant for a smooth ellipse
|
|
13
|
+
const K = 0.5523;
|
|
14
|
+
function getOvalColor(state) {
|
|
15
|
+
switch (state) {
|
|
16
|
+
case 'confirmed':
|
|
17
|
+
case 'countdown':
|
|
18
|
+
case 'capturing':
|
|
19
|
+
case 'done':
|
|
20
|
+
return '#4CAF50';
|
|
21
|
+
default:
|
|
22
|
+
return '#FFFFFF';
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Returns an SVG path string tracing an ellipse (cx, cy, rx, ry) using
|
|
28
|
+
* cubic bezier curves. Used inside a compound path with fillRule="evenodd"
|
|
29
|
+
* to punch a transparent hole through the dark scrim.
|
|
30
|
+
*/
|
|
31
|
+
function ellipsePath(cx, cy, rx, ry) {
|
|
32
|
+
return [`M ${cx + rx} ${cy}`, `C ${cx + rx} ${cy - ry * K} ${cx + rx * K} ${cy - ry} ${cx} ${cy - ry}`, `C ${cx - rx * K} ${cy - ry} ${cx - rx} ${cy - ry * K} ${cx - rx} ${cy}`, `C ${cx - rx} ${cy + ry * K} ${cx - rx * K} ${cy + ry} ${cx} ${cy + ry}`, `C ${cx + rx * K} ${cy + ry} ${cx + rx} ${cy + ry * K} ${cx + rx} ${cy}`, 'Z'].join(' ');
|
|
33
|
+
}
|
|
34
|
+
function OvalOverlay({
|
|
35
|
+
width,
|
|
36
|
+
height,
|
|
37
|
+
state
|
|
38
|
+
}) {
|
|
39
|
+
if (width === 0 || height === 0) return null;
|
|
40
|
+
const cx = width / 2;
|
|
41
|
+
const cy = height / 2;
|
|
42
|
+
const rx = width * OVAL_H_RATIO / 2;
|
|
43
|
+
const ry = height * OVAL_V_RATIO / 2;
|
|
44
|
+
const color = getOvalColor(state);
|
|
45
|
+
|
|
46
|
+
// Compound path: outer rect + oval. evenodd fill rule makes the oval transparent.
|
|
47
|
+
const scrimD = `M0 0H${width}V${height}H0Z ${ellipsePath(cx, cy, rx, ry)}`;
|
|
48
|
+
const showDot = state === 'confirmed' || state === 'countdown' || state === 'capturing';
|
|
49
|
+
return /*#__PURE__*/_jsxs(Svg, {
|
|
50
|
+
style: StyleSheet.absoluteFill,
|
|
51
|
+
width: width,
|
|
52
|
+
height: height,
|
|
53
|
+
children: [/*#__PURE__*/_jsx(Path, {
|
|
54
|
+
d: scrimD,
|
|
55
|
+
fill: "rgba(0,0,0,0.55)",
|
|
56
|
+
fillRule: "evenodd"
|
|
57
|
+
}), /*#__PURE__*/_jsx(Ellipse, {
|
|
58
|
+
cx: cx,
|
|
59
|
+
cy: cy,
|
|
60
|
+
rx: rx,
|
|
61
|
+
ry: ry,
|
|
62
|
+
fill: "none",
|
|
63
|
+
stroke: color,
|
|
64
|
+
strokeWidth: STROKE_WIDTH
|
|
65
|
+
}), showDot && /*#__PURE__*/_jsx(Circle, {
|
|
66
|
+
cx: cx,
|
|
67
|
+
cy: cy - ry - 8,
|
|
68
|
+
r: 5,
|
|
69
|
+
fill: color
|
|
70
|
+
})]
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
function CountdownBubble({
|
|
74
|
+
value
|
|
75
|
+
}) {
|
|
76
|
+
// key={countdown} in the parent remounts this component on every tick,
|
|
77
|
+
// so [] deps are correct — each mount runs a fresh animation.
|
|
78
|
+
const scale = useRef(new Animated.Value(0)).current;
|
|
79
|
+
const opacity = useRef(new Animated.Value(0)).current;
|
|
80
|
+
useEffect(() => {
|
|
81
|
+
Animated.parallel([Animated.sequence([Animated.spring(scale, {
|
|
82
|
+
toValue: 1.2,
|
|
83
|
+
stiffness: 200,
|
|
84
|
+
damping: 6,
|
|
85
|
+
useNativeDriver: true
|
|
86
|
+
}), Animated.spring(scale, {
|
|
87
|
+
toValue: 1.0,
|
|
88
|
+
stiffness: 150,
|
|
89
|
+
damping: 8,
|
|
90
|
+
useNativeDriver: true
|
|
91
|
+
})]), Animated.timing(opacity, {
|
|
92
|
+
toValue: 1,
|
|
93
|
+
duration: 150,
|
|
94
|
+
useNativeDriver: true
|
|
95
|
+
})]).start();
|
|
96
|
+
return () => {
|
|
97
|
+
Animated.timing(opacity, {
|
|
98
|
+
toValue: 0,
|
|
99
|
+
duration: 200,
|
|
100
|
+
useNativeDriver: true
|
|
101
|
+
}).start();
|
|
102
|
+
};
|
|
103
|
+
}, []); // eslint-disable-line react-hooks/exhaustive-deps
|
|
104
|
+
|
|
105
|
+
return /*#__PURE__*/_jsx(Animated.View, {
|
|
106
|
+
style: [styles.countdownBubble, {
|
|
107
|
+
opacity,
|
|
108
|
+
transform: [{
|
|
109
|
+
scale
|
|
110
|
+
}]
|
|
111
|
+
}],
|
|
112
|
+
children: /*#__PURE__*/_jsx(Text, {
|
|
113
|
+
style: styles.countdownText,
|
|
114
|
+
children: value
|
|
115
|
+
})
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
export function LivenessCamera({
|
|
119
|
+
onCapture,
|
|
120
|
+
onLivenessConfirmed,
|
|
121
|
+
onError,
|
|
122
|
+
countdownFrom = 3,
|
|
123
|
+
livenessThreshold = 0.75,
|
|
124
|
+
confirmFrames = 15,
|
|
125
|
+
soundEnabled = true,
|
|
126
|
+
style
|
|
127
|
+
}) {
|
|
128
|
+
const {
|
|
129
|
+
hasPermission,
|
|
130
|
+
requestPermission
|
|
131
|
+
} = useCameraPermission();
|
|
132
|
+
const device = useCameraDevice('front');
|
|
133
|
+
const cameraRef = useRef(null);
|
|
134
|
+
const [containerSize, setContainerSize] = useState({
|
|
135
|
+
width: 0,
|
|
136
|
+
height: 0
|
|
137
|
+
});
|
|
138
|
+
const {
|
|
139
|
+
frameProcessor,
|
|
140
|
+
livenessState,
|
|
141
|
+
countdown,
|
|
142
|
+
feedback
|
|
143
|
+
} = useLivenessCamera({
|
|
144
|
+
livenessThreshold,
|
|
145
|
+
confirmFrames,
|
|
146
|
+
countdownFrom,
|
|
147
|
+
soundEnabled,
|
|
148
|
+
cameraRef,
|
|
149
|
+
onCapture,
|
|
150
|
+
onLivenessConfirmed,
|
|
151
|
+
onError
|
|
152
|
+
});
|
|
153
|
+
const handleLayout = useCallback(e => {
|
|
154
|
+
const {
|
|
155
|
+
width,
|
|
156
|
+
height
|
|
157
|
+
} = e.nativeEvent.layout;
|
|
158
|
+
setContainerSize({
|
|
159
|
+
width,
|
|
160
|
+
height
|
|
161
|
+
});
|
|
162
|
+
}, []);
|
|
163
|
+
useEffect(() => {
|
|
164
|
+
if (!hasPermission) {
|
|
165
|
+
requestPermission().catch(() => {
|
|
166
|
+
onError?.(new Error('Camera permission denied'));
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
}, [hasPermission, requestPermission, onError]);
|
|
170
|
+
if (!hasPermission) {
|
|
171
|
+
return /*#__PURE__*/_jsx(View, {
|
|
172
|
+
style: [styles.root, style, styles.centered],
|
|
173
|
+
children: /*#__PURE__*/_jsx(Text, {
|
|
174
|
+
style: styles.permissionText,
|
|
175
|
+
children: "Camera permission required"
|
|
176
|
+
})
|
|
177
|
+
});
|
|
178
|
+
}
|
|
179
|
+
if (!device) {
|
|
180
|
+
return /*#__PURE__*/_jsx(View, {
|
|
181
|
+
style: [styles.root, style, styles.centered],
|
|
182
|
+
children: /*#__PURE__*/_jsx(Text, {
|
|
183
|
+
style: styles.permissionText,
|
|
184
|
+
children: "No front camera found"
|
|
185
|
+
})
|
|
186
|
+
});
|
|
187
|
+
}
|
|
188
|
+
return /*#__PURE__*/_jsxs(View, {
|
|
189
|
+
style: [styles.root, style],
|
|
190
|
+
onLayout: handleLayout,
|
|
191
|
+
children: [/*#__PURE__*/_jsx(Camera, {
|
|
192
|
+
ref: cameraRef,
|
|
193
|
+
style: StyleSheet.absoluteFill,
|
|
194
|
+
device: device,
|
|
195
|
+
isActive: livenessState !== 'done' && livenessState !== 'error',
|
|
196
|
+
frameProcessor: frameProcessor,
|
|
197
|
+
photo: true,
|
|
198
|
+
pixelFormat: "yuv"
|
|
199
|
+
}), /*#__PURE__*/_jsx(OvalOverlay, {
|
|
200
|
+
width: containerSize.width,
|
|
201
|
+
height: containerSize.height,
|
|
202
|
+
state: livenessState
|
|
203
|
+
}), livenessState !== 'done' && /*#__PURE__*/_jsx(View, {
|
|
204
|
+
style: styles.feedbackContainer,
|
|
205
|
+
children: /*#__PURE__*/_jsx(Text, {
|
|
206
|
+
style: styles.feedbackText,
|
|
207
|
+
children: feedback
|
|
208
|
+
})
|
|
209
|
+
}), livenessState === 'countdown' && countdown !== null && /*#__PURE__*/_jsx(View, {
|
|
210
|
+
style: styles.countdownContainer,
|
|
211
|
+
children: /*#__PURE__*/_jsx(CountdownBubble, {
|
|
212
|
+
value: countdown
|
|
213
|
+
}, countdown)
|
|
214
|
+
}), livenessState === 'capturing' && /*#__PURE__*/_jsx(View, {
|
|
215
|
+
style: styles.captureFlash,
|
|
216
|
+
pointerEvents: "none"
|
|
217
|
+
})]
|
|
218
|
+
});
|
|
219
|
+
}
|
|
220
|
+
const styles = StyleSheet.create({
|
|
221
|
+
root: {
|
|
222
|
+
flex: 1,
|
|
223
|
+
backgroundColor: '#000',
|
|
224
|
+
overflow: 'hidden'
|
|
225
|
+
},
|
|
226
|
+
centered: {
|
|
227
|
+
justifyContent: 'center',
|
|
228
|
+
alignItems: 'center'
|
|
229
|
+
},
|
|
230
|
+
permissionText: {
|
|
231
|
+
color: '#fff',
|
|
232
|
+
fontSize: 16,
|
|
233
|
+
textAlign: 'center',
|
|
234
|
+
paddingHorizontal: 24
|
|
235
|
+
},
|
|
236
|
+
feedbackContainer: {
|
|
237
|
+
position: 'absolute',
|
|
238
|
+
bottom: '14%',
|
|
239
|
+
left: 0,
|
|
240
|
+
right: 0,
|
|
241
|
+
alignItems: 'center',
|
|
242
|
+
paddingHorizontal: 16
|
|
243
|
+
},
|
|
244
|
+
feedbackText: {
|
|
245
|
+
color: '#fff',
|
|
246
|
+
fontSize: 16,
|
|
247
|
+
fontWeight: '600',
|
|
248
|
+
textAlign: 'center',
|
|
249
|
+
textShadowColor: 'rgba(0,0,0,0.8)',
|
|
250
|
+
textShadowOffset: {
|
|
251
|
+
width: 0,
|
|
252
|
+
height: 1
|
|
253
|
+
},
|
|
254
|
+
textShadowRadius: 4
|
|
255
|
+
},
|
|
256
|
+
countdownContainer: {
|
|
257
|
+
...StyleSheet.absoluteFillObject,
|
|
258
|
+
justifyContent: 'center',
|
|
259
|
+
alignItems: 'center'
|
|
260
|
+
},
|
|
261
|
+
countdownBubble: {
|
|
262
|
+
width: 96,
|
|
263
|
+
height: 96,
|
|
264
|
+
borderRadius: 48,
|
|
265
|
+
backgroundColor: 'rgba(255,255,255,0.15)',
|
|
266
|
+
borderWidth: 2,
|
|
267
|
+
borderColor: '#fff',
|
|
268
|
+
justifyContent: 'center',
|
|
269
|
+
alignItems: 'center'
|
|
270
|
+
},
|
|
271
|
+
countdownText: {
|
|
272
|
+
color: '#fff',
|
|
273
|
+
fontSize: 52,
|
|
274
|
+
fontWeight: '700',
|
|
275
|
+
lineHeight: 60
|
|
276
|
+
},
|
|
277
|
+
captureFlash: {
|
|
278
|
+
...StyleSheet.absoluteFillObject,
|
|
279
|
+
backgroundColor: '#fff',
|
|
280
|
+
opacity: 0.4
|
|
281
|
+
}
|
|
282
|
+
});
|
|
283
|
+
//# sourceMappingURL=LivenessCamera.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":["useCallback","useEffect","useRef","useState","Animated","StyleSheet","Text","View","Camera","useCameraDevice","useCameraPermission","Circle","Ellipse","Path","Svg","useLivenessCamera","jsx","_jsx","jsxs","_jsxs","OVAL_H_RATIO","OVAL_V_RATIO","STROKE_WIDTH","K","getOvalColor","state","ellipsePath","cx","cy","rx","ry","join","OvalOverlay","width","height","color","scrimD","showDot","style","absoluteFill","children","d","fill","fillRule","stroke","strokeWidth","r","CountdownBubble","value","scale","Value","current","opacity","parallel","sequence","spring","toValue","stiffness","damping","useNativeDriver","timing","duration","start","styles","countdownBubble","transform","countdownText","LivenessCamera","onCapture","onLivenessConfirmed","onError","countdownFrom","livenessThreshold","confirmFrames","soundEnabled","hasPermission","requestPermission","device","cameraRef","containerSize","setContainerSize","frameProcessor","livenessState","countdown","feedback","handleLayout","e","nativeEvent","layout","catch","Error","root","centered","permissionText","onLayout","ref","isActive","photo","pixelFormat","feedbackContainer","feedbackText","countdownContainer","captureFlash","pointerEvents","create","flex","backgroundColor","overflow","justifyContent","alignItems","fontSize","textAlign","paddingHorizontal","position","bottom","left","right","fontWeight","textShadowColor","textShadowOffset","textShadowRadius","absoluteFillObject","borderRadius","borderWidth","borderColor","lineHeight"],"sourceRoot":"../../src","sources":["LivenessCamera.tsx"],"mappings":"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","ignoreList":[]}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
import { useMemo } from 'react';
|
|
4
|
+
import { VisionCameraProxy } from 'react-native-vision-camera';
|
|
5
|
+
function createPlugin() {
|
|
6
|
+
const plugin = VisionCameraProxy.initFrameProcessorPlugin('detectLiveness', {});
|
|
7
|
+
if (!plugin) {
|
|
8
|
+
throw new Error('[LivenessCamera] Frame Processor Plugin "detectLiveness" not found. ' + 'Make sure the native module is linked correctly.');
|
|
9
|
+
}
|
|
10
|
+
return {
|
|
11
|
+
detectLiveness: frame => {
|
|
12
|
+
'worklet';
|
|
13
|
+
|
|
14
|
+
const result = plugin.call(frame);
|
|
15
|
+
// plugin.call returns a loosely-typed BasicParameterType — cast via unknown
|
|
16
|
+
return result;
|
|
17
|
+
}
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
export function useLivenessPlugin() {
|
|
21
|
+
return useMemo(() => createPlugin(), []);
|
|
22
|
+
}
|
|
23
|
+
//# sourceMappingURL=LivenessDetector.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":["useMemo","VisionCameraProxy","createPlugin","plugin","initFrameProcessorPlugin","Error","detectLiveness","frame","result","call","useLivenessPlugin"],"sourceRoot":"../../src","sources":["LivenessDetector.ts"],"mappings":";;AAAA,SAASA,OAAO,QAAQ,OAAO;AAC/B,SAASC,iBAAiB,QAAoB,4BAA4B;AAO1E,SAASC,YAAYA,CAAA,EAAmB;EACtC,MAAMC,MAAM,GAAGF,iBAAiB,CAACG,wBAAwB,CACvD,gBAAgB,EAChB,CAAC,CACH,CAAC;EAED,IAAI,CAACD,MAAM,EAAE;IACX,MAAM,IAAIE,KAAK,CACb,sEAAsE,GACpE,kDACJ,CAAC;EACH;EAEA,OAAO;IACLC,cAAc,EAAGC,KAAY,IAAsB;MACjD,SAAS;;MACT,MAAMC,MAAM,GAAGL,MAAM,CAACM,IAAI,CAACF,KAAK,CAAC;MACjC;MACA,OAAOC,MAAM;IACf;EACF,CAAC;AACH;AAEA,OAAO,SAASE,iBAAiBA,CAAA,EAAmB;EAClD,OAAOV,OAAO,CAAC,MAAME,YAAY,CAAC,CAAC,EAAE,EAAE,CAAC;AAC1C","ignoreList":[]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":["LivenessCamera","useLivenessCamera"],"sourceRoot":"../../src","sources":["index.ts"],"mappings":";;AAAA,SAASA,cAAc,QAAQ,qBAAkB;AACjD,SAASC,iBAAiB,QAAQ,wBAAqB","ignoreList":[]}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
const WEIGHTS = {
|
|
4
|
+
faceDetected: 0.2,
|
|
5
|
+
faceSize: 0.2,
|
|
6
|
+
headPose: 0.3,
|
|
7
|
+
eyesOpen: 0.3
|
|
8
|
+
};
|
|
9
|
+
const FACE_SIZE_MIN = 0.2;
|
|
10
|
+
const FACE_SIZE_MAX = 0.65;
|
|
11
|
+
const MAX_YAW_DEG = 20;
|
|
12
|
+
const MAX_PITCH_DEG = 20;
|
|
13
|
+
export function scoreFrame(face, frameWidth) {
|
|
14
|
+
if (!face.detected || frameWidth === 0) {
|
|
15
|
+
return {
|
|
16
|
+
total: 0,
|
|
17
|
+
faceSize: 0,
|
|
18
|
+
headPose: 0,
|
|
19
|
+
eyesOpen: 0
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
const faceWidthRatio = face.bounds.width / frameWidth;
|
|
23
|
+
const faceSize = faceWidthRatio >= FACE_SIZE_MIN && faceWidthRatio <= FACE_SIZE_MAX ? 1.0 : 0.0;
|
|
24
|
+
const yawOK = Math.abs(face.yawAngle) < MAX_YAW_DEG;
|
|
25
|
+
const pitchOK = Math.abs(face.pitchAngle) < MAX_PITCH_DEG;
|
|
26
|
+
const headPose = (yawOK ? 0.5 : 0) + (pitchOK ? 0.5 : 0);
|
|
27
|
+
|
|
28
|
+
// ML Kit returns -1 when classification is disabled or unavailable
|
|
29
|
+
const leftEye = face.leftEyeOpenProbability >= 0 ? face.leftEyeOpenProbability : 0.5;
|
|
30
|
+
const rightEye = face.rightEyeOpenProbability >= 0 ? face.rightEyeOpenProbability : 0.5;
|
|
31
|
+
const eyesOpen = (leftEye + rightEye) / 2;
|
|
32
|
+
const total = WEIGHTS.faceDetected * 1.0 + WEIGHTS.faceSize * faceSize + WEIGHTS.headPose * headPose + WEIGHTS.eyesOpen * eyesOpen;
|
|
33
|
+
return {
|
|
34
|
+
total,
|
|
35
|
+
faceSize,
|
|
36
|
+
headPose,
|
|
37
|
+
eyesOpen
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
export function rollingAverage(scores) {
|
|
41
|
+
if (scores.length === 0) return 0;
|
|
42
|
+
return scores.reduce((a, b) => a + b, 0) / scores.length;
|
|
43
|
+
}
|
|
44
|
+
export function getFeedback(face, frameWidth, livenessConfirmed) {
|
|
45
|
+
if (livenessConfirmed) return 'Liveness confirmed';
|
|
46
|
+
if (!face.detected) return 'Position your face in the oval';
|
|
47
|
+
const faceWidthRatio = face.bounds.width / frameWidth;
|
|
48
|
+
if (faceWidthRatio < FACE_SIZE_MIN) return 'Move closer';
|
|
49
|
+
if (faceWidthRatio > FACE_SIZE_MAX) return 'Move farther away';
|
|
50
|
+
const yawBad = Math.abs(face.yawAngle) >= MAX_YAW_DEG;
|
|
51
|
+
const pitchBad = Math.abs(face.pitchAngle) >= MAX_PITCH_DEG;
|
|
52
|
+
if (yawBad || pitchBad) return 'Look straight ahead';
|
|
53
|
+
const leftEye = face.leftEyeOpenProbability >= 0 ? face.leftEyeOpenProbability : 1;
|
|
54
|
+
const rightEye = face.rightEyeOpenProbability >= 0 ? face.rightEyeOpenProbability : 1;
|
|
55
|
+
if (leftEye < 0.4 || rightEye < 0.4) return 'Open your eyes';
|
|
56
|
+
return 'Hold still...';
|
|
57
|
+
}
|
|
58
|
+
//# sourceMappingURL=livenessScoring.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":["WEIGHTS","faceDetected","faceSize","headPose","eyesOpen","FACE_SIZE_MIN","FACE_SIZE_MAX","MAX_YAW_DEG","MAX_PITCH_DEG","scoreFrame","face","frameWidth","detected","total","faceWidthRatio","bounds","width","yawOK","Math","abs","yawAngle","pitchOK","pitchAngle","leftEye","leftEyeOpenProbability","rightEye","rightEyeOpenProbability","rollingAverage","scores","length","reduce","a","b","getFeedback","livenessConfirmed","yawBad","pitchBad"],"sourceRoot":"../../src","sources":["livenessScoring.ts"],"mappings":"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","ignoreList":[]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"type":"module"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":[],"sourceRoot":"../../src","sources":["types.ts"],"mappings":"","ignoreList":[]}
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
|
4
|
+
import { useFrameProcessor } from 'react-native-vision-camera';
|
|
5
|
+
import { Worklets } from 'react-native-worklets-core';
|
|
6
|
+
import { useLivenessPlugin } from "./LivenessDetector.js";
|
|
7
|
+
import { getFeedback, rollingAverage, scoreFrame } from "./livenessScoring.js";
|
|
8
|
+
const WINDOW_SIZE = 20;
|
|
9
|
+
export function useLivenessCamera(options) {
|
|
10
|
+
const {
|
|
11
|
+
livenessThreshold,
|
|
12
|
+
confirmFrames,
|
|
13
|
+
countdownFrom,
|
|
14
|
+
soundEnabled,
|
|
15
|
+
cameraRef,
|
|
16
|
+
onCapture,
|
|
17
|
+
onLivenessConfirmed,
|
|
18
|
+
onError
|
|
19
|
+
} = options;
|
|
20
|
+
const plugin = useLivenessPlugin();
|
|
21
|
+
|
|
22
|
+
// Mutable refs — updated every frame, never cause re-renders
|
|
23
|
+
const frameScores = useRef([]);
|
|
24
|
+
const consecutiveGood = useRef(0);
|
|
25
|
+
const frameWidth = useRef(0);
|
|
26
|
+
const stateRef = useRef('scanning');
|
|
27
|
+
const isCaptured = useRef(false);
|
|
28
|
+
const [state, setState] = useState({
|
|
29
|
+
livenessState: 'scanning',
|
|
30
|
+
livenessScore: 0,
|
|
31
|
+
countdown: null,
|
|
32
|
+
feedback: 'Position your face in the oval'
|
|
33
|
+
});
|
|
34
|
+
const setLivenessState = useCallback(next => {
|
|
35
|
+
stateRef.current = next;
|
|
36
|
+
setState(prev => ({
|
|
37
|
+
...prev,
|
|
38
|
+
livenessState: next
|
|
39
|
+
}));
|
|
40
|
+
}, []);
|
|
41
|
+
|
|
42
|
+
// ─── Capture ──────────────────────────────────────────────────────────────
|
|
43
|
+
|
|
44
|
+
const capture = useCallback(async () => {
|
|
45
|
+
if (isCaptured.current || !cameraRef.current) return;
|
|
46
|
+
isCaptured.current = true;
|
|
47
|
+
setLivenessState('capturing');
|
|
48
|
+
try {
|
|
49
|
+
const photo = await cameraRef.current.takePhoto({
|
|
50
|
+
flash: 'off',
|
|
51
|
+
enableShutterSound: soundEnabled
|
|
52
|
+
});
|
|
53
|
+
const score = rollingAverage(frameScores.current);
|
|
54
|
+
setLivenessState('done');
|
|
55
|
+
onCapture({
|
|
56
|
+
photo,
|
|
57
|
+
livenessScore: score,
|
|
58
|
+
timestamp: Date.now()
|
|
59
|
+
});
|
|
60
|
+
} catch (err) {
|
|
61
|
+
setLivenessState('error');
|
|
62
|
+
onError?.(err instanceof Error ? err : new Error(String(err)));
|
|
63
|
+
}
|
|
64
|
+
}, [cameraRef, onCapture, onError, soundEnabled, setLivenessState]);
|
|
65
|
+
|
|
66
|
+
// ─── Countdown ────────────────────────────────────────────────────────────
|
|
67
|
+
|
|
68
|
+
const startCountdown = useCallback(() => {
|
|
69
|
+
setLivenessState('countdown');
|
|
70
|
+
let tick = countdownFrom;
|
|
71
|
+
setState(prev => ({
|
|
72
|
+
...prev,
|
|
73
|
+
countdown: tick
|
|
74
|
+
}));
|
|
75
|
+
const interval = setInterval(() => {
|
|
76
|
+
tick -= 1;
|
|
77
|
+
if (tick <= 0) {
|
|
78
|
+
clearInterval(interval);
|
|
79
|
+
setState(prev => ({
|
|
80
|
+
...prev,
|
|
81
|
+
countdown: null
|
|
82
|
+
}));
|
|
83
|
+
capture();
|
|
84
|
+
} else {
|
|
85
|
+
setState(prev => ({
|
|
86
|
+
...prev,
|
|
87
|
+
countdown: tick
|
|
88
|
+
}));
|
|
89
|
+
}
|
|
90
|
+
}, 1000);
|
|
91
|
+
return () => clearInterval(interval);
|
|
92
|
+
}, [capture, countdownFrom, setLivenessState]);
|
|
93
|
+
|
|
94
|
+
// ─── Per-frame face handler (runs on JS thread, called from worklet) ──────
|
|
95
|
+
|
|
96
|
+
const handleFaceData = useCallback((face, width) => {
|
|
97
|
+
if (stateRef.current === 'capturing' || stateRef.current === 'done' || stateRef.current === 'error') {
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
frameWidth.current = width;
|
|
101
|
+
const safeFace = face ?? {
|
|
102
|
+
detected: false,
|
|
103
|
+
bounds: {
|
|
104
|
+
x: 0,
|
|
105
|
+
y: 0,
|
|
106
|
+
width: 0,
|
|
107
|
+
height: 0
|
|
108
|
+
},
|
|
109
|
+
yawAngle: 0,
|
|
110
|
+
pitchAngle: 0,
|
|
111
|
+
rollAngle: 0,
|
|
112
|
+
leftEyeOpenProbability: -1,
|
|
113
|
+
rightEyeOpenProbability: -1,
|
|
114
|
+
smilingProbability: -1
|
|
115
|
+
};
|
|
116
|
+
const {
|
|
117
|
+
total
|
|
118
|
+
} = scoreFrame(safeFace, width);
|
|
119
|
+
frameScores.current.push(total);
|
|
120
|
+
if (frameScores.current.length > WINDOW_SIZE) {
|
|
121
|
+
frameScores.current.shift();
|
|
122
|
+
}
|
|
123
|
+
const avgScore = rollingAverage(frameScores.current);
|
|
124
|
+
if (total >= livenessThreshold) {
|
|
125
|
+
consecutiveGood.current += 1;
|
|
126
|
+
} else {
|
|
127
|
+
consecutiveGood.current = 0;
|
|
128
|
+
}
|
|
129
|
+
const isLive = consecutiveGood.current >= confirmFrames && avgScore >= livenessThreshold;
|
|
130
|
+
const feedback = getFeedback(safeFace, width, isLive);
|
|
131
|
+
setState(prev => ({
|
|
132
|
+
...prev,
|
|
133
|
+
livenessScore: avgScore,
|
|
134
|
+
feedback
|
|
135
|
+
}));
|
|
136
|
+
if (isLive && stateRef.current === 'scanning') {
|
|
137
|
+
setLivenessState('confirmed');
|
|
138
|
+
onLivenessConfirmed?.();
|
|
139
|
+
startCountdown();
|
|
140
|
+
}
|
|
141
|
+
}, [confirmFrames, livenessThreshold, onLivenessConfirmed, setLivenessState, startCountdown]);
|
|
142
|
+
|
|
143
|
+
// ─── Frame processor ──────────────────────────────────────────────────────
|
|
144
|
+
|
|
145
|
+
const handleFaceDataJS = useMemo(() => Worklets.createRunOnJS(handleFaceData), [handleFaceData]);
|
|
146
|
+
const frameProcessor = useFrameProcessor(frame => {
|
|
147
|
+
'worklet';
|
|
148
|
+
|
|
149
|
+
const face = plugin.detectLiveness(frame);
|
|
150
|
+
handleFaceDataJS(face, frame.width);
|
|
151
|
+
}, [plugin, handleFaceDataJS]);
|
|
152
|
+
useEffect(() => {
|
|
153
|
+
return () => {
|
|
154
|
+
frameScores.current = [];
|
|
155
|
+
consecutiveGood.current = 0;
|
|
156
|
+
isCaptured.current = false;
|
|
157
|
+
};
|
|
158
|
+
}, []);
|
|
159
|
+
return {
|
|
160
|
+
frameProcessor,
|
|
161
|
+
livenessState: state.livenessState,
|
|
162
|
+
livenessScore: state.livenessScore,
|
|
163
|
+
countdown: state.countdown,
|
|
164
|
+
feedback: state.feedback
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
//# sourceMappingURL=useLivenessCamera.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":["useCallback","useEffect","useMemo","useRef","useState","useFrameProcessor","Worklets","useLivenessPlugin","getFeedback","rollingAverage","scoreFrame","WINDOW_SIZE","useLivenessCamera","options","livenessThreshold","confirmFrames","countdownFrom","soundEnabled","cameraRef","onCapture","onLivenessConfirmed","onError","plugin","frameScores","consecutiveGood","frameWidth","stateRef","isCaptured","state","setState","livenessState","livenessScore","countdown","feedback","setLivenessState","next","current","prev","capture","photo","takePhoto","flash","enableShutterSound","score","timestamp","Date","now","err","Error","String","startCountdown","tick","interval","setInterval","clearInterval","handleFaceData","face","width","safeFace","detected","bounds","x","y","height","yawAngle","pitchAngle","rollAngle","leftEyeOpenProbability","rightEyeOpenProbability","smilingProbability","total","push","length","shift","avgScore","isLive","handleFaceDataJS","createRunOnJS","frameProcessor","frame","detectLiveness"],"sourceRoot":"../../src","sources":["useLivenessCamera.ts"],"mappings":"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","ignoreList":[]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"type":"module"}
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import type { LivenessCameraProps } from './types';
|
|
2
|
+
export declare function LivenessCamera({ onCapture, onLivenessConfirmed, onError, countdownFrom, livenessThreshold, confirmFrames, soundEnabled, style, }: LivenessCameraProps): import("react/jsx-runtime").JSX.Element;
|
|
3
|
+
//# sourceMappingURL=LivenessCamera.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"LivenessCamera.d.ts","sourceRoot":"","sources":["../../../src/LivenessCamera.tsx"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,mBAAmB,EAAiB,MAAM,SAAS,CAAC;AA2HlE,wBAAgB,cAAc,CAAC,EAC7B,SAAS,EACT,mBAAmB,EACnB,OAAO,EACP,aAAiB,EACjB,iBAAwB,EACxB,aAAkB,EAClB,YAAmB,EACnB,KAAK,GACN,EAAE,mBAAmB,2CAiFrB"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { type Frame } from 'react-native-vision-camera';
|
|
2
|
+
import type { FaceData } from './types';
|
|
3
|
+
type LivenessPlugin = {
|
|
4
|
+
detectLiveness: (frame: Frame) => FaceData | null;
|
|
5
|
+
};
|
|
6
|
+
export declare function useLivenessPlugin(): LivenessPlugin;
|
|
7
|
+
export {};
|
|
8
|
+
//# sourceMappingURL=LivenessDetector.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"LivenessDetector.d.ts","sourceRoot":"","sources":["../../../src/LivenessDetector.ts"],"names":[],"mappings":"AACA,OAAO,EAAqB,KAAK,KAAK,EAAE,MAAM,4BAA4B,CAAC;AAC3E,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAExC,KAAK,cAAc,GAAG;IACpB,cAAc,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,QAAQ,GAAG,IAAI,CAAC;CACnD,CAAC;AAyBF,wBAAgB,iBAAiB,IAAI,cAAc,CAElD"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAClD,OAAO,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AACxD,YAAY,EACV,aAAa,EACb,QAAQ,EACR,eAAe,EACf,mBAAmB,EACnB,aAAa,GACd,MAAM,SAAS,CAAC"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { FaceData, FeedbackMessage } from './types';
|
|
2
|
+
export type FrameScore = {
|
|
3
|
+
total: number;
|
|
4
|
+
faceSize: number;
|
|
5
|
+
headPose: number;
|
|
6
|
+
eyesOpen: number;
|
|
7
|
+
};
|
|
8
|
+
export declare function scoreFrame(face: FaceData, frameWidth: number): FrameScore;
|
|
9
|
+
export declare function rollingAverage(scores: number[]): number;
|
|
10
|
+
export declare function getFeedback(face: FaceData, frameWidth: number, livenessConfirmed: boolean): FeedbackMessage;
|
|
11
|
+
//# sourceMappingURL=livenessScoring.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"livenessScoring.d.ts","sourceRoot":"","sources":["../../../src/livenessScoring.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAczD,MAAM,MAAM,UAAU,GAAG;IACvB,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;CAClB,CAAC;AAEF,wBAAgB,UAAU,CAAC,IAAI,EAAE,QAAQ,EAAE,UAAU,EAAE,MAAM,GAAG,UAAU,CA6BzE;AAED,wBAAgB,cAAc,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,CAGvD;AAED,wBAAgB,WAAW,CACzB,IAAI,EAAE,QAAQ,EACd,UAAU,EAAE,MAAM,EAClB,iBAAiB,EAAE,OAAO,GACzB,eAAe,CAmBjB"}
|