![40ad633c05f8da3a3f830cf1964f7a48.png](https://i-blog.csdnimg.cn/blog_migrate/2ee42e573744e5b7c30b5a5d69b1de2f.jpeg)
1.HAAR级联分类器实现人的眼睛检测
1.1 人脸的生物学特征
- 两个眼睛之间的宽度大致等于一个眼睛的距离
- 左右对称
- 眼睛到嘴巴之间的距离大致在两个眼睛的宽度大小左右
- 鼻子到嘴巴距离大致等于两个嘴唇的厚度
先检测到人脸,然后截取人脸的一半区域(眼睛在脸的上半部分),而上半部分又可以平均分割成左右两边,还可以在细化把这个上半部分分成四份,最上面的四分之一可以去掉,还可以把人脸的横方向分为八分,八分之一的地方可以去掉。当找到人的眼睛之后,可以把它保存起来当做模板,利用模板匹配实现在下一次(下一帧)的跟踪,这样可以减少计算量,而且准确度会更高。
![4343c2d225a8843b1a158d536fc2ad9e.png](https://i-blog.csdnimg.cn/blog_migrate/b057a041c0792b760639ea9485653a2f.png)
1.2 人眼检测
例子代码:
#include<opencv2/opencv.hpp>
#include<iostream>
using namespace std;
using namespace cv;
// haarcascade_lefteye_2splits.xml haarcascade_righteye_2splits.xml
string facefile = "D:/opencv33_contrib/etc/haarcascades/haarcascade_frontalface_alt.xml";
//string lefteyefile = "D:/opencv33_contrib/etc/haarcascades/haarcascade_eye.xml";
//string righteyefile = "D:/opencv33_contrib/etc/haarcascades/haarcascade_eye.xml";
string lefteyefile = "D:/opencv33_contrib/etc/haarcascades/haarcascade_lefteye_2splits.xml";
string righteyefile = "D:/opencv33_contrib/etc/haarcascades/haarcascade_righteye_2splits.xml";
CascadeClassifier face_detector;
CascadeClassifier leftyeye_detector;
CascadeClassifier righteye_detector;
Rect leftEye, rightEye; //用于保存检测到的左右眼
void trackEye(Mat &inputImg,Mat &tplImg,Rect &rect)
{
//模板匹配固定写法
Mat result;
int result_cols = inputImg.cols - tplImg.cols + 1;
int result_rows = inputImg.rows - tplImg.rows + 1;
//模板匹配
result.create(result_rows, result_cols, CV_32FC1);
//TM_CCORR_NORMED相关系数 模板匹配比较方法
matchTemplate(inputImg, tplImg, result, TM_CCORR_NORMED);
//寻找位置
double minval, maxval;
Point minloc, maxloc;
minMaxLoc(result, &minval, &maxval, &minloc, &maxloc);
if (maxval > 0.75) //相关系数大于 0.75时,模板匹配成功
{
rect.x = rect.x + maxloc.x;
rect.y = rect.y + maxloc.y;
}
else //没找到时全部设置为 0
{
rect.x = rect.y = rect.width = rect.height = 0;
}
}
void test()
{
//VideoCapture capture(0); //打开摄像头
VideoCapture capture("01.mp4");
if (!capture.isOpened())
{
cout << "could not load video data...n" << endl;
}
Mat frame, grayImg;
vector<Rect> faces;
vector<Rect> eyes;
if (!face_detector.load(facefile))
{
cout << "could not load face feature data...n" << endl;
}
if (!leftyeye_detector.load(lefteyefile))
{
cout << "could not load leftyeye feature data...n" << endl;
}
if (!righteye_detector.load(righteyefile))
{
cout << "could not load righteye feature data...n" << endl;
}
namedWindow("result", CV_WINDOW_NORMAL);
Mat lefttpl, righttpl; // 模板
while (capture.read(frame))
{
cvtColor(frame, grayImg, COLOR_BGR2GRAY);
equalizeHist(grayImg, grayImg);
face_detector.detectMultiScale(grayImg, faces, 1.1, 4, 0, Size(48, 48));
for (int i = 0; i < faces.size(); i++)
{
rectangle(frame, faces[i], Scalar(255, 0, 0), 2, 8, 0);
// 计算 offset ROI
int offsety = faces[i].height / 4;
int offsetx = faces[i].width / 8;
int eyeheight = faces[i].height / 2 - offsety;
int eyewidth = faces[i].width / 2 - offsetx;
// 截取左眼区域
Rect leftRect;
leftRect.x = faces[i].x + offsetx;
leftRect.y = faces[i].y + offsety;
leftRect.width = eyewidth;
leftRect.height = eyeheight;
Mat leftRoi = grayImg(leftRect);
// 检测左眼
leftyeye_detector.detectMultiScale(leftRoi, eyes, 1.1, 0, 0, Size(20, 20));
if (lefttpl.empty()) //如果发现模板为空的话,就去计算得到模板
{
if (eyes.size())
{
//检测眼睛矩形平移
leftRect = eyes[0] + Point(leftRect.x, leftRect.y);
lefttpl = grayImg(leftRect); //获取左眼的模板存到 righttpl 中
rectangle(frame, leftRect, Scalar(0, 0, 255), 2, 8, 0);
}
}
else //不为空,就调用模板
{
// 跟踪, 基于模板匹配
leftEye.x = leftRect.x; //初始化
leftEye.y = leftRect.y;
//leftRoi 获取的眼睛图 lefttpl 模板数据 leftEye 结果图
trackEye(leftRoi, lefttpl, leftEye);
if (leftEye.x > 0 && leftEye.y > 0)
{
//设置宽度和高度,再画出来
leftEye.width = lefttpl.cols;
leftEye.height = lefttpl.rows;
rectangle(frame, leftEye, Scalar(0, 0, 255), 2, 8, 0);
}
}
// 截取右眼区域
Rect rightRect;
rightRect.x = faces[i].x + faces[i].width / 2;
rightRect.y = faces[i].y + offsety;
rightRect.width = eyewidth;
rightRect.height = eyeheight;
Mat rightRoi = grayImg(rightRect);
// 检测右眼
righteye_detector.detectMultiScale(rightRoi, eyes, 1.1, 0, 0, Size(20, 20));
if (righttpl.empty())
{
if (eyes.size())
{
rightRect = eyes[0] + Point(rightRect.x, rightRect.y);
righttpl = grayImg(rightRect); //获取右眼的模板存到 righttpl 中
rectangle(frame, rightRect, Scalar(0, 255, 255), 2, 8, 0);
}
}
else
{
// 跟踪, 基于模板匹配
rightEye.x = rightRect.x;
rightEye.y = rightRect.y;
trackEye(rightRoi, righttpl, rightEye);
if (rightEye.x > 0 && rightEye.y > 0)
{
rightEye.width = righttpl.cols;
rightEye.height = righttpl.rows;
rectangle(frame, rightEye, Scalar(0, 255, 255), 2, 8, 0);
}
}
}
imshow("result", frame);
char c = waitKey(100);
if (c == 27)
{
break;
}
}
capture.release();
}
int main()
{
test();
waitKey(0);
return 0;
}
效果:
Video_eyes_detectorv.youku.com![d876616cbb5f5e98da07c2f2203c56ab.png](https://i-blog.csdnimg.cn/blog_migrate/9cdd7a785f40e197a37e95752fb2babd.png)
1.3 猫脸检测
例子代码:
#include<opencv2/opencv.hpp>
#include<iostream>
using namespace std;
using namespace cv;
//haarcascade_frontalcatface.xml 猫脸检测
string catfile = "D:/opencv33_contrib/etc/haarcascades/haarcascade_frontalcatface.xml";
CascadeClassifier catface_detector;
void test()
{
if (!catface_detector.load(catfile))
{
cout << "could not load catface feature data...n" << endl;
}
Mat srcImg = imread("cat3.jpg");
if (srcImg.empty())
{
cout << "could not load image...n" << endl;
}
Mat grayImg;
vector<Rect> cats;
cvtColor(srcImg, grayImg, COLOR_BGR2GRAY);
equalizeHist(grayImg, grayImg);
catface_detector.detectMultiScale(grayImg, cats,
1.1,
1, //相邻的矩形窗口要达到或者超过这个参数,才保留。
0,
Size(24, 24)); //可以接受的最小窗口尺寸,如果小于这个它就不会考虑成猫脸
for (int i = 0; i < cats.size(); i++)
{
rectangle(srcImg, cats[i], Scalar(0, 255, 0), 2, 8, 0);
}
namedWindow("Cat Face Detector Demo", CV_WINDOW_AUTOSIZE);
imshow("Cat Face Detector Demo", srcImg);
}
int main()
{
test();
waitKey(0);
return 0;
}
效果图:
![5508a27c05d367ed5b2cde573fd33a31.png](https://i-blog.csdnimg.cn/blog_migrate/d655ad23d82be1bd8f2d7f237f045543.jpeg)
![472cfeac7e10a68b5224ec2a9f21ba84.png](https://i-blog.csdnimg.cn/blog_migrate/1f06fc69578d7f41a02bd10e3f1186bc.png)
欢迎关注我的微信公众号“OpenCV图像处理算法”,主要是记录自己学习图像处理算法的历程,包括特征提取、目标跟踪、定位、机器学习和深度学习,每一个例子都会提供源码和例子所用的资料,欢迎同行的同学关注我和我一起虚度光阴吧!!!