@moveris/shared 2.7.0 → 2.9.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/README.md +22 -3
- package/dist/index.d.mts +17 -6
- package/dist/index.d.ts +17 -6
- package/dist/index.js +55 -17
- package/dist/index.mjs +52 -17
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -94,6 +94,7 @@ const crops: CropData[] = capturedFrames.map((f) => ({
|
|
|
94
94
|
const result = await client.fastCheckCrops(crops, {
|
|
95
95
|
model: '10',
|
|
96
96
|
source: 'live',
|
|
97
|
+
bgSegmentation: true, // optional — sent as bg_segmentation in payload
|
|
97
98
|
});
|
|
98
99
|
```
|
|
99
100
|
|
|
@@ -443,6 +444,9 @@ const status = getStatusMessage('capturing', DEFAULT_LOCALE);
|
|
|
443
444
|
| `eyes_overexposed` | "Eye region overexposed…" | Eye region too bright |
|
|
444
445
|
| `glasses_glare` | "Glare detected…" | Specular highlights on eyes |
|
|
445
446
|
| `eye_quality_poor` | "Eye region quality is poor" | Generic eye quality failure |
|
|
447
|
+
| `camera_angle_low` | "Raise camera to eye level" | Camera below face (Y < 0.3) |
|
|
448
|
+
| `camera_angle_high` | "Lower camera to eye level" | Camera above face (Y > 0.7) |
|
|
449
|
+
| `camera_tilted` | "Hold camera level" | Eye line deviates > 15° |
|
|
446
450
|
|
|
447
451
|
---
|
|
448
452
|
|
|
@@ -529,7 +533,13 @@ if (bounds) {
|
|
|
529
533
|
Utilities for assessing frame quality before submission.
|
|
530
534
|
|
|
531
535
|
```typescript
|
|
532
|
-
import {
|
|
536
|
+
import {
|
|
537
|
+
isFaceFullyVisible,
|
|
538
|
+
isFaceInOval,
|
|
539
|
+
calculateFaceCropRegion,
|
|
540
|
+
detectFaceRoll,
|
|
541
|
+
detectCameraAngle,
|
|
542
|
+
} from '@moveris/shared';
|
|
533
543
|
|
|
534
544
|
// Check if face is fully visible in frame
|
|
535
545
|
const visibility = isFaceFullyVisible(faceBbox, frameWidth, frameHeight);
|
|
@@ -547,8 +557,17 @@ const cropRegion = calculateFaceCropRegion(faceBbox, frameWidth, frameHeight);
|
|
|
547
557
|
|
|
548
558
|
// Calculate adaptive crop multiplier based on face size
|
|
549
559
|
import { calculateAdaptiveCropMultiplier } from '@moveris/shared';
|
|
550
|
-
const multiplier = calculateAdaptiveCropMultiplier(
|
|
551
|
-
// Returns
|
|
560
|
+
const multiplier = calculateAdaptiveCropMultiplier(faceBox, frameWidth, frameHeight);
|
|
561
|
+
// Returns 2.5–4.0x (matched to cognito-check for ~33% face coverage in 224×224 crop)
|
|
562
|
+
|
|
563
|
+
// Detect face roll (device tilt) from eye-corner landmarks
|
|
564
|
+
const rollResult = detectFaceRoll(landmarks); // requires ≥364 landmarks
|
|
565
|
+
// { roll: number (degrees), tooTilted: boolean }
|
|
566
|
+
|
|
567
|
+
// Detect camera vertical angle from forehead/nose/chin landmark ratio
|
|
568
|
+
const angleResult = detectCameraAngle(landmarks); // requires ≥153 landmarks
|
|
569
|
+
// { ratio: number, cameraAbove: boolean, cameraBelow: boolean }
|
|
570
|
+
// ratio ~1.0 at eye level; >1.35 = camera above user; <0.75 = camera below user
|
|
552
571
|
```
|
|
553
572
|
|
|
554
573
|
#### Crop Constants (aligned with cognito-check)
|
package/dist/index.d.mts
CHANGED
|
@@ -301,7 +301,7 @@ interface FrameQualityResult {
|
|
|
301
301
|
alignment?: FaceAlignmentResult;
|
|
302
302
|
rejectionReason?: 'blur' | 'backlit' | 'low_light' | 'partial_face' | 'no_face' | 'too_close' | 'too_far' | 'misaligned';
|
|
303
303
|
}
|
|
304
|
-
declare const DEFAULT_BLUR_THRESHOLD =
|
|
304
|
+
declare const DEFAULT_BLUR_THRESHOLD = 110;
|
|
305
305
|
declare const BLUR_THRESHOLD_MOBILE = 60;
|
|
306
306
|
declare const BACKLIT_RATIO_THRESHOLD = 0.6;
|
|
307
307
|
declare const LOW_LIGHT_THRESHOLD = 50;
|
|
@@ -353,6 +353,14 @@ interface FaceRollResult {
|
|
|
353
353
|
}
|
|
354
354
|
declare const MAX_FACE_ROLL_DEGREES = 15;
|
|
355
355
|
declare function detectFaceRoll(landmarks: FaceLandmarkPoint[]): FaceRollResult;
|
|
356
|
+
interface CameraAngleResult {
|
|
357
|
+
ratio: number;
|
|
358
|
+
cameraAbove: boolean;
|
|
359
|
+
cameraBelow: boolean;
|
|
360
|
+
}
|
|
361
|
+
declare const CAMERA_ANGLE_HIGH_RATIO = 1.35;
|
|
362
|
+
declare const CAMERA_ANGLE_LOW_RATIO = 0.75;
|
|
363
|
+
declare function detectCameraAngle(landmarks: FaceLandmarkPoint[]): CameraAngleResult;
|
|
356
364
|
declare class BaseFrameCollector {
|
|
357
365
|
protected frames: CapturedFrame[];
|
|
358
366
|
protected maxFrames: number;
|
|
@@ -489,6 +497,7 @@ declare class LivenessClient {
|
|
|
489
497
|
model?: FastCheckModel;
|
|
490
498
|
source?: FrameSource;
|
|
491
499
|
warnings?: string[];
|
|
500
|
+
bgSegmentation?: boolean;
|
|
492
501
|
}): Promise<LivenessResult>;
|
|
493
502
|
streamFrame(frame: CapturedFrame, options: {
|
|
494
503
|
sessionId: string;
|
|
@@ -704,8 +713,9 @@ declare const FEEDBACK_MESSAGES: {
|
|
|
704
713
|
readonly poor_lighting: "Improve lighting";
|
|
705
714
|
readonly too_dark: "Low lighting - move to a brighter area";
|
|
706
715
|
readonly backlit: "Backlit - try facing the light source";
|
|
707
|
-
readonly
|
|
708
|
-
readonly
|
|
716
|
+
readonly camera_angle_low: "Raise camera to eye level";
|
|
717
|
+
readonly camera_angle_high: "Lower camera to eye level";
|
|
718
|
+
readonly camera_tilted: "Hold camera level";
|
|
709
719
|
readonly hand_detected: "Remove hand from face";
|
|
710
720
|
readonly eyes_not_visible: "Eyes not clearly visible";
|
|
711
721
|
readonly eyes_shadowed: "Eyes are in shadow - improve lighting";
|
|
@@ -738,8 +748,9 @@ interface CaptureQualityState {
|
|
|
738
748
|
targetFrames: number;
|
|
739
749
|
tooFarFromIdeal?: boolean;
|
|
740
750
|
tooCloseToIdeal?: boolean;
|
|
741
|
-
|
|
742
|
-
|
|
751
|
+
cameraAngleLow?: boolean;
|
|
752
|
+
cameraAngleHigh?: boolean;
|
|
753
|
+
cameraTilted?: boolean;
|
|
743
754
|
}
|
|
744
755
|
declare function getCaptureQualityFeedback(state: CaptureQualityState): string;
|
|
745
756
|
declare function canCaptureFrame(state: Omit<CaptureQualityState, 'framesCaptured' | 'targetFrames' | 'isCapturing'>): boolean;
|
|
@@ -790,4 +801,4 @@ declare function analyzeEyeRegionContrast(pixels: Uint8Array | Uint8ClampedArray
|
|
|
790
801
|
declare function detectSpecularHighlights(pixels: Uint8Array | Uint8ClampedArray, relativeFactor?: number, meanBrightness?: number): number;
|
|
791
802
|
declare function checkEyeRegionQuality(pixels: Uint8Array | Uint8ClampedArray, thresholds?: EyeQualityThresholds): EyeRegionQuality;
|
|
792
803
|
|
|
793
|
-
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 };
|
|
804
|
+
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, CAMERA_ANGLE_HIGH_RATIO, CAMERA_ANGLE_LOW_RATIO, type CameraAngleResult, 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, detectCameraAngle, 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
|
@@ -301,7 +301,7 @@ interface FrameQualityResult {
|
|
|
301
301
|
alignment?: FaceAlignmentResult;
|
|
302
302
|
rejectionReason?: 'blur' | 'backlit' | 'low_light' | 'partial_face' | 'no_face' | 'too_close' | 'too_far' | 'misaligned';
|
|
303
303
|
}
|
|
304
|
-
declare const DEFAULT_BLUR_THRESHOLD =
|
|
304
|
+
declare const DEFAULT_BLUR_THRESHOLD = 110;
|
|
305
305
|
declare const BLUR_THRESHOLD_MOBILE = 60;
|
|
306
306
|
declare const BACKLIT_RATIO_THRESHOLD = 0.6;
|
|
307
307
|
declare const LOW_LIGHT_THRESHOLD = 50;
|
|
@@ -353,6 +353,14 @@ interface FaceRollResult {
|
|
|
353
353
|
}
|
|
354
354
|
declare const MAX_FACE_ROLL_DEGREES = 15;
|
|
355
355
|
declare function detectFaceRoll(landmarks: FaceLandmarkPoint[]): FaceRollResult;
|
|
356
|
+
interface CameraAngleResult {
|
|
357
|
+
ratio: number;
|
|
358
|
+
cameraAbove: boolean;
|
|
359
|
+
cameraBelow: boolean;
|
|
360
|
+
}
|
|
361
|
+
declare const CAMERA_ANGLE_HIGH_RATIO = 1.35;
|
|
362
|
+
declare const CAMERA_ANGLE_LOW_RATIO = 0.75;
|
|
363
|
+
declare function detectCameraAngle(landmarks: FaceLandmarkPoint[]): CameraAngleResult;
|
|
356
364
|
declare class BaseFrameCollector {
|
|
357
365
|
protected frames: CapturedFrame[];
|
|
358
366
|
protected maxFrames: number;
|
|
@@ -489,6 +497,7 @@ declare class LivenessClient {
|
|
|
489
497
|
model?: FastCheckModel;
|
|
490
498
|
source?: FrameSource;
|
|
491
499
|
warnings?: string[];
|
|
500
|
+
bgSegmentation?: boolean;
|
|
492
501
|
}): Promise<LivenessResult>;
|
|
493
502
|
streamFrame(frame: CapturedFrame, options: {
|
|
494
503
|
sessionId: string;
|
|
@@ -704,8 +713,9 @@ declare const FEEDBACK_MESSAGES: {
|
|
|
704
713
|
readonly poor_lighting: "Improve lighting";
|
|
705
714
|
readonly too_dark: "Low lighting - move to a brighter area";
|
|
706
715
|
readonly backlit: "Backlit - try facing the light source";
|
|
707
|
-
readonly
|
|
708
|
-
readonly
|
|
716
|
+
readonly camera_angle_low: "Raise camera to eye level";
|
|
717
|
+
readonly camera_angle_high: "Lower camera to eye level";
|
|
718
|
+
readonly camera_tilted: "Hold camera level";
|
|
709
719
|
readonly hand_detected: "Remove hand from face";
|
|
710
720
|
readonly eyes_not_visible: "Eyes not clearly visible";
|
|
711
721
|
readonly eyes_shadowed: "Eyes are in shadow - improve lighting";
|
|
@@ -738,8 +748,9 @@ interface CaptureQualityState {
|
|
|
738
748
|
targetFrames: number;
|
|
739
749
|
tooFarFromIdeal?: boolean;
|
|
740
750
|
tooCloseToIdeal?: boolean;
|
|
741
|
-
|
|
742
|
-
|
|
751
|
+
cameraAngleLow?: boolean;
|
|
752
|
+
cameraAngleHigh?: boolean;
|
|
753
|
+
cameraTilted?: boolean;
|
|
743
754
|
}
|
|
744
755
|
declare function getCaptureQualityFeedback(state: CaptureQualityState): string;
|
|
745
756
|
declare function canCaptureFrame(state: Omit<CaptureQualityState, 'framesCaptured' | 'targetFrames' | 'isCapturing'>): boolean;
|
|
@@ -790,4 +801,4 @@ declare function analyzeEyeRegionContrast(pixels: Uint8Array | Uint8ClampedArray
|
|
|
790
801
|
declare function detectSpecularHighlights(pixels: Uint8Array | Uint8ClampedArray, relativeFactor?: number, meanBrightness?: number): number;
|
|
791
802
|
declare function checkEyeRegionQuality(pixels: Uint8Array | Uint8ClampedArray, thresholds?: EyeQualityThresholds): EyeRegionQuality;
|
|
792
803
|
|
|
793
|
-
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 };
|
|
804
|
+
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, CAMERA_ANGLE_HIGH_RATIO, CAMERA_ANGLE_LOW_RATIO, type CameraAngleResult, 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, detectCameraAngle, 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
|
@@ -31,6 +31,8 @@ __export(index_exports, {
|
|
|
31
31
|
BACKLIT_RATIO_THRESHOLD: () => BACKLIT_RATIO_THRESHOLD,
|
|
32
32
|
BLUR_THRESHOLD_MOBILE: () => BLUR_THRESHOLD_MOBILE,
|
|
33
33
|
BaseFrameCollector: () => BaseFrameCollector,
|
|
34
|
+
CAMERA_ANGLE_HIGH_RATIO: () => CAMERA_ANGLE_HIGH_RATIO,
|
|
35
|
+
CAMERA_ANGLE_LOW_RATIO: () => CAMERA_ANGLE_LOW_RATIO,
|
|
34
36
|
DEFAULT_BLUR_THRESHOLD: () => DEFAULT_BLUR_THRESHOLD,
|
|
35
37
|
DEFAULT_ENDPOINT: () => DEFAULT_ENDPOINT,
|
|
36
38
|
DEFAULT_FACE_DETECTION_TIERS: () => DEFAULT_FACE_DETECTION_TIERS,
|
|
@@ -95,6 +97,7 @@ __export(index_exports, {
|
|
|
95
97
|
checkEyeRegionQuality: () => checkEyeRegionQuality,
|
|
96
98
|
checkFrameQuality: () => checkFrameQuality,
|
|
97
99
|
decodeBase64: () => decodeBase64,
|
|
100
|
+
detectCameraAngle: () => detectCameraAngle,
|
|
98
101
|
detectFaceRoll: () => detectFaceRoll,
|
|
99
102
|
detectSpecularHighlights: () => detectSpecularHighlights,
|
|
100
103
|
encodeBase64: () => encodeBase64,
|
|
@@ -481,7 +484,8 @@ var LivenessClient = class _LivenessClient {
|
|
|
481
484
|
model: options.model ?? "10",
|
|
482
485
|
source: options.source ?? "live",
|
|
483
486
|
crops,
|
|
484
|
-
...options.warnings?.length ? { warnings: options.warnings } : {}
|
|
487
|
+
...options.warnings?.length ? { warnings: options.warnings } : {},
|
|
488
|
+
...options.bgSegmentation !== void 0 ? { bg_segmentation: options.bgSegmentation } : {}
|
|
485
489
|
};
|
|
486
490
|
const response = await this.requestWithRetry(API_PATHS.fastCheckCrops, {
|
|
487
491
|
method: "POST",
|
|
@@ -1254,9 +1258,10 @@ var FEEDBACK_MESSAGES = {
|
|
|
1254
1258
|
poor_lighting: "Improve lighting",
|
|
1255
1259
|
too_dark: "Low lighting - move to a brighter area",
|
|
1256
1260
|
backlit: "Backlit - try facing the light source",
|
|
1257
|
-
//
|
|
1258
|
-
|
|
1259
|
-
|
|
1261
|
+
// Camera angle (platform-agnostic)
|
|
1262
|
+
camera_angle_low: "Raise camera to eye level",
|
|
1263
|
+
camera_angle_high: "Lower camera to eye level",
|
|
1264
|
+
camera_tilted: "Hold camera level",
|
|
1260
1265
|
// Hand occlusion
|
|
1261
1266
|
hand_detected: "Remove hand from face",
|
|
1262
1267
|
// Eye region quality
|
|
@@ -1316,9 +1321,10 @@ var ES_LOCALE = {
|
|
|
1316
1321
|
poor_lighting: "Mejora la iluminaci\xF3n",
|
|
1317
1322
|
too_dark: "Poca luz - mu\xE9vete a un \xE1rea m\xE1s iluminada",
|
|
1318
1323
|
backlit: "Contraluz - intenta mirar hacia la fuente de luz",
|
|
1319
|
-
//
|
|
1320
|
-
|
|
1321
|
-
|
|
1324
|
+
// Camera angle (platform-agnostic)
|
|
1325
|
+
camera_angle_low: "Levanta la c\xE1mara a la altura de los ojos",
|
|
1326
|
+
camera_angle_high: "Baja la c\xE1mara a la altura de los ojos",
|
|
1327
|
+
camera_tilted: "Mant\xE9n la c\xE1mara nivelada",
|
|
1322
1328
|
// Hand occlusion
|
|
1323
1329
|
hand_detected: "Retira la mano del rostro",
|
|
1324
1330
|
// Eye region quality
|
|
@@ -1354,17 +1360,21 @@ function getCaptureQualityFeedback(state) {
|
|
|
1354
1360
|
targetFrames,
|
|
1355
1361
|
tooFarFromIdeal,
|
|
1356
1362
|
tooCloseToIdeal,
|
|
1357
|
-
|
|
1358
|
-
|
|
1363
|
+
cameraAngleLow,
|
|
1364
|
+
cameraAngleHigh,
|
|
1365
|
+
cameraTilted
|
|
1359
1366
|
} = state;
|
|
1360
1367
|
if (!hasFace) {
|
|
1361
1368
|
return FEEDBACK_MESSAGES.no_face;
|
|
1362
1369
|
}
|
|
1363
|
-
if (
|
|
1364
|
-
return FEEDBACK_MESSAGES.
|
|
1370
|
+
if (cameraTilted) {
|
|
1371
|
+
return FEEDBACK_MESSAGES.camera_tilted;
|
|
1372
|
+
}
|
|
1373
|
+
if (cameraAngleLow) {
|
|
1374
|
+
return FEEDBACK_MESSAGES.camera_angle_low;
|
|
1365
1375
|
}
|
|
1366
|
-
if (
|
|
1367
|
-
return FEEDBACK_MESSAGES.
|
|
1376
|
+
if (cameraAngleHigh) {
|
|
1377
|
+
return FEEDBACK_MESSAGES.camera_angle_high;
|
|
1368
1378
|
}
|
|
1369
1379
|
if (tooClose) {
|
|
1370
1380
|
return FEEDBACK_MESSAGES.too_close;
|
|
@@ -1408,10 +1418,11 @@ function canCaptureFrame(state) {
|
|
|
1408
1418
|
isPartialFace,
|
|
1409
1419
|
tooFarFromIdeal,
|
|
1410
1420
|
tooCloseToIdeal,
|
|
1411
|
-
|
|
1412
|
-
|
|
1421
|
+
cameraAngleLow,
|
|
1422
|
+
cameraAngleHigh,
|
|
1423
|
+
cameraTilted
|
|
1413
1424
|
} = state;
|
|
1414
|
-
return hasFace && alignment >= ALIGNMENT_THRESHOLD_CAPTURE && !tooClose && !tooFar && !isBlurry && !isPartialFace && !tooFarFromIdeal && !tooCloseToIdeal && !
|
|
1425
|
+
return hasFace && alignment >= ALIGNMENT_THRESHOLD_CAPTURE && !tooClose && !tooFar && !isBlurry && !isPartialFace && !tooFarFromIdeal && !tooCloseToIdeal && !cameraAngleLow && !cameraAngleHigh && !cameraTilted;
|
|
1415
1426
|
}
|
|
1416
1427
|
|
|
1417
1428
|
// src/utils/validators.ts
|
|
@@ -1478,7 +1489,7 @@ function decodeBase64(base64) {
|
|
|
1478
1489
|
}
|
|
1479
1490
|
|
|
1480
1491
|
// src/utils/frameAnalysis.ts
|
|
1481
|
-
var DEFAULT_BLUR_THRESHOLD =
|
|
1492
|
+
var DEFAULT_BLUR_THRESHOLD = 110;
|
|
1482
1493
|
var BLUR_THRESHOLD_MOBILE = 60;
|
|
1483
1494
|
var BACKLIT_RATIO_THRESHOLD = 0.6;
|
|
1484
1495
|
var LOW_LIGHT_THRESHOLD = 50;
|
|
@@ -1779,6 +1790,30 @@ function detectFaceRoll(landmarks) {
|
|
|
1779
1790
|
tooTilted: roll > MAX_FACE_ROLL_DEGREES
|
|
1780
1791
|
};
|
|
1781
1792
|
}
|
|
1793
|
+
var CAMERA_ANGLE_HIGH_RATIO = 1.35;
|
|
1794
|
+
var CAMERA_ANGLE_LOW_RATIO = 0.75;
|
|
1795
|
+
function detectCameraAngle(landmarks) {
|
|
1796
|
+
if (landmarks.length < 153) {
|
|
1797
|
+
return { ratio: 1, cameraAbove: false, cameraBelow: false };
|
|
1798
|
+
}
|
|
1799
|
+
const forehead = landmarks[10];
|
|
1800
|
+
const noseTip = landmarks[1];
|
|
1801
|
+
const chin = landmarks[152];
|
|
1802
|
+
if (!forehead || !noseTip || !chin) {
|
|
1803
|
+
return { ratio: 1, cameraAbove: false, cameraBelow: false };
|
|
1804
|
+
}
|
|
1805
|
+
const foreheadToNose = Math.abs(forehead.y - noseTip.y);
|
|
1806
|
+
const noseToChin = Math.abs(noseTip.y - chin.y);
|
|
1807
|
+
if (noseToChin < 1e-3) {
|
|
1808
|
+
return { ratio: 1, cameraAbove: false, cameraBelow: false };
|
|
1809
|
+
}
|
|
1810
|
+
const ratio = foreheadToNose / noseToChin;
|
|
1811
|
+
return {
|
|
1812
|
+
ratio,
|
|
1813
|
+
cameraAbove: ratio > CAMERA_ANGLE_HIGH_RATIO,
|
|
1814
|
+
cameraBelow: ratio < CAMERA_ANGLE_LOW_RATIO
|
|
1815
|
+
};
|
|
1816
|
+
}
|
|
1782
1817
|
var BaseFrameCollector = class {
|
|
1783
1818
|
constructor(maxFrames = 10) {
|
|
1784
1819
|
this.frames = [];
|
|
@@ -1996,6 +2031,8 @@ function checkEyeRegionQuality(pixels, thresholds = EYE_QUALITY_THRESHOLDS) {
|
|
|
1996
2031
|
BACKLIT_RATIO_THRESHOLD,
|
|
1997
2032
|
BLUR_THRESHOLD_MOBILE,
|
|
1998
2033
|
BaseFrameCollector,
|
|
2034
|
+
CAMERA_ANGLE_HIGH_RATIO,
|
|
2035
|
+
CAMERA_ANGLE_LOW_RATIO,
|
|
1999
2036
|
DEFAULT_BLUR_THRESHOLD,
|
|
2000
2037
|
DEFAULT_ENDPOINT,
|
|
2001
2038
|
DEFAULT_FACE_DETECTION_TIERS,
|
|
@@ -2060,6 +2097,7 @@ function checkEyeRegionQuality(pixels, thresholds = EYE_QUALITY_THRESHOLDS) {
|
|
|
2060
2097
|
checkEyeRegionQuality,
|
|
2061
2098
|
checkFrameQuality,
|
|
2062
2099
|
decodeBase64,
|
|
2100
|
+
detectCameraAngle,
|
|
2063
2101
|
detectFaceRoll,
|
|
2064
2102
|
detectSpecularHighlights,
|
|
2065
2103
|
encodeBase64,
|
package/dist/index.mjs
CHANGED
|
@@ -348,7 +348,8 @@ var LivenessClient = class _LivenessClient {
|
|
|
348
348
|
model: options.model ?? "10",
|
|
349
349
|
source: options.source ?? "live",
|
|
350
350
|
crops,
|
|
351
|
-
...options.warnings?.length ? { warnings: options.warnings } : {}
|
|
351
|
+
...options.warnings?.length ? { warnings: options.warnings } : {},
|
|
352
|
+
...options.bgSegmentation !== void 0 ? { bg_segmentation: options.bgSegmentation } : {}
|
|
352
353
|
};
|
|
353
354
|
const response = await this.requestWithRetry(API_PATHS.fastCheckCrops, {
|
|
354
355
|
method: "POST",
|
|
@@ -1121,9 +1122,10 @@ var FEEDBACK_MESSAGES = {
|
|
|
1121
1122
|
poor_lighting: "Improve lighting",
|
|
1122
1123
|
too_dark: "Low lighting - move to a brighter area",
|
|
1123
1124
|
backlit: "Backlit - try facing the light source",
|
|
1124
|
-
//
|
|
1125
|
-
|
|
1126
|
-
|
|
1125
|
+
// Camera angle (platform-agnostic)
|
|
1126
|
+
camera_angle_low: "Raise camera to eye level",
|
|
1127
|
+
camera_angle_high: "Lower camera to eye level",
|
|
1128
|
+
camera_tilted: "Hold camera level",
|
|
1127
1129
|
// Hand occlusion
|
|
1128
1130
|
hand_detected: "Remove hand from face",
|
|
1129
1131
|
// Eye region quality
|
|
@@ -1183,9 +1185,10 @@ var ES_LOCALE = {
|
|
|
1183
1185
|
poor_lighting: "Mejora la iluminaci\xF3n",
|
|
1184
1186
|
too_dark: "Poca luz - mu\xE9vete a un \xE1rea m\xE1s iluminada",
|
|
1185
1187
|
backlit: "Contraluz - intenta mirar hacia la fuente de luz",
|
|
1186
|
-
//
|
|
1187
|
-
|
|
1188
|
-
|
|
1188
|
+
// Camera angle (platform-agnostic)
|
|
1189
|
+
camera_angle_low: "Levanta la c\xE1mara a la altura de los ojos",
|
|
1190
|
+
camera_angle_high: "Baja la c\xE1mara a la altura de los ojos",
|
|
1191
|
+
camera_tilted: "Mant\xE9n la c\xE1mara nivelada",
|
|
1189
1192
|
// Hand occlusion
|
|
1190
1193
|
hand_detected: "Retira la mano del rostro",
|
|
1191
1194
|
// Eye region quality
|
|
@@ -1221,17 +1224,21 @@ function getCaptureQualityFeedback(state) {
|
|
|
1221
1224
|
targetFrames,
|
|
1222
1225
|
tooFarFromIdeal,
|
|
1223
1226
|
tooCloseToIdeal,
|
|
1224
|
-
|
|
1225
|
-
|
|
1227
|
+
cameraAngleLow,
|
|
1228
|
+
cameraAngleHigh,
|
|
1229
|
+
cameraTilted
|
|
1226
1230
|
} = state;
|
|
1227
1231
|
if (!hasFace) {
|
|
1228
1232
|
return FEEDBACK_MESSAGES.no_face;
|
|
1229
1233
|
}
|
|
1230
|
-
if (
|
|
1231
|
-
return FEEDBACK_MESSAGES.
|
|
1234
|
+
if (cameraTilted) {
|
|
1235
|
+
return FEEDBACK_MESSAGES.camera_tilted;
|
|
1236
|
+
}
|
|
1237
|
+
if (cameraAngleLow) {
|
|
1238
|
+
return FEEDBACK_MESSAGES.camera_angle_low;
|
|
1232
1239
|
}
|
|
1233
|
-
if (
|
|
1234
|
-
return FEEDBACK_MESSAGES.
|
|
1240
|
+
if (cameraAngleHigh) {
|
|
1241
|
+
return FEEDBACK_MESSAGES.camera_angle_high;
|
|
1235
1242
|
}
|
|
1236
1243
|
if (tooClose) {
|
|
1237
1244
|
return FEEDBACK_MESSAGES.too_close;
|
|
@@ -1275,10 +1282,11 @@ function canCaptureFrame(state) {
|
|
|
1275
1282
|
isPartialFace,
|
|
1276
1283
|
tooFarFromIdeal,
|
|
1277
1284
|
tooCloseToIdeal,
|
|
1278
|
-
|
|
1279
|
-
|
|
1285
|
+
cameraAngleLow,
|
|
1286
|
+
cameraAngleHigh,
|
|
1287
|
+
cameraTilted
|
|
1280
1288
|
} = state;
|
|
1281
|
-
return hasFace && alignment >= ALIGNMENT_THRESHOLD_CAPTURE && !tooClose && !tooFar && !isBlurry && !isPartialFace && !tooFarFromIdeal && !tooCloseToIdeal && !
|
|
1289
|
+
return hasFace && alignment >= ALIGNMENT_THRESHOLD_CAPTURE && !tooClose && !tooFar && !isBlurry && !isPartialFace && !tooFarFromIdeal && !tooCloseToIdeal && !cameraAngleLow && !cameraAngleHigh && !cameraTilted;
|
|
1282
1290
|
}
|
|
1283
1291
|
|
|
1284
1292
|
// src/utils/validators.ts
|
|
@@ -1345,7 +1353,7 @@ function decodeBase64(base64) {
|
|
|
1345
1353
|
}
|
|
1346
1354
|
|
|
1347
1355
|
// src/utils/frameAnalysis.ts
|
|
1348
|
-
var DEFAULT_BLUR_THRESHOLD =
|
|
1356
|
+
var DEFAULT_BLUR_THRESHOLD = 110;
|
|
1349
1357
|
var BLUR_THRESHOLD_MOBILE = 60;
|
|
1350
1358
|
var BACKLIT_RATIO_THRESHOLD = 0.6;
|
|
1351
1359
|
var LOW_LIGHT_THRESHOLD = 50;
|
|
@@ -1646,6 +1654,30 @@ function detectFaceRoll(landmarks) {
|
|
|
1646
1654
|
tooTilted: roll > MAX_FACE_ROLL_DEGREES
|
|
1647
1655
|
};
|
|
1648
1656
|
}
|
|
1657
|
+
var CAMERA_ANGLE_HIGH_RATIO = 1.35;
|
|
1658
|
+
var CAMERA_ANGLE_LOW_RATIO = 0.75;
|
|
1659
|
+
function detectCameraAngle(landmarks) {
|
|
1660
|
+
if (landmarks.length < 153) {
|
|
1661
|
+
return { ratio: 1, cameraAbove: false, cameraBelow: false };
|
|
1662
|
+
}
|
|
1663
|
+
const forehead = landmarks[10];
|
|
1664
|
+
const noseTip = landmarks[1];
|
|
1665
|
+
const chin = landmarks[152];
|
|
1666
|
+
if (!forehead || !noseTip || !chin) {
|
|
1667
|
+
return { ratio: 1, cameraAbove: false, cameraBelow: false };
|
|
1668
|
+
}
|
|
1669
|
+
const foreheadToNose = Math.abs(forehead.y - noseTip.y);
|
|
1670
|
+
const noseToChin = Math.abs(noseTip.y - chin.y);
|
|
1671
|
+
if (noseToChin < 1e-3) {
|
|
1672
|
+
return { ratio: 1, cameraAbove: false, cameraBelow: false };
|
|
1673
|
+
}
|
|
1674
|
+
const ratio = foreheadToNose / noseToChin;
|
|
1675
|
+
return {
|
|
1676
|
+
ratio,
|
|
1677
|
+
cameraAbove: ratio > CAMERA_ANGLE_HIGH_RATIO,
|
|
1678
|
+
cameraBelow: ratio < CAMERA_ANGLE_LOW_RATIO
|
|
1679
|
+
};
|
|
1680
|
+
}
|
|
1649
1681
|
var BaseFrameCollector = class {
|
|
1650
1682
|
constructor(maxFrames = 10) {
|
|
1651
1683
|
this.frames = [];
|
|
@@ -1862,6 +1894,8 @@ export {
|
|
|
1862
1894
|
BACKLIT_RATIO_THRESHOLD,
|
|
1863
1895
|
BLUR_THRESHOLD_MOBILE,
|
|
1864
1896
|
BaseFrameCollector,
|
|
1897
|
+
CAMERA_ANGLE_HIGH_RATIO,
|
|
1898
|
+
CAMERA_ANGLE_LOW_RATIO,
|
|
1865
1899
|
DEFAULT_BLUR_THRESHOLD,
|
|
1866
1900
|
DEFAULT_ENDPOINT,
|
|
1867
1901
|
DEFAULT_FACE_DETECTION_TIERS,
|
|
@@ -1926,6 +1960,7 @@ export {
|
|
|
1926
1960
|
checkEyeRegionQuality,
|
|
1927
1961
|
checkFrameQuality,
|
|
1928
1962
|
decodeBase64,
|
|
1963
|
+
detectCameraAngle,
|
|
1929
1964
|
detectFaceRoll,
|
|
1930
1965
|
detectSpecularHighlights,
|
|
1931
1966
|
encodeBase64,
|