@jaak.ai/stamps 2.0.0-dev.39 → 2.0.0-dev.41
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -1
- package/dist/cjs/jaak-stamps-webcomponent.cjs.js +1 -1
- package/dist/cjs/jaak-stamps.cjs.entry.js +875 -13
- package/dist/cjs/jaak-stamps.cjs.entry.js.map +1 -1
- package/dist/cjs/jaak-stamps.entry.cjs.js.map +1 -1
- package/dist/cjs/loader.cjs.js +1 -1
- package/dist/collection/components/my-component/my-component.css +66 -0
- package/dist/collection/components/my-component/my-component.js +501 -10
- package/dist/collection/components/my-component/my-component.js.map +1 -1
- package/dist/collection/services/CameraService.js +161 -0
- package/dist/collection/services/CameraService.js.map +1 -1
- package/dist/collection/services/DetectionService.js +164 -1
- package/dist/collection/services/DetectionService.js.map +1 -1
- package/dist/collection/services/ImageQualityService.js +329 -0
- package/dist/collection/services/ImageQualityService.js.map +1 -0
- package/dist/collection/services/ServiceContainer.js +6 -1
- package/dist/collection/services/ServiceContainer.js.map +1 -1
- package/dist/collection/services/interfaces/ICameraService.js.map +1 -1
- package/dist/collection/services/interfaces/IDetectionService.js.map +1 -1
- package/dist/collection/services/interfaces/IImageQualityService.js +2 -0
- package/dist/collection/services/interfaces/IImageQualityService.js.map +1 -0
- package/dist/components/jaak-stamps.js +892 -14
- 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 +875 -13
- package/dist/esm/jaak-stamps.entry.js.map +1 -1
- package/dist/esm/loader.js +1 -1
- package/dist/jaak-stamps-webcomponent/jaak-stamps-webcomponent.esm.js +1 -1
- package/dist/jaak-stamps-webcomponent/jaak-stamps.entry.esm.js.map +1 -1
- package/dist/jaak-stamps-webcomponent/p-47f37982.entry.js +2 -0
- package/dist/jaak-stamps-webcomponent/p-47f37982.entry.js.map +1 -0
- package/dist/types/components/my-component/my-component.d.ts +48 -0
- package/dist/types/components.d.ts +65 -0
- package/dist/types/services/CameraService.d.ts +5 -0
- package/dist/types/services/DetectionService.d.ts +22 -2
- package/dist/types/services/ImageQualityService.d.ts +31 -0
- package/dist/types/services/ServiceContainer.d.ts +7 -0
- package/dist/types/services/interfaces/ICameraService.d.ts +2 -0
- package/dist/types/services/interfaces/IDetectionService.d.ts +13 -0
- package/dist/types/services/interfaces/IImageQualityService.d.ts +58 -0
- package/package.json +1 -1
- package/dist/jaak-stamps-webcomponent/p-043b1cfd.entry.js +0 -2
- package/dist/jaak-stamps-webcomponent/p-043b1cfd.entry.js.map +0 -1
|
@@ -254,6 +254,8 @@ export class CameraService {
|
|
|
254
254
|
const capabilities = videoTrack.getCapabilities();
|
|
255
255
|
tempStream.getTracks().forEach(track => track.stop());
|
|
256
256
|
const constraints = { ...videoConstraints };
|
|
257
|
+
// Enhanced focus and image quality settings
|
|
258
|
+
this.applyAdvancedCameraSettings(constraints, capabilities);
|
|
257
259
|
if (capabilities.width && capabilities.height) {
|
|
258
260
|
const maxWidth = Math.min(capabilities.width.max, 1920);
|
|
259
261
|
const maxHeight = Math.min(capabilities.height.max, 1080);
|
|
@@ -267,6 +269,10 @@ export class CameraService {
|
|
|
267
269
|
constraints.height = { ideal: maxHeight };
|
|
268
270
|
}
|
|
269
271
|
}
|
|
272
|
+
this.logger.state('CONFIGURACION_CAMARA_OPTIMIZADA', {
|
|
273
|
+
constraints,
|
|
274
|
+
capabilities: this.getCapabilitiesSummary(capabilities)
|
|
275
|
+
});
|
|
270
276
|
return constraints;
|
|
271
277
|
}
|
|
272
278
|
catch (err) {
|
|
@@ -276,6 +282,8 @@ export class CameraService {
|
|
|
276
282
|
width: { ideal: isTablet ? 1280 : 1920 },
|
|
277
283
|
height: { ideal: isTablet ? 720 : 1080 }
|
|
278
284
|
};
|
|
285
|
+
// Apply basic focus settings even for fallback
|
|
286
|
+
this.applyBasicFocusSettings(fallbackConstraints);
|
|
279
287
|
if (this.selectedCameraId) {
|
|
280
288
|
fallbackConstraints.deviceId = { exact: this.selectedCameraId };
|
|
281
289
|
}
|
|
@@ -288,5 +296,158 @@ export class CameraService {
|
|
|
288
296
|
return fallbackConstraints;
|
|
289
297
|
}
|
|
290
298
|
}
|
|
299
|
+
applyAdvancedCameraSettings(constraints, capabilities) {
|
|
300
|
+
// Apply standard camera settings that are widely supported
|
|
301
|
+
// Frame rate optimization
|
|
302
|
+
if (capabilities.frameRate) {
|
|
303
|
+
constraints.frameRate = {
|
|
304
|
+
ideal: 30,
|
|
305
|
+
min: 15,
|
|
306
|
+
max: 60
|
|
307
|
+
};
|
|
308
|
+
}
|
|
309
|
+
// Width and height constraints for optimal resolution
|
|
310
|
+
if (capabilities.width && capabilities.height) {
|
|
311
|
+
constraints.width = {
|
|
312
|
+
ideal: Math.min(1920, capabilities.width.max || 1920),
|
|
313
|
+
min: 640
|
|
314
|
+
};
|
|
315
|
+
constraints.height = {
|
|
316
|
+
ideal: Math.min(1080, capabilities.height.max || 1080),
|
|
317
|
+
min: 480
|
|
318
|
+
};
|
|
319
|
+
}
|
|
320
|
+
// Apply enhanced constraints through advanced array for better browser compatibility
|
|
321
|
+
const advancedConstraints = [];
|
|
322
|
+
// Try to set optimal settings through advanced constraints
|
|
323
|
+
const advanced = {};
|
|
324
|
+
// Focus mode - use 'any' type to bypass TypeScript limitations
|
|
325
|
+
if (capabilities.focusMode) {
|
|
326
|
+
if (capabilities.focusMode.includes('continuous')) {
|
|
327
|
+
advanced.focusMode = 'continuous';
|
|
328
|
+
}
|
|
329
|
+
else if (capabilities.focusMode.includes('single-shot')) {
|
|
330
|
+
advanced.focusMode = 'single-shot';
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
// Focus distance for close objects
|
|
334
|
+
if (capabilities.focusDistance) {
|
|
335
|
+
advanced.focusDistance = {
|
|
336
|
+
ideal: 0.4, // 40cm - optimal for document capture
|
|
337
|
+
min: 0.2,
|
|
338
|
+
max: 1.0
|
|
339
|
+
};
|
|
340
|
+
}
|
|
341
|
+
// Zoom control
|
|
342
|
+
if (capabilities.zoom) {
|
|
343
|
+
advanced.zoom = {
|
|
344
|
+
ideal: 1.0,
|
|
345
|
+
min: capabilities.zoom.min,
|
|
346
|
+
max: Math.min(capabilities.zoom.max, 3.0)
|
|
347
|
+
};
|
|
348
|
+
}
|
|
349
|
+
// Add advanced constraints if any were set
|
|
350
|
+
if (Object.keys(advanced).length > 0) {
|
|
351
|
+
advancedConstraints.push(advanced);
|
|
352
|
+
}
|
|
353
|
+
if (advancedConstraints.length > 0) {
|
|
354
|
+
constraints.advanced = advancedConstraints;
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
applyBasicFocusSettings(constraints) {
|
|
358
|
+
// Apply basic focus settings when capabilities are not available
|
|
359
|
+
// Use advanced constraints for non-standard properties
|
|
360
|
+
const advanced = {
|
|
361
|
+
focusMode: 'continuous',
|
|
362
|
+
exposureMode: 'continuous',
|
|
363
|
+
whiteBalanceMode: 'continuous'
|
|
364
|
+
};
|
|
365
|
+
if (!constraints.advanced) {
|
|
366
|
+
constraints.advanced = [];
|
|
367
|
+
}
|
|
368
|
+
constraints.advanced.push(advanced);
|
|
369
|
+
constraints.frameRate = { ideal: 30 };
|
|
370
|
+
}
|
|
371
|
+
getCapabilitiesSummary(capabilities) {
|
|
372
|
+
return {
|
|
373
|
+
hasAutoFocus: !!capabilities.focusMode,
|
|
374
|
+
hasFocusDistance: !!capabilities.focusDistance,
|
|
375
|
+
hasExposureControl: !!capabilities.exposureMode,
|
|
376
|
+
hasWhiteBalance: !!capabilities.whiteBalanceMode,
|
|
377
|
+
hasZoom: !!capabilities.zoom,
|
|
378
|
+
hasTorch: capabilities.torch !== undefined,
|
|
379
|
+
hasImageControls: !!(capabilities.brightness || capabilities.contrast || capabilities.saturation || capabilities.sharpness),
|
|
380
|
+
resolutionRange: capabilities.width && capabilities.height ?
|
|
381
|
+
`${capabilities.width.min}x${capabilities.height.min} - ${capabilities.width.max}x${capabilities.height.max}` : 'unknown'
|
|
382
|
+
};
|
|
383
|
+
}
|
|
384
|
+
// New method to enable torch/flash for better illumination
|
|
385
|
+
async setTorchEnabled(enabled, stream) {
|
|
386
|
+
try {
|
|
387
|
+
if (!stream) {
|
|
388
|
+
this.logger.warn('No hay stream disponible para controlar el flash');
|
|
389
|
+
return false;
|
|
390
|
+
}
|
|
391
|
+
const videoTrack = stream.getVideoTracks()[0];
|
|
392
|
+
if (!videoTrack) {
|
|
393
|
+
this.logger.warn('No hay track de video disponible');
|
|
394
|
+
return false;
|
|
395
|
+
}
|
|
396
|
+
const capabilities = videoTrack.getCapabilities();
|
|
397
|
+
if (capabilities.torch === undefined) {
|
|
398
|
+
this.logger.warn('El dispositivo no soporta control de flash');
|
|
399
|
+
return false;
|
|
400
|
+
}
|
|
401
|
+
await videoTrack.applyConstraints({
|
|
402
|
+
advanced: [{ torch: enabled }]
|
|
403
|
+
});
|
|
404
|
+
this.logger.state('FLASH_CONTROLADO', { enabled });
|
|
405
|
+
return true;
|
|
406
|
+
}
|
|
407
|
+
catch (error) {
|
|
408
|
+
this.logger.error('Error al controlar el flash:', error);
|
|
409
|
+
return false;
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
// Method to manually trigger focus on a specific point
|
|
413
|
+
async focusAtPoint(x, y, stream) {
|
|
414
|
+
try {
|
|
415
|
+
if (!stream) {
|
|
416
|
+
this.logger.warn('No hay stream disponible para enfocar');
|
|
417
|
+
return false;
|
|
418
|
+
}
|
|
419
|
+
const videoTrack = stream.getVideoTracks()[0];
|
|
420
|
+
if (!videoTrack) {
|
|
421
|
+
this.logger.warn('No hay track de video disponible');
|
|
422
|
+
return false;
|
|
423
|
+
}
|
|
424
|
+
const capabilities = videoTrack.getCapabilities();
|
|
425
|
+
if (!capabilities.focusMode || !capabilities.focusMode.includes('single-shot')) {
|
|
426
|
+
this.logger.warn('El dispositivo no soporta enfoque manual');
|
|
427
|
+
return false;
|
|
428
|
+
}
|
|
429
|
+
// Trigger single-shot focus
|
|
430
|
+
await videoTrack.applyConstraints({
|
|
431
|
+
advanced: [{ focusMode: 'single-shot' }]
|
|
432
|
+
});
|
|
433
|
+
// Return to continuous focus after a short delay
|
|
434
|
+
setTimeout(async () => {
|
|
435
|
+
try {
|
|
436
|
+
await videoTrack.applyConstraints({
|
|
437
|
+
advanced: [{ focusMode: 'continuous' }]
|
|
438
|
+
});
|
|
439
|
+
}
|
|
440
|
+
catch (error) {
|
|
441
|
+
this.logger.warn('Error al volver a enfoque continuo:', error);
|
|
442
|
+
}
|
|
443
|
+
}, 1000);
|
|
444
|
+
this.logger.state('ENFOQUE_MANUAL_ACTIVADO', { x, y });
|
|
445
|
+
return true;
|
|
446
|
+
}
|
|
447
|
+
catch (error) {
|
|
448
|
+
this.logger.error('Error al enfocar manualmente:', error);
|
|
449
|
+
return false;
|
|
450
|
+
}
|
|
451
|
+
}
|
|
291
452
|
}
|
|
292
453
|
//# sourceMappingURL=CameraService.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CameraService.js","sourceRoot":"","sources":["../../src/services/CameraService.ts"],"names":[],"mappings":"AAIA,MAAM,OAAO,aAAa;IAQd;IACA;IARF,gBAAgB,GAAsB,EAAE,CAAC;IACzC,gBAAgB,GAAkB,IAAI,CAAC;IACvC,UAAU,GAAoC,SAAS,CAAC;IACxD,qBAAqB,GAAkC,IAAI,CAAC;IAC5D,eAAe,GAA8B,MAAM,CAAC;IAE5D,YACU,MAAe,EACf,QAAmB,EAC3B,kBAA6C,MAAM;QAF3C,WAAM,GAAN,MAAM,CAAS;QACf,aAAQ,GAAR,QAAQ,CAAW;QAG3B,IAAI,CAAC,eAAe,GAAG,eAAe,CAAC;IACzC,CAAC;IAED,KAAK,CAAC,gBAAgB;QACpB,MAAM,SAAS,GAAG,SAAS,CAAC,SAAS,CAAC;QACtC,MAAM,QAAQ,GAAG,0DAA0D,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;QAC5F,MAAM,QAAQ,GAAG,eAAe,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,MAAM,CAAC,UAAU,IAAI,GAAG,CAAC;QAE7E,IAAI,QAAQ,EAAE,CAAC;YACb,IAAI,CAAC,UAAU,GAAG,QAAQ,CAAC;QAC7B,CAAC;aAAM,IAAI,QAAQ,EAAE,CAAC;YACpB,IAAI,CAAC,UAAU,GAAG,QAAQ,CAAC;QAC7B,CAAC;aAAM,CAAC;YACN,IAAI,CAAC,UAAU,GAAG,SAAS,CAAC;QAC9B,CAAC;QAED,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,uBAAuB,EAAE;YACzC,UAAU,EAAE,IAAI,CAAC,UAAU;YAC3B,SAAS,EAAE,SAAS,CAAC,SAAS;YAC9B,gBAAgB,EAAE,EAAE,KAAK,EAAE,MAAM,CAAC,UAAU,EAAE,MAAM,EAAE,MAAM,CAAC,WAAW,EAAE;SAC3E,CAAC,CAAC;QAEH,OAAO,IAAI,CAAC,UAAU,CAAC;IACzB,CAAC;IAED,KAAK,CAAC,gBAAgB;QACpB,IAAI,CAAC;YACH,MAAM,gBAAgB,GAAG,MAAM,IAAI,CAAC,qBAAqB,EAAE,CAAC;YAE5D,IAAI,gBAAgB,KAAK,QAAQ,EAAE,CAAC;gBAClC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,2CAA2C,CAAC,CAAC;gBAC/D,OAAO,EAAE,CAAC;YACZ,CAAC;YAED,IAAI,gBAAgB,KAAK,QAAQ,EAAE,CAAC;gBAClC,MAAM,UAAU,GAAG,MAAM,SAAS,CAAC,YAAY,CAAC,YAAY,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;gBAC9E,UAAU,CAAC,SAAS,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC;YACxD,CAAC;YAED,MAAM,OAAO,GAAG,MAAM,SAAS,CAAC,YAAY,CAAC,gBAAgB,EAAE,CAAC;YAChE,IAAI,CAAC,gBAAgB,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC,MAAM,CAAC,IAAI,KAAK,YAAY,CAAC,CAAC;YAE/E,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,oBAAoB,EAAE;gBACtC,KAAK,EAAE,IAAI,CAAC,gBAAgB,CAAC,MAAM;gBACnC,OAAO,EAAE,IAAI,CAAC,gBAAgB,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;oBACzC,EAAE,EAAE,GAAG,CAAC,QAAQ;oBAChB,KAAK,EAAE,GAAG,CAAC,KAAK,IAAI,gBAAgB;iBACrC,CAAC,CAAC;aACJ,CAAC,CAAC;YAEH,IAAI,CAAC,0BAA0B,EAAE,CAAC;YAClC,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,gBAAgB,EAAE,IAAI,CAAC,gBAAgB,CAAC,CAAC;YAE5D,OAAO,IAAI,CAAC,gBAAgB,CAAC;QAC/B,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,wCAAwC,EAAE,KAAK,CAAC,CAAC;YACnE,IAAI,CAAC,2BAA2B,CAAC,KAAK,CAAC,CAAC;YACxC,OAAO,EAAE,CAAC;QACZ,CAAC;IACH,CAAC;IAED,mBAAmB;QACjB,OAAO,CAAC,GAAG,IAAI,CAAC,gBAAgB,CAAC,CAAC;IACpC,CAAC;IAED,0BAA0B;QACxB,OAAO,IAAI,CAAC,gBAAgB,CAAC,MAAM,GAAG,CAAC,CAAC;IAC1C,CAAC;IAED,mBAAmB;QACjB,OAAO,IAAI,CAAC,gBAAgB,CAAC;IAC/B,CAAC;IAED,KAAK,CAAC,iBAAiB,CAAC,QAAgB;QACtC,MAAM,MAAM,GAAG,IAAI,CAAC,gBAAgB,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,QAAQ,KAAK,QAAQ,CAAC,CAAC;QAC5E,IAAI,CAAC,MAAM,EAAE,CAAC;YACZ,MAAM,IAAI,KAAK,CAAC,kBAAkB,QAAQ,YAAY,CAAC,CAAC;QAC1D,CAAC;QAED,IAAI,CAAC,gBAAgB,GAAG,QAAQ,CAAC;QACjC,IAAI,CAAC,qBAAqB,CAAC,MAAM,CAAC,CAAC;QACnC,IAAI,CAAC,cAAc,EAAE,CAAC;QACtB,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,gBAAgB,EAAE,QAAQ,CAAC,CAAC;IACjD,CAAC;IAED,kBAAkB;QAChB,OAAO,IAAI,CAAC,qBAAqB,CAAC;IACpC,CAAC;IAED,KAAK,CAAC,WAAW,CAAC,WAAmC;QACnD,MAAM,gBAAgB,GAAG,WAAW,IAAI,MAAM,IAAI,CAAC,gBAAgB,EAAE,CAAC;QAEtE,MAAM,MAAM,GAAG,MAAM,SAAS,CAAC,YAAY,CAAC,YAAY,CAAC;YACvD,KAAK,EAAE,gBAAgB;YACvB,KAAK,EAAE,KAAK;SACb,CAAC,CAAC;QAEH,OAAO,MAAM,CAAC;IAChB,CAAC;IAED,KAAK,CAAC,YAAY,CAAC,QAAgB;QACjC,MAAM,cAAc,GAAG,IAAI,CAAC,gBAAgB,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,QAAQ,KAAK,QAAQ,CAAC,CAAC;QACpF,IAAI,CAAC,cAAc,EAAE,CAAC;YACpB,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,kEAAkE,CAAC,CAAC;YACrF,MAAM,IAAI,CAAC,gBAAgB,EAAE,CAAC;YAC9B,OAAO;QACT,CAAC;QAED,MAAM,IAAI,CAAC,iBAAiB,CAAC,QAAQ,CAAC,CAAC;QACvC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,iBAAiB,EAAE;YACnC,KAAK,EAAE,cAAc,CAAC,KAAK;YAC3B,QAAQ,EAAE,cAAc,CAAC,QAAQ;SAClC,CAAC,CAAC;IACL,CAAC;IAED,KAAK,CAAC,gBAAgB;QACpB,IAAI,CAAC,IAAI,CAAC,0BAA0B,EAAE;YAAE,OAAO;QAE/C,MAAM,YAAY,GAAG,IAAI,CAAC,gBAAgB,CAAC,SAAS,CAClD,MAAM,CAAC,EAAE,CAAC,MAAM,CAAC,QAAQ,KAAK,IAAI,CAAC,gBAAgB,CACpD,CAAC;QAEF,MAAM,SAAS,GAAG,CAAC,YAAY,GAAG,CAAC,CAAC,GAAG,IAAI,CAAC,gBAAgB,CAAC,MAAM,CAAC;QACpE,MAAM,UAAU,GAAG,IAAI,CAAC,gBAAgB,CAAC,SAAS,CAAC,CAAC;QAEpD,MAAM,IAAI,CAAC,YAAY,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC;IAC/C,CAAC;IAED,cAAc;QACZ,IAAI,CAAC;YACH,MAAM,UAAU,GAAqB;gBACnC,QAAQ,EAAE,IAAI,CAAC,gBAAiB;gBAChC,MAAM,EAAE,IAAI,CAAC,qBAAqB;gBAClC,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE;aACtB,CAAC;YAEF,YAAY,CAAC,OAAO,CAAC,+BAA+B,EAAE,IAAI,CAAC,SAAS,CAAC,UAAU,CAAC,CAAC,CAAC;YAClF,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,6BAA6B,EAAE,UAAU,CAAC,CAAC;QAC/D,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,yCAAyC,EAAE,KAAK,CAAC,CAAC;QACrE,CAAC;IACH,CAAC;IAED,cAAc;QACZ,IAAI,CAAC;YACH,MAAM,KAAK,GAAG,YAAY,CAAC,OAAO,CAAC,+BAA+B,CAAC,CAAC;YACpE,IAAI,KAAK,EAAE,CAAC;gBACV,MAAM,UAAU,GAAqB,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;gBAEvD,MAAM,gBAAgB,GAAG,IAAI,CAAC,gBAAgB,CAAC,IAAI,CACjD,MAAM,CAAC,EAAE,CAAC,MAAM,CAAC,QAAQ,KAAK,UAAU,CAAC,QAAQ,CAClD,CAAC;gBAEF,IAAI,gBAAgB,EAAE,CAAC;oBACrB,IAAI,CAAC,gBAAgB,GAAG,UAAU,CAAC,QAAQ,CAAC;oBAC5C,IAAI,CAAC,qBAAqB,GAAG,UAAU,CAAC,MAAM,CAAC;oBAC/C,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,4BAA4B,EAAE,UAAU,CAAC,CAAC;gBAC9D,CAAC;YACH,CAAC;QACH,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,wCAAwC,EAAE,KAAK,CAAC,CAAC;QACpE,CAAC;IACH,CAAC;IAED,YAAY,CAAC,MAAmB;QAC9B,MAAM,UAAU,GAAG,MAAM,CAAC,cAAc,EAAE,CAAC,CAAC,CAAC,CAAC;QAC9C,IAAI,CAAC,UAAU;YAAE,OAAO,KAAK,CAAC;QAE9B,MAAM,QAAQ,GAAG,UAAU,CAAC,WAAW,EAAE,CAAC;QAC1C,OAAO,QAAQ,CAAC,UAAU,KAAK,aAAa,CAAC;IAC/C,CAAC;IAED,aAAa;QAOX,OAAO;YACL,gBAAgB,EAAE,IAAI,CAAC,gBAAgB,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;gBACrD,EAAE,EAAE,MAAM,CAAC,QAAQ;gBACnB,KAAK,EAAE,MAAM,CAAC,KAAK,IAAI,gBAAgB;gBACvC,QAAQ,EAAE,MAAM,CAAC,QAAQ,KAAK,IAAI,CAAC,gBAAgB;aACpD,CAAC,CAAC;YACH,gBAAgB,EAAE,IAAI,CAAC,gBAAgB;YACvC,UAAU,EAAE,IAAI,CAAC,UAAU;YAC3B,0BAA0B,EAAE,IAAI,CAAC,0BAA0B,EAAE;YAC7D,eAAe,EAAE,IAAI,CAAC,qBAAqB;SAC5C,CAAC;IACJ,CAAC;IAEO,KAAK,CAAC,qBAAqB;QACjC,IAAI,CAAC;YACH,IAAI,CAAC,SAAS,CAAC,WAAW,EAAE,CAAC;gBAC3B,OAAO,QAAQ,CAAC;YAClB,CAAC;YAED,MAAM,UAAU,GAAG,MAAM,SAAS,CAAC,WAAW,CAAC,KAAK,CAAC,EAAE,IAAI,EAAE,QAA0B,EAAE,CAAC,CAAC;YAC3F,OAAO,UAAU,CAAC,KAAK,CAAC;QAC1B,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,0CAA0C,EAAE,KAAK,CAAC,CAAC;YACpE,OAAO,QAAQ,CAAC;QAClB,CAAC;IACH,CAAC;IAEO,2BAA2B,CAAC,KAAU;QAC5C,IAAI,CAAC,gBAAgB,GAAG,EAAE,CAAC;QAC3B,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,OAAO,EAAE,IAAI,KAAK,CAAC,4BAA4B,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;IACtF,CAAC;IAEO,0BAA0B;QAChC,IAAI,IAAI,CAAC,gBAAgB,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO;QAE/C,IAAI,IAAI,CAAC,eAAe,KAAK,OAAO,EAAE,CAAC;YACrC,IAAI,CAAC,iBAAiB,EAAE,CAAC;QAC3B,CAAC;aAAM,IAAI,IAAI,CAAC,eAAe,KAAK,MAAM,EAAE,CAAC;YAC3C,IAAI,CAAC,gBAAgB,EAAE,CAAC;QAC1B,CAAC;aAAM,CAAC;YACN,IAAI,CAAC,gBAAgB,EAAE,CAAC;QAC1B,CAAC;IACH,CAAC;IAEO,iBAAiB;QACvB,IAAI,CAAC,qBAAqB,GAAG,MAAM,CAAC;QAEpC,MAAM,WAAW,GAAG,IAAI,CAAC,gBAAgB,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,CACtD,MAAM,CAAC,KAAK,CAAC,WAAW,EAAE,CAAC,QAAQ,CAAC,OAAO,CAAC;YAC5C,MAAM,CAAC,KAAK,CAAC,WAAW,EAAE,CAAC,QAAQ,CAAC,MAAM,CAAC;YAC3C,MAAM,CAAC,KAAK,CAAC,WAAW,EAAE,CAAC,QAAQ,CAAC,QAAQ,CAAC;YAC7C,CAAC,MAAM,CAAC,KAAK,CAAC,WAAW,EAAE,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,WAAW,EAAE,CAAC,QAAQ,CAAC,MAAM,CAAC,CAC7F,CAAC;QAEF,IAAI,CAAC,gBAAgB,GAAG,WAAW,CAAC,CAAC,CAAC,WAAW,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,gBAAgB,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC;QAC/F,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,6BAA6B,EAAE;YAC/C,KAAK,EAAE,WAAW,EAAE,KAAK,IAAI,IAAI,CAAC,gBAAgB,CAAC,CAAC,CAAC,CAAC,KAAK;YAC3D,QAAQ,EAAE,IAAI,CAAC,gBAAgB;SAChC,CAAC,CAAC;IACL,CAAC;IAEO,gBAAgB;QACtB,IAAI,CAAC,qBAAqB,GAAG,aAAa,CAAC;QAE3C,MAAM,UAAU,GAAG,IAAI,CAAC,gBAAgB,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,CACrD,MAAM,CAAC,KAAK,CAAC,WAAW,EAAE,CAAC,QAAQ,CAAC,MAAM,CAAC;YAC3C,MAAM,CAAC,KAAK,CAAC,WAAW,EAAE,CAAC,QAAQ,CAAC,MAAM,CAAC;YAC3C,MAAM,CAAC,KAAK,CAAC,WAAW,EAAE,CAAC,QAAQ,CAAC,aAAa,CAAC,CACnD,CAAC;QAEF,IAAI,CAAC,gBAAgB,GAAG,UAAU,CAAC,CAAC,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,gBAAgB,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC;QAC7F,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,6BAA6B,EAAE;YAC/C,KAAK,EAAE,UAAU,EAAE,KAAK,IAAI,IAAI,CAAC,gBAAgB,CAAC,CAAC,CAAC,CAAC,KAAK;YAC1D,QAAQ,EAAE,IAAI,CAAC,gBAAgB;SAChC,CAAC,CAAC;IACL,CAAC;IAEO,gBAAgB;QACtB,IAAI,IAAI,CAAC,UAAU,KAAK,QAAQ,IAAI,IAAI,CAAC,UAAU,KAAK,QAAQ,EAAE,CAAC;YACjE,IAAI,CAAC,gBAAgB,EAAE,CAAC;YACxB,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,iCAAiC,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC,CAAC;QACzE,CAAC;aAAM,CAAC;YACN,IAAI,CAAC,gBAAgB,GAAG,IAAI,CAAC,gBAAgB,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC;YAC1D,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,kCAAkC,EAAE;gBACpD,KAAK,EAAE,IAAI,CAAC,gBAAgB,CAAC,CAAC,CAAC,CAAC,KAAK;gBACrC,QAAQ,EAAE,IAAI,CAAC,gBAAgB;aAChC,CAAC,CAAC;QACL,CAAC;IACH,CAAC;IAEO,qBAAqB,CAAC,MAAuB;QACnD,MAAM,YAAY,GAAG,MAAM,CAAC,KAAK,CAAC,WAAW,EAAE,CAAC,QAAQ,CAAC,MAAM,CAAC;YAC5C,MAAM,CAAC,KAAK,CAAC,WAAW,EAAE,CAAC,QAAQ,CAAC,MAAM,CAAC;YAC3C,MAAM,CAAC,KAAK,CAAC,WAAW,EAAE,CAAC,QAAQ,CAAC,aAAa,CAAC,CAAC;QACvE,IAAI,CAAC,qBAAqB,GAAG,YAAY,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,MAAM,CAAC;IACrE,CAAC;IAEO,KAAK,CAAC,gBAAgB;QAC5B,IAAI,CAAC;YACH,MAAM,gBAAgB,GAA0B,EAAE,CAAC;YAEnD,IAAI,IAAI,CAAC,gBAAgB,EAAE,CAAC;gBAC1B,gBAAgB,CAAC,QAAQ,GAAG,EAAE,KAAK,EAAE,IAAI,CAAC,gBAAgB,EAAE,CAAC;YAC/D,CAAC;iBAAM,IAAI,IAAI,CAAC,qBAAqB,EAAE,CAAC;gBACtC,gBAAgB,CAAC,UAAU,GAAG,IAAI,CAAC,qBAAqB,CAAC;YAC3D,CAAC;iBAAM,CAAC;gBACN,gBAAgB,CAAC,UAAU,GAAG,aAAa,CAAC;YAC9C,CAAC;YAED,MAAM,UAAU,GAAG,MAAM,SAAS,CAAC,YAAY,CAAC,YAAY,CAAC;gBAC3D,KAAK,EAAE,gBAAgB;aACxB,CAAC,CAAC;YAEH,MAAM,UAAU,GAAG,UAAU,CAAC,cAAc,EAAE,CAAC,CAAC,CAAC,CAAC;YAClD,MAAM,YAAY,GAAG,UAAU,CAAC,eAAe,EAAE,CAAC;YAClD,UAAU,CAAC,SAAS,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC;YAEtD,MAAM,WAAW,GAA0B,EAAE,GAAG,gBAAgB,EAAE,CAAC;YAEnE,IAAI,YAAY,CAAC,KAAK,IAAI,YAAY,CAAC,MAAM,EAAE,CAAC;gBAC9C,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,CAAC,YAAY,CAAC,KAAK,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;gBACxD,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,CAAC,YAAY,CAAC,MAAM,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;gBAE1D,MAAM,QAAQ,GAAG,eAAe,CAAC,IAAI,CAAC,SAAS,CAAC,SAAS,CAAC,IAAI,MAAM,CAAC,UAAU,IAAI,GAAG,CAAC;gBAEvF,IAAI,QAAQ,EAAE,CAAC;oBACb,WAAW,CAAC,KAAK,GAAG,EAAE,KAAK,EAAE,IAAI,CAAC,GAAG,CAAC,QAAQ,EAAE,IAAI,CAAC,EAAE,CAAC;oBACxD,WAAW,CAAC,MAAM,GAAG,EAAE,KAAK,EAAE,IAAI,CAAC,GAAG,CAAC,SAAS,EAAE,GAAG,CAAC,EAAE,CAAC;gBAC3D,CAAC;qBAAM,CAAC;oBACN,WAAW,CAAC,KAAK,GAAG,EAAE,KAAK,EAAE,QAAQ,EAAE,CAAC;oBACxC,WAAW,CAAC,MAAM,GAAG,EAAE,KAAK,EAAE,SAAS,EAAE,CAAC;gBAC5C,CAAC;YACH,CAAC;YAED,OAAO,WAAW,CAAC;QACrB,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,gFAAgF,CAAC,CAAC;YAEnG,MAAM,QAAQ,GAAG,eAAe,CAAC,IAAI,CAAC,SAAS,CAAC,SAAS,CAAC,IAAI,MAAM,CAAC,UAAU,IAAI,GAAG,CAAC;YACvF,MAAM,mBAAmB,GAA0B;gBACjD,KAAK,EAAE,EAAE,KAAK,EAAE,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,EAAE;gBACxC,MAAM,EAAE,EAAE,KAAK,EAAE,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE;aACzC,CAAC;YAEF,IAAI,IAAI,CAAC,gBAAgB,EAAE,CAAC;gBAC1B,mBAAmB,CAAC,QAAQ,GAAG,EAAE,KAAK,EAAE,IAAI,CAAC,gBAAgB,EAAE,CAAC;YAClE,CAAC;iBAAM,IAAI,IAAI,CAAC,qBAAqB,EAAE,CAAC;gBACtC,mBAAmB,CAAC,UAAU,GAAG,IAAI,CAAC,qBAAqB,CAAC;YAC9D,CAAC;iBAAM,CAAC;gBACN,mBAAmB,CAAC,UAAU,GAAG,aAAa,CAAC;YACjD,CAAC;YAED,OAAO,mBAAmB,CAAC;QAC7B,CAAC;IACH,CAAC;CACF","sourcesContent":["import { ICameraService, CameraInfo, CameraPreference } from './interfaces/ICameraService';\nimport { ILogger } from './interfaces/ILogger';\nimport { IEventBus } from './interfaces/IEventBus';\n\nexport class CameraService implements ICameraService {\n private availableCameras: MediaDeviceInfo[] = [];\n private selectedCameraId: string | null = null;\n private deviceType: 'mobile' | 'desktop' | 'tablet' = 'desktop';\n private preferredCameraFacing: 'environment' | 'user' | null = null;\n private preferredCamera: 'auto' | 'front' | 'back' = 'auto';\n\n constructor(\n private logger: ILogger,\n private eventBus: IEventBus,\n preferredCamera: 'auto' | 'front' | 'back' = 'auto'\n ) {\n this.preferredCamera = preferredCamera;\n }\n\n async detectDeviceType(): Promise<'mobile' | 'desktop' | 'tablet'> {\n const userAgent = navigator.userAgent;\n const isMobile = /Android|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(userAgent);\n const isTablet = /iPad|Android/i.test(userAgent) && window.innerWidth >= 768;\n \n if (isTablet) {\n this.deviceType = 'tablet';\n } else if (isMobile) {\n this.deviceType = 'mobile';\n } else {\n this.deviceType = 'desktop';\n }\n \n this.logger.state('DISPOSITIVO_DETECTADO', {\n deviceType: this.deviceType,\n userAgent: navigator.userAgent,\n screenDimensions: { width: window.innerWidth, height: window.innerHeight }\n });\n \n return this.deviceType;\n }\n\n async enumerateDevices(): Promise<MediaDeviceInfo[]> {\n try {\n const permissionStatus = await this.checkCameraPermission();\n \n if (permissionStatus === 'denied') {\n this.logger.error('Permiso de cámara denegado por el usuario');\n return [];\n }\n \n if (permissionStatus === 'prompt') {\n const tempStream = await navigator.mediaDevices.getUserMedia({ video: true });\n tempStream.getTracks().forEach(track => track.stop());\n }\n \n const devices = await navigator.mediaDevices.enumerateDevices();\n this.availableCameras = devices.filter(device => device.kind === 'videoinput');\n \n this.logger.state('CAMARAS_DETECTADAS', {\n count: this.availableCameras.length,\n cameras: this.availableCameras.map(cam => ({\n id: cam.deviceId,\n label: cam.label || 'Unknown Camera'\n }))\n });\n \n this.setInitialCameraPreference();\n this.eventBus.emit('camera-changed', this.selectedCameraId);\n \n return this.availableCameras;\n } catch (error) {\n this.logger.error('Error al enumerar cámaras disponibles:', error);\n this.handleCameraPermissionError(error);\n return [];\n }\n }\n\n getAvailableCameras(): MediaDeviceInfo[] {\n return [...this.availableCameras];\n }\n\n isMultipleCamerasAvailable(): boolean {\n return this.availableCameras.length > 1;\n }\n\n getSelectedCameraId(): string | null {\n return this.selectedCameraId;\n }\n\n async setSelectedCamera(cameraId: string): Promise<void> {\n const camera = this.availableCameras.find(cam => cam.deviceId === cameraId);\n if (!camera) {\n throw new Error(`Camera with ID ${cameraId} not found`);\n }\n \n this.selectedCameraId = cameraId;\n this.updatePreferredFacing(camera);\n this.savePreference();\n this.eventBus.emit('camera-changed', cameraId);\n }\n\n getPreferredFacing(): 'environment' | 'user' | null {\n return this.preferredCameraFacing;\n }\n\n async setupCamera(constraints?: MediaTrackConstraints): Promise<MediaStream> {\n const finalConstraints = constraints || await this.getMaxResolution();\n \n const stream = await navigator.mediaDevices.getUserMedia({\n video: finalConstraints,\n audio: false\n });\n\n return stream;\n }\n\n async switchCamera(cameraId: string): Promise<void> {\n const selectedCamera = this.availableCameras.find(cam => cam.deviceId === cameraId);\n if (!selectedCamera) {\n this.logger.warn('Cámara seleccionada no encontrada, re-enumerando dispositivos...');\n await this.enumerateDevices();\n return;\n }\n \n await this.setSelectedCamera(cameraId);\n this.logger.state('CAMARA_CAMBIADA', { \n label: selectedCamera.label, \n deviceId: selectedCamera.deviceId \n });\n }\n\n async flipToNextCamera(): Promise<void> {\n if (!this.isMultipleCamerasAvailable()) return;\n \n const currentIndex = this.availableCameras.findIndex(\n camera => camera.deviceId === this.selectedCameraId\n );\n \n const nextIndex = (currentIndex + 1) % this.availableCameras.length;\n const nextCamera = this.availableCameras[nextIndex];\n \n await this.switchCamera(nextCamera.deviceId);\n }\n\n savePreference(): void {\n try {\n const preference: CameraPreference = {\n cameraId: this.selectedCameraId!,\n facing: this.preferredCameraFacing,\n timestamp: Date.now()\n };\n \n localStorage.setItem('jaak-stamps-camera-preference', JSON.stringify(preference));\n this.logger.state('PREFERENCIA_CAMARA_GUARDADA', preference);\n } catch (error) {\n this.logger.warn('Error al guardar preferencia de cámara:', error);\n }\n }\n\n loadPreference(): void {\n try {\n const saved = localStorage.getItem('jaak-stamps-camera-preference');\n if (saved) {\n const preference: CameraPreference = JSON.parse(saved);\n \n const isStillAvailable = this.availableCameras.some(\n camera => camera.deviceId === preference.cameraId\n );\n \n if (isStillAvailable) {\n this.selectedCameraId = preference.cameraId;\n this.preferredCameraFacing = preference.facing;\n this.logger.state('PREFERENCIA_CAMARA_CARGADA', preference);\n }\n }\n } catch (error) {\n this.logger.warn('Error al cargar preferencia de cámara:', error);\n }\n }\n\n isRearCamera(stream: MediaStream): boolean {\n const videoTrack = stream.getVideoTracks()[0];\n if (!videoTrack) return false;\n \n const settings = videoTrack.getSettings();\n return settings.facingMode === 'environment';\n }\n\n getCameraInfo(): {\n availableCameras: CameraInfo[];\n selectedCameraId: string | null;\n deviceType: string;\n isMultipleCamerasAvailable: boolean;\n preferredFacing: 'environment' | 'user' | null;\n } {\n return {\n availableCameras: this.availableCameras.map(camera => ({\n id: camera.deviceId,\n label: camera.label || 'Unknown Camera',\n selected: camera.deviceId === this.selectedCameraId\n })),\n selectedCameraId: this.selectedCameraId,\n deviceType: this.deviceType,\n isMultipleCamerasAvailable: this.isMultipleCamerasAvailable(),\n preferredFacing: this.preferredCameraFacing\n };\n }\n\n private async checkCameraPermission(): Promise<'granted' | 'denied' | 'prompt'> {\n try {\n if (!navigator.permissions) {\n return 'prompt';\n }\n \n const permission = await navigator.permissions.query({ name: 'camera' as PermissionName });\n return permission.state;\n } catch (error) {\n this.logger.warn('No se pudo verificar permisos de cámara:', error);\n return 'prompt';\n }\n }\n\n private handleCameraPermissionError(error: any): void {\n this.availableCameras = [];\n this.eventBus.emit('error', new Error(`Camera permission error: ${error.message}`));\n }\n\n private setInitialCameraPreference(): void {\n if (this.availableCameras.length === 0) return;\n \n if (this.preferredCamera === 'front') {\n this.selectFrontCamera();\n } else if (this.preferredCamera === 'back') {\n this.selectBackCamera();\n } else {\n this.selectAutoCamera();\n }\n }\n\n private selectFrontCamera(): void {\n this.preferredCameraFacing = 'user';\n \n const frontCamera = this.availableCameras.find(camera => \n camera.label.toLowerCase().includes('front') || \n camera.label.toLowerCase().includes('user') ||\n camera.label.toLowerCase().includes('selfie') ||\n !camera.label.toLowerCase().includes('back') && !camera.label.toLowerCase().includes('rear')\n );\n \n this.selectedCameraId = frontCamera ? frontCamera.deviceId : this.availableCameras[0].deviceId;\n this.logger.state('CAMARA_FRONTAL_SELECCIONADA', { \n label: frontCamera?.label || this.availableCameras[0].label,\n deviceId: this.selectedCameraId \n });\n }\n\n private selectBackCamera(): void {\n this.preferredCameraFacing = 'environment';\n \n const backCamera = this.availableCameras.find(camera => \n camera.label.toLowerCase().includes('back') || \n camera.label.toLowerCase().includes('rear') ||\n camera.label.toLowerCase().includes('environment')\n );\n \n this.selectedCameraId = backCamera ? backCamera.deviceId : this.availableCameras[0].deviceId;\n this.logger.state('CAMARA_TRASERA_SELECCIONADA', { \n label: backCamera?.label || this.availableCameras[0].label,\n deviceId: this.selectedCameraId \n });\n }\n\n private selectAutoCamera(): void {\n if (this.deviceType === 'mobile' || this.deviceType === 'tablet') {\n this.selectBackCamera();\n this.logger.state('CAMARA_AUTO_SELECCIONADA_MOBILE', { type: 'rear' });\n } else {\n this.selectedCameraId = this.availableCameras[0].deviceId;\n this.logger.state('CAMARA_AUTO_SELECCIONADA_DESKTOP', { \n label: this.availableCameras[0].label, \n deviceId: this.selectedCameraId \n });\n }\n }\n\n private updatePreferredFacing(camera: MediaDeviceInfo): void {\n const isRearCamera = camera.label.toLowerCase().includes('back') || \n camera.label.toLowerCase().includes('rear') ||\n camera.label.toLowerCase().includes('environment');\n this.preferredCameraFacing = isRearCamera ? 'environment' : 'user';\n }\n\n private async getMaxResolution(): Promise<MediaTrackConstraints> {\n try {\n const videoConstraints: MediaTrackConstraints = {};\n \n if (this.selectedCameraId) {\n videoConstraints.deviceId = { exact: this.selectedCameraId };\n } else if (this.preferredCameraFacing) {\n videoConstraints.facingMode = this.preferredCameraFacing;\n } else {\n videoConstraints.facingMode = \"environment\";\n }\n \n const tempStream = await navigator.mediaDevices.getUserMedia({\n video: videoConstraints\n });\n \n const videoTrack = tempStream.getVideoTracks()[0];\n const capabilities = videoTrack.getCapabilities();\n tempStream.getTracks().forEach(track => track.stop());\n \n const constraints: MediaTrackConstraints = { ...videoConstraints };\n \n if (capabilities.width && capabilities.height) {\n const maxWidth = Math.min(capabilities.width.max, 1920);\n const maxHeight = Math.min(capabilities.height.max, 1080);\n \n const isTablet = /iPad|Android/i.test(navigator.userAgent) && window.innerWidth >= 768;\n \n if (isTablet) {\n constraints.width = { ideal: Math.min(maxWidth, 1280) };\n constraints.height = { ideal: Math.min(maxHeight, 720) };\n } else {\n constraints.width = { ideal: maxWidth };\n constraints.height = { ideal: maxHeight };\n }\n }\n \n return constraints;\n } catch (err) {\n this.logger.warn('No se pudieron obtener capacidades de cámara, usando configuración de respaldo');\n \n const isTablet = /iPad|Android/i.test(navigator.userAgent) && window.innerWidth >= 768;\n const fallbackConstraints: MediaTrackConstraints = {\n width: { ideal: isTablet ? 1280 : 1920 },\n height: { ideal: isTablet ? 720 : 1080 }\n };\n \n if (this.selectedCameraId) {\n fallbackConstraints.deviceId = { exact: this.selectedCameraId };\n } else if (this.preferredCameraFacing) {\n fallbackConstraints.facingMode = this.preferredCameraFacing;\n } else {\n fallbackConstraints.facingMode = \"environment\";\n }\n \n return fallbackConstraints;\n }\n }\n}"]}
|
|
1
|
+
{"version":3,"file":"CameraService.js","sourceRoot":"","sources":["../../src/services/CameraService.ts"],"names":[],"mappings":"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","sourcesContent":["import { ICameraService, CameraInfo, CameraPreference } from './interfaces/ICameraService';\nimport { ILogger } from './interfaces/ILogger';\nimport { IEventBus } from './interfaces/IEventBus';\n\nexport class CameraService implements ICameraService {\n private availableCameras: MediaDeviceInfo[] = [];\n private selectedCameraId: string | null = null;\n private deviceType: 'mobile' | 'desktop' | 'tablet' = 'desktop';\n private preferredCameraFacing: 'environment' | 'user' | null = null;\n private preferredCamera: 'auto' | 'front' | 'back' = 'auto';\n\n constructor(\n private logger: ILogger,\n private eventBus: IEventBus,\n preferredCamera: 'auto' | 'front' | 'back' = 'auto'\n ) {\n this.preferredCamera = preferredCamera;\n }\n\n async detectDeviceType(): Promise<'mobile' | 'desktop' | 'tablet'> {\n const userAgent = navigator.userAgent;\n const isMobile = /Android|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(userAgent);\n const isTablet = /iPad|Android/i.test(userAgent) && window.innerWidth >= 768;\n \n if (isTablet) {\n this.deviceType = 'tablet';\n } else if (isMobile) {\n this.deviceType = 'mobile';\n } else {\n this.deviceType = 'desktop';\n }\n \n this.logger.state('DISPOSITIVO_DETECTADO', {\n deviceType: this.deviceType,\n userAgent: navigator.userAgent,\n screenDimensions: { width: window.innerWidth, height: window.innerHeight }\n });\n \n return this.deviceType;\n }\n\n async enumerateDevices(): Promise<MediaDeviceInfo[]> {\n try {\n const permissionStatus = await this.checkCameraPermission();\n \n if (permissionStatus === 'denied') {\n this.logger.error('Permiso de cámara denegado por el usuario');\n return [];\n }\n \n if (permissionStatus === 'prompt') {\n const tempStream = await navigator.mediaDevices.getUserMedia({ video: true });\n tempStream.getTracks().forEach(track => track.stop());\n }\n \n const devices = await navigator.mediaDevices.enumerateDevices();\n this.availableCameras = devices.filter(device => device.kind === 'videoinput');\n \n this.logger.state('CAMARAS_DETECTADAS', {\n count: this.availableCameras.length,\n cameras: this.availableCameras.map(cam => ({\n id: cam.deviceId,\n label: cam.label || 'Unknown Camera'\n }))\n });\n \n this.setInitialCameraPreference();\n this.eventBus.emit('camera-changed', this.selectedCameraId);\n \n return this.availableCameras;\n } catch (error) {\n this.logger.error('Error al enumerar cámaras disponibles:', error);\n this.handleCameraPermissionError(error);\n return [];\n }\n }\n\n getAvailableCameras(): MediaDeviceInfo[] {\n return [...this.availableCameras];\n }\n\n isMultipleCamerasAvailable(): boolean {\n return this.availableCameras.length > 1;\n }\n\n getSelectedCameraId(): string | null {\n return this.selectedCameraId;\n }\n\n async setSelectedCamera(cameraId: string): Promise<void> {\n const camera = this.availableCameras.find(cam => cam.deviceId === cameraId);\n if (!camera) {\n throw new Error(`Camera with ID ${cameraId} not found`);\n }\n \n this.selectedCameraId = cameraId;\n this.updatePreferredFacing(camera);\n this.savePreference();\n this.eventBus.emit('camera-changed', cameraId);\n }\n\n getPreferredFacing(): 'environment' | 'user' | null {\n return this.preferredCameraFacing;\n }\n\n async setupCamera(constraints?: MediaTrackConstraints): Promise<MediaStream> {\n const finalConstraints = constraints || await this.getMaxResolution();\n \n const stream = await navigator.mediaDevices.getUserMedia({\n video: finalConstraints,\n audio: false\n });\n\n return stream;\n }\n\n async switchCamera(cameraId: string): Promise<void> {\n const selectedCamera = this.availableCameras.find(cam => cam.deviceId === cameraId);\n if (!selectedCamera) {\n this.logger.warn('Cámara seleccionada no encontrada, re-enumerando dispositivos...');\n await this.enumerateDevices();\n return;\n }\n \n await this.setSelectedCamera(cameraId);\n this.logger.state('CAMARA_CAMBIADA', { \n label: selectedCamera.label, \n deviceId: selectedCamera.deviceId \n });\n }\n\n async flipToNextCamera(): Promise<void> {\n if (!this.isMultipleCamerasAvailable()) return;\n \n const currentIndex = this.availableCameras.findIndex(\n camera => camera.deviceId === this.selectedCameraId\n );\n \n const nextIndex = (currentIndex + 1) % this.availableCameras.length;\n const nextCamera = this.availableCameras[nextIndex];\n \n await this.switchCamera(nextCamera.deviceId);\n }\n\n savePreference(): void {\n try {\n const preference: CameraPreference = {\n cameraId: this.selectedCameraId!,\n facing: this.preferredCameraFacing,\n timestamp: Date.now()\n };\n \n localStorage.setItem('jaak-stamps-camera-preference', JSON.stringify(preference));\n this.logger.state('PREFERENCIA_CAMARA_GUARDADA', preference);\n } catch (error) {\n this.logger.warn('Error al guardar preferencia de cámara:', error);\n }\n }\n\n loadPreference(): void {\n try {\n const saved = localStorage.getItem('jaak-stamps-camera-preference');\n if (saved) {\n const preference: CameraPreference = JSON.parse(saved);\n \n const isStillAvailable = this.availableCameras.some(\n camera => camera.deviceId === preference.cameraId\n );\n \n if (isStillAvailable) {\n this.selectedCameraId = preference.cameraId;\n this.preferredCameraFacing = preference.facing;\n this.logger.state('PREFERENCIA_CAMARA_CARGADA', preference);\n }\n }\n } catch (error) {\n this.logger.warn('Error al cargar preferencia de cámara:', error);\n }\n }\n\n isRearCamera(stream: MediaStream): boolean {\n const videoTrack = stream.getVideoTracks()[0];\n if (!videoTrack) return false;\n \n const settings = videoTrack.getSettings();\n return settings.facingMode === 'environment';\n }\n\n getCameraInfo(): {\n availableCameras: CameraInfo[];\n selectedCameraId: string | null;\n deviceType: string;\n isMultipleCamerasAvailable: boolean;\n preferredFacing: 'environment' | 'user' | null;\n } {\n return {\n availableCameras: this.availableCameras.map(camera => ({\n id: camera.deviceId,\n label: camera.label || 'Unknown Camera',\n selected: camera.deviceId === this.selectedCameraId\n })),\n selectedCameraId: this.selectedCameraId,\n deviceType: this.deviceType,\n isMultipleCamerasAvailable: this.isMultipleCamerasAvailable(),\n preferredFacing: this.preferredCameraFacing\n };\n }\n\n private async checkCameraPermission(): Promise<'granted' | 'denied' | 'prompt'> {\n try {\n if (!navigator.permissions) {\n return 'prompt';\n }\n \n const permission = await navigator.permissions.query({ name: 'camera' as PermissionName });\n return permission.state;\n } catch (error) {\n this.logger.warn('No se pudo verificar permisos de cámara:', error);\n return 'prompt';\n }\n }\n\n private handleCameraPermissionError(error: any): void {\n this.availableCameras = [];\n this.eventBus.emit('error', new Error(`Camera permission error: ${error.message}`));\n }\n\n private setInitialCameraPreference(): void {\n if (this.availableCameras.length === 0) return;\n \n if (this.preferredCamera === 'front') {\n this.selectFrontCamera();\n } else if (this.preferredCamera === 'back') {\n this.selectBackCamera();\n } else {\n this.selectAutoCamera();\n }\n }\n\n private selectFrontCamera(): void {\n this.preferredCameraFacing = 'user';\n \n const frontCamera = this.availableCameras.find(camera => \n camera.label.toLowerCase().includes('front') || \n camera.label.toLowerCase().includes('user') ||\n camera.label.toLowerCase().includes('selfie') ||\n !camera.label.toLowerCase().includes('back') && !camera.label.toLowerCase().includes('rear')\n );\n \n this.selectedCameraId = frontCamera ? frontCamera.deviceId : this.availableCameras[0].deviceId;\n this.logger.state('CAMARA_FRONTAL_SELECCIONADA', { \n label: frontCamera?.label || this.availableCameras[0].label,\n deviceId: this.selectedCameraId \n });\n }\n\n private selectBackCamera(): void {\n this.preferredCameraFacing = 'environment';\n \n const backCamera = this.availableCameras.find(camera => \n camera.label.toLowerCase().includes('back') || \n camera.label.toLowerCase().includes('rear') ||\n camera.label.toLowerCase().includes('environment')\n );\n \n this.selectedCameraId = backCamera ? backCamera.deviceId : this.availableCameras[0].deviceId;\n this.logger.state('CAMARA_TRASERA_SELECCIONADA', { \n label: backCamera?.label || this.availableCameras[0].label,\n deviceId: this.selectedCameraId \n });\n }\n\n private selectAutoCamera(): void {\n if (this.deviceType === 'mobile' || this.deviceType === 'tablet') {\n this.selectBackCamera();\n this.logger.state('CAMARA_AUTO_SELECCIONADA_MOBILE', { type: 'rear' });\n } else {\n this.selectedCameraId = this.availableCameras[0].deviceId;\n this.logger.state('CAMARA_AUTO_SELECCIONADA_DESKTOP', { \n label: this.availableCameras[0].label, \n deviceId: this.selectedCameraId \n });\n }\n }\n\n private updatePreferredFacing(camera: MediaDeviceInfo): void {\n const isRearCamera = camera.label.toLowerCase().includes('back') || \n camera.label.toLowerCase().includes('rear') ||\n camera.label.toLowerCase().includes('environment');\n this.preferredCameraFacing = isRearCamera ? 'environment' : 'user';\n }\n\n private async getMaxResolution(): Promise<MediaTrackConstraints> {\n try {\n const videoConstraints: MediaTrackConstraints = {};\n \n if (this.selectedCameraId) {\n videoConstraints.deviceId = { exact: this.selectedCameraId };\n } else if (this.preferredCameraFacing) {\n videoConstraints.facingMode = this.preferredCameraFacing;\n } else {\n videoConstraints.facingMode = \"environment\";\n }\n \n const tempStream = await navigator.mediaDevices.getUserMedia({\n video: videoConstraints\n });\n \n const videoTrack = tempStream.getVideoTracks()[0];\n const capabilities = videoTrack.getCapabilities();\n tempStream.getTracks().forEach(track => track.stop());\n \n const constraints: MediaTrackConstraints = { ...videoConstraints };\n \n // Enhanced focus and image quality settings\n this.applyAdvancedCameraSettings(constraints, capabilities);\n \n if (capabilities.width && capabilities.height) {\n const maxWidth = Math.min(capabilities.width.max, 1920);\n const maxHeight = Math.min(capabilities.height.max, 1080);\n \n const isTablet = /iPad|Android/i.test(navigator.userAgent) && window.innerWidth >= 768;\n \n if (isTablet) {\n constraints.width = { ideal: Math.min(maxWidth, 1280) };\n constraints.height = { ideal: Math.min(maxHeight, 720) };\n } else {\n constraints.width = { ideal: maxWidth };\n constraints.height = { ideal: maxHeight };\n }\n }\n \n this.logger.state('CONFIGURACION_CAMARA_OPTIMIZADA', {\n constraints,\n capabilities: this.getCapabilitiesSummary(capabilities)\n });\n \n return constraints;\n } catch (err) {\n this.logger.warn('No se pudieron obtener capacidades de cámara, usando configuración de respaldo');\n \n const isTablet = /iPad|Android/i.test(navigator.userAgent) && window.innerWidth >= 768;\n const fallbackConstraints: MediaTrackConstraints = {\n width: { ideal: isTablet ? 1280 : 1920 },\n height: { ideal: isTablet ? 720 : 1080 }\n };\n \n // Apply basic focus settings even for fallback\n this.applyBasicFocusSettings(fallbackConstraints);\n \n if (this.selectedCameraId) {\n fallbackConstraints.deviceId = { exact: this.selectedCameraId };\n } else if (this.preferredCameraFacing) {\n fallbackConstraints.facingMode = this.preferredCameraFacing;\n } else {\n fallbackConstraints.facingMode = \"environment\";\n }\n \n return fallbackConstraints;\n }\n }\n\n private applyAdvancedCameraSettings(constraints: MediaTrackConstraints, capabilities: MediaTrackCapabilities): void {\n // Apply standard camera settings that are widely supported\n \n // Frame rate optimization\n if (capabilities.frameRate) {\n constraints.frameRate = {\n ideal: 30,\n min: 15,\n max: 60\n };\n }\n\n // Width and height constraints for optimal resolution\n if (capabilities.width && capabilities.height) {\n constraints.width = {\n ideal: Math.min(1920, capabilities.width.max || 1920),\n min: 640\n };\n constraints.height = {\n ideal: Math.min(1080, capabilities.height.max || 1080),\n min: 480\n };\n }\n\n // Apply enhanced constraints through advanced array for better browser compatibility\n const advancedConstraints: MediaTrackConstraintSet[] = [];\n \n // Try to set optimal settings through advanced constraints\n const advanced: any = {};\n \n // Focus mode - use 'any' type to bypass TypeScript limitations\n if ((capabilities as any).focusMode) {\n if ((capabilities as any).focusMode.includes('continuous')) {\n advanced.focusMode = 'continuous';\n } else if ((capabilities as any).focusMode.includes('single-shot')) {\n advanced.focusMode = 'single-shot';\n }\n }\n\n // Focus distance for close objects\n if ((capabilities as any).focusDistance) {\n advanced.focusDistance = {\n ideal: 0.4, // 40cm - optimal for document capture\n min: 0.2,\n max: 1.0\n };\n }\n\n // Zoom control\n if ((capabilities as any).zoom) {\n advanced.zoom = {\n ideal: 1.0,\n min: (capabilities as any).zoom.min,\n max: Math.min((capabilities as any).zoom.max, 3.0)\n };\n }\n\n // Add advanced constraints if any were set\n if (Object.keys(advanced).length > 0) {\n advancedConstraints.push(advanced);\n }\n\n if (advancedConstraints.length > 0) {\n constraints.advanced = advancedConstraints;\n }\n }\n\n private applyBasicFocusSettings(constraints: MediaTrackConstraints): void {\n // Apply basic focus settings when capabilities are not available\n // Use advanced constraints for non-standard properties\n const advanced: any = {\n focusMode: 'continuous',\n exposureMode: 'continuous',\n whiteBalanceMode: 'continuous'\n };\n \n if (!constraints.advanced) {\n constraints.advanced = [];\n }\n constraints.advanced.push(advanced);\n \n constraints.frameRate = { ideal: 30 };\n }\n\n private getCapabilitiesSummary(capabilities: MediaTrackCapabilities): any {\n return {\n hasAutoFocus: !!(capabilities as any).focusMode,\n hasFocusDistance: !!(capabilities as any).focusDistance,\n hasExposureControl: !!(capabilities as any).exposureMode,\n hasWhiteBalance: !!(capabilities as any).whiteBalanceMode,\n hasZoom: !!(capabilities as any).zoom,\n hasTorch: (capabilities as any).torch !== undefined,\n hasImageControls: !!((capabilities as any).brightness || (capabilities as any).contrast || (capabilities as any).saturation || (capabilities as any).sharpness),\n resolutionRange: capabilities.width && capabilities.height ? \n `${capabilities.width.min}x${capabilities.height.min} - ${capabilities.width.max}x${capabilities.height.max}` : 'unknown'\n };\n }\n\n // New method to enable torch/flash for better illumination\n async setTorchEnabled(enabled: boolean, stream?: MediaStream): Promise<boolean> {\n try {\n if (!stream) {\n this.logger.warn('No hay stream disponible para controlar el flash');\n return false;\n }\n\n const videoTrack = stream.getVideoTracks()[0];\n if (!videoTrack) {\n this.logger.warn('No hay track de video disponible');\n return false;\n }\n\n const capabilities = videoTrack.getCapabilities();\n if ((capabilities as any).torch === undefined) {\n this.logger.warn('El dispositivo no soporta control de flash');\n return false;\n }\n\n await videoTrack.applyConstraints({\n advanced: [{ torch: enabled } as any]\n });\n\n this.logger.state('FLASH_CONTROLADO', { enabled });\n return true;\n } catch (error) {\n this.logger.error('Error al controlar el flash:', error);\n return false;\n }\n }\n\n // Method to manually trigger focus on a specific point\n async focusAtPoint(x: number, y: number, stream?: MediaStream): Promise<boolean> {\n try {\n if (!stream) {\n this.logger.warn('No hay stream disponible para enfocar');\n return false;\n }\n\n const videoTrack = stream.getVideoTracks()[0];\n if (!videoTrack) {\n this.logger.warn('No hay track de video disponible');\n return false;\n }\n\n const capabilities = videoTrack.getCapabilities();\n if (!(capabilities as any).focusMode || !(capabilities as any).focusMode.includes('single-shot')) {\n this.logger.warn('El dispositivo no soporta enfoque manual');\n return false;\n }\n\n // Trigger single-shot focus\n await videoTrack.applyConstraints({\n advanced: [{ focusMode: 'single-shot' } as any]\n });\n\n // Return to continuous focus after a short delay\n setTimeout(async () => {\n try {\n await videoTrack.applyConstraints({\n advanced: [{ focusMode: 'continuous' } as any]\n });\n } catch (error) {\n this.logger.warn('Error al volver a enfoque continuo:', error);\n }\n }, 1000);\n\n this.logger.state('ENFOQUE_MANUAL_ACTIVADO', { x, y });\n return true;\n } catch (error) {\n this.logger.error('Error al enfocar manualmente:', error);\n return false;\n }\n }\n}"]}
|
|
@@ -1,13 +1,16 @@
|
|
|
1
1
|
import { DeviceStrategyFactory } from "./factories/DeviceStrategyFactory";
|
|
2
|
+
import { ImageQualityService } from "./ImageQualityService";
|
|
2
3
|
export class DetectionService {
|
|
3
4
|
logger;
|
|
4
5
|
debug;
|
|
5
6
|
useDocumentClassification;
|
|
7
|
+
qualityThresholds;
|
|
6
8
|
session;
|
|
7
9
|
mobileNetSession;
|
|
8
10
|
mobileNetClassMap;
|
|
9
11
|
modelLoaded = false;
|
|
10
12
|
deviceStrategy;
|
|
13
|
+
imageQualityService;
|
|
11
14
|
MODEL_PATH = "https://storage.googleapis.com/jaak-static/web/component/stamps/ddmyp-v2.onnx";
|
|
12
15
|
MOBILENET_MODEL_PATH = "https://storage.googleapis.com/jaak-static/web/component/stamps/cdmmp-v1.onnx";
|
|
13
16
|
MOBILENET_CLASSES_PATH = "https://storage.googleapis.com/jaak-static/web/component/stamps/cdmmp-v1.json";
|
|
@@ -17,11 +20,13 @@ export class DetectionService {
|
|
|
17
20
|
preprocessCanvas;
|
|
18
21
|
preprocessCtx;
|
|
19
22
|
captureCanvas;
|
|
20
|
-
constructor(logger, debug = false, useDocumentClassification = false) {
|
|
23
|
+
constructor(logger, debug = false, useDocumentClassification = false, qualityThresholds = {}) {
|
|
21
24
|
this.logger = logger;
|
|
22
25
|
this.debug = debug;
|
|
23
26
|
this.useDocumentClassification = useDocumentClassification;
|
|
27
|
+
this.qualityThresholds = qualityThresholds;
|
|
24
28
|
this.deviceStrategy = DeviceStrategyFactory.createStrategy();
|
|
29
|
+
this.imageQualityService = new ImageQualityService(logger);
|
|
25
30
|
this.initializeCanvasPool();
|
|
26
31
|
}
|
|
27
32
|
async loadModel() {
|
|
@@ -365,5 +370,163 @@ export class DetectionService {
|
|
|
365
370
|
}
|
|
366
371
|
return new window.ort.Tensor('float32', arr, [1, 3, 224, 224]);
|
|
367
372
|
}
|
|
373
|
+
// New methods for image quality validation
|
|
374
|
+
validateImageQuality(video, detectedBox) {
|
|
375
|
+
// Create a canvas to capture the current frame
|
|
376
|
+
const canvas = document.createElement('canvas');
|
|
377
|
+
canvas.width = video.videoWidth;
|
|
378
|
+
canvas.height = video.videoHeight;
|
|
379
|
+
const ctx = canvas.getContext('2d');
|
|
380
|
+
ctx.drawImage(video, 0, 0);
|
|
381
|
+
// If we have a detected document, focus quality analysis on that area
|
|
382
|
+
let documentBounds;
|
|
383
|
+
if (detectedBox) {
|
|
384
|
+
const scaleX = video.videoWidth / this.INPUT_SIZE;
|
|
385
|
+
const scaleY = video.videoHeight / this.INPUT_SIZE;
|
|
386
|
+
documentBounds = {
|
|
387
|
+
x: Math.max(0, detectedBox.x * scaleX),
|
|
388
|
+
y: Math.max(0, detectedBox.y * scaleY),
|
|
389
|
+
w: Math.min(detectedBox.w * scaleX, video.videoWidth),
|
|
390
|
+
h: Math.min(detectedBox.h * scaleY, video.videoHeight)
|
|
391
|
+
};
|
|
392
|
+
}
|
|
393
|
+
return this.imageQualityService.analyzeImageQuality(canvas, documentBounds);
|
|
394
|
+
}
|
|
395
|
+
isImageQualityAcceptable(qualityResult) {
|
|
396
|
+
// Use configurable thresholds with fallback defaults
|
|
397
|
+
const minQualityScore = this.qualityThresholds.minQualityScore ?? 45;
|
|
398
|
+
const minFocusScore = this.qualityThresholds.minFocusScore ?? 16;
|
|
399
|
+
const minBlurScore = this.qualityThresholds.minBlurScore ?? 22;
|
|
400
|
+
const maxReflectionScore = this.qualityThresholds.maxReflectionScore ?? 15;
|
|
401
|
+
// Check overall quality score - primary metric
|
|
402
|
+
if (qualityResult.qualityScore < minQualityScore) {
|
|
403
|
+
this.logger.state('QUALITY_REJECTION_SCORE', {
|
|
404
|
+
qualityScore: qualityResult.qualityScore,
|
|
405
|
+
minQualityScore,
|
|
406
|
+
reason: 'score_too_low'
|
|
407
|
+
});
|
|
408
|
+
return false;
|
|
409
|
+
}
|
|
410
|
+
// Check focus score
|
|
411
|
+
if (qualityResult.focus.focusScore < minFocusScore) {
|
|
412
|
+
this.logger.state('QUALITY_REJECTION_FOCUS', {
|
|
413
|
+
focusScore: qualityResult.focus.focusScore,
|
|
414
|
+
minFocusScore,
|
|
415
|
+
reason: 'severe_focus_issues'
|
|
416
|
+
});
|
|
417
|
+
return false;
|
|
418
|
+
}
|
|
419
|
+
// Check blur score
|
|
420
|
+
if (qualityResult.blur.blurScore < minBlurScore) {
|
|
421
|
+
this.logger.state('QUALITY_REJECTION_BLUR', {
|
|
422
|
+
blurScore: qualityResult.blur.blurScore,
|
|
423
|
+
minBlurScore,
|
|
424
|
+
reason: 'severe_blur_issues'
|
|
425
|
+
});
|
|
426
|
+
return false;
|
|
427
|
+
}
|
|
428
|
+
// Check reflection score
|
|
429
|
+
if (qualityResult.reflection.hasReflection && qualityResult.reflection.reflectionScore > maxReflectionScore) {
|
|
430
|
+
this.logger.state('QUALITY_REJECTION_REFLECTION', {
|
|
431
|
+
hasReflection: qualityResult.reflection.hasReflection,
|
|
432
|
+
reflectionScore: qualityResult.reflection.reflectionScore,
|
|
433
|
+
maxReflectionScore,
|
|
434
|
+
reason: 'severe_reflection_issues'
|
|
435
|
+
});
|
|
436
|
+
return false;
|
|
437
|
+
}
|
|
438
|
+
this.logger.state('QUALITY_ACCEPTANCE', {
|
|
439
|
+
qualityScore: qualityResult.qualityScore,
|
|
440
|
+
focusScore: qualityResult.focus.focusScore,
|
|
441
|
+
blurScore: qualityResult.blur.blurScore,
|
|
442
|
+
reflectionScore: qualityResult.reflection.reflectionScore,
|
|
443
|
+
issues: qualityResult.issues,
|
|
444
|
+
reason: 'quality_acceptable'
|
|
445
|
+
});
|
|
446
|
+
return true;
|
|
447
|
+
}
|
|
448
|
+
updateQualityThresholds(thresholds) {
|
|
449
|
+
this.qualityThresholds = { ...this.qualityThresholds, ...thresholds };
|
|
450
|
+
this.logger.state('QUALITY_THRESHOLDS_UPDATED', {
|
|
451
|
+
newThresholds: this.qualityThresholds
|
|
452
|
+
});
|
|
453
|
+
}
|
|
454
|
+
getQualityThresholds() {
|
|
455
|
+
return { ...this.qualityThresholds };
|
|
456
|
+
}
|
|
457
|
+
getQualityFeedback(qualityResult) {
|
|
458
|
+
if (qualityResult.qualityScore >= 85) {
|
|
459
|
+
return 'Calidad excelente - listo para capturar';
|
|
460
|
+
}
|
|
461
|
+
else if (qualityResult.qualityScore >= 70) {
|
|
462
|
+
return 'Buena calidad - puede capturar';
|
|
463
|
+
}
|
|
464
|
+
else if (qualityResult.qualityScore >= 50) {
|
|
465
|
+
const primaryIssue = qualityResult.issues[0];
|
|
466
|
+
return primaryIssue ? `Calidad aceptable - ${primaryIssue.toLowerCase()}` : 'Calidad aceptable';
|
|
467
|
+
}
|
|
468
|
+
else {
|
|
469
|
+
const mainIssues = qualityResult.issues.slice(0, 2).join(', ').toLowerCase();
|
|
470
|
+
return `Calidad insuficiente - ${mainIssues}`;
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
// Enhanced detection method that includes quality validation
|
|
474
|
+
async runInferenceWithQuality(inputTensor, video) {
|
|
475
|
+
const detections = await this.runInference(inputTensor);
|
|
476
|
+
// Get the best detection for quality analysis
|
|
477
|
+
const bestDetection = detections.length > 0 ?
|
|
478
|
+
detections.reduce((best, current) => current.score > best.score ? current : best) :
|
|
479
|
+
undefined;
|
|
480
|
+
const qualityResult = this.validateImageQuality(video, bestDetection);
|
|
481
|
+
const canCapture = this.isImageQualityAcceptable(qualityResult);
|
|
482
|
+
const feedback = this.getQualityFeedback(qualityResult);
|
|
483
|
+
this.logger.state('DETECCION_CON_CALIDAD', {
|
|
484
|
+
detectionsCount: detections.length,
|
|
485
|
+
qualityScore: qualityResult.qualityScore,
|
|
486
|
+
canCapture,
|
|
487
|
+
issues: qualityResult.issues.length,
|
|
488
|
+
bestDetectionScore: bestDetection?.score || 0
|
|
489
|
+
});
|
|
490
|
+
return {
|
|
491
|
+
detections,
|
|
492
|
+
qualityResult,
|
|
493
|
+
canCapture,
|
|
494
|
+
feedback
|
|
495
|
+
};
|
|
496
|
+
}
|
|
497
|
+
// Method to get real-time quality feedback without full analysis
|
|
498
|
+
getQuickQualityFeedback(video) {
|
|
499
|
+
const canvas = document.createElement('canvas');
|
|
500
|
+
canvas.width = Math.min(video.videoWidth, 640); // Reduced resolution for performance
|
|
501
|
+
canvas.height = Math.min(video.videoHeight, 480);
|
|
502
|
+
const ctx = canvas.getContext('2d');
|
|
503
|
+
ctx.drawImage(video, 0, 0, canvas.width, canvas.height);
|
|
504
|
+
const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
|
|
505
|
+
// Quick blur detection
|
|
506
|
+
const blurMetrics = this.imageQualityService.detectBlur(imageData);
|
|
507
|
+
// Quick reflection detection
|
|
508
|
+
const reflectionMetrics = this.imageQualityService.detectReflections(imageData);
|
|
509
|
+
// Quick focus measurement
|
|
510
|
+
const focusMetrics = this.imageQualityService.measureFocus(imageData);
|
|
511
|
+
let feedback = 'Analizando calidad...';
|
|
512
|
+
if (!blurMetrics.isAcceptable) {
|
|
513
|
+
feedback = 'Estabilice el dispositivo';
|
|
514
|
+
}
|
|
515
|
+
else if (reflectionMetrics.hasReflection) {
|
|
516
|
+
feedback = 'Evite reflejos en el documento';
|
|
517
|
+
}
|
|
518
|
+
else if (!focusMetrics.isFocused) {
|
|
519
|
+
feedback = 'Permitiendo enfoque automático...';
|
|
520
|
+
}
|
|
521
|
+
else {
|
|
522
|
+
feedback = 'Calidad óptima';
|
|
523
|
+
}
|
|
524
|
+
return {
|
|
525
|
+
hasBlur: !blurMetrics.isAcceptable,
|
|
526
|
+
hasReflections: reflectionMetrics.hasReflection,
|
|
527
|
+
isFocused: focusMetrics.isFocused,
|
|
528
|
+
feedback
|
|
529
|
+
};
|
|
530
|
+
}
|
|
368
531
|
}
|
|
369
532
|
//# sourceMappingURL=DetectionService.js.map
|