@jaak.ai/stamps 2.1.0-dev.2 → 2.1.0-dev.20
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/app-globals-V2Kpy_OQ.js +8 -0
- package/dist/cjs/app-globals-V2Kpy_OQ.js.map +1 -0
- package/dist/cjs/{index-BfhtOB0D.js → index-Ga0t6BMe.js} +176 -119
- package/dist/cjs/index-Ga0t6BMe.js.map +1 -0
- package/dist/cjs/jaak-stamps-webcomponent.cjs.js +5 -4
- package/dist/cjs/jaak-stamps-webcomponent.cjs.js.map +1 -1
- package/dist/cjs/jaak-stamps.cjs.entry.js +542 -68
- 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 +4 -3
- package/dist/cjs/loader.cjs.js.map +1 -1
- package/dist/collection/collection-manifest.json +1 -1
- package/dist/collection/components/my-component/my-component.css +56 -12
- package/dist/collection/components/my-component/my-component.js +429 -67
- package/dist/collection/components/my-component/my-component.js.map +1 -1
- package/dist/collection/services/CameraService.js +84 -2
- package/dist/collection/services/CameraService.js.map +1 -1
- package/dist/collection/services/DetectionService.js +32 -4
- package/dist/collection/services/DetectionService.js.map +1 -1
- package/dist/components/index.js +165 -113
- package/dist/components/index.js.map +1 -1
- package/dist/components/jaak-stamps.js +546 -68
- package/dist/components/jaak-stamps.js.map +1 -1
- package/dist/esm/app-globals-DQuL1Twl.js +6 -0
- package/dist/esm/app-globals-DQuL1Twl.js.map +1 -0
- package/dist/esm/{index-BP1Q4KOg.js → index-Drbzcuq-.js} +177 -119
- package/dist/esm/index-Drbzcuq-.js.map +1 -0
- package/dist/esm/jaak-stamps-webcomponent.js +5 -4
- package/dist/esm/jaak-stamps-webcomponent.js.map +1 -1
- package/dist/esm/jaak-stamps.entry.js +542 -68
- package/dist/esm/jaak-stamps.entry.js.map +1 -1
- package/dist/esm/loader.js +4 -3
- package/dist/esm/loader.js.map +1 -1
- package/dist/jaak-stamps-webcomponent/jaak-stamps-webcomponent.esm.js +1 -1
- package/dist/jaak-stamps-webcomponent/jaak-stamps-webcomponent.esm.js.map +1 -1
- package/dist/jaak-stamps-webcomponent/jaak-stamps.entry.esm.js.map +1 -1
- package/dist/jaak-stamps-webcomponent/loader.esm.js.map +1 -1
- package/dist/jaak-stamps-webcomponent/p-DQuL1Twl.js +2 -0
- package/dist/jaak-stamps-webcomponent/p-DQuL1Twl.js.map +1 -0
- package/dist/jaak-stamps-webcomponent/p-Drbzcuq-.js +3 -0
- package/dist/jaak-stamps-webcomponent/p-Drbzcuq-.js.map +1 -0
- package/dist/jaak-stamps-webcomponent/p-c06e5d7b.entry.js +2 -0
- package/dist/jaak-stamps-webcomponent/p-c06e5d7b.entry.js.map +1 -0
- package/dist/types/components/my-component/my-component.d.ts +19 -0
- package/dist/types/services/CameraService.d.ts +1 -0
- package/dist/types/stencil-public-runtime.d.ts +12 -5
- package/package.json +3 -1
- package/dist/cjs/index-BfhtOB0D.js.map +0 -1
- package/dist/esm/index-BP1Q4KOg.js.map +0 -1
- package/dist/jaak-stamps-webcomponent/p-41e88688.entry.js +0 -2
- package/dist/jaak-stamps-webcomponent/p-41e88688.entry.js.map +0 -1
- package/dist/jaak-stamps-webcomponent/p-BP1Q4KOg.js +0 -3
- package/dist/jaak-stamps-webcomponent/p-BP1Q4KOg.js.map +0 -1
|
@@ -49,6 +49,10 @@ export class JaakStamps {
|
|
|
49
49
|
};
|
|
50
50
|
backDocumentTimerRemaining = 0;
|
|
51
51
|
showManualCaptureButton = false;
|
|
52
|
+
performanceDegradedMode = false; // Auto-switched to manual mode due to performance
|
|
53
|
+
showPerformanceMessage = false; // Show performance message temporarily
|
|
54
|
+
captureStateVersion = 0; // Triggers re-render when StateManager changes
|
|
55
|
+
processingButton = null; // Track which button is processing
|
|
52
56
|
// Services
|
|
53
57
|
serviceContainer;
|
|
54
58
|
eventBus;
|
|
@@ -67,12 +71,12 @@ export class JaakStamps {
|
|
|
67
71
|
alignmentStartTime;
|
|
68
72
|
alignmentTimer;
|
|
69
73
|
backDocumentTimer;
|
|
74
|
+
orientationTimer;
|
|
70
75
|
// Performance monitoring
|
|
71
76
|
performanceMetrics = {
|
|
72
77
|
fps: 0,
|
|
73
78
|
inferenceTime: 0,
|
|
74
79
|
memoryUsage: 0,
|
|
75
|
-
cpuUsage: 0,
|
|
76
80
|
onnxLoadTime: 0,
|
|
77
81
|
frameProcessingTime: 0,
|
|
78
82
|
totalDetections: 0,
|
|
@@ -91,18 +95,29 @@ export class JaakStamps {
|
|
|
91
95
|
MIN_INFERENCE_INTERVAL = 50;
|
|
92
96
|
performanceHistory = [];
|
|
93
97
|
PERFORMANCE_HISTORY_SIZE = 10;
|
|
98
|
+
// Sistema simplificado de monitoreo de rendimiento
|
|
99
|
+
PERFORMANCE_THRESHOLD_MS = 2000; // Umbral único para todos los frames
|
|
100
|
+
SLOW_FRAMES_TO_TRIGGER = 2; // Cuántos frames lentos consecutivos antes de cambiar
|
|
101
|
+
slowFrameCount = 0; // Contador de frames consecutivos lentos
|
|
102
|
+
processedFramesCount = 0; // Contador total de frames procesados
|
|
103
|
+
hasAutoSwitchedToManual = false;
|
|
94
104
|
// Canvas pool for optimized screenshot capture
|
|
95
105
|
canvasPool = [];
|
|
96
106
|
MAX_CANVAS_POOL_SIZE = 3;
|
|
97
107
|
async componentDidLoad() {
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
this.
|
|
108
|
+
try {
|
|
109
|
+
this.updateStatus('Preparando capturador...', 'Configurando herramientas de captura', 'initializing');
|
|
110
|
+
await this.initializeServices();
|
|
111
|
+
this.updateStatus('Preparando funciones...', 'Configurando herramientas de trabajo', 'initializing');
|
|
112
|
+
await this.setupEventListeners();
|
|
113
|
+
this.updateStatus('Configurando cámara...', 'Detectando dispositivos disponibles', 'initializing');
|
|
114
|
+
await this.initializeComponent();
|
|
115
|
+
if (this.debug) {
|
|
116
|
+
this.initializePerformanceMonitor();
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
catch (error) {
|
|
120
|
+
this.updateStatus('Error de inicialización', error.message, 'error');
|
|
106
121
|
}
|
|
107
122
|
}
|
|
108
123
|
async initializeServices() {
|
|
@@ -123,14 +138,8 @@ export class JaakStamps {
|
|
|
123
138
|
this.detectionService = this.serviceContainer.getDetectionService();
|
|
124
139
|
}
|
|
125
140
|
async setupEventListeners() {
|
|
126
|
-
this.eventBus.on('state-changed', (
|
|
127
|
-
this.handleStateChange(
|
|
128
|
-
});
|
|
129
|
-
this.eventBus.on('camera-changed', () => {
|
|
130
|
-
// Camera changed event
|
|
131
|
-
});
|
|
132
|
-
this.eventBus.on('error', () => {
|
|
133
|
-
// Handle service errors
|
|
141
|
+
this.eventBus.on('state-changed', () => {
|
|
142
|
+
this.handleStateChange();
|
|
134
143
|
});
|
|
135
144
|
}
|
|
136
145
|
async initializeComponent() {
|
|
@@ -166,7 +175,7 @@ export class JaakStamps {
|
|
|
166
175
|
if (!window.ort) {
|
|
167
176
|
this.updateStatus('Preparando herramientas...', 'Configurando funciones de captura', 'initializing');
|
|
168
177
|
const script = document.createElement('script');
|
|
169
|
-
script.src = 'https://cdn.jsdelivr.net/npm/onnxruntime-web/dist/ort.min.js';
|
|
178
|
+
script.src = 'https://cdn.jsdelivr.net/npm/onnxruntime-web@1.23.0/dist/ort.min.js';
|
|
170
179
|
document.head.appendChild(script);
|
|
171
180
|
await new Promise((resolve) => {
|
|
172
181
|
script.onload = () => {
|
|
@@ -237,13 +246,8 @@ export class JaakStamps {
|
|
|
237
246
|
return false;
|
|
238
247
|
}
|
|
239
248
|
}
|
|
240
|
-
handleStateChange(
|
|
241
|
-
//
|
|
242
|
-
// This is where the component updates its internal state based on service state changes
|
|
243
|
-
if (data.changes.isLoading !== undefined) {
|
|
244
|
-
// Force re-render when loading state changes
|
|
245
|
-
// Note: Stencil automatically re-renders when @State changes
|
|
246
|
-
}
|
|
249
|
+
handleStateChange() {
|
|
250
|
+
// Handle state changes from StateManager if needed
|
|
247
251
|
}
|
|
248
252
|
initializeResizeObserver() {
|
|
249
253
|
if ('ResizeObserver' in window && this.detectionContainer) {
|
|
@@ -252,6 +256,18 @@ export class JaakStamps {
|
|
|
252
256
|
});
|
|
253
257
|
resizeObserver.observe(this.detectionContainer.parentElement);
|
|
254
258
|
}
|
|
259
|
+
// Handle mobile device orientation changes
|
|
260
|
+
window.addEventListener('orientationchange', () => {
|
|
261
|
+
// Clear any existing orientation timer
|
|
262
|
+
if (this.orientationTimer) {
|
|
263
|
+
clearTimeout(this.orientationTimer);
|
|
264
|
+
}
|
|
265
|
+
// Add delay to ensure video dimensions are updated after rotation
|
|
266
|
+
this.orientationTimer = window.setTimeout(() => {
|
|
267
|
+
this.handleResize();
|
|
268
|
+
this.orientationTimer = undefined;
|
|
269
|
+
}, 300);
|
|
270
|
+
});
|
|
255
271
|
}
|
|
256
272
|
handleResize() {
|
|
257
273
|
if (this.detectionContainer) {
|
|
@@ -260,10 +276,17 @@ export class JaakStamps {
|
|
|
260
276
|
this.updateMaskDimensions(rect);
|
|
261
277
|
}
|
|
262
278
|
}
|
|
279
|
+
triggerRerender() {
|
|
280
|
+
this.captureStateVersion = this.captureStateVersion + 1;
|
|
281
|
+
}
|
|
263
282
|
getGuideText(step) {
|
|
264
283
|
if (step === 'completed') {
|
|
265
284
|
return 'Proceso completado';
|
|
266
285
|
}
|
|
286
|
+
// Check if we should show performance message
|
|
287
|
+
if (this.showPerformanceMessage && step === 'front') {
|
|
288
|
+
return 'Modo manual activado por rendimiento. Use el botón para capturar.';
|
|
289
|
+
}
|
|
267
290
|
if (step === 'front') {
|
|
268
291
|
// Para el frente, si el detector manual está activo, mostrar instrucciones de captura manual
|
|
269
292
|
if (this.showManualCaptureButton) {
|
|
@@ -421,8 +444,14 @@ export class JaakStamps {
|
|
|
421
444
|
}
|
|
422
445
|
}
|
|
423
446
|
async skipBackCapture() {
|
|
447
|
+
console.log('🟡 Botón clicked: Saltar reverso');
|
|
448
|
+
// Set processing state immediately
|
|
449
|
+
this.processingButton = 'skip-back';
|
|
450
|
+
// Add small delay to show spinner
|
|
451
|
+
await new Promise(resolve => setTimeout(resolve, 100));
|
|
424
452
|
const readyCheck = this.isComponentReady();
|
|
425
453
|
if (!readyCheck.ready) {
|
|
454
|
+
this.processingButton = null;
|
|
426
455
|
return { success: false, error: readyCheck.message };
|
|
427
456
|
}
|
|
428
457
|
try {
|
|
@@ -439,6 +468,10 @@ export class JaakStamps {
|
|
|
439
468
|
catch (error) {
|
|
440
469
|
return { success: false, error: error.message };
|
|
441
470
|
}
|
|
471
|
+
finally {
|
|
472
|
+
// Always clear processing state when done
|
|
473
|
+
this.processingButton = null;
|
|
474
|
+
}
|
|
442
475
|
}
|
|
443
476
|
async getStatus() {
|
|
444
477
|
const readyCheck = this.isComponentReady();
|
|
@@ -493,9 +526,21 @@ export class JaakStamps {
|
|
|
493
526
|
if (this.stateManager) {
|
|
494
527
|
this.stateManager.updateCaptureState({ isLoading: true });
|
|
495
528
|
}
|
|
496
|
-
|
|
529
|
+
try {
|
|
530
|
+
await this.detectionService.loadModel();
|
|
531
|
+
}
|
|
532
|
+
catch (modelError) {
|
|
533
|
+
this.switchToManualModeWithError('Error al cargar modelo de detección');
|
|
534
|
+
throw modelError;
|
|
535
|
+
}
|
|
497
536
|
this.updateStatus('Optimizando detección...', 'Configurando reconocimiento de documentos', 'loading');
|
|
498
|
-
|
|
537
|
+
try {
|
|
538
|
+
await this.detectionService.loadClassificationModel();
|
|
539
|
+
}
|
|
540
|
+
catch (classificationError) {
|
|
541
|
+
this.switchToManualModeWithError('Error al cargar modelo de clasificación');
|
|
542
|
+
throw classificationError;
|
|
543
|
+
}
|
|
499
544
|
const loadEndTime = performance.now();
|
|
500
545
|
const loadTime = loadEndTime - loadStartTime;
|
|
501
546
|
// Record ONNX load time
|
|
@@ -604,17 +649,63 @@ export class JaakStamps {
|
|
|
604
649
|
// Always allow getting capture delay, even during initialization
|
|
605
650
|
return this.captureDelay;
|
|
606
651
|
}
|
|
652
|
+
// Memory and device capability detection
|
|
653
|
+
checkDeviceCapabilities() {
|
|
654
|
+
const deviceMemory = navigator.deviceMemory;
|
|
655
|
+
// Siempre intentar usar ML, independientemente del dispositivo
|
|
656
|
+
// Las restricciones anteriores no eran confiables
|
|
657
|
+
return {
|
|
658
|
+
canUseML: true,
|
|
659
|
+
reason: 'Dispositivo compatible con detección automática.',
|
|
660
|
+
memoryMB: deviceMemory ? deviceMemory * 1024 : null
|
|
661
|
+
};
|
|
662
|
+
}
|
|
607
663
|
// DETECTION METHODS
|
|
608
664
|
async startDetection() {
|
|
609
665
|
try {
|
|
666
|
+
// Verificar capacidades del dispositivo antes de cargar modelos
|
|
667
|
+
const capabilities = this.checkDeviceCapabilities();
|
|
668
|
+
// Si el dispositivo no puede usar ML o tenemos problemas de memoria, usar modo manual
|
|
669
|
+
if (!capabilities.canUseML && this.useDocumentDetector) {
|
|
670
|
+
this.switchToManualModeWithWarning(capabilities.reason);
|
|
671
|
+
return;
|
|
672
|
+
}
|
|
610
673
|
// Paso 1: Verificar y cargar modelos si es necesario
|
|
611
674
|
if (!this.detectionService.isModelLoaded()) {
|
|
612
675
|
const loadStartTime = performance.now();
|
|
613
676
|
this.updateStatus('Preparando reconocimiento...', 'Configurando detección de documentos', 'loading');
|
|
614
677
|
this.stateManager.updateCaptureState({ isLoading: true });
|
|
615
|
-
|
|
678
|
+
try {
|
|
679
|
+
await this.detectionService.loadModel();
|
|
680
|
+
}
|
|
681
|
+
catch (modelError) {
|
|
682
|
+
// Si es un error de memoria, cambiar a modo manual con mensaje específico
|
|
683
|
+
if (modelError.message.includes('Out of memory') || modelError.message.includes('no available backend')) {
|
|
684
|
+
const memoryInfo = navigator.deviceMemory ? `(${navigator.deviceMemory}GB disponible)` : '';
|
|
685
|
+
this.switchToManualModeWithWarning(`Memoria insuficiente para detección automática ${memoryInfo}. Modo manual activado - funciona igual de bien!`);
|
|
686
|
+
return;
|
|
687
|
+
}
|
|
688
|
+
else {
|
|
689
|
+
this.switchToManualModeWithError('Error al cargar modelo de detección');
|
|
690
|
+
throw modelError;
|
|
691
|
+
}
|
|
692
|
+
}
|
|
616
693
|
this.updateStatus('Optimizando detección...', 'Configurando reconocimiento de documentos', 'loading');
|
|
617
|
-
|
|
694
|
+
try {
|
|
695
|
+
await this.detectionService.loadClassificationModel();
|
|
696
|
+
}
|
|
697
|
+
catch (classificationError) {
|
|
698
|
+
// Si es un error de memoria, cambiar a modo manual con mensaje específico
|
|
699
|
+
if (classificationError.message.includes('Out of memory') || classificationError.message.includes('no available backend')) {
|
|
700
|
+
const memoryInfo = navigator.deviceMemory ? `(${navigator.deviceMemory}GB disponible)` : '';
|
|
701
|
+
this.switchToManualModeWithWarning(`Memoria insuficiente para clasificación automática ${memoryInfo}. Modo manual activado para optimizar rendimiento.`);
|
|
702
|
+
return;
|
|
703
|
+
}
|
|
704
|
+
else {
|
|
705
|
+
this.switchToManualModeWithError('Error al cargar modelo de clasificación');
|
|
706
|
+
throw classificationError;
|
|
707
|
+
}
|
|
708
|
+
}
|
|
618
709
|
const loadEndTime = performance.now();
|
|
619
710
|
const loadTime = loadEndTime - loadStartTime;
|
|
620
711
|
// Record ONNX load time
|
|
@@ -624,19 +715,40 @@ export class JaakStamps {
|
|
|
624
715
|
}
|
|
625
716
|
// Paso 2: Detectar y configurar dispositivos
|
|
626
717
|
this.updateStatus('Configurando cámara...', 'Buscando dispositivos disponibles', 'loading');
|
|
627
|
-
|
|
628
|
-
|
|
718
|
+
try {
|
|
719
|
+
await this.cameraService.enumerateDevices();
|
|
720
|
+
}
|
|
721
|
+
catch (enumerateError) {
|
|
722
|
+
throw new Error(`Error al enumerar dispositivos: ${enumerateError.message}`);
|
|
723
|
+
}
|
|
724
|
+
try {
|
|
725
|
+
await this.updateCameraInfoWithAutofocus();
|
|
726
|
+
}
|
|
727
|
+
catch (cameraInfoError) {
|
|
728
|
+
throw new Error(`Error al actualizar información de cámara: ${cameraInfoError.message}`);
|
|
729
|
+
}
|
|
629
730
|
// Paso 3: Configurar cámara seleccionada
|
|
630
731
|
this.updateStatus('Ajustando cámara...', 'Configurando calidad de imagen', 'loading');
|
|
631
|
-
|
|
632
|
-
|
|
732
|
+
let stream;
|
|
733
|
+
try {
|
|
734
|
+
stream = await this.cameraService.setupCamera();
|
|
735
|
+
}
|
|
736
|
+
catch (setupError) {
|
|
737
|
+
throw new Error(`Error al configurar cámara: ${setupError.message}`);
|
|
738
|
+
}
|
|
739
|
+
// Paso 4: Inicializar video y mostrar estado de análisis inicial
|
|
633
740
|
if (this.useDocumentDetector) {
|
|
634
|
-
this.updateStatus('
|
|
741
|
+
this.updateStatus('Analizando estabilidad...', 'Evaluando rendimiento del sistema', 'loading');
|
|
635
742
|
}
|
|
636
743
|
else {
|
|
637
744
|
this.updateStatus('Captura activa', 'Posicione su documento y use el botón para capturar', 'active');
|
|
638
745
|
}
|
|
639
|
-
|
|
746
|
+
try {
|
|
747
|
+
await this.initializeVideoStream(stream);
|
|
748
|
+
}
|
|
749
|
+
catch (videoError) {
|
|
750
|
+
throw new Error(`Error al inicializar video: ${videoError.message}`);
|
|
751
|
+
}
|
|
640
752
|
// Paso 5: Calibrar máscara
|
|
641
753
|
if (this.detectionContainer) {
|
|
642
754
|
const container = this.detectionContainer.parentElement;
|
|
@@ -667,6 +779,12 @@ export class JaakStamps {
|
|
|
667
779
|
this.videoRef.onloadedmetadata = async () => {
|
|
668
780
|
await this.videoRef.play();
|
|
669
781
|
this.stateManager.updateCaptureState({ isVideoActive: true });
|
|
782
|
+
// Recalculate mask dimensions when new camera stream loads
|
|
783
|
+
if (this.detectionContainer) {
|
|
784
|
+
const container = this.detectionContainer.parentElement;
|
|
785
|
+
const rect = container.getBoundingClientRect();
|
|
786
|
+
this.updateMaskDimensions(rect);
|
|
787
|
+
}
|
|
670
788
|
resolve();
|
|
671
789
|
};
|
|
672
790
|
});
|
|
@@ -681,8 +799,8 @@ export class JaakStamps {
|
|
|
681
799
|
const captureState = this.stateManager.getCaptureState();
|
|
682
800
|
if (!this.videoRef || !this.detectionContainer || !this.detectionService.isModelLoaded())
|
|
683
801
|
return;
|
|
684
|
-
// Show manual capture button when document detector is disabled
|
|
685
|
-
if (!this.useDocumentDetector) {
|
|
802
|
+
// Show manual capture button when document detector is disabled or performance is degraded
|
|
803
|
+
if (!this.useDocumentDetector || this.performanceDegradedMode) {
|
|
686
804
|
this.showManualCaptureButton = true;
|
|
687
805
|
// Continue the loop for UI updates but skip detection logic
|
|
688
806
|
if (captureState.step !== 'completed') {
|
|
@@ -690,7 +808,7 @@ export class JaakStamps {
|
|
|
690
808
|
}
|
|
691
809
|
return;
|
|
692
810
|
}
|
|
693
|
-
else {
|
|
811
|
+
else if (!this.performanceDegradedMode) {
|
|
694
812
|
this.showManualCaptureButton = false;
|
|
695
813
|
}
|
|
696
814
|
if (captureState.isDetectionPaused) {
|
|
@@ -720,10 +838,27 @@ export class JaakStamps {
|
|
|
720
838
|
this.lastInferenceTime = currentTime;
|
|
721
839
|
// Measure preprocessing and inference time
|
|
722
840
|
const inferenceStartTime = performance.now();
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
841
|
+
let inputTensor;
|
|
842
|
+
let detections;
|
|
843
|
+
let inferenceTime;
|
|
844
|
+
try {
|
|
845
|
+
inputTensor = this.detectionService.preprocess(this.videoRef);
|
|
846
|
+
}
|
|
847
|
+
catch (preprocessError) {
|
|
848
|
+
this.switchToManualModeWithError('Error al procesar imagen');
|
|
849
|
+
return;
|
|
850
|
+
}
|
|
851
|
+
try {
|
|
852
|
+
// Standard detection without timeout (timeout is handled by frame counting)
|
|
853
|
+
detections = await this.detectionService.runInference(inputTensor);
|
|
854
|
+
inferenceTime = performance.now() - inferenceStartTime;
|
|
855
|
+
}
|
|
856
|
+
catch (inferenceError) {
|
|
857
|
+
this.switchToManualModeWithError('Error en detección automática');
|
|
858
|
+
return;
|
|
859
|
+
}
|
|
860
|
+
// Increment frame count
|
|
861
|
+
this.processedFramesCount++;
|
|
727
862
|
// Record ONNX performance metrics
|
|
728
863
|
if (this.debug) {
|
|
729
864
|
this.recordOnnxPerformance(0, inferenceTime);
|
|
@@ -762,9 +897,30 @@ export class JaakStamps {
|
|
|
762
897
|
}
|
|
763
898
|
this.updateMaskColor(detections);
|
|
764
899
|
// Record frame processing metrics
|
|
900
|
+
const frameEndTime = performance.now();
|
|
901
|
+
const totalFrameTime = frameEndTime - frameStartTime;
|
|
902
|
+
// Sistema simplificado: Verificar rendimiento de cada frame (solo si no se ha cambiado a manual)
|
|
903
|
+
if (this.useDocumentDetector && !this.hasAutoSwitchedToManual) {
|
|
904
|
+
// Verificar si el frame es lento
|
|
905
|
+
if (totalFrameTime >= this.PERFORMANCE_THRESHOLD_MS) {
|
|
906
|
+
this.slowFrameCount++;
|
|
907
|
+
// Si tenemos suficientes frames lentos consecutivos, cambiar a manual
|
|
908
|
+
if (this.slowFrameCount >= this.SLOW_FRAMES_TO_TRIGGER) {
|
|
909
|
+
this.switchToManualMode();
|
|
910
|
+
}
|
|
911
|
+
}
|
|
912
|
+
else {
|
|
913
|
+
// Frame rápido, reiniciar contador
|
|
914
|
+
if (this.slowFrameCount > 0) {
|
|
915
|
+
this.slowFrameCount = 0;
|
|
916
|
+
}
|
|
917
|
+
}
|
|
918
|
+
// Cambiar mensaje de estado después de analizar los primeros 2 frames
|
|
919
|
+
if (this.processedFramesCount === 2) {
|
|
920
|
+
this.updateStatus('Captura activa', 'Buscando documento en el marco de captura', 'active');
|
|
921
|
+
}
|
|
922
|
+
}
|
|
765
923
|
if (this.debug) {
|
|
766
|
-
const frameEndTime = performance.now();
|
|
767
|
-
const totalFrameTime = frameEndTime - frameStartTime;
|
|
768
924
|
this.recordFrameProcessing(totalFrameTime, detections.length);
|
|
769
925
|
}
|
|
770
926
|
// Continue detection loop
|
|
@@ -800,6 +956,12 @@ export class JaakStamps {
|
|
|
800
956
|
clearTimeout(this.alignmentTimer);
|
|
801
957
|
this.alignmentTimer = undefined;
|
|
802
958
|
}
|
|
959
|
+
if (this.orientationTimer) {
|
|
960
|
+
clearTimeout(this.orientationTimer);
|
|
961
|
+
this.orientationTimer = undefined;
|
|
962
|
+
}
|
|
963
|
+
// Liberar recursos de ONNX al limpiar el componente
|
|
964
|
+
this.releaseOnnxResources();
|
|
803
965
|
if (this.performanceUpdateInterval) {
|
|
804
966
|
clearInterval(this.performanceUpdateInterval);
|
|
805
967
|
this.performanceUpdateInterval = undefined;
|
|
@@ -821,7 +983,7 @@ export class JaakStamps {
|
|
|
821
983
|
isCapturing: false
|
|
822
984
|
};
|
|
823
985
|
const cameraInfo = this.cameraInfoWithAutofocus;
|
|
824
|
-
return (h("div", { key: '
|
|
986
|
+
return (h("div", { key: 'f9cbca2fd6b8548a29ace7fcb972b53f402f61af', class: "detector-container" }, h("div", { key: '4f3cac734eb6bfd44a8de644eadca17e7893e11a', class: "video-container" }, h("video", { key: '5e15a38c989a72d5274281272d41ba064447ca6f', ref: el => this.videoRef = el, autoplay: true, muted: true, playsinline: true, class: this.shouldMirrorVideo ? 'mirror' : '', style: { display: captureState.isVideoActive ? 'block' : 'none' } }), h("div", { key: '38c8ad8b691db60e9b5a2a8b641fe6febbeacfca', 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: {
|
|
825
987
|
position: 'absolute',
|
|
826
988
|
left: `${box.x}px`,
|
|
827
989
|
top: `${box.y}px`,
|
|
@@ -830,9 +992,9 @@ export class JaakStamps {
|
|
|
830
992
|
border: '2px solid #32406C',
|
|
831
993
|
pointerEvents: 'none',
|
|
832
994
|
boxSizing: 'border-box'
|
|
833
|
-
} })))), this.isMaskReady && (h("div", { key: '
|
|
995
|
+
} })))), this.isMaskReady && (h("div", { key: 'c56fde56b7a86a4e8a4cf558745291ea5cde12fd', class: "overlay-mask" }, h("div", { key: '04ed3f01f271dc84eed594cb4b60c2453645bd41', class: "card-outline" }, h("div", { key: '648bd423a5e886d4768115237de5896c7223b30b', class: "side side-top" }), h("div", { key: 'b8878059493ca61961b9b3c82ecc18b50b79ac85', class: "side side-right" }), h("div", { key: '09e5ad64ff33dbd5c112bf52b71e14409ab6aa65', class: "side side-bottom" }), h("div", { key: 'b8ba3d83c9bc1b4a0da859d7ed4b9a15eaddcc2a', class: "side side-left" })), !captureState.showFlipAnimation && !captureState.showSuccessAnimation && (h("div", { key: '3279701a240f77ac96e68a34ad95c099340c9a6a', class: `guide-text ${this.showPerformanceMessage ? 'performance-warning-text' : ''}` }, this.getGuideText(captureState.step))), captureState.step === 'back' && !captureState.showFlipAnimation && !captureState.showSuccessAnimation && (h("div", { key: '2adf96d0a57e482372fd9586229dac2301b64fd4', class: "back-capture-section" }, h("div", { key: '305a147c9cfee854c60ac946a48a1d21b2792725', class: "back-capture-buttons" }, (!this.useDocumentDetector || this.performanceDegradedMode || this.showManualCaptureButton) && (h("button", { key: '43270a0647f39307229c540dd5130fb58b1ff4fb', class: "capture-button primary-button", onClick: () => this.takeManualScreenshot(), type: "button", disabled: this.processingButton !== null }, this.processingButton === 'capture-back' && (h("span", { key: 'eb4666603774564eef95a120ac13faeb9e10c8b4', class: "button-spinner" })), "Capturar Reverso")), h("button", { key: 'cac667d9548ae2314ad5a677c5ed90f25184f728', class: "skip-button", onClick: () => this.skipBackCapture(), type: "button", disabled: this.processingButton !== null }, this.processingButton === 'skip-back' && (h("span", { key: '0af7aa4106ce189ae2d79bec78ede01abe25b1e7', class: "button-spinner" })), this.enableBackDocumentTimer && this.backDocumentTimerRemaining > 0
|
|
834
996
|
? `Saltar reverso (${this.backDocumentTimerRemaining}s)`
|
|
835
|
-
: 'Saltar reverso')))), captureState.isVideoActive && (h("div", { key: '
|
|
997
|
+
: 'Saltar reverso')))), captureState.isVideoActive && (h("div", { key: 'de6e5c4fad4d4d996b894457c9af52bc71e533fb', class: "camera-controls" }, h("button", { key: 'cfddcf2420d5f5923a6b02ca446bbe1b8e9fc5e4', 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: '5fb73f958b9a5a6f9242032c4530b0bdab84f6c5', class: "camera-selector-dropdown" }, h("div", { key: '8ee2d26b1001e571e7b6c326e927926fa08287fd', class: "camera-selector-header" }, h("span", { key: '78350b0fb1bd933f7c8c59d8dab0b7122ffbe363' }, "Seleccionar C\u00E1mara"), h("button", { key: 'bb750e0ddd5a1ee5ff96bd57805a7f90b8821e9f', class: "close-selector", onClick: () => this.toggleCameraSelector(), type: "button" }, "\u00D7")), h("div", { key: 'd0f6343cf1b7367f3da33ec8cf64891109d923b8', 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: 'c9642fbfb0acea766d3f0542f4cc9f17f4af4d7b', class: "device-info" }, h("small", { key: 'b6e9507ff7addf011f339f6c22a88a96b40db68a' }, "Dispositivo: ", cameraInfo.deviceType)))), this.showManualCaptureButton && captureState.step === 'front' && !captureState.showFlipAnimation && !captureState.showSuccessAnimation && (h("div", { key: '04ae9262409284782850b90945eb7038673e2097', class: "manual-capture-section" }, h("button", { key: '27c315e503ccba468b89ebd58d6b70f40ddd06ba', class: "manual-capture-button", onClick: () => this.takeManualScreenshot(), type: "button", disabled: this.processingButton !== null }, this.processingButton === 'capture-front' && (h("span", { key: '8f2dcd2df9a218b37eb0b8204a8724362cfeaa53', class: "button-spinner" })), "Capturar Frente"))))), captureState.isCapturing && (h("div", { key: '6fd6fe1d0213b01c8119a67fb8108f73227feee1', class: "capture-animation" })), captureState.showFlipAnimation && (h("div", { key: 'fba3816b48b99ca64510ecbefc0d41431569c9ae', class: "flip-animation" }, h("div", { key: 'beea1c8245b5d81f1008efc6d1b079a61c821e7f', class: "id-card-icon" }), h("div", { key: 'f0fbc34f619a653e2c0ade764ca99e7aa582d157', class: "flip-text" }, "\u00A1Voltea tu identificaci\u00F3n!"))), captureState.showSuccessAnimation && (h("div", { key: 'e4a0e6d31112e3a50024d1d0430b4ad997049fe7', class: "success-animation" }, h("div", { key: '5dd3f781f55630bc904c81207548ae35bc79b452', class: "check-icon" }), h("div", { key: 'a10ebc557582325091efb0abd12d62b27a43b98c', class: "success-text" }, "\u00A1Proceso completado!"))), h("div", { key: '975865955dee1803b5215ecf049701c98b324855', class: `component-status status-${this.currentStatus.type}` }, (this.currentStatus.type === 'loading' || this.currentStatus.type === 'initializing') && (h("div", { key: '84f6f67202e26ce80f7fc5d5f321e23b5bc55c57', class: "status-spinner" })), h("div", { key: 'a1ae56ed156e8feb279a1bf160f749c5d0b5ce45', class: "status-content" }, h("div", { key: 'd62932554cea257ce19763df26e0ba702e50eeca', class: "status-message" }, this.currentStatus.message), this.currentStatus.description && (h("div", { key: 'c5506fa12dd0d37d26dc26c9f2381c843330451f', class: "status-description" }, this.currentStatus.description)))), this.debug && (h("div", { key: '2097eda63185f336f7319014fae2ef6db1e41761', class: "performance-monitor" }, h("div", { key: 'd611c494c6b1a5930ef64aebdcba41e5d94c9ee6', class: "performance-expanded" }, h("div", { key: '800d46831e7b9bec260d9c5e67ea1a3eb9c2933e', class: "metrics-row" }, h("div", { key: 'dd0db1faa9050d98e71da4b5cd26a4a484c37c64', class: "metric-compact" }, h("span", { key: 'f0e6ea61a35ddb2ae5c2a6aff8c255a02c5c1219', class: "metric-label" }, "FPS"), h("span", { key: 'cbf4df4d142f6f01e45f566a8ede8fbbf29ad673', class: `metric-value ${this.performanceData.fps < 15 ? 'warning' : this.performanceData.fps < 10 ? 'danger' : 'good'}` }, this.performanceData.fps)), h("div", { key: '83861ad31fa46c8199a5583e4b3f8eefd44bacd6', class: "metric-compact" }, h("span", { key: '41b8de4cda47959dfc9df393e95fdadde534dd21', class: "metric-label" }, "MEM"), h("span", { key: 'b268f13cc78066e240ed158f1443113a94827aff', class: `metric-value ${this.performanceData.memoryUsage > 100 ? 'warning' : this.performanceData.memoryUsage > 200 ? 'danger' : 'good'}` }, this.performanceData.memoryUsage, "MB"))), h("div", { key: '9f9a9d0690906b186492af23b900c5fa0f5cff89', class: "metrics-row" }, h("div", { key: '84fa6e453dc36e4a74e8b573447b17230766229e', class: "metric-compact" }, h("span", { key: 'f17d52899090c89f532d74c552dd8c5338fc2d83', class: "metric-label" }, "INF"), h("span", { key: '2beca0d7ce55ed574f6b01efc086c5846983e024', class: `metric-value ${this.performanceData.inferenceTime > 100 ? 'warning' : this.performanceData.inferenceTime > 200 ? 'danger' : 'good'}` }, this.performanceData.inferenceTime, "ms")), h("div", { key: '981e300cf076e81bfe946974f6cffd63619eacde', class: "metric-compact" }, h("span", { key: '3dfb0c996a69eb87b0f711322675cad69c471146', class: "metric-label" }, "FRAME"), h("span", { key: '3ae2ac6e90cda77f23e559f3197c3b761f9bdc40', class: `metric-value ${this.performanceData.frameProcessingTime > 50 ? 'warning' : this.performanceData.frameProcessingTime > 100 ? 'danger' : 'good'}` }, this.performanceData.frameProcessingTime, "ms"))), h("div", { key: '2c51216838d87728d9d7764e5ba8a50fa02eb493', class: "metrics-row" }, h("div", { key: 'eda810ffb80ec38ab84583f59b26706f90a50fc9', class: "metric-compact" }, h("span", { key: '978ba9c0c67d6da6b29b3535af33a314bab9276a', class: "metric-label" }, "DET"), h("span", { key: '3e5e169fd4a3906283d8f918b5bbc7c5df6ccd0f', class: "metric-value good" }, this.performanceData.successfulDetections, "/", this.performanceData.totalDetections)), h("div", { key: 'b4b915c745e35c09470a4dc5edfc112d32cfb8b6', class: "metric-compact" }, h("span", { key: '3e2a7b88a4446813c955efb0dfa9e2958e8e1448', class: "metric-label" }, "RATE"), h("span", { key: 'f4f073f7ee622167a241c6a1c535ef580c13d9ab', class: `metric-value ${this.performanceData.detectionRate < 30 ? 'danger' : this.performanceData.detectionRate < 60 ? 'warning' : 'good'}` }, this.performanceData.detectionRate, "%")))))), h("div", { key: '76c556e92f6d7980539f1a30efe8751f665164fb', class: "watermark" }, h("img", { key: 'af23e115d6b4e92a72e55d6d98fde0e4b1d09e9b', src: "https://storage.googleapis.com/jaak-static/commons/powered-by-jaak.png", alt: "Powered by Jaak" })))));
|
|
836
998
|
}
|
|
837
999
|
// Utility methods
|
|
838
1000
|
updateDetectionBoxes(boxes) {
|
|
@@ -948,9 +1110,24 @@ export class JaakStamps {
|
|
|
948
1110
|
}
|
|
949
1111
|
}
|
|
950
1112
|
async takeManualScreenshot() {
|
|
951
|
-
|
|
1113
|
+
console.log('🔵 Botón clicked: Capturar (Frente/Reverso)');
|
|
1114
|
+
// Set processing state immediately
|
|
1115
|
+
const captureState = this.stateManager?.getCaptureState();
|
|
1116
|
+
if (captureState?.step === 'front') {
|
|
1117
|
+
this.processingButton = 'capture-front';
|
|
1118
|
+
}
|
|
1119
|
+
else if (captureState?.step === 'back') {
|
|
1120
|
+
this.processingButton = 'capture-back';
|
|
1121
|
+
}
|
|
1122
|
+
console.log('🔵 processingButton set to:', this.processingButton);
|
|
1123
|
+
// Add small delay to show spinner
|
|
1124
|
+
await new Promise(resolve => setTimeout(resolve, 100));
|
|
1125
|
+
if (!this.videoRef) {
|
|
1126
|
+
this.processingButton = null;
|
|
952
1127
|
return;
|
|
1128
|
+
}
|
|
953
1129
|
// When using manual capture, use mask coordinates for cropping
|
|
1130
|
+
// Note: processingButton will be cleared inside takeScreenshotWithMaskCoordinates
|
|
954
1131
|
await this.takeScreenshotWithMaskCoordinates();
|
|
955
1132
|
}
|
|
956
1133
|
async takeScreenshotWithMaskCoordinates() {
|
|
@@ -1014,18 +1191,25 @@ export class JaakStamps {
|
|
|
1014
1191
|
if (captureState.step === 'front') {
|
|
1015
1192
|
this.stateManager.setCapturedImages({
|
|
1016
1193
|
front: {
|
|
1017
|
-
fullFrame: captureCanvas.toDataURL('image/
|
|
1018
|
-
cropped: croppedCanvas.toDataURL('image/
|
|
1194
|
+
fullFrame: captureCanvas.toDataURL('image/jpeg'),
|
|
1195
|
+
cropped: croppedCanvas.toDataURL('image/jpeg')
|
|
1019
1196
|
}
|
|
1020
1197
|
});
|
|
1021
1198
|
// Check if document classification is enabled (independent of detector)
|
|
1022
1199
|
if (this.useDocumentClassification) {
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1200
|
+
try {
|
|
1201
|
+
const classification = await this.detectionService.classifyDocument(croppedCanvas);
|
|
1202
|
+
if (classification && classification.class === 'passport') {
|
|
1203
|
+
this.completeProcess(true);
|
|
1204
|
+
this.returnCanvasToPool(captureCanvas);
|
|
1205
|
+
this.returnCanvasToPool(croppedCanvas);
|
|
1206
|
+
// Clear processing button when passport is detected
|
|
1207
|
+
this.processingButton = null;
|
|
1208
|
+
return;
|
|
1209
|
+
}
|
|
1210
|
+
}
|
|
1211
|
+
catch (classifyError) {
|
|
1212
|
+
// No cambiar a manual aquí, solo registrar el error y continuar
|
|
1029
1213
|
}
|
|
1030
1214
|
}
|
|
1031
1215
|
this.stateManager.updateCaptureState({
|
|
@@ -1033,22 +1217,28 @@ export class JaakStamps {
|
|
|
1033
1217
|
isDetectionPaused: true,
|
|
1034
1218
|
showFlipAnimation: true
|
|
1035
1219
|
});
|
|
1220
|
+
this.triggerRerender();
|
|
1036
1221
|
setTimeout(() => {
|
|
1037
1222
|
this.stateManager.updateCaptureState({
|
|
1038
1223
|
showFlipAnimation: false,
|
|
1039
1224
|
isDetectionPaused: false
|
|
1040
1225
|
});
|
|
1226
|
+
this.triggerRerender();
|
|
1041
1227
|
this.startBackDocumentTimer();
|
|
1228
|
+
// Clear processing button after animation completes
|
|
1229
|
+
this.processingButton = null;
|
|
1042
1230
|
}, 3000);
|
|
1043
1231
|
}
|
|
1044
1232
|
else if (captureState.step === 'back') {
|
|
1045
1233
|
this.stateManager.setCapturedImages({
|
|
1046
1234
|
back: {
|
|
1047
|
-
fullFrame: captureCanvas.toDataURL('image/
|
|
1048
|
-
cropped: croppedCanvas.toDataURL('image/
|
|
1235
|
+
fullFrame: captureCanvas.toDataURL('image/jpeg'),
|
|
1236
|
+
cropped: croppedCanvas.toDataURL('image/jpeg')
|
|
1049
1237
|
}
|
|
1050
1238
|
});
|
|
1051
1239
|
this.completeProcess(false);
|
|
1240
|
+
// Clear processing button after back capture completes
|
|
1241
|
+
this.processingButton = null;
|
|
1052
1242
|
}
|
|
1053
1243
|
// Return canvases to pool after use
|
|
1054
1244
|
this.returnCanvasToPool(captureCanvas);
|
|
@@ -1081,16 +1271,21 @@ export class JaakStamps {
|
|
|
1081
1271
|
if (captureState.step === 'front') {
|
|
1082
1272
|
this.stateManager.setCapturedImages({
|
|
1083
1273
|
front: {
|
|
1084
|
-
fullFrame: captureCanvas.toDataURL('image/
|
|
1085
|
-
cropped: croppedCanvas.toDataURL('image/
|
|
1274
|
+
fullFrame: captureCanvas.toDataURL('image/jpeg'),
|
|
1275
|
+
cropped: croppedCanvas.toDataURL('image/jpeg')
|
|
1086
1276
|
}
|
|
1087
1277
|
});
|
|
1088
1278
|
// Check if document classification is enabled
|
|
1089
1279
|
if (this.useDocumentClassification) {
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1280
|
+
try {
|
|
1281
|
+
const classification = await this.detectionService.classifyDocument(croppedCanvas);
|
|
1282
|
+
if (classification && classification.class === 'passport') {
|
|
1283
|
+
this.completeProcess(true);
|
|
1284
|
+
return;
|
|
1285
|
+
}
|
|
1286
|
+
}
|
|
1287
|
+
catch (classifyError) {
|
|
1288
|
+
// No cambiar a manual aquí, solo registrar el error y continuar
|
|
1094
1289
|
}
|
|
1095
1290
|
}
|
|
1096
1291
|
this.stateManager.updateCaptureState({
|
|
@@ -1109,8 +1304,8 @@ export class JaakStamps {
|
|
|
1109
1304
|
else if (captureState.step === 'back') {
|
|
1110
1305
|
this.stateManager.setCapturedImages({
|
|
1111
1306
|
back: {
|
|
1112
|
-
fullFrame: captureCanvas.toDataURL('image/
|
|
1113
|
-
cropped: croppedCanvas.toDataURL('image/
|
|
1307
|
+
fullFrame: captureCanvas.toDataURL('image/jpeg'),
|
|
1308
|
+
cropped: croppedCanvas.toDataURL('image/jpeg')
|
|
1114
1309
|
}
|
|
1115
1310
|
});
|
|
1116
1311
|
this.completeProcess(false);
|
|
@@ -1127,6 +1322,138 @@ export class JaakStamps {
|
|
|
1127
1322
|
cardOutline?.classList.remove('capturing');
|
|
1128
1323
|
}, 600);
|
|
1129
1324
|
}
|
|
1325
|
+
// Método simplificado para cambio a modo manual
|
|
1326
|
+
switchToManualMode() {
|
|
1327
|
+
// Only switch if detector is enabled and not already switched
|
|
1328
|
+
if (!this.useDocumentDetector || this.hasAutoSwitchedToManual) {
|
|
1329
|
+
return;
|
|
1330
|
+
}
|
|
1331
|
+
// Detener todo el monitoreo de performance
|
|
1332
|
+
this.stopPerformanceMonitoring();
|
|
1333
|
+
// Liberar recursos de ONNX
|
|
1334
|
+
this.releaseOnnxResources();
|
|
1335
|
+
// Resetear máscara a blanco y limpiar detecciones
|
|
1336
|
+
this.resetMaskToWhite();
|
|
1337
|
+
this.detectionBoxes = [];
|
|
1338
|
+
this.hasAutoSwitchedToManual = true;
|
|
1339
|
+
this.performanceDegradedMode = true;
|
|
1340
|
+
this.showManualCaptureButton = true;
|
|
1341
|
+
this.showPerformanceMessage = true;
|
|
1342
|
+
// Hide performance message after 5 seconds
|
|
1343
|
+
setTimeout(() => {
|
|
1344
|
+
this.showPerformanceMessage = false;
|
|
1345
|
+
}, 5000);
|
|
1346
|
+
}
|
|
1347
|
+
// Método para cambiar a modo manual por errores de ONNX
|
|
1348
|
+
switchToManualModeWithWarning(warningMessage) {
|
|
1349
|
+
// Solo cambiar si el detector está habilitado y no se ha cambiado ya
|
|
1350
|
+
if (!this.useDocumentDetector || this.hasAutoSwitchedToManual) {
|
|
1351
|
+
return;
|
|
1352
|
+
}
|
|
1353
|
+
// No necesitamos liberar recursos ONNX porque no se han cargado aún
|
|
1354
|
+
// Resetear máscara a blanco y limpiar detecciones
|
|
1355
|
+
this.resetMaskToWhite();
|
|
1356
|
+
this.detectionBoxes = [];
|
|
1357
|
+
// Cambiar a modo manual
|
|
1358
|
+
this.hasAutoSwitchedToManual = true;
|
|
1359
|
+
this.performanceDegradedMode = true;
|
|
1360
|
+
this.showManualCaptureButton = true;
|
|
1361
|
+
this.useDocumentDetector = false; // Desactivar detector automático
|
|
1362
|
+
// Actualizar el estado con mensaje informativo (no error)
|
|
1363
|
+
this.updateStatus('Modo manual activado', warningMessage, 'active');
|
|
1364
|
+
// Continuar con la configuración de cámara
|
|
1365
|
+
this.continueWithCameraSetup();
|
|
1366
|
+
}
|
|
1367
|
+
async continueWithCameraSetup() {
|
|
1368
|
+
try {
|
|
1369
|
+
// Paso 2: Detectar y configurar dispositivos
|
|
1370
|
+
this.updateStatus('Configurando cámara...', 'Buscando dispositivos disponibles', 'loading');
|
|
1371
|
+
try {
|
|
1372
|
+
await this.cameraService.enumerateDevices();
|
|
1373
|
+
}
|
|
1374
|
+
catch (enumerateError) {
|
|
1375
|
+
throw new Error(`Error al enumerar dispositivos: ${enumerateError.message}`);
|
|
1376
|
+
}
|
|
1377
|
+
try {
|
|
1378
|
+
await this.updateCameraInfoWithAutofocus();
|
|
1379
|
+
}
|
|
1380
|
+
catch (cameraInfoError) {
|
|
1381
|
+
throw new Error(`Error al actualizar información de cámara: ${cameraInfoError.message}`);
|
|
1382
|
+
}
|
|
1383
|
+
// Paso 3: Configurar cámara seleccionada
|
|
1384
|
+
this.updateStatus('Ajustando cámara...', 'Configurando calidad de imagen', 'loading');
|
|
1385
|
+
let stream;
|
|
1386
|
+
try {
|
|
1387
|
+
stream = await this.cameraService.setupCamera();
|
|
1388
|
+
}
|
|
1389
|
+
catch (setupError) {
|
|
1390
|
+
throw new Error(`Error al configurar cámara: ${setupError.message}`);
|
|
1391
|
+
}
|
|
1392
|
+
// Paso 4: Inicializar video
|
|
1393
|
+
this.updateStatus('Captura manual', 'Posicione su documento y use el botón para capturar', 'active');
|
|
1394
|
+
try {
|
|
1395
|
+
await this.initializeVideoStream(stream);
|
|
1396
|
+
}
|
|
1397
|
+
catch (videoError) {
|
|
1398
|
+
throw new Error(`Error al inicializar video: ${videoError.message}`);
|
|
1399
|
+
}
|
|
1400
|
+
// Paso 5: Calibrar máscara
|
|
1401
|
+
if (this.detectionContainer) {
|
|
1402
|
+
const container = this.detectionContainer.parentElement;
|
|
1403
|
+
const rect = container.getBoundingClientRect();
|
|
1404
|
+
this.updateMaskDimensions(rect);
|
|
1405
|
+
}
|
|
1406
|
+
// Finalizar
|
|
1407
|
+
this.startTime = Date.now();
|
|
1408
|
+
this.stateManager.updateCaptureState({ isLoading: false });
|
|
1409
|
+
// Mostrar botón manual (ya debe estar habilitado)
|
|
1410
|
+
this.showManualCaptureButton = true;
|
|
1411
|
+
}
|
|
1412
|
+
catch (error) {
|
|
1413
|
+
this.updateStatus('Error de cámara', error.message, 'error');
|
|
1414
|
+
throw error;
|
|
1415
|
+
}
|
|
1416
|
+
}
|
|
1417
|
+
switchToManualModeWithError(errorMessage) {
|
|
1418
|
+
// Solo cambiar si el detector está habilitado y no se ha cambiado ya
|
|
1419
|
+
if (!this.useDocumentDetector || this.hasAutoSwitchedToManual) {
|
|
1420
|
+
return;
|
|
1421
|
+
}
|
|
1422
|
+
// Liberar recursos de ONNX inmediatamente
|
|
1423
|
+
this.releaseOnnxResources();
|
|
1424
|
+
// Resetear máscara a blanco y limpiar detecciones
|
|
1425
|
+
this.resetMaskToWhite();
|
|
1426
|
+
this.detectionBoxes = [];
|
|
1427
|
+
// Cambiar a modo manual
|
|
1428
|
+
this.hasAutoSwitchedToManual = true;
|
|
1429
|
+
this.performanceDegradedMode = true;
|
|
1430
|
+
this.showManualCaptureButton = true;
|
|
1431
|
+
this.useDocumentDetector = false; // Desactivar detector automático
|
|
1432
|
+
// Actualizar el estado con mensaje de error
|
|
1433
|
+
this.updateStatus('Modo manual activado', errorMessage, 'error');
|
|
1434
|
+
// Continuar con la configuración de cámara para mostrar el video
|
|
1435
|
+
this.continueWithCameraSetup().catch(() => {
|
|
1436
|
+
// Si falla la configuración de cámara, mantener el mensaje de error pero cambiar el estado
|
|
1437
|
+
setTimeout(() => {
|
|
1438
|
+
this.updateStatus('Captura manual', 'Use el botón para capturar', 'ready');
|
|
1439
|
+
}, 5000);
|
|
1440
|
+
});
|
|
1441
|
+
}
|
|
1442
|
+
stopPerformanceMonitoring() {
|
|
1443
|
+
// Nota: No necesitamos limpiar más variables porque ya no se usarán
|
|
1444
|
+
// El sistema automáticamente saltará la lógica de performance una vez que hasAutoSwitchedToManual = true
|
|
1445
|
+
}
|
|
1446
|
+
// Método para liberar recursos de ONNX cuando se cambia a modo manual
|
|
1447
|
+
releaseOnnxResources() {
|
|
1448
|
+
try {
|
|
1449
|
+
// Liberar recursos del servicio de detección
|
|
1450
|
+
if (this.detectionService) {
|
|
1451
|
+
this.detectionService.cleanup();
|
|
1452
|
+
}
|
|
1453
|
+
}
|
|
1454
|
+
catch (error) {
|
|
1455
|
+
}
|
|
1456
|
+
}
|
|
1130
1457
|
completeProcess(skippedBack = false) {
|
|
1131
1458
|
this.stateManager.updateCaptureState({
|
|
1132
1459
|
step: 'completed',
|
|
@@ -1197,6 +1524,12 @@ export class JaakStamps {
|
|
|
1197
1524
|
try {
|
|
1198
1525
|
// Close the selector immediately when user selects a camera
|
|
1199
1526
|
this.showCameraSelector = false;
|
|
1527
|
+
// Check if user selected the same camera that's already active
|
|
1528
|
+
const currentCameraId = this.cameraService.getSelectedCameraId();
|
|
1529
|
+
if (currentCameraId === cameraId) {
|
|
1530
|
+
// Same camera selected, just close the selector without switching
|
|
1531
|
+
return;
|
|
1532
|
+
}
|
|
1200
1533
|
this.isSwitchingCamera = true;
|
|
1201
1534
|
// Stop current video stream
|
|
1202
1535
|
if (this.videoStream) {
|
|
@@ -1240,6 +1573,13 @@ export class JaakStamps {
|
|
|
1240
1573
|
this.detectionBoxes = [];
|
|
1241
1574
|
this.alignmentStartTime = undefined;
|
|
1242
1575
|
this.hasDocumentDetected = false;
|
|
1576
|
+
// Reset sistema simplificado
|
|
1577
|
+
this.processedFramesCount = 0;
|
|
1578
|
+
this.slowFrameCount = 0;
|
|
1579
|
+
// Reset performance degradation state
|
|
1580
|
+
this.hasAutoSwitchedToManual = false;
|
|
1581
|
+
this.performanceDegradedMode = false;
|
|
1582
|
+
this.showPerformanceMessage = false;
|
|
1243
1583
|
if (this.alignmentTimer) {
|
|
1244
1584
|
clearTimeout(this.alignmentTimer);
|
|
1245
1585
|
this.alignmentTimer = undefined;
|
|
@@ -1343,6 +1683,24 @@ export class JaakStamps {
|
|
|
1343
1683
|
// Performance is good, use base frame skip
|
|
1344
1684
|
return this.BASE_FRAME_SKIP;
|
|
1345
1685
|
}
|
|
1686
|
+
// Método para resetear la máscara a color blanco
|
|
1687
|
+
resetMaskToWhite() {
|
|
1688
|
+
const cardOutline = this.el.shadowRoot?.querySelector('.card-outline');
|
|
1689
|
+
const corners = this.el.shadowRoot?.querySelectorAll('.corner');
|
|
1690
|
+
const topSide = this.el.shadowRoot?.querySelector('.side-top');
|
|
1691
|
+
const rightSide = this.el.shadowRoot?.querySelector('.side-right');
|
|
1692
|
+
const bottomSide = this.el.shadowRoot?.querySelector('.side-bottom');
|
|
1693
|
+
const leftSide = this.el.shadowRoot?.querySelector('.side-left');
|
|
1694
|
+
// Remover todas las clases de alineación y estado perfecto
|
|
1695
|
+
cardOutline?.classList.remove('perfect-match');
|
|
1696
|
+
corners?.forEach(corner => corner.classList.remove('perfect-match'));
|
|
1697
|
+
topSide?.classList.remove('aligned');
|
|
1698
|
+
rightSide?.classList.remove('aligned');
|
|
1699
|
+
bottomSide?.classList.remove('aligned');
|
|
1700
|
+
leftSide?.classList.remove('aligned');
|
|
1701
|
+
// Resetear estado de alineación
|
|
1702
|
+
this.sideAlignment = { top: false, right: false, bottom: false, left: false };
|
|
1703
|
+
}
|
|
1346
1704
|
// Canvas pool management for screenshots
|
|
1347
1705
|
getPooledCanvas(width, height) {
|
|
1348
1706
|
// Try to find a canvas with matching dimensions
|
|
@@ -1602,7 +1960,11 @@ export class JaakStamps {
|
|
|
1602
1960
|
"currentStatus": {},
|
|
1603
1961
|
"performanceData": {},
|
|
1604
1962
|
"backDocumentTimerRemaining": {},
|
|
1605
|
-
"showManualCaptureButton": {}
|
|
1963
|
+
"showManualCaptureButton": {},
|
|
1964
|
+
"performanceDegradedMode": {},
|
|
1965
|
+
"showPerformanceMessage": {},
|
|
1966
|
+
"captureStateVersion": {},
|
|
1967
|
+
"processingButton": {}
|
|
1606
1968
|
};
|
|
1607
1969
|
}
|
|
1608
1970
|
static get events() {
|