Blog
Detección de Sentimientos y Emociones, usando Marcas Faciales
- Publicado por: Rafael Fernandez
- Categoría: DLib Machine Learning Procesamiento de Imagenes

-Aquí veremos como se puede detectar los sentimientos y emociones mediante un código en Python con tan solo una foto.
-Además, te dejamos algunos resultados de ejemplo, el código y los requisitos para correrlo.
Vamos a utilizar las marcas faciales y un algoritmo de machine learning (aprendizaje automático) para ver si podemos predecir las emociones y sentimientos de una persona a partir de su imagen.
Se recomienda conocimientos previos sobre los algoritmos de marcas faciales, machine learning y procesamiento de imágenes, te aconsejamos visitar:
-Detección de Marcas Faciales I
-Curso de Machine Learning (apartado 3)
-Curso de Procesamiento de Imágenes con OpenCV
Requisitos para correr el algoritmo
- cv2 (librería openCV)
- dlib (Un juego de herramientas para hacer aplicaciones de learning machine y análisis de datos)
- numpy
- archivo : shape_predictor_68_face_landmarks.dat (lo puedes descargar desde aquí shape_predictor_68_face_landmarks)
- carpeta de imagenes de Sentimientos y Emociones para entrenar al algoritmo de machine learning (lo puedes descargar desde aquí dataset )
- Imágenes de test
- scikit learn
Código Python para Detección de Sentimientos y Emociones:
import cv2, glob, random, math, numpy as np, dlib from sklearn.svm import SVC emotions = ["miedo", "feliz", "neutral", "tristeza"] #lista de sentimientos clahe = cv2.createCLAHE(clipLimit=2.0, tileGridSize=(8,8)) detector = dlib.get_frontal_face_detector() predictor = dlib.shape_predictor("shape_predictor_68_face_landmarks.dat") #archivo de marcas faciales clf = SVC(kernel='linear', probability=True, tol=1e-3)#, verbose = True) #Establece el clasificador como un vector de apoyo de máquinas polinomial kernel def get_files(emotion): files = glob.glob("dataset\\%s\\*" %emotion) random.shuffle(files) training = files[:int(len(files))] prediction=['dataset\\2.png'] #aqui se añade la imagen que quieres procesar return training, prediction def get_landmarks(image): detections = detector(image, 1) for k,d in enumerate(detections): #Para todas las cara detectadas de forma individual shape = predictor(image, d) xlist = [] ylist = [] for i in range(1,68): #Guarda coordenadas X e Y en dos listas xlist.append(float(shape.part(i).x)) ylist.append(float(shape.part(i).y)) xmean = np.mean(xlist) # Obtiene la media de ambos ejes para determinar el centro de gravedad ymean = np.mean(ylist) xcentral = [(x-xmean) for x in xlist] #calcula distancia entre cada punto y el punto central en ambos ejes ycentral = [(y-ymean) for y in ylist] if xlist[26] == xlist[29]: # Si la coordenada x del conjunto son las mismas, el ángulo es 0, evitamos el error 'divide by 0' en la función anglenose = 0 else: anglenose = int(math.atan((ylist[26]-ylist[29])/(xlist[26]-xlist[29]))*180/math.pi) if anglenose < 0: anglenose += 90 else: anglenose -= 90 landmarks_vectorised = [] for x, y, w, z in zip(xcentral, ycentral, xlist, ylist): landmarks_vectorised.append(x) landmarks_vectorised.append(y) meannp = np.asarray((ymean,xmean)) coornp = np.asarray((z,w)) dist = np.linalg.norm(coornp-meannp) anglerelative = (math.atan((z-ymean)/(w-xmean))*180/math.pi) - anglenose landmarks_vectorised.append(dist) landmarks_vectorised.append(anglerelative) if len(detections) < 1: landmarks_vectorised = "error" return landmarks_vectorised def make_sets(): training_data = [] training_labels = [] prediction_data = [] prediction_labels = [] training = [] prediction = [] for emotion in emotions: training, prediction = get_files(emotion) #Append data to training and prediction list, and generate labels 0-7 for item in training: image = cv2.imread(item) #open image gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) #convertimos a escala de grises clahe_image = clahe.apply(gray) landmarks_vectorised = get_landmarks(clahe_image) if landmarks_vectorised == "error": pass else: training_data.append(landmarks_vectorised) # vector de imágenes a la lista de datos de entrenamiento training_labels.append(emotions.index(emotion)) for item in prediction: image = cv2.imread(item) gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) clahe_image = clahe.apply(gray) landmarks_vectorised = get_landmarks(clahe_image) if landmarks_vectorised == "error": pass else: prediction_data.append(landmarks_vectorised) prediction_labels.append(emotions.index(emotion)) return training_data, training_labels, prediction_data, prediction_labels, probam1 = np.zeros((4,10)) probam2 = np.zeros((1,4)) accur_lin = [] for i in range(0,10): print("Making sets %s" %i) #hace un muestreo aleatorio 80/20% training_data, training_labels, prediction_data, prediction_labels = make_sets() npar_train = np.array(training_data) #gira el conjunto de entrenamiento en una matriz numpy para el clasificador npar_trainlabs = np.array(training_labels) print("training SVM linear %s" %i) #entrenamiento SVM clf.fit(npar_train, training_labels) print("getting accuracies %s" %i) #Utilice la función score () para obtener mayor precisión npar_pred = np.array(prediction_data) pred_lin = clf.score(npar_pred, prediction_labels) print ("linear: ", pred_lin) accur_lin.append(pred_lin) #guarda la precision en una lista proba=clf.predict_proba(prediction_data) print ("proba: ", proba) probam1[:,i]=proba[1,:] probam2=proba[1,:]+probam2 #probam(:,i)=probam+proba proba=probam2/10 p1=round(proba[0,0],2) p2=round(proba[0,1],2) p3=round(proba[0,2],2) p4=round(proba[0,3],2) print("Mean value lin svm: %.3f" %np.mean(accur_lin)) #hacemos 10 ejecuciones para aumentar precision frame=cv2.imread('dataset\\2.png') #aqui se añade la imagen que quieres procesar pero aqui solo se carga para el resultado final #ploteamos el resultado cv2.putText(frame, "Miedo: {}".format(p1), (10, 30), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0, 0, 255), 2) cv2.putText(frame, "Feliz: {:.2f}".format(p2), (10, 60), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0, 0, 255), 2) cv2.putText(frame, "Neutral: {}".format(p3), (10, 90), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0, 0, 255), 2) cv2.putText(frame, "Triste: {:.2f}".format(p4), (10, 120), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0, 0, 255), 2) # mostramos la imagen cv2.imshow("Frame", frame) cv2.imwrite('resultado.png',frame) cv2.waitKey(0) cv2.destroyAllWindows()
Resultados del código de Detección de Sentimientos
Aquí mostramos algunos de los resultados para algunas caras conocidas y no tan conocidas que hemos seleccionado a modo de test, ustedes mismos podéis evaluar la precisión del código:
En la predicción de sentimientos hay un for que recorre el algoritmo de machine learning 10 veces para mejorar los resultados
Puedes añadir mas sentimientos como enfado, asco, sorpresa… te animamos a modificar el código y mejorarlo
No olvides tener en la carpeta dataset las carpetas de imágenes de los sentimientos así como la imagen en la que quieres predecir los sentimientos.
No olvides cambiar en la linea del código 15 y 126 y añadir la imagen
➡ Aprende mucho mas de Machine Learning con nuestro curso:
Hola cuando corro el for que esta en la linea 98, me genera el siguiente error: “ValueError: Expected 2D array, got 1D array instead:
array=[].
Reshape your data either using array.reshape(-1, 1) if your data has a single feature or array.reshape(1, -1) if it contains a single sample.”
Hola Rafael, gracias por escribir. Lo que tienes que hacer es debugear exactamente esa parte del for y verás el error de forma clara. A priori yo diria que espera un vector 2D y esta llegando un vector 1D. Saludos!
Hola
Está genial, solo una duda, cuando lo corro, toma 18 segundos por cada iteración, cómo se podría mejorar este tiempo de ejecución por favor?
Hola Alexis,
Puedes probar varias cosas:
1) Hacer el machine learning fuera del codigo y solo aplicar el codigo de prediccion
2) Usar otro algoritmo de machine learning mas rápido
3) Bajar el numero de veces que aplica el machine learning, ahora mismo esta en 10 para que los resultados sean mejores
Hola!, me genera el siguiente error
Making sets 0
Traceback (most recent call last):
File “C:\Users\JMB\Downloads\aprender-python-deteccion-sentimientos\proyecto IA.py”, line 91, in
training_data, training_labels, prediction_data, prediction_labels = make_sets()
File “C:\Users\JMB\Downloads\aprender-python-deteccion-sentimientos\proyecto IA.py”, line 77, in make_sets
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
cv2.error: OpenCV(4.1.0) C:\projects\opencv-python\opencv\modules\imgproc\src\color.cpp:182: error: (-215:Assertion failed) !_src.empty() in function ‘cv::cvtColor’
Hola Jonathan, hace un par de dias ejecute este código y funciona correctamente. No veo claro cual puede ser el error que tienes así que lo mejor es que depures el código paso a paso para que veas donde falla, para depurar te dejo este post https://unipython.com/funciones-depurar-debbugin-spyder-anaconda/
Hola Jonathan. Pudiste solucionar, este error?
Buenas, como estas?… Me parecio muy interesante el programa , lo pude ejecutar y me esta entregando los resultados con las imagenes . El problema que estoy teniendo es que los resultados no coinciden con la caracteristica de la imagen, estuve probando con la imagen que en su ejemplo da los siguientes valores: (Miedo : 0,26 feliz 0.50 neutral 0,19 y triste 0.04) … y obtuve resultados muy diferentes los cuales fueron ( Miedo 0.63 Feliz 0.05 Neutral 0.3 Triste 0.04 ) … Queria saber si esto se puede deber a que no tenga la base de datos de las fotos de emociones completas, o alguna actualizacion de alguna libreria q este haciendo trabajar mal alguna funcion. Desde ya muchas gracias..
Hola Franco, revisa bien tu código ya que a mi me funciona correctamente. Los valores no te saldrán iguales ya que el algoritmo de machine learning varía cada vez que se ejecuta, de todas maneras te animo a que revises a probar con una imagen de la base datos. Saludos!
Muchas gracias por la pronta respuesta Rafa… Hoy continuei testeando el programa, y realice cambio en el tamaño de la imagen y a eso se debia el inconveniente, una imagen de un buen tamaño hace que la prediccion sea correcta, hice un zoom antes de capturar la pantalla, y provee usar el mismo programa para 2 imagenes de diferente tamaño , la chica me dio los resultados de Miedo 0,32 feliz 0,11 Neutral 0,41 , y triste 0,16 … Y con la que le aplique zoom obtuve Miedo 0,08 Feliz 0,89 Neutral 0,01 y triste 0.02 , esto me mostro que alli radicaba mi problema … Esta excelente la pagina, Muchas gracias … Saludos….
Cordial saludo, compañero. Queria preguntar por un error que me sale de la importacion de las librerias, me dice que el DLL no se pudo cargar y que %1 no es una aplicacion Win32 válida. No sé, realmente, cómo se debe instalar dlib y el resto de librerías, pues numpy sí la tengo instalada y funcionando correctamente y todo. Gracias de antemano, saludos.
Hola Martin, tienes que tener Dlib para ejecutar este proyecto. Si no tienes Dlib instalado prueba a revisar la nueva forma de instalarlo en este post https://bitly.com/2wpK9C4 saludos
Me da error de sintaxis “unexpected indent”
Hola Luis, tienes instaladas las librerías de opencv y dlib? saludos
AYUDA ME SALE EL SIGUIENTE ERROR
if anglenose&amp;lt;0:
^
SyntaxError: invalid syntax
Hola WALTER, error corregido, gracias por comentar. Saludos!
hola me genera este error, no se si se está o no cargando la imagen
File “Sentimientos.py”, line 86, in
training_data, training_labels, prediction_data, prediction_labels = make_sets()
File “Sentimientos.py”, line 72, in make_sets
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
cv2.error: OpenCV(4.1.2-pre) /Users/MrRabbit/opencv/modules/imgproc/src/color.cpp:182: error: (-215:Assertion failed) !_src.empty() in function ‘cvtColor’
Ya encontré el error.
Se trataba de la ruta de la imagen de prueba.
como estoy trabajando en mac OSX no se usa el ” \ ” para los directorios, sino “/”. y con eso se soluciona el problema
Buenas a todos, Gracias por el aporte, en realidad ya implemente mi programa con otras estrategias con machine learning aun así te agradezco el dataset en especial.
Una observación que tengo es que sugeriste que se puede agregar mas emociones, pero esto no es así, el rostro humano puede hacer mas de 7000 gestos diferentes pero según algún que otro articulo de estudios sobre las expresiones humanas que e estado leyendo, casi todos afirman que hay cuatro expresiones irreducibles y la expresión neutral, de las cuales derivan todas las demás, entonces para las demás expresiones que quisiéramos detectar, una estrategia que me propuse es estudiar las distintas combinaciones den porciones de las cuatro emociones básicas data como entrada una imagen con una expresión que la red neuronal no conozca y ver si los resultados coinciden con imágenes diferentes de la misma expresión.
Cómo se le puede hacer para ingresar una nueva emocion a la lista? c:
Hola Karla, para añadir una nueva emoción tienes que tener las imagenes que expresen esa emoción y añadirla al dataset, luego seguir la indicaciones del código para incluir tu nueva emoción en el modelo de machine learning.
Hola!, felicitaciones por el código me ha ayudado de mucho para un proyecto de la universidad, por hacer una consulta, en que se basa para poder determinar estadisticamente el porcentaje de emoción que posee la persona de la foto?
Gracias Sebastián, el porcentaje se basa en la calificación que da a la imagen el algoritmo de machine learning usado en este post.
¡Hola! Primero de todo felicidades por el trabajo, este código me está sirviendo de gran ayuda para un trabajo de la universidad. Me he atrevido a escribir en el blog por si podías explicarme una parte que no llego a entender del todo, ya que no estoy muy familiarizado con machine learning. Basicamente y siendo muy breve no entiendo el por qué de la creación de los arrays:
probam1 = np.zeros((4,10))
probam2 = np.zeros((1,4))
Es decir, ¿por qué tendrán esa estructura de 4 filas-10 columnas y 1 fila-10 columnas respectivamente? ¿Para que utilizaremos luego ese probam1 y probam2?
Seguidamente, no entiendo:
proba=clf.predict_proba(prediction_data)
print (“proba: “, proba)
print(“”)
probam1[:,i]=proba[1,:] #Primer espacio filas (: todas las filas) // segundo espacio columnas (: todas las columnas)
probam2=proba[1,:]+probam2
Entiendo la funcionalidad de predict_proba pero no su ejecución y el por qué de las dos últimas lineas del codigo pegado justo antes.
Gracias de antemano y un saludo.
Una pregunta como instalo dlib en pymchar?
Hola Daniel, en pymchar no se instala, dlib se instala en tu enviroment (revisa esto para crear tu enviroment https://unipython.com/entornos-virtuales-en-python-y-anaconda/ ) y para instalar dlib https://unipython.com/instalar-dlib/ usa la forma 1
Hola Rafael!, gracias en primer lugar por tu aporte a la comunidad 😉
Seria muy complejo adaptar el codigo a que la fuente de entrada fuera la webcam?
Gracias!