@jaak.ai/stamps 2.0.0-dev.23 → 2.0.0-dev.24
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 +22 -9
- package/dist/cjs/jaak-stamps.cjs.entry.js +116 -16
- package/dist/cjs/jaak-stamps.cjs.entry.js.map +1 -1
- package/dist/cjs/jaak-stamps.entry.cjs.js.map +1 -1
- package/dist/collection/components/my-component/my-component.js +116 -16
- package/dist/collection/components/my-component/my-component.js.map +1 -1
- package/dist/components/jaak-stamps.js +116 -16
- package/dist/components/jaak-stamps.js.map +1 -1
- package/dist/esm/jaak-stamps.entry.js +116 -16
- package/dist/esm/jaak-stamps.entry.js.map +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-0521d8f5.entry.js +2 -0
- package/dist/jaak-stamps-webcomponent/p-0521d8f5.entry.js.map +1 -0
- package/dist/types/components/my-component/my-component.d.ts +7 -0
- package/package.json +1 -1
- package/dist/jaak-stamps-webcomponent/p-380b1ebe.entry.js +0 -2
- package/dist/jaak-stamps-webcomponent/p-380b1ebe.entry.js.map +0 -1
|
@@ -38,6 +38,8 @@ export class JaakStamps {
|
|
|
38
38
|
animationId;
|
|
39
39
|
hasScreenshotTaken = false;
|
|
40
40
|
lastDetectedBox;
|
|
41
|
+
mobileNetSession;
|
|
42
|
+
mobileNetClassMap;
|
|
41
43
|
// Performance optimization properties
|
|
42
44
|
frameSkipCounter = 0;
|
|
43
45
|
FRAME_SKIP = 2; // Process every 3rd frame (reduces CPU by ~66%)
|
|
@@ -50,7 +52,9 @@ export class JaakStamps {
|
|
|
50
52
|
preprocessCtx;
|
|
51
53
|
captureCanvas;
|
|
52
54
|
captureCtx;
|
|
53
|
-
MODEL_PATH = "https://storage.googleapis.com/jaak-static/web/component/stamps/
|
|
55
|
+
MODEL_PATH = "https://storage.googleapis.com/jaak-static/web/component/stamps/ddmyp-v1.onnx";
|
|
56
|
+
MOBILENET_MODEL_PATH = "https://storage.googleapis.com/jaak-static/web/component/stamps/cdmmp-v1.onnx";
|
|
57
|
+
MOBILENET_CLASSES_PATH = "https://storage.googleapis.com/jaak-static/web/component/stamps/cdmmp-v1.json";
|
|
54
58
|
INPUT_SIZE = 320;
|
|
55
59
|
CONFIDENCE_THRESHOLD = 0.6;
|
|
56
60
|
// ISO/IEC 7810 ID-1 standard dimensions (85.60mm x 53.98mm)
|
|
@@ -256,25 +260,27 @@ export class JaakStamps {
|
|
|
256
260
|
}
|
|
257
261
|
try {
|
|
258
262
|
this.isLoading = true;
|
|
259
|
-
this.statusMessage = "Precargando
|
|
263
|
+
this.statusMessage = "Precargando modelos...";
|
|
260
264
|
this.statusColor = "#007bff";
|
|
261
265
|
const modelPath = this.MODEL_PATH;
|
|
262
|
-
this.debugLog('🤖 Preloading model:', modelPath);
|
|
266
|
+
this.debugLog('🤖 Preloading detection model:', modelPath);
|
|
263
267
|
// Configure ONNX Runtime with device-specific optimizations
|
|
264
268
|
const sessionOptions = this.getSessionOptions();
|
|
265
269
|
this.session = await window.ort.InferenceSession.create(modelPath, sessionOptions);
|
|
270
|
+
// Preload MobileNet model and classes
|
|
271
|
+
await this.loadMobileNetModel();
|
|
266
272
|
this.isModelPreloaded = true;
|
|
267
273
|
this.isLoading = false;
|
|
268
|
-
this.statusMessage = "
|
|
274
|
+
this.statusMessage = "Modelos precargados. Listo para comenzar detección";
|
|
269
275
|
this.statusColor = "#28a745";
|
|
270
276
|
this.emitReadyEvent();
|
|
271
|
-
this.debugLog('✅
|
|
272
|
-
return { success: true, message: '
|
|
277
|
+
this.debugLog('✅ Models preloaded successfully');
|
|
278
|
+
return { success: true, message: 'Models preloaded successfully' };
|
|
273
279
|
}
|
|
274
280
|
catch (error) {
|
|
275
|
-
this.debugLog('❌ Error preloading
|
|
281
|
+
this.debugLog('❌ Error preloading models:', error);
|
|
276
282
|
this.isLoading = false;
|
|
277
|
-
this.statusMessage = "Error al precargar
|
|
283
|
+
this.statusMessage = "Error al precargar los modelos";
|
|
278
284
|
this.statusColor = "#ff6b6b";
|
|
279
285
|
return { success: false, error: error.message };
|
|
280
286
|
}
|
|
@@ -284,6 +290,80 @@ export class JaakStamps {
|
|
|
284
290
|
this.completeProcess(true);
|
|
285
291
|
}
|
|
286
292
|
}
|
|
293
|
+
async loadMobileNetModel() {
|
|
294
|
+
try {
|
|
295
|
+
this.debugLog('🤖 Loading MobileNet model...');
|
|
296
|
+
// Load class map
|
|
297
|
+
const classResponse = await fetch(this.MOBILENET_CLASSES_PATH);
|
|
298
|
+
if (!classResponse.ok) {
|
|
299
|
+
throw new Error(`Failed to load class map: ${this.MOBILENET_CLASSES_PATH}`);
|
|
300
|
+
}
|
|
301
|
+
this.mobileNetClassMap = await classResponse.json();
|
|
302
|
+
this.debugLog('📋 MobileNet classes loaded:', this.mobileNetClassMap);
|
|
303
|
+
// Load model
|
|
304
|
+
const sessionOptions = this.getSessionOptions();
|
|
305
|
+
this.mobileNetSession = await window.ort.InferenceSession.create(this.MOBILENET_MODEL_PATH, sessionOptions);
|
|
306
|
+
this.debugLog('✅ MobileNet model loaded successfully');
|
|
307
|
+
}
|
|
308
|
+
catch (error) {
|
|
309
|
+
this.debugLog('❌ Error loading MobileNet model:', error);
|
|
310
|
+
throw error;
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
preprocessMobileNet(canvas) {
|
|
314
|
+
// Create a temporary canvas for MobileNet preprocessing (224x224)
|
|
315
|
+
const tempCanvas = document.createElement('canvas');
|
|
316
|
+
tempCanvas.width = 224;
|
|
317
|
+
tempCanvas.height = 224;
|
|
318
|
+
const tempCtx = tempCanvas.getContext('2d');
|
|
319
|
+
// Draw the cropped image onto the 224x224 canvas
|
|
320
|
+
tempCtx.drawImage(canvas, 0, 0, 224, 224);
|
|
321
|
+
// Get image data and preprocess for MobileNet
|
|
322
|
+
const imageData = tempCtx.getImageData(0, 0, 224, 224);
|
|
323
|
+
const data = imageData.data;
|
|
324
|
+
const hw = 224 * 224;
|
|
325
|
+
const arr = new Float32Array(3 * hw);
|
|
326
|
+
// Normalize pixels: (pixel/255 - 0.5) / 0.5 = (pixel - 127.5) / 127.5
|
|
327
|
+
for (let i = 0; i < hw; i++) {
|
|
328
|
+
arr[i] = (data[i * 4 + 0] / 255 - 0.5) / 0.5; // R
|
|
329
|
+
arr[hw + i] = (data[i * 4 + 1] / 255 - 0.5) / 0.5; // G
|
|
330
|
+
arr[2 * hw + i] = (data[i * 4 + 2] / 255 - 0.5) / 0.5; // B
|
|
331
|
+
}
|
|
332
|
+
return new window.ort.Tensor('float32', arr, [1, 3, 224, 224]);
|
|
333
|
+
}
|
|
334
|
+
async classifyDocument(canvas) {
|
|
335
|
+
if (!this.mobileNetSession || !this.mobileNetClassMap) {
|
|
336
|
+
this.debugLog('⚠️ MobileNet model not loaded');
|
|
337
|
+
return null;
|
|
338
|
+
}
|
|
339
|
+
try {
|
|
340
|
+
this.debugLog('🔍 Classifying document...');
|
|
341
|
+
// Preprocess image for MobileNet
|
|
342
|
+
const inputTensor = this.preprocessMobileNet(canvas);
|
|
343
|
+
// Run inference
|
|
344
|
+
const feeds = { input: inputTensor };
|
|
345
|
+
const results = await this.mobileNetSession.run(feeds);
|
|
346
|
+
const output = results[Object.keys(results)[0]].data;
|
|
347
|
+
// Find the class with highest confidence
|
|
348
|
+
const maxIdx = output.reduce((bestIdx, val, idx, arr) => val > arr[bestIdx] ? idx : bestIdx, 0);
|
|
349
|
+
const confidence = output[maxIdx];
|
|
350
|
+
const className = this.mobileNetClassMap[maxIdx.toString()] || "unknown";
|
|
351
|
+
this.debugLog('📄 Document classification result:', {
|
|
352
|
+
class: className,
|
|
353
|
+
confidence: confidence.toFixed(3),
|
|
354
|
+
classIndex: maxIdx
|
|
355
|
+
});
|
|
356
|
+
return {
|
|
357
|
+
class: className,
|
|
358
|
+
confidence: confidence,
|
|
359
|
+
classIndex: maxIdx
|
|
360
|
+
};
|
|
361
|
+
}
|
|
362
|
+
catch (error) {
|
|
363
|
+
this.debugLog('❌ Error classifying document:', error);
|
|
364
|
+
return null;
|
|
365
|
+
}
|
|
366
|
+
}
|
|
287
367
|
cleanup() {
|
|
288
368
|
if (this.animationId) {
|
|
289
369
|
cancelAnimationFrame(this.animationId);
|
|
@@ -311,11 +391,16 @@ export class JaakStamps {
|
|
|
311
391
|
this.captureCtx = undefined;
|
|
312
392
|
this.captureCanvas = undefined;
|
|
313
393
|
}
|
|
314
|
-
// Disposed ONNX
|
|
394
|
+
// Disposed ONNX sessions
|
|
315
395
|
if (this.session) {
|
|
316
396
|
this.session.release?.();
|
|
317
397
|
this.session = undefined;
|
|
318
398
|
}
|
|
399
|
+
if (this.mobileNetSession) {
|
|
400
|
+
this.mobileNetSession.release?.();
|
|
401
|
+
this.mobileNetSession = undefined;
|
|
402
|
+
}
|
|
403
|
+
this.mobileNetClassMap = undefined;
|
|
319
404
|
this.isModelPreloaded = false;
|
|
320
405
|
this.debugLog('🧹 Canvas pool cleaned up');
|
|
321
406
|
}
|
|
@@ -446,18 +531,22 @@ export class JaakStamps {
|
|
|
446
531
|
// Check if model is already preloaded
|
|
447
532
|
if (!this.session) {
|
|
448
533
|
this.isLoading = true;
|
|
449
|
-
this.statusMessage = "Cargando
|
|
534
|
+
this.statusMessage = "Cargando modelos...";
|
|
450
535
|
this.statusColor = "#007bff";
|
|
451
536
|
const modelPath = this.MODEL_PATH;
|
|
452
|
-
this.debugLog('🤖 Loading model:', modelPath);
|
|
537
|
+
this.debugLog('🤖 Loading detection model:', modelPath);
|
|
453
538
|
const sessionOptions = this.getSessionOptions();
|
|
454
539
|
this.session = await window.ort.InferenceSession.create(modelPath, sessionOptions);
|
|
540
|
+
// Load MobileNet model if not already loaded
|
|
541
|
+
if (!this.mobileNetSession) {
|
|
542
|
+
await this.loadMobileNetModel();
|
|
543
|
+
}
|
|
455
544
|
this.isModelPreloaded = true;
|
|
456
545
|
this.emitReadyEvent();
|
|
457
546
|
}
|
|
458
547
|
else {
|
|
459
|
-
this.debugLog('🚀 Using preloaded
|
|
460
|
-
this.statusMessage = "Usando
|
|
548
|
+
this.debugLog('🚀 Using preloaded models');
|
|
549
|
+
this.statusMessage = "Usando modelos precargados...";
|
|
461
550
|
this.statusColor = "#007bff";
|
|
462
551
|
}
|
|
463
552
|
this.statusMessage = "Configurando cámara...";
|
|
@@ -676,7 +765,9 @@ export class JaakStamps {
|
|
|
676
765
|
corners?.forEach(corner => corner.classList.add('perfect-match'));
|
|
677
766
|
if (!this.hasScreenshotTaken) {
|
|
678
767
|
this.lastDetectedBox = bestBox;
|
|
679
|
-
this.takeScreenshot()
|
|
768
|
+
this.takeScreenshot().catch(error => {
|
|
769
|
+
this.debugLog('❌ Error taking screenshot:', error);
|
|
770
|
+
});
|
|
680
771
|
this.hasScreenshotTaken = true;
|
|
681
772
|
// Reset para permitir segunda captura
|
|
682
773
|
setTimeout(() => {
|
|
@@ -759,7 +850,7 @@ export class JaakStamps {
|
|
|
759
850
|
ctx.strokeRect(x, y, w, h);
|
|
760
851
|
});
|
|
761
852
|
}
|
|
762
|
-
takeScreenshot() {
|
|
853
|
+
async takeScreenshot() {
|
|
763
854
|
if (!this.videoRef || !this.lastDetectedBox)
|
|
764
855
|
return;
|
|
765
856
|
// Activar animación
|
|
@@ -793,6 +884,15 @@ export class JaakStamps {
|
|
|
793
884
|
// Captura del frente usando canvas reutilizado
|
|
794
885
|
this.capturedFullFrame = this.captureCanvas.toDataURL('image/png');
|
|
795
886
|
this.capturedCroppedId = croppedCanvas.toDataURL('image/png');
|
|
887
|
+
// Classify the cropped document
|
|
888
|
+
const classification = await this.classifyDocument(croppedCanvas);
|
|
889
|
+
if (classification && classification.class === 'passport') {
|
|
890
|
+
// If it's a passport, skip back capture since passports don't have a back side
|
|
891
|
+
this.debugLog('📄 Passport detected - skipping back capture');
|
|
892
|
+
this.completeProcess(true);
|
|
893
|
+
return;
|
|
894
|
+
}
|
|
895
|
+
// For other IDs, continue with normal flow (back capture)
|
|
796
896
|
this.captureStep = 'back';
|
|
797
897
|
this.statusMessage = "Voltee la identificación y muestre la parte trasera";
|
|
798
898
|
this.statusColor = "#007bff";
|
|
@@ -922,7 +1022,7 @@ export class JaakStamps {
|
|
|
922
1022
|
this.cleanup();
|
|
923
1023
|
}
|
|
924
1024
|
render() {
|
|
925
|
-
return (h("div", { key: '
|
|
1025
|
+
return (h("div", { key: 'd6cd4225500c44e5c7fb4f2b3dc453a055467066', class: "detector-container" }, h("div", { key: '6bd49b0bcd686648aeffb8fb95203dc0779f1d8a', class: "video-container" }, h("video", { key: 'd2666ce9d1f5b84703ac86fd4eb901f4298ed7d5', ref: el => this.videoRef = el, autoplay: true, muted: true, playsinline: true, class: this.shouldMirrorVideo ? 'mirror' : '', style: { display: this.isVideoActive ? 'block' : 'none' } }), h("canvas", { key: 'e997f063450d81d2a15398f0761c9d06c28a85c9', ref: el => this.canvasRef = el, class: this.shouldMirrorVideo ? 'mirror' : '' }), this.isMaskReady && (h("div", { key: '4ccb8bf05091a7173255181eae1a8699fe512b56', class: "overlay-mask" }, h("div", { key: 'b35defa8c207b05e41d64c8e1eefc905d31fad55', class: "card-outline" }, h("div", { key: '6ed8dad63d6d79c1bb4a64f0b28467f5b29d778a', class: "side side-top" }), h("div", { key: '403d4a5ba681a58ad1870a4339e19b4f037a8795', class: "side side-right" }), h("div", { key: '3cb3276bb184a4b76419b4cb0b4fcb151a4df631', class: "side side-bottom" }), h("div", { key: 'ed7c9bec0e708ab282ba8b24a08d21b8126af27e', class: "side side-left" }), h("div", { key: '157be211355bf9260dc7a73d03831dbb1a2945e3', class: "corner corner-tl" }), h("div", { key: 'b19af5f319333ff05f981063af939d2c4cfc9b5f', class: "corner corner-tr" }), h("div", { key: '6fcd3e2c8842261dcd32c36e6df5b2f3dd0c8fc8', class: "corner corner-bl" }), h("div", { key: '7caab5eaee62f3f9039075b3748083c89c8127e0', class: "corner corner-br" }), !this.showFlipAnimation && !this.showSuccessAnimation && (h("div", { key: 'e3dd42ddb75b22aeef438d8651955f3dc039f00c', class: "guide-text" }, this.statusMessage))), this.captureStep === 'back' && !this.showFlipAnimation && !this.showSuccessAnimation && (h("button", { key: '54b8651817cd52bad2f34ac1413a4b1e21166cee', class: "skip-button", onClick: () => this.skipBackCapture(), type: "button" }, "Saltar reverso")))), this.isCapturing && (h("div", { key: '853707a4fa71eeacbee7985e619fca80d6522cbd', class: "capture-animation" })), this.showFlipAnimation && (h("div", { key: '246ed3f5dc57ee57345105cdebe5f950d5ff4c90', class: "flip-animation" }, h("div", { key: 'f03c6c87c8f5e98a32176cca061d369a521da2e8', class: "id-card-icon" }), h("div", { key: 'e357785b076a9e41ea4f9b3d9c14e847bacc9604', class: "flip-text" }, "\u00A1Voltea tu identificaci\u00F3n!"))), this.showSuccessAnimation && (h("div", { key: 'df3adaaecd3d547938638c58ccc0e89f8325fc23', class: "success-animation" }, h("div", { key: 'a023985b8afd6ed72f9e1c2ee63e2c7ba9fb960b', class: "check-icon" }), h("div", { key: '739ff7f329f2131061a6860529c07c8819800aab', class: "success-text" }, "\u00A1Proceso completado!"))), this.isLoading && (h("div", { key: '9b02df640528a19cb15e5dbabf9ecb43afbceffa', class: "loading-overlay" }, h("div", { key: '3652c8e355ea7fe623c478229bf548bdf30e399a', class: "loading-spinner" }), h("div", { key: '334f4469b5672f6af78b8679c15ed8c59ecefa85', class: "loading-text" }, this.statusMessage))), h("div", { key: 'a0b7c9b92771cff96e97bd67da2fd3090b080f8a', class: "watermark" }, h("img", { key: 'b5c8f0de1d8138d4a0c80526fd33b2daefa86b16', src: "https://storage.googleapis.com/jaak-static/commons/powered-by-jaak.png", alt: "Powered by Jaak" })))));
|
|
926
1026
|
}
|
|
927
1027
|
static get is() { return "jaak-stamps"; }
|
|
928
1028
|
static get encapsulation() { return "shadow"; }
|