Reconocimiento Facial
OpenCV cuenta con la clase FaceRecognizer para facilitarnos el reconocimiento de un rostro presente en una imagen, haremos uso de los clasificadores en cascada para detectar el rostro y luego lo identificaremos con FaceRecognizer usando alguno de los algoritmos disponibles como: EigenFace, FisherFace y LBPH. Nuestro proyecto de Reconocimiento Facial usara LBPH.
1. Detección del Rostro
Utilizaremos uno de los clasificadores en cascada para detectar el rostro presente el la captura de la webcam, funcionara para rostros frontales, también necesitamos detectar los ojos, apliquemos los conceptos aprendidos en Detección de rostros y Detección de Ojos.
Este método será el encargado de obtener las coordenadas del rostro y ambos ojos, devolverá true si los encuentra, esto nos indicara que tenemos un rostro válido para procesar.
Esta función devuelve el rostro alineado, recortado y convertido a escala de grises, indicamos como parámetros las coordenadas obtenidas con la función anterior.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 | bool GetFaceAndEyes(Mat& frame, Rect& rostro, Rect& lEye, Rect& rEye) { vector<Rect> faces; faceDetector.detectMultiScale(frame, faces, 1.1, 3, CASCADE_FIND_BIGGEST_OBJECT | CASCADE_DO_ROUGH_SEARCH); if(faces.size() == 1) { rostro = faces[0]; Mat face = frame(rostro); int leftX = cvRound(face.cols * EYE_SX); int topY = cvRound(face.rows * EYE_SY); int widthX = cvRound(face.cols * EYE_SW); int heightY = cvRound(face.rows * EYE_SH); int rightX = cvRound(face.cols * (1.0-EYE_SX-EYE_SW)); Mat topLeftOfFace = face(Rect(leftX, topY, widthX,heightY)); Mat topRightOfFace = face(Rect(rightX, topY, widthX, heightY)); vector<Rect> lEyeR, rEyeR; lEyeDetector.detectMultiScale(topLeftOfFace, lEyeR, 1.1, 3, CASCADE_DO_ROUGH_SEARCH); lEyeDetector.detectMultiScale(topRightOfFace, rEyeR, 1.1, 3, CASCADE_DO_ROUGH_SEARCH); if(lEyeR.size() == 1 && rEyeR.size() == 1) { lEye = lEyeR[0]; rEye = rEyeR[0]; lEye.x += leftX; lEye.y += topY; rEye.x += rightX; rEye.y += topY; return true; } } return false; } |
2. Procesar el Rostro
Una vez tengamos las coordenadas válidas de los ojos y el rostro procedemos a recortarlo y alinearlo, aplicando transformaciones de rotación, escala y luego recorte, también convertimos la imagen a escala de grises.
Esta función devuelve el rostro alineado, recortado y convertido a escala de grises, indicamos como parámetros las coordenadas obtenidas con la función anterior.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | void CropFace(Mat& face, Mat& warped, Rect leftEye, Rect rightEye) { Point left = Point(leftEye.x + leftEye.width/2, leftEye.y + leftEye.height/2); Point right = Point(rightEye.x + rightEye.width/2, rightEye.y + rightEye.height/2); Point2f eyesCenter = Point2f( (left.x + right.x) * 0.5f, (left.y + right.y) * 0.5f ); // Get the angle between the 2 eyes. double dy = (right.y - left.y); double dx = (right.x - left.x); double len = sqrt(dx*dx + dy*dy); double angle = atan2(dy, dx) * 180.0 / CV_PI; // Hand measurements shown that the left eye center should ideally be at roughly (0.19, 0.14) of a scaled face image. const double DESIRED_RIGHT_EYE_X = (1.0f - DESIRED_LEFT_EYE_X); // Get the amount we need to scale the image to be the desired fixed size we want. double desiredLen = (DESIRED_RIGHT_EYE_X - DESIRED_LEFT_EYE_X) * FaceWidth; double scale = desiredLen / len; // Get the transformation matrix for rotating and scaling the face to the desired angle & size. Mat rot_mat = getRotationMatrix2D(eyesCenter, angle, scale); // Shift the center of the eyes to be the desired center between the eyes. rot_mat.at<double>(0, 2) += FaceWidth * 0.5f - eyesCenter.x; rot_mat.at<double>(1, 2) += FaceHeight * DESIRED_LEFT_EYE_Y - eyesCenter.y; warped = Mat(FaceHeight, FaceWidth, CV_8U, Scalar(128)); warpAffine(face, warped, rot_mat, warped.size()); } |
3. Reconocimiento del Rostro
En este punto ya tenemos el rostro que vamos a usar para entrenar nuestro algoritmo, OpenCV cuenta con implementaciones de los algoritmos: Eigenfaces Principal Component Analysis (PCA), Fisherfaces Linear Discriminant Analysis (LDA) y LBPH Local Binary Pattern Histograms, inventado por Ahonen, Hadid and Pietikäinen en el año 2004.
Para este proyecto utilizaremos LBPH, el método createLBPHFaceRecognizer() nos devolverá un objeto de tipo FaceRecognizer que implementa el algoritmo LBPH, del mismo modo podemos usar createFisherFaceRecognizer() o createEigenFaceRecognizer().
3.1 Fase de Entrenamiento
Para agregar un rostro a FaceRecognizer haremos varias capturas del mismo, teniendo en cuenta que debemos capturar rostros diferentes de la misma persona, cuanto más rostros capturemos mejor funcionara nuestro programa, procuremos capturas caras con diferentes gestos, iluminación y rotaciones hacia la derecha o izquierda.
Cada captura la almacenaremos en una Lista de Objetos Mat, cuando tengamos suficientes imágenes iniciamos el entrenamiento llamando al método train de facerecognizer pasamos como parámetros la lista de caras capturadas y una lista de objetos int que representan un identificador para el rostro, llamamos a update para agregar mas rostros.
1 2 3 4 | Ptr<FaceRecognizer> model = createLBPHFaceRecognizer(); vector<Mat> rostros; vector<int> ids; map<int , string> names; |
Funcionamiento de la aplicación en la fase de entrenamiento (solo si se ha detectado un rostro válido):
- Para iniciar el entrenamiento presionamos la tecla E, esto cuando deseemos agregar una persona a la base de datos.
- Para recolectar un rostro, presionamos la tecla A, cuando estamos en modo entrenamiento podemos recolectar la cantidad de caras que deseemos.
- Para agregar los rostros capturados de la persona que añadiremos a la base de datos presionamos la tecla T, debimos haber capturado por lo menos un rostro. La consola nos pedirá un nombre para identificar el rostro, lo tecleamos y presionamos ENTER al hacer esto volveremos al modo reconocimiento.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | //si el modo entrenamiento esta activo if(entrenado) { Mat nface = CropFace(frame(face), lEye, rEye); //Agregar el rostro y su numero id a las correspondientes listas if(agregarRostro) { rostros.push_back(nface); ids.push_back(identificador); agregarRostro = false; } //entrenar el modelo con los rostros capturados if(entrenar && rostros.size() >= 1) { model->update(rostros, ids); entrenar = agregarRostro = entrenado = false; rostros.clear(); ids.clear(); identificador += 1; } } |
3.2 Fase de Reconocimiento
La aplicación estará permanentemente en fase de reconocimiento, siempre que no este en modo entrenamiento, se intentara identificar el rostro que se haya detectado y se mostrara el resultado en la parte de abajo de la imagen.
Para la identificación del rostro FaceRecognizer cuenta con el método predict(face), le pasamos el rostro que deseamos identificar, este nos devolverá el id correspondiente al rostro.
Podemos usar el método predict(face, id, confidence), id donde se almacenara el identificador, confidence es un valor matemático que indica el nivel de similitud entre los rostros de modo que entre más cercano a cero más confiable es nuestra identificación.
Estableciendo un valor para threshold indicamos a FeceRecognizer que cualquier identificación con un confidence mayor que threshold sea tomado como desconocido.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | int id = -1; double confidence = 0.0; Mat nface; CropFace(copyFrame(face), nface, lEye, rEye); //calquier confidence mayor que threshold id = -1 //redicir o aumentar este valor segun nos convenga model->set("threshold", 70); model->predict(nface, id, confidence); if(id >= 0) { string msg = names[id] + " : " + to_string((int)confidence); DrawMarker(frame, face, msg , 20); } else DrawMarker(frame, face, "???", 20); |
Hola, he seguido tu tutorial, incluso me he descargado el código y al compilarlo me arroja los siguientes errores. Podrías ayudarme?
ResponderEliminarReconocimiento.cpp:210: error: invalid initialization of non-const reference of type ‘cv::Mat&’ from a temporary of type ‘cv::Mat’
Reconocimiento.cpp:80: error: in passing argument 1 of ‘void CropFace(cv::Mat&, cv::Mat&, cv::Rect, cv::Rect)’
Reconocimiento.cpp:254: error: invalid initialization of non-const reference of type ‘cv::Mat&’ from a temporary of type ‘cv::Mat’
Reconocimiento.cpp:80: error: in passing argument 1 of ‘void CropFace(cv::Mat&, cv::Mat&, cv::Rect, cv::Rect)’
Reconocimiento.cpp:263: error: ‘to_string’ was not declared in this scope
He solucionado el problema, me gustaria realizar unos cambios para no estar entrenando cada que se abre el programa, sino tener una base de datos predefinidos y nomas utilizar el programa para identificar... En donde se guardan los rostros de entrenamiento? . Saludos
ResponderEliminarhola colega yo tengo el mismo problema al compilar ....como lo solucionaste ...me podrias ayudar xfa
EliminarComo hiciste?????????????????????????????????????????????????????'
Eliminares que me pasa lo mismo
como lo hiciste a todos nos pasa lo mismo
EliminarHola! Tengo los mismos errores. Me podrias ayudar CANGUHACK?
ResponderEliminarNo estoy muy seguro del origen de estos errores, mi experiencia me dice que tal vez se deba a que el compilador que estoy usando es MSVC y ustedes quizás usen otro.
EliminarUna ayuda por favor, me sale error en lo siguiente:
ResponderEliminar1>..\..\..\..\..\Desktop\Reconocimiento facial.cpp(256): error C2668: 'std::to_string' : llamada ambigua a una función sobrecargada
en este pedazo de codigo
string msg = names[id] + " : " + to_string((int)confidence);
to_string((int)confidence) convierte el valor confidence a string, si este método no funciona puedes usar std::stringstream o cualquier otra forma que te permita la conversión de int a string;
EliminarComo asi? podrias ponerlo exactamente como debe ir porfa ...tengo el mismo error
EliminarLo primero es agregar el encabezado sstream :
Eliminar#include <sstream>
Segundo reemplazar el código con error por este:
std::stringstream txt;
txt << confidence;
string msg = names[id] + " : " txt.str();
Nuevamente he cometido un error, la ultima linea del código debería ser:
Eliminarstring msg = names[id] + " : " + txt.str();
Como seria con algoritmos geneticos?
ResponderEliminarHola estoy trabajando con openCV me gustria que me ayudarias a como guardar una imagen en un archivo XML o YAML para tener una base de datos con los rostros entrenados y no entrenar cada que inicie la aplcacion, gracias.
ResponderEliminarAquí puedes ver como guardar Mat en yaml:
Eliminarhttp://acodigo.blogspot.com/2014/07/persistencia-de-datos-yml-xml.html
Saludos, me he descargado tu código de Reconocimiento facial y al momento de compilarlo me salen los siguientes errores::::
ResponderEliminar---Reconocimiento facial.cpp:207:48: error: invalid initialization of non-const reference of type 'cv::Mat&' from an rvalue of type 'cv::Mat'
CropFace(copyFrame(face), nface, lEye, rEye);
---Reconocimiento facial.cpp:77:6: error: in passing argument 1 of 'void CropFace(cv::Mat&, cv::Mat&, cv::Rect, cv::Rect)'
void CropFace(Mat& face, Mat& warped, Rect leftEye, Rect rightEye)
---Reconocimiento facial.cpp:251:48: error: invalid initialization of non-const reference of type 'cv::Mat&' from an rvalue of type 'cv::Mat'
CropFace(copyFrame(face), nface, lEye, rEye);
---Reconocimiento facial.cpp:77:6: error: in passing argument 1 of 'void CropFace(cv::Mat&, cv::Mat&, cv::Rect, cv::Rect)'
void CropFace(Mat& face, Mat& warped, Rect leftEye, Rect rightEye)
---Reconocimiento facial.cpp:77:6: error: 'to_string' was not declared in this scope
string msg = names[id] + " : " + to_string((int)confidence);
Te agradeceria que me ayudaras a solucionarlo....!!!!
Para los que tengan el error:
ResponderEliminarerror: invalid initialization of non-const reference of type 'cv::Mat&' from an rvalue of type 'cv::Mat'
CropFace(copyFrame(face), nface, lEye, rEye);
o
In function ‘int main()’:
error: inicialización inválida de una referencia que no es constante de tipo ‘cv::Mat&’ desde un temporal de tipo ‘cv::Mat’
error: al pasar el argumento 1 de ‘void CropFace(cv::Mat&, cv::Mat&, cv::Rect, cv::Rect)’
agregar const antes del primer parámetro en la declaración de la función CropFace
o en otras palabras, sustituir:
void CropFace(Mat& face, Mat& warped, Rect leftEye, Rect rightEye)
por
void CropFace(const Mat& face, Mat& warped, Rect leftEye, Rect rightEye)
Y para el error de string:
error: 'to_string' was not declared in this scope
string msg = names[id] + " : " + to_string((int)confidence);
o
error: ‘to_string’ no se declaró en este ámbito
sustituir (como ya lo explico Carmelo Marin A):
string msg = names[id] + " : " + to_string((int)confidence);
por
std::stringstream txt;
txt << confidence;
string msg = names[id] + " : " + txt.str();
y agregar al inicio del archivo:
#include
Saludos
Este comentario ha sido eliminado por el autor.
ResponderEliminarHola,
ResponderEliminarsabes que tengo un error con la declaración:
Ptr model = createLBPHFaceRecognizer();
No me encuentra FaceRecognizer y createLBPHFaceRecognizer. Estoy con la ultima version de OpenCV y estoy utilizando Visual studio 2013, por lo demas del codigo me funciona bien menos esta linea de codigo.
Espero que me puedas ayudar,
Saludos,
Hola, amigo si pudiste resolver el error? Tengo el mismo problema,.. Agradezco respuesta.
EliminarSaludos
que tal broter estuve checando tus tutos y son los mejores en la web pero me gustaria poder tener mas comunicacion directa via correo para que me apolles devido que me metere de lleno devido a un proyecto universitario , mi correo es charly290909@hotmail.com gracias
ResponderEliminarBuenas, he estado probando tu código sobre Ubuntu y me he encontrado con un problemilla. Lo primero, tuve que cambiar lo que ya se ha hablado en comentarios anteriores. Hasta ahí sin problemas, lo nuevo viene cuando al ejecutarlo, no hace nada cuando se le pulsan las teclas. No reconoce ningún comando. Y lo más extraño es que cuando lo ejecutas por segunda vez, deja completamente frito al ordenador. Y hay que reiniciar. Tú lo habías probado en Ubuntu? Yo lo he compilado así: 'g++ RecFac.cpp `pkg-config --libs --cflags opencv` -std=c++11'.
ResponderEliminarMuchas gracias!!
Prueba con este shell, es el que uso para compilar rapido en opencv y me pudo compilar este ejemplo.
Eliminar#!/bin/bash
echo "compiling $1"
if [[ $1 == *.c ]]
then
gcc -ggdb `pkg-config --cflags opencv` -o `basename $1 .c` $1 `pkg-config --libs opencv`;
elif [[ $1 == *.cpp ]]
then
g++ -ggdb `pkg-config --cflags opencv` -o `basename $1 .cpp` $1 `pkg-config --libs opencv`;
else
echo "Please compile only .c or .cpp files"
fi
echo "Output file => ${1%.*}"
Hola, me estan ayudando mucho estos tutoriales en un proyecto que estoy desarrollando. Quiero mover un laser a la posicion en la que está detectanto. Estoy intentando poner un if para mover los ejes en el caso de que este detectanto. por ejemplo if !detect o si detect == 0. No logro obtener si esta detectando rostros ni el numero. ¿Podrias decirme como poner un if si esta detectando en ese momento?
ResponderEliminarAl final lo vi en el tutorial de deteccion de ojos, puse así:
Eliminarif (!rect.size() > 0), ¿sería la forma correcta o habría otra forma mejor?
Hola, tienes el ejemplo en Java?
ResponderEliminarAl parecer no lo tiene.. :(
EliminarSaludos, Puedes realizar el ejemplo en java? Pues me tome la tarea de traducir éste ejemplo a lenguaje java, pero hay funciones que no son similares a las nombradas en las librerias de java. Gracias.
ResponderEliminarhola, primero te felicito por tus tutoriales no ha habido errores en el proceso , necesitaba ayuda con mi proyecto universitario ya que necesito hacer un entrenador que reconozca las manos y despues detectar las respectivas señas que se hagan con las manos, he estdo chequeando algoritmos, asi como en el reconocedor facial pero no he encontrado mucha informacion.
ResponderEliminarhe estado hablando con otra persona acerca del tema, me dice que guarde la trayectoria de los centroides para guardar la trayctoria como referencia
ResponderEliminarha sido de gran ayuda tus tutoriales aunque no mencionas las herramientas para crear los clasificadores y crear los archivos xml para el id del codigo de genero
ResponderEliminarhola, sería de gran ayuda que pusieran el código del reconocimiento facial en GitHub junto con los demás ejemplos, ya que no puedo acceder al sitio actual( https://db.tt/vsYOyBBx)
ResponderEliminarEl enlace está reparado, ya puedes descargar el proyecto de reconocimiento facial, gracias por avisarme.
EliminarHola, si te es posible sería bueno algún tutorial de la librería tensorflow
EliminarHola tengo un problema con la libreria de opencv
ResponderEliminarNecesito su ayuda, el error que me sale es este:Exception in thread "main" java.lang.UnsatisfiedLinkError: C:\opencv\build\java\x64\opencv_java310.dll: Can't load AMD 64-bit .dll on a IA 32-bit platform
at java.lang.ClassLoader$NativeLibrary.load(Native Method)
at java.lang.ClassLoader.loadLibrary0(ClassLoader.java:1941)
at java.lang.ClassLoader.loadLibrary(ClassLoader.java:1857)
at java.lang.Runtime.loadLibrary0(Runtime.java:870)
at java.lang.System.loadLibrary(System.java:1122)
at Principal.main(Principal.java:90)
Java Result: 1
Puede ser que estes usando una librería de 64 bits en un sistema de 32 bits, asegurate de usar la versión acorde a tu sistema.
Eliminar