Java – Detección de Rostros con OpenCV

Este pequeño ejemplo está basado en OpenCV 3.1, es sencillo y requiere, además de descargar la biblioteca, una webcam conectada a tu equipo.  Es necesario señalar que la detección de rostros o de cualquier objeto no es cien por ciento preciso, tampoco debes esperar que te permita diferenciar entre distintos tipos de rostros.   Lo que se logra con este ejercicio es mostrar sobre una imagen tomada de una webcam el area en la cual se ha detectado un rostro humano.  Es un ejemplo interesante que seguro disfrutarás.

OpenCV

OpenCV es una biblioteca fabulosa que te facilita el trabajo si estás interesado en crear aplicaciones que requieren de reconocimiento y rastreo de objetos o rostros sobre imágenes o video en tiempo real.  En Windows su instalación es sencilla y para ello debes dirigirte a la página de descarga del proyecto.Desde luego, necesitas Java y en esta ocasión usaremos Eclipse para editar y hacer funcionar el ejemplo.

descargados


Después de descargar y dar click en el archivo opencv-3.1.0.exe, una ventana de diálogo te preguntará donde deseas extraer OpenCV, elige la ruta que desees y recuérdala, más tarde las usaremos.

extraeropencv

Configurar Eclipse

Suponiendo que ya conoces y tienes instalado Eclipse, debes ir al menú principal y seleccionar la opción: Window > Preferences.

configuraeclipse01

A continuación vamos a crear una librería de usuario nueva.  Buscamos la opción Java>Build Path>User Libraries>New.

configuraeclipse02

Creamos una nueva librería de usuario y la llamamos opencv3.1 y damos enter, después seleccionamos la nueva librería y damos click en "Add External JARs...",

configuraeclipse03

En la carpeta de OpenCV busca el archivo opencv\build\java\opencv-310.jar y selecciónalo.

configuraeclipse04


Una vez seleccionado el archivo correcto, podrás ver que opciones nuevas aparecen.  Selecciona "Native library location".

configuraeclipse05

Da click en "Edit" y selecciona la carpeta "...opencv/build/java/x64".  Da click en OK y terminamos.

Crear el proyecto en Eclipse

Al crear un nuevo proyecto en eclipse seleccionaremos la librería recién creada.

crear proyecto

Puedes nombrar a tu proyecto como desees.

post01_eclipse02

Antes de dar click en Finish, agregaremos la librería que creamos, dando click en Next y en la pestaña "Libraries" damos click nuevamente en la opción "Add Library...".

post01_eclipse03

Elegimos la opción "User Library".

post01_eclipse04

Y seleccionamos el check box de "opencv3.1".

post01_eclipse05

Veremos nuestra nueva librería agregada en lista del nuevo proyecto.

post01_eclipse06

El resultado será como se muestra a continuación:

post01_eclipse07

Ahora el código

Debes crear una clase llamada Principal y usar el siguiente código como contenido.

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.JFrame;
import javax.swing.JPanel;
import org.opencv.core.Core;
import org.opencv.core.Mat;
import org.opencv.core.MatOfByte;
import org.opencv.core.MatOfRect;
import org.opencv.core.Point;
import org.opencv.core.Rect;
import org.opencv.core.Scalar;
import org.opencv.imgcodecs.Imgcodecs;
import org.opencv.imgproc.Imgproc;
import org.opencv.objdetect.CascadeClassifier;
import org.opencv.videoio.VideoCapture;

class PanelDeRostros extends JPanel {
	private static final long serialVersionUID = 1L;
	private BufferedImage imagen;

	public PanelDeRostros() {
		super();
	}

	/*
	 * Convierte y escribe una Matriz en un objeto BufferedImage
	 */
	public boolean convierteMatABufferedImage(Mat matriz) {
		MatOfByte mb = new MatOfByte();
		Imgcodecs.imencode("ima.jpg", matriz, mb);
		try {
			this.imagen = ImageIO.read(new ByteArrayInputStream(mb.toArray()));
		} catch (IOException e) {
			e.printStackTrace();
			return false; // error
		}
		return true; // éxito
	}

	public void paintComponent(Graphics g) {
		super.paintComponent(g);
		if (this.imagen == null)
			return;
		g.drawImage(this.imagen, 10, 10, this.imagen.getWidth(), this.imagen.getHeight(), null);
	}
}

class DetectorRostros {
	private CascadeClassifier clasificador;

	public DetectorRostros() {
		//Se lee el archivo Haar que le permite a OpenCV detectar rostros frontales en una imagen
		clasificador = new CascadeClassifier("C:/Users/decodigo/Documents/opencv/sources/data/haarcascades/haarcascade_frontalface_alt.xml");
		if (clasificador.empty()) {
			System.out.println("Error de lectura.");
			return;
		} else {
			System.out.println("Detector de rostros leido.");
		}
	}

	public Mat detecta(Mat frameDeEntrada) {
		Mat mRgba = new Mat();
		Mat mGrey = new Mat();
		MatOfRect rostros = new MatOfRect();
		frameDeEntrada.copyTo(mRgba);
		frameDeEntrada.copyTo(mGrey);
		Imgproc.cvtColor(mRgba, mGrey, Imgproc.COLOR_BGR2GRAY);
		Imgproc.equalizeHist(mGrey, mGrey);
		clasificador.detectMultiScale(mGrey, rostros);
		System.out.println(String.format("Detectando %s rostros", rostros.toArray().length));
		for (Rect rect : rostros.toArray()) {
			//Se dibuja un rectángulo donde se ha encontrado el rostro
			Imgproc.rectangle(mRgba, new Point(rect.x, rect.y), new Point(rect.x + rect.width, rect.y + rect.height), new Scalar(255, 0, 0));

		}
		return mRgba;
	}
}

public class Principal {

	public static void main(String arg[]) throws InterruptedException {
		// Leyendo librería nativa
		System.loadLibrary(Core.NATIVE_LIBRARY_NAME);

		// Se crea el JFrame
		JFrame frame = new JFrame("Detección de rostros");
		frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

		DetectorRostros detectorRostros = new DetectorRostros();
		PanelDeRostros panel = new PanelDeRostros();
		frame.setSize(400, 400);
		frame.setBackground(Color.BLUE);
		frame.add(panel, BorderLayout.CENTER);
		frame.setVisible(true);

		// Se crea una matriz que contendrá la imagen
		Mat imagenDeWebCam = new Mat();
		VideoCapture webCam = new VideoCapture(0);

		if (webCam.isOpened()) {
			Thread.sleep(500); // Se interrumpe el thread para permitir que la webcam se inicialice
			while (true) {
				webCam.read(imagenDeWebCam);
				if (!imagenDeWebCam.empty()) {
					Thread.sleep(200); // Permite que la lectura se complete
					frame.setSize(imagenDeWebCam.width() + 40, imagenDeWebCam.height() + 60);
					// Invocamos la rutina de opencv que detecta rostros sobre la imagen obtenida por la webcam
					imagenDeWebCam = detectorRostros.detecta(imagenDeWebCam);
					// Muestra la imagen
					panel.convierteMatABufferedImage(imagenDeWebCam);
					panel.repaint();
				} else {
					System.out.println("No se capturó nada");
					break;
				}
			}
		}
		webCam.release(); // Se libera el recurso de la webcam
	}
}

En este ejemplo hay varias cosas importantes que debes notar.

La primera de ellas es cuando leemos el archivo HAAR que contiene la información que usará OpenCV para detectar los rostros en la imagen.  Es quizá el único cambio que tendrás que hacer en tu código, buscando el mismo archivo en tu instalación de OpenCV.

clasificador = new CascadeClassifier("C:/Users/decodigo/Documents/opencv/sources/data/haarcascades/haarcascade_frontalface_alt.xml");

En estas líneas tomamos el control de la webcam:

VideoCapture webCam = new VideoCapture(0);

El argumento que pasamos es el índice con el que está registrada la webcam internamente. Si no te funciona a la primera prueba con 1.

Después, depositamos una sola imagen en el objeto imagenDeWebCam.

webCam.read(imagenDeWebCam);

El cual leemos constantemente en un ciclo while.

Lo interesante viene en las siguientes líneas de código:

// Invocamos la rutina de opencv que detecta rostros sobre la imagen obtenida por la webcam
imagenDeWebCam = detectorRostros.detecta(imagenDeWebCam);
// Muestra la imagen
panel.convierteMatABufferedImage(imagenDeWebCam);
panel.repaint();

En la función detecta ocurren varias cosas.

	public Mat detecta(Mat frameDeEntrada) {
		Mat mRgba = new Mat();
		Mat mGrey = new Mat();
		MatOfRect rostros = new MatOfRect();
		frameDeEntrada.copyTo(mRgba);
		frameDeEntrada.copyTo(mGrey);
		Imgproc.cvtColor(mRgba, mGrey, Imgproc.COLOR_BGR2GRAY);
		Imgproc.equalizeHist(mGrey, mGrey);
		clasificador.detectMultiScale(mGrey, rostros);
		System.out.println(String.format("Detectando %s rostros", rostros.toArray().length));
		for (Rect rect : rostros.toArray()) {
			//Se dibuja un rectángulo donde se ha encontrado el rostro
			Imgproc.rectangle(mRgba, new Point(rect.x, rect.y), new Point(rect.x + rect.width, rect.y + rect.height), new Scalar(255, 0, 0));

		}
		return mRgba;
	}
}

Se recibe el contenido de un solo frame de la webcam, se genera una copia del mismo en RGB y en escala de grises y se deposita en las variables mRgba y mGrey. La función detectMultiScale obtiene un arreglo de posibles rostros detectados (En una sola imagen puede haber más de uno) y con la función Imgproc.rectangle(..) se dibuja un rectángulo de color azul.

Para ver lo que hace el código sólo tienes ejecutar la clase Principal.

post01_correrprincipal

He usado una imagen cualquiera para mostrar el resultado en pantalla.

post01_capturas

También puedes notar que la salida de la consola muestra todos los resultados exitosos de detección.

Este método no es cien por ciento fiable, pero ahorra una gran cantidad de trabajo.  Si exploras la carpeta de los archivos HAAR notarás que hay diversos archivos para detectar ojos, nariz y otras cosas más que pueden ser de utilidad para tus proyectos.

Espero que disfrutes haciendo este ejemplo.

12 comentarios en «Java – Detección de Rostros con OpenCV»

  1. 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

    Responder
  2. 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)

    Responder
  3. 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.

    Responder

Deja un comentario

eighty + = 81