@jaak.ai/stamps 2.0.0-dev.56 → 2.0.0-dev.58
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/jaak-stamps-webcomponent.cjs.js +1 -1
- package/dist/cjs/jaak-stamps.cjs.entry.js +547 -612
- 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 +14 -2
- package/dist/collection/components/my-component/my-component.js +347 -238
- package/dist/collection/components/my-component/my-component.js.map +1 -1
- package/dist/collection/services/CameraService.js +156 -344
- package/dist/collection/services/CameraService.js.map +1 -1
- package/dist/collection/services/DetectionService.js +53 -38
- package/dist/collection/services/DetectionService.js.map +1 -1
- package/dist/collection/services/EventBusService.js +1 -1
- package/dist/collection/services/EventBusService.js.map +1 -1
- package/dist/collection/services/ServiceContainer.js +2 -12
- package/dist/collection/services/ServiceContainer.js.map +1 -1
- package/dist/collection/services/interfaces/ICameraService.js.map +1 -1
- package/dist/collection/types/component-types.js.map +1 -1
- package/dist/components/jaak-stamps.js +549 -615
- 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 +547 -612
- 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-39560f23.entry.js +2 -0
- package/dist/jaak-stamps-webcomponent/p-39560f23.entry.js.map +1 -0
- package/dist/types/components/my-component/my-component.d.ts +62 -17
- package/dist/types/components.d.ts +6 -8
- package/dist/types/services/CameraService.d.ts +12 -14
- package/dist/types/services/DetectionService.d.ts +9 -3
- package/dist/types/services/ServiceContainer.d.ts +0 -2
- package/dist/types/services/interfaces/ICameraService.d.ts +3 -12
- package/dist/types/types/component-types.d.ts +3 -0
- package/package.json +2 -2
- package/dist/collection/services/LoggerService.js +0 -40
- package/dist/collection/services/LoggerService.js.map +0 -1
- package/dist/collection/services/interfaces/ILogger.js +0 -2
- package/dist/collection/services/interfaces/ILogger.js.map +0 -1
- package/dist/jaak-stamps-webcomponent/p-65990bdd.entry.js +0 -2
- package/dist/jaak-stamps-webcomponent/p-65990bdd.entry.js.map +0 -1
- package/dist/types/services/LoggerService.d.ts +0 -12
- package/dist/types/services/interfaces/ILogger.d.ts +0 -8
|
@@ -23,9 +23,16 @@ export class JaakStamps {
|
|
|
23
23
|
showCameraSelector = false;
|
|
24
24
|
isSwitchingCamera = false;
|
|
25
25
|
hasDocumentDetected = false;
|
|
26
|
+
cameraInfoWithAutofocus = {
|
|
27
|
+
availableCameras: [],
|
|
28
|
+
selectedCameraId: null,
|
|
29
|
+
deviceType: 'desktop',
|
|
30
|
+
isMultipleCamerasAvailable: false,
|
|
31
|
+
preferredFacing: null
|
|
32
|
+
};
|
|
26
33
|
currentStatus = {
|
|
27
|
-
message: '
|
|
28
|
-
description: 'Configurando
|
|
34
|
+
message: 'Preparando capturador...',
|
|
35
|
+
description: 'Configurando herramientas de captura',
|
|
29
36
|
type: 'initializing',
|
|
30
37
|
isInitialized: false
|
|
31
38
|
};
|
|
@@ -42,7 +49,6 @@ export class JaakStamps {
|
|
|
42
49
|
backDocumentTimerRemaining = 0;
|
|
43
50
|
// Services
|
|
44
51
|
serviceContainer;
|
|
45
|
-
logger;
|
|
46
52
|
eventBus;
|
|
47
53
|
stateManager;
|
|
48
54
|
cameraService;
|
|
@@ -73,19 +79,25 @@ export class JaakStamps {
|
|
|
73
79
|
lastUpdateTime: 0
|
|
74
80
|
};
|
|
75
81
|
performanceUpdateInterval;
|
|
76
|
-
// Performance optimization
|
|
82
|
+
// Performance optimization with adaptive frame skipping
|
|
77
83
|
frameSkipCounter = 0;
|
|
78
|
-
|
|
84
|
+
BASE_FRAME_SKIP = 2;
|
|
85
|
+
MAX_FRAME_SKIP = 8;
|
|
79
86
|
consecutiveFailures = 0;
|
|
80
87
|
MAX_FAILURES = 30;
|
|
81
88
|
lastInferenceTime = 0;
|
|
82
89
|
MIN_INFERENCE_INTERVAL = 50;
|
|
90
|
+
performanceHistory = [];
|
|
91
|
+
PERFORMANCE_HISTORY_SIZE = 10;
|
|
92
|
+
// Canvas pool for optimized screenshot capture
|
|
93
|
+
canvasPool = [];
|
|
94
|
+
MAX_CANVAS_POOL_SIZE = 3;
|
|
83
95
|
async componentDidLoad() {
|
|
84
|
-
this.updateStatus('
|
|
96
|
+
this.updateStatus('Preparando capturador...', 'Configurando herramientas de captura', 'initializing');
|
|
85
97
|
await this.initializeServices();
|
|
86
|
-
this.updateStatus('
|
|
98
|
+
this.updateStatus('Preparando funciones...', 'Configurando herramientas de trabajo', 'initializing');
|
|
87
99
|
await this.setupEventListeners();
|
|
88
|
-
this.updateStatus('
|
|
100
|
+
this.updateStatus('Configurando cámara...', 'Detectando dispositivos disponibles', 'initializing');
|
|
89
101
|
await this.initializeComponent();
|
|
90
102
|
if (this.debug) {
|
|
91
103
|
this.initializePerformanceMonitor();
|
|
@@ -102,33 +114,23 @@ export class JaakStamps {
|
|
|
102
114
|
captureDelay: this.captureDelay
|
|
103
115
|
};
|
|
104
116
|
this.serviceContainer = new ServiceContainer(config);
|
|
105
|
-
this.logger = this.serviceContainer.getLogger();
|
|
106
117
|
this.eventBus = this.serviceContainer.getEventBus();
|
|
107
118
|
this.stateManager = this.serviceContainer.getStateManager();
|
|
108
119
|
this.cameraService = this.serviceContainer.getCameraService();
|
|
109
120
|
this.detectionService = this.serviceContainer.getDetectionService();
|
|
110
|
-
this.logger.state('SERVICIOS_INICIALIZADOS', { timestamp: Date.now() });
|
|
111
121
|
}
|
|
112
122
|
async setupEventListeners() {
|
|
113
123
|
this.eventBus.on('state-changed', (data) => {
|
|
114
124
|
this.handleStateChange(data);
|
|
115
125
|
});
|
|
116
|
-
this.eventBus.on('camera-changed', (
|
|
117
|
-
|
|
126
|
+
this.eventBus.on('camera-changed', () => {
|
|
127
|
+
// Camera changed event
|
|
118
128
|
});
|
|
119
|
-
this.eventBus.on('error', (
|
|
120
|
-
|
|
129
|
+
this.eventBus.on('error', () => {
|
|
130
|
+
// Handle service errors
|
|
121
131
|
});
|
|
122
132
|
}
|
|
123
133
|
async initializeComponent() {
|
|
124
|
-
this.logger.state('COMPONENTE_INICIALIZANDO', {
|
|
125
|
-
debug: this.debug,
|
|
126
|
-
maskSize: this.maskSize,
|
|
127
|
-
cropMargin: this.cropMargin,
|
|
128
|
-
useDocumentClassification: this.useDocumentClassification,
|
|
129
|
-
preferredCamera: this.preferredCamera,
|
|
130
|
-
captureDelay: this.captureDelay
|
|
131
|
-
});
|
|
132
134
|
this.validateProps();
|
|
133
135
|
if (this.debug) {
|
|
134
136
|
this.stateManager.updateCaptureState({ isLoading: true });
|
|
@@ -137,32 +139,29 @@ export class JaakStamps {
|
|
|
137
139
|
this.updateStatus('Detectando cámaras...', 'Buscando dispositivos de captura', 'initializing');
|
|
138
140
|
await this.cameraService.detectDeviceType();
|
|
139
141
|
await this.cameraService.enumerateDevices();
|
|
140
|
-
this.
|
|
142
|
+
await this.updateCameraInfoWithAutofocus();
|
|
143
|
+
this.updateStatus('Preparando reconocimiento...', 'Configurando detección de documentos', 'initializing');
|
|
141
144
|
await this.loadOnnxRuntime();
|
|
142
145
|
this.initializeResizeObserver();
|
|
143
146
|
}
|
|
144
147
|
validateProps() {
|
|
145
148
|
if (this.maskSize < 50 || this.maskSize > 100) {
|
|
146
|
-
this.logger.warn(`Propiedad maskSize inválida. Valor: ${this.maskSize}, esperado: 50-100. Usando valor por defecto: 90`);
|
|
147
149
|
this.maskSize = 90;
|
|
148
150
|
}
|
|
149
151
|
if (this.cropMargin < 0 || this.cropMargin > 100) {
|
|
150
|
-
this.logger.warn(`Propiedad cropMargin inválida. Valor: ${this.cropMargin}, esperado: 0-100. Usando valor por defecto: 0`);
|
|
151
152
|
this.cropMargin = 0;
|
|
152
153
|
}
|
|
153
154
|
const validOptions = ['auto', 'front', 'back'];
|
|
154
155
|
if (!validOptions.includes(this.preferredCamera)) {
|
|
155
|
-
this.logger.warn(`Propiedad preferredCamera inválida. Valor: ${this.preferredCamera}, esperado: ${validOptions.join(', ')}. Usando valor por defecto: 'auto'`);
|
|
156
156
|
this.preferredCamera = 'auto';
|
|
157
157
|
}
|
|
158
158
|
if (this.captureDelay < 0 || this.captureDelay > 10000) {
|
|
159
|
-
this.logger.warn(`Propiedad captureDelay inválida. Valor: ${this.captureDelay}, esperado: 0-10000. Usando valor por defecto: 1500`);
|
|
160
159
|
this.captureDelay = 1500;
|
|
161
160
|
}
|
|
162
161
|
}
|
|
163
162
|
async loadOnnxRuntime() {
|
|
164
163
|
if (!window.ort) {
|
|
165
|
-
this.updateStatus('
|
|
164
|
+
this.updateStatus('Preparando herramientas...', 'Configurando funciones de captura', 'initializing');
|
|
166
165
|
const script = document.createElement('script');
|
|
167
166
|
script.src = 'https://cdn.jsdelivr.net/npm/onnxruntime-web/dist/ort.min.js';
|
|
168
167
|
document.head.appendChild(script);
|
|
@@ -202,11 +201,6 @@ export class JaakStamps {
|
|
|
202
201
|
emitReadyEvent() {
|
|
203
202
|
const isDocumentReady = !!window.ort && this.detectionService.isModelLoaded();
|
|
204
203
|
this.isReady.emit(isDocumentReady);
|
|
205
|
-
this.logger.state('COMPONENTE_LISTO', {
|
|
206
|
-
ortLibraryLoaded: !!window.ort,
|
|
207
|
-
modelPreloaded: this.detectionService.isModelLoaded(),
|
|
208
|
-
isReady: isDocumentReady
|
|
209
|
-
});
|
|
210
204
|
}
|
|
211
205
|
handleStateChange(data) {
|
|
212
206
|
// React to state changes from StateManager
|
|
@@ -229,7 +223,6 @@ export class JaakStamps {
|
|
|
229
223
|
const container = this.detectionContainer.parentElement;
|
|
230
224
|
const rect = container.getBoundingClientRect();
|
|
231
225
|
this.updateMaskDimensions(rect);
|
|
232
|
-
this.logger.debug('Container redimensionado:', { width: rect.width, height: rect.height });
|
|
233
226
|
}
|
|
234
227
|
}
|
|
235
228
|
updateMaskDimensions(containerRect) {
|
|
@@ -283,16 +276,38 @@ export class JaakStamps {
|
|
|
283
276
|
this.el.style.setProperty('--mask-center-x', `${videoCenterXPercent}%`);
|
|
284
277
|
this.el.style.setProperty('--mask-center-y', `${videoCenterYPercent}%`);
|
|
285
278
|
this.isMaskReady = true;
|
|
286
|
-
this.logger.state('DIMENSIONES_MASCARA_ACTUALIZADAS', {
|
|
287
|
-
video: { width: videoWidth, height: videoHeight },
|
|
288
|
-
displayed: { width: displayedVideoWidth, height: displayedVideoHeight },
|
|
289
|
-
mask: { widthPercent: maskWidthPercent, heightPercent: maskHeightPercent },
|
|
290
|
-
center: { x: videoCenterXPercent, y: videoCenterYPercent },
|
|
291
|
-
offset: { x: videoOffsetX, y: videoOffsetY }
|
|
292
|
-
});
|
|
293
279
|
}
|
|
294
280
|
// PUBLIC METHODS
|
|
281
|
+
// Helper method to check if component is ready for operations
|
|
282
|
+
isComponentReady() {
|
|
283
|
+
if (!this.currentStatus.isInitialized) {
|
|
284
|
+
return {
|
|
285
|
+
ready: false,
|
|
286
|
+
message: 'El componente aún se está inicializando',
|
|
287
|
+
status: 'initializing'
|
|
288
|
+
};
|
|
289
|
+
}
|
|
290
|
+
if (this.currentStatus.type === 'loading' || this.currentStatus.type === 'initializing') {
|
|
291
|
+
return {
|
|
292
|
+
ready: false,
|
|
293
|
+
message: 'El componente está cargando recursos',
|
|
294
|
+
status: 'loading'
|
|
295
|
+
};
|
|
296
|
+
}
|
|
297
|
+
if (!this.serviceContainer || !this.stateManager || !this.cameraService || !this.detectionService) {
|
|
298
|
+
return {
|
|
299
|
+
ready: false,
|
|
300
|
+
message: 'Los servicios del componente no están disponibles',
|
|
301
|
+
status: 'error'
|
|
302
|
+
};
|
|
303
|
+
}
|
|
304
|
+
return { ready: true };
|
|
305
|
+
}
|
|
295
306
|
async getCapturedImages() {
|
|
307
|
+
const readyCheck = this.isComponentReady();
|
|
308
|
+
if (!readyCheck.ready) {
|
|
309
|
+
throw new Error(readyCheck.message);
|
|
310
|
+
}
|
|
296
311
|
const state = this.stateManager.getCaptureState();
|
|
297
312
|
if (state.step !== 'completed') {
|
|
298
313
|
throw new Error('El proceso de captura no ha sido completado');
|
|
@@ -300,47 +315,128 @@ export class JaakStamps {
|
|
|
300
315
|
return this.stateManager.getCapturedImages();
|
|
301
316
|
}
|
|
302
317
|
async isProcessCompleted() {
|
|
318
|
+
const readyCheck = this.isComponentReady();
|
|
319
|
+
if (!readyCheck.ready) {
|
|
320
|
+
return false; // Return false instead of error for this method
|
|
321
|
+
}
|
|
303
322
|
return this.stateManager.isProcessCompleted();
|
|
304
323
|
}
|
|
305
324
|
async startCapture() {
|
|
306
|
-
|
|
325
|
+
const readyCheck = this.isComponentReady();
|
|
326
|
+
if (!readyCheck.ready) {
|
|
327
|
+
this.updateStatus(readyCheck.message, 'Espere a que termine la inicialización', readyCheck.status);
|
|
328
|
+
return { success: false, error: readyCheck.message };
|
|
329
|
+
}
|
|
330
|
+
try {
|
|
331
|
+
await this.startDetection();
|
|
332
|
+
return { success: true };
|
|
333
|
+
}
|
|
334
|
+
catch (error) {
|
|
335
|
+
this.updateStatus('Error al iniciar captura', error.message, 'error');
|
|
336
|
+
return { success: false, error: error.message };
|
|
337
|
+
}
|
|
307
338
|
}
|
|
308
339
|
async stopCapture() {
|
|
309
|
-
this.
|
|
340
|
+
const readyCheck = this.isComponentReady();
|
|
341
|
+
if (!readyCheck.ready) {
|
|
342
|
+
return { success: false, error: readyCheck.message };
|
|
343
|
+
}
|
|
344
|
+
try {
|
|
345
|
+
this.exitSession();
|
|
346
|
+
return { success: true };
|
|
347
|
+
}
|
|
348
|
+
catch (error) {
|
|
349
|
+
return { success: false, error: error.message };
|
|
350
|
+
}
|
|
310
351
|
}
|
|
311
352
|
async resetCapture() {
|
|
312
|
-
this.
|
|
353
|
+
const readyCheck = this.isComponentReady();
|
|
354
|
+
if (!readyCheck.ready) {
|
|
355
|
+
return { success: false, error: readyCheck.message };
|
|
356
|
+
}
|
|
357
|
+
try {
|
|
358
|
+
this.resetDetection();
|
|
359
|
+
return { success: true };
|
|
360
|
+
}
|
|
361
|
+
catch (error) {
|
|
362
|
+
return { success: false, error: error.message };
|
|
363
|
+
}
|
|
313
364
|
}
|
|
314
365
|
async skipBackCapture() {
|
|
315
|
-
const
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
366
|
+
const readyCheck = this.isComponentReady();
|
|
367
|
+
if (!readyCheck.ready) {
|
|
368
|
+
return { success: false, error: readyCheck.message };
|
|
369
|
+
}
|
|
370
|
+
try {
|
|
371
|
+
const captureState = this.stateManager.getCaptureState();
|
|
372
|
+
const capturedImages = this.stateManager.getCapturedImages();
|
|
373
|
+
if (captureState.step === 'back' && capturedImages.front.fullFrame) {
|
|
374
|
+
this.completeProcess(true);
|
|
375
|
+
return { success: true };
|
|
376
|
+
}
|
|
377
|
+
else {
|
|
378
|
+
return { success: false, error: 'No se puede saltar el reverso en el estado actual' };
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
catch (error) {
|
|
382
|
+
return { success: false, error: error.message };
|
|
319
383
|
}
|
|
320
384
|
}
|
|
321
385
|
async getStatus() {
|
|
322
|
-
const
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
386
|
+
const readyCheck = this.isComponentReady();
|
|
387
|
+
if (!readyCheck.ready) {
|
|
388
|
+
return {
|
|
389
|
+
isVideoActive: false,
|
|
390
|
+
captureStep: 'front',
|
|
391
|
+
hasImages: false,
|
|
392
|
+
isProcessCompleted: false,
|
|
393
|
+
isModelPreloaded: false,
|
|
394
|
+
componentStatus: readyCheck.status,
|
|
395
|
+
componentMessage: readyCheck.message
|
|
396
|
+
};
|
|
397
|
+
}
|
|
398
|
+
try {
|
|
399
|
+
const captureState = this.stateManager.getCaptureState();
|
|
400
|
+
const capturedImages = this.stateManager.getCapturedImages();
|
|
401
|
+
return {
|
|
402
|
+
isVideoActive: captureState.isVideoActive,
|
|
403
|
+
captureStep: captureState.step,
|
|
404
|
+
hasImages: !!(capturedImages.front.fullFrame || capturedImages.back.fullFrame),
|
|
405
|
+
isProcessCompleted: this.stateManager.isProcessCompleted(),
|
|
406
|
+
isModelPreloaded: this.detectionService.isModelLoaded(),
|
|
407
|
+
componentStatus: 'ready'
|
|
408
|
+
};
|
|
409
|
+
}
|
|
410
|
+
catch (error) {
|
|
411
|
+
return {
|
|
412
|
+
isVideoActive: false,
|
|
413
|
+
captureStep: 'front',
|
|
414
|
+
hasImages: false,
|
|
415
|
+
isProcessCompleted: false,
|
|
416
|
+
isModelPreloaded: false,
|
|
417
|
+
componentStatus: 'error',
|
|
418
|
+
componentMessage: error.message
|
|
419
|
+
};
|
|
420
|
+
}
|
|
331
421
|
}
|
|
332
422
|
async preloadModel() {
|
|
423
|
+
const readyCheck = this.isComponentReady();
|
|
424
|
+
if (!readyCheck.ready) {
|
|
425
|
+
this.updateStatus(readyCheck.message, 'Espere a que termine la inicialización', readyCheck.status);
|
|
426
|
+
return { success: false, error: readyCheck.message };
|
|
427
|
+
}
|
|
333
428
|
if (this.detectionService.isModelLoaded()) {
|
|
334
|
-
this.
|
|
335
|
-
this.updateStatus('Modelos ya cargados', '', 'ready');
|
|
429
|
+
this.updateStatus('Reconocimiento listo', '', 'ready');
|
|
336
430
|
return { success: true, message: 'Model already loaded' };
|
|
337
431
|
}
|
|
338
432
|
try {
|
|
339
433
|
const loadStartTime = performance.now();
|
|
340
|
-
this.updateStatus('
|
|
341
|
-
this.stateManager
|
|
434
|
+
this.updateStatus('Preparando reconocimiento...', 'Configurando detección de documentos', 'loading');
|
|
435
|
+
if (this.stateManager) {
|
|
436
|
+
this.stateManager.updateCaptureState({ isLoading: true });
|
|
437
|
+
}
|
|
342
438
|
await this.detectionService.loadModel();
|
|
343
|
-
this.updateStatus('
|
|
439
|
+
this.updateStatus('Optimizando detección...', 'Configurando reconocimiento de documentos', 'loading');
|
|
344
440
|
await this.detectionService.loadClassificationModel();
|
|
345
441
|
const loadEndTime = performance.now();
|
|
346
442
|
const loadTime = loadEndTime - loadStartTime;
|
|
@@ -348,80 +444,118 @@ export class JaakStamps {
|
|
|
348
444
|
if (this.debug) {
|
|
349
445
|
this.recordOnnxPerformance(loadTime, 0);
|
|
350
446
|
}
|
|
351
|
-
this.updateStatus('
|
|
447
|
+
this.updateStatus('Finalizando configuración...', 'Preparando herramientas para captura', 'loading');
|
|
352
448
|
await new Promise(resolve => setTimeout(resolve, 300));
|
|
353
|
-
this.updateStatus('
|
|
354
|
-
this.stateManager
|
|
449
|
+
this.updateStatus('Reconocimiento preparado', '', 'ready');
|
|
450
|
+
if (this.stateManager) {
|
|
451
|
+
this.stateManager.updateCaptureState({ isLoading: false });
|
|
452
|
+
}
|
|
355
453
|
this.emitReadyEvent();
|
|
356
|
-
this.logger.state('MODELOS_PRECARGADOS_EXITOSAMENTE', { loadTime: Math.round(loadTime) });
|
|
357
454
|
return { success: true, message: 'Models preloaded successfully' };
|
|
358
455
|
}
|
|
359
456
|
catch (error) {
|
|
360
|
-
this.
|
|
361
|
-
this.
|
|
362
|
-
|
|
457
|
+
this.updateStatus('Error de configuración', 'No se pudieron preparar las herramientas necesarias', 'error');
|
|
458
|
+
if (this.stateManager) {
|
|
459
|
+
this.stateManager.updateCaptureState({ isLoading: false });
|
|
460
|
+
}
|
|
363
461
|
return { success: false, error: error.message };
|
|
364
462
|
}
|
|
365
463
|
}
|
|
366
464
|
async getCameraInfo() {
|
|
367
|
-
|
|
465
|
+
const readyCheck = this.isComponentReady();
|
|
466
|
+
if (!readyCheck.ready) {
|
|
467
|
+
return {
|
|
468
|
+
availableCameras: [],
|
|
469
|
+
selectedCameraId: null,
|
|
470
|
+
deviceType: 'desktop',
|
|
471
|
+
isMultipleCamerasAvailable: false,
|
|
472
|
+
preferredFacing: null,
|
|
473
|
+
error: readyCheck.message
|
|
474
|
+
};
|
|
475
|
+
}
|
|
476
|
+
try {
|
|
477
|
+
return await this.cameraService.getCameraInfo();
|
|
478
|
+
}
|
|
479
|
+
catch (error) {
|
|
480
|
+
return {
|
|
481
|
+
availableCameras: [],
|
|
482
|
+
selectedCameraId: null,
|
|
483
|
+
deviceType: 'desktop',
|
|
484
|
+
isMultipleCamerasAvailable: false,
|
|
485
|
+
preferredFacing: null,
|
|
486
|
+
error: error.message
|
|
487
|
+
};
|
|
488
|
+
}
|
|
368
489
|
}
|
|
369
490
|
async setPreferredCamera(camera) {
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
491
|
+
const readyCheck = this.isComponentReady();
|
|
492
|
+
if (!readyCheck.ready) {
|
|
493
|
+
return {
|
|
494
|
+
success: false,
|
|
495
|
+
error: readyCheck.message,
|
|
496
|
+
selectedCamera: null,
|
|
497
|
+
availableCameras: 0
|
|
498
|
+
};
|
|
499
|
+
}
|
|
500
|
+
try {
|
|
501
|
+
this.preferredCamera = camera;
|
|
502
|
+
this.serviceContainer.updateConfig({ preferredCamera: camera });
|
|
503
|
+
await this.cameraService.enumerateDevices();
|
|
504
|
+
await this.updateCameraInfoWithAutofocus();
|
|
505
|
+
const captureState = this.stateManager.getCaptureState();
|
|
506
|
+
if (captureState.isVideoActive) {
|
|
507
|
+
const selectedCameraId = this.cameraService.getSelectedCameraId();
|
|
508
|
+
if (selectedCameraId) {
|
|
509
|
+
await this.cameraService.switchCamera(selectedCameraId);
|
|
510
|
+
}
|
|
378
511
|
}
|
|
512
|
+
return {
|
|
513
|
+
success: true,
|
|
514
|
+
selectedCamera: this.cameraService.getSelectedCameraId(),
|
|
515
|
+
availableCameras: this.cameraService.getAvailableCameras().length
|
|
516
|
+
};
|
|
517
|
+
}
|
|
518
|
+
catch (error) {
|
|
519
|
+
return {
|
|
520
|
+
success: false,
|
|
521
|
+
error: error.message,
|
|
522
|
+
selectedCamera: null,
|
|
523
|
+
availableCameras: 0
|
|
524
|
+
};
|
|
379
525
|
}
|
|
380
|
-
return {
|
|
381
|
-
success: true,
|
|
382
|
-
selectedCamera: this.cameraService.getSelectedCameraId(),
|
|
383
|
-
availableCameras: this.cameraService.getAvailableCameras().length
|
|
384
|
-
};
|
|
385
526
|
}
|
|
386
527
|
async setCaptureDelay(delay) {
|
|
387
528
|
if (delay < 0 || delay > 10000) {
|
|
388
|
-
|
|
529
|
+
return {
|
|
530
|
+
success: false,
|
|
531
|
+
error: 'Capture delay must be between 0 and 10000 milliseconds',
|
|
532
|
+
captureDelay: this.captureDelay
|
|
533
|
+
};
|
|
389
534
|
}
|
|
535
|
+
// Allow setting capture delay even during initialization
|
|
390
536
|
this.captureDelay = delay;
|
|
391
|
-
this.serviceContainer
|
|
537
|
+
if (this.serviceContainer) {
|
|
538
|
+
this.serviceContainer.updateConfig({ captureDelay: delay });
|
|
539
|
+
}
|
|
392
540
|
return {
|
|
393
541
|
success: true,
|
|
394
542
|
captureDelay: this.captureDelay
|
|
395
543
|
};
|
|
396
544
|
}
|
|
397
545
|
async getCaptureDelay() {
|
|
546
|
+
// Always allow getting capture delay, even during initialization
|
|
398
547
|
return this.captureDelay;
|
|
399
548
|
}
|
|
400
|
-
async setTorchEnabled(enabled) {
|
|
401
|
-
const torchEnabled = await this.cameraService.setTorchEnabled(enabled, this.videoStream);
|
|
402
|
-
return {
|
|
403
|
-
success: torchEnabled,
|
|
404
|
-
enabled: torchEnabled ? enabled : false
|
|
405
|
-
};
|
|
406
|
-
}
|
|
407
|
-
async focusAtPoint(x, y) {
|
|
408
|
-
const focused = await this.cameraService.focusAtPoint(x, y, this.videoStream);
|
|
409
|
-
return {
|
|
410
|
-
success: focused,
|
|
411
|
-
coordinates: { x, y }
|
|
412
|
-
};
|
|
413
|
-
}
|
|
414
549
|
// DETECTION METHODS
|
|
415
550
|
async startDetection() {
|
|
416
|
-
this.logger.state('INICIANDO_DETECCION');
|
|
417
551
|
try {
|
|
418
552
|
// Paso 1: Verificar y cargar modelos si es necesario
|
|
419
553
|
if (!this.detectionService.isModelLoaded()) {
|
|
420
554
|
const loadStartTime = performance.now();
|
|
421
|
-
this.updateStatus('
|
|
555
|
+
this.updateStatus('Preparando reconocimiento...', 'Configurando detección de documentos', 'loading');
|
|
422
556
|
this.stateManager.updateCaptureState({ isLoading: true });
|
|
423
557
|
await this.detectionService.loadModel();
|
|
424
|
-
this.updateStatus('
|
|
558
|
+
this.updateStatus('Optimizando detección...', 'Configurando reconocimiento de documentos', 'loading');
|
|
425
559
|
await this.detectionService.loadClassificationModel();
|
|
426
560
|
const loadEndTime = performance.now();
|
|
427
561
|
const loadTime = loadEndTime - loadStartTime;
|
|
@@ -429,13 +563,13 @@ export class JaakStamps {
|
|
|
429
563
|
if (this.debug) {
|
|
430
564
|
this.recordOnnxPerformance(loadTime, 0);
|
|
431
565
|
}
|
|
432
|
-
this.logger.state('MODELOS_CARGADOS_EN_DETECCION', { loadTime: Math.round(loadTime) });
|
|
433
566
|
}
|
|
434
567
|
// Paso 2: Detectar y configurar dispositivos
|
|
435
|
-
this.updateStatus('
|
|
568
|
+
this.updateStatus('Configurando cámara...', 'Buscando dispositivos disponibles', 'loading');
|
|
436
569
|
await this.cameraService.enumerateDevices();
|
|
570
|
+
await this.updateCameraInfoWithAutofocus();
|
|
437
571
|
// Paso 3: Configurar cámara seleccionada
|
|
438
|
-
this.updateStatus('
|
|
572
|
+
this.updateStatus('Ajustando cámara...', 'Configurando calidad de imagen', 'loading');
|
|
439
573
|
const stream = await this.cameraService.setupCamera();
|
|
440
574
|
// Paso 4: Inicializar video y ocultar estado inmediatamente
|
|
441
575
|
this.updateStatus('Captura activa', 'Buscando documento en el marco de captura', 'active');
|
|
@@ -452,8 +586,7 @@ export class JaakStamps {
|
|
|
452
586
|
this.detectFrame();
|
|
453
587
|
}
|
|
454
588
|
catch (err) {
|
|
455
|
-
this.
|
|
456
|
-
this.updateStatus('Error al iniciar captura', 'No se pudo completar la inicialización', 'error');
|
|
589
|
+
this.updateStatus('Error al iniciar', 'No se pudo preparar la captura', 'error');
|
|
457
590
|
this.stateManager.updateCaptureState({ isLoading: false });
|
|
458
591
|
}
|
|
459
592
|
}
|
|
@@ -463,10 +596,6 @@ export class JaakStamps {
|
|
|
463
596
|
this.videoStream = stream;
|
|
464
597
|
const isRear = this.cameraService.isRearCamera(stream);
|
|
465
598
|
this.shouldMirrorVideo = !isRear;
|
|
466
|
-
this.logger.state('CAMARA_CONFIGURADA', {
|
|
467
|
-
isRearCamera: isRear,
|
|
468
|
-
shouldMirrorVideo: this.shouldMirrorVideo
|
|
469
|
-
});
|
|
470
599
|
return new Promise((resolve) => {
|
|
471
600
|
this.videoRef.onloadedmetadata = async () => {
|
|
472
601
|
await this.videoRef.play();
|
|
@@ -491,9 +620,10 @@ export class JaakStamps {
|
|
|
491
620
|
}
|
|
492
621
|
return;
|
|
493
622
|
}
|
|
494
|
-
//
|
|
623
|
+
// Adaptive frame skipping for performance
|
|
495
624
|
this.frameSkipCounter++;
|
|
496
|
-
|
|
625
|
+
const currentFrameSkip = this.getAdaptiveFrameSkip();
|
|
626
|
+
if (this.frameSkipCounter <= currentFrameSkip) {
|
|
497
627
|
if (captureState.step !== 'completed') {
|
|
498
628
|
this.animationId = requestAnimationFrame(() => this.detectFrame());
|
|
499
629
|
}
|
|
@@ -569,7 +699,6 @@ export class JaakStamps {
|
|
|
569
699
|
}
|
|
570
700
|
}
|
|
571
701
|
catch (e) {
|
|
572
|
-
this.logger.error('Error en inferencia de modelo:', e);
|
|
573
702
|
const captureState = this.stateManager.getCaptureState();
|
|
574
703
|
if (captureState.step !== 'completed') {
|
|
575
704
|
setTimeout(() => this.detectFrame(), 100);
|
|
@@ -596,6 +725,8 @@ export class JaakStamps {
|
|
|
596
725
|
clearInterval(this.performanceUpdateInterval);
|
|
597
726
|
this.performanceUpdateInterval = undefined;
|
|
598
727
|
}
|
|
728
|
+
// Clear canvas pool
|
|
729
|
+
this.canvasPool = [];
|
|
599
730
|
this.detectionBoxes = [];
|
|
600
731
|
this.alignmentStartTime = undefined;
|
|
601
732
|
this.hasDocumentDetected = false;
|
|
@@ -610,14 +741,8 @@ export class JaakStamps {
|
|
|
610
741
|
step: 'front',
|
|
611
742
|
isCapturing: false
|
|
612
743
|
};
|
|
613
|
-
const cameraInfo = this.
|
|
614
|
-
|
|
615
|
-
isMultipleCamerasAvailable: false,
|
|
616
|
-
selectedCameraId: null,
|
|
617
|
-
deviceType: 'desktop',
|
|
618
|
-
preferredFacing: null
|
|
619
|
-
};
|
|
620
|
-
return (h("div", { key: '9d4d77042218ab3d0bc4fb1ce879db09dc3e78b4', class: "detector-container" }, h("div", { key: '51ac8ccb1275637282b038ad8364d19f14ef61b1', class: "video-container" }, h("video", { key: 'a34f0481da20ec5b7b62b21aa275ec5f6dfec5d1', ref: el => this.videoRef = el, autoplay: true, muted: true, playsinline: true, class: this.shouldMirrorVideo ? 'mirror' : '', style: { display: captureState.isVideoActive ? 'block' : 'none' } }), h("div", { key: '43b71dfe3eaf20ffa66a4139e24b1af6a2074631', ref: el => this.detectionContainer = el, class: `detection-overlay ${this.shouldMirrorVideo ? 'mirror' : ''}` }, this.debug && this.detectionBoxes.map((box, index) => (h("div", { key: index, class: "detection-box", style: {
|
|
744
|
+
const cameraInfo = this.cameraInfoWithAutofocus;
|
|
745
|
+
return (h("div", { key: '69a8df7969288e19088e8809f1fefcc89b237cda', class: "detector-container" }, h("div", { key: '79e59a315d266be9d4fb5eed5740e5ad825d5890', class: "video-container" }, h("video", { key: 'a3312788c739748b774a25acb30c2dcea7f01695', ref: el => this.videoRef = el, autoplay: true, muted: true, playsinline: true, class: this.shouldMirrorVideo ? 'mirror' : '', style: { display: captureState.isVideoActive ? 'block' : 'none' } }), h("div", { key: 'ddab107500c2dab24c140049bef0b44aafc35dc7', ref: el => this.detectionContainer = el, class: `detection-overlay ${this.shouldMirrorVideo ? 'mirror' : ''}` }, this.debug && this.detectionBoxes.map((box, index) => (h("div", { key: index, class: "detection-box", style: {
|
|
621
746
|
position: 'absolute',
|
|
622
747
|
left: `${box.x}px`,
|
|
623
748
|
top: `${box.y}px`,
|
|
@@ -626,9 +751,9 @@ export class JaakStamps {
|
|
|
626
751
|
border: '2px solid #32406C',
|
|
627
752
|
pointerEvents: 'none',
|
|
628
753
|
boxSizing: 'border-box'
|
|
629
|
-
} })))), this.isMaskReady && (h("div", { key: '
|
|
754
|
+
} })))), this.isMaskReady && (h("div", { key: '370337089a3de949b8cb4ae84dc0037e6044b2d5', class: "overlay-mask" }, h("div", { key: 'cb35c2e42bf1729ad02a8fe7e7abaf27cf1a8d7a', class: "card-outline" }, h("div", { key: '637d72ada63805e7f25051cd1ad9093030cb025a', class: "side side-top" }), h("div", { key: 'c7a465aa686afa41f80433d949715b40f6f349dc', class: "side side-right" }), h("div", { key: '384aade0610b5bc7bdd7db350a70d82f9bebd8ab', class: "side side-bottom" }), h("div", { key: '5506cc346f3fd718e3fda4365f69ea780304b6d6', class: "side side-left" }), !captureState.showFlipAnimation && !captureState.showSuccessAnimation && (h("div", { key: 'f5b9b62937eabab7c096b7417a13bbf6f822ee8a', class: "guide-text" }, "Alinee su identificaci\u00F3n con el marco"))), captureState.step === 'back' && !captureState.showFlipAnimation && !captureState.showSuccessAnimation && (h("div", { key: '74b122125a42839d2600cf99fb855140042ab761', class: "skip-section" }, h("div", { key: '0b4a39d1599854d6c445e2c35dcc89a0e4970846', class: "skip-explanation" }, "Si tu documento no tiene lado trasero, da clic en el bot\u00F3n para continuar con el proceso"), h("button", { key: 'b52b47df390cc099c8847ca8a676270bb154294d', class: "skip-button", onClick: () => this.skipBackCapture(), type: "button" }, this.enableBackDocumentTimer && this.backDocumentTimerRemaining > 0
|
|
630
755
|
? `Saltar reverso (${this.backDocumentTimerRemaining}s)`
|
|
631
|
-
: 'Saltar reverso'))), captureState.isVideoActive && (h("div", { key: '
|
|
756
|
+
: 'Saltar reverso'))), captureState.isVideoActive && (h("div", { key: 'f04e47749a25197c434b849844c67eadca5c6af3', class: "camera-controls" }, h("button", { key: '82625eef057f59b6dac11c4443ccb315231eac7f', class: `camera-selector-button ${this.isSwitchingCamera ? 'loading' : ''}`, onClick: () => this.toggleCameraSelector(), type: "button", title: "Seleccionar c\u00E1mara", disabled: this.isSwitchingCamera }, this.isSwitchingCamera ? (h("div", { class: "button-spinner" })) : ('Cámaras')))), this.showCameraSelector && cameraInfo.availableCameras.length > 0 && (h("div", { key: '61cfb89cd9bb549cfff2cb70293db06ee6cdf06b', class: "camera-selector-dropdown" }, h("div", { key: '60f751d8db7b305f2bdfdc089d2110bcff940150', class: "camera-selector-header" }, h("span", { key: '9dae045a34cc06f323ff4850921b495fc845be47' }, "Seleccionar C\u00E1mara"), h("button", { key: '4584d7964d91ddd7f3d980c3e5e90493530d33bd', class: "close-selector", onClick: () => this.toggleCameraSelector(), type: "button" }, "\u00D7")), h("div", { key: '9881f2b4fc6ffbdc76db06a7b49d060cefbe6277', class: "camera-list" }, cameraInfo.availableCameras.map((camera) => (h("button", { key: camera.id, class: `camera-option ${cameraInfo.selectedCameraId === camera.id ? 'selected' : ''}`, onClick: () => this.handleCameraSwitch(camera.id), type: "button" }, h("span", { class: "camera-label" }, camera.label || `Cámara ${cameraInfo.availableCameras.indexOf(camera) + 1}`, camera.hasAutofocus && (h("span", { class: "autofocus-icon", title: "Autofoco disponible" }, "\u29BF"))), cameraInfo.selectedCameraId === camera.id && (h("span", { class: "selected-indicator" }, "\u2713")))))), h("div", { key: '3dfe9ffc7b4cc9afb199a78026cef6d4b469805e', class: "device-info" }, h("small", { key: 'bddc865a30469bd2724624b2f7c8f641f9d97a07' }, "Dispositivo: ", cameraInfo.deviceType)))))), captureState.isCapturing && (h("div", { key: '2fd963e44f69f80d17c6bbbfa3c9b5e11bc816be', class: "capture-animation" })), captureState.showFlipAnimation && (h("div", { key: 'ba4872ea5388a0ac3134be61feff5412373465ab', class: "flip-animation" }, h("div", { key: 'e83a92cea79f3e85aefc6964c5d9de7b495d511e', class: "id-card-icon" }), h("div", { key: '1d06b51fd6ad14d9b37aa6bca72bd13c951a386a', class: "flip-text" }, "\u00A1Voltea tu identificaci\u00F3n!"))), captureState.showSuccessAnimation && (h("div", { key: '503c377896d5fdc3ae8476b09f511d571f23ae72', class: "success-animation" }, h("div", { key: 'e1929c245b0d1479a6fb1513b0b37c53070c4291', class: "check-icon" }), h("div", { key: '024c897c70427b205390770fc3dfb999ffcc84d3', class: "success-text" }, "\u00A1Proceso completado!"))), h("div", { key: '54e350707628e5c88ae2900e412e6bda2eace943', class: `component-status status-${this.currentStatus.type}` }, (this.currentStatus.type === 'loading' || this.currentStatus.type === 'initializing') && (h("div", { key: '82ea10569ed46285686cf3e63885ca1941bb559c', class: "status-spinner" })), h("div", { key: '1fecf179c10965cccf1be3dc0a54afd377ba38ad', class: "status-content" }, h("div", { key: '1b301c1bbb02e5cac2e6babcb20c190662d45c68', class: "status-message" }, this.currentStatus.message), this.currentStatus.description && (h("div", { key: '5b6c61308d17176daade02c951a436d114a74100', class: "status-description" }, this.currentStatus.description)))), this.debug && (h("div", { key: 'b8104db64208dc87a617a072957c007f28055194', class: "performance-monitor" }, h("div", { key: 'e66aec071f10821be2713858dcc251c04f480e63', class: "performance-expanded" }, h("div", { key: '1ae09a91d0704ab21b9eb51097cf452fd5f44359', class: "metrics-row" }, h("div", { key: '340d4d16d048a5217fe13d5de63618c9ea221165', class: "metric-compact" }, h("span", { key: '20cfda14969ef70d232edd68f2e9902bd44b5d9a', class: "metric-label" }, "FPS"), h("span", { key: '3a089c2814a71ed6513bf05b0120a66aa159daee', class: `metric-value ${this.performanceData.fps < 15 ? 'warning' : this.performanceData.fps < 10 ? 'danger' : 'good'}` }, this.performanceData.fps)), h("div", { key: '23f700def988c3220fc5df37aeb57a0a20ff56b3', class: "metric-compact" }, h("span", { key: '441e8490a871469813a6d832114e03583d375e91', class: "metric-label" }, "MEM"), h("span", { key: '51f7c49164c9d5e699141c92c88375fc99b312ed', class: `metric-value ${this.performanceData.memoryUsage > 100 ? 'warning' : this.performanceData.memoryUsage > 200 ? 'danger' : 'good'}` }, this.performanceData.memoryUsage, "MB"))), h("div", { key: '6547fee80e2a1db7c9ff889a1c478fc8248159e6', class: "metrics-row" }, h("div", { key: '7b2d1642fd122ee0fbed36387afb23152afe10c2', class: "metric-compact" }, h("span", { key: 'a9137119bf7da366f18feaf8f28807ee40ad7a25', class: "metric-label" }, "INF"), h("span", { key: '5cbc0c040102688f1039cdcd4bb58020248cf700', class: `metric-value ${this.performanceData.inferenceTime > 100 ? 'warning' : this.performanceData.inferenceTime > 200 ? 'danger' : 'good'}` }, this.performanceData.inferenceTime, "ms")), h("div", { key: 'cb4a86f5da46e4081d997c8b8bb6a899f06dfcd2', class: "metric-compact" }, h("span", { key: '88ad5e1d842eb156cf6813ecaa9d2bec76a5999e', class: "metric-label" }, "FRAME"), h("span", { key: 'a9b14cd98b64dcfc058d3b68a3c168a9e092fb3c', class: `metric-value ${this.performanceData.frameProcessingTime > 50 ? 'warning' : this.performanceData.frameProcessingTime > 100 ? 'danger' : 'good'}` }, this.performanceData.frameProcessingTime, "ms"))), h("div", { key: '86e01f3596682cb8ad2ff5464748e6ecb7db374a', class: "metrics-row" }, h("div", { key: 'e73b9d39e9bcbc5d46fa0ba0a737700e3e5566d9', class: "metric-compact" }, h("span", { key: '8ecfe969dfe77c87d9da76d7390706014b4872f3', class: "metric-label" }, "DET"), h("span", { key: 'bd1a6a52e0bd1fb4d377413d7515e4e0f00da10f', class: "metric-value good" }, this.performanceData.successfulDetections, "/", this.performanceData.totalDetections)), h("div", { key: '36539ae98787fd60642082db1bf89031003c4bef', class: "metric-compact" }, h("span", { key: '7f3a81629c09053e3beeca97f485da0f60412052', class: "metric-label" }, "RATE"), h("span", { key: '72399e0ac39270c82b265ce64e402f84cda9fb2c', class: `metric-value ${this.performanceData.detectionRate < 30 ? 'danger' : this.performanceData.detectionRate < 60 ? 'warning' : 'good'}` }, this.performanceData.detectionRate, "%")))))), h("div", { key: '4e3a914ba2f5993f85de7b9dd6dc93d76b41c96d', class: "watermark" }, h("img", { key: 'aa634c2ba2090f64ee9b41aa1d75a9114d46fd63', src: "https://storage.googleapis.com/jaak-static/commons/powered-by-jaak.png", alt: "Powered by Jaak" })))));
|
|
632
757
|
}
|
|
633
758
|
// Utility methods
|
|
634
759
|
updateDetectionBoxes(boxes) {
|
|
@@ -709,35 +834,18 @@ export class JaakStamps {
|
|
|
709
834
|
if (allSidesAligned && bestBox) {
|
|
710
835
|
cardOutline?.classList.add('perfect-match');
|
|
711
836
|
corners?.forEach(corner => corner.classList.add('perfect-match'));
|
|
712
|
-
// Debug logging
|
|
713
|
-
this.logger.state('CAPTURE_EVALUATION', {
|
|
714
|
-
allSidesAligned: true,
|
|
715
|
-
hasScreenshotTaken: this.hasScreenshotTaken,
|
|
716
|
-
captureDelay: this.captureDelay,
|
|
717
|
-
alignmentStartTime: this.alignmentStartTime
|
|
718
|
-
});
|
|
719
837
|
if (!this.hasScreenshotTaken) {
|
|
720
838
|
const currentTime = Date.now();
|
|
721
839
|
// Initialize alignment start time if not set
|
|
722
840
|
if (!this.alignmentStartTime) {
|
|
723
841
|
this.alignmentStartTime = currentTime;
|
|
724
|
-
this.logger.state('ALIGNMENT_TIMER_STARTED', { startTime: currentTime });
|
|
725
842
|
}
|
|
726
843
|
// Check if document has been aligned for the configured delay
|
|
727
844
|
const alignmentDuration = currentTime - this.alignmentStartTime;
|
|
728
|
-
this.logger.state('ALIGNMENT_DURATION_CHECK', {
|
|
729
|
-
alignmentDuration,
|
|
730
|
-
captureDelay: this.captureDelay,
|
|
731
|
-
readyToCapture: alignmentDuration >= this.captureDelay
|
|
732
|
-
});
|
|
733
845
|
if (alignmentDuration >= this.captureDelay) {
|
|
734
|
-
this.logger.state('TRIGGERING_CAPTURE', {
|
|
735
|
-
alignmentDuration,
|
|
736
|
-
captureDelay: this.captureDelay
|
|
737
|
-
});
|
|
738
846
|
this.lastDetectedBox = bestBox;
|
|
739
|
-
this.takeScreenshot().catch(
|
|
740
|
-
|
|
847
|
+
this.takeScreenshot().catch(() => {
|
|
848
|
+
// Handle screenshot error silently
|
|
741
849
|
});
|
|
742
850
|
this.hasScreenshotTaken = true;
|
|
743
851
|
this.alignmentStartTime = undefined;
|
|
@@ -763,21 +871,12 @@ export class JaakStamps {
|
|
|
763
871
|
async takeScreenshot() {
|
|
764
872
|
if (!this.videoRef || !this.lastDetectedBox)
|
|
765
873
|
return;
|
|
766
|
-
this.logger.state('INICIANDO_CAPTURA', {
|
|
767
|
-
captureStep: this.stateManager.getCaptureState().step,
|
|
768
|
-
detectedBox: this.lastDetectedBox,
|
|
769
|
-
videoResolution: {
|
|
770
|
-
width: this.videoRef.videoWidth,
|
|
771
|
-
height: this.videoRef.videoHeight
|
|
772
|
-
}
|
|
773
|
-
});
|
|
774
874
|
this.stateManager.updateCaptureState({ isCapturing: true });
|
|
775
875
|
this.triggerCaptureAnimation();
|
|
776
|
-
//
|
|
777
|
-
const captureCanvas =
|
|
778
|
-
captureCanvas.width = this.videoRef.videoWidth;
|
|
779
|
-
captureCanvas.height = this.videoRef.videoHeight;
|
|
876
|
+
// Use pooled canvas for optimization
|
|
877
|
+
const captureCanvas = this.getPooledCanvas(this.videoRef.videoWidth, this.videoRef.videoHeight);
|
|
780
878
|
const captureCtx = captureCanvas.getContext('2d', { alpha: false });
|
|
879
|
+
captureCtx.clearRect(0, 0, captureCanvas.width, captureCanvas.height);
|
|
781
880
|
captureCtx.drawImage(this.videoRef, 0, 0, captureCanvas.width, captureCanvas.height);
|
|
782
881
|
// Calculate crop coordinates
|
|
783
882
|
const INPUT_SIZE = 320;
|
|
@@ -787,11 +886,10 @@ export class JaakStamps {
|
|
|
787
886
|
const cropY = Math.max(0, (this.lastDetectedBox.y * scaleY) - this.cropMargin);
|
|
788
887
|
const cropWidth = Math.min((this.lastDetectedBox.w * scaleX) + (2 * this.cropMargin), this.videoRef.videoWidth - cropX);
|
|
789
888
|
const cropHeight = Math.min((this.lastDetectedBox.h * scaleY) + (2 * this.cropMargin), this.videoRef.videoHeight - cropY);
|
|
790
|
-
//
|
|
791
|
-
const croppedCanvas =
|
|
792
|
-
croppedCanvas.width = cropWidth;
|
|
793
|
-
croppedCanvas.height = cropHeight;
|
|
889
|
+
// Use pooled canvas for cropped version
|
|
890
|
+
const croppedCanvas = this.getPooledCanvas(cropWidth, cropHeight);
|
|
794
891
|
const croppedCtx = croppedCanvas.getContext('2d', { alpha: false });
|
|
892
|
+
croppedCtx.clearRect(0, 0, croppedCanvas.width, croppedCanvas.height);
|
|
795
893
|
croppedCtx.drawImage(this.videoRef, cropX, cropY, cropWidth, cropHeight, 0, 0, cropWidth, cropHeight);
|
|
796
894
|
const captureState = this.stateManager.getCaptureState();
|
|
797
895
|
if (captureState.step === 'front') {
|
|
@@ -805,7 +903,6 @@ export class JaakStamps {
|
|
|
805
903
|
if (this.useDocumentClassification) {
|
|
806
904
|
const classification = await this.detectionService.classifyDocument(croppedCanvas);
|
|
807
905
|
if (classification && classification.class === 'passport') {
|
|
808
|
-
this.logger.state('PASAPORTE_DETECTADO_SALTANDO_REVERSO', { classification: classification?.class });
|
|
809
906
|
this.completeProcess(true);
|
|
810
907
|
return;
|
|
811
908
|
}
|
|
@@ -832,6 +929,9 @@ export class JaakStamps {
|
|
|
832
929
|
});
|
|
833
930
|
this.completeProcess(false);
|
|
834
931
|
}
|
|
932
|
+
// Return canvases to pool after use
|
|
933
|
+
this.returnCanvasToPool(captureCanvas);
|
|
934
|
+
this.returnCanvasToPool(croppedCanvas);
|
|
835
935
|
}
|
|
836
936
|
triggerCaptureAnimation() {
|
|
837
937
|
const cardOutline = this.el.shadowRoot?.querySelector('.card-outline');
|
|
@@ -851,7 +951,7 @@ export class JaakStamps {
|
|
|
851
951
|
capturedImages.metadata.backCaptureSkipped = skippedBack;
|
|
852
952
|
this.stateManager.setCapturedImages(capturedImages);
|
|
853
953
|
this.stopDetection();
|
|
854
|
-
this.updateStatus('Proceso completado',
|
|
954
|
+
this.updateStatus('Proceso completado', `Imágenes capturadas exitosamente`, 'ready');
|
|
855
955
|
const finalImages = {
|
|
856
956
|
...capturedImages,
|
|
857
957
|
timestamp: new Date().toISOString()
|
|
@@ -860,11 +960,6 @@ export class JaakStamps {
|
|
|
860
960
|
setTimeout(() => {
|
|
861
961
|
this.stateManager.updateCaptureState({ showSuccessAnimation: false });
|
|
862
962
|
}, 3000);
|
|
863
|
-
this.logger.state('PROCESO_COMPLETADO', {
|
|
864
|
-
skippedBack,
|
|
865
|
-
totalImages: capturedImages.metadata.totalImages,
|
|
866
|
-
timestamp: new Date().toISOString()
|
|
867
|
-
});
|
|
868
963
|
}
|
|
869
964
|
stopDetection() {
|
|
870
965
|
if (this.animationId) {
|
|
@@ -873,7 +968,6 @@ export class JaakStamps {
|
|
|
873
968
|
}
|
|
874
969
|
this.clearBackDocumentTimer();
|
|
875
970
|
this.detectionBoxes = [];
|
|
876
|
-
this.logger.state('DETECTOR_DETENIDO', { timestamp: Date.now() });
|
|
877
971
|
}
|
|
878
972
|
startBackDocumentTimer() {
|
|
879
973
|
if (!this.enableBackDocumentTimer)
|
|
@@ -902,6 +996,15 @@ export class JaakStamps {
|
|
|
902
996
|
return; // Don't toggle if switching camera
|
|
903
997
|
this.showCameraSelector = !this.showCameraSelector;
|
|
904
998
|
}
|
|
999
|
+
async updateCameraInfoWithAutofocus() {
|
|
1000
|
+
try {
|
|
1001
|
+
const cameraInfo = await this.cameraService.getCameraInfo();
|
|
1002
|
+
this.cameraInfoWithAutofocus = cameraInfo;
|
|
1003
|
+
}
|
|
1004
|
+
catch (error) {
|
|
1005
|
+
// Keep existing state if update fails
|
|
1006
|
+
}
|
|
1007
|
+
}
|
|
905
1008
|
async handleCameraSwitch(cameraId) {
|
|
906
1009
|
if (this.isSwitchingCamera)
|
|
907
1010
|
return; // Prevent multiple simultaneous switches
|
|
@@ -909,10 +1012,6 @@ export class JaakStamps {
|
|
|
909
1012
|
// Close the selector immediately when user selects a camera
|
|
910
1013
|
this.showCameraSelector = false;
|
|
911
1014
|
this.isSwitchingCamera = true;
|
|
912
|
-
this.logger.state('INICIANDO_CAMBIO_CAMARA', {
|
|
913
|
-
from: this.cameraService.getSelectedCameraId(),
|
|
914
|
-
to: cameraId
|
|
915
|
-
});
|
|
916
1015
|
// Stop current video stream
|
|
917
1016
|
if (this.videoStream) {
|
|
918
1017
|
this.videoStream.getTracks().forEach(track => track.stop());
|
|
@@ -923,21 +1022,17 @@ export class JaakStamps {
|
|
|
923
1022
|
const newStream = await this.cameraService.setupCamera();
|
|
924
1023
|
// Update video element
|
|
925
1024
|
await this.initializeVideoStream(newStream);
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
isRearCamera: this.cameraService.isRearCamera(newStream)
|
|
929
|
-
});
|
|
1025
|
+
// Update camera info with autofocus data
|
|
1026
|
+
await this.updateCameraInfoWithAutofocus();
|
|
930
1027
|
}
|
|
931
1028
|
catch (error) {
|
|
932
|
-
this.logger.error('Error al cambiar cámara:', error);
|
|
933
1029
|
// Try to restore previous camera if switch failed
|
|
934
1030
|
try {
|
|
935
1031
|
const fallbackStream = await this.cameraService.setupCamera();
|
|
936
1032
|
await this.initializeVideoStream(fallbackStream);
|
|
937
1033
|
}
|
|
938
1034
|
catch (fallbackError) {
|
|
939
|
-
this.
|
|
940
|
-
this.updateStatus('Error al cambiar cámara', 'No se pudo completar el cambio de dispositivo', 'error');
|
|
1035
|
+
this.updateStatus('Error al cambiar cámara', 'No se pudo cambiar el dispositivo', 'error');
|
|
941
1036
|
}
|
|
942
1037
|
}
|
|
943
1038
|
finally {
|
|
@@ -978,7 +1073,7 @@ export class JaakStamps {
|
|
|
978
1073
|
this.stateManager.updateCaptureState({ isVideoActive: false, isLoading: false });
|
|
979
1074
|
}
|
|
980
1075
|
this.isMaskReady = false;
|
|
981
|
-
this.updateStatus('
|
|
1076
|
+
this.updateStatus('Captura finalizada', '', 'ready');
|
|
982
1077
|
this.detectionBoxes = [];
|
|
983
1078
|
this.cleanup();
|
|
984
1079
|
}
|
|
@@ -989,7 +1084,6 @@ export class JaakStamps {
|
|
|
989
1084
|
this.performanceUpdateInterval = window.setInterval(() => {
|
|
990
1085
|
this.updatePerformanceMetrics();
|
|
991
1086
|
}, 500);
|
|
992
|
-
this.logger.debug('Monitor de performance inicializado');
|
|
993
1087
|
}
|
|
994
1088
|
updatePerformanceMetrics() {
|
|
995
1089
|
const currentTime = performance.now();
|
|
@@ -1032,6 +1126,66 @@ export class JaakStamps {
|
|
|
1032
1126
|
if (detectionsFound > 0) {
|
|
1033
1127
|
this.performanceMetrics.successfulDetections++;
|
|
1034
1128
|
}
|
|
1129
|
+
// Update performance history for adaptive frame skipping
|
|
1130
|
+
this.performanceHistory.push(processingTime);
|
|
1131
|
+
if (this.performanceHistory.length > this.PERFORMANCE_HISTORY_SIZE) {
|
|
1132
|
+
this.performanceHistory.shift();
|
|
1133
|
+
}
|
|
1134
|
+
}
|
|
1135
|
+
// Adaptive frame skipping based on performance
|
|
1136
|
+
getAdaptiveFrameSkip() {
|
|
1137
|
+
// Base conditions for high frame skip
|
|
1138
|
+
if (this.consecutiveFailures > 20)
|
|
1139
|
+
return this.MAX_FRAME_SKIP;
|
|
1140
|
+
if (this.performanceMetrics.inferenceTime > 200)
|
|
1141
|
+
return 6;
|
|
1142
|
+
if (this.performanceMetrics.memoryUsage > 200)
|
|
1143
|
+
return 5;
|
|
1144
|
+
// Calculate average processing time
|
|
1145
|
+
if (this.performanceHistory.length > 0) {
|
|
1146
|
+
const avgProcessingTime = this.performanceHistory.reduce((a, b) => a + b, 0) / this.performanceHistory.length;
|
|
1147
|
+
if (avgProcessingTime > 100)
|
|
1148
|
+
return 4;
|
|
1149
|
+
if (avgProcessingTime > 80)
|
|
1150
|
+
return 3;
|
|
1151
|
+
if (avgProcessingTime > 60)
|
|
1152
|
+
return this.BASE_FRAME_SKIP + 1;
|
|
1153
|
+
}
|
|
1154
|
+
// Low memory devices get higher frame skip
|
|
1155
|
+
if (this.performanceMetrics.memoryUsage > 150)
|
|
1156
|
+
return 4;
|
|
1157
|
+
// Performance is good, use base frame skip
|
|
1158
|
+
return this.BASE_FRAME_SKIP;
|
|
1159
|
+
}
|
|
1160
|
+
// Canvas pool management for screenshots
|
|
1161
|
+
getPooledCanvas(width, height) {
|
|
1162
|
+
// Try to find a canvas with matching dimensions
|
|
1163
|
+
const matchingIndex = this.canvasPool.findIndex(canvas => canvas.width === width && canvas.height === height);
|
|
1164
|
+
if (matchingIndex !== -1) {
|
|
1165
|
+
return this.canvasPool.splice(matchingIndex, 1)[0];
|
|
1166
|
+
}
|
|
1167
|
+
// If no matching canvas, try to reuse any canvas and resize
|
|
1168
|
+
if (this.canvasPool.length > 0) {
|
|
1169
|
+
const canvas = this.canvasPool.pop();
|
|
1170
|
+
canvas.width = width;
|
|
1171
|
+
canvas.height = height;
|
|
1172
|
+
return canvas;
|
|
1173
|
+
}
|
|
1174
|
+
// Create new canvas if pool is empty
|
|
1175
|
+
const canvas = document.createElement('canvas');
|
|
1176
|
+
canvas.width = width;
|
|
1177
|
+
canvas.height = height;
|
|
1178
|
+
return canvas;
|
|
1179
|
+
}
|
|
1180
|
+
returnCanvasToPool(canvas) {
|
|
1181
|
+
// Only return to pool if not at max capacity
|
|
1182
|
+
if (this.canvasPool.length < this.MAX_CANVAS_POOL_SIZE) {
|
|
1183
|
+
// Clear the canvas before returning to pool
|
|
1184
|
+
const ctx = canvas.getContext('2d');
|
|
1185
|
+
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
|
1186
|
+
this.canvasPool.push(canvas);
|
|
1187
|
+
}
|
|
1188
|
+
// If pool is full, let the canvas be garbage collected
|
|
1035
1189
|
}
|
|
1036
1190
|
static get is() { return "jaak-stamps"; }
|
|
1037
1191
|
static get encapsulation() { return "shadow"; }
|
|
@@ -1238,6 +1392,7 @@ export class JaakStamps {
|
|
|
1238
1392
|
"showCameraSelector": {},
|
|
1239
1393
|
"isSwitchingCamera": {},
|
|
1240
1394
|
"hasDocumentDetected": {},
|
|
1395
|
+
"cameraInfoWithAutofocus": {},
|
|
1241
1396
|
"currentStatus": {},
|
|
1242
1397
|
"performanceData": {},
|
|
1243
1398
|
"backDocumentTimerRemaining": {}
|
|
@@ -1319,7 +1474,7 @@ export class JaakStamps {
|
|
|
1319
1474
|
},
|
|
1320
1475
|
"startCapture": {
|
|
1321
1476
|
"complexType": {
|
|
1322
|
-
"signature": "() => Promise<
|
|
1477
|
+
"signature": "() => Promise<{ success: boolean; error?: undefined; } | { success: boolean; error: any; }>",
|
|
1323
1478
|
"parameters": [],
|
|
1324
1479
|
"references": {
|
|
1325
1480
|
"Promise": {
|
|
@@ -1327,7 +1482,7 @@ export class JaakStamps {
|
|
|
1327
1482
|
"id": "global::Promise"
|
|
1328
1483
|
}
|
|
1329
1484
|
},
|
|
1330
|
-
"return": "Promise<
|
|
1485
|
+
"return": "Promise<{ success: boolean; error?: undefined; } | { success: boolean; error: any; }>"
|
|
1331
1486
|
},
|
|
1332
1487
|
"docs": {
|
|
1333
1488
|
"text": "",
|
|
@@ -1336,7 +1491,7 @@ export class JaakStamps {
|
|
|
1336
1491
|
},
|
|
1337
1492
|
"stopCapture": {
|
|
1338
1493
|
"complexType": {
|
|
1339
|
-
"signature": "() => Promise<
|
|
1494
|
+
"signature": "() => Promise<{ success: boolean; error?: undefined; } | { success: boolean; error: any; }>",
|
|
1340
1495
|
"parameters": [],
|
|
1341
1496
|
"references": {
|
|
1342
1497
|
"Promise": {
|
|
@@ -1344,7 +1499,7 @@ export class JaakStamps {
|
|
|
1344
1499
|
"id": "global::Promise"
|
|
1345
1500
|
}
|
|
1346
1501
|
},
|
|
1347
|
-
"return": "Promise<
|
|
1502
|
+
"return": "Promise<{ success: boolean; error?: undefined; } | { success: boolean; error: any; }>"
|
|
1348
1503
|
},
|
|
1349
1504
|
"docs": {
|
|
1350
1505
|
"text": "",
|
|
@@ -1353,7 +1508,7 @@ export class JaakStamps {
|
|
|
1353
1508
|
},
|
|
1354
1509
|
"resetCapture": {
|
|
1355
1510
|
"complexType": {
|
|
1356
|
-
"signature": "() => Promise<
|
|
1511
|
+
"signature": "() => Promise<{ success: boolean; error?: undefined; } | { success: boolean; error: any; }>",
|
|
1357
1512
|
"parameters": [],
|
|
1358
1513
|
"references": {
|
|
1359
1514
|
"Promise": {
|
|
@@ -1361,7 +1516,7 @@ export class JaakStamps {
|
|
|
1361
1516
|
"id": "global::Promise"
|
|
1362
1517
|
}
|
|
1363
1518
|
},
|
|
1364
|
-
"return": "Promise<
|
|
1519
|
+
"return": "Promise<{ success: boolean; error?: undefined; } | { success: boolean; error: any; }>"
|
|
1365
1520
|
},
|
|
1366
1521
|
"docs": {
|
|
1367
1522
|
"text": "",
|
|
@@ -1370,7 +1525,7 @@ export class JaakStamps {
|
|
|
1370
1525
|
},
|
|
1371
1526
|
"skipBackCapture": {
|
|
1372
1527
|
"complexType": {
|
|
1373
|
-
"signature": "() => Promise<
|
|
1528
|
+
"signature": "() => Promise<{ success: boolean; error?: undefined; } | { success: boolean; error: any; }>",
|
|
1374
1529
|
"parameters": [],
|
|
1375
1530
|
"references": {
|
|
1376
1531
|
"Promise": {
|
|
@@ -1378,7 +1533,7 @@ export class JaakStamps {
|
|
|
1378
1533
|
"id": "global::Promise"
|
|
1379
1534
|
}
|
|
1380
1535
|
},
|
|
1381
|
-
"return": "Promise<
|
|
1536
|
+
"return": "Promise<{ success: boolean; error?: undefined; } | { success: boolean; error: any; }>"
|
|
1382
1537
|
},
|
|
1383
1538
|
"docs": {
|
|
1384
1539
|
"text": "",
|
|
@@ -1448,7 +1603,7 @@ export class JaakStamps {
|
|
|
1448
1603
|
},
|
|
1449
1604
|
"setPreferredCamera": {
|
|
1450
1605
|
"complexType": {
|
|
1451
|
-
"signature": "(camera: \"auto\" | \"front\" | \"back\") => Promise<{ success: boolean; selectedCamera: string; availableCameras: number; }>",
|
|
1606
|
+
"signature": "(camera: \"auto\" | \"front\" | \"back\") => Promise<{ success: boolean; selectedCamera: string; availableCameras: number; error?: undefined; } | { success: boolean; error: any; selectedCamera: any; availableCameras: number; }>",
|
|
1452
1607
|
"parameters": [{
|
|
1453
1608
|
"name": "camera",
|
|
1454
1609
|
"type": "\"auto\" | \"front\" | \"back\"",
|
|
@@ -1460,7 +1615,7 @@ export class JaakStamps {
|
|
|
1460
1615
|
"id": "global::Promise"
|
|
1461
1616
|
}
|
|
1462
1617
|
},
|
|
1463
|
-
"return": "Promise<{ success: boolean; selectedCamera: string; availableCameras: number; }>"
|
|
1618
|
+
"return": "Promise<{ success: boolean; selectedCamera: string; availableCameras: number; error?: undefined; } | { success: boolean; error: any; selectedCamera: any; availableCameras: number; }>"
|
|
1464
1619
|
},
|
|
1465
1620
|
"docs": {
|
|
1466
1621
|
"text": "",
|
|
@@ -1469,7 +1624,7 @@ export class JaakStamps {
|
|
|
1469
1624
|
},
|
|
1470
1625
|
"setCaptureDelay": {
|
|
1471
1626
|
"complexType": {
|
|
1472
|
-
"signature": "(delay: number) => Promise<{ success: boolean; captureDelay: number; }>",
|
|
1627
|
+
"signature": "(delay: number) => Promise<{ success: boolean; error: string; captureDelay: number; } | { success: boolean; captureDelay: number; error?: undefined; }>",
|
|
1473
1628
|
"parameters": [{
|
|
1474
1629
|
"name": "delay",
|
|
1475
1630
|
"type": "number",
|
|
@@ -1481,7 +1636,7 @@ export class JaakStamps {
|
|
|
1481
1636
|
"id": "global::Promise"
|
|
1482
1637
|
}
|
|
1483
1638
|
},
|
|
1484
|
-
"return": "Promise<{ success: boolean; captureDelay: number; }>"
|
|
1639
|
+
"return": "Promise<{ success: boolean; error: string; captureDelay: number; } | { success: boolean; captureDelay: number; error?: undefined; }>"
|
|
1485
1640
|
},
|
|
1486
1641
|
"docs": {
|
|
1487
1642
|
"text": "",
|
|
@@ -1504,52 +1659,6 @@ export class JaakStamps {
|
|
|
1504
1659
|
"text": "",
|
|
1505
1660
|
"tags": []
|
|
1506
1661
|
}
|
|
1507
|
-
},
|
|
1508
|
-
"setTorchEnabled": {
|
|
1509
|
-
"complexType": {
|
|
1510
|
-
"signature": "(enabled: boolean) => Promise<{ success: boolean; enabled: boolean; }>",
|
|
1511
|
-
"parameters": [{
|
|
1512
|
-
"name": "enabled",
|
|
1513
|
-
"type": "boolean",
|
|
1514
|
-
"docs": ""
|
|
1515
|
-
}],
|
|
1516
|
-
"references": {
|
|
1517
|
-
"Promise": {
|
|
1518
|
-
"location": "global",
|
|
1519
|
-
"id": "global::Promise"
|
|
1520
|
-
}
|
|
1521
|
-
},
|
|
1522
|
-
"return": "Promise<{ success: boolean; enabled: boolean; }>"
|
|
1523
|
-
},
|
|
1524
|
-
"docs": {
|
|
1525
|
-
"text": "",
|
|
1526
|
-
"tags": []
|
|
1527
|
-
}
|
|
1528
|
-
},
|
|
1529
|
-
"focusAtPoint": {
|
|
1530
|
-
"complexType": {
|
|
1531
|
-
"signature": "(x: number, y: number) => Promise<{ success: boolean; coordinates: { x: number; y: number; }; }>",
|
|
1532
|
-
"parameters": [{
|
|
1533
|
-
"name": "x",
|
|
1534
|
-
"type": "number",
|
|
1535
|
-
"docs": ""
|
|
1536
|
-
}, {
|
|
1537
|
-
"name": "y",
|
|
1538
|
-
"type": "number",
|
|
1539
|
-
"docs": ""
|
|
1540
|
-
}],
|
|
1541
|
-
"references": {
|
|
1542
|
-
"Promise": {
|
|
1543
|
-
"location": "global",
|
|
1544
|
-
"id": "global::Promise"
|
|
1545
|
-
}
|
|
1546
|
-
},
|
|
1547
|
-
"return": "Promise<{ success: boolean; coordinates: { x: number; y: number; }; }>"
|
|
1548
|
-
},
|
|
1549
|
-
"docs": {
|
|
1550
|
-
"text": "",
|
|
1551
|
-
"tags": []
|
|
1552
|
-
}
|
|
1553
1662
|
}
|
|
1554
1663
|
};
|
|
1555
1664
|
}
|