@moveris/shared 2.2.0 → 2.3.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 +62 -0
- package/dist/index.d.mts +42 -1
- package/dist/index.d.ts +42 -1
- package/dist/index.js +173 -0
- package/dist/index.mjs +166 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -426,6 +426,11 @@ const status = getStatusMessage('capturing', DEFAULT_LOCALE);
|
|
|
426
426
|
| `processing` | "Processing..." | API verification in progress |
|
|
427
427
|
| `success` | "Verification complete" | Successful completion |
|
|
428
428
|
| `failed` | "Verification failed" | Failed verification |
|
|
429
|
+
| `eyes_not_visible` | "Eyes not clearly visible" | Eye region featureless |
|
|
430
|
+
| `eyes_shadowed` | "Eyes are in shadow…" | Eye region too dark |
|
|
431
|
+
| `eyes_overexposed` | "Eye region overexposed…" | Eye region too bright |
|
|
432
|
+
| `glasses_glare` | "Glare detected…" | Specular highlights on eyes |
|
|
433
|
+
| `eye_quality_poor` | "Eye region quality is poor" | Generic eye quality failure |
|
|
429
434
|
|
|
430
435
|
---
|
|
431
436
|
|
|
@@ -452,6 +457,59 @@ const color = OVAL_GUIDE_COLORS[state];
|
|
|
452
457
|
|
|
453
458
|
---
|
|
454
459
|
|
|
460
|
+
### Eye Region Quality Analysis
|
|
461
|
+
|
|
462
|
+
Pre-request eye-region quality gate. Platform-agnostic functions that analyze RGBA pixel data from eye regions to detect shadows, glare, occlusion, and poor visibility.
|
|
463
|
+
|
|
464
|
+
```typescript
|
|
465
|
+
import {
|
|
466
|
+
checkEyeRegionQuality,
|
|
467
|
+
analyzeEyeRegionBrightness,
|
|
468
|
+
analyzeEyeRegionContrast,
|
|
469
|
+
detectSpecularHighlights,
|
|
470
|
+
EYE_QUALITY_THRESHOLDS,
|
|
471
|
+
} from '@moveris/shared';
|
|
472
|
+
|
|
473
|
+
// Combined quality check (recommended)
|
|
474
|
+
const quality = checkEyeRegionQuality(eyeRegionPixels);
|
|
475
|
+
console.log(quality.passed); // boolean
|
|
476
|
+
console.log(quality.brightness); // 0-255
|
|
477
|
+
console.log(quality.contrast); // standard deviation of luminance
|
|
478
|
+
console.log(quality.hasGlare); // boolean
|
|
479
|
+
console.log(quality.glareRatio); // 0-1
|
|
480
|
+
console.log(quality.message); // user-facing feedback or null
|
|
481
|
+
|
|
482
|
+
// Individual checks
|
|
483
|
+
const brightness = analyzeEyeRegionBrightness(pixels);
|
|
484
|
+
const contrast = analyzeEyeRegionContrast(pixels);
|
|
485
|
+
const glareRatio = detectSpecularHighlights(pixels);
|
|
486
|
+
```
|
|
487
|
+
|
|
488
|
+
| Check | Threshold | Condition |
|
|
489
|
+
| ----------- | --------------------- | --------------------------------------- |
|
|
490
|
+
| Shadowed | `minBrightness: 40` | Average luminance too low |
|
|
491
|
+
| Overexposed | `maxBrightness: 230` | Average luminance too high |
|
|
492
|
+
| Glare | `maxGlareRatio: 0.15` | >15% of pixels are specular highlights |
|
|
493
|
+
| Occluded | `minContrast: 12` | Standard deviation of luminance too low |
|
|
494
|
+
|
|
495
|
+
Custom thresholds can be passed as a second argument to `checkEyeRegionQuality()`.
|
|
496
|
+
|
|
497
|
+
### Eye Region Landmarks
|
|
498
|
+
|
|
499
|
+
Extract bounding boxes for left and right eye regions from MediaPipe 468-point face mesh landmarks.
|
|
500
|
+
|
|
501
|
+
```typescript
|
|
502
|
+
import { getEyeRegionBounds, EYE_LANDMARK_INDICES, validateFaceLandmarks } from '@moveris/shared';
|
|
503
|
+
|
|
504
|
+
const bounds = getEyeRegionBounds(landmarks);
|
|
505
|
+
if (bounds) {
|
|
506
|
+
console.log(bounds.leftEye); // { x, y, width, height } (normalized 0-1)
|
|
507
|
+
console.log(bounds.rightEye); // { x, y, width, height } (normalized 0-1)
|
|
508
|
+
}
|
|
509
|
+
```
|
|
510
|
+
|
|
511
|
+
---
|
|
512
|
+
|
|
455
513
|
### Frame Analysis Utilities
|
|
456
514
|
|
|
457
515
|
Utilities for assessing frame quality before submission.
|
|
@@ -527,6 +585,10 @@ import type {
|
|
|
527
585
|
HeadPose,
|
|
528
586
|
FaceLandmarkPoint,
|
|
529
587
|
LandmarkValidationResult,
|
|
588
|
+
EyeRegionQuality,
|
|
589
|
+
EyeQualityThresholds,
|
|
590
|
+
EyeRegionBounds,
|
|
591
|
+
EyeRegionsBounds,
|
|
530
592
|
} from '@moveris/shared';
|
|
531
593
|
```
|
|
532
594
|
|
package/dist/index.d.mts
CHANGED
|
@@ -322,6 +322,21 @@ declare const LANDMARK_INDEX: {
|
|
|
322
322
|
readonly UPPER_LIP: 13;
|
|
323
323
|
readonly LOWER_LIP: 14;
|
|
324
324
|
};
|
|
325
|
+
declare const EYE_LANDMARK_INDICES: {
|
|
326
|
+
readonly RIGHT_EYE: readonly [33, 7, 163, 144, 145, 153, 154, 155, 133, 173, 157, 158, 159, 160, 161, 246];
|
|
327
|
+
readonly LEFT_EYE: readonly [362, 382, 381, 380, 374, 373, 390, 249, 263, 466, 388, 387, 386, 385, 384, 398];
|
|
328
|
+
};
|
|
329
|
+
interface EyeRegionBounds {
|
|
330
|
+
x: number;
|
|
331
|
+
y: number;
|
|
332
|
+
width: number;
|
|
333
|
+
height: number;
|
|
334
|
+
}
|
|
335
|
+
interface EyeRegionsBounds {
|
|
336
|
+
leftEye: EyeRegionBounds;
|
|
337
|
+
rightEye: EyeRegionBounds;
|
|
338
|
+
}
|
|
339
|
+
declare function getEyeRegionBounds(landmarks: FaceLandmarkPoint[]): EyeRegionsBounds | null;
|
|
325
340
|
declare const LANDMARK_MIN_BOUND = 0.1;
|
|
326
341
|
declare const LANDMARK_MAX_BOUND = 0.9;
|
|
327
342
|
declare const MIN_LANDMARK_COUNT = 15;
|
|
@@ -655,6 +670,11 @@ declare const FEEDBACK_MESSAGES: {
|
|
|
655
670
|
readonly too_dark: "Low lighting - move to a brighter area";
|
|
656
671
|
readonly backlit: "Backlit - try facing the light source";
|
|
657
672
|
readonly hand_detected: "Remove hand from face";
|
|
673
|
+
readonly eyes_not_visible: "Eyes not clearly visible";
|
|
674
|
+
readonly eyes_shadowed: "Eyes are in shadow - improve lighting";
|
|
675
|
+
readonly eyes_overexposed: "Eye region overexposed - reduce lighting";
|
|
676
|
+
readonly glasses_glare: "Glare detected - adjust angle or remove glasses";
|
|
677
|
+
readonly eye_quality_poor: "Eye region quality is poor";
|
|
658
678
|
readonly capturing: "Capturing...";
|
|
659
679
|
readonly almost_done: "Almost done...";
|
|
660
680
|
readonly verification_complete: "Verification complete";
|
|
@@ -708,4 +728,25 @@ interface RetryOptions {
|
|
|
708
728
|
declare function retryWithBackoff<T>(fn: () => Promise<T>, options?: RetryOptions): Promise<T>;
|
|
709
729
|
declare function sleep(ms: number): Promise<void>;
|
|
710
730
|
|
|
711
|
-
|
|
731
|
+
interface EyeRegionQuality {
|
|
732
|
+
passed: boolean;
|
|
733
|
+
brightness: number;
|
|
734
|
+
contrast: number;
|
|
735
|
+
hasGlare: boolean;
|
|
736
|
+
glareRatio: number;
|
|
737
|
+
message: string | null;
|
|
738
|
+
}
|
|
739
|
+
interface EyeQualityThresholds {
|
|
740
|
+
minBrightness: number;
|
|
741
|
+
maxBrightness: number;
|
|
742
|
+
minContrast: number;
|
|
743
|
+
glarePixelThreshold: number;
|
|
744
|
+
maxGlareRatio: number;
|
|
745
|
+
}
|
|
746
|
+
declare const EYE_QUALITY_THRESHOLDS: EyeQualityThresholds;
|
|
747
|
+
declare function analyzeEyeRegionBrightness(pixels: Uint8Array | Uint8ClampedArray): number;
|
|
748
|
+
declare function analyzeEyeRegionContrast(pixels: Uint8Array | Uint8ClampedArray, meanBrightness?: number): number;
|
|
749
|
+
declare function detectSpecularHighlights(pixels: Uint8Array | Uint8ClampedArray, threshold?: number): number;
|
|
750
|
+
declare function checkEyeRegionQuality(pixels: Uint8Array | Uint8ClampedArray, thresholds?: EyeQualityThresholds): EyeRegionQuality;
|
|
751
|
+
|
|
752
|
+
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 ModelType, 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, getApiErrorMessage, getCaptureQualityFeedback, getEyeRegionBounds, getFeedbackMessage, getMinFramesForModel, getOvalGuideState, getStatusMessage, hasEnoughFrames, 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
|
@@ -322,6 +322,21 @@ declare const LANDMARK_INDEX: {
|
|
|
322
322
|
readonly UPPER_LIP: 13;
|
|
323
323
|
readonly LOWER_LIP: 14;
|
|
324
324
|
};
|
|
325
|
+
declare const EYE_LANDMARK_INDICES: {
|
|
326
|
+
readonly RIGHT_EYE: readonly [33, 7, 163, 144, 145, 153, 154, 155, 133, 173, 157, 158, 159, 160, 161, 246];
|
|
327
|
+
readonly LEFT_EYE: readonly [362, 382, 381, 380, 374, 373, 390, 249, 263, 466, 388, 387, 386, 385, 384, 398];
|
|
328
|
+
};
|
|
329
|
+
interface EyeRegionBounds {
|
|
330
|
+
x: number;
|
|
331
|
+
y: number;
|
|
332
|
+
width: number;
|
|
333
|
+
height: number;
|
|
334
|
+
}
|
|
335
|
+
interface EyeRegionsBounds {
|
|
336
|
+
leftEye: EyeRegionBounds;
|
|
337
|
+
rightEye: EyeRegionBounds;
|
|
338
|
+
}
|
|
339
|
+
declare function getEyeRegionBounds(landmarks: FaceLandmarkPoint[]): EyeRegionsBounds | null;
|
|
325
340
|
declare const LANDMARK_MIN_BOUND = 0.1;
|
|
326
341
|
declare const LANDMARK_MAX_BOUND = 0.9;
|
|
327
342
|
declare const MIN_LANDMARK_COUNT = 15;
|
|
@@ -655,6 +670,11 @@ declare const FEEDBACK_MESSAGES: {
|
|
|
655
670
|
readonly too_dark: "Low lighting - move to a brighter area";
|
|
656
671
|
readonly backlit: "Backlit - try facing the light source";
|
|
657
672
|
readonly hand_detected: "Remove hand from face";
|
|
673
|
+
readonly eyes_not_visible: "Eyes not clearly visible";
|
|
674
|
+
readonly eyes_shadowed: "Eyes are in shadow - improve lighting";
|
|
675
|
+
readonly eyes_overexposed: "Eye region overexposed - reduce lighting";
|
|
676
|
+
readonly glasses_glare: "Glare detected - adjust angle or remove glasses";
|
|
677
|
+
readonly eye_quality_poor: "Eye region quality is poor";
|
|
658
678
|
readonly capturing: "Capturing...";
|
|
659
679
|
readonly almost_done: "Almost done...";
|
|
660
680
|
readonly verification_complete: "Verification complete";
|
|
@@ -708,4 +728,25 @@ interface RetryOptions {
|
|
|
708
728
|
declare function retryWithBackoff<T>(fn: () => Promise<T>, options?: RetryOptions): Promise<T>;
|
|
709
729
|
declare function sleep(ms: number): Promise<void>;
|
|
710
730
|
|
|
711
|
-
|
|
731
|
+
interface EyeRegionQuality {
|
|
732
|
+
passed: boolean;
|
|
733
|
+
brightness: number;
|
|
734
|
+
contrast: number;
|
|
735
|
+
hasGlare: boolean;
|
|
736
|
+
glareRatio: number;
|
|
737
|
+
message: string | null;
|
|
738
|
+
}
|
|
739
|
+
interface EyeQualityThresholds {
|
|
740
|
+
minBrightness: number;
|
|
741
|
+
maxBrightness: number;
|
|
742
|
+
minContrast: number;
|
|
743
|
+
glarePixelThreshold: number;
|
|
744
|
+
maxGlareRatio: number;
|
|
745
|
+
}
|
|
746
|
+
declare const EYE_QUALITY_THRESHOLDS: EyeQualityThresholds;
|
|
747
|
+
declare function analyzeEyeRegionBrightness(pixels: Uint8Array | Uint8ClampedArray): number;
|
|
748
|
+
declare function analyzeEyeRegionContrast(pixels: Uint8Array | Uint8ClampedArray, meanBrightness?: number): number;
|
|
749
|
+
declare function detectSpecularHighlights(pixels: Uint8Array | Uint8ClampedArray, threshold?: number): number;
|
|
750
|
+
declare function checkEyeRegionQuality(pixels: Uint8Array | Uint8ClampedArray, thresholds?: EyeQualityThresholds): EyeRegionQuality;
|
|
751
|
+
|
|
752
|
+
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 ModelType, 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, getApiErrorMessage, getCaptureQualityFeedback, getEyeRegionBounds, getFeedbackMessage, getMinFramesForModel, getOvalGuideState, getStatusMessage, hasEnoughFrames, isFaceCropFullyInFrame, isFaceFullyVisible, isFaceInOval, isRetryableError, retryWithBackoff, rgbaToGrayscale, sleep, toFrameData, toHybridFrameData, toLivenessResult, toLivenessResultFromStream, validateApiKey, validateFaceLandmarks, validateFrameCount, validateFrameData, validateFrameIndex, validateTimestamp, validateUUID, validateUrl };
|
package/dist/index.js
CHANGED
|
@@ -44,6 +44,8 @@ __export(index_exports, {
|
|
|
44
44
|
ERROR_MESSAGES: () => ERROR_MESSAGES,
|
|
45
45
|
ERROR_MESSAGES_ES: () => ERROR_MESSAGES_ES,
|
|
46
46
|
ES_LOCALE: () => ES_LOCALE,
|
|
47
|
+
EYE_LANDMARK_INDICES: () => EYE_LANDMARK_INDICES,
|
|
48
|
+
EYE_QUALITY_THRESHOLDS: () => EYE_QUALITY_THRESHOLDS,
|
|
47
49
|
FACE_CENTER_VERTICAL_OFFSET: () => FACE_CENTER_VERTICAL_OFFSET,
|
|
48
50
|
FACE_CROP_OUTPUT_SIZE: () => FACE_CROP_OUTPUT_SIZE,
|
|
49
51
|
FEEDBACK_MESSAGES: () => FEEDBACK_MESSAGES,
|
|
@@ -77,18 +79,23 @@ __export(index_exports, {
|
|
|
77
79
|
RETRY_CONFIG: () => RETRY_CONFIG,
|
|
78
80
|
TARGET_FACE_PERCENTAGE_IN_CROP: () => TARGET_FACE_PERCENTAGE_IN_CROP,
|
|
79
81
|
analyzeBlur: () => analyzeBlur,
|
|
82
|
+
analyzeEyeRegionBrightness: () => analyzeEyeRegionBrightness,
|
|
83
|
+
analyzeEyeRegionContrast: () => analyzeEyeRegionContrast,
|
|
80
84
|
analyzeLighting: () => analyzeLighting,
|
|
81
85
|
calculateAdaptiveCropMultiplier: () => calculateAdaptiveCropMultiplier,
|
|
82
86
|
calculateBrightness: () => calculateBrightness,
|
|
83
87
|
calculateFaceAlignment: () => calculateFaceAlignment,
|
|
84
88
|
calculateFaceCropRegion: () => calculateFaceCropRegion,
|
|
85
89
|
canCaptureFrame: () => canCaptureFrame,
|
|
90
|
+
checkEyeRegionQuality: () => checkEyeRegionQuality,
|
|
86
91
|
checkFrameQuality: () => checkFrameQuality,
|
|
87
92
|
decodeBase64: () => decodeBase64,
|
|
93
|
+
detectSpecularHighlights: () => detectSpecularHighlights,
|
|
88
94
|
encodeBase64: () => encodeBase64,
|
|
89
95
|
generateSessionId: () => generateSessionId,
|
|
90
96
|
getApiErrorMessage: () => getApiErrorMessage,
|
|
91
97
|
getCaptureQualityFeedback: () => getCaptureQualityFeedback,
|
|
98
|
+
getEyeRegionBounds: () => getEyeRegionBounds,
|
|
92
99
|
getFeedbackMessage: () => getFeedbackMessage,
|
|
93
100
|
getMinFramesForModel: () => getMinFramesForModel,
|
|
94
101
|
getOvalGuideState: () => getOvalGuideState,
|
|
@@ -1161,6 +1168,12 @@ var FEEDBACK_MESSAGES = {
|
|
|
1161
1168
|
backlit: "Backlit - try facing the light source",
|
|
1162
1169
|
// Hand occlusion
|
|
1163
1170
|
hand_detected: "Remove hand from face",
|
|
1171
|
+
// Eye region quality
|
|
1172
|
+
eyes_not_visible: "Eyes not clearly visible",
|
|
1173
|
+
eyes_shadowed: "Eyes are in shadow - improve lighting",
|
|
1174
|
+
eyes_overexposed: "Eye region overexposed - reduce lighting",
|
|
1175
|
+
glasses_glare: "Glare detected - adjust angle or remove glasses",
|
|
1176
|
+
eye_quality_poor: "Eye region quality is poor",
|
|
1164
1177
|
// Progress messages
|
|
1165
1178
|
capturing: "Capturing...",
|
|
1166
1179
|
almost_done: "Almost done...",
|
|
@@ -1212,6 +1225,12 @@ var ES_LOCALE = {
|
|
|
1212
1225
|
backlit: "Contraluz - intenta mirar hacia la fuente de luz",
|
|
1213
1226
|
// Hand occlusion
|
|
1214
1227
|
hand_detected: "Retira la mano del rostro",
|
|
1228
|
+
// Eye region quality
|
|
1229
|
+
eyes_not_visible: "Ojos no visibles claramente",
|
|
1230
|
+
eyes_shadowed: "Ojos en sombra - mejora la iluminaci\xF3n",
|
|
1231
|
+
eyes_overexposed: "Regi\xF3n de ojos sobreexpuesta - reduce la iluminaci\xF3n",
|
|
1232
|
+
glasses_glare: "Reflejo detectado - ajusta el \xE1ngulo o retira los lentes",
|
|
1233
|
+
eye_quality_poor: "Calidad de la regi\xF3n de ojos insuficiente",
|
|
1215
1234
|
// Progress messages
|
|
1216
1235
|
capturing: "Capturando...",
|
|
1217
1236
|
almost_done: "Casi listo...",
|
|
@@ -1645,6 +1664,44 @@ var LANDMARK_INDEX = {
|
|
|
1645
1664
|
/** Lower lip center */
|
|
1646
1665
|
LOWER_LIP: 14
|
|
1647
1666
|
};
|
|
1667
|
+
var EYE_LANDMARK_INDICES = {
|
|
1668
|
+
/** Right eye contour (viewer's left) */
|
|
1669
|
+
RIGHT_EYE: [33, 7, 163, 144, 145, 153, 154, 155, 133, 173, 157, 158, 159, 160, 161, 246],
|
|
1670
|
+
/** Left eye contour (viewer's right) */
|
|
1671
|
+
LEFT_EYE: [362, 382, 381, 380, 374, 373, 390, 249, 263, 466, 388, 387, 386, 385, 384, 398]
|
|
1672
|
+
};
|
|
1673
|
+
function getEyeRegionBounds(landmarks) {
|
|
1674
|
+
if (landmarks.length < 468) return null;
|
|
1675
|
+
const PADDING = 0.2;
|
|
1676
|
+
function computeBounds(indices) {
|
|
1677
|
+
let minX = 1;
|
|
1678
|
+
let minY = 1;
|
|
1679
|
+
let maxX = 0;
|
|
1680
|
+
let maxY = 0;
|
|
1681
|
+
for (const idx of indices) {
|
|
1682
|
+
const lm = landmarks[idx];
|
|
1683
|
+
if (!lm) continue;
|
|
1684
|
+
if (lm.x < minX) minX = lm.x;
|
|
1685
|
+
if (lm.y < minY) minY = lm.y;
|
|
1686
|
+
if (lm.x > maxX) maxX = lm.x;
|
|
1687
|
+
if (lm.y > maxY) maxY = lm.y;
|
|
1688
|
+
}
|
|
1689
|
+
const width = maxX - minX;
|
|
1690
|
+
const height = maxY - minY;
|
|
1691
|
+
const padX = width * PADDING;
|
|
1692
|
+
const padY = height * PADDING;
|
|
1693
|
+
return {
|
|
1694
|
+
x: Math.max(0, minX - padX),
|
|
1695
|
+
y: Math.max(0, minY - padY),
|
|
1696
|
+
width: Math.min(1 - Math.max(0, minX - padX), width + padX * 2),
|
|
1697
|
+
height: Math.min(1 - Math.max(0, minY - padY), height + padY * 2)
|
|
1698
|
+
};
|
|
1699
|
+
}
|
|
1700
|
+
return {
|
|
1701
|
+
rightEye: computeBounds(EYE_LANDMARK_INDICES.RIGHT_EYE),
|
|
1702
|
+
leftEye: computeBounds(EYE_LANDMARK_INDICES.LEFT_EYE)
|
|
1703
|
+
};
|
|
1704
|
+
}
|
|
1648
1705
|
var LANDMARK_MIN_BOUND = 0.1;
|
|
1649
1706
|
var LANDMARK_MAX_BOUND = 0.9;
|
|
1650
1707
|
var MIN_LANDMARK_COUNT = 15;
|
|
@@ -1666,6 +1723,115 @@ function validateFaceLandmarks(landmarks) {
|
|
|
1666
1723
|
}
|
|
1667
1724
|
return { valid: true };
|
|
1668
1725
|
}
|
|
1726
|
+
|
|
1727
|
+
// src/utils/eyeRegionAnalysis.ts
|
|
1728
|
+
var EYE_QUALITY_THRESHOLDS = {
|
|
1729
|
+
minBrightness: 40,
|
|
1730
|
+
maxBrightness: 230,
|
|
1731
|
+
minContrast: 12,
|
|
1732
|
+
glarePixelThreshold: 240,
|
|
1733
|
+
maxGlareRatio: 0.15
|
|
1734
|
+
};
|
|
1735
|
+
function rgbaToLuminance(r, g, b) {
|
|
1736
|
+
return 0.299 * r + 0.587 * g + 0.114 * b;
|
|
1737
|
+
}
|
|
1738
|
+
function analyzeEyeRegionBrightness(pixels) {
|
|
1739
|
+
const pixelCount = pixels.length / 4;
|
|
1740
|
+
if (pixelCount === 0) return 0;
|
|
1741
|
+
let totalLuminance = 0;
|
|
1742
|
+
for (let i = 0; i < pixels.length; i += 4) {
|
|
1743
|
+
totalLuminance += rgbaToLuminance(pixels[i], pixels[i + 1], pixels[i + 2]);
|
|
1744
|
+
}
|
|
1745
|
+
return totalLuminance / pixelCount;
|
|
1746
|
+
}
|
|
1747
|
+
function analyzeEyeRegionContrast(pixels, meanBrightness) {
|
|
1748
|
+
const pixelCount = pixels.length / 4;
|
|
1749
|
+
if (pixelCount === 0) return 0;
|
|
1750
|
+
const mean = meanBrightness ?? analyzeEyeRegionBrightness(pixels);
|
|
1751
|
+
let sumSqDiff = 0;
|
|
1752
|
+
for (let i = 0; i < pixels.length; i += 4) {
|
|
1753
|
+
const lum = rgbaToLuminance(pixels[i], pixels[i + 1], pixels[i + 2]);
|
|
1754
|
+
const diff = lum - mean;
|
|
1755
|
+
sumSqDiff += diff * diff;
|
|
1756
|
+
}
|
|
1757
|
+
return Math.sqrt(sumSqDiff / pixelCount);
|
|
1758
|
+
}
|
|
1759
|
+
function detectSpecularHighlights(pixels, threshold = EYE_QUALITY_THRESHOLDS.glarePixelThreshold) {
|
|
1760
|
+
const pixelCount = pixels.length / 4;
|
|
1761
|
+
if (pixelCount === 0) return 0;
|
|
1762
|
+
let glareCount = 0;
|
|
1763
|
+
for (let i = 0; i < pixels.length; i += 4) {
|
|
1764
|
+
const lum = rgbaToLuminance(pixels[i], pixels[i + 1], pixels[i + 2]);
|
|
1765
|
+
if (lum >= threshold) {
|
|
1766
|
+
glareCount++;
|
|
1767
|
+
}
|
|
1768
|
+
}
|
|
1769
|
+
return glareCount / pixelCount;
|
|
1770
|
+
}
|
|
1771
|
+
function checkEyeRegionQuality(pixels, thresholds = EYE_QUALITY_THRESHOLDS) {
|
|
1772
|
+
if (pixels.length < 4) {
|
|
1773
|
+
return {
|
|
1774
|
+
passed: false,
|
|
1775
|
+
brightness: 0,
|
|
1776
|
+
contrast: 0,
|
|
1777
|
+
hasGlare: false,
|
|
1778
|
+
glareRatio: 0,
|
|
1779
|
+
message: "Eyes not clearly visible"
|
|
1780
|
+
};
|
|
1781
|
+
}
|
|
1782
|
+
const brightness = analyzeEyeRegionBrightness(pixels);
|
|
1783
|
+
const contrast = analyzeEyeRegionContrast(pixels, brightness);
|
|
1784
|
+
const glareRatio = detectSpecularHighlights(pixels, thresholds.glarePixelThreshold);
|
|
1785
|
+
const hasGlare = glareRatio > thresholds.maxGlareRatio;
|
|
1786
|
+
if (brightness < thresholds.minBrightness) {
|
|
1787
|
+
return {
|
|
1788
|
+
passed: false,
|
|
1789
|
+
brightness,
|
|
1790
|
+
contrast,
|
|
1791
|
+
hasGlare,
|
|
1792
|
+
glareRatio,
|
|
1793
|
+
message: "Eyes are in shadow - improve lighting"
|
|
1794
|
+
};
|
|
1795
|
+
}
|
|
1796
|
+
if (brightness > thresholds.maxBrightness) {
|
|
1797
|
+
return {
|
|
1798
|
+
passed: false,
|
|
1799
|
+
brightness,
|
|
1800
|
+
contrast,
|
|
1801
|
+
hasGlare,
|
|
1802
|
+
glareRatio,
|
|
1803
|
+
message: "Eye region overexposed - reduce lighting"
|
|
1804
|
+
};
|
|
1805
|
+
}
|
|
1806
|
+
if (hasGlare) {
|
|
1807
|
+
return {
|
|
1808
|
+
passed: false,
|
|
1809
|
+
brightness,
|
|
1810
|
+
contrast,
|
|
1811
|
+
hasGlare,
|
|
1812
|
+
glareRatio,
|
|
1813
|
+
message: "Glare detected - adjust angle or remove glasses"
|
|
1814
|
+
};
|
|
1815
|
+
}
|
|
1816
|
+
if (contrast < thresholds.minContrast) {
|
|
1817
|
+
return {
|
|
1818
|
+
passed: false,
|
|
1819
|
+
brightness,
|
|
1820
|
+
contrast,
|
|
1821
|
+
hasGlare,
|
|
1822
|
+
glareRatio,
|
|
1823
|
+
message: "Eyes not clearly visible"
|
|
1824
|
+
};
|
|
1825
|
+
}
|
|
1826
|
+
return {
|
|
1827
|
+
passed: true,
|
|
1828
|
+
brightness,
|
|
1829
|
+
contrast,
|
|
1830
|
+
hasGlare,
|
|
1831
|
+
glareRatio,
|
|
1832
|
+
message: null
|
|
1833
|
+
};
|
|
1834
|
+
}
|
|
1669
1835
|
// Annotate the CommonJS export names for ESM import in node:
|
|
1670
1836
|
0 && (module.exports = {
|
|
1671
1837
|
ALIGNMENT_THRESHOLD_CAPTURE,
|
|
@@ -1692,6 +1858,8 @@ function validateFaceLandmarks(landmarks) {
|
|
|
1692
1858
|
ERROR_MESSAGES,
|
|
1693
1859
|
ERROR_MESSAGES_ES,
|
|
1694
1860
|
ES_LOCALE,
|
|
1861
|
+
EYE_LANDMARK_INDICES,
|
|
1862
|
+
EYE_QUALITY_THRESHOLDS,
|
|
1695
1863
|
FACE_CENTER_VERTICAL_OFFSET,
|
|
1696
1864
|
FACE_CROP_OUTPUT_SIZE,
|
|
1697
1865
|
FEEDBACK_MESSAGES,
|
|
@@ -1725,18 +1893,23 @@ function validateFaceLandmarks(landmarks) {
|
|
|
1725
1893
|
RETRY_CONFIG,
|
|
1726
1894
|
TARGET_FACE_PERCENTAGE_IN_CROP,
|
|
1727
1895
|
analyzeBlur,
|
|
1896
|
+
analyzeEyeRegionBrightness,
|
|
1897
|
+
analyzeEyeRegionContrast,
|
|
1728
1898
|
analyzeLighting,
|
|
1729
1899
|
calculateAdaptiveCropMultiplier,
|
|
1730
1900
|
calculateBrightness,
|
|
1731
1901
|
calculateFaceAlignment,
|
|
1732
1902
|
calculateFaceCropRegion,
|
|
1733
1903
|
canCaptureFrame,
|
|
1904
|
+
checkEyeRegionQuality,
|
|
1734
1905
|
checkFrameQuality,
|
|
1735
1906
|
decodeBase64,
|
|
1907
|
+
detectSpecularHighlights,
|
|
1736
1908
|
encodeBase64,
|
|
1737
1909
|
generateSessionId,
|
|
1738
1910
|
getApiErrorMessage,
|
|
1739
1911
|
getCaptureQualityFeedback,
|
|
1912
|
+
getEyeRegionBounds,
|
|
1740
1913
|
getFeedbackMessage,
|
|
1741
1914
|
getMinFramesForModel,
|
|
1742
1915
|
getOvalGuideState,
|
package/dist/index.mjs
CHANGED
|
@@ -1043,6 +1043,12 @@ var FEEDBACK_MESSAGES = {
|
|
|
1043
1043
|
backlit: "Backlit - try facing the light source",
|
|
1044
1044
|
// Hand occlusion
|
|
1045
1045
|
hand_detected: "Remove hand from face",
|
|
1046
|
+
// Eye region quality
|
|
1047
|
+
eyes_not_visible: "Eyes not clearly visible",
|
|
1048
|
+
eyes_shadowed: "Eyes are in shadow - improve lighting",
|
|
1049
|
+
eyes_overexposed: "Eye region overexposed - reduce lighting",
|
|
1050
|
+
glasses_glare: "Glare detected - adjust angle or remove glasses",
|
|
1051
|
+
eye_quality_poor: "Eye region quality is poor",
|
|
1046
1052
|
// Progress messages
|
|
1047
1053
|
capturing: "Capturing...",
|
|
1048
1054
|
almost_done: "Almost done...",
|
|
@@ -1094,6 +1100,12 @@ var ES_LOCALE = {
|
|
|
1094
1100
|
backlit: "Contraluz - intenta mirar hacia la fuente de luz",
|
|
1095
1101
|
// Hand occlusion
|
|
1096
1102
|
hand_detected: "Retira la mano del rostro",
|
|
1103
|
+
// Eye region quality
|
|
1104
|
+
eyes_not_visible: "Ojos no visibles claramente",
|
|
1105
|
+
eyes_shadowed: "Ojos en sombra - mejora la iluminaci\xF3n",
|
|
1106
|
+
eyes_overexposed: "Regi\xF3n de ojos sobreexpuesta - reduce la iluminaci\xF3n",
|
|
1107
|
+
glasses_glare: "Reflejo detectado - ajusta el \xE1ngulo o retira los lentes",
|
|
1108
|
+
eye_quality_poor: "Calidad de la regi\xF3n de ojos insuficiente",
|
|
1097
1109
|
// Progress messages
|
|
1098
1110
|
capturing: "Capturando...",
|
|
1099
1111
|
almost_done: "Casi listo...",
|
|
@@ -1527,6 +1539,44 @@ var LANDMARK_INDEX = {
|
|
|
1527
1539
|
/** Lower lip center */
|
|
1528
1540
|
LOWER_LIP: 14
|
|
1529
1541
|
};
|
|
1542
|
+
var EYE_LANDMARK_INDICES = {
|
|
1543
|
+
/** Right eye contour (viewer's left) */
|
|
1544
|
+
RIGHT_EYE: [33, 7, 163, 144, 145, 153, 154, 155, 133, 173, 157, 158, 159, 160, 161, 246],
|
|
1545
|
+
/** Left eye contour (viewer's right) */
|
|
1546
|
+
LEFT_EYE: [362, 382, 381, 380, 374, 373, 390, 249, 263, 466, 388, 387, 386, 385, 384, 398]
|
|
1547
|
+
};
|
|
1548
|
+
function getEyeRegionBounds(landmarks) {
|
|
1549
|
+
if (landmarks.length < 468) return null;
|
|
1550
|
+
const PADDING = 0.2;
|
|
1551
|
+
function computeBounds(indices) {
|
|
1552
|
+
let minX = 1;
|
|
1553
|
+
let minY = 1;
|
|
1554
|
+
let maxX = 0;
|
|
1555
|
+
let maxY = 0;
|
|
1556
|
+
for (const idx of indices) {
|
|
1557
|
+
const lm = landmarks[idx];
|
|
1558
|
+
if (!lm) continue;
|
|
1559
|
+
if (lm.x < minX) minX = lm.x;
|
|
1560
|
+
if (lm.y < minY) minY = lm.y;
|
|
1561
|
+
if (lm.x > maxX) maxX = lm.x;
|
|
1562
|
+
if (lm.y > maxY) maxY = lm.y;
|
|
1563
|
+
}
|
|
1564
|
+
const width = maxX - minX;
|
|
1565
|
+
const height = maxY - minY;
|
|
1566
|
+
const padX = width * PADDING;
|
|
1567
|
+
const padY = height * PADDING;
|
|
1568
|
+
return {
|
|
1569
|
+
x: Math.max(0, minX - padX),
|
|
1570
|
+
y: Math.max(0, minY - padY),
|
|
1571
|
+
width: Math.min(1 - Math.max(0, minX - padX), width + padX * 2),
|
|
1572
|
+
height: Math.min(1 - Math.max(0, minY - padY), height + padY * 2)
|
|
1573
|
+
};
|
|
1574
|
+
}
|
|
1575
|
+
return {
|
|
1576
|
+
rightEye: computeBounds(EYE_LANDMARK_INDICES.RIGHT_EYE),
|
|
1577
|
+
leftEye: computeBounds(EYE_LANDMARK_INDICES.LEFT_EYE)
|
|
1578
|
+
};
|
|
1579
|
+
}
|
|
1530
1580
|
var LANDMARK_MIN_BOUND = 0.1;
|
|
1531
1581
|
var LANDMARK_MAX_BOUND = 0.9;
|
|
1532
1582
|
var MIN_LANDMARK_COUNT = 15;
|
|
@@ -1548,6 +1598,115 @@ function validateFaceLandmarks(landmarks) {
|
|
|
1548
1598
|
}
|
|
1549
1599
|
return { valid: true };
|
|
1550
1600
|
}
|
|
1601
|
+
|
|
1602
|
+
// src/utils/eyeRegionAnalysis.ts
|
|
1603
|
+
var EYE_QUALITY_THRESHOLDS = {
|
|
1604
|
+
minBrightness: 40,
|
|
1605
|
+
maxBrightness: 230,
|
|
1606
|
+
minContrast: 12,
|
|
1607
|
+
glarePixelThreshold: 240,
|
|
1608
|
+
maxGlareRatio: 0.15
|
|
1609
|
+
};
|
|
1610
|
+
function rgbaToLuminance(r, g, b) {
|
|
1611
|
+
return 0.299 * r + 0.587 * g + 0.114 * b;
|
|
1612
|
+
}
|
|
1613
|
+
function analyzeEyeRegionBrightness(pixels) {
|
|
1614
|
+
const pixelCount = pixels.length / 4;
|
|
1615
|
+
if (pixelCount === 0) return 0;
|
|
1616
|
+
let totalLuminance = 0;
|
|
1617
|
+
for (let i = 0; i < pixels.length; i += 4) {
|
|
1618
|
+
totalLuminance += rgbaToLuminance(pixels[i], pixels[i + 1], pixels[i + 2]);
|
|
1619
|
+
}
|
|
1620
|
+
return totalLuminance / pixelCount;
|
|
1621
|
+
}
|
|
1622
|
+
function analyzeEyeRegionContrast(pixels, meanBrightness) {
|
|
1623
|
+
const pixelCount = pixels.length / 4;
|
|
1624
|
+
if (pixelCount === 0) return 0;
|
|
1625
|
+
const mean = meanBrightness ?? analyzeEyeRegionBrightness(pixels);
|
|
1626
|
+
let sumSqDiff = 0;
|
|
1627
|
+
for (let i = 0; i < pixels.length; i += 4) {
|
|
1628
|
+
const lum = rgbaToLuminance(pixels[i], pixels[i + 1], pixels[i + 2]);
|
|
1629
|
+
const diff = lum - mean;
|
|
1630
|
+
sumSqDiff += diff * diff;
|
|
1631
|
+
}
|
|
1632
|
+
return Math.sqrt(sumSqDiff / pixelCount);
|
|
1633
|
+
}
|
|
1634
|
+
function detectSpecularHighlights(pixels, threshold = EYE_QUALITY_THRESHOLDS.glarePixelThreshold) {
|
|
1635
|
+
const pixelCount = pixels.length / 4;
|
|
1636
|
+
if (pixelCount === 0) return 0;
|
|
1637
|
+
let glareCount = 0;
|
|
1638
|
+
for (let i = 0; i < pixels.length; i += 4) {
|
|
1639
|
+
const lum = rgbaToLuminance(pixels[i], pixels[i + 1], pixels[i + 2]);
|
|
1640
|
+
if (lum >= threshold) {
|
|
1641
|
+
glareCount++;
|
|
1642
|
+
}
|
|
1643
|
+
}
|
|
1644
|
+
return glareCount / pixelCount;
|
|
1645
|
+
}
|
|
1646
|
+
function checkEyeRegionQuality(pixels, thresholds = EYE_QUALITY_THRESHOLDS) {
|
|
1647
|
+
if (pixels.length < 4) {
|
|
1648
|
+
return {
|
|
1649
|
+
passed: false,
|
|
1650
|
+
brightness: 0,
|
|
1651
|
+
contrast: 0,
|
|
1652
|
+
hasGlare: false,
|
|
1653
|
+
glareRatio: 0,
|
|
1654
|
+
message: "Eyes not clearly visible"
|
|
1655
|
+
};
|
|
1656
|
+
}
|
|
1657
|
+
const brightness = analyzeEyeRegionBrightness(pixels);
|
|
1658
|
+
const contrast = analyzeEyeRegionContrast(pixels, brightness);
|
|
1659
|
+
const glareRatio = detectSpecularHighlights(pixels, thresholds.glarePixelThreshold);
|
|
1660
|
+
const hasGlare = glareRatio > thresholds.maxGlareRatio;
|
|
1661
|
+
if (brightness < thresholds.minBrightness) {
|
|
1662
|
+
return {
|
|
1663
|
+
passed: false,
|
|
1664
|
+
brightness,
|
|
1665
|
+
contrast,
|
|
1666
|
+
hasGlare,
|
|
1667
|
+
glareRatio,
|
|
1668
|
+
message: "Eyes are in shadow - improve lighting"
|
|
1669
|
+
};
|
|
1670
|
+
}
|
|
1671
|
+
if (brightness > thresholds.maxBrightness) {
|
|
1672
|
+
return {
|
|
1673
|
+
passed: false,
|
|
1674
|
+
brightness,
|
|
1675
|
+
contrast,
|
|
1676
|
+
hasGlare,
|
|
1677
|
+
glareRatio,
|
|
1678
|
+
message: "Eye region overexposed - reduce lighting"
|
|
1679
|
+
};
|
|
1680
|
+
}
|
|
1681
|
+
if (hasGlare) {
|
|
1682
|
+
return {
|
|
1683
|
+
passed: false,
|
|
1684
|
+
brightness,
|
|
1685
|
+
contrast,
|
|
1686
|
+
hasGlare,
|
|
1687
|
+
glareRatio,
|
|
1688
|
+
message: "Glare detected - adjust angle or remove glasses"
|
|
1689
|
+
};
|
|
1690
|
+
}
|
|
1691
|
+
if (contrast < thresholds.minContrast) {
|
|
1692
|
+
return {
|
|
1693
|
+
passed: false,
|
|
1694
|
+
brightness,
|
|
1695
|
+
contrast,
|
|
1696
|
+
hasGlare,
|
|
1697
|
+
glareRatio,
|
|
1698
|
+
message: "Eyes not clearly visible"
|
|
1699
|
+
};
|
|
1700
|
+
}
|
|
1701
|
+
return {
|
|
1702
|
+
passed: true,
|
|
1703
|
+
brightness,
|
|
1704
|
+
contrast,
|
|
1705
|
+
hasGlare,
|
|
1706
|
+
glareRatio,
|
|
1707
|
+
message: null
|
|
1708
|
+
};
|
|
1709
|
+
}
|
|
1551
1710
|
export {
|
|
1552
1711
|
ALIGNMENT_THRESHOLD_CAPTURE,
|
|
1553
1712
|
ALIGNMENT_THRESHOLD_GOOD,
|
|
@@ -1573,6 +1732,8 @@ export {
|
|
|
1573
1732
|
ERROR_MESSAGES,
|
|
1574
1733
|
ERROR_MESSAGES_ES,
|
|
1575
1734
|
ES_LOCALE,
|
|
1735
|
+
EYE_LANDMARK_INDICES,
|
|
1736
|
+
EYE_QUALITY_THRESHOLDS,
|
|
1576
1737
|
FACE_CENTER_VERTICAL_OFFSET,
|
|
1577
1738
|
FACE_CROP_OUTPUT_SIZE,
|
|
1578
1739
|
FEEDBACK_MESSAGES,
|
|
@@ -1606,18 +1767,23 @@ export {
|
|
|
1606
1767
|
RETRY_CONFIG,
|
|
1607
1768
|
TARGET_FACE_PERCENTAGE_IN_CROP,
|
|
1608
1769
|
analyzeBlur,
|
|
1770
|
+
analyzeEyeRegionBrightness,
|
|
1771
|
+
analyzeEyeRegionContrast,
|
|
1609
1772
|
analyzeLighting,
|
|
1610
1773
|
calculateAdaptiveCropMultiplier,
|
|
1611
1774
|
calculateBrightness,
|
|
1612
1775
|
calculateFaceAlignment,
|
|
1613
1776
|
calculateFaceCropRegion,
|
|
1614
1777
|
canCaptureFrame,
|
|
1778
|
+
checkEyeRegionQuality,
|
|
1615
1779
|
checkFrameQuality,
|
|
1616
1780
|
decodeBase64,
|
|
1781
|
+
detectSpecularHighlights,
|
|
1617
1782
|
encodeBase64,
|
|
1618
1783
|
generateSessionId,
|
|
1619
1784
|
getApiErrorMessage,
|
|
1620
1785
|
getCaptureQualityFeedback,
|
|
1786
|
+
getEyeRegionBounds,
|
|
1621
1787
|
getFeedbackMessage,
|
|
1622
1788
|
getMinFramesForModel,
|
|
1623
1789
|
getOvalGuideState,
|