@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
|
@@ -46,6 +46,8 @@ const JaakStamps = class {
|
|
|
46
46
|
animationId;
|
|
47
47
|
hasScreenshotTaken = false;
|
|
48
48
|
lastDetectedBox;
|
|
49
|
+
mobileNetSession;
|
|
50
|
+
mobileNetClassMap;
|
|
49
51
|
// Performance optimization properties
|
|
50
52
|
frameSkipCounter = 0;
|
|
51
53
|
FRAME_SKIP = 2; // Process every 3rd frame (reduces CPU by ~66%)
|
|
@@ -58,7 +60,9 @@ const JaakStamps = class {
|
|
|
58
60
|
preprocessCtx;
|
|
59
61
|
captureCanvas;
|
|
60
62
|
captureCtx;
|
|
61
|
-
MODEL_PATH = "https://storage.googleapis.com/jaak-static/web/component/stamps/
|
|
63
|
+
MODEL_PATH = "https://storage.googleapis.com/jaak-static/web/component/stamps/ddmyp-v1.onnx";
|
|
64
|
+
MOBILENET_MODEL_PATH = "https://storage.googleapis.com/jaak-static/web/component/stamps/cdmmp-v1.onnx";
|
|
65
|
+
MOBILENET_CLASSES_PATH = "https://storage.googleapis.com/jaak-static/web/component/stamps/cdmmp-v1.json";
|
|
62
66
|
INPUT_SIZE = 320;
|
|
63
67
|
CONFIDENCE_THRESHOLD = 0.6;
|
|
64
68
|
// ISO/IEC 7810 ID-1 standard dimensions (85.60mm x 53.98mm)
|
|
@@ -264,25 +268,27 @@ const JaakStamps = class {
|
|
|
264
268
|
}
|
|
265
269
|
try {
|
|
266
270
|
this.isLoading = true;
|
|
267
|
-
this.statusMessage = "Precargando
|
|
271
|
+
this.statusMessage = "Precargando modelos...";
|
|
268
272
|
this.statusColor = "#007bff";
|
|
269
273
|
const modelPath = this.MODEL_PATH;
|
|
270
|
-
this.debugLog('🤖 Preloading model:', modelPath);
|
|
274
|
+
this.debugLog('🤖 Preloading detection model:', modelPath);
|
|
271
275
|
// Configure ONNX Runtime with device-specific optimizations
|
|
272
276
|
const sessionOptions = this.getSessionOptions();
|
|
273
277
|
this.session = await window.ort.InferenceSession.create(modelPath, sessionOptions);
|
|
278
|
+
// Preload MobileNet model and classes
|
|
279
|
+
await this.loadMobileNetModel();
|
|
274
280
|
this.isModelPreloaded = true;
|
|
275
281
|
this.isLoading = false;
|
|
276
|
-
this.statusMessage = "
|
|
282
|
+
this.statusMessage = "Modelos precargados. Listo para comenzar detección";
|
|
277
283
|
this.statusColor = "#28a745";
|
|
278
284
|
this.emitReadyEvent();
|
|
279
|
-
this.debugLog('✅
|
|
280
|
-
return { success: true, message: '
|
|
285
|
+
this.debugLog('✅ Models preloaded successfully');
|
|
286
|
+
return { success: true, message: 'Models preloaded successfully' };
|
|
281
287
|
}
|
|
282
288
|
catch (error) {
|
|
283
|
-
this.debugLog('❌ Error preloading
|
|
289
|
+
this.debugLog('❌ Error preloading models:', error);
|
|
284
290
|
this.isLoading = false;
|
|
285
|
-
this.statusMessage = "Error al precargar
|
|
291
|
+
this.statusMessage = "Error al precargar los modelos";
|
|
286
292
|
this.statusColor = "#ff6b6b";
|
|
287
293
|
return { success: false, error: error.message };
|
|
288
294
|
}
|
|
@@ -292,6 +298,80 @@ const JaakStamps = class {
|
|
|
292
298
|
this.completeProcess(true);
|
|
293
299
|
}
|
|
294
300
|
}
|
|
301
|
+
async loadMobileNetModel() {
|
|
302
|
+
try {
|
|
303
|
+
this.debugLog('🤖 Loading MobileNet model...');
|
|
304
|
+
// Load class map
|
|
305
|
+
const classResponse = await fetch(this.MOBILENET_CLASSES_PATH);
|
|
306
|
+
if (!classResponse.ok) {
|
|
307
|
+
throw new Error(`Failed to load class map: ${this.MOBILENET_CLASSES_PATH}`);
|
|
308
|
+
}
|
|
309
|
+
this.mobileNetClassMap = await classResponse.json();
|
|
310
|
+
this.debugLog('📋 MobileNet classes loaded:', this.mobileNetClassMap);
|
|
311
|
+
// Load model
|
|
312
|
+
const sessionOptions = this.getSessionOptions();
|
|
313
|
+
this.mobileNetSession = await window.ort.InferenceSession.create(this.MOBILENET_MODEL_PATH, sessionOptions);
|
|
314
|
+
this.debugLog('✅ MobileNet model loaded successfully');
|
|
315
|
+
}
|
|
316
|
+
catch (error) {
|
|
317
|
+
this.debugLog('❌ Error loading MobileNet model:', error);
|
|
318
|
+
throw error;
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
preprocessMobileNet(canvas) {
|
|
322
|
+
// Create a temporary canvas for MobileNet preprocessing (224x224)
|
|
323
|
+
const tempCanvas = document.createElement('canvas');
|
|
324
|
+
tempCanvas.width = 224;
|
|
325
|
+
tempCanvas.height = 224;
|
|
326
|
+
const tempCtx = tempCanvas.getContext('2d');
|
|
327
|
+
// Draw the cropped image onto the 224x224 canvas
|
|
328
|
+
tempCtx.drawImage(canvas, 0, 0, 224, 224);
|
|
329
|
+
// Get image data and preprocess for MobileNet
|
|
330
|
+
const imageData = tempCtx.getImageData(0, 0, 224, 224);
|
|
331
|
+
const data = imageData.data;
|
|
332
|
+
const hw = 224 * 224;
|
|
333
|
+
const arr = new Float32Array(3 * hw);
|
|
334
|
+
// Normalize pixels: (pixel/255 - 0.5) / 0.5 = (pixel - 127.5) / 127.5
|
|
335
|
+
for (let i = 0; i < hw; i++) {
|
|
336
|
+
arr[i] = (data[i * 4 + 0] / 255 - 0.5) / 0.5; // R
|
|
337
|
+
arr[hw + i] = (data[i * 4 + 1] / 255 - 0.5) / 0.5; // G
|
|
338
|
+
arr[2 * hw + i] = (data[i * 4 + 2] / 255 - 0.5) / 0.5; // B
|
|
339
|
+
}
|
|
340
|
+
return new window.ort.Tensor('float32', arr, [1, 3, 224, 224]);
|
|
341
|
+
}
|
|
342
|
+
async classifyDocument(canvas) {
|
|
343
|
+
if (!this.mobileNetSession || !this.mobileNetClassMap) {
|
|
344
|
+
this.debugLog('⚠️ MobileNet model not loaded');
|
|
345
|
+
return null;
|
|
346
|
+
}
|
|
347
|
+
try {
|
|
348
|
+
this.debugLog('🔍 Classifying document...');
|
|
349
|
+
// Preprocess image for MobileNet
|
|
350
|
+
const inputTensor = this.preprocessMobileNet(canvas);
|
|
351
|
+
// Run inference
|
|
352
|
+
const feeds = { input: inputTensor };
|
|
353
|
+
const results = await this.mobileNetSession.run(feeds);
|
|
354
|
+
const output = results[Object.keys(results)[0]].data;
|
|
355
|
+
// Find the class with highest confidence
|
|
356
|
+
const maxIdx = output.reduce((bestIdx, val, idx, arr) => val > arr[bestIdx] ? idx : bestIdx, 0);
|
|
357
|
+
const confidence = output[maxIdx];
|
|
358
|
+
const className = this.mobileNetClassMap[maxIdx.toString()] || "unknown";
|
|
359
|
+
this.debugLog('📄 Document classification result:', {
|
|
360
|
+
class: className,
|
|
361
|
+
confidence: confidence.toFixed(3),
|
|
362
|
+
classIndex: maxIdx
|
|
363
|
+
});
|
|
364
|
+
return {
|
|
365
|
+
class: className,
|
|
366
|
+
confidence: confidence,
|
|
367
|
+
classIndex: maxIdx
|
|
368
|
+
};
|
|
369
|
+
}
|
|
370
|
+
catch (error) {
|
|
371
|
+
this.debugLog('❌ Error classifying document:', error);
|
|
372
|
+
return null;
|
|
373
|
+
}
|
|
374
|
+
}
|
|
295
375
|
cleanup() {
|
|
296
376
|
if (this.animationId) {
|
|
297
377
|
cancelAnimationFrame(this.animationId);
|
|
@@ -319,11 +399,16 @@ const JaakStamps = class {
|
|
|
319
399
|
this.captureCtx = undefined;
|
|
320
400
|
this.captureCanvas = undefined;
|
|
321
401
|
}
|
|
322
|
-
// Disposed ONNX
|
|
402
|
+
// Disposed ONNX sessions
|
|
323
403
|
if (this.session) {
|
|
324
404
|
this.session.release?.();
|
|
325
405
|
this.session = undefined;
|
|
326
406
|
}
|
|
407
|
+
if (this.mobileNetSession) {
|
|
408
|
+
this.mobileNetSession.release?.();
|
|
409
|
+
this.mobileNetSession = undefined;
|
|
410
|
+
}
|
|
411
|
+
this.mobileNetClassMap = undefined;
|
|
327
412
|
this.isModelPreloaded = false;
|
|
328
413
|
this.debugLog('🧹 Canvas pool cleaned up');
|
|
329
414
|
}
|
|
@@ -454,18 +539,22 @@ const JaakStamps = class {
|
|
|
454
539
|
// Check if model is already preloaded
|
|
455
540
|
if (!this.session) {
|
|
456
541
|
this.isLoading = true;
|
|
457
|
-
this.statusMessage = "Cargando
|
|
542
|
+
this.statusMessage = "Cargando modelos...";
|
|
458
543
|
this.statusColor = "#007bff";
|
|
459
544
|
const modelPath = this.MODEL_PATH;
|
|
460
|
-
this.debugLog('🤖 Loading model:', modelPath);
|
|
545
|
+
this.debugLog('🤖 Loading detection model:', modelPath);
|
|
461
546
|
const sessionOptions = this.getSessionOptions();
|
|
462
547
|
this.session = await window.ort.InferenceSession.create(modelPath, sessionOptions);
|
|
548
|
+
// Load MobileNet model if not already loaded
|
|
549
|
+
if (!this.mobileNetSession) {
|
|
550
|
+
await this.loadMobileNetModel();
|
|
551
|
+
}
|
|
463
552
|
this.isModelPreloaded = true;
|
|
464
553
|
this.emitReadyEvent();
|
|
465
554
|
}
|
|
466
555
|
else {
|
|
467
|
-
this.debugLog('🚀 Using preloaded
|
|
468
|
-
this.statusMessage = "Usando
|
|
556
|
+
this.debugLog('🚀 Using preloaded models');
|
|
557
|
+
this.statusMessage = "Usando modelos precargados...";
|
|
469
558
|
this.statusColor = "#007bff";
|
|
470
559
|
}
|
|
471
560
|
this.statusMessage = "Configurando cámara...";
|
|
@@ -684,7 +773,9 @@ const JaakStamps = class {
|
|
|
684
773
|
corners?.forEach(corner => corner.classList.add('perfect-match'));
|
|
685
774
|
if (!this.hasScreenshotTaken) {
|
|
686
775
|
this.lastDetectedBox = bestBox;
|
|
687
|
-
this.takeScreenshot()
|
|
776
|
+
this.takeScreenshot().catch(error => {
|
|
777
|
+
this.debugLog('❌ Error taking screenshot:', error);
|
|
778
|
+
});
|
|
688
779
|
this.hasScreenshotTaken = true;
|
|
689
780
|
// Reset para permitir segunda captura
|
|
690
781
|
setTimeout(() => {
|
|
@@ -767,7 +858,7 @@ const JaakStamps = class {
|
|
|
767
858
|
ctx.strokeRect(x, y, w, h);
|
|
768
859
|
});
|
|
769
860
|
}
|
|
770
|
-
takeScreenshot() {
|
|
861
|
+
async takeScreenshot() {
|
|
771
862
|
if (!this.videoRef || !this.lastDetectedBox)
|
|
772
863
|
return;
|
|
773
864
|
// Activar animación
|
|
@@ -801,6 +892,15 @@ const JaakStamps = class {
|
|
|
801
892
|
// Captura del frente usando canvas reutilizado
|
|
802
893
|
this.capturedFullFrame = this.captureCanvas.toDataURL('image/png');
|
|
803
894
|
this.capturedCroppedId = croppedCanvas.toDataURL('image/png');
|
|
895
|
+
// Classify the cropped document
|
|
896
|
+
const classification = await this.classifyDocument(croppedCanvas);
|
|
897
|
+
if (classification && classification.class === 'passport') {
|
|
898
|
+
// If it's a passport, skip back capture since passports don't have a back side
|
|
899
|
+
this.debugLog('📄 Passport detected - skipping back capture');
|
|
900
|
+
this.completeProcess(true);
|
|
901
|
+
return;
|
|
902
|
+
}
|
|
903
|
+
// For other IDs, continue with normal flow (back capture)
|
|
804
904
|
this.captureStep = 'back';
|
|
805
905
|
this.statusMessage = "Voltee la identificación y muestre la parte trasera";
|
|
806
906
|
this.statusColor = "#007bff";
|
|
@@ -930,7 +1030,7 @@ const JaakStamps = class {
|
|
|
930
1030
|
this.cleanup();
|
|
931
1031
|
}
|
|
932
1032
|
render() {
|
|
933
|
-
return (h("div", { key: '
|
|
1033
|
+
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" })))));
|
|
934
1034
|
}
|
|
935
1035
|
};
|
|
936
1036
|
JaakStamps.style = myComponentCss;
|