代码来源于<Mastering OpenCV with Practical Computer Vision Projects >
她的另外几篇文章,也翻译的很好
http://blog.csdn.net/raby_gyl/article/details/12611861
http://blog.csdn.net/raby_gyl/article/details/12623539
http://blog.csdn.net/raby_gyl/article/details/12338371
我感觉下面的程序——对人眼定位,人脸矫正,人脸尺寸化,对于初学人脸识别,做人脸的预处理非常有帮助~
程序的思路是:首先通过人脸检测级联器检测到人脸区域,对于人脸区域我们采用经验理论(即不同的人眼检测器在不同的人脸搜索区域具有最优性),也即人眼在人脸区域中的位置,得到人眼的大体位置,采用opencv的人眼级联检测器检测人眼,获取每一个人眼的中心位置,两个人眼的连线与水平位置的夹角来确定人脸旋转、矫正的角度。同时通过我们想要得到的目标图像来计算得到仿射矩阵的scale尺度因子,即图像缩放的比例,以及通过平移来计算得到的距离,进而实现定位人眼在目的图像的位置。
代码如下:
- #include "stdafx.h"
- #include "opencv2/imgproc/imgproc.hpp"
- #include "opencv2/highgui/highgui.hpp"
- #include "opencv2/opencv.hpp"
- #include<iostream>
- #include<vector>
- using namespace std;
- using namespace cv;
-
- const double DESIRED_LEFT_EYE_X = 0.16;
- const double DESIRED_LEFT_EYE_Y = 0.14;
- const double FACE_ELLIPSE_CY = 0.40;
- const double FACE_ELLIPSE_W = 0.50;
- const double FACE_ELLIPSE_H = 0.80;
-
-
- void detectObjectsCustom(const Mat &img, CascadeClassifier &cascade, vector<Rect> &objects, int scaledWidth, int flags, Size minFeatureSize, float searchScaleFactor, int minNeighbors);
- void detectLargestObject(const Mat &img, CascadeClassifier &cascade, Rect &largestObject, int scaledWidth);
- void detectManyObjects(const Mat &img, CascadeClassifier &cascade, vector<Rect> &objects, int scaledWidth);
-
-
- void detectBothEyes(const Mat &face, CascadeClassifier &eyeCascade1, CascadeClassifier &eyeCascade2, Point &leftEye, Point &rightEye, Rect *searchedLeftEye, Rect *searchedRightEye);
- Mat getPreprocessedFace(Mat &srcImg, int desiredFaceWidth, CascadeClassifier &faceCascade, CascadeClassifier &eyeCascade1, CascadeClassifier &eyeCascade2, bool doLeftAndRightSeparately, Rect *storeFaceRect, Point *storeLeftEye, Point *storeRightEye, Rect *searchedLeftEye, Rect *searchedRightEye);
-
- int main(int argc,char **argv)
- {
- CascadeClassifier faceDetector;
- CascadeClassifier eyeDetector1;
- CascadeClassifier eyeDetector2;
-
- try{
-
- faceDetector.load("E:\\OpenCV-2.3.0\\data\\lbpcascades\\lbpcascade_frontalface.xml");
- eyeDetector1.load("E:\\OpenCV-2.3.0\\data\\haarcascades\\haarcascade_eye.xml");
- eyeDetector2.load("E:\\OpenCV-2.3.0\\data\\haarcascades\\haarcascade_eye_tree_eyeglasses.xml");
-
-
- }catch (cv::Exception e){}
- if(faceDetector.empty())
- {
- cerr<<"error:couldn't load face detector (";
- cerr<<"lbpcascade_frontalface.xml)!"<<endl;
- exit(1);
- }
-
- Mat img=imread(argv[1],1);
- Rect largestObject;
- const int scaledWidth=320;
- detectLargestObject(img,faceDetector,largestObject,scaledWidth);
- Mat img_rect(img,largestObject);
- Point leftEye,rightEye;
- Rect searchedLeftEye,searchedRightEye;
- detectBothEyes(img_rect,eyeDetector1,eyeDetector2,leftEye,rightEye,&searchedLeftEye,&searchedRightEye);
-
- Point2f eyesCenter;
- eyesCenter.x=(leftEye.x+rightEye.x)*0.5f;
- eyesCenter.y=(leftEye.y+rightEye.y)*0.5f;
- cout<<"左眼中心坐标 "<<leftEye.x<<" and "<<leftEye.y<<endl;
- cout<<"右眼中心坐标 "<<rightEye.x<<" and "<<rightEye.y<<endl;
-
- double dy=(rightEye.y-leftEye.y);
- double dx=(rightEye.x-leftEye.x);
- double len=sqrt(dx*dx+dy*dy);
- cout<<"dx is "<<dx<<endl;
- cout<<"dy is "<<dy<<endl;
- cout<<"len is "<<len<<endl;
- double angle=atan2(dy,dx)*180.0/CV_PI;
- const double DESIRED_RIGHT_EYE_X=1.0f-0.16;
-
- const int DESIRED_FACE_WIDTH=70;
- const int DESIRED_FACE_HEIGHT=70;
- double desiredLen=(DESIRED_RIGHT_EYE_X-0.16);
- cout<<"desiredlen is "<<desiredLen<<endl;
- double scale=desiredLen*DESIRED_FACE_WIDTH/len;
- cout<<"the scale is "<<scale<<endl;
- Mat rot_mat = getRotationMatrix2D(eyesCenter, angle, scale);
- double ex=DESIRED_FACE_WIDTH * 0.5f - eyesCenter.x;
- double ey = DESIRED_FACE_HEIGHT * DESIRED_LEFT_EYE_Y-eyesCenter.y;
- rot_mat.at<double>(0, 2) += ex;
- rot_mat.at<double>(1, 2) += ey;
- Mat warped = Mat(DESIRED_FACE_HEIGHT, DESIRED_FACE_WIDTH,CV_8U, Scalar(128));
- warpAffine(img_rect, warped, rot_mat, warped.size());
-
- imshow("warped",warped);
-
- rectangle(img,Point(largestObject.x,largestObject.y),Point(largestObject.x+largestObject.width,largestObject.y+largestObject.height),Scalar(0,0,255),2,8);
- rectangle(img_rect,Point(searchedLeftEye.x,searchedLeftEye.y),Point(searchedLeftEye.x+searchedLeftEye.width,searchedLeftEye.y+searchedLeftEye.height),Scalar(0,255,0),2,8);
- rectangle(img_rect,Point(searchedRightEye.x,searchedRightEye.y),Point(searchedRightEye.x+searchedRightEye.width,searchedRightEye.y+searchedRightEye.height),Scalar(0,255,0),2,8);
-
-
-
- imshow("img_rect",img_rect);
- imwrite("img_rect.jpg",img_rect);
- imshow("img",img);
- waitKey();
-
- }
-
-
-
-
-
-
-
-
-
-
-
- void detectObjectsCustom(const Mat &img, CascadeClassifier &cascade, vector<Rect> &objects, int scaledWidth, int flags, Size minFeatureSize, float searchScaleFactor, int minNeighbors)
- {
-
-
- Mat gray;
- if (img.channels() == 3) {
- cvtColor(img, gray, CV_BGR2GRAY);
- }
- else if (img.channels() == 4) {
- cvtColor(img, gray, CV_BGRA2GRAY);
- }
- else {
-
- gray = img;
- }
-
-
- Mat inputImg;
-
- float scale = img.cols / (float)scaledWidth;
- if (img.cols > scaledWidth) {
-
- int scaledHeight = cvRound(img.rows / scale);
- resize(gray, inputImg, Size(scaledWidth, scaledHeight));
- }
- else {
-
- inputImg = gray;
- }
-
-
- Mat equalizedImg;
- equalizeHist(inputImg, equalizedImg);
-
-
- cascade.detectMultiScale(equalizedImg, objects, searchScaleFactor, minNeighbors, flags, minFeatureSize);
-
-
- if (img.cols > scaledWidth) {
- for (int i = 0; i < (int)objects.size(); i++ ) {
- objects[i].x = cvRound(objects[i].x * scale);
- objects[i].y = cvRound(objects[i].y * scale);
- objects[i].width = cvRound(objects[i].width * scale);
- objects[i].height = cvRound(objects[i].height * scale);
- }
- }
-
-
- for (int i = 0; i < (int)objects.size(); i++ ) {
- if (objects[i].x < 0)
- objects[i].x = 0;
- if (objects[i].y < 0)
- objects[i].y = 0;
- if (objects[i].x + objects[i].width > img.cols)
- objects[i].x = img.cols - objects[i].width;
- if (objects[i].y + objects[i].height > img.rows)
- objects[i].y = img.rows - objects[i].height;
- }
-
-
- }
-
-
-
-
-
-
-
- void detectLargestObject(const Mat &img, CascadeClassifier &cascade, Rect &largestObject, int scaledWidth)
- {
-
- int flags = CV_HAAR_FIND_BIGGEST_OBJECT;
-
- Size minFeatureSize = Size(20, 20);
-
- float searchScaleFactor = 1.1f;
-
-
- int minNeighbors = 4;
-
-
- vector<Rect> objects;
- detectObjectsCustom(img, cascade, objects, scaledWidth, flags, minFeatureSize, searchScaleFactor, minNeighbors);
- if (objects.size() > 0) {
-
- largestObject = (Rect)objects.at(0);
- }
- else {
-
- largestObject = Rect(-1,-1,-1,-1);
- }
- }
-
- void detectManyObjects(const Mat &img, CascadeClassifier &cascade, vector<Rect> &objects, int scaledWidth)
- {
-
- int flags = CV_HAAR_SCALE_IMAGE;
-
-
- Size minFeatureSize = Size(20, 20);
-
- float searchScaleFactor = 1.1f;
-
-
- int minNeighbors = 4;
-
-
- detectObjectsCustom(img, cascade, objects, scaledWidth, flags, minFeatureSize, searchScaleFactor, minNeighbors);
- }
-
-
-
-
-
-
- void detectBothEyes(const Mat &face, CascadeClassifier &eyeCascade1, CascadeClassifier &eyeCascade2, Point &leftEye, Point &rightEye, Rect *searchedLeftEye, Rect *searchedRightEye)
- {
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- const float EYE_SX = 0.16f;
- const float EYE_SY = 0.26f;
- const float EYE_SW = 0.30f;
- const float EYE_SH = 0.28f;
-
- 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));
- Rect leftEyeRect, rightEyeRect;
-
-
- if (searchedLeftEye)
- *searchedLeftEye = Rect(leftX, topY, widthX, heightY);
- if (searchedRightEye)
- *searchedRightEye = Rect(rightX, topY, widthX, heightY);
-
-
- detectLargestObject(topLeftOfFace, eyeCascade1, leftEyeRect, topLeftOfFace.cols);
- detectLargestObject(topRightOfFace, eyeCascade1, rightEyeRect, topRightOfFace.cols);
-
-
- if (leftEyeRect.width <= 0 && !eyeCascade2.empty()) {
- detectLargestObject(topLeftOfFace, eyeCascade2, leftEyeRect, topLeftOfFace.cols);
-
-
-
-
- }
-
-
-
-
- if (rightEyeRect.width <= 0 && !eyeCascade2.empty()) {
- detectLargestObject(topRightOfFace, eyeCascade2, rightEyeRect, topRightOfFace.cols);
-
-
-
-
- }
-
-
-
- if (leftEyeRect.width > 0) {
- leftEyeRect.x += leftX;
- leftEyeRect.y += topY;
- leftEye = Point(leftEyeRect.x + leftEyeRect.width/2, leftEyeRect.y + leftEyeRect.height/2);
- }
- else {
- leftEye = Point(-1, -1);
- }
-
- if (rightEyeRect.width > 0) {
- rightEyeRect.x += rightX;
- rightEyeRect.y += topY;
- rightEye = Point(rightEyeRect.x + rightEyeRect.width/2, rightEyeRect.y + rightEyeRect.height/2);
- }
- else {
- rightEye = Point(-1, -1);
- }
- }
运行效果图:
1、
2、检测到的人脸矩形区域:
3、人脸矫正和尺寸归一化到70*70后的结果图:
我要说的:
1、代码是截取的原文中的一小部分,搭配好环境可以直接运行,人家的程序可能适用于网络摄像头拍的正对着人脸的,一个人脸图像。而不是针对一般的有一群人,人脸小一些的,或者人脸不是正面的图像,你可以那lena图像试一下,它只能检测到一只左眼(真实的右眼),而另外一只检测不到,那么就会返回一个无效的点Point(-1,-1)作为眼睛的中心,那么更别提后面的旋转了,即后面的旋转肯定也是不对的。在你用本程序测试的时候,一定要选择一个合理的图像。
2、我讲一下关于旋转平移的代码的理解:
首先我们看下图:
这是我们的目的图像,满足的要求为:
(1)大小为70*70;
(2)两个眼睛直接的距离为(1-0.16)*70;(图中的(0.16,0.14)是左眼中心在图像中比例位置,由于人眼时对称的,则右眼所在比例位置为(0.68,0.14),要想获得真实的位置乘以70即可,对于本例是这样的)
(3)两个人眼连线的中心位置;
有了上述三个条件,我们就可以固定一双人眼在一个大小(这里是70*70)固定的图像中的具体位置。下面的代码就是要实现这个功能:
- Point2f eyesCenter;
- eyesCenter.x=(leftEye.x+rightEye.x)*0.5f;
- eyesCenter.y=(leftEye.y+rightEye.y)*0.5f;
- double dy=(rightEye.y-leftEye.y);
- double dx=(rightEye.x-leftEye.x);
- double len=sqrt(dx*dx+dy*dy);
- double angle=atan2(dy,dx)*180.0/CV_PI;
-
- const double DESIRED_RIGHT_EYE_X=1.0f-0.16;
- const int DESIRED_FACE_WIDTH=70;
- const int DESIRED_FACE_HEIGHT=70;
- double desiredLen=(DESIRED_RIGHT_EYE_X-0.16);
- double scale=desiredLen*DESIRED_FACE_WIDTH/len;
- Mat rot_mat = getRotationMatrix2D(eyesCenter, angle, scale);
-
- double ex=DESIRED_FACE_WIDTH * 0.5f - eyesCenter.x;
- double ey = DESIRED_FACE_HEIGHT * DESIRED_LEFT_EYE_Y-eyesCenter.y;
- rot_mat.at<double>(0, 2) += ex;
- rot_mat.at<double>(1, 2) += ey;
- Mat warped = Mat(DESIRED_FACE_HEIGHT, DESIRED_FACE_WIDTH,CV_8U, Scalar(128));
- warpAffine(img_rect, warped, rot_mat, warped.size());
我们可以假想一下,上边的代码如果没有最后一个中心点的平移,之前的旋转矩阵只能控制图像的缩放和两个眼睛直接的相对位置,但是控制不了两个眼睛在图像中的位置,即固定两个眼在图像中的位置。
补充知识(仿射变换):
上图中a0,b0为控制平移的因子,如果我们领a2=1,a1=0,b2=0,b1=1,即变为u=x+a0,v=y+b0;
参考图: