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:

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

RequisitoVersión/EspecificaciónObligatorioNotas
Node.js16.0+Para proyectos que usen npm/yarn
Navegadores modernosChrome 88+, Firefox 85+, Safari 14+, Edge 88+Soporte completo de WebComponents y MediaDevices API
HTTPSProtocolo seguroSí en producciónRequerido para acceso a cámara web
JavaScriptES2017+Soporte nativo de Custom Elements
TypeScript4.0+NoDefiniciones de tipos incluidas

b) Credenciales y configuración de accesos

Requisitos de acceso:

  • Paquete NPM: Acceso al componente jaak-visage a 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/session

De 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/visage

O 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/node

b) 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/visage

b) 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)

PropiedadTipoRequeridoValor por DefectoDescripciónEjemplo
licensestringundefinedClave de licencia para autenticaciónlicense="your-key"
licenseEnvironment'dev' | 'qa' | 'sandbox' | 'prod'No'prod'Ambiente para validación de licencialicense-environment="prod"
appIdstringNo'jaak-visage-web'Identificador de aplicaciónapp-id="my-app"
traceIdstringNoundefinedID de traza para tracking distribuidotrace-id="abc123"

Propiedades de Configuración Base

PropiedadTipoRequeridoValor por DefectoDescripciónEjemplo
debugbooleanNofalseHabilita logs de depuracióndebug="true"
camera'front' | 'back' | 'auto'No'auto'Preferencia de cámaracamera="front"
detectionMode'full' | 'light' | 'auto'No'auto'Modo de deteccióndetection-mode="auto"
captureMode'auto' | 'manual'No'auto'Modo de capturacapture-mode="auto"
language'es' | 'en'No'es'Idioma de la interfazlanguage="en"
showHelpOnStartbooleanNofalseMuestra instrucciones al iniciarshow-help-on-start="true"
textsVisageTextsNo{}Textos personalizados para UIVer interfaz VisageTexts

Propiedades de Telemetría y OpenTelemetry

PropiedadTipoRequeridoValor por DefectoDescripción
enableTelemetrybooleanNotrueHabilita trazas distribuidas
telemetryCollectorUrlstringNo'https://collector.jaak.ai/v1/traces'URL del recolector OTLP para trazas
enableMetricsbooleanNotrueHabilita métricas
metricsCollectorUrlstringNo'https://collector.jaak.ai/v1/metrics'URL del recolector OTLP para métricas
metricsExportIntervalMillisnumberNo60000Intervalo de exportación de métricas

Propiedades de Contexto

PropiedadTipoRequeridoValor por DefectoDescripción
customerIdstringNoundefinedID del cliente para telemetría
tenantIdstringNoundefinedID del tenant para multi-tenancy
environmentstringNo'production'Ambiente de ejecución

2.4.3 Métodos públicos

MétodoParámetrosRetornoDescripción
start()voidPromise<void>Inicia el componente, precarga modelos y activa la cámara
stop()voidPromise<void>Detiene completamente el componente y libera recursos
restart()voidPromise<void>Reinicia el componente manteniendo la configuración
preload()voidPromise<void>Precarga los modelos de detección facial sin iniciar
startCamera()voidPromise<void>Inicia solo la cámara sin detección facial
stopCamera()voidPromise<void>Detiene solo la cámara
showHelp()voidPromise<void>Muestra las instrucciones de ayuda
switchCamera()deviceId?: stringPromise<void>Cambia entre cámaras disponibles
getCameraDevices()voidPromise<MediaDeviceInfo[]>Obtiene lista de cámaras disponibles
getAvailableCameras()voidPromise<MediaDeviceInfo[]>Obtiene cámaras sin pedir permisos
getCurrentCameraId()voidPromise<string>Obtiene el ID de la cámara actual
captureNow()voidPromise<void>Dispara captura manual inmediata (solo modo manual)

2.4.4 Eventos

EventoDatosDescripció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

EstadoDescripción
inactiveComponente cargado pero no iniciado
startingIniciando el componente
initializingInicializando detección facial
preloadingPrecargando modelos
preloadedModelos precargados
requesting-cameraSolicitando acceso a la cámara
camera-activeCámara activa
readyDetección facial lista
runningDetección facial activa
face-detectedRostro detectado correctamente
countdownCuenta regresiva para grabación
recordingGrabando video
capture-completeCaptura completada
processing-videoProcesando video capturado
video-readyVideo listo
face-lostRostro perdido durante grabación
recording-cancelledGrabación cancelada
switching-cameraCambiando cámara
errorError 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 realizadas
  • jaak.errors.total - Total de errores por tipo
  • jaak.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 activas
  • jaak.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

AmbienteURL de ValidaciónUso
devhttps://api.dev.jaak.aiDesarrollo local
qahttps://api.qa.jaak.aiPruebas de calidad
sandboxhttps://api.sandbox.jaak.aiPruebas de integración
prodhttps://services.api.jaak.aiProducció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 PruebaEntrada/ConfiguraciónResultado EsperadoCriterio de Éxito
Carga básica<jaak-visage></jaak-visage>Estado 'inactive' inicialComponente se inicializa sin errores
Detección facial exitosaUsuario con rostro visible y bien iluminadoMarco verde alrededor del rostroColor del marco cambia a verde
Posición facial correctaCara centrada con tamaño adecuadoInicio de countdown automáticoGrabación automática se inicia
Grabación automáticaDetección facial válida por 3 framesVideo grabado automáticamenteEvento videoCaptured emitido
Cambio de cámaraswitchCamera() con múltiples cámarasCambio exitoso entre cámarasStream 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érminoDefinición
WebComponentEstándar web que permite crear elementos HTML personalizados reutilizables
StencilCompilador para crear Web Components optimizados
Face DetectionTecnología que identifica y localiza rostros humanos en imágenes o video
MediaDevices APIAPI del navegador para acceder a cámaras y micrófonos
MediaPipePlataforma de Google para pipelines de percepción multimodal
Data URLFormato que incluye datos binarios codificados en Base64 dentro de una URL
PropsPropiedades que se pasan a un componente para configurar su comportamiento
Shadow DOMTecnología que encapsula el DOM y CSS de un Web Component
OpenTelemetryFramework de observabilidad para instrumentación y exportación de telemetría
OTLPOpenTelemetry Protocol - Protocolo estándar para transmisión de telemetría

Anexo B. Enlaces de referencia