@moveris/shared 2.5.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 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;
@@ -272,15 +306,17 @@ 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 = 2;
276
- declare const MIN_CROP_MULTIPLIER = 1.8;
277
- declare const MAX_CROP_MULTIPLIER = 2.5;
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.6;
283
- declare const TARGET_FACE_PERCENTAGE_IN_CROP = 0.5;
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;
@@ -306,6 +342,12 @@ declare function checkFrameQuality(options: {
306
342
  lightingAnalysis?: LightingAnalysis;
307
343
  minAlignment?: number;
308
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;
309
351
  declare class BaseFrameCollector {
310
352
  protected frames: CapturedFrame[];
311
353
  protected maxFrames: number;
@@ -321,40 +363,6 @@ declare class BaseFrameCollector {
321
363
  getNextIndex(): number;
322
364
  }
323
365
 
324
- interface FaceLandmarkPoint {
325
- x: number;
326
- y: number;
327
- z: number;
328
- }
329
- interface LandmarkValidationResult {
330
- valid: boolean;
331
- message?: string;
332
- }
333
- declare const LANDMARK_INDEX: {
334
- readonly NOSE_TIP: 1;
335
- readonly UPPER_LIP: 13;
336
- readonly LOWER_LIP: 14;
337
- };
338
- declare const EYE_LANDMARK_INDICES: {
339
- readonly RIGHT_EYE: readonly [33, 7, 163, 144, 145, 153, 154, 155, 133, 173, 157, 158, 159, 160, 161, 246];
340
- readonly LEFT_EYE: readonly [362, 382, 381, 380, 374, 373, 390, 249, 263, 466, 388, 387, 386, 385, 384, 398];
341
- };
342
- interface EyeRegionBounds {
343
- x: number;
344
- y: number;
345
- width: number;
346
- height: number;
347
- }
348
- interface EyeRegionsBounds {
349
- leftEye: EyeRegionBounds;
350
- rightEye: EyeRegionBounds;
351
- }
352
- declare function getEyeRegionBounds(landmarks: FaceLandmarkPoint[]): EyeRegionsBounds | null;
353
- declare const LANDMARK_MIN_BOUND = 0.1;
354
- declare const LANDMARK_MAX_BOUND = 0.9;
355
- declare const MIN_LANDMARK_COUNT = 15;
356
- declare function validateFaceLandmarks(landmarks: FaceLandmarkPoint[] | undefined): LandmarkValidationResult;
357
-
358
366
  interface DetectionResult {
359
367
  type: string;
360
368
  passed: boolean;
@@ -620,7 +628,7 @@ declare const ERROR_MESSAGES_ES: Record<string, string>;
620
628
  declare function getApiErrorMessage(code: string | undefined, message?: string, customMessages?: Record<string, string>): string;
621
629
  declare function isRetryableError(code: string | undefined): boolean;
622
630
 
623
- declare const ALIGNMENT_THRESHOLD_CAPTURE = 0.6;
631
+ declare const ALIGNMENT_THRESHOLD_CAPTURE = 0.85;
624
632
  declare const ALIGNMENT_THRESHOLD_POOR = 0.6;
625
633
  declare const ALIGNMENT_THRESHOLD_GOOD = 0.6;
626
634
  declare const ALIGNMENT_THRESHOLD_PERFECT = 0.85;
@@ -677,6 +685,8 @@ declare const FEEDBACK_MESSAGES: {
677
685
  readonly move_back: "Move back - face too close";
678
686
  readonly too_close: "Move back - face too close";
679
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";
680
690
  readonly face_not_visible: "Center your face - edges cut off";
681
691
  readonly partial_face: "Center your face - edges cut off";
682
692
  readonly hold_still: "Hold still - image blurry";
@@ -684,6 +694,8 @@ declare const FEEDBACK_MESSAGES: {
684
694
  readonly poor_lighting: "Improve lighting";
685
695
  readonly too_dark: "Low lighting - move to a brighter area";
686
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";
687
699
  readonly hand_detected: "Remove hand from face";
688
700
  readonly eyes_not_visible: "Eyes not clearly visible";
689
701
  readonly eyes_shadowed: "Eyes are in shadow - improve lighting";
@@ -714,6 +726,10 @@ interface CaptureQualityState {
714
726
  isPartialFace: boolean;
715
727
  framesCaptured: number;
716
728
  targetFrames: number;
729
+ tooFarFromIdeal?: boolean;
730
+ tooCloseToIdeal?: boolean;
731
+ phoneAngled?: boolean;
732
+ phoneTilted?: boolean;
717
733
  }
718
734
  declare function getCaptureQualityFeedback(state: CaptureQualityState): string;
719
735
  declare function canCaptureFrame(state: Omit<CaptureQualityState, 'framesCaptured' | 'targetFrames' | 'isCapturing'>): boolean;
@@ -764,4 +780,4 @@ declare function analyzeEyeRegionContrast(pixels: Uint8Array | Uint8ClampedArray
764
780
  declare function detectSpecularHighlights(pixels: Uint8Array | Uint8ClampedArray, threshold?: number): number;
765
781
  declare function checkEyeRegionQuality(pixels: Uint8Array | Uint8ClampedArray, thresholds?: EyeQualityThresholds): EyeRegionQuality;
766
782
 
767
- 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, 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, 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;
@@ -272,15 +306,17 @@ 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 = 2;
276
- declare const MIN_CROP_MULTIPLIER = 1.8;
277
- declare const MAX_CROP_MULTIPLIER = 2.5;
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.6;
283
- declare const TARGET_FACE_PERCENTAGE_IN_CROP = 0.5;
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;
@@ -306,6 +342,12 @@ declare function checkFrameQuality(options: {
306
342
  lightingAnalysis?: LightingAnalysis;
307
343
  minAlignment?: number;
308
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;
309
351
  declare class BaseFrameCollector {
310
352
  protected frames: CapturedFrame[];
311
353
  protected maxFrames: number;
@@ -321,40 +363,6 @@ declare class BaseFrameCollector {
321
363
  getNextIndex(): number;
322
364
  }
323
365
 
324
- interface FaceLandmarkPoint {
325
- x: number;
326
- y: number;
327
- z: number;
328
- }
329
- interface LandmarkValidationResult {
330
- valid: boolean;
331
- message?: string;
332
- }
333
- declare const LANDMARK_INDEX: {
334
- readonly NOSE_TIP: 1;
335
- readonly UPPER_LIP: 13;
336
- readonly LOWER_LIP: 14;
337
- };
338
- declare const EYE_LANDMARK_INDICES: {
339
- readonly RIGHT_EYE: readonly [33, 7, 163, 144, 145, 153, 154, 155, 133, 173, 157, 158, 159, 160, 161, 246];
340
- readonly LEFT_EYE: readonly [362, 382, 381, 380, 374, 373, 390, 249, 263, 466, 388, 387, 386, 385, 384, 398];
341
- };
342
- interface EyeRegionBounds {
343
- x: number;
344
- y: number;
345
- width: number;
346
- height: number;
347
- }
348
- interface EyeRegionsBounds {
349
- leftEye: EyeRegionBounds;
350
- rightEye: EyeRegionBounds;
351
- }
352
- declare function getEyeRegionBounds(landmarks: FaceLandmarkPoint[]): EyeRegionsBounds | null;
353
- declare const LANDMARK_MIN_BOUND = 0.1;
354
- declare const LANDMARK_MAX_BOUND = 0.9;
355
- declare const MIN_LANDMARK_COUNT = 15;
356
- declare function validateFaceLandmarks(landmarks: FaceLandmarkPoint[] | undefined): LandmarkValidationResult;
357
-
358
366
  interface DetectionResult {
359
367
  type: string;
360
368
  passed: boolean;
@@ -620,7 +628,7 @@ declare const ERROR_MESSAGES_ES: Record<string, string>;
620
628
  declare function getApiErrorMessage(code: string | undefined, message?: string, customMessages?: Record<string, string>): string;
621
629
  declare function isRetryableError(code: string | undefined): boolean;
622
630
 
623
- declare const ALIGNMENT_THRESHOLD_CAPTURE = 0.6;
631
+ declare const ALIGNMENT_THRESHOLD_CAPTURE = 0.85;
624
632
  declare const ALIGNMENT_THRESHOLD_POOR = 0.6;
625
633
  declare const ALIGNMENT_THRESHOLD_GOOD = 0.6;
626
634
  declare const ALIGNMENT_THRESHOLD_PERFECT = 0.85;
@@ -677,6 +685,8 @@ declare const FEEDBACK_MESSAGES: {
677
685
  readonly move_back: "Move back - face too close";
678
686
  readonly too_close: "Move back - face too close";
679
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";
680
690
  readonly face_not_visible: "Center your face - edges cut off";
681
691
  readonly partial_face: "Center your face - edges cut off";
682
692
  readonly hold_still: "Hold still - image blurry";
@@ -684,6 +694,8 @@ declare const FEEDBACK_MESSAGES: {
684
694
  readonly poor_lighting: "Improve lighting";
685
695
  readonly too_dark: "Low lighting - move to a brighter area";
686
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";
687
699
  readonly hand_detected: "Remove hand from face";
688
700
  readonly eyes_not_visible: "Eyes not clearly visible";
689
701
  readonly eyes_shadowed: "Eyes are in shadow - improve lighting";
@@ -714,6 +726,10 @@ interface CaptureQualityState {
714
726
  isPartialFace: boolean;
715
727
  framesCaptured: number;
716
728
  targetFrames: number;
729
+ tooFarFromIdeal?: boolean;
730
+ tooCloseToIdeal?: boolean;
731
+ phoneAngled?: boolean;
732
+ phoneTilted?: boolean;
717
733
  }
718
734
  declare function getCaptureQualityFeedback(state: CaptureQualityState): string;
719
735
  declare function canCaptureFrame(state: Omit<CaptureQualityState, 'framesCaptured' | 'targetFrames' | 'isCapturing'>): boolean;
@@ -764,4 +780,4 @@ declare function analyzeEyeRegionContrast(pixels: Uint8Array | Uint8ClampedArray
764
780
  declare function detectSpecularHighlights(pixels: Uint8Array | Uint8ClampedArray, threshold?: number): number;
765
781
  declare function checkEyeRegionQuality(pixels: Uint8Array | Uint8ClampedArray, thresholds?: EyeQualityThresholds): EyeRegionQuality;
766
782
 
767
- 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, 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, 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,12 +66,15 @@ __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,
@@ -92,6 +95,7 @@ __export(index_exports, {
92
95
  checkEyeRegionQuality: () => checkEyeRegionQuality,
93
96
  checkFrameQuality: () => checkFrameQuality,
94
97
  decodeBase64: () => decodeBase64,
98
+ detectFaceRoll: () => detectFaceRoll,
95
99
  detectSpecularHighlights: () => detectSpecularHighlights,
96
100
  encodeBase64: () => encodeBase64,
97
101
  generateSessionId: () => generateSessionId,
@@ -1177,7 +1181,7 @@ function isRetryableError(code) {
1177
1181
  }
1178
1182
 
1179
1183
  // src/constants/feedback.ts
1180
- var ALIGNMENT_THRESHOLD_CAPTURE = 0.6;
1184
+ var ALIGNMENT_THRESHOLD_CAPTURE = 0.85;
1181
1185
  var ALIGNMENT_THRESHOLD_POOR = 0.6;
1182
1186
  var ALIGNMENT_THRESHOLD_GOOD = 0.6;
1183
1187
  var ALIGNMENT_THRESHOLD_PERFECT = 0.85;
@@ -1230,6 +1234,8 @@ var FEEDBACK_MESSAGES = {
1230
1234
  move_back: "Move back - face too close",
1231
1235
  too_close: "Move back - face too close",
1232
1236
  too_far: "Move closer - face too far",
1237
+ move_closer_ideal: "Move a little closer",
1238
+ move_back_ideal: "Move back slightly",
1233
1239
  // Visibility issues
1234
1240
  face_not_visible: "Center your face - edges cut off",
1235
1241
  partial_face: "Center your face - edges cut off",
@@ -1240,6 +1246,9 @@ var FEEDBACK_MESSAGES = {
1240
1246
  poor_lighting: "Improve lighting",
1241
1247
  too_dark: "Low lighting - move to a brighter area",
1242
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",
1243
1252
  // Hand occlusion
1244
1253
  hand_detected: "Remove hand from face",
1245
1254
  // Eye region quality
@@ -1287,6 +1296,8 @@ var ES_LOCALE = {
1287
1296
  move_back: "Al\xE9jate - rostro muy cerca",
1288
1297
  too_close: "Al\xE9jate - rostro muy cerca",
1289
1298
  too_far: "Ac\xE9rcate - rostro muy lejos",
1299
+ move_closer_ideal: "Ac\xE9rcate un poco",
1300
+ move_back_ideal: "Al\xE9jate un poco",
1290
1301
  // Visibility issues
1291
1302
  face_not_visible: "Centra tu rostro - bordes cortados",
1292
1303
  partial_face: "Centra tu rostro - bordes cortados",
@@ -1297,6 +1308,9 @@ var ES_LOCALE = {
1297
1308
  poor_lighting: "Mejora la iluminaci\xF3n",
1298
1309
  too_dark: "Poca luz - mu\xE9vete a un \xE1rea m\xE1s iluminada",
1299
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",
1300
1314
  // Hand occlusion
1301
1315
  hand_detected: "Retira la mano del rostro",
1302
1316
  // Eye region quality
@@ -1329,11 +1343,21 @@ function getCaptureQualityFeedback(state) {
1329
1343
  isBlurry,
1330
1344
  isPartialFace,
1331
1345
  framesCaptured,
1332
- targetFrames
1346
+ targetFrames,
1347
+ tooFarFromIdeal,
1348
+ tooCloseToIdeal,
1349
+ phoneAngled,
1350
+ phoneTilted
1333
1351
  } = state;
1334
1352
  if (!hasFace) {
1335
1353
  return FEEDBACK_MESSAGES.no_face;
1336
1354
  }
1355
+ if (phoneTilted) {
1356
+ return FEEDBACK_MESSAGES.phone_tilted;
1357
+ }
1358
+ if (phoneAngled) {
1359
+ return FEEDBACK_MESSAGES.phone_angle_low;
1360
+ }
1337
1361
  if (tooClose) {
1338
1362
  return FEEDBACK_MESSAGES.too_close;
1339
1363
  }
@@ -1346,6 +1370,12 @@ function getCaptureQualityFeedback(state) {
1346
1370
  if (isBlurry) {
1347
1371
  return FEEDBACK_MESSAGES.blurry;
1348
1372
  }
1373
+ if (tooFarFromIdeal) {
1374
+ return FEEDBACK_MESSAGES.move_closer_ideal;
1375
+ }
1376
+ if (tooCloseToIdeal) {
1377
+ return FEEDBACK_MESSAGES.move_back_ideal;
1378
+ }
1349
1379
  if (alignment < ALIGNMENT_THRESHOLD_POOR) {
1350
1380
  return FEEDBACK_MESSAGES.move_closer_to_center;
1351
1381
  }
@@ -1361,8 +1391,19 @@ function getCaptureQualityFeedback(state) {
1361
1391
  return FEEDBACK_MESSAGES.perfect;
1362
1392
  }
1363
1393
  function canCaptureFrame(state) {
1364
- const { hasFace, alignment, tooClose, tooFar, isBlurry, isPartialFace } = state;
1365
- return hasFace && alignment >= ALIGNMENT_THRESHOLD_CAPTURE && !tooClose && !tooFar && !isBlurry && !isPartialFace;
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;
1366
1407
  }
1367
1408
 
1368
1409
  // src/utils/validators.ts
@@ -1439,15 +1480,17 @@ var MIN_FACE_SIDE_MARGIN = 0.05;
1439
1480
  var MIN_CAPTURE_ALIGNMENT = 0.6;
1440
1481
  var HIGH_ALIGNMENT = 0.85;
1441
1482
  var GOOD_ALIGNMENT = 0.5;
1442
- var IDEAL_CROP_MULTIPLIER = 2;
1443
- var MIN_CROP_MULTIPLIER = 1.8;
1444
- var MAX_CROP_MULTIPLIER = 2.5;
1483
+ var IDEAL_CROP_MULTIPLIER = 3;
1484
+ var MIN_CROP_MULTIPLIER = 2.5;
1485
+ var MAX_CROP_MULTIPLIER = 4;
1445
1486
  var FACE_CENTER_VERTICAL_OFFSET = 0.05;
1487
+ var MIN_IDEAL_FACE_RATIO = 0.05;
1488
+ var MAX_IDEAL_FACE_RATIO = 0.2;
1446
1489
  var MIN_FACE_RATIO = 0.036;
1447
1490
  var MAX_FACE_RATIO = 0.7;
1448
1491
  var FACE_CROP_OUTPUT_SIZE = 224;
1449
- var MAX_FACE_PERCENTAGE_IN_CROP = 0.6;
1450
- var TARGET_FACE_PERCENTAGE_IN_CROP = 0.5;
1492
+ var MAX_FACE_PERCENTAGE_IN_CROP = 0.45;
1493
+ var TARGET_FACE_PERCENTAGE_IN_CROP = 0.33;
1451
1494
  function analyzeBlur(grayscalePixels, width, height, threshold = DEFAULT_BLUR_THRESHOLD) {
1452
1495
  const laplacian = [];
1453
1496
  for (let y = 1; y < height - 1; y++) {
@@ -1543,9 +1586,9 @@ var OVAL_REGION_DESKTOP = {
1543
1586
  var OVAL_REGION_MOBILE = {
1544
1587
  centerX: 0.5,
1545
1588
  centerY: 0.5,
1546
- width: 0.55,
1547
- height: 0.73
1548
- // 0.55 * (4/3)
1589
+ width: 0.48,
1590
+ height: 0.64
1591
+ // 0.48 * (4/3)
1549
1592
  };
1550
1593
  var DEFAULT_OVAL_REGION = OVAL_REGION_DESKTOP;
1551
1594
  function isFaceInOval(faceBox, frameWidth, frameHeight, oval = DEFAULT_OVAL_REGION, tolerance = 0.3) {
@@ -1701,6 +1744,33 @@ function checkFrameQuality(options) {
1701
1744
  }
1702
1745
  return result;
1703
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
+ }
1704
1774
  var BaseFrameCollector = class {
1705
1775
  constructor(maxFrames = 10) {
1706
1776
  this.frames = [];
@@ -1961,12 +2031,15 @@ function checkEyeRegionQuality(pixels, thresholds = EYE_QUALITY_THRESHOLDS) {
1961
2031
  MAX_CROP_MULTIPLIER,
1962
2032
  MAX_FACE_PERCENTAGE_IN_CROP,
1963
2033
  MAX_FACE_RATIO,
2034
+ MAX_FACE_ROLL_DEGREES,
2035
+ MAX_IDEAL_FACE_RATIO,
1964
2036
  MIN_CAPTURE_ALIGNMENT,
1965
2037
  MIN_CROP_MULTIPLIER,
1966
2038
  MIN_FACE_BOTTOM_MARGIN,
1967
2039
  MIN_FACE_RATIO,
1968
2040
  MIN_FACE_SIDE_MARGIN,
1969
2041
  MIN_FACE_TOP_MARGIN,
2042
+ MIN_IDEAL_FACE_RATIO,
1970
2043
  MIN_LANDMARK_COUNT,
1971
2044
  MODEL_CONFIGS,
1972
2045
  OVAL_GUIDE_COLORS,
@@ -1987,6 +2060,7 @@ function checkEyeRegionQuality(pixels, thresholds = EYE_QUALITY_THRESHOLDS) {
1987
2060
  checkEyeRegionQuality,
1988
2061
  checkFrameQuality,
1989
2062
  decodeBase64,
2063
+ detectFaceRoll,
1990
2064
  detectSpecularHighlights,
1991
2065
  encodeBase64,
1992
2066
  generateSessionId,
package/dist/index.mjs CHANGED
@@ -1048,7 +1048,7 @@ function isRetryableError(code) {
1048
1048
  }
1049
1049
 
1050
1050
  // src/constants/feedback.ts
1051
- var ALIGNMENT_THRESHOLD_CAPTURE = 0.6;
1051
+ var ALIGNMENT_THRESHOLD_CAPTURE = 0.85;
1052
1052
  var ALIGNMENT_THRESHOLD_POOR = 0.6;
1053
1053
  var ALIGNMENT_THRESHOLD_GOOD = 0.6;
1054
1054
  var ALIGNMENT_THRESHOLD_PERFECT = 0.85;
@@ -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 { hasFace, alignment, tooClose, tooFar, isBlurry, isPartialFace } = state;
1236
- return hasFace && alignment >= ALIGNMENT_THRESHOLD_CAPTURE && !tooClose && !tooFar && !isBlurry && !isPartialFace;
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
@@ -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 = 2;
1314
- var MIN_CROP_MULTIPLIER = 1.8;
1315
- var MAX_CROP_MULTIPLIER = 2.5;
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.6;
1321
- var TARGET_FACE_PERCENTAGE_IN_CROP = 0.5;
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++) {
@@ -1414,9 +1453,9 @@ var OVAL_REGION_DESKTOP = {
1414
1453
  var OVAL_REGION_MOBILE = {
1415
1454
  centerX: 0.5,
1416
1455
  centerY: 0.5,
1417
- width: 0.55,
1418
- height: 0.73
1419
- // 0.55 * (4/3)
1456
+ width: 0.48,
1457
+ height: 0.64
1458
+ // 0.48 * (4/3)
1420
1459
  };
1421
1460
  var DEFAULT_OVAL_REGION = OVAL_REGION_DESKTOP;
1422
1461
  function isFaceInOval(faceBox, frameWidth, frameHeight, oval = DEFAULT_OVAL_REGION, tolerance = 0.3) {
@@ -1572,6 +1611,33 @@ function checkFrameQuality(options) {
1572
1611
  }
1573
1612
  return result;
1574
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
+ }
1575
1641
  var BaseFrameCollector = class {
1576
1642
  constructor(maxFrames = 10) {
1577
1643
  this.frames = [];
@@ -1831,12 +1897,15 @@ export {
1831
1897
  MAX_CROP_MULTIPLIER,
1832
1898
  MAX_FACE_PERCENTAGE_IN_CROP,
1833
1899
  MAX_FACE_RATIO,
1900
+ MAX_FACE_ROLL_DEGREES,
1901
+ MAX_IDEAL_FACE_RATIO,
1834
1902
  MIN_CAPTURE_ALIGNMENT,
1835
1903
  MIN_CROP_MULTIPLIER,
1836
1904
  MIN_FACE_BOTTOM_MARGIN,
1837
1905
  MIN_FACE_RATIO,
1838
1906
  MIN_FACE_SIDE_MARGIN,
1839
1907
  MIN_FACE_TOP_MARGIN,
1908
+ MIN_IDEAL_FACE_RATIO,
1840
1909
  MIN_LANDMARK_COUNT,
1841
1910
  MODEL_CONFIGS,
1842
1911
  OVAL_GUIDE_COLORS,
@@ -1857,6 +1926,7 @@ export {
1857
1926
  checkEyeRegionQuality,
1858
1927
  checkFrameQuality,
1859
1928
  decodeBase64,
1929
+ detectFaceRoll,
1860
1930
  detectSpecularHighlights,
1861
1931
  encodeBase64,
1862
1932
  generateSessionId,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@moveris/shared",
3
- "version": "2.5.0",
3
+ "version": "2.6.0",
4
4
  "description": "Core business logic for Moveris Live SDK",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",