La versión anterior de esta página explicaba cómo instalar OpenCV 3.1 en Windows, configurarlo a mano en Eclipse y detectar caras con la cámara. Hoy no hace falta instalar nada: una dependencia de Maven trae OpenCV con sus bibliotecas nativas para Windows, Linux y macOS.
También te puede interesar
La dependencia
<dependency> <groupId>org.openpnp</groupId> <artifactId>opencv</artifactId> <version>4.9.0-0</version> </dependency>
Es un empaquetado de OpenCV 4.9 para Java que incluye dentro del jar las bibliotecas nativas de cada sistema y las carga solo, con una llamada. Es lo que sustituye a descargar OpenCV, descomprimirlo y apuntar Eclipse a la carpeta de las DLL, que era la mitad de la página anterior.
UnsatisfiedLinkError, es que no se han cargado: falta la llamada a OpenCV.loadLocally() o se está usando otro jar sin los nativos.Dos detectores, la misma foto
OpenCV ofrece dos formas de encontrar caras. Las cascadas Haar, las que usaba esta página y casi todos los tutoriales, y YuNet, un detector basado en redes neuronales. Las probamos las dos sobre la foto oficial de la tripulación del Apolo 11:
package ejemplo;
import nu.pattern.OpenCV;
import org.opencv.core.*;
import org.opencv.imgcodecs.Imgcodecs;
import org.opencv.imgproc.Imgproc;
import org.opencv.objdetect.CascadeClassifier;
import org.opencv.objdetect.FaceDetectorYN;
public class Caras {
public static void main(String[] args) {
OpenCV.loadLocally();
System.out.println("OpenCV " + Core.VERSION);
Mat imagen = Imgcodecs.imread("tripulacion.jpg");
if (imagen.empty()) throw new IllegalStateException("no se pudo leer tripulacion.jpg");
// --- YuNet: el detector basado en redes neuronales
FaceDetectorYN yunet = FaceDetectorYN.create("yunet.onnx", "", imagen.size(), 0.8f, 0.3f, 5000);
Mat caras = new Mat();
yunet.detect(imagen, caras);
System.out.println("YuNet: " + caras.rows() + " caras");
Mat conYunet = imagen.clone();
for (int i = 0; i < caras.rows(); i++) {
float[] c = new float[15];
caras.get(i, 0, c);
System.out.printf(" en (%d, %d), %dx%d | confianza %.2f%n",
(int) c[0], (int) c[1], (int) c[2], (int) c[3], c[14]);
Imgproc.rectangle(conYunet, new Rect((int) c[0], (int) c[1], (int) c[2], (int) c[3]),
new Scalar(255, 100, 30), 3);
for (int p = 0; p < 5; p++)
Imgproc.circle(conYunet, new Point(c[4 + 2 * p], c[5 + 2 * p]), 5, new Scalar(60, 200, 60), -1);
}
Imgcodecs.imwrite("resultado_yunet.jpg", conYunet);
// --- Cascada Haar: el método de los tutoriales antiguos
CascadeClassifier haar = new CascadeClassifier("haarcascade_frontalface_default.xml");
System.out.println("cascada cargada: " + !haar.empty());
Mat gris = new Mat();
Imgproc.cvtColor(imagen, gris, Imgproc.COLOR_BGR2GRAY);
MatOfRect rects = new MatOfRect();
haar.detectMultiScale(gris, rects, 1.1, 5, 0, new Size(60, 60));
System.out.println("Haar: " + rects.toArray().length + " caras");
for (Rect r : rects.toArray()) System.out.printf(" en (%d, %d), %dx%d%n", r.x, r.y, r.width, r.height);
}
}salidaOpenCV 4.9.0 YuNet: 3 caras en (247, 324), 105x132 | confianza 0.94 en (532, 159), 93x123 | confianza 0.93 en (823, 338), 96x127 | confianza 0.92 cascada cargada: true Haar: 4 caras en (523, 164), 114x114 en (806, 354), 106x106 en (248, 332), 121x121 en (683, 596), 101x101

Lo que hace falta descargar
| Archivo | Para qué | De dónde |
|---|---|---|
face_detection_yunet_2023mar.onnx | el modelo de YuNet, unos 230 KB, licencia MIT | opencv_zoo |
haarcascade_frontalface_default.xml | la cascada Haar | el repositorio de OpenCV |
En el ejemplo están en la carpeta desde la que se ejecuta el programa. La versión anterior cargaba la cascada desde C:/Users/decodigo/Documents/opencv/…, una ruta que solo existía en un ordenador.
Cómo leer el resultado de YuNet
detect devuelve una matriz con una fila por cara y 15 números por fila: los cuatro primeros son el recuadro —x, y, ancho y alto—; los diez siguientes, cinco puntos —los dos ojos, la nariz y las dos comisuras de la boca—, y el último, la confianza. Se leen con caras.get(i, 0, c) sobre un float[15]. El tamaño que se pasa a create tiene que ser el de la imagen; si procesas imágenes de tamaños distintos, se cambia antes con setInputSize.
Por qué falla la cascada
Una cascada Haar busca patrones de luces y sombras con forma de cara: una franja oscura para los ojos sobre otra más clara, un hueco claro para la nariz. Un bolsillo con su sombra los cumple lo bastante bien. Ajustando sus parámetros se cambia un error por otro: con menos exigencia aparecen más falsos positivos, y con más, se escapan caras. YuNet aprendió a reconocer caras a partir de miles de ejemplos y da una confianza con la que decidir.
Can't open file: 'no_existe.xml' in read mode— y crea un clasificador vacío, y el fallo llega después, al detectar. Por eso el ejemplo comprueba haar.empty() justo al cargarla.Con la cámara web
package ejemplo;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.SwingUtilities;
import nu.pattern.OpenCV;
import org.opencv.core.*;
import org.opencv.imgcodecs.Imgcodecs;
import org.opencv.imgproc.Imgproc;
import org.opencv.objdetect.FaceDetectorYN;
import org.opencv.videoio.VideoCapture;
public class Camara {
public static void main(String[] args) throws Exception {
OpenCV.loadLocally();
VideoCapture camara = new VideoCapture(0);
if (!camara.isOpened()) throw new IllegalStateException("no hay cámara disponible");
Mat cuadro = new Mat();
camara.read(cuadro);
FaceDetectorYN detector = FaceDetectorYN.create("yunet.onnx", "", cuadro.size(), 0.8f, 0.3f, 5000);
JLabel pantalla = new JLabel();
JFrame ventana = new JFrame("Detección de caras");
ventana.add(pantalla);
ventana.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
ventana.setSize(cuadro.width() + 20, cuadro.height() + 40);
ventana.setVisible(true);
Mat caras = new Mat();
while (ventana.isDisplayable() && camara.read(cuadro)) {
detector.detect(cuadro, caras);
for (int i = 0; i < caras.rows(); i++) {
float[] c = new float[15];
caras.get(i, 0, c);
Imgproc.rectangle(cuadro, new Rect((int) c[0], (int) c[1], (int) c[2], (int) c[3]),
new Scalar(255, 100, 30), 2);
}
BufferedImage imagen = aImagen(cuadro);
SwingUtilities.invokeLater(() -> pantalla.setIcon(new ImageIcon(imagen)));
}
camara.release();
}
static BufferedImage aImagen(Mat m) throws IOException {
MatOfByte jpg = new MatOfByte();
Imgcodecs.imencode(".jpg", m, jpg);
return ImageIO.read(new ByteArrayInputStream(jpg.toArray()));
}
}Es lo mismo, aplicado a cada fotograma, con una ventana de Swing para mostrar el resultado. La conversión de la imagen de OpenCV a una de Java se hace pasándola por JPEG con imencode: no es lo más rápido, pero son tres líneas, frente a la clase de cuarenta que usaba la versión anterior.
Este programa compila y se ha ejecutado, pero sin cámara: el contenedor de pruebas no tiene. En ese caso falla con su propio mensaje, que es lo que debe hacer:
salida[ WARN:0@0.006] global cap_v4l.cpp:997 open VIDEOIO(V4L2:/dev/video0): can't open camera by index Exception in thread "main" java.lang.IllegalStateException: no hay cámara disponible

Un aviso de Java 25
salidaWARNING: A restricted method in java.lang.System has been called WARNING: java.lang.System::load has been called by nu.pattern.OpenCV$LocalLoader in an unnamed module WARNING: Use --enable-native-access=ALL-UNNAMED to avoid a warning for callers in this module
Sale al cargar las bibliotecas nativas. No es un error y el programa funciona; para que no aparezca, se arranca con java --enable-native-access=ALL-UNNAMED ….
Todo el código se compiló y se ejecutó con Java 25 LTS en un contenedor limpio antes de publicar esta página; las salidas están copiadas de esa ejecución.
la camara se bloque despues de un rato
Hola me podrias compartir tu archivo, ya que me sale este error
utilice opencv 3.0.0
Error: no se ha encontrado o cargado la clase principal C:\Program
Bonita explicacion…te estaria agradecido si me pudieras apoyar con ese tema
Hola, el código usado es el mismo que el del primer bloque de este post. Saludos!
Muchas gracias por compartir, queria pedirte una pequeña ayuda, que puedo agregar para recortar el rostro y guardarlo.
Hola muy buen post. Quiero saber como puedo registrarme en el sitio.
Tienes el codigo para la deteccion de ojos, nariz y boca?
este ejemplo «detecta» rostros pero no los identifica !!
Buenas Tardes, yo ejecuto este programa y siempre me sale este error.
Error de lectura.
OpenCV Error: Assertion failed (!empty()) in cv::CascadeClassifier::detectMultiScale, file C:\builds\master_PackSlaveAddon-win64-vc12-static\opencv\modules\objdetect\src\cascadedetect.cpp, line 1639
Exception in thread «main» CvException [org.opencv.core.CvException: cv::Exception: C:\builds\master_PackSlaveAddon-win64-vc12-static\opencv\modules\objdetect\src\cascadedetect.cpp:1639: error: (-215) !empty() in function cv::CascadeClassifier::detectMultiScale
]
at org.opencv.objdetect.CascadeClassifier.detectMultiScale_1(Native Method)
at org.opencv.objdetect.CascadeClassifier.detectMultiScale(CascadeClassifier.java:159)
at Principal.java.DetectorRostros.detecta(Principal.java:78)
at Principal.java.Principal.main(Principal.java:118)
Hola amigo, tengo una duda, que tipo de entrenamiento realiza tu programa, entiendo que todo algoritmo debe realizar un entrenamiento antes para proceder a realizar la detección, exactamente en que punto se realiza el entrenamiento.
Excelente post me ha servido de mucho. Gracias por el aporte.
buenas noches, de antemano gracias una pregunta ¿como podria implementar machine learning a este proyecto?
Hola muchas gracias el codigo funciono muy bien