smartcomply-web-sdk 1.0.50 → 1.0.52
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/dist/camera/ActionDetector.d.ts +11 -22
- package/dist/camera/ActionDetector.d.ts.map +1 -1
- package/dist/camera/ActionDetector.js +43 -107
- package/dist/camera/ActionDetector.js.map +1 -1
- package/dist/esm/camera/ActionDetector.d.ts +11 -22
- package/dist/esm/camera/ActionDetector.d.ts.map +1 -1
- package/dist/esm/camera/ActionDetector.js +43 -107
- package/dist/esm/camera/ActionDetector.js.map +1 -1
- package/dist/esm/flow/SmartComplyFlow.d.ts.map +1 -1
- package/dist/esm/flow/SmartComplyFlow.js +147 -148
- package/dist/esm/flow/SmartComplyFlow.js.map +1 -1
- package/dist/esm/modules/liveness/LivenessUI.d.ts +18 -16
- package/dist/esm/modules/liveness/LivenessUI.d.ts.map +1 -1
- package/dist/esm/modules/liveness/LivenessUI.js +99 -311
- package/dist/esm/modules/liveness/LivenessUI.js.map +1 -1
- package/dist/esm/modules/liveness/liveness.d.ts +10 -9
- package/dist/esm/modules/liveness/liveness.d.ts.map +1 -1
- package/dist/esm/modules/liveness/liveness.js +58 -107
- package/dist/esm/modules/liveness/liveness.js.map +1 -1
- package/dist/esm/utils/constants.d.ts +11 -7
- package/dist/esm/utils/constants.d.ts.map +1 -1
- package/dist/esm/utils/constants.js +12 -8
- package/dist/esm/utils/constants.js.map +1 -1
- package/dist/flow/SmartComplyFlow.d.ts.map +1 -1
- package/dist/flow/SmartComplyFlow.js +147 -148
- package/dist/flow/SmartComplyFlow.js.map +1 -1
- package/dist/modules/liveness/LivenessUI.d.ts +18 -16
- package/dist/modules/liveness/LivenessUI.d.ts.map +1 -1
- package/dist/modules/liveness/LivenessUI.js +99 -311
- package/dist/modules/liveness/LivenessUI.js.map +1 -1
- package/dist/modules/liveness/liveness.d.ts +10 -9
- package/dist/modules/liveness/liveness.d.ts.map +1 -1
- package/dist/modules/liveness/liveness.js +57 -106
- package/dist/modules/liveness/liveness.js.map +1 -1
- package/dist/smartcomply.browser.js +202 -516
- package/dist/smartcomply.browser.js.map +3 -3
- package/dist/utils/constants.d.ts +11 -7
- package/dist/utils/constants.d.ts.map +1 -1
- package/dist/utils/constants.js +13 -9
- package/dist/utils/constants.js.map +1 -1
- package/package.json +1 -1
|
@@ -1,28 +1,17 @@
|
|
|
1
1
|
import { FaceDetectionResult } from "./FaceDetector";
|
|
2
|
-
export interface
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
2
|
+
export interface ScanProgress {
|
|
3
|
+
faceDetected: boolean;
|
|
4
|
+
blinkDetected: boolean;
|
|
5
|
+
motionDetected: boolean;
|
|
6
|
+
livenessConfirmed: boolean;
|
|
7
7
|
}
|
|
8
|
-
/**
|
|
9
|
-
* Sequential action detector aligned with backend's 5 UPPERCASE actions:
|
|
10
|
-
* BLINK, TURN_LEFT, TURN_RIGHT, TURN_HEAD, OPEN_MOUTH
|
|
11
|
-
*/
|
|
12
8
|
export declare class ActionDetector {
|
|
13
|
-
private
|
|
14
|
-
private
|
|
15
|
-
private
|
|
16
|
-
private
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
check(result: FaceDetectionResult): ActionState[];
|
|
20
|
-
getCurrentAction(): string | null;
|
|
21
|
-
private buildStates;
|
|
22
|
-
private getConfidence;
|
|
9
|
+
private blinkHoldCount;
|
|
10
|
+
private blinkDetected;
|
|
11
|
+
private motionDetected;
|
|
12
|
+
private baselineYaw;
|
|
13
|
+
check(result: FaceDetectionResult): ScanProgress;
|
|
14
|
+
private buildProgress;
|
|
23
15
|
private getHeadYaw;
|
|
24
|
-
allCompleted(): boolean;
|
|
25
|
-
completedCount(): number;
|
|
26
|
-
reset(): void;
|
|
27
16
|
}
|
|
28
17
|
//# sourceMappingURL=ActionDetector.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ActionDetector.d.ts","sourceRoot":"","sources":["../../src/camera/ActionDetector.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,MAAM,gBAAgB,CAAC;
|
|
1
|
+
{"version":3,"file":"ActionDetector.d.ts","sourceRoot":"","sources":["../../src/camera/ActionDetector.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,MAAM,gBAAgB,CAAC;AAsBrD,MAAM,WAAW,YAAY;IAC3B,YAAY,EAAE,OAAO,CAAC;IACtB,aAAa,EAAE,OAAO,CAAC;IACvB,cAAc,EAAE,OAAO,CAAC;IACxB,iBAAiB,EAAE,OAAO,CAAC;CAC5B;AAED,qBAAa,cAAc;IACzB,OAAO,CAAC,cAAc,CAAK;IAC3B,OAAO,CAAC,aAAa,CAAS;IAC9B,OAAO,CAAC,cAAc,CAAS;IAC/B,OAAO,CAAC,WAAW,CAAuB;IAE1C,KAAK,CAAC,MAAM,EAAE,mBAAmB,GAAG,YAAY;IA+BhD,OAAO,CAAC,aAAa;IASrB,OAAO,CAAC,UAAU;CAInB"}
|
|
@@ -2,135 +2,71 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.ActionDetector = void 0;
|
|
4
4
|
/**
|
|
5
|
-
*
|
|
6
|
-
* - threshold : minimum confidence score required to start counting.
|
|
7
|
-
* - holdFrames: consecutive frames above threshold before action is confirmed.
|
|
5
|
+
* Passive natural-motion liveness detector.
|
|
8
6
|
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
const DEFAULT_CONFIG = { threshold: 0.40, holdFrames: 4 };
|
|
21
|
-
/**
|
|
22
|
-
* Sequential action detector aligned with backend's 5 UPPERCASE actions:
|
|
23
|
-
* BLINK, TURN_LEFT, TURN_RIGHT, TURN_HEAD, OPEN_MOUTH
|
|
7
|
+
* Replaces the earlier sequential "do BLINK, then TURN_HEAD, then OPEN_MOUTH"
|
|
8
|
+
* choreography with a single continuous scan window: the user is simply told
|
|
9
|
+
* to "blink and look around naturally" while the detector watches for signs
|
|
10
|
+
* of a live face (a genuine blink, or natural head movement) without
|
|
11
|
+
* commanding specific actions in order. This mirrors a passive liveness scan
|
|
12
|
+
* rather than a step-by-step obstacle course.
|
|
13
|
+
*
|
|
14
|
+
* Pass criteria: at least one genuine blink OR a natural head-yaw excursion
|
|
15
|
+
* detected at any point during the window — whichever happens first, since a
|
|
16
|
+
* real person glancing around naturally will trigger one or both without
|
|
17
|
+
* being told to.
|
|
24
18
|
*/
|
|
19
|
+
const BLINK_THRESHOLD = 0.25;
|
|
20
|
+
const BLINK_HOLD_FRAMES = 2;
|
|
21
|
+
const YAW_MOTION_THRESHOLD_DEG = 6; // small natural head movement, not a commanded turn
|
|
25
22
|
class ActionDetector {
|
|
26
|
-
constructor(
|
|
27
|
-
this.
|
|
28
|
-
this.
|
|
29
|
-
this.
|
|
30
|
-
this.
|
|
31
|
-
this.YAW_HISTORY_SIZE = 18; // ~0.6s of frames
|
|
23
|
+
constructor() {
|
|
24
|
+
this.blinkHoldCount = 0;
|
|
25
|
+
this.blinkDetected = false;
|
|
26
|
+
this.motionDetected = false;
|
|
27
|
+
this.baselineYaw = null;
|
|
32
28
|
}
|
|
33
29
|
check(result) {
|
|
34
|
-
const currentAction = this.getCurrentAction();
|
|
35
30
|
if (!result.faceDetected) {
|
|
36
|
-
return this.
|
|
31
|
+
return this.buildProgress(false);
|
|
37
32
|
}
|
|
38
33
|
const bs = result.blendshapes;
|
|
39
|
-
|
|
40
|
-
if (
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
this.holdCounters.set(currentAction, count);
|
|
46
|
-
if (count >= cfg.holdFrames) {
|
|
47
|
-
this.completedActions.add(currentAction);
|
|
48
|
-
this.holdCounters.delete(currentAction);
|
|
34
|
+
const blinkConf = ((bs.get("eyeBlinkLeft") || 0) + (bs.get("eyeBlinkRight") || 0)) / 2;
|
|
35
|
+
if (!this.blinkDetected) {
|
|
36
|
+
if (blinkConf >= BLINK_THRESHOLD) {
|
|
37
|
+
this.blinkHoldCount++;
|
|
38
|
+
if (this.blinkHoldCount >= BLINK_HOLD_FRAMES) {
|
|
39
|
+
this.blinkDetected = true;
|
|
49
40
|
}
|
|
50
41
|
}
|
|
51
42
|
else {
|
|
52
|
-
this.
|
|
43
|
+
this.blinkHoldCount = 0;
|
|
53
44
|
}
|
|
54
|
-
return this.buildStates(currentAction, conf);
|
|
55
|
-
}
|
|
56
|
-
return this.buildStates(currentAction, 0);
|
|
57
|
-
}
|
|
58
|
-
getCurrentAction() {
|
|
59
|
-
for (const action of this.requiredActions) {
|
|
60
|
-
if (!this.completedActions.has(action))
|
|
61
|
-
return action;
|
|
62
45
|
}
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
action,
|
|
68
|
-
detected: this.completedActions.has(action),
|
|
69
|
-
active: action === currentAction,
|
|
70
|
-
confidence: action === currentAction ? currentConf : 0,
|
|
71
|
-
}));
|
|
72
|
-
}
|
|
73
|
-
getConfidence(action, bs, matrix) {
|
|
74
|
-
switch (action) {
|
|
75
|
-
case "BLINK": {
|
|
76
|
-
const left = bs.get("eyeBlinkLeft") || 0;
|
|
77
|
-
const right = bs.get("eyeBlinkRight") || 0;
|
|
78
|
-
return (left + right) / 2;
|
|
79
|
-
}
|
|
80
|
-
case "TURN_LEFT": {
|
|
81
|
-
// Accept either direction — instruction is "turn sideways"
|
|
82
|
-
// Using absolute yaw so any head turn registers
|
|
83
|
-
const yaw = Math.abs(this.getHeadYaw(matrix));
|
|
84
|
-
return yaw > 5 ? Math.min(1, yaw / 20) : 0;
|
|
46
|
+
if (!this.motionDetected) {
|
|
47
|
+
const yaw = this.getHeadYaw(result.transformMatrix);
|
|
48
|
+
if (this.baselineYaw === null) {
|
|
49
|
+
this.baselineYaw = yaw;
|
|
85
50
|
}
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
const yaw = Math.abs(this.getHeadYaw(matrix));
|
|
89
|
-
return yaw > 5 ? Math.min(1, yaw / 20) : 0;
|
|
51
|
+
else if (Math.abs(yaw - this.baselineYaw) >= YAW_MOTION_THRESHOLD_DEG) {
|
|
52
|
+
this.motionDetected = true;
|
|
90
53
|
}
|
|
91
|
-
case "TURN_HEAD": {
|
|
92
|
-
// Proper side-to-side: head must reach left AND right within the
|
|
93
|
-
// recent frame window. 0.5 when one side is seen, 1.0 when both.
|
|
94
|
-
const yaw = this.getHeadYaw(matrix);
|
|
95
|
-
this.yawHistory.push(yaw);
|
|
96
|
-
if (this.yawHistory.length > this.YAW_HISTORY_SIZE) {
|
|
97
|
-
this.yawHistory.shift();
|
|
98
|
-
}
|
|
99
|
-
if (this.yawHistory.length < this.YAW_HISTORY_SIZE) {
|
|
100
|
-
return 0;
|
|
101
|
-
}
|
|
102
|
-
const left = this.yawHistory.some((y) => y <= -10);
|
|
103
|
-
const right = this.yawHistory.some((y) => y >= 10);
|
|
104
|
-
if (left && right)
|
|
105
|
-
return 1.0;
|
|
106
|
-
if (left || right)
|
|
107
|
-
return 0.5;
|
|
108
|
-
return 0;
|
|
109
|
-
}
|
|
110
|
-
case "OPEN_MOUTH":
|
|
111
|
-
return bs.get("jawOpen") || 0;
|
|
112
|
-
default:
|
|
113
|
-
return 0;
|
|
114
54
|
}
|
|
55
|
+
return this.buildProgress(true);
|
|
56
|
+
}
|
|
57
|
+
buildProgress(faceDetected) {
|
|
58
|
+
return {
|
|
59
|
+
faceDetected,
|
|
60
|
+
blinkDetected: this.blinkDetected,
|
|
61
|
+
motionDetected: this.motionDetected,
|
|
62
|
+
livenessConfirmed: this.blinkDetected || this.motionDetected,
|
|
63
|
+
};
|
|
115
64
|
}
|
|
116
65
|
getHeadYaw(matrix) {
|
|
117
66
|
if (!matrix || matrix.length < 16)
|
|
118
67
|
return 0;
|
|
119
|
-
// 4x4 column-major matrix from MediaPipe FaceLandmarker
|
|
120
|
-
// Yaw (rotation around Y axis): atan2(-m20, m00) where m20=matrix[2], m00=matrix[0]
|
|
121
68
|
return Math.atan2(-matrix[2], matrix[0]) * (180 / Math.PI);
|
|
122
69
|
}
|
|
123
|
-
allCompleted() {
|
|
124
|
-
return this.requiredActions.every((a) => this.completedActions.has(a));
|
|
125
|
-
}
|
|
126
|
-
completedCount() {
|
|
127
|
-
return this.completedActions.size;
|
|
128
|
-
}
|
|
129
|
-
reset() {
|
|
130
|
-
this.completedActions.clear();
|
|
131
|
-
this.holdCounters.clear();
|
|
132
|
-
this.yawHistory = [];
|
|
133
|
-
}
|
|
134
70
|
}
|
|
135
71
|
exports.ActionDetector = ActionDetector;
|
|
136
72
|
//# sourceMappingURL=ActionDetector.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ActionDetector.js","sourceRoot":"","sources":["../../src/camera/ActionDetector.ts"],"names":[],"mappings":";;;
|
|
1
|
+
{"version":3,"file":"ActionDetector.js","sourceRoot":"","sources":["../../src/camera/ActionDetector.ts"],"names":[],"mappings":";;;AAEA;;;;;;;;;;;;;;GAcG;AAEH,MAAM,eAAe,GAAG,IAAI,CAAC;AAC7B,MAAM,iBAAiB,GAAG,CAAC,CAAC;AAC5B,MAAM,wBAAwB,GAAG,CAAC,CAAC,CAAC,oDAAoD;AASxF,MAAa,cAAc;IAA3B;QACU,mBAAc,GAAG,CAAC,CAAC;QACnB,kBAAa,GAAG,KAAK,CAAC;QACtB,mBAAc,GAAG,KAAK,CAAC;QACvB,gBAAW,GAAkB,IAAI,CAAC;IA8C5C,CAAC;IA5CC,KAAK,CAAC,MAA2B;QAC/B,IAAI,CAAC,MAAM,CAAC,YAAY,EAAE,CAAC;YACzB,OAAO,IAAI,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC;QACnC,CAAC;QAED,MAAM,EAAE,GAAG,MAAM,CAAC,WAAW,CAAC;QAC9B,MAAM,SAAS,GAAG,CAAC,CAAC,EAAE,CAAC,GAAG,CAAC,cAAc,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,eAAe,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;QAEvF,IAAI,CAAC,IAAI,CAAC,aAAa,EAAE,CAAC;YACxB,IAAI,SAAS,IAAI,eAAe,EAAE,CAAC;gBACjC,IAAI,CAAC,cAAc,EAAE,CAAC;gBACtB,IAAI,IAAI,CAAC,cAAc,IAAI,iBAAiB,EAAE,CAAC;oBAC7C,IAAI,CAAC,aAAa,GAAG,IAAI,CAAC;gBAC5B,CAAC;YACH,CAAC;iBAAM,CAAC;gBACN,IAAI,CAAC,cAAc,GAAG,CAAC,CAAC;YAC1B,CAAC;QACH,CAAC;QAED,IAAI,CAAC,IAAI,CAAC,cAAc,EAAE,CAAC;YACzB,MAAM,GAAG,GAAG,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,eAAe,CAAC,CAAC;YACpD,IAAI,IAAI,CAAC,WAAW,KAAK,IAAI,EAAE,CAAC;gBAC9B,IAAI,CAAC,WAAW,GAAG,GAAG,CAAC;YACzB,CAAC;iBAAM,IAAI,IAAI,CAAC,GAAG,CAAC,GAAG,GAAG,IAAI,CAAC,WAAW,CAAC,IAAI,wBAAwB,EAAE,CAAC;gBACxE,IAAI,CAAC,cAAc,GAAG,IAAI,CAAC;YAC7B,CAAC;QACH,CAAC;QAED,OAAO,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,CAAC;IAClC,CAAC;IAEO,aAAa,CAAC,YAAqB;QACzC,OAAO;YACL,YAAY;YACZ,aAAa,EAAE,IAAI,CAAC,aAAa;YACjC,cAAc,EAAE,IAAI,CAAC,cAAc;YACnC,iBAAiB,EAAE,IAAI,CAAC,aAAa,IAAI,IAAI,CAAC,cAAc;SAC7D,CAAC;IACJ,CAAC;IAEO,UAAU,CAAC,MAAuB;QACxC,IAAI,CAAC,MAAM,IAAI,MAAM,CAAC,MAAM,GAAG,EAAE;YAAE,OAAO,CAAC,CAAC;QAC5C,OAAO,IAAI,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,GAAG,IAAI,CAAC,EAAE,CAAC,CAAC;IAC7D,CAAC;CACF;AAlDD,wCAkDC"}
|
|
@@ -1,28 +1,17 @@
|
|
|
1
1
|
import { FaceDetectionResult } from "./FaceDetector";
|
|
2
|
-
export interface
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
2
|
+
export interface ScanProgress {
|
|
3
|
+
faceDetected: boolean;
|
|
4
|
+
blinkDetected: boolean;
|
|
5
|
+
motionDetected: boolean;
|
|
6
|
+
livenessConfirmed: boolean;
|
|
7
7
|
}
|
|
8
|
-
/**
|
|
9
|
-
* Sequential action detector aligned with backend's 5 UPPERCASE actions:
|
|
10
|
-
* BLINK, TURN_LEFT, TURN_RIGHT, TURN_HEAD, OPEN_MOUTH
|
|
11
|
-
*/
|
|
12
8
|
export declare class ActionDetector {
|
|
13
|
-
private
|
|
14
|
-
private
|
|
15
|
-
private
|
|
16
|
-
private
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
check(result: FaceDetectionResult): ActionState[];
|
|
20
|
-
getCurrentAction(): string | null;
|
|
21
|
-
private buildStates;
|
|
22
|
-
private getConfidence;
|
|
9
|
+
private blinkHoldCount;
|
|
10
|
+
private blinkDetected;
|
|
11
|
+
private motionDetected;
|
|
12
|
+
private baselineYaw;
|
|
13
|
+
check(result: FaceDetectionResult): ScanProgress;
|
|
14
|
+
private buildProgress;
|
|
23
15
|
private getHeadYaw;
|
|
24
|
-
allCompleted(): boolean;
|
|
25
|
-
completedCount(): number;
|
|
26
|
-
reset(): void;
|
|
27
16
|
}
|
|
28
17
|
//# sourceMappingURL=ActionDetector.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ActionDetector.d.ts","sourceRoot":"","sources":["../../../src/camera/ActionDetector.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,MAAM,gBAAgB,CAAC;
|
|
1
|
+
{"version":3,"file":"ActionDetector.d.ts","sourceRoot":"","sources":["../../../src/camera/ActionDetector.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,MAAM,gBAAgB,CAAC;AAsBrD,MAAM,WAAW,YAAY;IAC3B,YAAY,EAAE,OAAO,CAAC;IACtB,aAAa,EAAE,OAAO,CAAC;IACvB,cAAc,EAAE,OAAO,CAAC;IACxB,iBAAiB,EAAE,OAAO,CAAC;CAC5B;AAED,qBAAa,cAAc;IACzB,OAAO,CAAC,cAAc,CAAK;IAC3B,OAAO,CAAC,aAAa,CAAS;IAC9B,OAAO,CAAC,cAAc,CAAS;IAC/B,OAAO,CAAC,WAAW,CAAuB;IAE1C,KAAK,CAAC,MAAM,EAAE,mBAAmB,GAAG,YAAY;IA+BhD,OAAO,CAAC,aAAa;IASrB,OAAO,CAAC,UAAU;CAInB"}
|
|
@@ -1,132 +1,68 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
* - threshold : minimum confidence score required to start counting.
|
|
4
|
-
* - holdFrames: consecutive frames above threshold before action is confirmed.
|
|
2
|
+
* Passive natural-motion liveness detector.
|
|
5
3
|
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
const DEFAULT_CONFIG = { threshold: 0.40, holdFrames: 4 };
|
|
18
|
-
/**
|
|
19
|
-
* Sequential action detector aligned with backend's 5 UPPERCASE actions:
|
|
20
|
-
* BLINK, TURN_LEFT, TURN_RIGHT, TURN_HEAD, OPEN_MOUTH
|
|
4
|
+
* Replaces the earlier sequential "do BLINK, then TURN_HEAD, then OPEN_MOUTH"
|
|
5
|
+
* choreography with a single continuous scan window: the user is simply told
|
|
6
|
+
* to "blink and look around naturally" while the detector watches for signs
|
|
7
|
+
* of a live face (a genuine blink, or natural head movement) without
|
|
8
|
+
* commanding specific actions in order. This mirrors a passive liveness scan
|
|
9
|
+
* rather than a step-by-step obstacle course.
|
|
10
|
+
*
|
|
11
|
+
* Pass criteria: at least one genuine blink OR a natural head-yaw excursion
|
|
12
|
+
* detected at any point during the window — whichever happens first, since a
|
|
13
|
+
* real person glancing around naturally will trigger one or both without
|
|
14
|
+
* being told to.
|
|
21
15
|
*/
|
|
16
|
+
const BLINK_THRESHOLD = 0.25;
|
|
17
|
+
const BLINK_HOLD_FRAMES = 2;
|
|
18
|
+
const YAW_MOTION_THRESHOLD_DEG = 6; // small natural head movement, not a commanded turn
|
|
22
19
|
export class ActionDetector {
|
|
23
|
-
constructor(
|
|
24
|
-
this.
|
|
25
|
-
this.
|
|
26
|
-
this.
|
|
27
|
-
this.
|
|
28
|
-
this.YAW_HISTORY_SIZE = 18; // ~0.6s of frames
|
|
20
|
+
constructor() {
|
|
21
|
+
this.blinkHoldCount = 0;
|
|
22
|
+
this.blinkDetected = false;
|
|
23
|
+
this.motionDetected = false;
|
|
24
|
+
this.baselineYaw = null;
|
|
29
25
|
}
|
|
30
26
|
check(result) {
|
|
31
|
-
const currentAction = this.getCurrentAction();
|
|
32
27
|
if (!result.faceDetected) {
|
|
33
|
-
return this.
|
|
28
|
+
return this.buildProgress(false);
|
|
34
29
|
}
|
|
35
30
|
const bs = result.blendshapes;
|
|
36
|
-
|
|
37
|
-
if (
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
this.holdCounters.set(currentAction, count);
|
|
43
|
-
if (count >= cfg.holdFrames) {
|
|
44
|
-
this.completedActions.add(currentAction);
|
|
45
|
-
this.holdCounters.delete(currentAction);
|
|
31
|
+
const blinkConf = ((bs.get("eyeBlinkLeft") || 0) + (bs.get("eyeBlinkRight") || 0)) / 2;
|
|
32
|
+
if (!this.blinkDetected) {
|
|
33
|
+
if (blinkConf >= BLINK_THRESHOLD) {
|
|
34
|
+
this.blinkHoldCount++;
|
|
35
|
+
if (this.blinkHoldCount >= BLINK_HOLD_FRAMES) {
|
|
36
|
+
this.blinkDetected = true;
|
|
46
37
|
}
|
|
47
38
|
}
|
|
48
39
|
else {
|
|
49
|
-
this.
|
|
40
|
+
this.blinkHoldCount = 0;
|
|
50
41
|
}
|
|
51
|
-
return this.buildStates(currentAction, conf);
|
|
52
|
-
}
|
|
53
|
-
return this.buildStates(currentAction, 0);
|
|
54
|
-
}
|
|
55
|
-
getCurrentAction() {
|
|
56
|
-
for (const action of this.requiredActions) {
|
|
57
|
-
if (!this.completedActions.has(action))
|
|
58
|
-
return action;
|
|
59
42
|
}
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
action,
|
|
65
|
-
detected: this.completedActions.has(action),
|
|
66
|
-
active: action === currentAction,
|
|
67
|
-
confidence: action === currentAction ? currentConf : 0,
|
|
68
|
-
}));
|
|
69
|
-
}
|
|
70
|
-
getConfidence(action, bs, matrix) {
|
|
71
|
-
switch (action) {
|
|
72
|
-
case "BLINK": {
|
|
73
|
-
const left = bs.get("eyeBlinkLeft") || 0;
|
|
74
|
-
const right = bs.get("eyeBlinkRight") || 0;
|
|
75
|
-
return (left + right) / 2;
|
|
76
|
-
}
|
|
77
|
-
case "TURN_LEFT": {
|
|
78
|
-
// Accept either direction — instruction is "turn sideways"
|
|
79
|
-
// Using absolute yaw so any head turn registers
|
|
80
|
-
const yaw = Math.abs(this.getHeadYaw(matrix));
|
|
81
|
-
return yaw > 5 ? Math.min(1, yaw / 20) : 0;
|
|
43
|
+
if (!this.motionDetected) {
|
|
44
|
+
const yaw = this.getHeadYaw(result.transformMatrix);
|
|
45
|
+
if (this.baselineYaw === null) {
|
|
46
|
+
this.baselineYaw = yaw;
|
|
82
47
|
}
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
const yaw = Math.abs(this.getHeadYaw(matrix));
|
|
86
|
-
return yaw > 5 ? Math.min(1, yaw / 20) : 0;
|
|
48
|
+
else if (Math.abs(yaw - this.baselineYaw) >= YAW_MOTION_THRESHOLD_DEG) {
|
|
49
|
+
this.motionDetected = true;
|
|
87
50
|
}
|
|
88
|
-
case "TURN_HEAD": {
|
|
89
|
-
// Proper side-to-side: head must reach left AND right within the
|
|
90
|
-
// recent frame window. 0.5 when one side is seen, 1.0 when both.
|
|
91
|
-
const yaw = this.getHeadYaw(matrix);
|
|
92
|
-
this.yawHistory.push(yaw);
|
|
93
|
-
if (this.yawHistory.length > this.YAW_HISTORY_SIZE) {
|
|
94
|
-
this.yawHistory.shift();
|
|
95
|
-
}
|
|
96
|
-
if (this.yawHistory.length < this.YAW_HISTORY_SIZE) {
|
|
97
|
-
return 0;
|
|
98
|
-
}
|
|
99
|
-
const left = this.yawHistory.some((y) => y <= -10);
|
|
100
|
-
const right = this.yawHistory.some((y) => y >= 10);
|
|
101
|
-
if (left && right)
|
|
102
|
-
return 1.0;
|
|
103
|
-
if (left || right)
|
|
104
|
-
return 0.5;
|
|
105
|
-
return 0;
|
|
106
|
-
}
|
|
107
|
-
case "OPEN_MOUTH":
|
|
108
|
-
return bs.get("jawOpen") || 0;
|
|
109
|
-
default:
|
|
110
|
-
return 0;
|
|
111
51
|
}
|
|
52
|
+
return this.buildProgress(true);
|
|
53
|
+
}
|
|
54
|
+
buildProgress(faceDetected) {
|
|
55
|
+
return {
|
|
56
|
+
faceDetected,
|
|
57
|
+
blinkDetected: this.blinkDetected,
|
|
58
|
+
motionDetected: this.motionDetected,
|
|
59
|
+
livenessConfirmed: this.blinkDetected || this.motionDetected,
|
|
60
|
+
};
|
|
112
61
|
}
|
|
113
62
|
getHeadYaw(matrix) {
|
|
114
63
|
if (!matrix || matrix.length < 16)
|
|
115
64
|
return 0;
|
|
116
|
-
// 4x4 column-major matrix from MediaPipe FaceLandmarker
|
|
117
|
-
// Yaw (rotation around Y axis): atan2(-m20, m00) where m20=matrix[2], m00=matrix[0]
|
|
118
65
|
return Math.atan2(-matrix[2], matrix[0]) * (180 / Math.PI);
|
|
119
66
|
}
|
|
120
|
-
allCompleted() {
|
|
121
|
-
return this.requiredActions.every((a) => this.completedActions.has(a));
|
|
122
|
-
}
|
|
123
|
-
completedCount() {
|
|
124
|
-
return this.completedActions.size;
|
|
125
|
-
}
|
|
126
|
-
reset() {
|
|
127
|
-
this.completedActions.clear();
|
|
128
|
-
this.holdCounters.clear();
|
|
129
|
-
this.yawHistory = [];
|
|
130
|
-
}
|
|
131
67
|
}
|
|
132
68
|
//# sourceMappingURL=ActionDetector.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ActionDetector.js","sourceRoot":"","sources":["../../../src/camera/ActionDetector.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"ActionDetector.js","sourceRoot":"","sources":["../../../src/camera/ActionDetector.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;GAcG;AAEH,MAAM,eAAe,GAAG,IAAI,CAAC;AAC7B,MAAM,iBAAiB,GAAG,CAAC,CAAC;AAC5B,MAAM,wBAAwB,GAAG,CAAC,CAAC,CAAC,oDAAoD;AASxF,MAAM,OAAO,cAAc;IAA3B;QACU,mBAAc,GAAG,CAAC,CAAC;QACnB,kBAAa,GAAG,KAAK,CAAC;QACtB,mBAAc,GAAG,KAAK,CAAC;QACvB,gBAAW,GAAkB,IAAI,CAAC;IA8C5C,CAAC;IA5CC,KAAK,CAAC,MAA2B;QAC/B,IAAI,CAAC,MAAM,CAAC,YAAY,EAAE,CAAC;YACzB,OAAO,IAAI,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC;QACnC,CAAC;QAED,MAAM,EAAE,GAAG,MAAM,CAAC,WAAW,CAAC;QAC9B,MAAM,SAAS,GAAG,CAAC,CAAC,EAAE,CAAC,GAAG,CAAC,cAAc,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,eAAe,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;QAEvF,IAAI,CAAC,IAAI,CAAC,aAAa,EAAE,CAAC;YACxB,IAAI,SAAS,IAAI,eAAe,EAAE,CAAC;gBACjC,IAAI,CAAC,cAAc,EAAE,CAAC;gBACtB,IAAI,IAAI,CAAC,cAAc,IAAI,iBAAiB,EAAE,CAAC;oBAC7C,IAAI,CAAC,aAAa,GAAG,IAAI,CAAC;gBAC5B,CAAC;YACH,CAAC;iBAAM,CAAC;gBACN,IAAI,CAAC,cAAc,GAAG,CAAC,CAAC;YAC1B,CAAC;QACH,CAAC;QAED,IAAI,CAAC,IAAI,CAAC,cAAc,EAAE,CAAC;YACzB,MAAM,GAAG,GAAG,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,eAAe,CAAC,CAAC;YACpD,IAAI,IAAI,CAAC,WAAW,KAAK,IAAI,EAAE,CAAC;gBAC9B,IAAI,CAAC,WAAW,GAAG,GAAG,CAAC;YACzB,CAAC;iBAAM,IAAI,IAAI,CAAC,GAAG,CAAC,GAAG,GAAG,IAAI,CAAC,WAAW,CAAC,IAAI,wBAAwB,EAAE,CAAC;gBACxE,IAAI,CAAC,cAAc,GAAG,IAAI,CAAC;YAC7B,CAAC;QACH,CAAC;QAED,OAAO,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,CAAC;IAClC,CAAC;IAEO,aAAa,CAAC,YAAqB;QACzC,OAAO;YACL,YAAY;YACZ,aAAa,EAAE,IAAI,CAAC,aAAa;YACjC,cAAc,EAAE,IAAI,CAAC,cAAc;YACnC,iBAAiB,EAAE,IAAI,CAAC,aAAa,IAAI,IAAI,CAAC,cAAc;SAC7D,CAAC;IACJ,CAAC;IAEO,UAAU,CAAC,MAAuB;QACxC,IAAI,CAAC,MAAM,IAAI,MAAM,CAAC,MAAM,GAAG,EAAE;YAAE,OAAO,CAAC,CAAC;QAC5C,OAAO,IAAI,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,GAAG,IAAI,CAAC,EAAE,CAAC,CAAC;IAC7D,CAAC;CACF"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SmartComplyFlow.d.ts","sourceRoot":"","sources":["../../../src/flow/SmartComplyFlow.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"SmartComplyFlow.d.ts","sourceRoot":"","sources":["../../../src/flow/SmartComplyFlow.ts"],"names":[],"mappings":"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"}
|