人脸检测和人眼跟踪:
一、普通版人脸及人眼的检测:
1. 这里我们使用的是opencv库官方提供的训练好的xml文件如下:
2.人脸检测原理:
将摄像头或者视频文件进行抓帧成一帧帧图象,然后将图象灰度化,直方图均衡化,最后通过face的xml文件进行人脸检测。
3.在每张人脸中进行人脸检测:
在进行人眼检测时,首先根据人眼的生物学特征在人脸上找到人眼的兴趣位置:
这里简单的将人眼的兴趣区域分在了人脸1/2的上半部,然后与人脸检测一样采用eye的xml文件进行人眼检测。
最后将检测结果人脸和人眼画出来。
代码如下:
1 #include<opencv2/opencv.hpp> 2 #include<iostream> 3 4 using namespace cv; 5 using namespace std; 6 7 CascadeClassifier face_cascader; //人脸检测类 8 CascadeClassifier eye_cascader; //人眼的类 9 String facefile = "E:/opencv/build/etc/haarcascades/haarcascade_frontalface_alt.xml"; 10 String eyefile = "E:/opencv/build/etc/haarcascades/haarcascade_eye.xml"; 11 // 12 int main(int argc, char** argv) { 13 if (!face_cascader.load(facefile)) { //将facefile文件加载到face_cascader(文件起作用关键步骤) 14 printf("could not load face feature data...\n"); 15 return -1; //没有加载到脸部文件() 16 } 17 if (!eye_cascader.load(eyefile)) { //将eyefile文件加载到eye_cascader(文件起作用关键步骤) 18 printf("could not load eye feature data...\n"); 19 return -1; //没有加载到眼睛文件 20 } 21 namedWindow("camera-demo", CV_WINDOW_AUTOSIZE);//相机窗口 22 //VideoCapture capture; //可以直接抓取视频文件 23 VideoCapture capture(0); //抓取默认摄像头内容显示在窗口 24 Mat frame; 25 Mat gray; 26 vector<Rect> faces; //一个faces变量的对象 27 vector<Rect> eyes; //一个eyes变量的对象 28 while (capture.read(frame)) { //capture.read读取视频后将每一帧图像放在frame中 29 cvtColor(frame, gray, COLOR_BGR2GRAY); //每帧图象转化为灰度图象 30 equalizeHist(gray, gray); //直方图均衡化 31 face_cascader.detectMultiScale(gray, faces, 1.2, 3, 0, Size(30, 30)); 32 //face_cascader文件下的detectMultiScale方法 33 //detectMultiScale函数: 34 // 1.输入图像 2.检测输出目标 3.每次图像减小比例 4. 每一个目标至少要被检测到3次才算 35 // 5.minSize为目标的最小尺寸 6.maxSize为目标的最大尺寸 36 for (size_t t = 0; t < faces.size(); t++) { //每张脸人眼检测 37 Rect roi; //一个对象roi放置人脸face 38 roi.x = faces[static_cast<int>(t)].x; //每个人脸的坐标 39 roi.y = faces[static_cast<int>(t)].y; 40 roi.width = faces[static_cast<int>(t)].width; //每个人脸的高度和宽度/2 41 roi.height = faces[static_cast<int>(t)].height / 2; 42 Mat faceROI = frame(roi); //在没帧图象frame上截取roi(上面设好坐标和宽高)区域,为faceROI 43 eye_cascader.detectMultiScale(faceROI, eyes, 1.2, 3, 0, Size(20, 20)); //在每个faceROI区域做人眼eyes检测 44 //eye_cascader文件下的detectMultiScale方法, detectMultiScale函数与上述一样。 45 for (size_t k = 0; k < eyes.size(); k++) { //找出每个眼睛的位置 46 Rect rect; 47 rect.x = faces[static_cast<int>(t)].x + eyes[k].x; 48 rect.y = faces[static_cast<int>(t)].y + eyes[k].y; 49 rect.width = eyes[k].width; 50 rect.height = eyes[k].height; 51 rectangle(frame, rect, Scalar(0, 255, 0), 2, 8, 0); //在frame上画出每个眼睛的位置 52 } 53 rectangle(frame, faces[static_cast<int>(t)], Scalar(0, 0, 255), 2, 8, 0); //在frame上画出每个脸的位置 54 } 55 imshow("camera-demo", frame); 56 char c = waitKey(30); 57 if (c == 27) { 58 break; 59 } 60 } 61 waitKey(0); 62 return 0; 63 }
检测结果如下:
二、人脸检测+模板匹配法人眼跟踪
1.使用opencv库官方提供的训练好的xml文件如下:
2.人脸检测原理与上面一样
3.人眼检测:这里将人眼的兴趣区域更加精确化
如图将左眼的兴趣区域画在人脸的左绿色框,右眼的兴趣区域画在人脸的右绿色框。上面大约是人脸高度的1/4
左右两边大约是人脸宽度的1/8
模板匹配法:https://www.cnblogs.com/Jack-Elvis/p/11530283.html
将检测到的第一个人脸的左眼和右眼作为模板,通过模板匹配将其他人脸上的左眼和右眼检测出来。
代码如下:
1 #include <opencv2/opencv.hpp> 2 #include <iostream> 3 4 using namespace cv; 5 using namespace std; 6 7 String facefile = "D:/opencv3.1/opencv/build/etc/haarcascades/haarcascade_frontalface_alt.xml"; //脸文件 8 String lefteyefile = "D:/opencv3.1/opencv/build/etc/haarcascades/haarcascade_eye.xml"; //左眼文件 9 String righteyefile = "D:/opencv3.1/opencv/build/etc/haarcascades/haarcascade_eye.xml"; //右眼文件 10 CascadeClassifier face_detector; //face_detector类用于加载文件 11 CascadeClassifier leftyeye_detector; 12 CascadeClassifier righteye_detector; 13 Rect leftEye, rightEye; //左眼和右眼的数组变量(对象) 14 15 //自定义一个模板匹配模块主函数中调用 16 void trackEye(Mat& im, Mat& tpl, Rect& rect) { 17 //1. im人眼兴趣图象 2. tpl人眼模板图象 3. rect输出目标人眼 18 Mat result; 19 int result_cols = im.cols - tpl.cols + 1; 20 int result_rows = im.rows - tpl.rows + 1; 21 // 模板匹配 22 //定义一个result矩阵,必须是单通道32位浮点数, 23 //假设源图像WxH,模板图像wxh,则结果必须为W - w + 1, H - h + 1的大小。 24 result.create(result_rows, result_cols, CV_32FC1); 25 matchTemplate(im, tpl, result, TM_CCORR_NORMED); 26 27 // 寻找位置 28 double minval, maxval; 29 Point minloc, maxloc; 30 minMaxLoc(result, &minval, &maxval, &minloc, &maxloc); 31 if (maxval > 0.75) { //归一化后的结果最大值若大于0.75 32 //人眼兴趣图象的始点坐标+匹配最大值所在的坐标(兴趣图象上的)=人眼坐标绝对值 33 rect.x = rect.x + maxloc.x; 34 rect.y = rect.y + maxloc.y; 35 } 36 else { 37 rect.x = rect.y = rect.width = rect.height = 0; 38 } 39 } 40 //trackEye(im, tpl, rect)函数输出为rect(对象Rect),即人眼模板匹配后,检测出人眼始点位置 41 42 int main(int argc, char** argv) { 43 if (!face_detector.load(facefile)) { 44 printf("could not load data file...\n"); 45 return -1; 46 } 47 if (!leftyeye_detector.load(lefteyefile)) { 48 printf("could not load data file...\n"); 49 return -1; 50 } 51 if (!righteye_detector.load(righteyefile)) { 52 printf("could not load data file...\n"); 53 return -1; 54 } 55 56 Mat frame; 57 VideoCapture capture(0); 58 namedWindow("demo-win", CV_WINDOW_AUTOSIZE); 59 60 Mat gray; 61 vector<Rect> faces; 62 vector<Rect> eyes; 63 Mat lefttpl, righttpl; // 定义左右眼模板为Mat格式 64 while (capture.read(frame)) { 65 flip(frame, frame, 1); 66 cvtColor(frame, gray, COLOR_BGR2GRAY); 67 equalizeHist(gray, gray);//直方图均衡化 68 face_detector.detectMultiScale(gray, faces, 1.1, 3, 0, Size(30, 30)); 69 for (size_t t = 0; t < faces.size(); t++) { 70 rectangle(frame, faces[t], Scalar(255, 0, 0), 2, 8, 0); 71 72 // 计算 offset ROI参数 73 int offsety = faces[t].height / 4; 74 int offsetx = faces[t].width / 8; 75 int eyeheight = faces[t].height / 2 - offsety; 76 int eyewidth = faces[t].width / 2 - offsetx; 77 78 // 截取左眼兴趣(ROI)区域 79 Rect leftRect; 80 leftRect.x = faces[t].x + offsetx; 81 leftRect.y = faces[t].y + offsety; 82 leftRect.width = eyewidth; 83 leftRect.height = eyeheight; 84 Mat leftRoi = gray(leftRect); 85 86 // 在leftROi区域检测左眼位置 87 leftyeye_detector.detectMultiScale(leftRoi, eyes, 1.1, 1, 0, Size(20, 20)); 88 if (lefttpl.empty()) { //左眼模板为空 89 if (eyes.size()) { //检测到眼睛 90 leftRect = eyes[0] + Point(leftRect.x, leftRect.y); 91 //将检测到的第一个眼睛对象eyes[0]赋给对象leftRect,并加上兴趣区域的始点坐标 92 lefttpl = gray(leftRect);//左眼灰度图象模板 93 rectangle(frame, leftRect, Scalar(0, 0, 255), 2, 8, 0);//画出左眼 94 } 95 } 96 else { 97 // 跟踪, 基于模板匹配 98 leftEye.x = leftRect.x; //左眼兴趣区域(ROI)的始点坐标 99 leftEye.y = leftRect.y; 100 //调用trackEye函数得到模板匹配后,左眼始点坐标(leftEye.x,leftEye.y) 101 trackEye(leftRoi, lefttpl, leftEye); 102 //将人眼模板的宽高参数赋值给leftEye对象(Rect) 103 if (leftEye.x > 0 && leftEye.y > 0) { 104 leftEye.width = lefttpl.cols; 105 leftEye.height = lefttpl.rows; 106 rectangle(frame, leftEye, Scalar(0, 0, 255), 2, 8, 0);//画出左眼 107 } 108 } 109 //右眼检测的方法与左眼一样 110 // 截取右眼区域 111 Rect rightRect; 112 rightRect.x = faces[t].x + faces[t].width / 2; 113 rightRect.y = faces[t].y + offsety; 114 rightRect.width = eyewidth; 115 rightRect.height = eyeheight; 116 Mat rightRoi = gray(rightRect); 117 118 // 检测右眼 119 righteye_detector.detectMultiScale(rightRoi, eyes, 1.1, 1, 0, Size(20, 20)); 120 if (righttpl.empty()) { 121 if (eyes.size()) { 122 rightRect = eyes[0] + Point(rightRect.x, rightRect.y); 123 righttpl = gray(rightRect); 124 rectangle(frame, rightRect, Scalar(0, 255, 255), 2, 8, 0); 125 } 126 } 127 else { 128 // 跟踪, 基于模板匹配 129 rightEye.x = rightRect.x; 130 rightEye.y = rightRect.y; 131 trackEye(rightRoi, righttpl, rightEye); 132 if (rightEye.x > 0 && rightEye.y > 0) { 133 rightEye.width = righttpl.cols; 134 rightEye.height = righttpl.rows; 135 rectangle(frame, rightEye, Scalar(0, 255, 255), 2, 8, 0); 136 } 137 } 138 } 139 imshow("demo-win", frame); 140 char c = waitKey(100); 141 if (c == 27) { // ESC 142 break; 143 } 144 } 145 146 // release resource 147 capture.release(); 148 waitKey(0); 149 return 0; 150 }
检测结果如下: