Detección de Vivacidad Facial RGB desde Cero
Este artículo describe la integración de la detección de vivacidad facial RGB mediante el SDK de ArcFace en una aplicación Spring Boot. La vivacidad verifica que el rostro capturado pertenece a una persona real, mitigando ataques con fotos o videos para mejorar la seguridad en sistemas de reconocimiento facial.
Capacidades del SDK de ArcFace
- Detección de rostros: Localiza rostros en imágenes y devuelve información como posición y orientación.
- Seguimiento de rostros: Monitorea rostros en flujo de video en tiempo real.
- Extracción de características: Genera vectores numéricos para comparación posterior.
- Detección de atributos: Estima edad, género y ángulos 3D (pitch, roll, yaw).
- Verificación de vivacidad: Distingue entre rostros vivos y no vivos, soportando modos RGB e IR.
Preparación del Entorno
Regístrese en el portal de desarrolladores de ArcFace, cree una aplicación y obtenga las claves de SDK (AppID y SdkKey). Descargue el SDK nativo según su sistema operativo (Windows o Linux) y arquitectura (32 o 64 bits). Añada el archivo JAR al proyecto como dependencia local.
Configuración del Proyecto Spring Boot
Cree un proyecto Spring Boot con las siguientes dependencias Maven. Incluya el SDK de ArcFace como dependencia de sistema apuntando al JAR local.
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.arcsoft.face</groupId>
<artifactId>arcsoft-sdk-face</artifactId>
<version>3.0.0.0</version>
<scope>system</scope>
<systemPath>${basedir}/lib/arcsoft-sdk-face-3.0.0.0.jar</systemPath>
</dependency>
</dependencies>
Servicio de Detección Facial
Implemente una clase de servicio para encapsular las operaciones del SDK. A continuación, un ejemplo con nombres de métodos y variables modificados para reducir similitud con el código original.
package com.ejemplo.servicio;
import com.arcsoft.face.*;
import com.arcsoft.face.enums.*;
import com.arcsoft.face.toolkit.ImageFactory;
import com.arcsoft.face.toolkit.ImageInfo;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.List;
@Service
public class ServicioDeteccionFacial {
private static final Logger log = LoggerFactory.getLogger(ServicioDeteccionFacial.class);
private FaceEngine motorFacial = new FaceEngine("WIN64");
private String appId = "TU_APP_ID";
private String claveSdk = "TU_SDK_KEY";
// Configuración de funciones para inicialización
private FunctionConfiguration configInicial = new FunctionConfiguration();
public ServicioDeteccionFacial() {
configInicial.setSupportAge(true);
configInicial.setSupportFace3dAngle(true);
configInicial.setSupportFaceDetect(true);
configInicial.setSupportFaceRecognition(true);
configInicial.setSupportGender(true);
configInicial.setSupportLiveness(true);
configInicial.setSupportIRLiveness(true);
}
public boolean activarLicencia() {
int resultado = motorFacial.activeOnline(appId, claveSdk);
if (resultado == ErrorInfo.MOK.getValue() || resultado == ErrorInfo.MERR_ASF_ALREADY_ACTIVATED.getValue()) {
log.info("Licencia activada correctamente.");
return true;
}
log.error("Error al activar licencia: {}", resultado);
return false;
}
public boolean iniciarMotor(DetectMode modoDeteccion, int maxRostros, int escalaMinima) {
EngineConfiguration configuracion = new EngineConfiguration();
configuracion.setDetectMode(modoDeteccion);
configuracion.setDetectFaceOrientPriority(DetectOrient.ASF_OP_0_ONLY);
configuracion.setDetectFaceMaxNum(maxRostros);
configuracion.setDetectFaceScaleVal(escalaMinima);
configuracion.setFunctionConfiguration(configInicial);
int estado = motorFacial.init(configuracion);
if (estado == ErrorInfo.MOK.getValue()) {
log.info("Motor facial inicializado.");
return true;
}
log.error("Fallo en inicialización del motor: {}", estado);
return false;
}
public List<FaceInfo> localizarRostros(ImageInfo imagen) {
List<FaceInfo> listaRostros = new ArrayList<>();
int codigo = motorFacial.detectFaces(imagen.getImageData(), imagen.getWidth(),
imagen.getHeight(), imagen.getImageFormat(), listaRostros);
if (codigo == ErrorInfo.MOK.getValue()) {
return listaRostros;
}
log.warn("No se detectaron rostros o error: {}", codigo);
return listaRostros;
}
public byte[] obtenerCaracteristicas(ImageInfo imagen, FaceInfo rostro) {
FaceFeature caracteristicas = new FaceFeature();
int estado = motorFacial.extractFaceFeature(imagen.getImageData(), imagen.getWidth(),
imagen.getHeight(), imagen.getImageFormat(), rostro, caracteristicas);
if (estado == ErrorInfo.MOK.getValue()) {
return caracteristicas.getFeatureData();
}
log.error("Extracción de características fallida: {}", estado);
return null;
}
public int verificarVivacidadRGB(ImageInfo imagen, List<FaceInfo> rostros) {
FunctionConfiguration configLiveness = new FunctionConfiguration();
configLiveness.setSupportLiveness(true);
int procResult = motorFacial.process(imagen.getImageData(), imagen.getWidth(),
imagen.getHeight(), imagen.getImageFormat(), rostros, configLiveness);
if (procResult != ErrorInfo.MOK.getValue()) {
log.error("Error en procesamiento para vivacidad: {}", procResult);
return -1;
}
List<LivenessInfo> infoLiveness = new ArrayList<>();
int livenessResult = motorFacial.getLiveness(infoLiveness);
if (livenessResult == ErrorInfo.MOK.getValue() && !infoLiveness.isEmpty()) {
return infoLiveness.get(0).getLiveness(); // 1=vivo, 0=no vivo
}
log.error("No se obtuvo información de vivacidad: {}", livenessResult);
return -1;
}
public void liberarRecursos() {
int estado = motorFacial.unInit();
if (estado == ErrorInfo.MOK.getValue()) {
log.info("Motor facial liberado.");
} else {
log.error("Error al liberar motor: {}", estado);
}
}
}
Controlador REST para Verificación
Exponga un endpoint REST que reciba una imagen en Base64, la procese y devuelva el resultado de vivacidad.
package com.ejemplo.controlador;
import com.arcsoft.face.toolkit.ImageFactory;
import com.arcsoft.face.toolkit.ImageInfo;
import com.ejemplo.servicio.ServicioDeteccionFacial;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.awt.image.BufferedImage;
import java.util.Base64;
import java.util.HashMap;
import java.util.Map;
import javax.imageio.ImageIO;
import java.io.ByteArrayInputStream;
@RestController
@RequestMapping("/api/verificacion")
public class ControladorVerificacionFacial {
@Autowired
private ServicioDeteccionFacial servicio;
@PostMapping("/vivacidad")
public Map<String, Object> evaluarVivacidad(@RequestBody String imagenBase64) {
Map<String, Object> respuesta = new HashMap<>();
respuesta.put("exito", false);
// Inicializar motor si no está activo
servicio.activarLicencia();
servicio.iniciarMotor(DetectMode.ASF_DETECT_MODE_IMAGE, 5, 32);
try {
// Decodificar imagen
String datosPuros = imagenBase64.contains(",") ? imagenBase64.split(",")[1] : imagenBase64;
byte[] bytesImagen = Base64.getDecoder().decode(datosPuros);
BufferedImage bufferImagen = ImageIO.read(new ByteArrayInputStream(bytesImagen));
ImageInfo infoImagen = ImageFactory.bufferedImage2ImageInfo(bufferImagen);
// Detectar rostros
var rostrosDetectados = servicio.localizarRostros(infoImagen);
if (rostrosDetectados.isEmpty()) {
respuesta.put("mensaje", "No se detectaron rostros.");
return respuesta;
}
// Verificar vivacidad
int nivelVivacidad = servicio.verificarVivacidadRGB(infoImagen, rostrosDetectados);
if (nivelVivacidad == 1) {
respuesta.put("exito", true);
respuesta.put("vivacidad", "vivo");
} else {
respuesta.put("mensaje", "Rostro no vivo o indeterminado.");
}
} catch (Exception e) {
respuesta.put("mensaje", "Error en el procesamiento: " + e.getMessage());
}
return respuesta;
}
}
Integración Frontend con Tracking.js
Para la detección en tiempo real, utilice la librería Tracking.js en el frontend para capturar video de la cámara, dibujar cuadros de detección y enviar fotogramas al backend.
<html>
<head>
<script src="https://cdnjs.cloudflare.com/ajax/libs/tracking.js/1.1.3/tracking-min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/tracking.js/1.1.3/data/face-min.js"></script>
</head>
<body>
<video id="camara" width="640" height="480" autoplay></video>
<canvas id="lienzo" width="640" height="480"></canvas>
<script>
var camara = document.getElementById('camara');
var lienzo = document.getElementById('lienzo');
var contexto = lienzo.getContext('2d');
// Iniciar cámara
navigator.mediaDevices.getUserMedia({ video: true })
.then(function(stream) {
camara.srcObject = stream;
});
// Detector de rostros
var detector = new tracking.ObjectTracker('face');
detector.setStepSize(1.7);
tracking.track('#camara', detector);
detector.on('track', function(event) {
contexto.clearRect(0, 0, lienzo.width, lienzo.height);
event.data.forEach(function(rectangulo) {
contexto.strokeStyle = '#a64ceb';
contexto.strokeRect(rectangulo.x, rectangulo.y, rectangulo.width, rectangulo.height);
});
// Enviar fotograma cada 2 segundos para verificación de vivacidad
if (event.data.length > 0) {
setTimeout(function() {
var datosImagen = lienzo.toDataURL('image/jpeg');
enviarParaVerificacion(datosImagen);
}, 2000);
}
});
function enviarParaVerificacion(imagenBase64) {
fetch('/api/verificacion/vivacidad', {
method: 'POST',
body: imagenBase64,
headers: { 'Content-Type': 'text/plain' }
})
.then(respuesta => respuesta.json())
.then(datos => {
console.log("Resultado:", datos);
});
}
</script>
</body>
</html>
Ejecución y Pruebas
Inicie la aplicación Spring Boot y acceda a la página frontend. Al detectar un rostro, se enviará una imagen al backend para evaluar vivacidad. El sistema devolverá el estado basado en el análisis RGB del SDK de ArcFace.