@simprints/simface-sdk 0.3.1 → 0.5.1
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/components/simface-capture.d.ts +71 -0
- package/dist/components/simface-capture.js +767 -0
- package/dist/components/simface-capture.js.map +1 -0
- package/dist/index.d.ts +36 -0
- package/dist/index.js +69 -0
- package/dist/index.js.map +1 -0
- package/dist/services/api-client.d.ts +10 -0
- package/dist/services/api-client.js +61 -0
- package/dist/services/api-client.js.map +1 -0
- package/dist/services/camera.d.ts +18 -0
- package/dist/services/camera.js +551 -0
- package/dist/services/camera.js.map +1 -0
- package/dist/services/face-detection.d.ts +9 -0
- package/dist/services/face-detection.js +69 -0
- package/dist/services/face-detection.js.map +1 -0
- package/dist/services/face-quality.d.ts +23 -0
- package/dist/services/face-quality.js +136 -0
- package/dist/services/face-quality.js.map +1 -0
- package/dist/simface-sdk.js +2778 -2346
- package/dist/simface-sdk.umd.cjs +221 -38
- package/dist/types/index.d.ts +61 -0
- package/dist/types/index.js +2 -0
- package/dist/types/index.js.map +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { FaceQualityResult } from '../types/index.js';
|
|
2
|
+
export interface FaceBoundingBox {
|
|
3
|
+
originX: number;
|
|
4
|
+
originY: number;
|
|
5
|
+
width: number;
|
|
6
|
+
height: number;
|
|
7
|
+
}
|
|
8
|
+
export interface FaceKeypoint {
|
|
9
|
+
x: number;
|
|
10
|
+
y: number;
|
|
11
|
+
}
|
|
12
|
+
export interface FaceDetectionSnapshot {
|
|
13
|
+
boundingBox?: FaceBoundingBox;
|
|
14
|
+
confidence: number;
|
|
15
|
+
keypoints: FaceKeypoint[];
|
|
16
|
+
}
|
|
17
|
+
interface FaceQualityInput {
|
|
18
|
+
width: number;
|
|
19
|
+
height: number;
|
|
20
|
+
detections: FaceDetectionSnapshot[];
|
|
21
|
+
}
|
|
22
|
+
export declare function evaluateFaceQuality(input: FaceQualityInput): FaceQualityResult;
|
|
23
|
+
export {};
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
const MIN_FACE_AREA_RATIO = 0.1;
|
|
2
|
+
const MAX_FACE_AREA_RATIO = 0.42;
|
|
3
|
+
const IDEAL_FACE_AREA_RATIO = 0.24;
|
|
4
|
+
const CENTER_TOLERANCE_X = 0.14;
|
|
5
|
+
const CENTER_TOLERANCE_Y = 0.18;
|
|
6
|
+
const MAX_NOSE_OFFSET_RATIO = 0.12;
|
|
7
|
+
const KEYPOINT_RIGHT_EYE = 0;
|
|
8
|
+
const KEYPOINT_LEFT_EYE = 1;
|
|
9
|
+
const KEYPOINT_NOSE = 2;
|
|
10
|
+
export function evaluateFaceQuality(input) {
|
|
11
|
+
const { detections, width, height } = input;
|
|
12
|
+
if (detections.length === 0) {
|
|
13
|
+
return createQualityResult({
|
|
14
|
+
hasFace: false,
|
|
15
|
+
faceCount: 0,
|
|
16
|
+
confidence: 0,
|
|
17
|
+
captureScore: 0,
|
|
18
|
+
isCentered: false,
|
|
19
|
+
passesQualityChecks: false,
|
|
20
|
+
feedback: 'no-face',
|
|
21
|
+
message: 'No face detected. Center your face in the oval and look at the camera.',
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
if (detections.length > 1) {
|
|
25
|
+
return createQualityResult({
|
|
26
|
+
hasFace: true,
|
|
27
|
+
faceCount: detections.length,
|
|
28
|
+
confidence: detections[0]?.confidence ?? 0,
|
|
29
|
+
captureScore: 0,
|
|
30
|
+
isCentered: false,
|
|
31
|
+
passesQualityChecks: false,
|
|
32
|
+
feedback: 'multiple-faces',
|
|
33
|
+
message: 'Multiple faces detected. Make sure only one person is in view.',
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
const detection = detections[0];
|
|
37
|
+
const bbox = detection.boundingBox;
|
|
38
|
+
if (!bbox) {
|
|
39
|
+
return createQualityResult({
|
|
40
|
+
hasFace: true,
|
|
41
|
+
faceCount: 1,
|
|
42
|
+
confidence: detection.confidence,
|
|
43
|
+
captureScore: 0,
|
|
44
|
+
isCentered: false,
|
|
45
|
+
passesQualityChecks: false,
|
|
46
|
+
feedback: 'face-unclear',
|
|
47
|
+
message: 'Face detected, but the frame is unclear. Hold still and try again.',
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
const faceAreaRatio = (bbox.width * bbox.height) / (width * height);
|
|
51
|
+
if (faceAreaRatio < MIN_FACE_AREA_RATIO) {
|
|
52
|
+
return createFrameAdjustmentResult(detection.confidence, 'too-far', 'Move closer to the camera.');
|
|
53
|
+
}
|
|
54
|
+
if (faceAreaRatio > MAX_FACE_AREA_RATIO) {
|
|
55
|
+
return createFrameAdjustmentResult(detection.confidence, 'too-close', 'Move slightly farther away.');
|
|
56
|
+
}
|
|
57
|
+
const faceCenterX = (bbox.originX + bbox.width / 2) / width;
|
|
58
|
+
const faceCenterY = (bbox.originY + bbox.height / 2) / height;
|
|
59
|
+
if (faceCenterX < 0.5 - CENTER_TOLERANCE_X) {
|
|
60
|
+
return createFrameAdjustmentResult(detection.confidence, 'move-right', 'Move your face a little to the right.');
|
|
61
|
+
}
|
|
62
|
+
if (faceCenterX > 0.5 + CENTER_TOLERANCE_X) {
|
|
63
|
+
return createFrameAdjustmentResult(detection.confidence, 'move-left', 'Move your face a little to the left.');
|
|
64
|
+
}
|
|
65
|
+
if (faceCenterY < 0.5 - CENTER_TOLERANCE_Y) {
|
|
66
|
+
return createFrameAdjustmentResult(detection.confidence, 'move-down', 'Move your face slightly down.');
|
|
67
|
+
}
|
|
68
|
+
if (faceCenterY > 0.5 + CENTER_TOLERANCE_Y) {
|
|
69
|
+
return createFrameAdjustmentResult(detection.confidence, 'move-up', 'Move your face slightly up.');
|
|
70
|
+
}
|
|
71
|
+
const turnFeedback = detectTurnFeedback(detection.keypoints);
|
|
72
|
+
if (turnFeedback) {
|
|
73
|
+
return createFrameAdjustmentResult(detection.confidence, turnFeedback.feedback, turnFeedback.message);
|
|
74
|
+
}
|
|
75
|
+
return createQualityResult({
|
|
76
|
+
hasFace: true,
|
|
77
|
+
faceCount: 1,
|
|
78
|
+
confidence: detection.confidence,
|
|
79
|
+
captureScore: calculateCaptureScore(detection.confidence, faceCenterX, faceCenterY, faceAreaRatio),
|
|
80
|
+
isCentered: true,
|
|
81
|
+
passesQualityChecks: true,
|
|
82
|
+
feedback: 'good',
|
|
83
|
+
message: 'Hold still. Capturing automatically...',
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
function detectTurnFeedback(keypoints) {
|
|
87
|
+
const rightEye = keypoints[KEYPOINT_RIGHT_EYE];
|
|
88
|
+
const leftEye = keypoints[KEYPOINT_LEFT_EYE];
|
|
89
|
+
const nose = keypoints[KEYPOINT_NOSE];
|
|
90
|
+
if (!rightEye || !leftEye || !nose) {
|
|
91
|
+
return null;
|
|
92
|
+
}
|
|
93
|
+
const eyeMidpointX = (rightEye.x + leftEye.x) / 2;
|
|
94
|
+
const eyeDistance = Math.abs(leftEye.x - rightEye.x);
|
|
95
|
+
if (eyeDistance === 0) {
|
|
96
|
+
return null;
|
|
97
|
+
}
|
|
98
|
+
const noseOffsetRatio = (nose.x - eyeMidpointX) / eyeDistance;
|
|
99
|
+
if (noseOffsetRatio <= -MAX_NOSE_OFFSET_RATIO) {
|
|
100
|
+
return {
|
|
101
|
+
feedback: 'turn-right',
|
|
102
|
+
message: 'Turn slightly right so your face points at the camera.',
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
if (noseOffsetRatio >= MAX_NOSE_OFFSET_RATIO) {
|
|
106
|
+
return {
|
|
107
|
+
feedback: 'turn-left',
|
|
108
|
+
message: 'Turn slightly left so your face points at the camera.',
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
return null;
|
|
112
|
+
}
|
|
113
|
+
function createFrameAdjustmentResult(confidence, feedback, message) {
|
|
114
|
+
return createQualityResult({
|
|
115
|
+
hasFace: true,
|
|
116
|
+
faceCount: 1,
|
|
117
|
+
confidence,
|
|
118
|
+
captureScore: 0,
|
|
119
|
+
isCentered: false,
|
|
120
|
+
passesQualityChecks: false,
|
|
121
|
+
feedback,
|
|
122
|
+
message,
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
function createQualityResult(result) {
|
|
126
|
+
return result;
|
|
127
|
+
}
|
|
128
|
+
function calculateCaptureScore(confidence, faceCenterX, faceCenterY, faceAreaRatio) {
|
|
129
|
+
const xPenalty = Math.abs(faceCenterX - 0.5) / CENTER_TOLERANCE_X;
|
|
130
|
+
const yPenalty = Math.abs(faceCenterY - 0.5) / CENTER_TOLERANCE_Y;
|
|
131
|
+
const centerScore = 1 - Math.min((xPenalty + yPenalty) / 2, 1);
|
|
132
|
+
const maxAreaDistance = Math.max(Math.abs(IDEAL_FACE_AREA_RATIO - MIN_FACE_AREA_RATIO), Math.abs(MAX_FACE_AREA_RATIO - IDEAL_FACE_AREA_RATIO));
|
|
133
|
+
const sizeScore = 1 - Math.min(Math.abs(faceAreaRatio - IDEAL_FACE_AREA_RATIO) / maxAreaDistance, 1);
|
|
134
|
+
return Number((confidence * 0.55 + centerScore * 0.3 + sizeScore * 0.15).toFixed(4));
|
|
135
|
+
}
|
|
136
|
+
//# sourceMappingURL=face-quality.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"face-quality.js","sourceRoot":"","sources":["../../src/services/face-quality.ts"],"names":[],"mappings":"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"}
|