@rick427/react-native-liveness 0.2.3 → 0.2.4

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/README.md CHANGED
@@ -226,16 +226,17 @@ The circle guide layers three animations built entirely with `Animated` + `react
226
226
 
227
227
  ## How liveness scoring works
228
228
 
229
- Each camera frame is scored across four signals at up to **20 fps** (ML Kit is throttled via `runAtTargetFps` while the preview renders at 60 fps):
229
+ Each camera frame is scored across three signals at up to **20 fps** (ML Kit is throttled via `runAtTargetFps` while the preview renders at 60 fps):
230
230
 
231
231
  | Signal | Weight | Detail |
232
232
  |---|---|---|
233
- | Face detected | 20% | ML Kit found a face in the frame. |
234
- | Face size | 20% | Face width is 15–80% of the frame. Soft-edge scoring at boundaries. |
235
- | Head pose | 30% | Yaw < ±25° and pitch < ±25° from frontal. Soft decay outside range. |
236
- | Eyes open | 30% | Average of left/right eye open probability from ML Kit. |
233
+ | Face detected | 25% | ML Kit found a face in the frame. |
234
+ | Face size | 30% | Face width is 15–80% of the frame. Soft-edge scoring at boundaries. |
235
+ | Head pose | 45% | Yaw < ±25° and pitch < ±25° from frontal. Soft decay outside range. |
237
236
 
238
- A rolling window of the last 20 frame scores is maintained. Liveness is confirmed once `confirmFrames` consecutive frames all score above `livenessThreshold`. Bad frames **decay** the streak by 2 instead of resetting it, making detection resilient to momentary noise.
237
+ Eye openness is intentionally excluded this is **passive** liveness detection. Natural blinks are involuntary and must not penalise the user.
238
+
239
+ Liveness is confirmed once `confirmFrames` consecutive frames all score above `livenessThreshold`. Bad frames decay the streak by 1 instead of resetting it, making detection resilient to momentary noise.
239
240
 
240
241
  ---
241
242
 
@@ -1,10 +1,12 @@
1
1
  "use strict";
2
2
 
3
+ // Eye openness is intentionally excluded — passive liveness detection does not
4
+ // require gestures. Natural blinks are involuntary and must never penalise the
5
+ // user. Liveness is confirmed via face presence, size, and frontal pose only.
3
6
  const WEIGHTS = {
4
- faceDetected: 0.2,
5
- faceSize: 0.2,
6
- headPose: 0.3,
7
- eyesOpen: 0.3
7
+ faceDetected: 0.25,
8
+ faceSize: 0.3,
9
+ headPose: 0.45
8
10
  };
9
11
 
10
12
  // Loosened from 0.20 / 0.65 — gives more room for the user to breathe
@@ -41,17 +43,12 @@ export function scoreFrame(face, frameWidth) {
41
43
  const yawScore = Math.abs(face.yawAngle) <= MAX_YAW_DEG ? 1.0 : Math.max(0, 1 - (Math.abs(face.yawAngle) - MAX_YAW_DEG) / MAX_YAW_DEG);
42
44
  const pitchScore = Math.abs(face.pitchAngle) <= MAX_PITCH_DEG ? 1.0 : Math.max(0, 1 - (Math.abs(face.pitchAngle) - MAX_PITCH_DEG) / MAX_PITCH_DEG);
43
45
  const headPose = (yawScore + pitchScore) / 2;
44
-
45
- // ML Kit returns -1 when classification is disabled or unavailable
46
- const leftEye = face.leftEyeOpenProbability >= 0 ? face.leftEyeOpenProbability : 0.5;
47
- const rightEye = face.rightEyeOpenProbability >= 0 ? face.rightEyeOpenProbability : 0.5;
48
- const eyesOpen = (leftEye + rightEye) / 2;
49
- const total = WEIGHTS.faceDetected * 1.0 + WEIGHTS.faceSize * faceSize + WEIGHTS.headPose * headPose + WEIGHTS.eyesOpen * eyesOpen;
46
+ const total = WEIGHTS.faceDetected * 1.0 + WEIGHTS.faceSize * faceSize + WEIGHTS.headPose * headPose;
50
47
  return {
51
48
  total,
52
49
  faceSize,
53
50
  headPose,
54
- eyesOpen
51
+ eyesOpen: 0
55
52
  };
56
53
  }
57
54
  export function rollingAverage(scores) {
@@ -67,9 +64,6 @@ export function getFeedback(face, frameWidth, livenessConfirmed) {
67
64
  const yawBad = Math.abs(face.yawAngle) >= MAX_YAW_DEG;
68
65
  const pitchBad = Math.abs(face.pitchAngle) >= MAX_PITCH_DEG;
69
66
  if (yawBad || pitchBad) return 'Look straight ahead';
70
- const leftEye = face.leftEyeOpenProbability >= 0 ? face.leftEyeOpenProbability : 1;
71
- const rightEye = face.rightEyeOpenProbability >= 0 ? face.rightEyeOpenProbability : 1;
72
- if (leftEye < 0.4 || rightEye < 0.4) return 'Open your eyes';
73
67
  return 'Hold still...';
74
68
  }
75
69
  //# sourceMappingURL=livenessScoring.js.map
@@ -1 +1 @@
1
- {"version":3,"names":["WEIGHTS","faceDetected","faceSize","headPose","eyesOpen","FACE_SIZE_MIN","FACE_SIZE_MAX","FACE_SIZE_SOFT_MARGIN","MAX_YAW_DEG","MAX_PITCH_DEG","scoreFrame","face","frameWidth","detected","total","faceWidthRatio","bounds","width","Math","max","yawScore","abs","yawAngle","pitchScore","pitchAngle","leftEye","leftEyeOpenProbability","rightEye","rightEyeOpenProbability","rollingAverage","scores","length","reduce","a","b","getFeedback","livenessConfirmed","yawBad","pitchBad"],"sourceRoot":"../../src","sources":["livenessScoring.ts"],"mappings":";;AAEA,MAAMA,OAAO,GAAG;EACdC,YAAY,EAAE,GAAG;EACjBC,QAAQ,EAAE,GAAG;EACbC,QAAQ,EAAE,GAAG;EACbC,QAAQ,EAAE;AACZ,CAAU;;AAEV;AACA,MAAMC,aAAa,GAAG,IAAI;AAC1B,MAAMC,aAAa,GAAG,GAAG;AACzB,MAAMC,qBAAqB,GAAG,IAAI,CAAC,CAAC;;AAEpC;AACA,MAAMC,WAAW,GAAG,EAAE;AACtB,MAAMC,aAAa,GAAG,EAAE;AASxB,OAAO,SAASC,UAAUA,CAACC,IAAc,EAAEC,UAAkB,EAAc;EACzE,IAAI,CAACD,IAAI,CAACE,QAAQ,IAAID,UAAU,KAAK,CAAC,EAAE;IACtC,OAAO;MAAEE,KAAK,EAAE,CAAC;MAAEZ,QAAQ,EAAE,CAAC;MAAEC,QAAQ,EAAE,CAAC;MAAEC,QAAQ,EAAE;IAAE,CAAC;EAC5D;EAEA,MAAMW,cAAc,GAAGJ,IAAI,CAACK,MAAM,CAACC,KAAK,GAAGL,UAAU;;EAErD;EACA;EACA,IAAIV,QAAgB;EACpB,IAAIa,cAAc,IAAIV,aAAa,IAAIU,cAAc,IAAIT,aAAa,EAAE;IACtEJ,QAAQ,GAAG,GAAG;EAChB,CAAC,MAAM,IAAIa,cAAc,GAAGV,aAAa,EAAE;IACzCH,QAAQ,GAAGgB,IAAI,CAACC,GAAG,CACjB,CAAC,EACD,CAACJ,cAAc,IAAIV,aAAa,GAAGE,qBAAqB,CAAC,IACvDA,qBACJ,CAAC;EACH,CAAC,MAAM;IACLL,QAAQ,GAAGgB,IAAI,CAACC,GAAG,CACjB,CAAC,EACD,CAACb,aAAa,GAAGC,qBAAqB,GAAGQ,cAAc,IACrDR,qBACJ,CAAC;EACH;;EAEA;EACA,MAAMa,QAAQ,GACZF,IAAI,CAACG,GAAG,CAACV,IAAI,CAACW,QAAQ,CAAC,IAAId,WAAW,GAClC,GAAG,GACHU,IAAI,CAACC,GAAG,CAAC,CAAC,EAAE,CAAC,GAAG,CAACD,IAAI,CAACG,GAAG,CAACV,IAAI,CAACW,QAAQ,CAAC,GAAGd,WAAW,IAAIA,WAAW,CAAC;EAC5E,MAAMe,UAAU,GACdL,IAAI,CAACG,GAAG,CAACV,IAAI,CAACa,UAAU,CAAC,IAAIf,aAAa,GACtC,GAAG,GACHS,IAAI,CAACC,GAAG,CACN,CAAC,EACD,CAAC,GAAG,CAACD,IAAI,CAACG,GAAG,CAACV,IAAI,CAACa,UAAU,CAAC,GAAGf,aAAa,IAAIA,aACpD,CAAC;EACP,MAAMN,QAAQ,GAAG,CAACiB,QAAQ,GAAGG,UAAU,IAAI,CAAC;;EAE5C;EACA,MAAME,OAAO,GACXd,IAAI,CAACe,sBAAsB,IAAI,CAAC,GAAGf,IAAI,CAACe,sBAAsB,GAAG,GAAG;EACtE,MAAMC,QAAQ,GACZhB,IAAI,CAACiB,uBAAuB,IAAI,CAAC,GAAGjB,IAAI,CAACiB,uBAAuB,GAAG,GAAG;EACxE,MAAMxB,QAAQ,GAAG,CAACqB,OAAO,GAAGE,QAAQ,IAAI,CAAC;EAEzC,MAAMb,KAAK,GACTd,OAAO,CAACC,YAAY,GAAG,GAAG,GAC1BD,OAAO,CAACE,QAAQ,GAAGA,QAAQ,GAC3BF,OAAO,CAACG,QAAQ,GAAGA,QAAQ,GAC3BH,OAAO,CAACI,QAAQ,GAAGA,QAAQ;EAE7B,OAAO;IAAEU,KAAK;IAAEZ,QAAQ;IAAEC,QAAQ;IAAEC;EAAS,CAAC;AAChD;AAEA,OAAO,SAASyB,cAAcA,CAACC,MAAgB,EAAU;EACvD,IAAIA,MAAM,CAACC,MAAM,KAAK,CAAC,EAAE,OAAO,CAAC;EACjC,OAAOD,MAAM,CAACE,MAAM,CAAC,CAACC,CAAC,EAAEC,CAAC,KAAKD,CAAC,GAAGC,CAAC,EAAE,CAAC,CAAC,GAAGJ,MAAM,CAACC,MAAM;AAC1D;AAEA,OAAO,SAASI,WAAWA,CACzBxB,IAAc,EACdC,UAAkB,EAClBwB,iBAA0B,EACT;EACjB,IAAIA,iBAAiB,EAAE,OAAO,oBAAoB;EAClD,IAAI,CAACzB,IAAI,CAACE,QAAQ,EAAE,OAAO,kCAAkC;EAE7D,MAAME,cAAc,GAAGJ,IAAI,CAACK,MAAM,CAACC,KAAK,GAAGL,UAAU;EACrD,IAAIG,cAAc,GAAGV,aAAa,EAAE,OAAO,aAAa;EACxD,IAAIU,cAAc,GAAGT,aAAa,EAAE,OAAO,mBAAmB;EAE9D,MAAM+B,MAAM,GAAGnB,IAAI,CAACG,GAAG,CAACV,IAAI,CAACW,QAAQ,CAAC,IAAId,WAAW;EACrD,MAAM8B,QAAQ,GAAGpB,IAAI,CAACG,GAAG,CAACV,IAAI,CAACa,UAAU,CAAC,IAAIf,aAAa;EAC3D,IAAI4B,MAAM,IAAIC,QAAQ,EAAE,OAAO,qBAAqB;EAEpD,MAAMb,OAAO,GACXd,IAAI,CAACe,sBAAsB,IAAI,CAAC,GAAGf,IAAI,CAACe,sBAAsB,GAAG,CAAC;EACpE,MAAMC,QAAQ,GACZhB,IAAI,CAACiB,uBAAuB,IAAI,CAAC,GAAGjB,IAAI,CAACiB,uBAAuB,GAAG,CAAC;EACtE,IAAIH,OAAO,GAAG,GAAG,IAAIE,QAAQ,GAAG,GAAG,EAAE,OAAO,gBAAgB;EAE5D,OAAO,eAAe;AACxB","ignoreList":[]}
1
+ {"version":3,"names":["WEIGHTS","faceDetected","faceSize","headPose","FACE_SIZE_MIN","FACE_SIZE_MAX","FACE_SIZE_SOFT_MARGIN","MAX_YAW_DEG","MAX_PITCH_DEG","scoreFrame","face","frameWidth","detected","total","eyesOpen","faceWidthRatio","bounds","width","Math","max","yawScore","abs","yawAngle","pitchScore","pitchAngle","rollingAverage","scores","length","reduce","a","b","getFeedback","livenessConfirmed","yawBad","pitchBad"],"sourceRoot":"../../src","sources":["livenessScoring.ts"],"mappings":"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","ignoreList":[]}
@@ -1 +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;AAkBzD,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,CAsDzE;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"}
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;AAoBzD,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,CA8CzE;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,CAajB"}
@@ -16,7 +16,7 @@ export type FaceData = {
16
16
  smilingProbability: number;
17
17
  };
18
18
  export type LivenessState = 'idle' | 'scanning' | 'confirmed' | 'countdown' | 'capturing' | 'done' | 'error';
19
- export type FeedbackMessage = 'Position your face in the circle' | 'Move closer' | 'Move farther away' | 'Look straight ahead' | 'Hold still...' | 'Stay still' | 'Open your eyes' | 'Liveness confirmed' | '';
19
+ export type FeedbackMessage = 'Position your face in the circle' | 'Move closer' | 'Move farther away' | 'Look straight ahead' | 'Hold still...' | 'Stay still' | 'Liveness confirmed' | '';
20
20
  export type CaptureResult = {
21
21
  photo: PhotoFile;
22
22
  livenessScore: number;
@@ -40,12 +40,14 @@ export type LivenessCameraProps = {
40
40
  */
41
41
  countdownFrom?: number;
42
42
  /**
43
- * Score (0–1) required to confirm liveness. Defaults to 0.75.
43
+ * Per-frame score (0–1) a frame must reach to count as a good frame.
44
+ * Defaults to 0.65. Scored on face presence, size, and head pose only —
45
+ * eye openness is not used (passive detection, no gestures required).
44
46
  */
45
47
  livenessThreshold?: number;
46
48
  /**
47
- * Number of consecutive high-score frames required before liveness is
48
- * confirmed. Defaults to 10 (~500ms at 20fps).
49
+ * Number of consecutive good frames required to confirm liveness.
50
+ * Defaults to 7 (~350ms at 20fps).
49
51
  */
50
52
  confirmFrames?: number;
51
53
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAC1D,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,4BAA4B,CAAC;AAE5D,MAAM,MAAM,QAAQ,GAAG;IACrB,QAAQ,EAAE,OAAO,CAAC;IAClB,MAAM,EAAE;QACN,CAAC,EAAE,MAAM,CAAC;QACV,CAAC,EAAE,MAAM,CAAC;QACV,KAAK,EAAE,MAAM,CAAC;QACd,MAAM,EAAE,MAAM,CAAC;KAChB,CAAC;IACF,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,sBAAsB,EAAE,MAAM,CAAC;IAC/B,uBAAuB,EAAE,MAAM,CAAC;IAChC,kBAAkB,EAAE,MAAM,CAAC;CAC5B,CAAC;AAEF,MAAM,MAAM,aAAa,GACrB,MAAM,GACN,UAAU,GACV,WAAW,GACX,WAAW,GACX,WAAW,GACX,MAAM,GACN,OAAO,CAAC;AAEZ,MAAM,MAAM,eAAe,GACvB,kCAAkC,GAClC,aAAa,GACb,mBAAmB,GACnB,qBAAqB,GACrB,eAAe,GACf,YAAY,GACZ,gBAAgB,GAChB,oBAAoB,GACpB,EAAE,CAAC;AAEP,MAAM,MAAM,aAAa,GAAG;IAC1B,KAAK,EAAE,SAAS,CAAC;IACjB,aAAa,EAAE,MAAM,CAAC;IACtB,SAAS,EAAE,MAAM,CAAC;CACnB,CAAC;AAEF,MAAM,MAAM,mBAAmB,GAAG;IAChC;;OAEG;IACH,SAAS,EAAE,CAAC,MAAM,EAAE,aAAa,KAAK,IAAI,CAAC;IAE3C;;OAEG;IACH,mBAAmB,CAAC,EAAE,MAAM,IAAI,CAAC;IAEjC;;OAEG;IACH,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;IAEjC;;OAEG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;IAEvB;;OAEG;IACH,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAE3B;;;OAGG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;IAEvB;;OAEG;IACH,KAAK,CAAC,EAAE,SAAS,CAAC;IAElB;;OAEG;IACH,YAAY,CAAC,EAAE,OAAO,CAAC;IAEvB;;;OAGG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB,CAAC;AAEF,MAAM,MAAM,wBAAwB,GAAG,IAAI,CAAC,mBAAmB,EAAE,OAAO,CAAC,GAAG;IAC1E;;OAEG;IACH,OAAO,EAAE,OAAO,CAAC;IAEjB;;;OAGG;IACH,OAAO,EAAE,MAAM,IAAI,CAAC;IAEpB;;OAEG;IACH,aAAa,CAAC,EAAE,UAAU,CAAC,eAAe,CAAC,CAAC;IAE5C;;;OAGG;IACH,gBAAgB,CAAC,EAAE,SAAS,CAAC;IAE7B;;OAEG;IACH,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAE9B;;OAEG;IACH,mBAAmB,CAAC,EAAE,MAAM,CAAC;CAC9B,CAAC"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAC1D,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,4BAA4B,CAAC;AAE5D,MAAM,MAAM,QAAQ,GAAG;IACrB,QAAQ,EAAE,OAAO,CAAC;IAClB,MAAM,EAAE;QACN,CAAC,EAAE,MAAM,CAAC;QACV,CAAC,EAAE,MAAM,CAAC;QACV,KAAK,EAAE,MAAM,CAAC;QACd,MAAM,EAAE,MAAM,CAAC;KAChB,CAAC;IACF,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,sBAAsB,EAAE,MAAM,CAAC;IAC/B,uBAAuB,EAAE,MAAM,CAAC;IAChC,kBAAkB,EAAE,MAAM,CAAC;CAC5B,CAAC;AAEF,MAAM,MAAM,aAAa,GACrB,MAAM,GACN,UAAU,GACV,WAAW,GACX,WAAW,GACX,WAAW,GACX,MAAM,GACN,OAAO,CAAC;AAEZ,MAAM,MAAM,eAAe,GACvB,kCAAkC,GAClC,aAAa,GACb,mBAAmB,GACnB,qBAAqB,GACrB,eAAe,GACf,YAAY,GACZ,oBAAoB,GACpB,EAAE,CAAC;AAEP,MAAM,MAAM,aAAa,GAAG;IAC1B,KAAK,EAAE,SAAS,CAAC;IACjB,aAAa,EAAE,MAAM,CAAC;IACtB,SAAS,EAAE,MAAM,CAAC;CACnB,CAAC;AAEF,MAAM,MAAM,mBAAmB,GAAG;IAChC;;OAEG;IACH,SAAS,EAAE,CAAC,MAAM,EAAE,aAAa,KAAK,IAAI,CAAC;IAE3C;;OAEG;IACH,mBAAmB,CAAC,EAAE,MAAM,IAAI,CAAC;IAEjC;;OAEG;IACH,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;IAEjC;;OAEG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;IAEvB;;;;OAIG;IACH,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAE3B;;;OAGG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;IAEvB;;OAEG;IACH,KAAK,CAAC,EAAE,SAAS,CAAC;IAElB;;OAEG;IACH,YAAY,CAAC,EAAE,OAAO,CAAC;IAEvB;;;OAGG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB,CAAC;AAEF,MAAM,MAAM,wBAAwB,GAAG,IAAI,CAAC,mBAAmB,EAAE,OAAO,CAAC,GAAG;IAC1E;;OAEG;IACH,OAAO,EAAE,OAAO,CAAC;IAEjB;;;OAGG;IACH,OAAO,EAAE,MAAM,IAAI,CAAC;IAEpB;;OAEG;IACH,aAAa,CAAC,EAAE,UAAU,CAAC,eAAe,CAAC,CAAC;IAE5C;;;OAGG;IACH,gBAAgB,CAAC,EAAE,SAAS,CAAC;IAE7B;;OAEG;IACH,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAE9B;;OAEG;IACH,mBAAmB,CAAC,EAAE,MAAM,CAAC;CAC9B,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rick427/react-native-liveness",
3
- "version": "0.2.3",
3
+ "version": "0.2.4",
4
4
  "description": "Liveness detection library for React Native using Vision Camera v4 and ML Kit",
5
5
  "main": "./lib/module/index.js",
6
6
  "types": "./lib/typescript/src/index.d.ts",
@@ -1,10 +1,12 @@
1
1
  import type { FaceData, FeedbackMessage } from './types';
2
2
 
3
+ // Eye openness is intentionally excluded — passive liveness detection does not
4
+ // require gestures. Natural blinks are involuntary and must never penalise the
5
+ // user. Liveness is confirmed via face presence, size, and frontal pose only.
3
6
  const WEIGHTS = {
4
- faceDetected: 0.2,
5
- faceSize: 0.2,
6
- headPose: 0.3,
7
- eyesOpen: 0.3,
7
+ faceDetected: 0.25,
8
+ faceSize: 0.3,
9
+ headPose: 0.45,
8
10
  } as const;
9
11
 
10
12
  // Loosened from 0.20 / 0.65 — gives more room for the user to breathe
@@ -63,20 +65,12 @@ export function scoreFrame(face: FaceData, frameWidth: number): FrameScore {
63
65
  );
64
66
  const headPose = (yawScore + pitchScore) / 2;
65
67
 
66
- // ML Kit returns -1 when classification is disabled or unavailable
67
- const leftEye =
68
- face.leftEyeOpenProbability >= 0 ? face.leftEyeOpenProbability : 0.5;
69
- const rightEye =
70
- face.rightEyeOpenProbability >= 0 ? face.rightEyeOpenProbability : 0.5;
71
- const eyesOpen = (leftEye + rightEye) / 2;
72
-
73
68
  const total =
74
69
  WEIGHTS.faceDetected * 1.0 +
75
70
  WEIGHTS.faceSize * faceSize +
76
- WEIGHTS.headPose * headPose +
77
- WEIGHTS.eyesOpen * eyesOpen;
71
+ WEIGHTS.headPose * headPose;
78
72
 
79
- return { total, faceSize, headPose, eyesOpen };
73
+ return { total, faceSize, headPose, eyesOpen: 0 };
80
74
  }
81
75
 
82
76
  export function rollingAverage(scores: number[]): number {
@@ -100,11 +94,5 @@ export function getFeedback(
100
94
  const pitchBad = Math.abs(face.pitchAngle) >= MAX_PITCH_DEG;
101
95
  if (yawBad || pitchBad) return 'Look straight ahead';
102
96
 
103
- const leftEye =
104
- face.leftEyeOpenProbability >= 0 ? face.leftEyeOpenProbability : 1;
105
- const rightEye =
106
- face.rightEyeOpenProbability >= 0 ? face.rightEyeOpenProbability : 1;
107
- if (leftEye < 0.4 || rightEye < 0.4) return 'Open your eyes';
108
-
109
97
  return 'Hold still...';
110
98
  }
package/src/types.ts CHANGED
@@ -33,7 +33,6 @@ export type FeedbackMessage =
33
33
  | 'Look straight ahead'
34
34
  | 'Hold still...'
35
35
  | 'Stay still'
36
- | 'Open your eyes'
37
36
  | 'Liveness confirmed'
38
37
  | '';
39
38
 
@@ -65,13 +64,15 @@ export type LivenessCameraProps = {
65
64
  countdownFrom?: number;
66
65
 
67
66
  /**
68
- * Score (0–1) required to confirm liveness. Defaults to 0.75.
67
+ * Per-frame score (0–1) a frame must reach to count as a good frame.
68
+ * Defaults to 0.65. Scored on face presence, size, and head pose only —
69
+ * eye openness is not used (passive detection, no gestures required).
69
70
  */
70
71
  livenessThreshold?: number;
71
72
 
72
73
  /**
73
- * Number of consecutive high-score frames required before liveness is
74
- * confirmed. Defaults to 10 (~500ms at 20fps).
74
+ * Number of consecutive good frames required to confirm liveness.
75
+ * Defaults to 7 (~350ms at 20fps).
75
76
  */
76
77
  confirmFrames?: number;
77
78