@moveris/shared 2.4.0 → 2.6.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/dist/index.d.mts +62 -44
- package/dist/index.d.ts +62 -44
- package/dist/index.js +100 -15
- package/dist/index.mjs +94 -15
- package/package.json +1 -1
package/dist/index.d.mts
CHANGED
|
@@ -215,6 +215,40 @@ interface LivenessCallbacks {
|
|
|
215
215
|
onStateChange?: OnStateChangeCallback;
|
|
216
216
|
}
|
|
217
217
|
|
|
218
|
+
interface FaceLandmarkPoint {
|
|
219
|
+
x: number;
|
|
220
|
+
y: number;
|
|
221
|
+
z: number;
|
|
222
|
+
}
|
|
223
|
+
interface LandmarkValidationResult {
|
|
224
|
+
valid: boolean;
|
|
225
|
+
message?: string;
|
|
226
|
+
}
|
|
227
|
+
declare const LANDMARK_INDEX: {
|
|
228
|
+
readonly NOSE_TIP: 1;
|
|
229
|
+
readonly UPPER_LIP: 13;
|
|
230
|
+
readonly LOWER_LIP: 14;
|
|
231
|
+
};
|
|
232
|
+
declare const EYE_LANDMARK_INDICES: {
|
|
233
|
+
readonly RIGHT_EYE: readonly [33, 7, 163, 144, 145, 153, 154, 155, 133, 173, 157, 158, 159, 160, 161, 246];
|
|
234
|
+
readonly LEFT_EYE: readonly [362, 382, 381, 380, 374, 373, 390, 249, 263, 466, 388, 387, 386, 385, 384, 398];
|
|
235
|
+
};
|
|
236
|
+
interface EyeRegionBounds {
|
|
237
|
+
x: number;
|
|
238
|
+
y: number;
|
|
239
|
+
width: number;
|
|
240
|
+
height: number;
|
|
241
|
+
}
|
|
242
|
+
interface EyeRegionsBounds {
|
|
243
|
+
leftEye: EyeRegionBounds;
|
|
244
|
+
rightEye: EyeRegionBounds;
|
|
245
|
+
}
|
|
246
|
+
declare function getEyeRegionBounds(landmarks: FaceLandmarkPoint[]): EyeRegionsBounds | null;
|
|
247
|
+
declare const LANDMARK_MIN_BOUND = 0.1;
|
|
248
|
+
declare const LANDMARK_MAX_BOUND = 0.9;
|
|
249
|
+
declare const MIN_LANDMARK_COUNT = 15;
|
|
250
|
+
declare function validateFaceLandmarks(landmarks: FaceLandmarkPoint[] | undefined): LandmarkValidationResult;
|
|
251
|
+
|
|
218
252
|
interface FaceBoundingBox {
|
|
219
253
|
originX: number;
|
|
220
254
|
originY: number;
|
|
@@ -263,7 +297,7 @@ interface FrameQualityResult {
|
|
|
263
297
|
rejectionReason?: 'blur' | 'backlit' | 'low_light' | 'partial_face' | 'no_face' | 'too_close' | 'too_far' | 'misaligned';
|
|
264
298
|
}
|
|
265
299
|
declare const DEFAULT_BLUR_THRESHOLD = 100;
|
|
266
|
-
declare const BLUR_THRESHOLD_MOBILE =
|
|
300
|
+
declare const BLUR_THRESHOLD_MOBILE = 60;
|
|
267
301
|
declare const BACKLIT_RATIO_THRESHOLD = 0.6;
|
|
268
302
|
declare const LOW_LIGHT_THRESHOLD = 50;
|
|
269
303
|
declare const MIN_FACE_TOP_MARGIN = 0.1;
|
|
@@ -272,20 +306,24 @@ declare const MIN_FACE_SIDE_MARGIN = 0.05;
|
|
|
272
306
|
declare const MIN_CAPTURE_ALIGNMENT = 0.6;
|
|
273
307
|
declare const HIGH_ALIGNMENT = 0.85;
|
|
274
308
|
declare const GOOD_ALIGNMENT = 0.5;
|
|
275
|
-
declare const IDEAL_CROP_MULTIPLIER =
|
|
276
|
-
declare const MIN_CROP_MULTIPLIER =
|
|
277
|
-
declare const MAX_CROP_MULTIPLIER =
|
|
309
|
+
declare const IDEAL_CROP_MULTIPLIER = 3;
|
|
310
|
+
declare const MIN_CROP_MULTIPLIER = 2.5;
|
|
311
|
+
declare const MAX_CROP_MULTIPLIER = 4;
|
|
278
312
|
declare const FACE_CENTER_VERTICAL_OFFSET = 0.05;
|
|
313
|
+
declare const MIN_IDEAL_FACE_RATIO = 0.05;
|
|
314
|
+
declare const MAX_IDEAL_FACE_RATIO = 0.2;
|
|
279
315
|
declare const MIN_FACE_RATIO = 0.036;
|
|
280
316
|
declare const MAX_FACE_RATIO = 0.7;
|
|
281
317
|
declare const FACE_CROP_OUTPUT_SIZE = 224;
|
|
282
|
-
declare const MAX_FACE_PERCENTAGE_IN_CROP = 0.
|
|
283
|
-
declare const TARGET_FACE_PERCENTAGE_IN_CROP = 0.
|
|
318
|
+
declare const MAX_FACE_PERCENTAGE_IN_CROP = 0.45;
|
|
319
|
+
declare const TARGET_FACE_PERCENTAGE_IN_CROP = 0.33;
|
|
284
320
|
declare function analyzeBlur(grayscalePixels: number[], width: number, height: number, threshold?: number): BlurAnalysis;
|
|
285
321
|
declare function rgbaToGrayscale(rgbaPixels: Uint8ClampedArray | number[]): number[];
|
|
286
322
|
declare function calculateBrightness(rgbaPixels: Uint8ClampedArray | number[]): number;
|
|
287
323
|
declare function analyzeLighting(faceBrightness: number, backgroundBrightness: number): LightingAnalysis;
|
|
288
324
|
declare function isFaceFullyVisible(boundingBox: FaceBoundingBox, frameWidth: number, frameHeight: number): FaceVisibilityResult;
|
|
325
|
+
declare const OVAL_REGION_DESKTOP: OvalRegion;
|
|
326
|
+
declare const OVAL_REGION_MOBILE: OvalRegion;
|
|
289
327
|
declare const DEFAULT_OVAL_REGION: OvalRegion;
|
|
290
328
|
declare function isFaceInOval(faceBox: FaceBoundingBox, frameWidth: number, frameHeight: number, oval?: OvalRegion, tolerance?: number): FaceInOvalResult;
|
|
291
329
|
declare function calculateFaceAlignment(boundingBox: FaceBoundingBox, frameWidth: number, frameHeight: number): FaceAlignmentResult;
|
|
@@ -304,6 +342,12 @@ declare function checkFrameQuality(options: {
|
|
|
304
342
|
lightingAnalysis?: LightingAnalysis;
|
|
305
343
|
minAlignment?: number;
|
|
306
344
|
}): FrameQualityResult;
|
|
345
|
+
interface FaceRollResult {
|
|
346
|
+
roll: number;
|
|
347
|
+
tooTilted: boolean;
|
|
348
|
+
}
|
|
349
|
+
declare const MAX_FACE_ROLL_DEGREES = 15;
|
|
350
|
+
declare function detectFaceRoll(landmarks: FaceLandmarkPoint[]): FaceRollResult;
|
|
307
351
|
declare class BaseFrameCollector {
|
|
308
352
|
protected frames: CapturedFrame[];
|
|
309
353
|
protected maxFrames: number;
|
|
@@ -319,40 +363,6 @@ declare class BaseFrameCollector {
|
|
|
319
363
|
getNextIndex(): number;
|
|
320
364
|
}
|
|
321
365
|
|
|
322
|
-
interface FaceLandmarkPoint {
|
|
323
|
-
x: number;
|
|
324
|
-
y: number;
|
|
325
|
-
z: number;
|
|
326
|
-
}
|
|
327
|
-
interface LandmarkValidationResult {
|
|
328
|
-
valid: boolean;
|
|
329
|
-
message?: string;
|
|
330
|
-
}
|
|
331
|
-
declare const LANDMARK_INDEX: {
|
|
332
|
-
readonly NOSE_TIP: 1;
|
|
333
|
-
readonly UPPER_LIP: 13;
|
|
334
|
-
readonly LOWER_LIP: 14;
|
|
335
|
-
};
|
|
336
|
-
declare const EYE_LANDMARK_INDICES: {
|
|
337
|
-
readonly RIGHT_EYE: readonly [33, 7, 163, 144, 145, 153, 154, 155, 133, 173, 157, 158, 159, 160, 161, 246];
|
|
338
|
-
readonly LEFT_EYE: readonly [362, 382, 381, 380, 374, 373, 390, 249, 263, 466, 388, 387, 386, 385, 384, 398];
|
|
339
|
-
};
|
|
340
|
-
interface EyeRegionBounds {
|
|
341
|
-
x: number;
|
|
342
|
-
y: number;
|
|
343
|
-
width: number;
|
|
344
|
-
height: number;
|
|
345
|
-
}
|
|
346
|
-
interface EyeRegionsBounds {
|
|
347
|
-
leftEye: EyeRegionBounds;
|
|
348
|
-
rightEye: EyeRegionBounds;
|
|
349
|
-
}
|
|
350
|
-
declare function getEyeRegionBounds(landmarks: FaceLandmarkPoint[]): EyeRegionsBounds | null;
|
|
351
|
-
declare const LANDMARK_MIN_BOUND = 0.1;
|
|
352
|
-
declare const LANDMARK_MAX_BOUND = 0.9;
|
|
353
|
-
declare const MIN_LANDMARK_COUNT = 15;
|
|
354
|
-
declare function validateFaceLandmarks(landmarks: FaceLandmarkPoint[] | undefined): LandmarkValidationResult;
|
|
355
|
-
|
|
356
366
|
interface DetectionResult {
|
|
357
367
|
type: string;
|
|
358
368
|
passed: boolean;
|
|
@@ -618,9 +628,9 @@ declare const ERROR_MESSAGES_ES: Record<string, string>;
|
|
|
618
628
|
declare function getApiErrorMessage(code: string | undefined, message?: string, customMessages?: Record<string, string>): string;
|
|
619
629
|
declare function isRetryableError(code: string | undefined): boolean;
|
|
620
630
|
|
|
621
|
-
declare const ALIGNMENT_THRESHOLD_CAPTURE = 0.
|
|
622
|
-
declare const ALIGNMENT_THRESHOLD_POOR = 0.
|
|
623
|
-
declare const ALIGNMENT_THRESHOLD_GOOD = 0.
|
|
631
|
+
declare const ALIGNMENT_THRESHOLD_CAPTURE = 0.85;
|
|
632
|
+
declare const ALIGNMENT_THRESHOLD_POOR = 0.6;
|
|
633
|
+
declare const ALIGNMENT_THRESHOLD_GOOD = 0.6;
|
|
624
634
|
declare const ALIGNMENT_THRESHOLD_PERFECT = 0.85;
|
|
625
635
|
type OvalGuideState = 'no_face' | 'poor' | 'good' | 'perfect';
|
|
626
636
|
declare const OVAL_GUIDE_COLORS: {
|
|
@@ -675,6 +685,8 @@ declare const FEEDBACK_MESSAGES: {
|
|
|
675
685
|
readonly move_back: "Move back - face too close";
|
|
676
686
|
readonly too_close: "Move back - face too close";
|
|
677
687
|
readonly too_far: "Move closer - face too far";
|
|
688
|
+
readonly move_closer_ideal: "Move a little closer";
|
|
689
|
+
readonly move_back_ideal: "Move back slightly";
|
|
678
690
|
readonly face_not_visible: "Center your face - edges cut off";
|
|
679
691
|
readonly partial_face: "Center your face - edges cut off";
|
|
680
692
|
readonly hold_still: "Hold still - image blurry";
|
|
@@ -682,6 +694,8 @@ declare const FEEDBACK_MESSAGES: {
|
|
|
682
694
|
readonly poor_lighting: "Improve lighting";
|
|
683
695
|
readonly too_dark: "Low lighting - move to a brighter area";
|
|
684
696
|
readonly backlit: "Backlit - try facing the light source";
|
|
697
|
+
readonly phone_angle_low: "Raise your phone to eye level";
|
|
698
|
+
readonly phone_tilted: "Hold your phone level";
|
|
685
699
|
readonly hand_detected: "Remove hand from face";
|
|
686
700
|
readonly eyes_not_visible: "Eyes not clearly visible";
|
|
687
701
|
readonly eyes_shadowed: "Eyes are in shadow - improve lighting";
|
|
@@ -712,6 +726,10 @@ interface CaptureQualityState {
|
|
|
712
726
|
isPartialFace: boolean;
|
|
713
727
|
framesCaptured: number;
|
|
714
728
|
targetFrames: number;
|
|
729
|
+
tooFarFromIdeal?: boolean;
|
|
730
|
+
tooCloseToIdeal?: boolean;
|
|
731
|
+
phoneAngled?: boolean;
|
|
732
|
+
phoneTilted?: boolean;
|
|
715
733
|
}
|
|
716
734
|
declare function getCaptureQualityFeedback(state: CaptureQualityState): string;
|
|
717
735
|
declare function canCaptureFrame(state: Omit<CaptureQualityState, 'framesCaptured' | 'targetFrames' | 'isCapturing'>): boolean;
|
|
@@ -762,4 +780,4 @@ declare function analyzeEyeRegionContrast(pixels: Uint8Array | Uint8ClampedArray
|
|
|
762
780
|
declare function detectSpecularHighlights(pixels: Uint8Array | Uint8ClampedArray, threshold?: number): number;
|
|
763
781
|
declare function checkEyeRegionQuality(pixels: Uint8Array | Uint8ClampedArray, thresholds?: EyeQualityThresholds): EyeRegionQuality;
|
|
764
782
|
|
|
765
|
-
export { ALIGNMENT_THRESHOLD_CAPTURE, ALIGNMENT_THRESHOLD_GOOD, ALIGNMENT_THRESHOLD_PERFECT, ALIGNMENT_THRESHOLD_POOR, API_ENDPOINTS, API_ERROR_CODES, API_PATHS, AUTH_CONFIG, type ApiErrorCode, BACKLIT_RATIO_THRESHOLD, BLUR_THRESHOLD_MOBILE, BaseFrameCollector, type BlurAnalysis, type CaptureQualityState, type CapturedFrame, type CropData, DEFAULT_BLUR_THRESHOLD, DEFAULT_ENDPOINT, DEFAULT_FACE_DETECTION_TIERS, DEFAULT_GAZE_THRESHOLDS, DEFAULT_HAND_OCCLUSION_CONFIG, DEFAULT_LIVENESS_CONFIG, DEFAULT_LOCALE, DEFAULT_OVAL_REGION, DEFAULT_STABILIZER_CONFIG, DEFAULT_STATUS_MESSAGES, type DetectionResult, type DetectionSummary, type DetectorConfig, ERROR_MESSAGES, ERROR_MESSAGES_ES, ES_LOCALE, EYE_LANDMARK_INDICES, EYE_QUALITY_THRESHOLDS, type ErrorResponse, type EyeQualityThresholds, type EyeRegionBounds, type EyeRegionQuality, type EyeRegionsBounds, FACE_CENTER_VERTICAL_OFFSET, FACE_CROP_OUTPUT_SIZE, FEEDBACK_MESSAGES, FRAME_BUFFER_CONFIG, FRAME_CONFIG, type FaceAlignmentResult, type FaceBoundingBox, type FaceDetectionTiers, type FaceInOvalResult, type FaceLandmarkPoint, type FaceVisibilityResult, type FastCheckCropsRequest, type FastCheckModel, type FastCheckRequest, type FastCheckResponse, type FastCheckStreamRequest, type FastCheckStreamResponse, type FeedbackLocale, type FeedbackMessageKey, type Frame, FrameBuffer, type FrameData, type FrameQualityResult, FrameQueue, type FrameSource, GOOD_ALIGNMENT, type GazeThresholds, HIGH_ALIGNMENT, HYBRID_MODEL_CONFIGS, type HandOcclusionConfig, type HeadPose, type HealthResponse, type Hybrid150CheckRequest, type Hybrid50CheckRequest, type HybridCheckRequest, type HybridCheckResponse, type HybridFrameData, type HybridModelConfig, IDEAL_CROP_MULTIPLIER, type JobStatus, type JobStatusResponse, LANDMARK_INDEX, LANDMARK_MAX_BOUND, LANDMARK_MIN_BOUND, LOW_LIGHT_THRESHOLD, type LandmarkValidationResult, type LightingAnalysis, LivenessApiError, type LivenessCallbacks, LivenessClient, type LivenessClientConfig, type LivenessConfig, type LivenessResult, type LivenessState, MAX_CROP_MULTIPLIER, MAX_FACE_PERCENTAGE_IN_CROP, MAX_FACE_RATIO, MIN_CAPTURE_ALIGNMENT, MIN_CROP_MULTIPLIER, MIN_FACE_BOTTOM_MARGIN, MIN_FACE_RATIO, MIN_FACE_SIDE_MARGIN, MIN_FACE_TOP_MARGIN, MIN_LANDMARK_COUNT, MODEL_CONFIGS, type ModelConfig, type ModelEntry, type ModelType, type ModelsResponse, OVAL_GUIDE_COLORS, OVAL_GUIDE_STYLES, type OnErrorCallback, type OnFrameCapturedCallback, type OnProgressCallback, type OnResultCallback, type OnStateChangeCallback, type OvalGuideState, type OvalRegion, type QueueStatsResponse, RETRY_CONFIG, type RetryOptions, type StabilizationProgress, type StabilizationResult, type StabilizerConfig, type StatusMessageKey, type StreamingStatus, TARGET_FACE_PERCENTAGE_IN_CROP, type Verdict, type VerifyRequest, type VerifyResponse, type VideoFrameMetadata, analyzeBlur, analyzeEyeRegionBrightness, analyzeEyeRegionContrast, analyzeLighting, calculateAdaptiveCropMultiplier, calculateBrightness, calculateFaceAlignment, calculateFaceCropRegion, canCaptureFrame, checkEyeRegionQuality, checkFrameQuality, decodeBase64, detectSpecularHighlights, encodeBase64, generateSessionId, getActiveModels, getApiErrorMessage, getCaptureQualityFeedback, getEyeRegionBounds, getFeedbackMessage, getMinFramesForModel, getOvalGuideState, getStatusMessage, hasEnoughFrames, isDeprecatedModel, isFaceCropFullyInFrame, isFaceFullyVisible, isFaceInOval, isRetryableError, retryWithBackoff, rgbaToGrayscale, sleep, toFrameData, toHybridFrameData, toLivenessResult, toLivenessResultFromStream, validateApiKey, validateFaceLandmarks, validateFrameCount, validateFrameData, validateFrameIndex, validateTimestamp, validateUUID, validateUrl };
|
|
783
|
+
export { ALIGNMENT_THRESHOLD_CAPTURE, ALIGNMENT_THRESHOLD_GOOD, ALIGNMENT_THRESHOLD_PERFECT, ALIGNMENT_THRESHOLD_POOR, API_ENDPOINTS, API_ERROR_CODES, API_PATHS, AUTH_CONFIG, type ApiErrorCode, BACKLIT_RATIO_THRESHOLD, BLUR_THRESHOLD_MOBILE, BaseFrameCollector, type BlurAnalysis, type CaptureQualityState, type CapturedFrame, type CropData, DEFAULT_BLUR_THRESHOLD, DEFAULT_ENDPOINT, DEFAULT_FACE_DETECTION_TIERS, DEFAULT_GAZE_THRESHOLDS, DEFAULT_HAND_OCCLUSION_CONFIG, DEFAULT_LIVENESS_CONFIG, DEFAULT_LOCALE, DEFAULT_OVAL_REGION, DEFAULT_STABILIZER_CONFIG, DEFAULT_STATUS_MESSAGES, type DetectionResult, type DetectionSummary, type DetectorConfig, ERROR_MESSAGES, ERROR_MESSAGES_ES, ES_LOCALE, EYE_LANDMARK_INDICES, EYE_QUALITY_THRESHOLDS, type ErrorResponse, type EyeQualityThresholds, type EyeRegionBounds, type EyeRegionQuality, type EyeRegionsBounds, FACE_CENTER_VERTICAL_OFFSET, FACE_CROP_OUTPUT_SIZE, FEEDBACK_MESSAGES, FRAME_BUFFER_CONFIG, FRAME_CONFIG, type FaceAlignmentResult, type FaceBoundingBox, type FaceDetectionTiers, type FaceInOvalResult, type FaceLandmarkPoint, type FaceRollResult, type FaceVisibilityResult, type FastCheckCropsRequest, type FastCheckModel, type FastCheckRequest, type FastCheckResponse, type FastCheckStreamRequest, type FastCheckStreamResponse, type FeedbackLocale, type FeedbackMessageKey, type Frame, FrameBuffer, type FrameData, type FrameQualityResult, FrameQueue, type FrameSource, GOOD_ALIGNMENT, type GazeThresholds, HIGH_ALIGNMENT, HYBRID_MODEL_CONFIGS, type HandOcclusionConfig, type HeadPose, type HealthResponse, type Hybrid150CheckRequest, type Hybrid50CheckRequest, type HybridCheckRequest, type HybridCheckResponse, type HybridFrameData, type HybridModelConfig, IDEAL_CROP_MULTIPLIER, type JobStatus, type JobStatusResponse, LANDMARK_INDEX, LANDMARK_MAX_BOUND, LANDMARK_MIN_BOUND, LOW_LIGHT_THRESHOLD, type LandmarkValidationResult, type LightingAnalysis, LivenessApiError, type LivenessCallbacks, LivenessClient, type LivenessClientConfig, type LivenessConfig, type LivenessResult, type LivenessState, MAX_CROP_MULTIPLIER, MAX_FACE_PERCENTAGE_IN_CROP, MAX_FACE_RATIO, MAX_FACE_ROLL_DEGREES, MAX_IDEAL_FACE_RATIO, MIN_CAPTURE_ALIGNMENT, MIN_CROP_MULTIPLIER, MIN_FACE_BOTTOM_MARGIN, MIN_FACE_RATIO, MIN_FACE_SIDE_MARGIN, MIN_FACE_TOP_MARGIN, MIN_IDEAL_FACE_RATIO, MIN_LANDMARK_COUNT, MODEL_CONFIGS, type ModelConfig, type ModelEntry, type ModelType, type ModelsResponse, OVAL_GUIDE_COLORS, OVAL_GUIDE_STYLES, OVAL_REGION_DESKTOP, OVAL_REGION_MOBILE, type OnErrorCallback, type OnFrameCapturedCallback, type OnProgressCallback, type OnResultCallback, type OnStateChangeCallback, type OvalGuideState, type OvalRegion, type QueueStatsResponse, RETRY_CONFIG, type RetryOptions, type StabilizationProgress, type StabilizationResult, type StabilizerConfig, type StatusMessageKey, type StreamingStatus, TARGET_FACE_PERCENTAGE_IN_CROP, type Verdict, type VerifyRequest, type VerifyResponse, type VideoFrameMetadata, analyzeBlur, analyzeEyeRegionBrightness, analyzeEyeRegionContrast, analyzeLighting, calculateAdaptiveCropMultiplier, calculateBrightness, calculateFaceAlignment, calculateFaceCropRegion, canCaptureFrame, checkEyeRegionQuality, checkFrameQuality, decodeBase64, detectFaceRoll, detectSpecularHighlights, encodeBase64, generateSessionId, getActiveModels, getApiErrorMessage, getCaptureQualityFeedback, getEyeRegionBounds, getFeedbackMessage, getMinFramesForModel, getOvalGuideState, getStatusMessage, hasEnoughFrames, isDeprecatedModel, isFaceCropFullyInFrame, isFaceFullyVisible, isFaceInOval, isRetryableError, retryWithBackoff, rgbaToGrayscale, sleep, toFrameData, toHybridFrameData, toLivenessResult, toLivenessResultFromStream, validateApiKey, validateFaceLandmarks, validateFrameCount, validateFrameData, validateFrameIndex, validateTimestamp, validateUUID, validateUrl };
|
package/dist/index.d.ts
CHANGED
|
@@ -215,6 +215,40 @@ interface LivenessCallbacks {
|
|
|
215
215
|
onStateChange?: OnStateChangeCallback;
|
|
216
216
|
}
|
|
217
217
|
|
|
218
|
+
interface FaceLandmarkPoint {
|
|
219
|
+
x: number;
|
|
220
|
+
y: number;
|
|
221
|
+
z: number;
|
|
222
|
+
}
|
|
223
|
+
interface LandmarkValidationResult {
|
|
224
|
+
valid: boolean;
|
|
225
|
+
message?: string;
|
|
226
|
+
}
|
|
227
|
+
declare const LANDMARK_INDEX: {
|
|
228
|
+
readonly NOSE_TIP: 1;
|
|
229
|
+
readonly UPPER_LIP: 13;
|
|
230
|
+
readonly LOWER_LIP: 14;
|
|
231
|
+
};
|
|
232
|
+
declare const EYE_LANDMARK_INDICES: {
|
|
233
|
+
readonly RIGHT_EYE: readonly [33, 7, 163, 144, 145, 153, 154, 155, 133, 173, 157, 158, 159, 160, 161, 246];
|
|
234
|
+
readonly LEFT_EYE: readonly [362, 382, 381, 380, 374, 373, 390, 249, 263, 466, 388, 387, 386, 385, 384, 398];
|
|
235
|
+
};
|
|
236
|
+
interface EyeRegionBounds {
|
|
237
|
+
x: number;
|
|
238
|
+
y: number;
|
|
239
|
+
width: number;
|
|
240
|
+
height: number;
|
|
241
|
+
}
|
|
242
|
+
interface EyeRegionsBounds {
|
|
243
|
+
leftEye: EyeRegionBounds;
|
|
244
|
+
rightEye: EyeRegionBounds;
|
|
245
|
+
}
|
|
246
|
+
declare function getEyeRegionBounds(landmarks: FaceLandmarkPoint[]): EyeRegionsBounds | null;
|
|
247
|
+
declare const LANDMARK_MIN_BOUND = 0.1;
|
|
248
|
+
declare const LANDMARK_MAX_BOUND = 0.9;
|
|
249
|
+
declare const MIN_LANDMARK_COUNT = 15;
|
|
250
|
+
declare function validateFaceLandmarks(landmarks: FaceLandmarkPoint[] | undefined): LandmarkValidationResult;
|
|
251
|
+
|
|
218
252
|
interface FaceBoundingBox {
|
|
219
253
|
originX: number;
|
|
220
254
|
originY: number;
|
|
@@ -263,7 +297,7 @@ interface FrameQualityResult {
|
|
|
263
297
|
rejectionReason?: 'blur' | 'backlit' | 'low_light' | 'partial_face' | 'no_face' | 'too_close' | 'too_far' | 'misaligned';
|
|
264
298
|
}
|
|
265
299
|
declare const DEFAULT_BLUR_THRESHOLD = 100;
|
|
266
|
-
declare const BLUR_THRESHOLD_MOBILE =
|
|
300
|
+
declare const BLUR_THRESHOLD_MOBILE = 60;
|
|
267
301
|
declare const BACKLIT_RATIO_THRESHOLD = 0.6;
|
|
268
302
|
declare const LOW_LIGHT_THRESHOLD = 50;
|
|
269
303
|
declare const MIN_FACE_TOP_MARGIN = 0.1;
|
|
@@ -272,20 +306,24 @@ declare const MIN_FACE_SIDE_MARGIN = 0.05;
|
|
|
272
306
|
declare const MIN_CAPTURE_ALIGNMENT = 0.6;
|
|
273
307
|
declare const HIGH_ALIGNMENT = 0.85;
|
|
274
308
|
declare const GOOD_ALIGNMENT = 0.5;
|
|
275
|
-
declare const IDEAL_CROP_MULTIPLIER =
|
|
276
|
-
declare const MIN_CROP_MULTIPLIER =
|
|
277
|
-
declare const MAX_CROP_MULTIPLIER =
|
|
309
|
+
declare const IDEAL_CROP_MULTIPLIER = 3;
|
|
310
|
+
declare const MIN_CROP_MULTIPLIER = 2.5;
|
|
311
|
+
declare const MAX_CROP_MULTIPLIER = 4;
|
|
278
312
|
declare const FACE_CENTER_VERTICAL_OFFSET = 0.05;
|
|
313
|
+
declare const MIN_IDEAL_FACE_RATIO = 0.05;
|
|
314
|
+
declare const MAX_IDEAL_FACE_RATIO = 0.2;
|
|
279
315
|
declare const MIN_FACE_RATIO = 0.036;
|
|
280
316
|
declare const MAX_FACE_RATIO = 0.7;
|
|
281
317
|
declare const FACE_CROP_OUTPUT_SIZE = 224;
|
|
282
|
-
declare const MAX_FACE_PERCENTAGE_IN_CROP = 0.
|
|
283
|
-
declare const TARGET_FACE_PERCENTAGE_IN_CROP = 0.
|
|
318
|
+
declare const MAX_FACE_PERCENTAGE_IN_CROP = 0.45;
|
|
319
|
+
declare const TARGET_FACE_PERCENTAGE_IN_CROP = 0.33;
|
|
284
320
|
declare function analyzeBlur(grayscalePixels: number[], width: number, height: number, threshold?: number): BlurAnalysis;
|
|
285
321
|
declare function rgbaToGrayscale(rgbaPixels: Uint8ClampedArray | number[]): number[];
|
|
286
322
|
declare function calculateBrightness(rgbaPixels: Uint8ClampedArray | number[]): number;
|
|
287
323
|
declare function analyzeLighting(faceBrightness: number, backgroundBrightness: number): LightingAnalysis;
|
|
288
324
|
declare function isFaceFullyVisible(boundingBox: FaceBoundingBox, frameWidth: number, frameHeight: number): FaceVisibilityResult;
|
|
325
|
+
declare const OVAL_REGION_DESKTOP: OvalRegion;
|
|
326
|
+
declare const OVAL_REGION_MOBILE: OvalRegion;
|
|
289
327
|
declare const DEFAULT_OVAL_REGION: OvalRegion;
|
|
290
328
|
declare function isFaceInOval(faceBox: FaceBoundingBox, frameWidth: number, frameHeight: number, oval?: OvalRegion, tolerance?: number): FaceInOvalResult;
|
|
291
329
|
declare function calculateFaceAlignment(boundingBox: FaceBoundingBox, frameWidth: number, frameHeight: number): FaceAlignmentResult;
|
|
@@ -304,6 +342,12 @@ declare function checkFrameQuality(options: {
|
|
|
304
342
|
lightingAnalysis?: LightingAnalysis;
|
|
305
343
|
minAlignment?: number;
|
|
306
344
|
}): FrameQualityResult;
|
|
345
|
+
interface FaceRollResult {
|
|
346
|
+
roll: number;
|
|
347
|
+
tooTilted: boolean;
|
|
348
|
+
}
|
|
349
|
+
declare const MAX_FACE_ROLL_DEGREES = 15;
|
|
350
|
+
declare function detectFaceRoll(landmarks: FaceLandmarkPoint[]): FaceRollResult;
|
|
307
351
|
declare class BaseFrameCollector {
|
|
308
352
|
protected frames: CapturedFrame[];
|
|
309
353
|
protected maxFrames: number;
|
|
@@ -319,40 +363,6 @@ declare class BaseFrameCollector {
|
|
|
319
363
|
getNextIndex(): number;
|
|
320
364
|
}
|
|
321
365
|
|
|
322
|
-
interface FaceLandmarkPoint {
|
|
323
|
-
x: number;
|
|
324
|
-
y: number;
|
|
325
|
-
z: number;
|
|
326
|
-
}
|
|
327
|
-
interface LandmarkValidationResult {
|
|
328
|
-
valid: boolean;
|
|
329
|
-
message?: string;
|
|
330
|
-
}
|
|
331
|
-
declare const LANDMARK_INDEX: {
|
|
332
|
-
readonly NOSE_TIP: 1;
|
|
333
|
-
readonly UPPER_LIP: 13;
|
|
334
|
-
readonly LOWER_LIP: 14;
|
|
335
|
-
};
|
|
336
|
-
declare const EYE_LANDMARK_INDICES: {
|
|
337
|
-
readonly RIGHT_EYE: readonly [33, 7, 163, 144, 145, 153, 154, 155, 133, 173, 157, 158, 159, 160, 161, 246];
|
|
338
|
-
readonly LEFT_EYE: readonly [362, 382, 381, 380, 374, 373, 390, 249, 263, 466, 388, 387, 386, 385, 384, 398];
|
|
339
|
-
};
|
|
340
|
-
interface EyeRegionBounds {
|
|
341
|
-
x: number;
|
|
342
|
-
y: number;
|
|
343
|
-
width: number;
|
|
344
|
-
height: number;
|
|
345
|
-
}
|
|
346
|
-
interface EyeRegionsBounds {
|
|
347
|
-
leftEye: EyeRegionBounds;
|
|
348
|
-
rightEye: EyeRegionBounds;
|
|
349
|
-
}
|
|
350
|
-
declare function getEyeRegionBounds(landmarks: FaceLandmarkPoint[]): EyeRegionsBounds | null;
|
|
351
|
-
declare const LANDMARK_MIN_BOUND = 0.1;
|
|
352
|
-
declare const LANDMARK_MAX_BOUND = 0.9;
|
|
353
|
-
declare const MIN_LANDMARK_COUNT = 15;
|
|
354
|
-
declare function validateFaceLandmarks(landmarks: FaceLandmarkPoint[] | undefined): LandmarkValidationResult;
|
|
355
|
-
|
|
356
366
|
interface DetectionResult {
|
|
357
367
|
type: string;
|
|
358
368
|
passed: boolean;
|
|
@@ -618,9 +628,9 @@ declare const ERROR_MESSAGES_ES: Record<string, string>;
|
|
|
618
628
|
declare function getApiErrorMessage(code: string | undefined, message?: string, customMessages?: Record<string, string>): string;
|
|
619
629
|
declare function isRetryableError(code: string | undefined): boolean;
|
|
620
630
|
|
|
621
|
-
declare const ALIGNMENT_THRESHOLD_CAPTURE = 0.
|
|
622
|
-
declare const ALIGNMENT_THRESHOLD_POOR = 0.
|
|
623
|
-
declare const ALIGNMENT_THRESHOLD_GOOD = 0.
|
|
631
|
+
declare const ALIGNMENT_THRESHOLD_CAPTURE = 0.85;
|
|
632
|
+
declare const ALIGNMENT_THRESHOLD_POOR = 0.6;
|
|
633
|
+
declare const ALIGNMENT_THRESHOLD_GOOD = 0.6;
|
|
624
634
|
declare const ALIGNMENT_THRESHOLD_PERFECT = 0.85;
|
|
625
635
|
type OvalGuideState = 'no_face' | 'poor' | 'good' | 'perfect';
|
|
626
636
|
declare const OVAL_GUIDE_COLORS: {
|
|
@@ -675,6 +685,8 @@ declare const FEEDBACK_MESSAGES: {
|
|
|
675
685
|
readonly move_back: "Move back - face too close";
|
|
676
686
|
readonly too_close: "Move back - face too close";
|
|
677
687
|
readonly too_far: "Move closer - face too far";
|
|
688
|
+
readonly move_closer_ideal: "Move a little closer";
|
|
689
|
+
readonly move_back_ideal: "Move back slightly";
|
|
678
690
|
readonly face_not_visible: "Center your face - edges cut off";
|
|
679
691
|
readonly partial_face: "Center your face - edges cut off";
|
|
680
692
|
readonly hold_still: "Hold still - image blurry";
|
|
@@ -682,6 +694,8 @@ declare const FEEDBACK_MESSAGES: {
|
|
|
682
694
|
readonly poor_lighting: "Improve lighting";
|
|
683
695
|
readonly too_dark: "Low lighting - move to a brighter area";
|
|
684
696
|
readonly backlit: "Backlit - try facing the light source";
|
|
697
|
+
readonly phone_angle_low: "Raise your phone to eye level";
|
|
698
|
+
readonly phone_tilted: "Hold your phone level";
|
|
685
699
|
readonly hand_detected: "Remove hand from face";
|
|
686
700
|
readonly eyes_not_visible: "Eyes not clearly visible";
|
|
687
701
|
readonly eyes_shadowed: "Eyes are in shadow - improve lighting";
|
|
@@ -712,6 +726,10 @@ interface CaptureQualityState {
|
|
|
712
726
|
isPartialFace: boolean;
|
|
713
727
|
framesCaptured: number;
|
|
714
728
|
targetFrames: number;
|
|
729
|
+
tooFarFromIdeal?: boolean;
|
|
730
|
+
tooCloseToIdeal?: boolean;
|
|
731
|
+
phoneAngled?: boolean;
|
|
732
|
+
phoneTilted?: boolean;
|
|
715
733
|
}
|
|
716
734
|
declare function getCaptureQualityFeedback(state: CaptureQualityState): string;
|
|
717
735
|
declare function canCaptureFrame(state: Omit<CaptureQualityState, 'framesCaptured' | 'targetFrames' | 'isCapturing'>): boolean;
|
|
@@ -762,4 +780,4 @@ declare function analyzeEyeRegionContrast(pixels: Uint8Array | Uint8ClampedArray
|
|
|
762
780
|
declare function detectSpecularHighlights(pixels: Uint8Array | Uint8ClampedArray, threshold?: number): number;
|
|
763
781
|
declare function checkEyeRegionQuality(pixels: Uint8Array | Uint8ClampedArray, thresholds?: EyeQualityThresholds): EyeRegionQuality;
|
|
764
782
|
|
|
765
|
-
export { ALIGNMENT_THRESHOLD_CAPTURE, ALIGNMENT_THRESHOLD_GOOD, ALIGNMENT_THRESHOLD_PERFECT, ALIGNMENT_THRESHOLD_POOR, API_ENDPOINTS, API_ERROR_CODES, API_PATHS, AUTH_CONFIG, type ApiErrorCode, BACKLIT_RATIO_THRESHOLD, BLUR_THRESHOLD_MOBILE, BaseFrameCollector, type BlurAnalysis, type CaptureQualityState, type CapturedFrame, type CropData, DEFAULT_BLUR_THRESHOLD, DEFAULT_ENDPOINT, DEFAULT_FACE_DETECTION_TIERS, DEFAULT_GAZE_THRESHOLDS, DEFAULT_HAND_OCCLUSION_CONFIG, DEFAULT_LIVENESS_CONFIG, DEFAULT_LOCALE, DEFAULT_OVAL_REGION, DEFAULT_STABILIZER_CONFIG, DEFAULT_STATUS_MESSAGES, type DetectionResult, type DetectionSummary, type DetectorConfig, ERROR_MESSAGES, ERROR_MESSAGES_ES, ES_LOCALE, EYE_LANDMARK_INDICES, EYE_QUALITY_THRESHOLDS, type ErrorResponse, type EyeQualityThresholds, type EyeRegionBounds, type EyeRegionQuality, type EyeRegionsBounds, FACE_CENTER_VERTICAL_OFFSET, FACE_CROP_OUTPUT_SIZE, FEEDBACK_MESSAGES, FRAME_BUFFER_CONFIG, FRAME_CONFIG, type FaceAlignmentResult, type FaceBoundingBox, type FaceDetectionTiers, type FaceInOvalResult, type FaceLandmarkPoint, type FaceVisibilityResult, type FastCheckCropsRequest, type FastCheckModel, type FastCheckRequest, type FastCheckResponse, type FastCheckStreamRequest, type FastCheckStreamResponse, type FeedbackLocale, type FeedbackMessageKey, type Frame, FrameBuffer, type FrameData, type FrameQualityResult, FrameQueue, type FrameSource, GOOD_ALIGNMENT, type GazeThresholds, HIGH_ALIGNMENT, HYBRID_MODEL_CONFIGS, type HandOcclusionConfig, type HeadPose, type HealthResponse, type Hybrid150CheckRequest, type Hybrid50CheckRequest, type HybridCheckRequest, type HybridCheckResponse, type HybridFrameData, type HybridModelConfig, IDEAL_CROP_MULTIPLIER, type JobStatus, type JobStatusResponse, LANDMARK_INDEX, LANDMARK_MAX_BOUND, LANDMARK_MIN_BOUND, LOW_LIGHT_THRESHOLD, type LandmarkValidationResult, type LightingAnalysis, LivenessApiError, type LivenessCallbacks, LivenessClient, type LivenessClientConfig, type LivenessConfig, type LivenessResult, type LivenessState, MAX_CROP_MULTIPLIER, MAX_FACE_PERCENTAGE_IN_CROP, MAX_FACE_RATIO, MIN_CAPTURE_ALIGNMENT, MIN_CROP_MULTIPLIER, MIN_FACE_BOTTOM_MARGIN, MIN_FACE_RATIO, MIN_FACE_SIDE_MARGIN, MIN_FACE_TOP_MARGIN, MIN_LANDMARK_COUNT, MODEL_CONFIGS, type ModelConfig, type ModelEntry, type ModelType, type ModelsResponse, OVAL_GUIDE_COLORS, OVAL_GUIDE_STYLES, type OnErrorCallback, type OnFrameCapturedCallback, type OnProgressCallback, type OnResultCallback, type OnStateChangeCallback, type OvalGuideState, type OvalRegion, type QueueStatsResponse, RETRY_CONFIG, type RetryOptions, type StabilizationProgress, type StabilizationResult, type StabilizerConfig, type StatusMessageKey, type StreamingStatus, TARGET_FACE_PERCENTAGE_IN_CROP, type Verdict, type VerifyRequest, type VerifyResponse, type VideoFrameMetadata, analyzeBlur, analyzeEyeRegionBrightness, analyzeEyeRegionContrast, analyzeLighting, calculateAdaptiveCropMultiplier, calculateBrightness, calculateFaceAlignment, calculateFaceCropRegion, canCaptureFrame, checkEyeRegionQuality, checkFrameQuality, decodeBase64, detectSpecularHighlights, encodeBase64, generateSessionId, getActiveModels, getApiErrorMessage, getCaptureQualityFeedback, getEyeRegionBounds, getFeedbackMessage, getMinFramesForModel, getOvalGuideState, getStatusMessage, hasEnoughFrames, isDeprecatedModel, isFaceCropFullyInFrame, isFaceFullyVisible, isFaceInOval, isRetryableError, retryWithBackoff, rgbaToGrayscale, sleep, toFrameData, toHybridFrameData, toLivenessResult, toLivenessResultFromStream, validateApiKey, validateFaceLandmarks, validateFrameCount, validateFrameData, validateFrameIndex, validateTimestamp, validateUUID, validateUrl };
|
|
783
|
+
export { ALIGNMENT_THRESHOLD_CAPTURE, ALIGNMENT_THRESHOLD_GOOD, ALIGNMENT_THRESHOLD_PERFECT, ALIGNMENT_THRESHOLD_POOR, API_ENDPOINTS, API_ERROR_CODES, API_PATHS, AUTH_CONFIG, type ApiErrorCode, BACKLIT_RATIO_THRESHOLD, BLUR_THRESHOLD_MOBILE, BaseFrameCollector, type BlurAnalysis, type CaptureQualityState, type CapturedFrame, type CropData, DEFAULT_BLUR_THRESHOLD, DEFAULT_ENDPOINT, DEFAULT_FACE_DETECTION_TIERS, DEFAULT_GAZE_THRESHOLDS, DEFAULT_HAND_OCCLUSION_CONFIG, DEFAULT_LIVENESS_CONFIG, DEFAULT_LOCALE, DEFAULT_OVAL_REGION, DEFAULT_STABILIZER_CONFIG, DEFAULT_STATUS_MESSAGES, type DetectionResult, type DetectionSummary, type DetectorConfig, ERROR_MESSAGES, ERROR_MESSAGES_ES, ES_LOCALE, EYE_LANDMARK_INDICES, EYE_QUALITY_THRESHOLDS, type ErrorResponse, type EyeQualityThresholds, type EyeRegionBounds, type EyeRegionQuality, type EyeRegionsBounds, FACE_CENTER_VERTICAL_OFFSET, FACE_CROP_OUTPUT_SIZE, FEEDBACK_MESSAGES, FRAME_BUFFER_CONFIG, FRAME_CONFIG, type FaceAlignmentResult, type FaceBoundingBox, type FaceDetectionTiers, type FaceInOvalResult, type FaceLandmarkPoint, type FaceRollResult, type FaceVisibilityResult, type FastCheckCropsRequest, type FastCheckModel, type FastCheckRequest, type FastCheckResponse, type FastCheckStreamRequest, type FastCheckStreamResponse, type FeedbackLocale, type FeedbackMessageKey, type Frame, FrameBuffer, type FrameData, type FrameQualityResult, FrameQueue, type FrameSource, GOOD_ALIGNMENT, type GazeThresholds, HIGH_ALIGNMENT, HYBRID_MODEL_CONFIGS, type HandOcclusionConfig, type HeadPose, type HealthResponse, type Hybrid150CheckRequest, type Hybrid50CheckRequest, type HybridCheckRequest, type HybridCheckResponse, type HybridFrameData, type HybridModelConfig, IDEAL_CROP_MULTIPLIER, type JobStatus, type JobStatusResponse, LANDMARK_INDEX, LANDMARK_MAX_BOUND, LANDMARK_MIN_BOUND, LOW_LIGHT_THRESHOLD, type LandmarkValidationResult, type LightingAnalysis, LivenessApiError, type LivenessCallbacks, LivenessClient, type LivenessClientConfig, type LivenessConfig, type LivenessResult, type LivenessState, MAX_CROP_MULTIPLIER, MAX_FACE_PERCENTAGE_IN_CROP, MAX_FACE_RATIO, MAX_FACE_ROLL_DEGREES, MAX_IDEAL_FACE_RATIO, MIN_CAPTURE_ALIGNMENT, MIN_CROP_MULTIPLIER, MIN_FACE_BOTTOM_MARGIN, MIN_FACE_RATIO, MIN_FACE_SIDE_MARGIN, MIN_FACE_TOP_MARGIN, MIN_IDEAL_FACE_RATIO, MIN_LANDMARK_COUNT, MODEL_CONFIGS, type ModelConfig, type ModelEntry, type ModelType, type ModelsResponse, OVAL_GUIDE_COLORS, OVAL_GUIDE_STYLES, OVAL_REGION_DESKTOP, OVAL_REGION_MOBILE, type OnErrorCallback, type OnFrameCapturedCallback, type OnProgressCallback, type OnResultCallback, type OnStateChangeCallback, type OvalGuideState, type OvalRegion, type QueueStatsResponse, RETRY_CONFIG, type RetryOptions, type StabilizationProgress, type StabilizationResult, type StabilizerConfig, type StatusMessageKey, type StreamingStatus, TARGET_FACE_PERCENTAGE_IN_CROP, type Verdict, type VerifyRequest, type VerifyResponse, type VideoFrameMetadata, analyzeBlur, analyzeEyeRegionBrightness, analyzeEyeRegionContrast, analyzeLighting, calculateAdaptiveCropMultiplier, calculateBrightness, calculateFaceAlignment, calculateFaceCropRegion, canCaptureFrame, checkEyeRegionQuality, checkFrameQuality, decodeBase64, detectFaceRoll, detectSpecularHighlights, encodeBase64, generateSessionId, getActiveModels, getApiErrorMessage, getCaptureQualityFeedback, getEyeRegionBounds, getFeedbackMessage, getMinFramesForModel, getOvalGuideState, getStatusMessage, hasEnoughFrames, isDeprecatedModel, isFaceCropFullyInFrame, isFaceFullyVisible, isFaceInOval, isRetryableError, retryWithBackoff, rgbaToGrayscale, sleep, toFrameData, toHybridFrameData, toLivenessResult, toLivenessResultFromStream, validateApiKey, validateFaceLandmarks, validateFrameCount, validateFrameData, validateFrameIndex, validateTimestamp, validateUUID, validateUrl };
|
package/dist/index.js
CHANGED
|
@@ -66,16 +66,21 @@ __export(index_exports, {
|
|
|
66
66
|
MAX_CROP_MULTIPLIER: () => MAX_CROP_MULTIPLIER,
|
|
67
67
|
MAX_FACE_PERCENTAGE_IN_CROP: () => MAX_FACE_PERCENTAGE_IN_CROP,
|
|
68
68
|
MAX_FACE_RATIO: () => MAX_FACE_RATIO,
|
|
69
|
+
MAX_FACE_ROLL_DEGREES: () => MAX_FACE_ROLL_DEGREES,
|
|
70
|
+
MAX_IDEAL_FACE_RATIO: () => MAX_IDEAL_FACE_RATIO,
|
|
69
71
|
MIN_CAPTURE_ALIGNMENT: () => MIN_CAPTURE_ALIGNMENT,
|
|
70
72
|
MIN_CROP_MULTIPLIER: () => MIN_CROP_MULTIPLIER,
|
|
71
73
|
MIN_FACE_BOTTOM_MARGIN: () => MIN_FACE_BOTTOM_MARGIN,
|
|
72
74
|
MIN_FACE_RATIO: () => MIN_FACE_RATIO,
|
|
73
75
|
MIN_FACE_SIDE_MARGIN: () => MIN_FACE_SIDE_MARGIN,
|
|
74
76
|
MIN_FACE_TOP_MARGIN: () => MIN_FACE_TOP_MARGIN,
|
|
77
|
+
MIN_IDEAL_FACE_RATIO: () => MIN_IDEAL_FACE_RATIO,
|
|
75
78
|
MIN_LANDMARK_COUNT: () => MIN_LANDMARK_COUNT,
|
|
76
79
|
MODEL_CONFIGS: () => MODEL_CONFIGS,
|
|
77
80
|
OVAL_GUIDE_COLORS: () => OVAL_GUIDE_COLORS,
|
|
78
81
|
OVAL_GUIDE_STYLES: () => OVAL_GUIDE_STYLES,
|
|
82
|
+
OVAL_REGION_DESKTOP: () => OVAL_REGION_DESKTOP,
|
|
83
|
+
OVAL_REGION_MOBILE: () => OVAL_REGION_MOBILE,
|
|
79
84
|
RETRY_CONFIG: () => RETRY_CONFIG,
|
|
80
85
|
TARGET_FACE_PERCENTAGE_IN_CROP: () => TARGET_FACE_PERCENTAGE_IN_CROP,
|
|
81
86
|
analyzeBlur: () => analyzeBlur,
|
|
@@ -90,6 +95,7 @@ __export(index_exports, {
|
|
|
90
95
|
checkEyeRegionQuality: () => checkEyeRegionQuality,
|
|
91
96
|
checkFrameQuality: () => checkFrameQuality,
|
|
92
97
|
decodeBase64: () => decodeBase64,
|
|
98
|
+
detectFaceRoll: () => detectFaceRoll,
|
|
93
99
|
detectSpecularHighlights: () => detectSpecularHighlights,
|
|
94
100
|
encodeBase64: () => encodeBase64,
|
|
95
101
|
generateSessionId: () => generateSessionId,
|
|
@@ -1175,9 +1181,9 @@ function isRetryableError(code) {
|
|
|
1175
1181
|
}
|
|
1176
1182
|
|
|
1177
1183
|
// src/constants/feedback.ts
|
|
1178
|
-
var ALIGNMENT_THRESHOLD_CAPTURE = 0.
|
|
1179
|
-
var ALIGNMENT_THRESHOLD_POOR = 0.
|
|
1180
|
-
var ALIGNMENT_THRESHOLD_GOOD = 0.
|
|
1184
|
+
var ALIGNMENT_THRESHOLD_CAPTURE = 0.85;
|
|
1185
|
+
var ALIGNMENT_THRESHOLD_POOR = 0.6;
|
|
1186
|
+
var ALIGNMENT_THRESHOLD_GOOD = 0.6;
|
|
1181
1187
|
var ALIGNMENT_THRESHOLD_PERFECT = 0.85;
|
|
1182
1188
|
var OVAL_GUIDE_COLORS = {
|
|
1183
1189
|
no_face: "#ef4444",
|
|
@@ -1228,6 +1234,8 @@ var FEEDBACK_MESSAGES = {
|
|
|
1228
1234
|
move_back: "Move back - face too close",
|
|
1229
1235
|
too_close: "Move back - face too close",
|
|
1230
1236
|
too_far: "Move closer - face too far",
|
|
1237
|
+
move_closer_ideal: "Move a little closer",
|
|
1238
|
+
move_back_ideal: "Move back slightly",
|
|
1231
1239
|
// Visibility issues
|
|
1232
1240
|
face_not_visible: "Center your face - edges cut off",
|
|
1233
1241
|
partial_face: "Center your face - edges cut off",
|
|
@@ -1238,6 +1246,9 @@ var FEEDBACK_MESSAGES = {
|
|
|
1238
1246
|
poor_lighting: "Improve lighting",
|
|
1239
1247
|
too_dark: "Low lighting - move to a brighter area",
|
|
1240
1248
|
backlit: "Backlit - try facing the light source",
|
|
1249
|
+
// Phone orientation
|
|
1250
|
+
phone_angle_low: "Raise your phone to eye level",
|
|
1251
|
+
phone_tilted: "Hold your phone level",
|
|
1241
1252
|
// Hand occlusion
|
|
1242
1253
|
hand_detected: "Remove hand from face",
|
|
1243
1254
|
// Eye region quality
|
|
@@ -1285,6 +1296,8 @@ var ES_LOCALE = {
|
|
|
1285
1296
|
move_back: "Al\xE9jate - rostro muy cerca",
|
|
1286
1297
|
too_close: "Al\xE9jate - rostro muy cerca",
|
|
1287
1298
|
too_far: "Ac\xE9rcate - rostro muy lejos",
|
|
1299
|
+
move_closer_ideal: "Ac\xE9rcate un poco",
|
|
1300
|
+
move_back_ideal: "Al\xE9jate un poco",
|
|
1288
1301
|
// Visibility issues
|
|
1289
1302
|
face_not_visible: "Centra tu rostro - bordes cortados",
|
|
1290
1303
|
partial_face: "Centra tu rostro - bordes cortados",
|
|
@@ -1295,6 +1308,9 @@ var ES_LOCALE = {
|
|
|
1295
1308
|
poor_lighting: "Mejora la iluminaci\xF3n",
|
|
1296
1309
|
too_dark: "Poca luz - mu\xE9vete a un \xE1rea m\xE1s iluminada",
|
|
1297
1310
|
backlit: "Contraluz - intenta mirar hacia la fuente de luz",
|
|
1311
|
+
// Phone orientation
|
|
1312
|
+
phone_angle_low: "Levanta el tel\xE9fono a la altura de los ojos",
|
|
1313
|
+
phone_tilted: "Mant\xE9n el tel\xE9fono nivelado",
|
|
1298
1314
|
// Hand occlusion
|
|
1299
1315
|
hand_detected: "Retira la mano del rostro",
|
|
1300
1316
|
// Eye region quality
|
|
@@ -1327,11 +1343,21 @@ function getCaptureQualityFeedback(state) {
|
|
|
1327
1343
|
isBlurry,
|
|
1328
1344
|
isPartialFace,
|
|
1329
1345
|
framesCaptured,
|
|
1330
|
-
targetFrames
|
|
1346
|
+
targetFrames,
|
|
1347
|
+
tooFarFromIdeal,
|
|
1348
|
+
tooCloseToIdeal,
|
|
1349
|
+
phoneAngled,
|
|
1350
|
+
phoneTilted
|
|
1331
1351
|
} = state;
|
|
1332
1352
|
if (!hasFace) {
|
|
1333
1353
|
return FEEDBACK_MESSAGES.no_face;
|
|
1334
1354
|
}
|
|
1355
|
+
if (phoneTilted) {
|
|
1356
|
+
return FEEDBACK_MESSAGES.phone_tilted;
|
|
1357
|
+
}
|
|
1358
|
+
if (phoneAngled) {
|
|
1359
|
+
return FEEDBACK_MESSAGES.phone_angle_low;
|
|
1360
|
+
}
|
|
1335
1361
|
if (tooClose) {
|
|
1336
1362
|
return FEEDBACK_MESSAGES.too_close;
|
|
1337
1363
|
}
|
|
@@ -1344,6 +1370,12 @@ function getCaptureQualityFeedback(state) {
|
|
|
1344
1370
|
if (isBlurry) {
|
|
1345
1371
|
return FEEDBACK_MESSAGES.blurry;
|
|
1346
1372
|
}
|
|
1373
|
+
if (tooFarFromIdeal) {
|
|
1374
|
+
return FEEDBACK_MESSAGES.move_closer_ideal;
|
|
1375
|
+
}
|
|
1376
|
+
if (tooCloseToIdeal) {
|
|
1377
|
+
return FEEDBACK_MESSAGES.move_back_ideal;
|
|
1378
|
+
}
|
|
1347
1379
|
if (alignment < ALIGNMENT_THRESHOLD_POOR) {
|
|
1348
1380
|
return FEEDBACK_MESSAGES.move_closer_to_center;
|
|
1349
1381
|
}
|
|
@@ -1359,8 +1391,19 @@ function getCaptureQualityFeedback(state) {
|
|
|
1359
1391
|
return FEEDBACK_MESSAGES.perfect;
|
|
1360
1392
|
}
|
|
1361
1393
|
function canCaptureFrame(state) {
|
|
1362
|
-
const {
|
|
1363
|
-
|
|
1394
|
+
const {
|
|
1395
|
+
hasFace,
|
|
1396
|
+
alignment,
|
|
1397
|
+
tooClose,
|
|
1398
|
+
tooFar,
|
|
1399
|
+
isBlurry,
|
|
1400
|
+
isPartialFace,
|
|
1401
|
+
tooFarFromIdeal,
|
|
1402
|
+
tooCloseToIdeal,
|
|
1403
|
+
phoneAngled,
|
|
1404
|
+
phoneTilted
|
|
1405
|
+
} = state;
|
|
1406
|
+
return hasFace && alignment >= ALIGNMENT_THRESHOLD_CAPTURE && !tooClose && !tooFar && !isBlurry && !isPartialFace && !tooFarFromIdeal && !tooCloseToIdeal && !phoneAngled && !phoneTilted;
|
|
1364
1407
|
}
|
|
1365
1408
|
|
|
1366
1409
|
// src/utils/validators.ts
|
|
@@ -1428,7 +1471,7 @@ function decodeBase64(base64) {
|
|
|
1428
1471
|
|
|
1429
1472
|
// src/utils/frameAnalysis.ts
|
|
1430
1473
|
var DEFAULT_BLUR_THRESHOLD = 100;
|
|
1431
|
-
var BLUR_THRESHOLD_MOBILE =
|
|
1474
|
+
var BLUR_THRESHOLD_MOBILE = 60;
|
|
1432
1475
|
var BACKLIT_RATIO_THRESHOLD = 0.6;
|
|
1433
1476
|
var LOW_LIGHT_THRESHOLD = 50;
|
|
1434
1477
|
var MIN_FACE_TOP_MARGIN = 0.1;
|
|
@@ -1437,15 +1480,17 @@ var MIN_FACE_SIDE_MARGIN = 0.05;
|
|
|
1437
1480
|
var MIN_CAPTURE_ALIGNMENT = 0.6;
|
|
1438
1481
|
var HIGH_ALIGNMENT = 0.85;
|
|
1439
1482
|
var GOOD_ALIGNMENT = 0.5;
|
|
1440
|
-
var IDEAL_CROP_MULTIPLIER =
|
|
1441
|
-
var MIN_CROP_MULTIPLIER =
|
|
1442
|
-
var MAX_CROP_MULTIPLIER =
|
|
1483
|
+
var IDEAL_CROP_MULTIPLIER = 3;
|
|
1484
|
+
var MIN_CROP_MULTIPLIER = 2.5;
|
|
1485
|
+
var MAX_CROP_MULTIPLIER = 4;
|
|
1443
1486
|
var FACE_CENTER_VERTICAL_OFFSET = 0.05;
|
|
1487
|
+
var MIN_IDEAL_FACE_RATIO = 0.05;
|
|
1488
|
+
var MAX_IDEAL_FACE_RATIO = 0.2;
|
|
1444
1489
|
var MIN_FACE_RATIO = 0.036;
|
|
1445
1490
|
var MAX_FACE_RATIO = 0.7;
|
|
1446
1491
|
var FACE_CROP_OUTPUT_SIZE = 224;
|
|
1447
|
-
var MAX_FACE_PERCENTAGE_IN_CROP = 0.
|
|
1448
|
-
var TARGET_FACE_PERCENTAGE_IN_CROP = 0.
|
|
1492
|
+
var MAX_FACE_PERCENTAGE_IN_CROP = 0.45;
|
|
1493
|
+
var TARGET_FACE_PERCENTAGE_IN_CROP = 0.33;
|
|
1449
1494
|
function analyzeBlur(grayscalePixels, width, height, threshold = DEFAULT_BLUR_THRESHOLD) {
|
|
1450
1495
|
const laplacian = [];
|
|
1451
1496
|
for (let y = 1; y < height - 1; y++) {
|
|
@@ -1531,14 +1576,21 @@ function isFaceFullyVisible(boundingBox, frameWidth, frameHeight) {
|
|
|
1531
1576
|
}
|
|
1532
1577
|
return { visible: true };
|
|
1533
1578
|
}
|
|
1534
|
-
var
|
|
1579
|
+
var OVAL_REGION_DESKTOP = {
|
|
1535
1580
|
centerX: 0.5,
|
|
1536
1581
|
centerY: 0.5,
|
|
1537
1582
|
width: 0.36,
|
|
1538
|
-
// 36% of frame width (+20%)
|
|
1539
1583
|
height: 0.48
|
|
1540
|
-
// 36
|
|
1584
|
+
// 0.36 * (4/3)
|
|
1541
1585
|
};
|
|
1586
|
+
var OVAL_REGION_MOBILE = {
|
|
1587
|
+
centerX: 0.5,
|
|
1588
|
+
centerY: 0.5,
|
|
1589
|
+
width: 0.48,
|
|
1590
|
+
height: 0.64
|
|
1591
|
+
// 0.48 * (4/3)
|
|
1592
|
+
};
|
|
1593
|
+
var DEFAULT_OVAL_REGION = OVAL_REGION_DESKTOP;
|
|
1542
1594
|
function isFaceInOval(faceBox, frameWidth, frameHeight, oval = DEFAULT_OVAL_REGION, tolerance = 0.3) {
|
|
1543
1595
|
const faceCenterX = (faceBox.originX + faceBox.width / 2) / frameWidth;
|
|
1544
1596
|
const faceCenterY = (faceBox.originY + faceBox.height / 2) / frameHeight;
|
|
@@ -1692,6 +1744,33 @@ function checkFrameQuality(options) {
|
|
|
1692
1744
|
}
|
|
1693
1745
|
return result;
|
|
1694
1746
|
}
|
|
1747
|
+
var MAX_FACE_ROLL_DEGREES = 15;
|
|
1748
|
+
function detectFaceRoll(landmarks) {
|
|
1749
|
+
if (landmarks.length < 364) {
|
|
1750
|
+
return { roll: 0, tooTilted: false };
|
|
1751
|
+
}
|
|
1752
|
+
const l33 = landmarks[33];
|
|
1753
|
+
const l133 = landmarks[133];
|
|
1754
|
+
const l362 = landmarks[362];
|
|
1755
|
+
const l263 = landmarks[263];
|
|
1756
|
+
if (!l33 || !l133 || !l362 || !l263) {
|
|
1757
|
+
return { roll: 0, tooTilted: false };
|
|
1758
|
+
}
|
|
1759
|
+
const leftEyeX = (l33.x + l133.x) / 2;
|
|
1760
|
+
const leftEyeY = (l33.y + l133.y) / 2;
|
|
1761
|
+
const rightEyeX = (l362.x + l263.x) / 2;
|
|
1762
|
+
const rightEyeY = (l362.y + l263.y) / 2;
|
|
1763
|
+
const eyeXDist = Math.abs(leftEyeX - rightEyeX);
|
|
1764
|
+
const eyeYDiff = Math.abs(leftEyeY - rightEyeY);
|
|
1765
|
+
if (eyeXDist < 0.01) {
|
|
1766
|
+
return { roll: 0, tooTilted: false };
|
|
1767
|
+
}
|
|
1768
|
+
const roll = Math.atan2(eyeYDiff, eyeXDist) * (180 / Math.PI);
|
|
1769
|
+
return {
|
|
1770
|
+
roll,
|
|
1771
|
+
tooTilted: roll > MAX_FACE_ROLL_DEGREES
|
|
1772
|
+
};
|
|
1773
|
+
}
|
|
1695
1774
|
var BaseFrameCollector = class {
|
|
1696
1775
|
constructor(maxFrames = 10) {
|
|
1697
1776
|
this.frames = [];
|
|
@@ -1952,16 +2031,21 @@ function checkEyeRegionQuality(pixels, thresholds = EYE_QUALITY_THRESHOLDS) {
|
|
|
1952
2031
|
MAX_CROP_MULTIPLIER,
|
|
1953
2032
|
MAX_FACE_PERCENTAGE_IN_CROP,
|
|
1954
2033
|
MAX_FACE_RATIO,
|
|
2034
|
+
MAX_FACE_ROLL_DEGREES,
|
|
2035
|
+
MAX_IDEAL_FACE_RATIO,
|
|
1955
2036
|
MIN_CAPTURE_ALIGNMENT,
|
|
1956
2037
|
MIN_CROP_MULTIPLIER,
|
|
1957
2038
|
MIN_FACE_BOTTOM_MARGIN,
|
|
1958
2039
|
MIN_FACE_RATIO,
|
|
1959
2040
|
MIN_FACE_SIDE_MARGIN,
|
|
1960
2041
|
MIN_FACE_TOP_MARGIN,
|
|
2042
|
+
MIN_IDEAL_FACE_RATIO,
|
|
1961
2043
|
MIN_LANDMARK_COUNT,
|
|
1962
2044
|
MODEL_CONFIGS,
|
|
1963
2045
|
OVAL_GUIDE_COLORS,
|
|
1964
2046
|
OVAL_GUIDE_STYLES,
|
|
2047
|
+
OVAL_REGION_DESKTOP,
|
|
2048
|
+
OVAL_REGION_MOBILE,
|
|
1965
2049
|
RETRY_CONFIG,
|
|
1966
2050
|
TARGET_FACE_PERCENTAGE_IN_CROP,
|
|
1967
2051
|
analyzeBlur,
|
|
@@ -1976,6 +2060,7 @@ function checkEyeRegionQuality(pixels, thresholds = EYE_QUALITY_THRESHOLDS) {
|
|
|
1976
2060
|
checkEyeRegionQuality,
|
|
1977
2061
|
checkFrameQuality,
|
|
1978
2062
|
decodeBase64,
|
|
2063
|
+
detectFaceRoll,
|
|
1979
2064
|
detectSpecularHighlights,
|
|
1980
2065
|
encodeBase64,
|
|
1981
2066
|
generateSessionId,
|
package/dist/index.mjs
CHANGED
|
@@ -1048,9 +1048,9 @@ function isRetryableError(code) {
|
|
|
1048
1048
|
}
|
|
1049
1049
|
|
|
1050
1050
|
// src/constants/feedback.ts
|
|
1051
|
-
var ALIGNMENT_THRESHOLD_CAPTURE = 0.
|
|
1052
|
-
var ALIGNMENT_THRESHOLD_POOR = 0.
|
|
1053
|
-
var ALIGNMENT_THRESHOLD_GOOD = 0.
|
|
1051
|
+
var ALIGNMENT_THRESHOLD_CAPTURE = 0.85;
|
|
1052
|
+
var ALIGNMENT_THRESHOLD_POOR = 0.6;
|
|
1053
|
+
var ALIGNMENT_THRESHOLD_GOOD = 0.6;
|
|
1054
1054
|
var ALIGNMENT_THRESHOLD_PERFECT = 0.85;
|
|
1055
1055
|
var OVAL_GUIDE_COLORS = {
|
|
1056
1056
|
no_face: "#ef4444",
|
|
@@ -1101,6 +1101,8 @@ var FEEDBACK_MESSAGES = {
|
|
|
1101
1101
|
move_back: "Move back - face too close",
|
|
1102
1102
|
too_close: "Move back - face too close",
|
|
1103
1103
|
too_far: "Move closer - face too far",
|
|
1104
|
+
move_closer_ideal: "Move a little closer",
|
|
1105
|
+
move_back_ideal: "Move back slightly",
|
|
1104
1106
|
// Visibility issues
|
|
1105
1107
|
face_not_visible: "Center your face - edges cut off",
|
|
1106
1108
|
partial_face: "Center your face - edges cut off",
|
|
@@ -1111,6 +1113,9 @@ var FEEDBACK_MESSAGES = {
|
|
|
1111
1113
|
poor_lighting: "Improve lighting",
|
|
1112
1114
|
too_dark: "Low lighting - move to a brighter area",
|
|
1113
1115
|
backlit: "Backlit - try facing the light source",
|
|
1116
|
+
// Phone orientation
|
|
1117
|
+
phone_angle_low: "Raise your phone to eye level",
|
|
1118
|
+
phone_tilted: "Hold your phone level",
|
|
1114
1119
|
// Hand occlusion
|
|
1115
1120
|
hand_detected: "Remove hand from face",
|
|
1116
1121
|
// Eye region quality
|
|
@@ -1158,6 +1163,8 @@ var ES_LOCALE = {
|
|
|
1158
1163
|
move_back: "Al\xE9jate - rostro muy cerca",
|
|
1159
1164
|
too_close: "Al\xE9jate - rostro muy cerca",
|
|
1160
1165
|
too_far: "Ac\xE9rcate - rostro muy lejos",
|
|
1166
|
+
move_closer_ideal: "Ac\xE9rcate un poco",
|
|
1167
|
+
move_back_ideal: "Al\xE9jate un poco",
|
|
1161
1168
|
// Visibility issues
|
|
1162
1169
|
face_not_visible: "Centra tu rostro - bordes cortados",
|
|
1163
1170
|
partial_face: "Centra tu rostro - bordes cortados",
|
|
@@ -1168,6 +1175,9 @@ var ES_LOCALE = {
|
|
|
1168
1175
|
poor_lighting: "Mejora la iluminaci\xF3n",
|
|
1169
1176
|
too_dark: "Poca luz - mu\xE9vete a un \xE1rea m\xE1s iluminada",
|
|
1170
1177
|
backlit: "Contraluz - intenta mirar hacia la fuente de luz",
|
|
1178
|
+
// Phone orientation
|
|
1179
|
+
phone_angle_low: "Levanta el tel\xE9fono a la altura de los ojos",
|
|
1180
|
+
phone_tilted: "Mant\xE9n el tel\xE9fono nivelado",
|
|
1171
1181
|
// Hand occlusion
|
|
1172
1182
|
hand_detected: "Retira la mano del rostro",
|
|
1173
1183
|
// Eye region quality
|
|
@@ -1200,11 +1210,21 @@ function getCaptureQualityFeedback(state) {
|
|
|
1200
1210
|
isBlurry,
|
|
1201
1211
|
isPartialFace,
|
|
1202
1212
|
framesCaptured,
|
|
1203
|
-
targetFrames
|
|
1213
|
+
targetFrames,
|
|
1214
|
+
tooFarFromIdeal,
|
|
1215
|
+
tooCloseToIdeal,
|
|
1216
|
+
phoneAngled,
|
|
1217
|
+
phoneTilted
|
|
1204
1218
|
} = state;
|
|
1205
1219
|
if (!hasFace) {
|
|
1206
1220
|
return FEEDBACK_MESSAGES.no_face;
|
|
1207
1221
|
}
|
|
1222
|
+
if (phoneTilted) {
|
|
1223
|
+
return FEEDBACK_MESSAGES.phone_tilted;
|
|
1224
|
+
}
|
|
1225
|
+
if (phoneAngled) {
|
|
1226
|
+
return FEEDBACK_MESSAGES.phone_angle_low;
|
|
1227
|
+
}
|
|
1208
1228
|
if (tooClose) {
|
|
1209
1229
|
return FEEDBACK_MESSAGES.too_close;
|
|
1210
1230
|
}
|
|
@@ -1217,6 +1237,12 @@ function getCaptureQualityFeedback(state) {
|
|
|
1217
1237
|
if (isBlurry) {
|
|
1218
1238
|
return FEEDBACK_MESSAGES.blurry;
|
|
1219
1239
|
}
|
|
1240
|
+
if (tooFarFromIdeal) {
|
|
1241
|
+
return FEEDBACK_MESSAGES.move_closer_ideal;
|
|
1242
|
+
}
|
|
1243
|
+
if (tooCloseToIdeal) {
|
|
1244
|
+
return FEEDBACK_MESSAGES.move_back_ideal;
|
|
1245
|
+
}
|
|
1220
1246
|
if (alignment < ALIGNMENT_THRESHOLD_POOR) {
|
|
1221
1247
|
return FEEDBACK_MESSAGES.move_closer_to_center;
|
|
1222
1248
|
}
|
|
@@ -1232,8 +1258,19 @@ function getCaptureQualityFeedback(state) {
|
|
|
1232
1258
|
return FEEDBACK_MESSAGES.perfect;
|
|
1233
1259
|
}
|
|
1234
1260
|
function canCaptureFrame(state) {
|
|
1235
|
-
const {
|
|
1236
|
-
|
|
1261
|
+
const {
|
|
1262
|
+
hasFace,
|
|
1263
|
+
alignment,
|
|
1264
|
+
tooClose,
|
|
1265
|
+
tooFar,
|
|
1266
|
+
isBlurry,
|
|
1267
|
+
isPartialFace,
|
|
1268
|
+
tooFarFromIdeal,
|
|
1269
|
+
tooCloseToIdeal,
|
|
1270
|
+
phoneAngled,
|
|
1271
|
+
phoneTilted
|
|
1272
|
+
} = state;
|
|
1273
|
+
return hasFace && alignment >= ALIGNMENT_THRESHOLD_CAPTURE && !tooClose && !tooFar && !isBlurry && !isPartialFace && !tooFarFromIdeal && !tooCloseToIdeal && !phoneAngled && !phoneTilted;
|
|
1237
1274
|
}
|
|
1238
1275
|
|
|
1239
1276
|
// src/utils/validators.ts
|
|
@@ -1301,7 +1338,7 @@ function decodeBase64(base64) {
|
|
|
1301
1338
|
|
|
1302
1339
|
// src/utils/frameAnalysis.ts
|
|
1303
1340
|
var DEFAULT_BLUR_THRESHOLD = 100;
|
|
1304
|
-
var BLUR_THRESHOLD_MOBILE =
|
|
1341
|
+
var BLUR_THRESHOLD_MOBILE = 60;
|
|
1305
1342
|
var BACKLIT_RATIO_THRESHOLD = 0.6;
|
|
1306
1343
|
var LOW_LIGHT_THRESHOLD = 50;
|
|
1307
1344
|
var MIN_FACE_TOP_MARGIN = 0.1;
|
|
@@ -1310,15 +1347,17 @@ var MIN_FACE_SIDE_MARGIN = 0.05;
|
|
|
1310
1347
|
var MIN_CAPTURE_ALIGNMENT = 0.6;
|
|
1311
1348
|
var HIGH_ALIGNMENT = 0.85;
|
|
1312
1349
|
var GOOD_ALIGNMENT = 0.5;
|
|
1313
|
-
var IDEAL_CROP_MULTIPLIER =
|
|
1314
|
-
var MIN_CROP_MULTIPLIER =
|
|
1315
|
-
var MAX_CROP_MULTIPLIER =
|
|
1350
|
+
var IDEAL_CROP_MULTIPLIER = 3;
|
|
1351
|
+
var MIN_CROP_MULTIPLIER = 2.5;
|
|
1352
|
+
var MAX_CROP_MULTIPLIER = 4;
|
|
1316
1353
|
var FACE_CENTER_VERTICAL_OFFSET = 0.05;
|
|
1354
|
+
var MIN_IDEAL_FACE_RATIO = 0.05;
|
|
1355
|
+
var MAX_IDEAL_FACE_RATIO = 0.2;
|
|
1317
1356
|
var MIN_FACE_RATIO = 0.036;
|
|
1318
1357
|
var MAX_FACE_RATIO = 0.7;
|
|
1319
1358
|
var FACE_CROP_OUTPUT_SIZE = 224;
|
|
1320
|
-
var MAX_FACE_PERCENTAGE_IN_CROP = 0.
|
|
1321
|
-
var TARGET_FACE_PERCENTAGE_IN_CROP = 0.
|
|
1359
|
+
var MAX_FACE_PERCENTAGE_IN_CROP = 0.45;
|
|
1360
|
+
var TARGET_FACE_PERCENTAGE_IN_CROP = 0.33;
|
|
1322
1361
|
function analyzeBlur(grayscalePixels, width, height, threshold = DEFAULT_BLUR_THRESHOLD) {
|
|
1323
1362
|
const laplacian = [];
|
|
1324
1363
|
for (let y = 1; y < height - 1; y++) {
|
|
@@ -1404,14 +1443,21 @@ function isFaceFullyVisible(boundingBox, frameWidth, frameHeight) {
|
|
|
1404
1443
|
}
|
|
1405
1444
|
return { visible: true };
|
|
1406
1445
|
}
|
|
1407
|
-
var
|
|
1446
|
+
var OVAL_REGION_DESKTOP = {
|
|
1408
1447
|
centerX: 0.5,
|
|
1409
1448
|
centerY: 0.5,
|
|
1410
1449
|
width: 0.36,
|
|
1411
|
-
// 36% of frame width (+20%)
|
|
1412
1450
|
height: 0.48
|
|
1413
|
-
// 36
|
|
1451
|
+
// 0.36 * (4/3)
|
|
1414
1452
|
};
|
|
1453
|
+
var OVAL_REGION_MOBILE = {
|
|
1454
|
+
centerX: 0.5,
|
|
1455
|
+
centerY: 0.5,
|
|
1456
|
+
width: 0.48,
|
|
1457
|
+
height: 0.64
|
|
1458
|
+
// 0.48 * (4/3)
|
|
1459
|
+
};
|
|
1460
|
+
var DEFAULT_OVAL_REGION = OVAL_REGION_DESKTOP;
|
|
1415
1461
|
function isFaceInOval(faceBox, frameWidth, frameHeight, oval = DEFAULT_OVAL_REGION, tolerance = 0.3) {
|
|
1416
1462
|
const faceCenterX = (faceBox.originX + faceBox.width / 2) / frameWidth;
|
|
1417
1463
|
const faceCenterY = (faceBox.originY + faceBox.height / 2) / frameHeight;
|
|
@@ -1565,6 +1611,33 @@ function checkFrameQuality(options) {
|
|
|
1565
1611
|
}
|
|
1566
1612
|
return result;
|
|
1567
1613
|
}
|
|
1614
|
+
var MAX_FACE_ROLL_DEGREES = 15;
|
|
1615
|
+
function detectFaceRoll(landmarks) {
|
|
1616
|
+
if (landmarks.length < 364) {
|
|
1617
|
+
return { roll: 0, tooTilted: false };
|
|
1618
|
+
}
|
|
1619
|
+
const l33 = landmarks[33];
|
|
1620
|
+
const l133 = landmarks[133];
|
|
1621
|
+
const l362 = landmarks[362];
|
|
1622
|
+
const l263 = landmarks[263];
|
|
1623
|
+
if (!l33 || !l133 || !l362 || !l263) {
|
|
1624
|
+
return { roll: 0, tooTilted: false };
|
|
1625
|
+
}
|
|
1626
|
+
const leftEyeX = (l33.x + l133.x) / 2;
|
|
1627
|
+
const leftEyeY = (l33.y + l133.y) / 2;
|
|
1628
|
+
const rightEyeX = (l362.x + l263.x) / 2;
|
|
1629
|
+
const rightEyeY = (l362.y + l263.y) / 2;
|
|
1630
|
+
const eyeXDist = Math.abs(leftEyeX - rightEyeX);
|
|
1631
|
+
const eyeYDiff = Math.abs(leftEyeY - rightEyeY);
|
|
1632
|
+
if (eyeXDist < 0.01) {
|
|
1633
|
+
return { roll: 0, tooTilted: false };
|
|
1634
|
+
}
|
|
1635
|
+
const roll = Math.atan2(eyeYDiff, eyeXDist) * (180 / Math.PI);
|
|
1636
|
+
return {
|
|
1637
|
+
roll,
|
|
1638
|
+
tooTilted: roll > MAX_FACE_ROLL_DEGREES
|
|
1639
|
+
};
|
|
1640
|
+
}
|
|
1568
1641
|
var BaseFrameCollector = class {
|
|
1569
1642
|
constructor(maxFrames = 10) {
|
|
1570
1643
|
this.frames = [];
|
|
@@ -1824,16 +1897,21 @@ export {
|
|
|
1824
1897
|
MAX_CROP_MULTIPLIER,
|
|
1825
1898
|
MAX_FACE_PERCENTAGE_IN_CROP,
|
|
1826
1899
|
MAX_FACE_RATIO,
|
|
1900
|
+
MAX_FACE_ROLL_DEGREES,
|
|
1901
|
+
MAX_IDEAL_FACE_RATIO,
|
|
1827
1902
|
MIN_CAPTURE_ALIGNMENT,
|
|
1828
1903
|
MIN_CROP_MULTIPLIER,
|
|
1829
1904
|
MIN_FACE_BOTTOM_MARGIN,
|
|
1830
1905
|
MIN_FACE_RATIO,
|
|
1831
1906
|
MIN_FACE_SIDE_MARGIN,
|
|
1832
1907
|
MIN_FACE_TOP_MARGIN,
|
|
1908
|
+
MIN_IDEAL_FACE_RATIO,
|
|
1833
1909
|
MIN_LANDMARK_COUNT,
|
|
1834
1910
|
MODEL_CONFIGS,
|
|
1835
1911
|
OVAL_GUIDE_COLORS,
|
|
1836
1912
|
OVAL_GUIDE_STYLES,
|
|
1913
|
+
OVAL_REGION_DESKTOP,
|
|
1914
|
+
OVAL_REGION_MOBILE,
|
|
1837
1915
|
RETRY_CONFIG,
|
|
1838
1916
|
TARGET_FACE_PERCENTAGE_IN_CROP,
|
|
1839
1917
|
analyzeBlur,
|
|
@@ -1848,6 +1926,7 @@ export {
|
|
|
1848
1926
|
checkEyeRegionQuality,
|
|
1849
1927
|
checkFrameQuality,
|
|
1850
1928
|
decodeBase64,
|
|
1929
|
+
detectFaceRoll,
|
|
1851
1930
|
detectSpecularHighlights,
|
|
1852
1931
|
encodeBase64,
|
|
1853
1932
|
generateSessionId,
|