@jaak.ai/stamps 2.0.0-dev.40 → 2.0.0-dev.42
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 +4 -1
- package/dist/cjs/jaak-stamps-webcomponent.cjs.js +1 -1
- package/dist/cjs/jaak-stamps.cjs.entry.js +868 -11
- package/dist/cjs/jaak-stamps.cjs.entry.js.map +1 -1
- package/dist/cjs/jaak-stamps.entry.cjs.js.map +1 -1
- package/dist/cjs/loader.cjs.js +1 -1
- package/dist/collection/components/my-component/my-component.css +66 -0
- package/dist/collection/components/my-component/my-component.js +494 -8
- package/dist/collection/components/my-component/my-component.js.map +1 -1
- package/dist/collection/services/CameraService.js +161 -0
- package/dist/collection/services/CameraService.js.map +1 -1
- package/dist/collection/services/DetectionService.js +164 -1
- package/dist/collection/services/DetectionService.js.map +1 -1
- package/dist/collection/services/ImageQualityService.js +329 -0
- package/dist/collection/services/ImageQualityService.js.map +1 -0
- package/dist/collection/services/ServiceContainer.js +6 -1
- package/dist/collection/services/ServiceContainer.js.map +1 -1
- package/dist/collection/services/interfaces/ICameraService.js.map +1 -1
- package/dist/collection/services/interfaces/IDetectionService.js.map +1 -1
- package/dist/collection/services/interfaces/IImageQualityService.js +2 -0
- package/dist/collection/services/interfaces/IImageQualityService.js.map +1 -0
- package/dist/components/jaak-stamps.js +885 -12
- package/dist/components/jaak-stamps.js.map +1 -1
- package/dist/esm/jaak-stamps-webcomponent.js +1 -1
- package/dist/esm/jaak-stamps.entry.js +868 -11
- package/dist/esm/jaak-stamps.entry.js.map +1 -1
- package/dist/esm/loader.js +1 -1
- package/dist/jaak-stamps-webcomponent/jaak-stamps-webcomponent.esm.js +1 -1
- package/dist/jaak-stamps-webcomponent/jaak-stamps.entry.esm.js.map +1 -1
- package/dist/jaak-stamps-webcomponent/p-47f37982.entry.js +2 -0
- package/dist/jaak-stamps-webcomponent/p-47f37982.entry.js.map +1 -0
- package/dist/types/components/my-component/my-component.d.ts +48 -0
- package/dist/types/components.d.ts +65 -0
- package/dist/types/services/CameraService.d.ts +5 -0
- package/dist/types/services/DetectionService.d.ts +22 -2
- package/dist/types/services/ImageQualityService.d.ts +31 -0
- package/dist/types/services/ServiceContainer.d.ts +7 -0
- package/dist/types/services/interfaces/ICameraService.d.ts +2 -0
- package/dist/types/services/interfaces/IDetectionService.d.ts +13 -0
- package/dist/types/services/interfaces/IImageQualityService.d.ts +58 -0
- package/package.json +1 -1
- package/dist/jaak-stamps-webcomponent/p-c30c7b47.entry.js +0 -2
- package/dist/jaak-stamps-webcomponent/p-c30c7b47.entry.js.map +0 -1
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { EventEmitter } from '../../stencil-public-runtime';
|
|
2
|
+
import { QualityThresholds } from '../../services/interfaces/IDetectionService';
|
|
2
3
|
export declare class JaakStamps {
|
|
3
4
|
el: HTMLElement;
|
|
4
5
|
debug: boolean;
|
|
@@ -7,6 +8,13 @@ export declare class JaakStamps {
|
|
|
7
8
|
cropMargin: number;
|
|
8
9
|
useDocumentClassification: boolean;
|
|
9
10
|
preferredCamera: 'auto' | 'front' | 'back';
|
|
11
|
+
captureDelay: number;
|
|
12
|
+
enableQualityValidation: boolean;
|
|
13
|
+
qualityThreshold: number;
|
|
14
|
+
minQualityScore: number;
|
|
15
|
+
minFocusScore: number;
|
|
16
|
+
minBlurScore: number;
|
|
17
|
+
maxReflectionScore: number;
|
|
10
18
|
captureCompleted: EventEmitter<any>;
|
|
11
19
|
isReady: EventEmitter<boolean>;
|
|
12
20
|
detectionBoxes: Array<{
|
|
@@ -26,12 +34,19 @@ export declare class JaakStamps {
|
|
|
26
34
|
shouldMirrorVideo: boolean;
|
|
27
35
|
showCameraSelector: boolean;
|
|
28
36
|
isSwitchingCamera: boolean;
|
|
37
|
+
hasDocumentDetected: boolean;
|
|
29
38
|
currentStatus: {
|
|
30
39
|
message: string;
|
|
31
40
|
description?: string;
|
|
32
41
|
type: 'initializing' | 'ready' | 'loading' | 'active' | 'error';
|
|
33
42
|
isInitialized: boolean;
|
|
34
43
|
};
|
|
44
|
+
qualityFeedback: {
|
|
45
|
+
message: string;
|
|
46
|
+
score: number;
|
|
47
|
+
hasIssues: boolean;
|
|
48
|
+
canCapture: boolean;
|
|
49
|
+
};
|
|
35
50
|
performanceData: {
|
|
36
51
|
fps: number;
|
|
37
52
|
inferenceTime: number;
|
|
@@ -112,6 +127,39 @@ export declare class JaakStamps {
|
|
|
112
127
|
selectedCamera: string;
|
|
113
128
|
availableCameras: number;
|
|
114
129
|
}>;
|
|
130
|
+
setCaptureDelay(delay: number): Promise<{
|
|
131
|
+
success: boolean;
|
|
132
|
+
captureDelay: number;
|
|
133
|
+
}>;
|
|
134
|
+
getCaptureDelay(): Promise<number>;
|
|
135
|
+
setTorchEnabled(enabled: boolean): Promise<{
|
|
136
|
+
success: boolean;
|
|
137
|
+
enabled: boolean;
|
|
138
|
+
}>;
|
|
139
|
+
focusAtPoint(x: number, y: number): Promise<{
|
|
140
|
+
success: boolean;
|
|
141
|
+
coordinates: {
|
|
142
|
+
x: number;
|
|
143
|
+
y: number;
|
|
144
|
+
};
|
|
145
|
+
}>;
|
|
146
|
+
getImageQuality(): Promise<{
|
|
147
|
+
qualityScore: any;
|
|
148
|
+
overallQuality: any;
|
|
149
|
+
issues: any;
|
|
150
|
+
recommendations: any;
|
|
151
|
+
canCapture: boolean;
|
|
152
|
+
}>;
|
|
153
|
+
setQualityThresholds(thresholds: QualityThresholds): Promise<{
|
|
154
|
+
success: boolean;
|
|
155
|
+
thresholds: QualityThresholds;
|
|
156
|
+
}>;
|
|
157
|
+
getQualityThresholds(): Promise<{
|
|
158
|
+
minQualityScore: number;
|
|
159
|
+
minFocusScore: number;
|
|
160
|
+
minBlurScore: number;
|
|
161
|
+
maxReflectionScore: number;
|
|
162
|
+
}>;
|
|
115
163
|
private startDetection;
|
|
116
164
|
private initializeVideoStream;
|
|
117
165
|
private detectFrame;
|
|
@@ -5,12 +5,18 @@
|
|
|
5
5
|
* It contains typing information for all components that exist in this project.
|
|
6
6
|
*/
|
|
7
7
|
import { HTMLStencilElement, JSXBase } from "./stencil-public-runtime";
|
|
8
|
+
import { QualityThresholds } from "./services/interfaces/IDetectionService";
|
|
9
|
+
export { QualityThresholds } from "./services/interfaces/IDetectionService";
|
|
8
10
|
export namespace Components {
|
|
9
11
|
interface JaakStamps {
|
|
10
12
|
/**
|
|
11
13
|
* @default 10
|
|
12
14
|
*/
|
|
13
15
|
"alignmentTolerance": number;
|
|
16
|
+
/**
|
|
17
|
+
* @default 1000
|
|
18
|
+
*/
|
|
19
|
+
"captureDelay": number;
|
|
14
20
|
/**
|
|
15
21
|
* @default 0
|
|
16
22
|
*/
|
|
@@ -19,21 +25,52 @@ export namespace Components {
|
|
|
19
25
|
* @default false
|
|
20
26
|
*/
|
|
21
27
|
"debug": boolean;
|
|
28
|
+
/**
|
|
29
|
+
* @default true
|
|
30
|
+
*/
|
|
31
|
+
"enableQualityValidation": boolean;
|
|
32
|
+
"focusAtPoint": (x: number, y: number) => Promise<{ success: boolean; coordinates: { x: number; y: number; }; }>;
|
|
22
33
|
"getCameraInfo": () => Promise<{ availableCameras: import("/home/runner/work/jaak-stamps-webcomponent/jaak-stamps-webcomponent/src/services/interfaces/ICameraService").CameraInfo[]; selectedCameraId: string | null; deviceType: string; isMultipleCamerasAvailable: boolean; preferredFacing: "environment" | "user" | null; }>;
|
|
34
|
+
"getCaptureDelay": () => Promise<number>;
|
|
23
35
|
"getCapturedImages": () => Promise<import("/home/runner/work/jaak-stamps-webcomponent/jaak-stamps-webcomponent/src/services/interfaces/IStateManager").CapturedImages>;
|
|
36
|
+
"getImageQuality": () => Promise<{ qualityScore: any; overallQuality: any; issues: any; recommendations: any; canCapture: boolean; }>;
|
|
37
|
+
"getQualityThresholds": () => Promise<{ minQualityScore: number; minFocusScore: number; minBlurScore: number; maxReflectionScore: number; }>;
|
|
24
38
|
"getStatus": () => Promise<{ isVideoActive: boolean; captureStep: "front" | "back" | "completed"; hasImages: boolean; isProcessCompleted: boolean; isModelPreloaded: boolean; }>;
|
|
25
39
|
"isProcessCompleted": () => Promise<boolean>;
|
|
26
40
|
/**
|
|
27
41
|
* @default 90
|
|
28
42
|
*/
|
|
29
43
|
"maskSize": number;
|
|
44
|
+
/**
|
|
45
|
+
* @default 15
|
|
46
|
+
*/
|
|
47
|
+
"maxReflectionScore": number;
|
|
48
|
+
/**
|
|
49
|
+
* @default 22
|
|
50
|
+
*/
|
|
51
|
+
"minBlurScore": number;
|
|
52
|
+
/**
|
|
53
|
+
* @default 16
|
|
54
|
+
*/
|
|
55
|
+
"minFocusScore": number;
|
|
56
|
+
/**
|
|
57
|
+
* @default 45
|
|
58
|
+
*/
|
|
59
|
+
"minQualityScore": number;
|
|
30
60
|
/**
|
|
31
61
|
* @default 'auto'
|
|
32
62
|
*/
|
|
33
63
|
"preferredCamera": 'auto' | 'front' | 'back';
|
|
34
64
|
"preloadModel": () => Promise<{ success: boolean; message: string; error?: undefined; } | { success: boolean; error: any; message?: undefined; }>;
|
|
65
|
+
/**
|
|
66
|
+
* @default 60
|
|
67
|
+
*/
|
|
68
|
+
"qualityThreshold": number;
|
|
35
69
|
"resetCapture": () => Promise<void>;
|
|
70
|
+
"setCaptureDelay": (delay: number) => Promise<{ success: boolean; captureDelay: number; }>;
|
|
36
71
|
"setPreferredCamera": (camera: "auto" | "front" | "back") => Promise<{ success: boolean; selectedCamera: string; availableCameras: number; }>;
|
|
72
|
+
"setQualityThresholds": (thresholds: QualityThresholds) => Promise<{ success: boolean; thresholds: QualityThresholds; }>;
|
|
73
|
+
"setTorchEnabled": (enabled: boolean) => Promise<{ success: boolean; enabled: boolean; }>;
|
|
37
74
|
"skipBackCapture": () => Promise<void>;
|
|
38
75
|
"startCapture": () => Promise<void>;
|
|
39
76
|
"stopCapture": () => Promise<void>;
|
|
@@ -76,6 +113,10 @@ declare namespace LocalJSX {
|
|
|
76
113
|
* @default 10
|
|
77
114
|
*/
|
|
78
115
|
"alignmentTolerance"?: number;
|
|
116
|
+
/**
|
|
117
|
+
* @default 1000
|
|
118
|
+
*/
|
|
119
|
+
"captureDelay"?: number;
|
|
79
120
|
/**
|
|
80
121
|
* @default 0
|
|
81
122
|
*/
|
|
@@ -84,16 +125,40 @@ declare namespace LocalJSX {
|
|
|
84
125
|
* @default false
|
|
85
126
|
*/
|
|
86
127
|
"debug"?: boolean;
|
|
128
|
+
/**
|
|
129
|
+
* @default true
|
|
130
|
+
*/
|
|
131
|
+
"enableQualityValidation"?: boolean;
|
|
87
132
|
/**
|
|
88
133
|
* @default 90
|
|
89
134
|
*/
|
|
90
135
|
"maskSize"?: number;
|
|
136
|
+
/**
|
|
137
|
+
* @default 15
|
|
138
|
+
*/
|
|
139
|
+
"maxReflectionScore"?: number;
|
|
140
|
+
/**
|
|
141
|
+
* @default 22
|
|
142
|
+
*/
|
|
143
|
+
"minBlurScore"?: number;
|
|
144
|
+
/**
|
|
145
|
+
* @default 16
|
|
146
|
+
*/
|
|
147
|
+
"minFocusScore"?: number;
|
|
148
|
+
/**
|
|
149
|
+
* @default 45
|
|
150
|
+
*/
|
|
151
|
+
"minQualityScore"?: number;
|
|
91
152
|
"onCaptureCompleted"?: (event: JaakStampsCustomEvent<any>) => void;
|
|
92
153
|
"onIsReady"?: (event: JaakStampsCustomEvent<boolean>) => void;
|
|
93
154
|
/**
|
|
94
155
|
* @default 'auto'
|
|
95
156
|
*/
|
|
96
157
|
"preferredCamera"?: 'auto' | 'front' | 'back';
|
|
158
|
+
/**
|
|
159
|
+
* @default 60
|
|
160
|
+
*/
|
|
161
|
+
"qualityThreshold"?: number;
|
|
97
162
|
/**
|
|
98
163
|
* @default false
|
|
99
164
|
*/
|
|
@@ -38,4 +38,9 @@ export declare class CameraService implements ICameraService {
|
|
|
38
38
|
private selectAutoCamera;
|
|
39
39
|
private updatePreferredFacing;
|
|
40
40
|
private getMaxResolution;
|
|
41
|
+
private applyAdvancedCameraSettings;
|
|
42
|
+
private applyBasicFocusSettings;
|
|
43
|
+
private getCapabilitiesSummary;
|
|
44
|
+
setTorchEnabled(enabled: boolean, stream?: MediaStream): Promise<boolean>;
|
|
45
|
+
focusAtPoint(x: number, y: number, stream?: MediaStream): Promise<boolean>;
|
|
41
46
|
}
|
|
@@ -1,14 +1,17 @@
|
|
|
1
|
-
import { IDetectionService, DetectionBox, SideAlignment, ClassificationResult } from './interfaces/IDetectionService';
|
|
1
|
+
import { IDetectionService, DetectionBox, SideAlignment, ClassificationResult, QualityThresholds } from './interfaces/IDetectionService';
|
|
2
2
|
import { ILogger } from './interfaces/ILogger';
|
|
3
|
+
import { ImageQualityResult } from './interfaces/IImageQualityService';
|
|
3
4
|
export declare class DetectionService implements IDetectionService {
|
|
4
5
|
private logger;
|
|
5
6
|
private debug;
|
|
6
7
|
private useDocumentClassification;
|
|
8
|
+
private qualityThresholds;
|
|
7
9
|
private session?;
|
|
8
10
|
private mobileNetSession?;
|
|
9
11
|
private mobileNetClassMap?;
|
|
10
12
|
private modelLoaded;
|
|
11
13
|
private deviceStrategy;
|
|
14
|
+
private imageQualityService;
|
|
12
15
|
private readonly MODEL_PATH;
|
|
13
16
|
private readonly MOBILENET_MODEL_PATH;
|
|
14
17
|
private readonly MOBILENET_CLASSES_PATH;
|
|
@@ -17,7 +20,7 @@ export declare class DetectionService implements IDetectionService {
|
|
|
17
20
|
private preprocessCanvas?;
|
|
18
21
|
private preprocessCtx?;
|
|
19
22
|
private captureCanvas?;
|
|
20
|
-
constructor(logger: ILogger, debug?: boolean, useDocumentClassification?: boolean);
|
|
23
|
+
constructor(logger: ILogger, debug?: boolean, useDocumentClassification?: boolean, qualityThresholds?: QualityThresholds);
|
|
21
24
|
loadModel(): Promise<void>;
|
|
22
25
|
loadClassificationModel(): Promise<void>;
|
|
23
26
|
isModelLoaded(): boolean;
|
|
@@ -32,4 +35,21 @@ export declare class DetectionService implements IDetectionService {
|
|
|
32
35
|
private cleanupCanvasPool;
|
|
33
36
|
private float32ToFloat16;
|
|
34
37
|
private preprocessMobileNet;
|
|
38
|
+
validateImageQuality(video: HTMLVideoElement, detectedBox?: DetectionBox): ImageQualityResult;
|
|
39
|
+
isImageQualityAcceptable(qualityResult: ImageQualityResult): boolean;
|
|
40
|
+
updateQualityThresholds(thresholds: QualityThresholds): void;
|
|
41
|
+
getQualityThresholds(): QualityThresholds;
|
|
42
|
+
getQualityFeedback(qualityResult: ImageQualityResult): string;
|
|
43
|
+
runInferenceWithQuality(inputTensor: any, video: HTMLVideoElement): Promise<{
|
|
44
|
+
detections: DetectionBox[];
|
|
45
|
+
qualityResult: ImageQualityResult;
|
|
46
|
+
canCapture: boolean;
|
|
47
|
+
feedback: string;
|
|
48
|
+
}>;
|
|
49
|
+
getQuickQualityFeedback(video: HTMLVideoElement): {
|
|
50
|
+
hasBlur: boolean;
|
|
51
|
+
hasReflections: boolean;
|
|
52
|
+
isFocused: boolean;
|
|
53
|
+
feedback: string;
|
|
54
|
+
};
|
|
35
55
|
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { IImageQualityService, ImageQualityResult, BlurMetrics, ReflectionMetrics, FocusMetrics, BrightnessMetrics, ContrastMetrics } from './interfaces/IImageQualityService';
|
|
2
|
+
import { ILogger } from './interfaces/ILogger';
|
|
3
|
+
export declare class ImageQualityService implements IImageQualityService {
|
|
4
|
+
private logger;
|
|
5
|
+
private readonly BLUR_THRESHOLD;
|
|
6
|
+
private readonly FOCUS_THRESHOLD;
|
|
7
|
+
private readonly REFLECTION_THRESHOLD;
|
|
8
|
+
private readonly MIN_BRIGHTNESS;
|
|
9
|
+
private readonly MAX_BRIGHTNESS;
|
|
10
|
+
private readonly MIN_CONTRAST;
|
|
11
|
+
constructor(logger: ILogger);
|
|
12
|
+
analyzeImageQuality(canvas: HTMLCanvasElement, documentBounds?: {
|
|
13
|
+
x: number;
|
|
14
|
+
y: number;
|
|
15
|
+
w: number;
|
|
16
|
+
h: number;
|
|
17
|
+
}): ImageQualityResult;
|
|
18
|
+
detectBlur(imageData: ImageData): BlurMetrics;
|
|
19
|
+
detectReflections(imageData: ImageData): ReflectionMetrics;
|
|
20
|
+
measureFocus(imageData: ImageData): FocusMetrics;
|
|
21
|
+
measureBrightness(imageData: ImageData): BrightnessMetrics;
|
|
22
|
+
measureContrast(imageData: ImageData): ContrastMetrics;
|
|
23
|
+
getQualityRecommendations(result: ImageQualityResult): string[];
|
|
24
|
+
private calculateOverallQuality;
|
|
25
|
+
private getQualityLevel;
|
|
26
|
+
private identifyIssues;
|
|
27
|
+
private getGrayscale;
|
|
28
|
+
private findConnectedRegion;
|
|
29
|
+
private sobelX;
|
|
30
|
+
private sobelY;
|
|
31
|
+
}
|
|
@@ -10,6 +10,13 @@ export interface ComponentConfig {
|
|
|
10
10
|
cropMargin: number;
|
|
11
11
|
useDocumentClassification: boolean;
|
|
12
12
|
preferredCamera: 'auto' | 'front' | 'back';
|
|
13
|
+
captureDelay: number;
|
|
14
|
+
enableQualityValidation?: boolean;
|
|
15
|
+
qualityThreshold?: number;
|
|
16
|
+
minQualityScore?: number;
|
|
17
|
+
minFocusScore?: number;
|
|
18
|
+
minBlurScore?: number;
|
|
19
|
+
maxReflectionScore?: number;
|
|
13
20
|
}
|
|
14
21
|
export declare class ServiceContainer {
|
|
15
22
|
private services;
|
|
@@ -22,6 +22,8 @@ export interface ICameraService {
|
|
|
22
22
|
savePreference(): void;
|
|
23
23
|
loadPreference(): void;
|
|
24
24
|
isRearCamera(stream: MediaStream): boolean;
|
|
25
|
+
setTorchEnabled(enabled: boolean, stream?: MediaStream): Promise<boolean>;
|
|
26
|
+
focusAtPoint(x: number, y: number, stream?: MediaStream): Promise<boolean>;
|
|
25
27
|
getCameraInfo(): {
|
|
26
28
|
availableCameras: CameraInfo[];
|
|
27
29
|
selectedCameraId: string | null;
|
|
@@ -17,6 +17,12 @@ export interface ClassificationResult {
|
|
|
17
17
|
confidence: number;
|
|
18
18
|
classIndex: number;
|
|
19
19
|
}
|
|
20
|
+
export interface QualityThresholds {
|
|
21
|
+
minQualityScore?: number;
|
|
22
|
+
minFocusScore?: number;
|
|
23
|
+
minBlurScore?: number;
|
|
24
|
+
maxReflectionScore?: number;
|
|
25
|
+
}
|
|
20
26
|
export interface IDetectionService {
|
|
21
27
|
loadModel(): Promise<void>;
|
|
22
28
|
loadClassificationModel(): Promise<void>;
|
|
@@ -27,5 +33,12 @@ export interface IDetectionService {
|
|
|
27
33
|
checkSideAlignment(box: DetectionBox, maskConfig: any): SideAlignment;
|
|
28
34
|
isCardInFrame(box: DetectionBox): boolean;
|
|
29
35
|
areAllSidesAligned(alignment: SideAlignment): boolean;
|
|
36
|
+
validateImageQuality(video: HTMLVideoElement, detectedBox?: DetectionBox): any;
|
|
37
|
+
isImageQualityAcceptable(qualityResult: any): boolean;
|
|
38
|
+
getQualityFeedback(qualityResult: any): string;
|
|
39
|
+
runInferenceWithQuality(inputTensor: any, video: HTMLVideoElement): Promise<any>;
|
|
40
|
+
getQuickQualityFeedback(video: HTMLVideoElement): any;
|
|
41
|
+
updateQualityThresholds(thresholds: QualityThresholds): void;
|
|
42
|
+
getQualityThresholds(): QualityThresholds;
|
|
30
43
|
cleanup(): void;
|
|
31
44
|
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
export interface BlurMetrics {
|
|
2
|
+
blurScore: number;
|
|
3
|
+
isAcceptable: boolean;
|
|
4
|
+
threshold: number;
|
|
5
|
+
}
|
|
6
|
+
export interface ReflectionMetrics {
|
|
7
|
+
reflectionScore: number;
|
|
8
|
+
hasReflection: boolean;
|
|
9
|
+
threshold: number;
|
|
10
|
+
reflectionAreas: {
|
|
11
|
+
x: number;
|
|
12
|
+
y: number;
|
|
13
|
+
width: number;
|
|
14
|
+
height: number;
|
|
15
|
+
}[];
|
|
16
|
+
}
|
|
17
|
+
export interface FocusMetrics {
|
|
18
|
+
focusScore: number;
|
|
19
|
+
isFocused: boolean;
|
|
20
|
+
threshold: number;
|
|
21
|
+
edgeStrength: number;
|
|
22
|
+
}
|
|
23
|
+
export interface BrightnessMetrics {
|
|
24
|
+
brightness: number;
|
|
25
|
+
isAcceptable: boolean;
|
|
26
|
+
minThreshold: number;
|
|
27
|
+
maxThreshold: number;
|
|
28
|
+
}
|
|
29
|
+
export interface ContrastMetrics {
|
|
30
|
+
contrast: number;
|
|
31
|
+
isAcceptable: boolean;
|
|
32
|
+
threshold: number;
|
|
33
|
+
}
|
|
34
|
+
export interface ImageQualityResult {
|
|
35
|
+
blur: BlurMetrics;
|
|
36
|
+
reflection: ReflectionMetrics;
|
|
37
|
+
focus: FocusMetrics;
|
|
38
|
+
brightness: BrightnessMetrics;
|
|
39
|
+
contrast: ContrastMetrics;
|
|
40
|
+
overallQuality: 'excellent' | 'good' | 'acceptable' | 'poor';
|
|
41
|
+
qualityScore: number;
|
|
42
|
+
issues: string[];
|
|
43
|
+
recommendations: string[];
|
|
44
|
+
}
|
|
45
|
+
export interface IImageQualityService {
|
|
46
|
+
analyzeImageQuality(canvas: HTMLCanvasElement, documentBounds?: {
|
|
47
|
+
x: number;
|
|
48
|
+
y: number;
|
|
49
|
+
w: number;
|
|
50
|
+
h: number;
|
|
51
|
+
}): ImageQualityResult;
|
|
52
|
+
detectBlur(imageData: ImageData): BlurMetrics;
|
|
53
|
+
detectReflections(imageData: ImageData): ReflectionMetrics;
|
|
54
|
+
measureFocus(imageData: ImageData): FocusMetrics;
|
|
55
|
+
measureBrightness(imageData: ImageData): BrightnessMetrics;
|
|
56
|
+
measureContrast(imageData: ImageData): ContrastMetrics;
|
|
57
|
+
getQualityRecommendations(result: ImageQualityResult): string[];
|
|
58
|
+
}
|
package/package.json
CHANGED
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
import{r as e,c as t,a as i,h as s}from"./p-BP1Q4KOg.js";class a{debugMode;constructor(e=false){this.debugMode=e}setDebugMode(e){this.debugMode=e}info(...e){if(this.debugMode){console.log(`[JAAK-STAMPS] [INFO] [${(new Date).toLocaleTimeString()}]`,...e)}}warn(...e){if(this.debugMode){console.warn(`[JAAK-STAMPS] [WARN] [${(new Date).toLocaleTimeString()}]`,...e)}}error(...e){if(this.debugMode){console.error(`[JAAK-STAMPS] [ERROR] [${(new Date).toLocaleTimeString()}]`,...e)}}debug(...e){if(this.debugMode){console.debug(`[JAAK-STAMPS] [DEBUG] [${(new Date).toLocaleTimeString()}]`,...e)}}state(e,t){if(this.debugMode){console.log(`[JAAK-STAMPS] [STATE] [${(new Date).toLocaleTimeString()}] ${e}`,t||"")}}performance(e,t){if(this.debugMode){console.log(`[JAAK-STAMPS] [PERF] [${(new Date).toLocaleTimeString()}] ${e}: ${t}ms`)}}}class o{events=new Map;on(e,t){if(!this.events.has(e)){this.events.set(e,[])}this.events.get(e).push(t)}off(e,t){const i=this.events.get(e);if(i){const e=i.indexOf(t);if(e>-1){i.splice(e,1)}}}emit(e,t){const i=this.events.get(e);if(i){i.forEach((i=>{try{i(t)}catch(t){console.error(`Error in event callback for ${e}:`,t)}}))}}once(e,t){const i=s=>{t(s);this.off(e,i)};this.on(e,i)}clear(){this.events.clear()}}class r{eventBus;captureState={step:"front",isCapturing:false,isDetectionPaused:false,isVideoActive:false,isLoading:false,showFlipAnimation:false,showSuccessAnimation:false,bestScore:0,hasScreenshotTaken:false};capturedImages={front:{fullFrame:null,cropped:null},back:{fullFrame:null,cropped:null},metadata:{totalImages:0,processCompleted:false,backCaptureSkipped:false}};constructor(e){this.eventBus=e}getCaptureState(){return{...this.captureState}}updateCaptureState(e){const t={...this.captureState};this.captureState={...this.captureState,...e};this.eventBus.emit("state-changed",{previous:t,current:this.captureState,changes:e})}getCapturedImages(){return JSON.parse(JSON.stringify(this.capturedImages))}setCapturedImages(e){this.capturedImages={...this.capturedImages,...e,metadata:{...this.capturedImages.metadata,...e.metadata}};let t=0;if(this.capturedImages.front.fullFrame&&this.capturedImages.front.cropped){t+=2}if(this.capturedImages.back.fullFrame&&this.capturedImages.back.cropped){t+=2}this.capturedImages.metadata.totalImages=t}reset(){this.captureState={step:"front",isCapturing:false,isDetectionPaused:false,isVideoActive:false,isLoading:false,showFlipAnimation:false,showSuccessAnimation:false,bestScore:0,hasScreenshotTaken:false};this.capturedImages={front:{fullFrame:null,cropped:null},back:{fullFrame:null,cropped:null},metadata:{totalImages:0,processCompleted:false,backCaptureSkipped:false}};this.eventBus.emit("state-changed",{previous:null,current:this.captureState,changes:{reset:true}})}isProcessCompleted(){return this.captureState.step==="completed"}canProceedToBack(){return this.captureState.step==="front"&&this.capturedImages.front.fullFrame!==null&&this.capturedImages.front.cropped!==null}}class n{logger;eventBus;availableCameras=[];selectedCameraId=null;deviceType="desktop";preferredCameraFacing=null;preferredCamera="auto";constructor(e,t,i="auto"){this.logger=e;this.eventBus=t;this.preferredCamera=i}async detectDeviceType(){const e=navigator.userAgent;const t=/Android|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(e);const i=/iPad|Android/i.test(e)&&window.innerWidth>=768;if(i){this.deviceType="tablet"}else if(t){this.deviceType="mobile"}else{this.deviceType="desktop"}this.logger.state("DISPOSITIVO_DETECTADO",{deviceType:this.deviceType,userAgent:navigator.userAgent,screenDimensions:{width:window.innerWidth,height:window.innerHeight}});return this.deviceType}async enumerateDevices(){try{const e=await this.checkCameraPermission();if(e==="denied"){this.logger.error("Permiso de cámara denegado por el usuario");return[]}if(e==="prompt"){const e=await navigator.mediaDevices.getUserMedia({video:true});e.getTracks().forEach((e=>e.stop()))}const t=await navigator.mediaDevices.enumerateDevices();this.availableCameras=t.filter((e=>e.kind==="videoinput"));this.logger.state("CAMARAS_DETECTADAS",{count:this.availableCameras.length,cameras:this.availableCameras.map((e=>({id:e.deviceId,label:e.label||"Unknown Camera"})))});this.setInitialCameraPreference();this.eventBus.emit("camera-changed",this.selectedCameraId);return this.availableCameras}catch(e){this.logger.error("Error al enumerar cámaras disponibles:",e);this.handleCameraPermissionError(e);return[]}}getAvailableCameras(){return[...this.availableCameras]}isMultipleCamerasAvailable(){return this.availableCameras.length>1}getSelectedCameraId(){return this.selectedCameraId}async setSelectedCamera(e){const t=this.availableCameras.find((t=>t.deviceId===e));if(!t){throw new Error(`Camera with ID ${e} not found`)}this.selectedCameraId=e;this.updatePreferredFacing(t);this.savePreference();this.eventBus.emit("camera-changed",e)}getPreferredFacing(){return this.preferredCameraFacing}async setupCamera(e){const t=e||await this.getMaxResolution();const i=await navigator.mediaDevices.getUserMedia({video:t,audio:false});return i}async switchCamera(e){const t=this.availableCameras.find((t=>t.deviceId===e));if(!t){this.logger.warn("Cámara seleccionada no encontrada, re-enumerando dispositivos...");await this.enumerateDevices();return}await this.setSelectedCamera(e);this.logger.state("CAMARA_CAMBIADA",{label:t.label,deviceId:t.deviceId})}async flipToNextCamera(){if(!this.isMultipleCamerasAvailable())return;const e=this.availableCameras.findIndex((e=>e.deviceId===this.selectedCameraId));const t=(e+1)%this.availableCameras.length;const i=this.availableCameras[t];await this.switchCamera(i.deviceId)}savePreference(){try{const e={cameraId:this.selectedCameraId,facing:this.preferredCameraFacing,timestamp:Date.now()};localStorage.setItem("jaak-stamps-camera-preference",JSON.stringify(e));this.logger.state("PREFERENCIA_CAMARA_GUARDADA",e)}catch(e){this.logger.warn("Error al guardar preferencia de cámara:",e)}}loadPreference(){try{const e=localStorage.getItem("jaak-stamps-camera-preference");if(e){const t=JSON.parse(e);const i=this.availableCameras.some((e=>e.deviceId===t.cameraId));if(i){this.selectedCameraId=t.cameraId;this.preferredCameraFacing=t.facing;this.logger.state("PREFERENCIA_CAMARA_CARGADA",t)}}}catch(e){this.logger.warn("Error al cargar preferencia de cámara:",e)}}isRearCamera(e){const t=e.getVideoTracks()[0];if(!t)return false;const i=t.getSettings();return i.facingMode==="environment"}getCameraInfo(){return{availableCameras:this.availableCameras.map((e=>({id:e.deviceId,label:e.label||"Unknown Camera",selected:e.deviceId===this.selectedCameraId}))),selectedCameraId:this.selectedCameraId,deviceType:this.deviceType,isMultipleCamerasAvailable:this.isMultipleCamerasAvailable(),preferredFacing:this.preferredCameraFacing}}async checkCameraPermission(){try{if(!navigator.permissions){return"prompt"}const e=await navigator.permissions.query({name:"camera"});return e.state}catch(e){this.logger.warn("No se pudo verificar permisos de cámara:",e);return"prompt"}}handleCameraPermissionError(e){this.availableCameras=[];this.eventBus.emit("error",new Error(`Camera permission error: ${e.message}`))}setInitialCameraPreference(){if(this.availableCameras.length===0)return;if(this.preferredCamera==="front"){this.selectFrontCamera()}else if(this.preferredCamera==="back"){this.selectBackCamera()}else{this.selectAutoCamera()}}selectFrontCamera(){this.preferredCameraFacing="user";const e=this.availableCameras.find((e=>e.label.toLowerCase().includes("front")||e.label.toLowerCase().includes("user")||e.label.toLowerCase().includes("selfie")||!e.label.toLowerCase().includes("back")&&!e.label.toLowerCase().includes("rear")));this.selectedCameraId=e?e.deviceId:this.availableCameras[0].deviceId;this.logger.state("CAMARA_FRONTAL_SELECCIONADA",{label:e?.label||this.availableCameras[0].label,deviceId:this.selectedCameraId})}selectBackCamera(){this.preferredCameraFacing="environment";const e=this.availableCameras.find((e=>e.label.toLowerCase().includes("back")||e.label.toLowerCase().includes("rear")||e.label.toLowerCase().includes("environment")));this.selectedCameraId=e?e.deviceId:this.availableCameras[0].deviceId;this.logger.state("CAMARA_TRASERA_SELECCIONADA",{label:e?.label||this.availableCameras[0].label,deviceId:this.selectedCameraId})}selectAutoCamera(){if(this.deviceType==="mobile"||this.deviceType==="tablet"){this.selectBackCamera();this.logger.state("CAMARA_AUTO_SELECCIONADA_MOBILE",{type:"rear"})}else{this.selectedCameraId=this.availableCameras[0].deviceId;this.logger.state("CAMARA_AUTO_SELECCIONADA_DESKTOP",{label:this.availableCameras[0].label,deviceId:this.selectedCameraId})}}updatePreferredFacing(e){const t=e.label.toLowerCase().includes("back")||e.label.toLowerCase().includes("rear")||e.label.toLowerCase().includes("environment");this.preferredCameraFacing=t?"environment":"user"}async getMaxResolution(){try{const e={};if(this.selectedCameraId){e.deviceId={exact:this.selectedCameraId}}else if(this.preferredCameraFacing){e.facingMode=this.preferredCameraFacing}else{e.facingMode="environment"}const t=await navigator.mediaDevices.getUserMedia({video:e});const i=t.getVideoTracks()[0];const s=i.getCapabilities();t.getTracks().forEach((e=>e.stop()));const a={...e};if(s.width&&s.height){const e=Math.min(s.width.max,1920);const t=Math.min(s.height.max,1080);const i=/iPad|Android/i.test(navigator.userAgent)&&window.innerWidth>=768;if(i){a.width={ideal:Math.min(e,1280)};a.height={ideal:Math.min(t,720)}}else{a.width={ideal:e};a.height={ideal:t}}}return a}catch(e){this.logger.warn("No se pudieron obtener capacidades de cámara, usando configuración de respaldo");const t=/iPad|Android/i.test(navigator.userAgent)&&window.innerWidth>=768;const i={width:{ideal:t?1280:1920},height:{ideal:t?720:1080}};if(this.selectedCameraId){i.deviceId={exact:this.selectedCameraId}}else if(this.preferredCameraFacing){i.facingMode=this.preferredCameraFacing}else{i.facingMode="environment"}return i}}}class c{getDeviceInfo(){const e=navigator;const t=e.deviceMemory||e.hardwareConcurrency||4;const i=e.connection||e.mozConnection||e.webkitConnection;const s=i&&(i.effectiveType==="slow-2g"||i.effectiveType==="2g");return{estimatedRAM:t,isLowMemory:true,isSlowConnection:s}}getSessionOptions(e){return{executionProviders:["wasm"],graphOptimizationLevel:"basic",logSeverityLevel:4,logVerbosityLevel:0,enableCpuMemArena:false,enableMemPattern:false,executionMode:"sequential",interOpNumThreads:1,intraOpNumThreads:1}}shouldUseSequentialLoading(){return true}}class d{getDeviceInfo(){const e=navigator;const t=e.deviceMemory||e.hardwareConcurrency||4;const i=e.connection||e.mozConnection||e.webkitConnection;const s=i&&(i.effectiveType==="slow-2g"||i.effectiveType==="2g");return{estimatedRAM:t,isLowMemory:false,isSlowConnection:s}}getSessionOptions(e){return{executionProviders:["webgl","wasm"],graphOptimizationLevel:"all",logSeverityLevel:e?2:4,logVerbosityLevel:0,enableCpuMemArena:true,enableMemPattern:true,executionMode:"parallel",interOpNumThreads:2,intraOpNumThreads:2}}shouldUseSequentialLoading(){return false}}class l{static createStrategy(){const e=navigator;const t=e.deviceMemory||e.hardwareConcurrency||4;const i=t<=4;if(i){return new c}else{return new d}}}class h{logger;debug;useDocumentClassification;session;mobileNetSession;mobileNetClassMap;modelLoaded=false;deviceStrategy;MODEL_PATH="https://storage.googleapis.com/jaak-static/web/component/stamps/ddmyp-v2.onnx";MOBILENET_MODEL_PATH="https://storage.googleapis.com/jaak-static/web/component/stamps/cdmmp-v1.onnx";MOBILENET_CLASSES_PATH="https://storage.googleapis.com/jaak-static/web/component/stamps/cdmmp-v1.json";INPUT_SIZE=320;CONFIDENCE_THRESHOLD=.6;preprocessCanvas;preprocessCtx;captureCanvas;constructor(e,t=false,i=false){this.logger=e;this.debug=t;this.useDocumentClassification=i;this.deviceStrategy=l.createStrategy();this.initializeCanvasPool()}async loadModel(){if(this.modelLoaded||this.session){this.logger.state("MODELO_YA_PRECARGADO",{sessionExists:!!this.session,modelPreloaded:this.modelLoaded});return}try{this.logger.state("PRECARGANDO_MODELO_DETECCION",{modelPath:this.MODEL_PATH});const e=this.deviceStrategy.getSessionOptions(this.debug);try{this.session=await window.ort.InferenceSession.create(this.MODEL_PATH,e)}catch(e){if(e.message.includes("failed to allocate a buffer")){this.logger.warn("Fallo en asignación de buffer durante precarga, intentando con configuración mínima");const e={executionProviders:["wasm"],graphOptimizationLevel:"disabled",logSeverityLevel:4,enableCpuMemArena:false,enableMemPattern:false,executionMode:"sequential",interOpNumThreads:1,intraOpNumThreads:1};this.session=await window.ort.InferenceSession.create(this.MODEL_PATH,e)}else{throw e}}this.modelLoaded=true;this.logger.state("MODELO_DETECCION_CARGADO_EXITOSAMENTE",{sessionCreated:!!this.session})}catch(e){this.logger.error("Error al precargar modelo de detección:",e);throw e}}async loadClassificationModel(){if(!this.useDocumentClassification){return}try{this.logger.state("CARGANDO_MODELO_MOBILENET",{path:this.MOBILENET_MODEL_PATH});const e=await fetch(this.MOBILENET_CLASSES_PATH);if(!e.ok){throw new Error(`Failed to load class map: ${this.MOBILENET_CLASSES_PATH}`)}this.mobileNetClassMap=await e.json();this.logger.state("CLASES_MOBILENET_CARGADAS",{classCount:Object.keys(this.mobileNetClassMap).length});const t=this.deviceStrategy.getSessionOptions(this.debug);try{this.mobileNetSession=await window.ort.InferenceSession.create(this.MOBILENET_MODEL_PATH,t)}catch(e){if(e.message.includes("failed to allocate a buffer")){this.logger.warn("Fallo en asignación de buffer de MobileNet, intentando con configuración mínima");const e={executionProviders:["wasm"],graphOptimizationLevel:"disabled",logSeverityLevel:4,enableCpuMemArena:false,enableMemPattern:false,executionMode:"sequential",interOpNumThreads:1,intraOpNumThreads:1};this.mobileNetSession=await window.ort.InferenceSession.create(this.MOBILENET_MODEL_PATH,e)}else{throw e}}this.logger.state("MODELO_MOBILENET_CARGADO_EXITOSAMENTE",{sessionCreated:!!this.mobileNetSession})}catch(e){this.logger.error("Error al cargar modelo MobileNet:",e);throw e}}isModelLoaded(){return this.modelLoaded&&!!this.session}preprocess(e){if(!this.preprocessCanvas||!this.preprocessCtx){this.initializeCanvasPool()}this.preprocessCtx.clearRect(0,0,this.INPUT_SIZE,this.INPUT_SIZE);this.preprocessCtx.drawImage(e,0,0,this.INPUT_SIZE,this.INPUT_SIZE);const t=this.preprocessCtx.getImageData(0,0,this.INPUT_SIZE,this.INPUT_SIZE);const[i,s,a]=[[],[],[]];const{data:o}=t;for(let e=0;e<o.length;e+=4){i.push(o[e]/255);s.push(o[e+1]/255);a.push(o[e+2]/255)}const r=new Float32Array(i.concat(s,a));const n=new Uint16Array(r.length);for(let e=0;e<r.length;e++){n[e]=this.float32ToFloat16(r[e])}return new window.ort.Tensor("float16",n,[1,3,this.INPUT_SIZE,this.INPUT_SIZE])}async runInference(e){if(!this.session){throw new Error("Detection model not loaded")}const t={[this.session.inputNames[0]]:e};const i=await this.session.run(t);const s=i[this.session.outputNames[0]].data;const a=[];for(let e=0;e<s.length;e+=6){const[t,i,o,r,n,c]=s.slice(e,e+6);if(n>this.CONFIDENCE_THRESHOLD){a.push({x:t,y:i,w:o-t,h:r-i,score:n,classId:c})}}return a}async classifyDocument(e){if(!this.mobileNetSession||!this.mobileNetClassMap){this.logger.warn("Modelo MobileNet no está cargado, saltando clasificación");return null}try{this.logger.state("CLASIFICANDO_DOCUMENTO",{timestamp:Date.now()});const t=this.preprocessMobileNet(e);const i={input:t};const s=await this.mobileNetSession.run(i);const a=s[Object.keys(s)[0]].data;const o=a.reduce(((e,t,i,s)=>t>s[e]?i:e),0);const r=a[o];const n=this.mobileNetClassMap[o.toString()]||"unknown";this.logger.state("DOCUMENTO_CLASIFICADO",{class:n,confidence:r.toFixed(3),classIndex:o,timestamp:Date.now()});return{class:n,confidence:r,classIndex:o}}catch(e){this.logger.error("Error al clasificar documento:",e);return null}}checkSideAlignment(e,t){const{INPUT_SIZE:i,ID1_ASPECT_RATIO:s,shouldMirrorVideo:a,alignmentTolerance:o,maskSize:r,videoRef:n}=t;if(!n){return{top:false,right:false,bottom:false,left:false}}const c=n.videoWidth;const d=n.videoHeight;if(c===0||d===0){return{top:false,right:false,bottom:false,left:false}}const l=c/d;const h=1;let f,p;if(l>h){f=i;p=i/l}else{p=i;f=i*l}const m=p*s;let u,g;const b=r/100;if(m<=f){g=p*b;u=g*s}else{u=f*b;g=u/s}const x=u*(i/f);const w=g*(i/p);const y=i/2;const v=i/2;const A=y-x/2;const C=y+x/2;const k=v-w/2;const E=v+w/2;let S=e.x;let M=e.x+e.w;const I=e.y;const D=e.y+e.h;if(a){const e=S;const t=M;S=i-t;M=i-e}const O=o;const T=Math.abs(I-k)<=O;const P=Math.abs(M-C)<=O;const N=Math.abs(D-E)<=O;const R=Math.abs(S-A)<=O;return{top:T&&R,right:T&&P,bottom:N&&R,left:N&&P}}isCardInFrame(e){const t=e.x+e.w/2;const i=e.y+e.h/2;const s=this.INPUT_SIZE/2;const a=this.INPUT_SIZE/2;const o=40;const r=30;const n=Math.abs(t-s)<o&&Math.abs(i-a)<r;const c=e.w>150&&e.w<300&&e.h>90&&e.h<200;return n&&c}areAllSidesAligned(e){return e.top&&e.right&&e.bottom&&e.left}cleanup(){this.cleanupCanvasPool();if(this.session){this.session.release?.();this.session=undefined}if(this.mobileNetSession){this.mobileNetSession.release?.();this.mobileNetSession=undefined}this.mobileNetClassMap=undefined;this.modelLoaded=false;this.logger.state("DETECCION_SERVICE_LIMPIADO",{timestamp:Date.now()})}initializeCanvasPool(){this.preprocessCanvas=document.createElement("canvas");this.preprocessCanvas.width=this.INPUT_SIZE;this.preprocessCanvas.height=this.INPUT_SIZE;this.preprocessCtx=this.preprocessCanvas.getContext("2d",{alpha:false,willReadFrequently:true});this.captureCanvas=document.createElement("canvas");this.logger.state("CANVAS_POOL_INICIALIZADO",{preprocessCanvasSize:this.INPUT_SIZE})}cleanupCanvasPool(){if(this.preprocessCanvas){this.preprocessCtx=undefined;this.preprocessCanvas=undefined}if(this.captureCanvas){this.captureCanvas=undefined}}float32ToFloat16(e){const t=new ArrayBuffer(4);const i=new DataView(t);i.setFloat32(0,e,true);const s=i.getUint32(0,true);const a=s>>31&1;const o=s>>23&255;const r=s&8388607;let n=o-127+15;if(o===0){n=0}else if(o===255){n=31}else if(n>=31){n=31;return a<<15|n<<10}else if(n<=0){return a<<15}return a<<15|n<<10|r>>13}preprocessMobileNet(e){const t=document.createElement("canvas");t.width=224;t.height=224;const i=t.getContext("2d");i.drawImage(e,0,0,224,224);const s=i.getImageData(0,0,224,224);const a=s.data;const o=224*224;const r=new Float32Array(3*o);for(let e=0;e<o;e++){r[e]=(a[e*4+0]/255-.5)/.5;r[o+e]=(a[e*4+1]/255-.5)/.5;r[2*o+e]=(a[e*4+2]/255-.5)/.5}return new window.ort.Tensor("float32",r,[1,3,224,224])}}class f{services=new Map;constructor(e){this.initializeServices(e)}initializeServices(e){const t=new o;const i=new a(e.debug);const s=new r(t);const c=new n(i,t,e.preferredCamera);const d=new h(i,e.debug,e.useDocumentClassification);this.services.set("eventBus",t);this.services.set("logger",i);this.services.set("stateManager",s);this.services.set("cameraService",c);this.services.set("detectionService",d)}get(e){const t=this.services.get(e);if(!t){throw new Error(`Service ${e} not found`)}return t}getLogger(){return this.get("logger")}getEventBus(){return this.get("eventBus")}getStateManager(){return this.get("stateManager")}getCameraService(){return this.get("cameraService")}getDetectionService(){return this.get("detectionService")}updateConfig(e){if(e.debug!==undefined){const t=this.services.get("logger");t.setDebugMode(e.debug)}}cleanup(){this.getDetectionService().cleanup();this.getEventBus().clear();this.services.clear()}}const p=":host{display:block;width:100%;height:100%;font-family:system-ui, -apple-system, sans-serif;color:#1a1a1a}.detector-container{display:flex;flex-direction:column;align-items:center;width:100%;height:100%}h1{font-size:24px;font-weight:500;color:#333;margin:0 0 24px 0}.video-container{position:relative;width:100%;height:100%;background:#333;border:1px solid #e0e0e0;border-radius:8px;overflow:hidden}video,.detection-overlay{position:absolute;width:100%;height:100%;border-radius:8px}video.mirror,.detection-overlay.mirror{transform:rotateY(180deg)}.detection-overlay{z-index:1;pointer-events:none}video{z-index:0}.detection-box{transition:opacity 0.2s ease}.status{margin-top:16px;font-size:12px;color:#666}.overlay-mask{position:absolute;top:0;left:0;width:100%;height:100%;z-index:10;display:block;pointer-events:none}.card-outline{position:absolute;top:var(--mask-center-y, 50%);left:var(--mask-center-x, 50%);transform:translate(-50%, -50%);width:var(--mask-width, 88%);height:var(--mask-height, 55%);border:none;border-radius:4px;background:transparent;opacity:0.8}.side{position:absolute;background:#999;transition:background-color 0.3s ease;border-radius:1px}.side-top.aligned{border-left-color:#28a745;border-top-color:#28a745}.side-right.aligned{border-right-color:#28a745;border-top-color:#28a745}.side-bottom.aligned{border-left-color:#28a745;border-bottom-color:#28a745}.side-left.aligned{border-right-color:#28a745;border-bottom-color:#28a745}.side-top{top:-3px;left:-3px;width:30px;height:30px;border-left:6px solid #ffffff;border-top:6px solid #ffffff;background:transparent}.side-right{top:-3px;right:-3px;width:30px;height:30px;border-right:6px solid #ffffff;border-top:6px solid #ffffff;background:transparent}.side-bottom{bottom:-3px;left:-3px;width:30px;height:30px;border-left:6px solid #ffffff;border-bottom:6px solid #ffffff;background:transparent}.side-left{bottom:-3px;right:-3px;width:30px;height:30px;border-right:6px solid #ffffff;border-bottom:6px solid #ffffff;background:transparent}.guide-text{position:absolute;top:50%;left:50%;transform:translate(-50%, -50%);color:#fff;font-size:14px;font-weight:600;text-align:center;white-space:normal;background:rgba(128, 128, 128, 0.8);padding:12px 20px;border-radius:20px;max-width:300px;z-index:20}.capture-animation{position:absolute;top:0;left:0;width:100%;height:100%;background:#fff;opacity:0;z-index:30;pointer-events:none;animation:captureFlash 0.6s ease-out}@keyframes captureFlash{0%{opacity:0}15%{opacity:0.8}30%{opacity:0}45%{opacity:0.4}60%{opacity:0}100%{opacity:0}}.card-outline.capturing{animation:pulseGreen 0.6s ease-out}@keyframes pulseGreen{0%{border-color:#28a745;box-shadow:0 0 0 4px rgba(40, 167, 69, 0)}50%{border-color:#28a745;box-shadow:0 0 0 8px rgba(40, 167, 69, 0.6)}100%{border-color:#28a745;box-shadow:0 0 0 4px rgba(40, 167, 69, 0)}}.flip-animation{position:absolute;top:50%;left:50%;transform:translate(-50%, -50%);z-index:35;pointer-events:none;opacity:0;animation:showFlipInstruction 3s ease-in-out;display:flex;flex-direction:column;align-items:center;justify-content:center}.id-card-icon{width:80px;height:50px;background:linear-gradient(135deg, #6c757d 0%, #495057 100%);border-radius:8px;position:relative;animation:flipCard 2s ease-in-out infinite;box-shadow:0 4px 12px rgba(0, 0, 0, 0.3)}.id-card-icon::before{content:'';position:absolute;top:8px;left:8px;width:16px;height:12px;background:rgba(255, 255, 255, 0.9);border-radius:2px}.id-card-icon::after{content:'';position:absolute;top:25px;left:8px;width:64px;height:3px;background:rgba(255, 255, 255, 0.7);border-radius:1px;box-shadow:0 6px 0 rgba(255, 255, 255, 0.7), 0 12px 0 rgba(255, 255, 255, 0.7)}.flip-text{margin-top:12px;color:#fff;font-size:14px;font-weight:600;text-align:center;background:rgba(128, 128, 128, 0.8);padding:12px 20px;border-radius:20px;max-width:300px}@keyframes showFlipInstruction{0%{opacity:0;transform:translate(-50%, -50%) scale(0.8)}15%{opacity:1;transform:translate(-50%, -50%) scale(1)}85%{opacity:1;transform:translate(-50%, -50%) scale(1)}100%{opacity:0;transform:translate(-50%, -50%) scale(0.8)}}@keyframes flipCard{0%{transform:rotateY(0deg)}50%{transform:rotateY(180deg)}100%{transform:rotateY(360deg)}}.success-animation{position:absolute;top:50%;left:50%;transform:translate(-50%, -50%);z-index:35;pointer-events:none;opacity:0;animation:showSuccessMessage 3s ease-in-out;display:flex;flex-direction:column;align-items:center;justify-content:center}.check-icon{width:80px;height:80px;background:linear-gradient(135deg, #6c757d 0%, #495057 100%);border-radius:50%;position:relative;animation:bounceSuccess 0.6s ease-out;box-shadow:0 4px 16px rgba(108, 117, 125, 0.4);display:flex;align-items:center;justify-content:center}.check-icon::before{content:'';position:absolute;width:20px;height:35px;border:4px solid #fff;border-top:none;border-left:none;transform:rotate(45deg);animation:drawCheck 0.4s ease-out 0.2s both}.success-text{margin-top:16px;color:#fff;font-size:14px;font-weight:600;text-align:center;background:rgba(128, 128, 128, 0.8);padding:12px 20px;border-radius:20px;max-width:300px;animation:fadeInUp 0.5s ease-out 0.4s both}@keyframes showSuccessMessage{0%{opacity:0;transform:translate(-50%, -50%) scale(0.5)}15%{opacity:1;transform:translate(-50%, -50%) scale(1)}85%{opacity:1;transform:translate(-50%, -50%) scale(1)}100%{opacity:0;transform:translate(-50%, -50%) scale(0.9)}}@keyframes bounceSuccess{0%{transform:scale(0)}50%{transform:scale(1.1)}100%{transform:scale(1)}}@keyframes drawCheck{0%{width:0;height:0}50%{width:20px;height:0}100%{width:20px;height:35px}}@keyframes fadeInUp{0%{opacity:0;transform:translateY(20px)}100%{opacity:1;transform:translateY(0)}}.skip-button{position:absolute;bottom:20px;left:50%;transform:translateX(-50%);z-index:25;pointer-events:auto;background:#fff;color:#333;border:none;border-radius:25px;padding:12px 24px;font-size:14px;font-weight:600;cursor:pointer;transition:all 0.3s ease}.skip-button:hover{background:#f8f9fa}.skip-button:active{background:#e9ecef;transform:translateX(-50%) translateY(0)}.camera-controls{position:absolute;top:16px;right:16px;z-index:25;display:flex;gap:8px;pointer-events:auto}.camera-selector-button{height:32px;padding:0 10px;border:none;border-radius:8px;background:rgba(0, 0, 0, 0.25);backdrop-filter:blur(12px);color:#ffffff;font-size:12px;font-weight:500;cursor:pointer;transition:all 0.2s ease;display:flex;align-items:center;justify-content:center;border:1px solid rgba(255, 255, 255, 0.1);white-space:nowrap;min-width:fit-content}.camera-selector-button:hover{background:rgba(0, 0, 0, 0.8);border-color:rgba(255, 255, 255, 0.2);transform:translateY(-1px)}.camera-selector-button:active{transform:translateY(0);background:rgba(0, 0, 0, 0.9)}.camera-selector-button:disabled,.camera-selector-button.loading{opacity:0.7;cursor:not-allowed;pointer-events:none}.camera-selector-button:disabled:hover,.camera-selector-button.loading:hover{transform:none;background:rgba(0, 0, 0, 0.6);border-color:rgba(255, 255, 255, 0.1)}.button-spinner{width:16px;height:16px;border:2px solid rgba(255, 255, 255, 0.3);border-top:2px solid #ffffff;border-radius:50%;animation:spin 1s linear infinite}@keyframes spin{0%{transform:rotate(0deg)}100%{transform:rotate(360deg)}}.camera-selector-dropdown{position:absolute;top:56px;right:16px;z-index:30;background:rgba(0, 0, 0, 0.25);backdrop-filter:blur(20px);border-radius:12px;min-width:260px;max-width:300px;border:1px solid rgba(255, 255, 255, 0.1);overflow:hidden;pointer-events:auto;animation:slideInFromTop 0.3s ease-out}@keyframes slideInFromTop{0%{opacity:0;transform:translateY(-8px) scale(0.95)}100%{opacity:1;transform:translateY(0) scale(1)}}.camera-selector-header{display:flex;justify-content:space-between;align-items:center;padding:8px 12px;border-bottom:1px solid rgba(255, 255, 255, 0.1)}.camera-selector-header span{font-weight:500;color:#ffffff;font-size:13px;opacity:0.9}.close-selector{width:20px;height:20px;border:none;background:none;color:rgba(255, 255, 255, 0.7);font-size:16px;cursor:pointer;display:flex;align-items:center;justify-content:center;border-radius:4px;transition:all 0.2s ease}.close-selector:hover{background:rgba(255, 255, 255, 0.1);color:#ffffff}.camera-list{padding:4px 0;max-height:240px;overflow-y:auto}.camera-option{width:100%;padding:8px 12px;border:none;background:none;text-align:left;cursor:pointer;transition:all 0.2s ease;display:flex;justify-content:space-between;align-items:center;color:rgba(255, 255, 255, 0.9)}.camera-option:hover{background:rgba(255, 255, 255, 0.08)}.camera-option.selected{background:rgba(255, 255, 255, 0.12);color:#ffffff}.camera-label{font-size:13px;flex:1;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;margin-right:8px;font-weight:400}.selected-indicator{font-size:14px;color:#ffffff;opacity:0.9}.device-info{padding:6px 12px;border-top:1px solid rgba(255, 255, 255, 0.1);background:rgba(255, 255, 255, 0.05)}.device-info small{color:rgba(255, 255, 255, 0.6);font-size:11px;text-transform:capitalize;font-weight:400}@media (max-width: 480px){.camera-controls{top:12px;right:12px;gap:6px}.camera-selector-button{height:36px;padding:0 12px;font-size:11px;border-radius:6px}.camera-selector-dropdown{right:12px;top:48px;min-width:240px;max-width:calc(100vw - 24px)}.camera-selector-header{padding:6px 10px}.camera-option{padding:6px 10px}.device-info{padding:4px 10px}}.watermark{position:absolute;bottom:12px;right:12px;z-index:15;pointer-events:none;opacity:0.7}.watermark img{height:24px;width:auto;filter:drop-shadow(0 1px 2px rgba(0, 0, 0, 0.3))}.component-status{position:absolute;top:16px;left:16px;background:rgba(0, 0, 0, 0.25);backdrop-filter:blur(20px);border-radius:12px;border:1px solid rgba(255, 255, 255, 0.1);display:flex;align-items:center;gap:6px;padding:6px 10px;z-index:40;height:32px;width:fit-content;animation:slideInFromTop 0.3s ease-out}.status-spinner{width:16px;height:16px;border:1px solid rgba(255, 255, 255, 0.3);border-top:1px solid #ffffff;border-radius:50%;animation:statusSpin 1s linear infinite;flex-shrink:0}.status-content{display:flex;flex-direction:column;gap:1px}.status-message{color:#ffffff;font-size:12px;font-weight:500;margin:0}.status-description{color:rgba(255, 255, 255, 0.7);font-size:10px;line-height:1.2;margin:0}@keyframes statusSpin{0%{transform:rotate(0deg)}100%{transform:rotate(360deg)}}@media (max-width: 480px){.component-status{top:12px;left:12px;padding:4px 8px;gap:4px;height:28px;border-radius:8px}.status-spinner{width:14px;height:14px}.status-message{font-size:11px}.status-description{font-size:9px}}.loading-overlay{position:absolute;top:0;left:0;width:100%;height:100%;background:rgba(0, 0, 0, 0.85);backdrop-filter:blur(4px);display:flex;flex-direction:column;align-items:center;justify-content:center;z-index:30;border-radius:8px}.loading-spinner{width:50px;height:50px;border:3px solid rgba(255, 255, 255, 0.15);border-top:3px solid #28a745;border-radius:50%;animation:spin 1s ease-in-out infinite;margin-bottom:16px}.loading-text{color:#ffffff;font-size:14px;font-weight:500;text-align:center;opacity:0.9;margin-bottom:4px}.loading-description{color:rgba(255, 255, 255, 0.7);font-size:12px;text-align:center;opacity:0.8}@keyframes spin{0%{transform:rotate(0deg)}100%{transform:rotate(360deg)}}.performance-monitor{position:absolute;top:70px;left:16px;background:rgba(0, 0, 0, 0.25);backdrop-filter:blur(20px);border-radius:12px;border:1px solid rgba(255, 255, 255, 0.1);z-index:35;width:fit-content;animation:slideInFromLeft 0.3s ease-out;transition:all 0.3s ease}@keyframes slideInFromLeft{0%{opacity:0;transform:translateX(-20px) scale(0.95)}100%{opacity:1;transform:translateX(0) scale(1)}}.performance-expanded{padding:6px 10px}.metrics-row{display:flex;gap:8px;margin-bottom:4px}.metrics-row:last-child{margin-bottom:0}.metric-compact{display:flex;flex-direction:column;align-items:center;gap:2px;min-width:50px}.metric-label{font-size:9px;color:rgba(255, 255, 255, 0.6);font-weight:500;text-transform:uppercase;letter-spacing:0.3px}.metric-value{font-size:10px;font-weight:600;font-family:'Monaco', 'Menlo', 'Consolas', monospace;padding:2px 4px;border-radius:3px;text-align:center;white-space:nowrap}.metric-value.good{color:#4ade80;background:rgba(74, 222, 128, 0.1);border:1px solid rgba(74, 222, 128, 0.2)}.metric-value.warning{color:#fbbf24;background:rgba(251, 191, 36, 0.1);border:1px solid rgba(251, 191, 36, 0.2)}.metric-value.danger{color:#f87171;background:rgba(248, 113, 113, 0.1);border:1px solid rgba(248, 113, 113, 0.2)}@media (max-width: 480px){.performance-monitor{top:60px;left:12px;width:fit-content}.performance-expanded{padding:4px 8px}.metrics-row{gap:6px;margin-bottom:3px}.metric-compact{min-width:45px;gap:1px}.metric-label{font-size:8px}.metric-value{font-size:9px;padding:1px 3px}}@keyframes pulse{0%,100%{opacity:1}50%{opacity:0.7}}.metric-value.danger{animation:pulse 2s infinite}";const m=class{constructor(i){e(this,i);this.captureCompleted=t(this,"captureCompleted");this.isReady=t(this,"isReady")}get el(){return i(this)}debug=false;alignmentTolerance=10;maskSize=90;cropMargin=0;useDocumentClassification=false;preferredCamera="auto";captureCompleted;isReady;detectionBoxes=[];sideAlignment={top:false,right:false,bottom:false,left:false};isMaskReady=false;shouldMirrorVideo=true;showCameraSelector=false;isSwitchingCamera=false;currentStatus={message:"Inicializando componente...",description:"Configurando servicios y cargando recursos",type:"initializing",isInitialized:false};performanceData={fps:0,inferenceTime:0,memoryUsage:0,onnxLoadTime:0,frameProcessingTime:0,totalDetections:0,successfulDetections:0,detectionRate:0};serviceContainer;logger;eventBus;stateManager;cameraService;detectionService;videoRef;detectionContainer;videoStream;animationId;lastDetectedBox;startTime;hasScreenshotTaken=false;alignmentStartTime;alignmentTimer;performanceMetrics={fps:0,inferenceTime:0,memoryUsage:0,cpuUsage:0,onnxLoadTime:0,frameProcessingTime:0,totalDetections:0,successfulDetections:0,detectionRate:0,lastUpdateTime:0};performanceUpdateInterval;frameSkipCounter=0;FRAME_SKIP=2;consecutiveFailures=0;MAX_FAILURES=30;lastInferenceTime=0;MIN_INFERENCE_INTERVAL=50;async componentDidLoad(){this.updateStatus("Iniciando servicios...","Configurando módulos internos","initializing");await this.initializeServices();this.updateStatus("Configurando eventos...","Preparando comunicación entre servicios","initializing");await this.setupEventListeners();this.updateStatus("Inicializando cámara...","Detectando dispositivos disponibles","initializing");await this.initializeComponent();if(this.debug){this.initializePerformanceMonitor()}}async initializeServices(){const e={debug:this.debug,alignmentTolerance:this.alignmentTolerance,maskSize:this.maskSize,cropMargin:this.cropMargin,useDocumentClassification:this.useDocumentClassification,preferredCamera:this.preferredCamera};this.serviceContainer=new f(e);this.logger=this.serviceContainer.getLogger();this.eventBus=this.serviceContainer.getEventBus();this.stateManager=this.serviceContainer.getStateManager();this.cameraService=this.serviceContainer.getCameraService();this.detectionService=this.serviceContainer.getDetectionService();this.logger.state("SERVICIOS_INICIALIZADOS",{timestamp:Date.now()})}async setupEventListeners(){this.eventBus.on("state-changed",(e=>{this.handleStateChange(e)}));this.eventBus.on("camera-changed",(e=>{this.logger.state("CAMARA_CAMBIADA_EVENT",{cameraId:e})}));this.eventBus.on("error",(e=>{this.logger.error("Error en servicio:",e)}))}async initializeComponent(){this.logger.state("COMPONENTE_INICIALIZANDO",{debug:this.debug,maskSize:this.maskSize,cropMargin:this.cropMargin,useDocumentClassification:this.useDocumentClassification,preferredCamera:this.preferredCamera});this.validateProps();if(this.debug){this.stateManager.updateCaptureState({isLoading:true});await new Promise((e=>setTimeout(e,500)))}this.updateStatus("Detectando cámaras...","Buscando dispositivos de captura","initializing");await this.cameraService.detectDeviceType();await this.cameraService.enumerateDevices();this.updateStatus("Cargando modelo IA...","Preparando reconocimiento de documentos","initializing");await this.loadOnnxRuntime();this.initializeResizeObserver()}validateProps(){if(this.maskSize<50||this.maskSize>100){this.logger.warn(`Propiedad maskSize inválida. Valor: ${this.maskSize}, esperado: 50-100. Usando valor por defecto: 90`);this.maskSize=90}if(this.cropMargin<0||this.cropMargin>100){this.logger.warn(`Propiedad cropMargin inválida. Valor: ${this.cropMargin}, esperado: 0-100. Usando valor por defecto: 0`);this.cropMargin=0}const e=["auto","front","back"];if(!e.includes(this.preferredCamera)){this.logger.warn(`Propiedad preferredCamera inválida. Valor: ${this.preferredCamera}, esperado: ${e.join(", ")}. Usando valor por defecto: 'auto'`);this.preferredCamera="auto"}}async loadOnnxRuntime(){if(!window.ort){this.updateStatus("Descargando librerías...","Obteniendo recursos de reconocimiento","initializing");const e=document.createElement("script");e.src="https://cdn.jsdelivr.net/npm/onnxruntime-web/dist/ort.min.js";document.head.appendChild(e);await new Promise((t=>{e.onload=()=>{setTimeout((()=>{this.finalizeInitialization();t(undefined)}),300)}}))}else{setTimeout((()=>{this.finalizeInitialization()}),300)}}finalizeInitialization(){this.stateManager.updateCaptureState({isLoading:false});this.currentStatus={message:"Listo para capturar",description:"",type:"ready",isInitialized:true};this.emitReadyEvent()}updateStatus(e,t,i="loading"){this.currentStatus={message:e,description:t,type:i,isInitialized:this.currentStatus.isInitialized}}emitReadyEvent(){const e=!!window.ort&&this.detectionService.isModelLoaded();this.isReady.emit(e);this.logger.state("COMPONENTE_LISTO",{ortLibraryLoaded:!!window.ort,modelPreloaded:this.detectionService.isModelLoaded(),isReady:e})}handleStateChange(e){}initializeResizeObserver(){if("ResizeObserver"in window&&this.detectionContainer){const e=new ResizeObserver((()=>{this.handleResize()}));e.observe(this.detectionContainer.parentElement)}}handleResize(){if(this.detectionContainer){const e=this.detectionContainer.parentElement;const t=e.getBoundingClientRect();this.updateMaskDimensions(t);this.logger.debug("Container redimensionado:",{width:t.width,height:t.height})}}updateMaskDimensions(e){if(!this.videoRef)return;const t=this.videoRef.videoWidth;const i=this.videoRef.videoHeight;if(t===0||i===0)return;const s=t/i;const a=e.width/e.height;let o,r;let n=0,c=0;if(s>a){o=e.width;r=e.width/s;c=(e.height-r)/2}else{r=e.height;o=e.height*s;n=(e.width-o)/2}const d=85.6/53.98;const l=r*d;let h,f;const p=this.maskSize/100;if(l<=o){f=r*p;h=f*d}else{h=o*p;f=h/d}const m=h/e.width*100;const u=f/e.height*100;const g=n+o/2;const b=c+r/2;const x=g/e.width*100;const w=b/e.height*100;this.el.style.setProperty("--mask-width",`${m}%`);this.el.style.setProperty("--mask-height",`${u}%`);this.el.style.setProperty("--mask-center-x",`${x}%`);this.el.style.setProperty("--mask-center-y",`${w}%`);this.isMaskReady=true;this.logger.state("DIMENSIONES_MASCARA_ACTUALIZADAS",{video:{width:t,height:i},displayed:{width:o,height:r},mask:{widthPercent:m,heightPercent:u},center:{x,y:w},offset:{x:n,y:c}})}async getCapturedImages(){const e=this.stateManager.getCaptureState();if(e.step!=="completed"){throw new Error("El proceso de captura no ha sido completado")}return this.stateManager.getCapturedImages()}async isProcessCompleted(){return this.stateManager.isProcessCompleted()}async startCapture(){await this.startDetection()}async stopCapture(){this.exitSession()}async resetCapture(){this.resetDetection()}async skipBackCapture(){const e=this.stateManager.getCaptureState();const t=this.stateManager.getCapturedImages();if(e.step==="back"&&t.front.fullFrame){this.completeProcess(true)}}async getStatus(){const e=this.stateManager.getCaptureState();const t=this.stateManager.getCapturedImages();return{isVideoActive:e.isVideoActive,captureStep:e.step,hasImages:!!(t.front.fullFrame||t.back.fullFrame),isProcessCompleted:this.stateManager.isProcessCompleted(),isModelPreloaded:this.detectionService.isModelLoaded()}}async preloadModel(){if(this.detectionService.isModelLoaded()){this.logger.state("MODELO_YA_PRECARGADO");this.updateStatus("Modelos ya cargados","","ready");return{success:true,message:"Model already loaded"}}try{const e=performance.now();this.updateStatus("Descargando modelo de detección...","Obteniendo red neuronal para reconocimiento de documentos","loading");this.stateManager.updateCaptureState({isLoading:true});await this.detectionService.loadModel();this.updateStatus("Cargando clasificador...","Preparando modelo de clasificación de tipos de documento","loading");await this.detectionService.loadClassificationModel();const t=performance.now();const i=t-e;if(this.debug){this.recordOnnxPerformance(i,0)}this.updateStatus("Optimizando modelos...","Configurando parámetros de rendimiento","loading");await new Promise((e=>setTimeout(e,300)));this.updateStatus("Modelos precargados","","ready");this.stateManager.updateCaptureState({isLoading:false});this.emitReadyEvent();this.logger.state("MODELOS_PRECARGADOS_EXITOSAMENTE",{loadTime:Math.round(i)});return{success:true,message:"Models preloaded successfully"}}catch(e){this.logger.error("Error al precargar modelos:",e);this.updateStatus("Error al cargar modelos","No se pudieron descargar los recursos necesarios","error");this.stateManager.updateCaptureState({isLoading:false});return{success:false,error:e.message}}}async getCameraInfo(){return this.cameraService.getCameraInfo()}async setPreferredCamera(e){this.preferredCamera=e;this.serviceContainer.updateConfig({preferredCamera:e});await this.cameraService.enumerateDevices();const t=this.stateManager.getCaptureState();if(t.isVideoActive){const e=this.cameraService.getSelectedCameraId();if(e){await this.cameraService.switchCamera(e)}}return{success:true,selectedCamera:this.cameraService.getSelectedCameraId(),availableCameras:this.cameraService.getAvailableCameras().length}}async startDetection(){this.logger.state("INICIANDO_DETECCION");try{if(!this.detectionService.isModelLoaded()){const e=performance.now();this.updateStatus("Cargando modelo de detección...","Descargando red neuronal para reconocimiento","loading");this.stateManager.updateCaptureState({isLoading:true});await this.detectionService.loadModel();this.updateStatus("Cargando clasificador...","Preparando modelo de clasificación de documentos","loading");await this.detectionService.loadClassificationModel();const t=performance.now();const i=t-e;if(this.debug){this.recordOnnxPerformance(i,0)}this.logger.state("MODELOS_CARGADOS_EN_DETECCION",{loadTime:Math.round(i)})}this.updateStatus("Detectando cámaras...","Buscando dispositivos de captura disponibles","loading");await this.cameraService.enumerateDevices();this.updateStatus("Configurando cámara...","Estableciendo resolución y parámetros óptimos","loading");const e=await this.cameraService.setupCamera();this.updateStatus("Captura activa","Buscando documento en el marco de captura","active");await this.initializeVideoStream(e);if(this.detectionContainer){const e=this.detectionContainer.parentElement;const t=e.getBoundingClientRect();this.updateMaskDimensions(t)}this.startTime=Date.now();this.stateManager.updateCaptureState({isLoading:false});this.detectFrame()}catch(e){this.logger.error("Error al inicializar detección:",e);this.updateStatus("Error al iniciar captura","No se pudo completar la inicialización","error");this.stateManager.updateCaptureState({isLoading:false})}}async initializeVideoStream(e){if(this.videoRef){this.videoRef.srcObject=e;this.videoStream=e;const t=this.cameraService.isRearCamera(e);this.shouldMirrorVideo=!t;this.logger.state("CAMARA_CONFIGURADA",{isRearCamera:t,shouldMirrorVideo:this.shouldMirrorVideo});return new Promise((e=>{this.videoRef.onloadedmetadata=async()=>{await this.videoRef.play();this.stateManager.updateCaptureState({isVideoActive:true});e()}}))}else{throw new Error("Video element not available")}}async detectFrame(){try{const e=performance.now();const t=this.stateManager.getCaptureState();if(!this.videoRef||!this.detectionContainer||!this.detectionService.isModelLoaded())return;if(t.isDetectionPaused){if(t.step!=="completed"){this.animationId=requestAnimationFrame((()=>this.detectFrame()))}return}this.frameSkipCounter++;if(this.frameSkipCounter<=this.FRAME_SKIP){if(t.step!=="completed"){this.animationId=requestAnimationFrame((()=>this.detectFrame()))}return}this.frameSkipCounter=0;const i=Date.now();if(i-this.lastInferenceTime<this.MIN_INFERENCE_INTERVAL){if(t.step!=="completed"){this.animationId=requestAnimationFrame((()=>this.detectFrame()))}return}this.lastInferenceTime=i;const s=performance.now();const a=this.detectionService.preprocess(this.videoRef);const o=await this.detectionService.runInference(a);const r=performance.now()-s;if(this.debug){this.recordOnnxPerformance(0,r)}if(this.startTime&&Date.now()-this.startTime<5e3){o.forEach((e=>{const i=this.detectionService.isCardInFrame(e);const s=i?e.score*1.2:e.score;if(s>t.bestScore){this.stateManager.updateCaptureState({bestScore:s})}}))}if(o.length===0){this.consecutiveFailures++}else{this.consecutiveFailures=0}if(this.debug){this.updateDetectionBoxes(o)}else{this.detectionBoxes=[]}this.updateMaskColor(o);if(this.debug){const t=performance.now();const i=t-e;this.recordFrameProcessing(i,o.length)}if(t.step!=="completed"){if(this.consecutiveFailures>this.MAX_FAILURES){setTimeout((()=>this.detectFrame()),200)}else{this.animationId=requestAnimationFrame((()=>this.detectFrame()))}}}catch(e){this.logger.error("Error en inferencia de modelo:",e);const t=this.stateManager.getCaptureState();if(t.step!=="completed"){setTimeout((()=>this.detectFrame()),100)}}}disconnectedCallback(){this.cleanup()}cleanup(){if(this.animationId){cancelAnimationFrame(this.animationId)}if(this.videoStream){this.videoStream.getTracks().forEach((e=>e.stop()))}if(this.alignmentTimer){clearTimeout(this.alignmentTimer);this.alignmentTimer=undefined}if(this.performanceUpdateInterval){clearInterval(this.performanceUpdateInterval);this.performanceUpdateInterval=undefined}this.detectionBoxes=[];this.alignmentStartTime=undefined;this.serviceContainer?.cleanup()}render(){const e=this.stateManager?.getCaptureState()||{isVideoActive:false,showFlipAnimation:false,showSuccessAnimation:false,step:"front",isCapturing:false};const t=this.cameraService?.getCameraInfo()||{availableCameras:[],selectedCameraId:null,deviceType:"desktop"};return s("div",{key:"91be2bd01dfa62ef439ff4b3d029539248b69364",class:"detector-container"},s("div",{key:"7dbfbe2b782a8d06e2303ec3edff4b5c68ba4444",class:"video-container"},s("video",{key:"a2b7ec84a2f49935a94f1afb12deb1137f472fcd",ref:e=>this.videoRef=e,autoplay:true,muted:true,playsinline:true,class:this.shouldMirrorVideo?"mirror":"",style:{display:e.isVideoActive?"block":"none"}}),s("div",{key:"63cf0b7a05fc6d604f3a273fd902717542ba47f5",ref:e=>this.detectionContainer=e,class:`detection-overlay ${this.shouldMirrorVideo?"mirror":""}`},this.debug&&this.detectionBoxes.map(((e,t)=>s("div",{key:t,class:"detection-box",style:{position:"absolute",left:`${e.x}px`,top:`${e.y}px`,width:`${e.w}px`,height:`${e.h}px`,border:"2px solid #32406C",pointerEvents:"none",boxSizing:"border-box"}})))),this.isMaskReady&&s("div",{key:"089227de9415e523e99261eeeca5dcb981816a83",class:"overlay-mask"},s("div",{key:"846bd914025e8da92ca85381a566c6d93caea2ea",class:"card-outline"},s("div",{key:"1f49ae76e9066a65e848e55e19839547d8b9bef0",class:"side side-top"}),s("div",{key:"5b69c6bcf604042f86383e9bf122204d4fe1aeb1",class:"side side-right"}),s("div",{key:"8aa4c1f09c8b7046a369ed8d7fae1a96a525f778",class:"side side-bottom"}),s("div",{key:"d8826c0347a219c154703d165ee5b4ced33f7a3f",class:"side side-left"}),!e.showFlipAnimation&&!e.showSuccessAnimation&&s("div",{key:"916e038d0d0f80971629916189ef39c2689e4e43",class:"guide-text"},"Alinee su identificación con el marco")),e.step==="back"&&!e.showFlipAnimation&&!e.showSuccessAnimation&&s("button",{key:"02b01dff08bdbf042c6df1403f955601964bff28",class:"skip-button",onClick:()=>this.skipBackCapture(),type:"button"},"Saltar reverso"),e.isVideoActive&&s("div",{key:"cd88fd0567e5b792ddc623c281616ea35616f8f6",class:"camera-controls"},s("button",{key:"9d2a63a53926d0c987e551bd49caa8143b4e15d4",class:`camera-selector-button ${this.isSwitchingCamera?"loading":""}`,onClick:()=>this.toggleCameraSelector(),type:"button",title:"Seleccionar cámara",disabled:this.isSwitchingCamera},this.isSwitchingCamera?s("div",{class:"button-spinner"}):"Cámaras")),this.showCameraSelector&&t.availableCameras.length>0&&s("div",{key:"43e9ea946f88498082339de62cf54cba352e765e",class:"camera-selector-dropdown"},s("div",{key:"9fe39e8fa32ee458e4261d3f0d8fd40d50fefcf4",class:"camera-selector-header"},s("span",{key:"edf45c7bd8c5a43df9aba3ff6310cc5d06bb1aa2"},"Seleccionar Cámara"),s("button",{key:"dd5d417bb5eadd3f3b488ecd901d8dfa075c29c8",class:"close-selector",onClick:()=>this.toggleCameraSelector(),type:"button"},"×")),s("div",{key:"8edcd693f137ddd4ada9654c01615a17c90cb9c3",class:"camera-list"},t.availableCameras.map((e=>s("button",{key:e.id,class:`camera-option ${t.selectedCameraId===e.id?"selected":""}`,onClick:()=>this.handleCameraSwitch(e.id),type:"button"},s("span",{class:"camera-label"},e.label||`Cámara ${t.availableCameras.indexOf(e)+1}`),t.selectedCameraId===e.id&&s("span",{class:"selected-indicator"},"✓"))))),s("div",{key:"b7f050513f25af03ca72a735bd8745f71a456d76",class:"device-info"},s("small",{key:"34305a7ca3418d46bceb2a9923a664fd5a0062fa"},"Dispositivo: ",t.deviceType)))),e.isCapturing&&s("div",{key:"54ec511bd6d22c8c25eb41717abcebd89ed315f8",class:"capture-animation"}),e.showFlipAnimation&&s("div",{key:"9392bccfd3576b1858610b543c0ac0b747cef153",class:"flip-animation"},s("div",{key:"030dc63ea455afa24fd0b370425aae14ab322613",class:"id-card-icon"}),s("div",{key:"3f7d8985af2d6eaec21699b87d5aeb0b27d2d55e",class:"flip-text"},"¡Voltea tu identificación!")),e.showSuccessAnimation&&s("div",{key:"0da0a234e87013fb2ccc652cb920318cf00b6733",class:"success-animation"},s("div",{key:"f6b62ff21e147c8b365ab8c88e42dcb106cb8743",class:"check-icon"}),s("div",{key:"8c8b56561234e140cd658c5fad252cd6a1e0251c",class:"success-text"},"¡Proceso completado!")),s("div",{key:"684119ec540276cc28a49724d6177c72aff74b02",class:`component-status status-${this.currentStatus.type}`},(this.currentStatus.type==="loading"||this.currentStatus.type==="initializing")&&s("div",{key:"abc9a89349b9d8d76e03c765aa0084ec364f73a8",class:"status-spinner"}),s("div",{key:"6903b83962ee971a95a18e9ac793d872a7ce4551",class:"status-content"},s("div",{key:"c01dd0268a223ee269b616cea19a9cdb69cc048f",class:"status-message"},this.currentStatus.message),this.currentStatus.description&&s("div",{key:"1f6ebecc7a1d058c34aca5b4572ace8a81b5ab69",class:"status-description"},this.currentStatus.description))),this.debug&&s("div",{key:"eff03d22449aafac6c986e71af2aafd487b1776d",class:"performance-monitor"},s("div",{key:"66dfd57c92f7ee67e7c52293b3ad3f1105793e70",class:"performance-expanded"},s("div",{key:"0d952f89275e551a1e4cdb9b215e1ba48776ebd4",class:"metrics-row"},s("div",{key:"36fab2101078e4da16a3a3ac50d927077d9e0874",class:"metric-compact"},s("span",{key:"a694bf26ff69522a6462963d735ffa145513d84c",class:"metric-label"},"FPS"),s("span",{key:"02a250b3863f8f158a4103e0817cf14accdb7b30",class:`metric-value ${this.performanceData.fps<15?"warning":this.performanceData.fps<10?"danger":"good"}`},this.performanceData.fps)),s("div",{key:"d6e849779f13ae0ee3641d98148f0ab151877157",class:"metric-compact"},s("span",{key:"983411baa6e79e3bfe7ad277879361c023dc6d48",class:"metric-label"},"MEM"),s("span",{key:"eea7060b9cef780ed47a534b7bc69c94162b67d9",class:`metric-value ${this.performanceData.memoryUsage>100?"warning":this.performanceData.memoryUsage>200?"danger":"good"}`},this.performanceData.memoryUsage,"MB"))),s("div",{key:"e0e5e80c208f1eadc606c20df844efcc66f6599c",class:"metrics-row"},s("div",{key:"d27ae06d877097c99c1796c36284427861ae0be3",class:"metric-compact"},s("span",{key:"229837e3037ce64fc5930a5732ad6a0e323371e9",class:"metric-label"},"INF"),s("span",{key:"82002332976cddf7dbb2aa6346ad9da0ce0901df",class:`metric-value ${this.performanceData.inferenceTime>100?"warning":this.performanceData.inferenceTime>200?"danger":"good"}`},this.performanceData.inferenceTime,"ms")),s("div",{key:"243afc7933e169084eda28cca270198420a4341d",class:"metric-compact"},s("span",{key:"834e9a69791abc42545dddc1c0de7372a3292d60",class:"metric-label"},"FRAME"),s("span",{key:"9106f8989818aa9111d7571006224befe7837406",class:`metric-value ${this.performanceData.frameProcessingTime>50?"warning":this.performanceData.frameProcessingTime>100?"danger":"good"}`},this.performanceData.frameProcessingTime,"ms"))),s("div",{key:"da911fc95ce6151fb5fb64b8d5df53d6b693ffef",class:"metrics-row"},s("div",{key:"c813a7152d51bd2402b9c2bdf5bd1fe09b6df7db",class:"metric-compact"},s("span",{key:"b5a7807764a897663ce9d37111188c26bcf2cb6d",class:"metric-label"},"DET"),s("span",{key:"17ec09407ac24800e0ec965c7b3a037730a6afd4",class:"metric-value good"},this.performanceData.successfulDetections,"/",this.performanceData.totalDetections)),s("div",{key:"5aa0b6f4d1643e3ec224d1a9d89d4d37407d3c58",class:"metric-compact"},s("span",{key:"6b8efdcf07e9eac7d5036a508804b017ccedaea9",class:"metric-label"},"RATE"),s("span",{key:"038a5c3377185784c806c614f0c098e417360acf",class:`metric-value ${this.performanceData.detectionRate<30?"danger":this.performanceData.detectionRate<60?"warning":"good"}`},this.performanceData.detectionRate,"%"))))),s("div",{key:"3317e18916b62bce70cc02540cfc274d588b466a",class:"watermark"},s("img",{key:"adf070f99c17c3fdfc3e2c69b1a63dfef66ad71e",src:"https://storage.googleapis.com/jaak-static/commons/powered-by-jaak.png",alt:"Powered by Jaak"}))))}updateDetectionBoxes(e){if(!this.videoRef||!this.detectionContainer)return;const t=this.videoRef.videoWidth;const i=this.videoRef.videoHeight;const s=this.detectionContainer.parentElement;const a=s.getBoundingClientRect();const o=a.width;const r=a.height;if(t===0||i===0)return;const n=t/i;const c=o/r;let d,l;let h=0,f=0;if(n>c){d=o;l=o/n;f=(r-l)/2}else{l=r;d=r*n;h=(o-d)/2}const p=320;const m=d/p;const u=l/p;this.detectionBoxes=e.map((e=>({x:e.x*m+h,y:e.y*u+f,w:e.w*m,h:e.h*u,score:e.score})))}updateMaskColor(e){const t=this.el.shadowRoot?.querySelector(".card-outline");const i=this.el.shadowRoot?.querySelectorAll(".corner");const s=this.el.shadowRoot?.querySelector(".side-top");const a=this.el.shadowRoot?.querySelector(".side-right");const o=this.el.shadowRoot?.querySelector(".side-bottom");const r=this.el.shadowRoot?.querySelector(".side-left");let n=null;let c={top:false,right:false,bottom:false,left:false};if(e.length>0){n=e.reduce(((e,t)=>t.score>e.score?t:e));const t={INPUT_SIZE:320,ID1_ASPECT_RATIO:85.6/53.98,shouldMirrorVideo:this.shouldMirrorVideo,alignmentTolerance:this.alignmentTolerance,maskSize:this.maskSize,videoRef:this.videoRef};c=this.detectionService.checkSideAlignment(n,t);this.sideAlignment=c}else{this.sideAlignment={top:false,right:false,bottom:false,left:false}}s?.classList.toggle("aligned",c.top);a?.classList.toggle("aligned",c.right);o?.classList.toggle("aligned",c.bottom);r?.classList.toggle("aligned",c.left);const d=this.detectionService.areAllSidesAligned(c);if(d&&n){t?.classList.add("perfect-match");i?.forEach((e=>e.classList.add("perfect-match")));if(!this.hasScreenshotTaken){const e=Date.now();if(!this.alignmentStartTime){this.alignmentStartTime=e}const t=e-this.alignmentStartTime;if(t>=1e3){this.lastDetectedBox=n;this.takeScreenshot().catch((e=>{this.logger.error("Error al tomar captura de pantalla:",e)}));this.hasScreenshotTaken=true;this.alignmentStartTime=undefined;setTimeout((()=>{this.hasScreenshotTaken=false}),2e3)}}}else{t?.classList.remove("perfect-match");i?.forEach((e=>e.classList.remove("perfect-match")));if(this.alignmentStartTime){this.alignmentStartTime=undefined}if(this.alignmentTimer){clearTimeout(this.alignmentTimer);this.alignmentTimer=undefined}}}async takeScreenshot(){if(!this.videoRef||!this.lastDetectedBox)return;this.logger.state("INICIANDO_CAPTURA",{captureStep:this.stateManager.getCaptureState().step,detectedBox:this.lastDetectedBox,videoResolution:{width:this.videoRef.videoWidth,height:this.videoRef.videoHeight}});this.stateManager.updateCaptureState({isCapturing:true});this.triggerCaptureAnimation();const e=document.createElement("canvas");e.width=this.videoRef.videoWidth;e.height=this.videoRef.videoHeight;const t=e.getContext("2d",{alpha:false});t.drawImage(this.videoRef,0,0,e.width,e.height);const i=320;const s=this.videoRef.videoWidth/i;const a=this.videoRef.videoHeight/i;const o=Math.max(0,this.lastDetectedBox.x*s-this.cropMargin);const r=Math.max(0,this.lastDetectedBox.y*a-this.cropMargin);const n=Math.min(this.lastDetectedBox.w*s+2*this.cropMargin,this.videoRef.videoWidth-o);const c=Math.min(this.lastDetectedBox.h*a+2*this.cropMargin,this.videoRef.videoHeight-r);const d=document.createElement("canvas");d.width=n;d.height=c;const l=d.getContext("2d",{alpha:false});l.drawImage(this.videoRef,o,r,n,c,0,0,n,c);const h=this.stateManager.getCaptureState();if(h.step==="front"){this.stateManager.setCapturedImages({front:{fullFrame:e.toDataURL("image/png"),cropped:d.toDataURL("image/png")}});if(this.useDocumentClassification){const e=await this.detectionService.classifyDocument(d);if(e&&e.class==="passport"){this.logger.state("PASAPORTE_DETECTADO_SALTANDO_REVERSO",{classification:e?.class});this.completeProcess(true);return}}this.stateManager.updateCaptureState({step:"back",isDetectionPaused:true,showFlipAnimation:true});setTimeout((()=>{this.stateManager.updateCaptureState({showFlipAnimation:false,isDetectionPaused:false})}),3e3)}else if(h.step==="back"){this.stateManager.setCapturedImages({back:{fullFrame:e.toDataURL("image/png"),cropped:d.toDataURL("image/png")}});this.completeProcess(false)}}triggerCaptureAnimation(){const e=this.el.shadowRoot?.querySelector(".card-outline");e?.classList.add("capturing");setTimeout((()=>{this.stateManager.updateCaptureState({isCapturing:false});e?.classList.remove("capturing")}),600)}completeProcess(e=false){this.stateManager.updateCaptureState({step:"completed",showSuccessAnimation:true});const t=this.stateManager.getCapturedImages();t.metadata.processCompleted=true;t.metadata.backCaptureSkipped=e;this.stateManager.setCapturedImages(t);this.stopDetection();this.updateStatus("Proceso completado",`${t.metadata.totalImages} imágenes capturadas exitosamente`,"ready");const i={...t,timestamp:(new Date).toISOString()};this.captureCompleted.emit(i);setTimeout((()=>{this.stateManager.updateCaptureState({showSuccessAnimation:false})}),3e3);this.logger.state("PROCESO_COMPLETADO",{skippedBack:e,totalImages:t.metadata.totalImages,timestamp:(new Date).toISOString()})}stopDetection(){if(this.animationId){cancelAnimationFrame(this.animationId);this.animationId=undefined}this.detectionBoxes=[];this.logger.state("DETECTOR_DETENIDO",{timestamp:Date.now()})}toggleCameraSelector(){if(this.isSwitchingCamera)return;this.showCameraSelector=!this.showCameraSelector}async handleCameraSwitch(e){if(this.isSwitchingCamera)return;try{this.showCameraSelector=false;this.isSwitchingCamera=true;this.logger.state("INICIANDO_CAMBIO_CAMARA",{from:this.cameraService.getSelectedCameraId(),to:e});if(this.videoStream){this.videoStream.getTracks().forEach((e=>e.stop()))}await this.cameraService.switchCamera(e);const t=await this.cameraService.setupCamera();await this.initializeVideoStream(t);this.logger.state("CAMBIO_CAMARA_EXITOSO",{newCameraId:e,isRearCamera:this.cameraService.isRearCamera(t)})}catch(e){this.logger.error("Error al cambiar cámara:",e);try{const e=await this.cameraService.setupCamera();await this.initializeVideoStream(e)}catch(e){this.logger.error("Error al restaurar cámara anterior:",e);this.updateStatus("Error al cambiar cámara","No se pudo completar el cambio de dispositivo","error")}}finally{this.isSwitchingCamera=false}}resetDetection(){const e=this.stateManager.getCaptureState();const t=e.isVideoActive;this.stateManager.reset();if(t){this.stateManager.updateCaptureState({isVideoActive:true})}this.hasScreenshotTaken=false;this.startTime=Date.now();this.frameSkipCounter=0;this.consecutiveFailures=0;this.lastInferenceTime=0;this.detectionBoxes=[];this.alignmentStartTime=undefined;if(this.alignmentTimer){clearTimeout(this.alignmentTimer);this.alignmentTimer=undefined}if(t&&this.detectionService.isModelLoaded()){this.updateStatus("Captura reiniciada","Buscando documento en el marco de captura","active");this.detectFrame()}else{this.updateStatus("Listo para capturar","","ready")}}exitSession(){if(this.videoStream){this.videoStream.getTracks().forEach((e=>e.stop()));this.videoStream=undefined;this.stateManager.updateCaptureState({isVideoActive:false,isLoading:false})}this.isMaskReady=false;this.updateStatus("Sesión finalizada","","ready");this.detectionBoxes=[];this.cleanup()}initializePerformanceMonitor(){this.performanceMetrics.lastUpdateTime=performance.now();this.performanceUpdateInterval=window.setInterval((()=>{this.updatePerformanceMetrics()}),500);this.logger.debug("Monitor de performance inicializado")}updatePerformanceMetrics(){const e=performance.now();const t=e-this.performanceMetrics.lastUpdateTime;if(t>0){this.performanceMetrics.fps=Math.round(1e3/(t/this.frameSkipCounter||1))}if("memory"in performance){const e=performance.memory;this.performanceMetrics.memoryUsage=Math.round(e.usedJSHeapSize/1048576)}if(this.performanceMetrics.totalDetections>0){this.performanceMetrics.successfulDetections=this.performanceMetrics.successfulDetections;const e=this.performanceMetrics.successfulDetections/this.performanceMetrics.totalDetections*100;this.performanceMetrics.detectionRate=Math.round(e)}this.performanceData={fps:this.performanceMetrics.fps,inferenceTime:this.performanceMetrics.inferenceTime,memoryUsage:this.performanceMetrics.memoryUsage,onnxLoadTime:this.performanceMetrics.onnxLoadTime,frameProcessingTime:this.performanceMetrics.frameProcessingTime,totalDetections:this.performanceMetrics.totalDetections,successfulDetections:this.performanceMetrics.successfulDetections,detectionRate:this.performanceMetrics.detectionRate};this.performanceMetrics.lastUpdateTime=e}recordOnnxPerformance(e,t){this.performanceMetrics.onnxLoadTime=Math.round(e);this.performanceMetrics.inferenceTime=Math.round(t)}recordFrameProcessing(e,t){this.performanceMetrics.frameProcessingTime=Math.round(e);this.performanceMetrics.totalDetections++;if(t>0){this.performanceMetrics.successfulDetections++}}};m.style=p;export{m as jaak_stamps};
|
|
2
|
-
//# sourceMappingURL=p-c30c7b47.entry.js.map
|