Mapeo facial con Dlib y Python

dlib localiza 68 puntos en cada cara: el contorno de la mandíbula, las cejas, la nariz, los ojos y la boca. Con ellos se puede medir si alguien tiene los ojos cerrados, hacia dónde mira o si está sonriendo. Antes de empezar, dos avisos: cuesta instalarlo, y el modelo de 68 puntos no se puede usar en un producto comercial.

Instalarlo

sudo apt install cmake g++        # en Debian o Ubuntu
pip install dlib opencv-python-headless

pip install dlib no descarga un paquete ya hecho: compila dlib desde el código fuente, y para eso necesita cmake y un compilador de C++. En nuestra prueba tardó varios minutos. En Windows hace falta Visual Studio con las herramientas de C++.

La versión anterior de esta página instalaba todo con sudo pip3 install. No uses sudo con pip: instala en el Python del sistema y puede romper programas que dependen de él. Usa un entorno virtual. Tampoco hace falta ya imutils: era para una sola conversión que se hace en una línea.

El modelo, y su licencia

Los puntos los calcula un modelo ya entrenado, shape_predictor_68_face_landmarks.dat, que se baja del repositorio de modelos de dlib. Pesa unos 100 MB.

Ese modelo no es para uso comercial. Está entrenado con el conjunto de datos iBUG 300-W, cuya licencia lo excluye, y su autor lo advierte en la propia página de descarga: si quieres usarlo en un producto comercial, tendrías que consultarlo con un abogado o con el Imperial College de Londres. Para un proyecto personal o de aprendizaje no hay problema. Si solo necesitas los ojos y la nariz, dlib tiene otro modelo de 5 puntos, entrenado con datos propios, que no arrastra esa limitación.

Los 68 puntos

import cv2, dlib

detector = dlib.get_frontal_face_detector()
predictor = dlib.shape_predictor("shape_predictor_68_face_landmarks.dat")

imagen = cv2.imread("tripulacion.jpg")
gris = cv2.cvtColor(imagen, cv2.COLOR_BGR2GRAY)

caras = detector(gris, 1)
print("caras:", len(caras))

for cara in caras:
    forma = predictor(gris, cara)
    puntos = [(forma.part(i).x, forma.part(i).y) for i in range(forma.num_parts)]
    print(f"  cara en ({cara.left()}, {cara.top()}): {len(puntos)} puntos")
    for (x, y) in puntos:
        cv2.circle(imagen, (x, y), 2, (60, 200, 60), -1)

cv2.imwrite("resultado_dlib.jpg", imagen)
salidacaras: 3
  cara en (259, 343): 68 puntos
  cara en (808, 355): 68 puntos
  cara en (534, 186): 68 puntos
Primer plano de Neil Armstrong sonriendo, con 68 puntos verdes sobre la mandíbula, las cejas, la nariz, los ojos y la boca
Los 68 puntos sobre la cara de Neil Armstrong. La foto es la oficial de la tripulación del Apolo 11 —Armstrong, Collins y Aldrin—, de la NASA, en dominio público.

Dos pasos: primero el detector de caras propio de dlib —que acertó las tres, sin el falso positivo de las cascadas Haar—, y luego el predictor, que coloca los puntos dentro de cada cara. El 1 de detector(gris, 1) amplía la imagen una vez antes de buscar, para encontrar caras más pequeñas. La lista de puntos se construye directamente con part(i), que es lo único que hacía imutils.

Qué punto es cada cosa

ZonaPuntos
Mandíbula0 a 16
Cejas17 a 26
Nariz27 a 35
Ojos36 a 47 (seis por ojo)
Boca48 a 67

Un ejemplo de uso: ¿tiene el ojo abierto?

ojo = puntos[36:42]
ancho_ojo = abs(ojo[3][0] - ojo[0][0])
alto_ojo = (abs(ojo[1][1] - ojo[5][1]) + abs(ojo[2][1] - ojo[4][1])) / 2
print(f"apertura del ojo: {alto_ojo / ancho_ojo:.2f}")
salidaapertura del ojo: 0.35

Con los seis puntos de un ojo se compara su altura con su anchura. Un ojo abierto da en torno a 0,3; si el valor cae cerca de cero durante varios fotogramas seguidos, la persona tiene los ojos cerrados. Es la base de los sistemas que avisan a un conductor cuando se está durmiendo.

Más información en inglés: Face mapping with dlib and Python.

Todo el código se ejecutó con Python 3.14 en un contenedor limpio antes de publicar esta página; las salidas están copiadas de esa ejecución.