Artículo
· 8 mayo, 2023 Lectura de 6 min

Reconocimiento facial con IRIS y Python Embebido

¡Bienvenidos, estimados miembros de la comunidad!

En este artículo vamos a demostrar el gran potencial que IRIS/HealthConnect pone a disposición de todos sus usuarios con el uso de Python Embebido y lo vamos a hacer mediante el desarrollo de una pequeña producción que nos va a permitir reconocer e identificar las caras presentes en un archivo JPG a partir de unas imágenes que usaremos como referencia.

Configurando el proyecto:

¡Empecemos! Hemos publicado en OpenExchange la aplicación entera para que sólo necesitéis descargarla y desplegarla en Docker como podréis leer en el README asociado, tenéis el acceso a la misma al final del artículo. Como veréis su nombre es Nomenclator, haciendo referencia a los esclavos que en la Antigua Roma, durante las fiestas y eventos sociales, permanecían junto a sus amos y se encargaban de recordarles los nombres de todos aquellas personas con las que se encontraban.

Como no soy ningún experto en Python he basado el desarrollo en este artículo de la web, no es necesario que reinventemos la rueda.

Echemos un vistazo a los fuentes del proyecto, empezando por el archivo docker-compose.yml:

version: "2.2"
services:
  # iris
  iris:
    init: true
    container_name: iris
    build:
      context: .
      dockerfile: iris/Dockerfile
    ports:
      - 52773:52773
      - 51773:51773
    command: --check-caps false
    volumes:
    - ./shared:/shared

Como podéis ver es extremadamente simple, únicamente necesitamos una instancia de IRIS corriendo. Expliquemos por encima los archivos más relevantes que forman parte del proyecto:

  • Dockerfile: archivo encargado de la configuración del proyecto, instalará las librerías de Python necesarias en el contenedor y desplegará el código y la producción.
  • mobilenet_graph.pb: archivo con el modelo pre-construido para el reconocimiento de caras existentes en archivos JPG.
  • facenet_keras_weights.h5: modelo pre-construido para la identificación de las caras respecto a las caras presentes en el repositorio.
  • \shared: carpetas definidas como volumes que serán accesibles desde Visual Studio Code y desde el contenedor del Docker.
    • \JPG: carpeta donde incluiremos la imagen que queremos identificar.
    • \knowns: carpeta que funcionará como repositorio de nuestras caras identificadas, nuestra imagen se comparará con las que tenemos en esta carpeta, si hay coincidencia mostraremos un mensaje indicando el fichero con el que coincide. Para nuestro ejemplo hemos incluido unas cuantas fotos del actor John Travolta.
    • \results: carpeta destinada a almacenar el resultado de las validaciones, se incluirá una imagen con las caras encontradas dentro de un recuadro, dicho recuadro será verde si la cara coincide con alguna de las presentes en la carpeta \shared\knowns y rojo si no coincide.
    • \test: carpeta con 2 imágenes que usaremos para nuestras pruebas, una de John Travolta (travi.jpg) y otra de Nicolas Cage (nicolas.jpg).
    • \unknowns: carpeta a la que moveremos la imagen recibida en \shared\JPG para su tratamiento desde el método de Python.
  • Installer.cls: clase de ObjectScript que nos creará el namespace NOMENCLATOR y la base de datos asociada en nuestra instancia de IRIS. 
  • Nomenclator.xml: archivo xml que contiene el código de la producción y del business service.
  • requeriments.txt: archivo con las librerías de Python necesarias para hacer funcionar el proyecto.

Aviso previo:

El propósito de este proyecto es meramente formativo y no se pretende que sea utilizado directamente en ningún entorno productivo.

Código Python:

Expliquemos brevemente el funcionamiento de nuestro código Python. Empecemos con el código que se ejecuta cuando invocamos el método:

isMatch = ''
	# Embeddings reference
	known_embeddings = []
	for name in os.listdir(DIR_KNOWNS):
		if name.endswith('.jpg'):
			image = load_image(DIR_KNOWNS,name)
			bboxes = detect_faces(image)					
			face = extract_faces(image,bboxes)
			if len(face) > 0 :
				known_embeddings.append(compute_embedding(facenet,face[0]))				
					
	# Searching matches for knowns faces		
	for name in os.listdir(DIR_UNKNOWNS):
		if name.endswith('.jpg'):
			image = load_image(DIR_UNKNOWNS,name)
			bboxes = detect_faces(image)
			faces = extract_faces(image,bboxes)
      
			# Computing embedding for each face
			img_with_boxes = image.copy()
			for face, box in zip(faces,bboxes):
				emb = compute_embedding(facenet,face)

				puntuacion, reconocimiento = compare_faces(known_embeddings,emb)

				if any(reconocimiento):							
					isMatch = isMatch + ' ' + name + ' match!'
					img_with_boxes = draw_box(img_with_boxes,box,(0,255,0))
				else:
					img_with_boxes = draw_box(img_with_boxes,box,(255,0,0))
            
			cv2.imwrite(f'{DIR_RESULTS}/{name}',cv2.cvtColor(img_with_boxes,cv2.COLOR_RGB2BGR))
	
	return isMatch

Muy bien, empecemos, tenemos nuestra variable isMatch en la que cargaremos la respuesta de nuestro método a la imagen introducida. A continuación tenemos un bucle for sobre todos los archivos que tenemos en el directorio de imágenes conocidas (lo que hemos llamado nuestro repositorio) en el que primeramente detectaremos todas las caras para las imágenes conocidas (detect_faces) usando el modelo pre-construido presente en el archivo mobilenet_graph.pb, las extraeremos del fichero en el formato que necesitemos (extract_faces) y extraeremos sus embeddings (compute_embedding) mediante el modelo definido por keras presente en el archivo facenet_keras_weights.h5, el embedding no será más que un vector de valores propios de cada cara encontrada en los archivos de imágenes conocidas.

Una vez que hemos obtenido los embeddings para todas las caras conocidas que tenemos repetiremos el proceso con las imágenes desconocidas que queremos validar, detectaremos primeramente las caras presentes en las imágenes, las extraeremos y finalmente calcularemos su embedding individualmente. Con el embedding calculado podremos compararlo con los embeddings que obtuvimos con las caras conocidas.

Echemos un vistazo a la función de Python que realiza la comparación:

def compare_faces(embs_ref, emb_desc, umbral=1.1):
		distancias = []
		for emb_ref in embs_ref:
			distancias.append(np.linalg.norm(emb_ref-emb_desc))
		distancias = np.array(distancias)
		return distancias, list(distancias<=umbral)

Aquí vemos como recorremos todos los embeddings conocidos que tenemos y lo comparamos con el perteneciente a la imagen desconocida que tenemos, si la "distancia" de ambos embeddings es menor de 1.1 consideraremos que son la misma persona, en caso contrario no tendremos coincidencia. El valor 1.1 es algo que podréis afinar por vuestra cuenta subiéndolo o bajándolo hasta adecuarse a vuestras necesidades.

Una vez finalizado el proceso de comparación crearemos un fichero que dejaremos en la carpeta \shared\results con la imagen original y un marco sobre la cara de color verde si hay coincidencia o rojo si no lo hay.

Demostración:

Veamos un ejemplo de como funcionaría todo junto.

Primeramente copiemos las imágenes que tenemos en nuestra carpeta \shared\test a \shared\JPG

Veamos que ha hecho nuestra producción en IRIS:

¡Perfecto! Hemos capturado las dos imágenes desde nuestro directorio /shared/JPG, las hemos procesado y hemos obtenido una coincidencia para el fichero travi.jpg y una divergencia para el fichero nicolas.jpg. Veamos los ficheros en nuestra carpeta /shared/results

Aquí tenemos al bueno de Nicolas triste por no haber obtenido una coincidencia con nuestras caras conocidas. Veamos la otra imagen.

¡Genial! John está más que contento por haber sido reconocido por nuestra producción de IRIS.

Ni que decir que esto es un simple ejemplo de las potencialidades de IRIS con Python Embebido. Podríais mejorar este ejemplo almacenando los embeddings de cada imagen conocida en la base de datos de IRIS y nos ahorraríamos el paso de calcularlo cada vez que tengamos una nueva imagen a probar, ya que sólo tendríamos que cargarlas desde la base de datos.

Pues esto sería todo. Espero que os pueda resultar de utilidad y si tenéis alguna pregunta o sugerencia no dudéis en escribir un comentario.

P.D.: un gallifante para el que haya descubierto la referencia a la película Cara a cara.

Comentarios (0)1
Inicie sesión o regístrese para continuar