JAAK Visage SDK
Manual Completo para Implementación de Detección Facial con el Componente Web @jaak.ai/visage
1. Objetivo, alcance y usuarios
El objetivo de este documento es proporcionar una guía completa para la implementación del WebComponent Jaak Visage de JAAK AI, un componente web avanzado para detección facial en tiempo real que utiliza cámara de video con captura automática.
Este documento abarca la instalación, configuración, implementación y uso del componente jaak-visage en aplicaciones web modernas, incluyendo frameworks como React, Angular y Vanilla JavaScript.
Dirigido a: Desarrolladores con experiencia en integraciones de WebComponents.
Nivel requerido: Conocimientos avanzados en desarrollo web, JavaScript/TypeScript, WebComponents, APIs de cámara web, y manejo de componentes de terceros.
Demo en Vivo
Antes de empezar con la implementación, puedes probar el componente en funcionamiento:
- Demo Live: https://visage.jaak.solutions/
Funcionalidades del demo:
- Probar detección facial en tiempo real
- Ver el comportamiento del componente
- Verificar compatibilidad con tu dispositivo
- Hacer debug en tiempo real
Funcionalidades Clave
- Detección facial en tiempo real: Identifica rostros usando tecnología de machine learning
- Grabación automática: Captura video automáticamente cuando detecta posición óptima
- Instrucciones interactivas: Sistema de guía visual para el usuario durante la captura
- Soporte multi-cámara: Detección e intercambio fluido entre cámaras
- Optimización de rendimiento: Carga bajo demanda de modelos de ML
- Telemetría integrada: Soporte para OpenTelemetry con trazas distribuidas y métricas
2. Desarrollo
2.1 Prerrequisitos técnicos
a) Requisitos técnicos
| Requisito | Versión/Especificación | Obligatorio | Notas |
|---|---|---|---|
| Node.js | 16.0+ | Sí | Para proyectos que usen npm/yarn |
| Navegadores modernos | Chrome 88+, Firefox 85+, Safari 14+, Edge 88+ | Sí | Soporte completo de WebComponents y MediaDevices API |
| HTTPS | Protocolo seguro | Sí en producción | Requerido para acceso a cámara web |
| JavaScript | ES2017+ | Sí | Soporte nativo de Custom Elements |
| TypeScript | 4.0+ | No | Definiciones de tipos incluidas |
b) Credenciales y configuración de accesos
Requisitos de acceso:
- Paquete NPM: Acceso al componente
jaak-visagea través de npm o CDN - Permisos específicos: Permisos de cámara del navegador (se solicitan automáticamente)
- Configuración adicional: Certificados SSL para HTTPS en producción
- Licencia del SDK: Obligatoria para el funcionamiento del componente
c) Obtención de Licencia
Existen dos formas de obtener la licencia del SDK:
Opción A: Solicitud Directa
- Formato: String alfanumérico único
- Ejemplo:
"ABC-123-XYZ-789" - Solicitar a: [email protected]
Opción B: Generación mediante API
Si no obtiene la licencia directamente del equipo JAAK, puede generarla mediante el siguiente proceso:
Paso 1: Obtener el Trace ID
Llamar al endpoint de inicio de flujo KYC:
POST /v1/kyc/sessionDe la respuesta, obtener los headers traceparent y x-trace-id:
traceparent: 00-cf143715d7a2d4ffc3ef122f62384844-6f7048446dffdb89-00
x-trace-id: 32922b9bf22570da5e9895fa592c6852
Paso 2: Validar la Licencia
Construir la licencia agregando el prefijo L al x-trace-id:
Lcf143715d7a2d4ffc3ef122f62384844
El campo obtenido se utilizará para autenticar los SDKs y el campo traceparent debe ser enviado en los headers de todos los llamados por API que se realicen.
2.2 Configuración del entorno
Paso 1. Instalación
a) Método principal de instalación
npm install @jaak.ai/visageO mediante CDN:
<script type="module" src="https://unpkg.com/@jaak.ai/visage/dist/jaak-visage-webcomponent/jaak-visage-webcomponent.esm.js"></script>b) Configuración inicial
<!DOCTYPE html>
<html lang="es">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Jaak Visage Demo</title>
</head>
<body>
<jaak-visage
id="faceDetector"
debug="false"
camera="auto">
</jaak-visage>
<script type="module" src="https://unpkg.com/@jaak.ai/visage/dist/jaak-visage-webcomponent/jaak-visage-webcomponent.esm.js"></script>
<script type="module">
const jaakVisage = document.getElementById('faceDetector');
// Escuchar cambios de estado del componente
jaakVisage.addEventListener('statusUpdated', (event) => {
console.log('Estado actualizado:', event.detail.status, event.detail.message);
});
// Escuchar cuando se capture el video
jaakVisage.addEventListener('videoCaptured', (event) => {
console.log('Video capturado:', event.detail.base64);
});
</script>
</body>
</html>Opción 1 - Importación en JavaScript/TypeScript:
import '@jaak.ai/visage';Opción 2 - Usando el loader:
import { defineCustomElements } from '@jaak.ai/visage/loader';
defineCustomElements();Paso 2. Configuración del Componente
Propiedades del componente jaak-visage:
// Props disponibles:
// debug: boolean = false // Habilitar logs de depuración
// camera: 'front' | 'back' | 'auto' = 'auto' // Preferencia de cámara
// showHelpOnStart: boolean = false // Mostrar instrucciones al iniciar
// detectionMode: 'full' | 'light' | 'auto' = 'auto' // Modo de detección
// captureMode: 'auto' | 'manual' = 'auto' // Modo de captura
// language: 'es' | 'en' = 'es' // Idioma de la interfaz
// Ejemplo de uso del componente
<jaak-visage
debug="true"
camera="front"
detection-mode="auto"
capture-mode="auto"
language="es"
show-help-on-start="true">
</jaak-visage>2.3 Guía de implementación
2.3.1 Implementación en Vanilla JavaScript
a) Ejemplo básico
<!DOCTYPE html>
<html dir="ltr" lang="es">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Jaak Visage - Demo Funcional</title>
<script type="module" src="https://unpkg.com/@jaak.ai/visage/dist/jaak-visage-webcomponent/jaak-visage-webcomponent.esm.js"></script>
<style>
body {
font-family: Arial, sans-serif;
max-width: 1000px;
margin: 0 auto;
padding: 20px;
}
.controls {
margin: 20px 0;
display: flex;
gap: 10px;
flex-wrap: wrap;
}
.status {
padding: 10px;
margin: 10px 0;
border-radius: 4px;
background: #e3f2fd;
}
button {
padding: 10px 20px;
border: none;
border-radius: 4px;
background: #2196F3;
color: white;
cursor: pointer;
}
button:hover {
background: #1976D2;
}
</style>
</head>
<body>
<h1>Jaak Visage - Demo Funcional</h1>
<div class="status">
Estado: <strong id="render-status">Inactivo</strong>
</div>
<div class="controls">
<button id="start-btn">Iniciar</button>
<button id="stop-btn">Detener</button>
<button id="restart-btn">Reiniciar</button>
<button id="help-btn">Mostrar Ayuda</button>
<button id="switch-camera-btn">Cambiar Cámara</button>
</div>
<jaak-visage id="jaakVisage" debug="true" camera="auto"></jaak-visage>
<script>
let jaakVisage;
let resultVideo;
async function initializeJaakVisage() {
jaakVisage = document.getElementById('jaakVisage');
const statusBox = document.getElementById('render-status');
// Event listeners para el componente
jaakVisage.addEventListener('videoCaptured', (event) => {
console.log('Video capturado:', event.detail);
displayCapturedVideo(event.detail.base64);
});
jaakVisage.addEventListener('statusUpdated', (event) => {
console.log('Status actualizado:', event.detail);
statusBox.textContent = event.detail.message;
});
// Botones
document.getElementById('start-btn').addEventListener('click', async () => {
try {
await jaakVisage.start();
} catch (error) {
console.error('Error iniciando:', error);
}
});
document.getElementById('stop-btn').addEventListener('click', async () => {
try {
await jaakVisage.stop();
} catch (error) {
console.error('Error deteniendo:', error);
}
});
document.getElementById('restart-btn').addEventListener('click', async () => {
try {
await jaakVisage.restart();
} catch (error) {
console.error('Error reiniciando:', error);
}
});
document.getElementById('help-btn').addEventListener('click', async () => {
try {
await jaakVisage.showHelp();
} catch (error) {
console.error('Error mostrando ayuda:', error);
}
});
document.getElementById('switch-camera-btn').addEventListener('click', async () => {
try {
await jaakVisage.switchCamera();
} catch (error) {
console.error('Error cambiando cámara:', error);
}
});
}
function displayCapturedVideo(base64Data) {
if (resultVideo) resultVideo.remove();
resultVideo = document.createElement('video');
resultVideo.controls = true;
resultVideo.style.width = '100%';
resultVideo.style.maxWidth = '400px';
resultVideo.style.marginTop = '20px';
resultVideo.src = base64Data;
jaakVisage.insertAdjacentElement('afterend', resultVideo);
}
window.addEventListener('load', initializeJaakVisage);
</script>
</body>
</html>2.3.2 Implementación en Angular
a) Instalación y configuración
npm install @jaak.ai/visage
npm install --save-dev @types/nodeb) Configuración del módulo
// app.module.ts
import { NgModule, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { AppComponent } from './app.component';
import { defineCustomElements } from '@jaak.ai/visage/loader';
defineCustomElements();
@NgModule({
declarations: [AppComponent],
imports: [BrowserModule],
providers: [],
bootstrap: [AppComponent],
schemas: [CUSTOM_ELEMENTS_SCHEMA]
})
export class AppModule { }c) Componente TypeScript
// face-detector.component.ts
import { Component, ElementRef, ViewChild } from '@angular/core';
@Component({
selector: 'app-face-detector',
templateUrl: './face-detector.component.html',
styleUrls: ['./face-detector.component.css']
})
export class FaceDetectorComponent {
@ViewChild('jaakVisage', { static: false }) jaakVisage!: ElementRef;
isReady = false;
isCapturing = false;
capturedVideo: any = null;
onStatusUpdated(event: any) {
const { status } = event.detail;
this.isCapturing = status === 'recording' || status === 'processing-video';
this.isReady = status === 'ready' || status === 'running' || status === 'inactive';
console.log('Estado actualizado:', event.detail);
}
onVideoCaptured(event: any) {
this.capturedVideo = event.detail;
this.isCapturing = false;
console.log('Video capturado:', this.capturedVideo);
}
async startCapture() {
if (!this.isReady) return;
try {
this.isCapturing = true;
await this.jaakVisage.nativeElement.start();
} catch (error) {
console.error('Error al iniciar captura:', error);
this.isCapturing = false;
}
}
async resetCapture() {
try {
await this.jaakVisage.nativeElement.restart();
this.capturedVideo = null;
this.isCapturing = false;
} catch (error) {
console.error('Error al reiniciar captura:', error);
}
}
}d) Template HTML
<!-- face-detector.component.html -->
<div class="capture-container">
<h2>Detector Facial</h2>
<div class="controls">
<button
(click)="startCapture()"
[disabled]="!isReady || isCapturing"
class="btn btn-primary">
{{ isCapturing ? 'Capturando...' : 'Iniciar Captura' }}
</button>
<button
(click)="resetCapture()"
[disabled]="!isReady"
class="btn btn-secondary">
Reiniciar
</button>
</div>
<jaak-visage
#jaakVisage
debug="false"
camera="auto"
(statusUpdated)="onStatusUpdated($event)"
(videoCaptured)="onVideoCaptured($event)">
</jaak-visage>
<div *ngIf="capturedVideo" class="results">
<h3>Video Capturado</h3>
<video [src]="capturedVideo.base64" controls style="max-width: 100%;"></video>
</div>
</div>2.3.3 Implementación en React
a) Instalación y configuración
npm install @jaak.ai/visageb) Componente React
// FaceDetector.jsx
import React, { useRef, useEffect, useState } from 'react';
import { defineCustomElements } from '@jaak.ai/visage/loader';
defineCustomElements();
const FaceDetector = () => {
const jaakVisageRef = useRef(null);
const [isReady, setIsReady] = useState(false);
const [isCapturing, setIsCapturing] = useState(false);
const [capturedVideo, setCapturedVideo] = useState(null);
useEffect(() => {
const component = jaakVisageRef.current;
const handleStatusUpdate = (event) => {
const { status } = event.detail;
setIsCapturing(status === 'recording' || status === 'processing-video');
setIsReady(status === 'ready' || status === 'running' || status === 'inactive');
console.log('Estado actualizado:', event.detail);
};
const handleVideoCaptured = (event) => {
setCapturedVideo(event.detail);
setIsCapturing(false);
console.log('Video capturado:', event.detail);
};
component.addEventListener('statusUpdated', handleStatusUpdate);
component.addEventListener('videoCaptured', handleVideoCaptured);
return () => {
component.removeEventListener('statusUpdated', handleStatusUpdate);
component.removeEventListener('videoCaptured', handleVideoCaptured);
};
}, []);
const startCapture = async () => {
if (!isReady) return;
try {
setIsCapturing(true);
await jaakVisageRef.current.start();
} catch (error) {
console.error('Error al iniciar captura:', error);
setIsCapturing(false);
}
};
const resetCapture = async () => {
try {
await jaakVisageRef.current.restart();
setCapturedVideo(null);
setIsCapturing(false);
} catch (error) {
console.error('Error al reiniciar captura:', error);
}
};
return (
<div className="face-detector-container">
<h2>Detector Facial</h2>
<div className="controls">
<button onClick={startCapture} disabled={!isReady || isCapturing}>
{isCapturing ? 'Capturando...' : 'Iniciar Captura'}
</button>
<button onClick={resetCapture} disabled={!isReady}>
Reiniciar
</button>
</div>
<jaak-visage ref={jaakVisageRef} debug="false" camera="auto" />
{capturedVideo && (
<div className="results">
<h3>Video Capturado</h3>
<video src={capturedVideo.base64} controls style={{ maxWidth: '100%' }} />
</div>
)}
</div>
);
};
export default FaceDetector;2.3.4 Implementación avanzada
<!DOCTYPE html>
<html lang="es">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Jaak Visage - Implementación Completa</title>
<script type="module" src="https://unpkg.com/@jaak.ai/visage/dist/jaak-visage-webcomponent/jaak-visage-webcomponent.esm.js"></script>
<style>
.container { max-width: 1000px; margin: 0 auto; padding: 20px; }
.controls { display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 10px; margin: 20px 0; }
.control-group { border: 1px solid #ddd; padding: 15px; border-radius: 8px; }
.control-group h3 { margin: 0 0 10px 0; color: #333; }
button { padding: 8px 16px; margin: 5px; border: none; border-radius: 4px; background: #007bff; color: white; cursor: pointer; }
button:hover { background: #0056b3; }
button:disabled { background: #6c757d; cursor: not-allowed; }
.status { padding: 10px; margin: 10px 0; border-radius: 4px; background: #e3f2fd; }
.logs { background: #f8f9fa; padding: 15px; border-radius: 8px; margin: 20px 0; max-height: 300px; overflow-y: auto; }
.log-entry { font-family: monospace; font-size: 12px; margin: 2px 0; }
</style>
</head>
<body>
<div class="container">
<h1>Jaak Visage - Implementación Completa</h1>
<jaak-visage id="jaakVisage" debug="true" camera="auto" style="width: 100%; height: 400px; border: 2px solid #ddd; border-radius: 8px;"></jaak-visage>
<div class="controls">
<div class="control-group">
<h3>Control Principal</h3>
<button onclick="startComponent()">Iniciar</button>
<button onclick="stopComponent()">Detener</button>
<button onclick="restartComponent()">Reiniciar</button>
</div>
<div class="control-group">
<h3>Control de Cámara</h3>
<button onclick="switchCamera()">Cambiar Cámara</button>
<button onclick="getCameraDevices()">Obtener Cámaras</button>
<button onclick="preloadModels()">Precargar Modelos</button>
</div>
<div class="control-group">
<h3>Funciones Adicionales</h3>
<button onclick="showHelp()">Mostrar Ayuda</button>
<button onclick="startCameraOnly()">Solo Cámara</button>
<button onclick="stopCameraOnly()">Detener Cámara</button>
</div>
</div>
<div id="status" class="status">Estado: Inicializando...</div>
<div class="logs">
<h3>Logs de Eventos:</h3>
<div id="logContainer"></div>
</div>
</div>
<script>
class AdvancedJaakVisageController {
constructor() {
this.jaakVisage = document.getElementById('jaakVisage');
this.statusDiv = document.getElementById('status');
this.logContainer = document.getElementById('logContainer');
this.setupAllEventListeners();
}
setupAllEventListeners() {
this.jaakVisage.addEventListener('videoCaptured', this.handleVideoCaptured.bind(this));
this.jaakVisage.addEventListener('statusUpdated', this.handleStatusUpdated.bind(this));
}
handleVideoCaptured(event) {
this.addLog('VIDEO_CAPTURED', 'Video capturado - Data URL disponible');
this.displayResult(event.detail.base64, true);
}
handleStatusUpdated(event) {
const { status, message } = event.detail;
this.statusDiv.textContent = `Estado: ${message}`;
switch (status) {
case 'running':
this.statusDiv.style.background = '#d4edda';
break;
case 'recording':
this.statusDiv.style.background = '#fff3cd';
break;
case 'error':
this.statusDiv.style.background = '#f8d7da';
break;
default:
this.statusDiv.style.background = '#e3f2fd';
}
this.addLog('STATUS', `Estado: ${status} - ${message}`);
}
displayResult(base64DataUrl, isVideo) {
const existingResult = document.getElementById('result');
if (existingResult) existingResult.remove();
const resultDiv = document.createElement('div');
resultDiv.id = 'result';
resultDiv.style.marginTop = '20px';
if (isVideo) {
const video = document.createElement('video');
video.controls = true;
video.style.maxWidth = '100%';
video.src = base64DataUrl;
resultDiv.appendChild(video);
}
document.querySelector('.container').appendChild(resultDiv);
}
addLog(type, message) {
const timestamp = new Date().toLocaleTimeString();
const logEntry = document.createElement('div');
logEntry.className = 'log-entry';
logEntry.textContent = `[${timestamp}] ${type}: ${message}`;
this.logContainer.appendChild(logEntry);
this.logContainer.scrollTop = this.logContainer.scrollHeight;
}
}
let controller;
window.addEventListener('DOMContentLoaded', () => {
controller = new AdvancedJaakVisageController();
});
async function startComponent() {
try {
await controller.jaakVisage.start();
controller.addLog('START', 'Componente iniciado');
} catch (error) {
controller.addLog('ERROR', error.message);
}
}
async function stopComponent() {
try {
await controller.jaakVisage.stop();
controller.addLog('STOP', 'Componente detenido');
} catch (error) {
controller.addLog('ERROR', error.message);
}
}
async function restartComponent() {
try {
await controller.jaakVisage.restart();
controller.addLog('RESTART', 'Componente reiniciado');
} catch (error) {
controller.addLog('ERROR', error.message);
}
}
async function switchCamera() {
try {
await controller.jaakVisage.switchCamera();
controller.addLog('SWITCH_CAMERA', 'Cámara cambiada');
} catch (error) {
controller.addLog('ERROR', error.message);
}
}
async function getCameraDevices() {
try {
const devices = await controller.jaakVisage.getCameraDevices();
controller.addLog('GET_CAMERAS', `${devices.length} cámaras encontradas`);
} catch (error) {
controller.addLog('ERROR', error.message);
}
}
async function preloadModels() {
try {
await controller.jaakVisage.preload();
controller.addLog('PRELOAD', 'Modelos precargados');
} catch (error) {
controller.addLog('ERROR', error.message);
}
}
async function showHelp() {
try {
await controller.jaakVisage.showHelp();
controller.addLog('SHOW_HELP', 'Ayuda mostrada');
} catch (error) {
controller.addLog('ERROR', error.message);
}
}
async function startCameraOnly() {
try {
await controller.jaakVisage.startCamera();
controller.addLog('START_CAMERA', 'Cámara iniciada');
} catch (error) {
controller.addLog('ERROR', error.message);
}
}
async function stopCameraOnly() {
try {
await controller.jaakVisage.stopCamera();
controller.addLog('STOP_CAMERA', 'Cámara detenida');
} catch (error) {
controller.addLog('ERROR', error.message);
}
}
</script>
</body>
</html>2.4 Referencias/Métodos
2.4.1 Especificación principal
WebComponent - jaak-visage
Componente web avanzado para detección facial en tiempo real que utiliza tecnología de machine learning para identificar rostros en video streams. Proporciona funcionalidades de grabación automática, validación biométrica, y sistema de instrucciones interactivas.
2.4.2 Propiedades de entrada
Propiedades de Licencia (Requeridas)
| Propiedad | Tipo | Requerido | Valor por Defecto | Descripción | Ejemplo |
|---|---|---|---|---|---|
| license | string | Sí | undefined | Clave de licencia para autenticación | license="your-key" |
| licenseEnvironment | 'dev' | 'qa' | 'sandbox' | 'prod' | No | 'prod' | Ambiente para validación de licencia | license-environment="prod" |
| appId | string | No | 'jaak-visage-web' | Identificador de aplicación | app-id="my-app" |
| traceId | string | No | undefined | ID de traza para tracking distribuido | trace-id="abc123" |
Propiedades de Configuración Base
| Propiedad | Tipo | Requerido | Valor por Defecto | Descripción | Ejemplo |
|---|---|---|---|---|---|
| debug | boolean | No | false | Habilita logs de depuración | debug="true" |
| camera | 'front' | 'back' | 'auto' | No | 'auto' | Preferencia de cámara | camera="front" |
| detectionMode | 'full' | 'light' | 'auto' | No | 'auto' | Modo de detección | detection-mode="auto" |
| captureMode | 'auto' | 'manual' | No | 'auto' | Modo de captura | capture-mode="auto" |
| language | 'es' | 'en' | No | 'es' | Idioma de la interfaz | language="en" |
| showHelpOnStart | boolean | No | false | Muestra instrucciones al iniciar | show-help-on-start="true" |
| texts | VisageTexts | No | {} | Textos personalizados para UI | Ver interfaz VisageTexts |
Propiedades de Telemetría y OpenTelemetry
| Propiedad | Tipo | Requerido | Valor por Defecto | Descripción |
|---|---|---|---|---|
| enableTelemetry | boolean | No | true | Habilita trazas distribuidas |
| telemetryCollectorUrl | string | No | 'https://collector.jaak.ai/v1/traces' | URL del recolector OTLP para trazas |
| enableMetrics | boolean | No | true | Habilita métricas |
| metricsCollectorUrl | string | No | 'https://collector.jaak.ai/v1/metrics' | URL del recolector OTLP para métricas |
| metricsExportIntervalMillis | number | No | 60000 | Intervalo de exportación de métricas |
Propiedades de Contexto
| Propiedad | Tipo | Requerido | Valor por Defecto | Descripción |
|---|---|---|---|---|
| customerId | string | No | undefined | ID del cliente para telemetría |
| tenantId | string | No | undefined | ID del tenant para multi-tenancy |
| environment | string | No | 'production' | Ambiente de ejecución |
2.4.3 Métodos públicos
| Método | Parámetros | Retorno | Descripción |
|---|---|---|---|
start() | void | Promise<void> | Inicia el componente, precarga modelos y activa la cámara |
stop() | void | Promise<void> | Detiene completamente el componente y libera recursos |
restart() | void | Promise<void> | Reinicia el componente manteniendo la configuración |
preload() | void | Promise<void> | Precarga los modelos de detección facial sin iniciar |
startCamera() | void | Promise<void> | Inicia solo la cámara sin detección facial |
stopCamera() | void | Promise<void> | Detiene solo la cámara |
showHelp() | void | Promise<void> | Muestra las instrucciones de ayuda |
switchCamera() | deviceId?: string | Promise<void> | Cambia entre cámaras disponibles |
getCameraDevices() | void | Promise<MediaDeviceInfo[]> | Obtiene lista de cámaras disponibles |
getAvailableCameras() | void | Promise<MediaDeviceInfo[]> | Obtiene cámaras sin pedir permisos |
getCurrentCameraId() | void | Promise<string> | Obtiene el ID de la cámara actual |
captureNow() | void | Promise<void> | Dispara captura manual inmediata (solo modo manual) |
2.4.4 Eventos
| Evento | Datos | Descripción |
|---|---|---|
videoCaptured | {base64: string} | Se emite cuando se captura un video exitosamente |
statusUpdated | {status: string, message: string} | Se emite cuando cambia el estado del componente |
traceIdGenerated | {traceId: string} | Se emite después de validación de licencia con el trace ID |
2.4.5 Estados del Componente
| Estado | Descripción |
|---|---|
inactive | Componente cargado pero no iniciado |
starting | Iniciando el componente |
initializing | Inicializando detección facial |
preloading | Precargando modelos |
preloaded | Modelos precargados |
requesting-camera | Solicitando acceso a la cámara |
camera-active | Cámara activa |
ready | Detección facial lista |
running | Detección facial activa |
face-detected | Rostro detectado correctamente |
countdown | Cuenta regresiva para grabación |
recording | Grabando video |
capture-complete | Captura completada |
processing-video | Procesando video capturado |
video-ready | Video listo |
face-lost | Rostro perdido durante grabación |
recording-cancelled | Grabación cancelada |
switching-camera | Cambiando cámara |
error | Error ocurrido |
2.5 Telemetría y Observabilidad (OpenTelemetry)
El componente Jaak Visage incluye integración completa con OpenTelemetry para proporcionar observabilidad, trazabilidad y monitoreo de rendimiento en producción.
2.5.1 Características de Telemetría
Trazas Distribuidas (Distributed Tracing)
- Seguimiento completo del flujo de detección facial
- Correlación de peticiones entre frontend y backend
- Identificación de cuellos de botella de rendimiento
- Propagación de contexto mediante headers W3C Trace Context (
traceparent)
Métricas de Rendimiento
Contadores:
jaak.detections.total- Total de detecciones faciales realizadasjaak.errors.total- Total de errores por tipojaak.model.loads.total- Total de cargas de modelo
Histogramas (Latencias):
jaak.detection.duration- Duración de operaciones de detección (ms)jaak.model.load.duration- Duración de carga de modelos (ms)
Gauges (Estado Actual):
jaak.sessions.active- Sesiones activasjaak.memory.usage- Uso de memoria JS heap (bytes)
2.5.2 Configuración de Telemetría
<jaak-visage
license="your-license-key"
enable-telemetry="true"
enable-metrics="true"
telemetry-collector-url="https://collector.jaak.ai/v1/traces"
metrics-collector-url="https://collector.jaak.ai/v1/metrics"
metrics-export-interval-millis="60000"
propagate-trace-header-cors-urls="https://api.example.com,https://backend.example.com"
customer-id="customer123"
tenant-id="tenant456"
environment="production">
</jaak-visage>2.6 Validación de Licencia
El componente requiere una licencia válida para funcionar. La validación ocurre automáticamente cuando el componente se carga.
2.6.1 Configuración de Licencia
<jaak-visage
license="your-license-key-here"
license-environment="prod"
app-id="my-application">
</jaak-visage>2.6.2 Ambientes Disponibles
| Ambiente | URL de Validación | Uso |
|---|---|---|
dev | https://api.dev.jaak.ai | Desarrollo local |
qa | https://api.qa.jaak.ai | Pruebas de calidad |
sandbox | https://api.sandbox.jaak.ai | Pruebas de integración |
prod | https://services.api.jaak.ai | Producción |
2.6.3 Manejo de Errores de Licencia
Mensajes de error comunes:
"License key is required"- No se proporcionó licencia"Invalid license key"- Licencia inválida o expirada"License validation failed"- Error de validación
2.7 Componentes adicionales
Dependencias principales incluidas automáticamente:
- @tensorflow-models/face-detection (^1.0.2): Modelo de detección facial TensorFlow
- @tensorflow/tfjs- (^4.15.0):* TensorFlow.js
- @stencil/core (^4.27.1): Framework para Web Components
- @opentelemetry/ (múltiples versiones):* OpenTelemetry para telemetría
Características técnicas integradas:
- Detección facial en tiempo real con MediaPipe
- Grabación automática basada en detección de posición óptima
- Interface de instrucciones con progreso visual
- Soporte multi-cámara con intercambio fluido
- Optimizaciones de rendimiento incluidas
2.8 Pruebas y validación
a) Casos de prueba
| Caso de Prueba | Entrada/Configuración | Resultado Esperado | Criterio de Éxito |
|---|---|---|---|
| Carga básica | <jaak-visage></jaak-visage> | Estado 'inactive' inicial | Componente se inicializa sin errores |
| Detección facial exitosa | Usuario con rostro visible y bien iluminado | Marco verde alrededor del rostro | Color del marco cambia a verde |
| Posición facial correcta | Cara centrada con tamaño adecuado | Inicio de countdown automático | Grabación automática se inicia |
| Grabación automática | Detección facial válida por 3 frames | Video grabado automáticamente | Evento videoCaptured emitido |
| Cambio de cámara | switchCamera() con múltiples cámaras | Cambio exitoso entre cámaras | Stream se actualiza sin errores |
2.9 Solución de problemas
a) Problemas comunes
| Problema: | Cámara no funciona en producción |
|---|---|
| Descripción: | El componente no puede acceder a la cámara y muestra error de permisos |
| Causas posibles: | Sitio servido con HTTP en lugar de HTTPS, permisos de cámara denegados |
| Solución: | 1. Asegurar que el sitio se sirva con HTTPS 2. Verificar permisos de cámara en el navegador 3. Para desarrollo local usar localhost o 127.0.0.1 |
| Problema: | Detección facial no funciona |
|---|---|
| Descripción: | El componente se carga pero no detecta rostros |
| Causas posibles: | Modelos de ML no cargados, mala iluminación, configuración incorrecta |
| Solución: | 1. Verificar conexión a internet para carga de modelos 2. Mejorar iluminación 3. Revisar que el componente esté iniciado con start() |
| Problema: | Performance lenta en dispositivos móviles |
|---|---|
| Descripción: | La detección facial es muy lenta o causa lag |
| Causas posibles: | Dispositivo con recursos limitados, resolución muy alta |
| Solución: | 1. Usar preload() antes de start()2. Reducir dimensiones del componente 3. Probar en dispositivos reales |
b) Debugging y diagnóstico
// Habilitar debug mode
<jaak-visage debug="true"></jaak-visage>
// Monitorear eventos para diagnóstico
const jaakVisage = document.getElementById('jaakVisage');
jaakVisage.addEventListener('statusUpdated', (event) => {
console.log('Status:', event.detail.status, event.detail.message);
});
jaakVisage.addEventListener('videoCaptured', (event) => {
console.log('Video captured:', event.detail.base64.substring(0, 50) + '...');
});
// Verificar cámaras disponibles
const cameras = await jaakVisage.getCameraDevices();
console.log('Available cameras:', cameras);Contacta al equipo de soporte ([email protected]) cuando:
- Los pasos de troubleshooting no resuelven el problema
- Recibes errores no documentados
- Necesitas configuraciones especiales para tu caso de uso
- Experimentas problemas de rendimiento persistentes
Información a incluir: Logs del navegador, configuración del componente, pasos para reproducir el error, versión del navegador y sistema operativo
2.10 Consideraciones importantes
a) Seguridad
- HTTPS Obligatorio: El acceso a cámara requiere protocolo seguro en producción
- Permisos de usuario: Se solicitan automáticamente, pero deben manejarse las denegaciones
- Datos biométricos: Los videos/imágenes se procesan localmente por defecto
- Privacidad: No se envían datos a servidores externos sin consentimiento explícito
b) Rendimiento
- Tiempo de carga inicial: 1-3 segundos
- Optimizado para dispositivos modernos
- Funciona eficientemente en dispositivos móviles y escritorio
c) Compatibilidad
- Navegadores soportados: Chrome 88+, Firefox 85+, Safari 14+, Edge 88+
- Frameworks: Compatible con React, Angular, Vue, Vanilla JS
- Responsive: Adaptable a dispositivos móviles y escritorio
- Offline: Funciona sin conexión una vez cargados los modelos
3. Anexos
Anexo A. Glosario de términos
| Término | Definición |
|---|---|
| WebComponent | Estándar web que permite crear elementos HTML personalizados reutilizables |
| Stencil | Compilador para crear Web Components optimizados |
| Face Detection | Tecnología que identifica y localiza rostros humanos en imágenes o video |
| MediaDevices API | API del navegador para acceder a cámaras y micrófonos |
| MediaPipe | Plataforma de Google para pipelines de percepción multimodal |
| Data URL | Formato que incluye datos binarios codificados en Base64 dentro de una URL |
| Props | Propiedades que se pasan a un componente para configurar su comportamiento |
| Shadow DOM | Tecnología que encapsula el DOM y CSS de un Web Component |
| OpenTelemetry | Framework de observabilidad para instrumentación y exportación de telemetría |
| OTLP | OpenTelemetry Protocol - Protocolo estándar para transmisión de telemetría |
Anexo B. Enlaces de referencia
- JAAK AI: https://jaak.ai
- Demo Live: https://visage.jaak.solutions/
- Stencil Framework: https://stenciljs.com
- MediaPipe Face Detection: https://developers.google.com/mediapipe/solutions/vision/face_detector
- Documentación WebComponents: https://developer.mozilla.org/en-US/docs/Web/Web_Components
- MediaDevices API: https://developer.mozilla.org/en-US/docs/Web/API/MediaDevices
Updated 9 days ago
