haar、LBP检测器原理
本人不擅长,请自行百度
级联器相关介绍
- Viola和Jones在2001在CVPR提出
- 一种实时对象(人脸)检测框架
- 训练速度非常慢,检测速度非常快
- 5000个正向人脸样本与3000万个非人脸负样本数据
级联分类器原理-AdaBoost
- 弱分类器-Weak classifier = Feature
- 强分类器-多个弱分类器的线性组合
- 级联分类器-多个强分类器的组合
opencv中级联检测器使用
- CascadeClassifier
- 加载特征数据
- 检测特征对象
- 代码演示
HAAR与LBP区别
- HAAR特征是浮点数运算
- LBP特征是整数计算
- LBP训练需要的样本数量要比HAAR大
- 同样的样本训练空间,HAAR训练出来的数据检测结果要比LBP准确
- 扩大LBP的样本数据,训练结果可以跟HAAR一样
- LBP的速度一般比HAAR快几倍
相关API
函数api
它可以检测出图片中所有的人脸,并将人脸用vector保存各个人脸的坐标、大小(用矩形表示),函数由分类器对象调用
void cv::CascadeClassifier::detectMultiScale ( InputArray image,
std::vector< Rect > & objects,
double scaleFactor = 1.1,
int minNeighbors = 3,
int flags = 0,
Size minSize = Size(),
Size maxSize = Size()
)
参数说明
- image 表示的是要检测的输入图像
- objects 表示检测到的人脸目标序列
- scaleFactor 表示每次图像尺寸减小的比例
- minNeighbors 表示每一个目标至少要被检测到3次才算是真的目标(因为周围的像素和不同的窗口大小都可以检测到人脸),
- minSize 为目标的最小尺寸
- maxSize 为目标的最大尺寸
函数api
void cv::matchTemplate ( InputArray image,
InputArray templ,
OutputArray result,
int method,
InputArray mask = noArray()
)
参数介绍
- image:待匹配的源图像
- templ:模板图像
- result:保存结果的矩阵,我们可以通过
minMaxLoc()
确定结果矩阵的最大值和最小值的位置.模板搜索结果输出图像,必须为单通道32-bit位浮点型图像,如果图像尺寸是WxH
而template尺寸是wxh
,则此参数result一定是**(W-w+1)x(H-h+1)** - method :模板匹配的算法有以下六种:
- 不同的方法会得到差异很大的结果,可以通过测试选择最合适的方法。
- (1).平方差匹配法TM_SQDIFF
- (2)归一化平方差匹配法TM_SQDIFF_NORMED
- (3)相关匹配法TM_CCORR
- (4)归一化相关匹配法TM_CCORR_NORMED
- (5)系数匹配法TM_CCOEFF
- (6)化相关系数匹配法TMCCOEFF_NORMED
- 通常来讲,随着从简单测量方法(平方差)到更复杂的测量方法(相关系数法),我们可以获得越来越准确的匹配。然而这同时也会以越来越大的计算量为代价。对于选取何种方法,针对不同的匹配情况进行对此分析比较,选取更适合自己应用场景同时兼顾速度和精度的最佳方案。
注意
值得注意的是对于方法SQDIFF和SQDIFF_NORMED两种方法来讲,越小的值就有着更高的匹配结果,而其余的方法则是数值越大匹配效果越好。
- 不同的方法会得到差异很大的结果,可以通过测试选择最合适的方法。
函数api
void minMaxLoc(const SparseMat& a,
double* minVal,
double* maxVal,
int* minIdx=0,
int* maxIdx=0);
参数说明
- a: 匹配结果矩阵
- &minVal 和 &maxVal: 在矩阵 result 中存储的最小值和最大值
- &minLoc 和 &maxLoc: 在结果矩阵中最小值和最大值的坐标.
小案例—视频中的人脸检测与眼睛跟踪
人脸检测与人脸的生物学特征
- HAAR级联检测器实现人脸检测
- 人脸的生物学特征
- 两个眼睛之间的距离大致等于一个眼睛的距离
- 左右对称
- 眼睛到嘴巴的距离大致在两个眼睛的宽度大小左右
- 鼻子到嘴唇的距离,大致等于两个嘴唇的厚度
检测方法
首先利用级联检测器检测人脸与眼睛,眼睛追踪不必一直检测,浪费算力,眼睛区域在脸部区域上半区域,眼睛区域在上半部分区域左右两侧,上班部分区域的上部1/4到1/3区域是眉毛、额头部分,不含有眼睛。左眼左边八分之一的宽度也不含有眼睛,右眼右边同样是这样。
为提高效率,减小检测面积是一方面,检查出眼睛位置后,获取眼睛图片,在相应的眼睛区域做模板匹配,之后就可以不用级联检测器检测眼睛,只需要检测面部即可。
代码 (缩小检测区域)
#include <opencv2/opencv.hpp>
#include <iostream>
#include <math.h>
using namespace std;
using namespace cv;
int main(void)
{
string face_data = "/work/opencv_data/haarcascade_frontalface_alt.xml";
string lefteyes_data = "/work/opencv_data/haarcascade_lefteye_2splits.xml";
string righteyes_data = "/work/opencv_data/haarcascade_righteye_2splits.xml";
CascadeClassifier face_classifier;
CascadeClassifier lefteyes_classifier;
CascadeClassifier righteyes_classifier;
if( !face_classifier.load(face_data) ||
!lefteyes_classifier.load(lefteyes_data) ||
!righteyes_classifier.load(righteyes_data) )
{
cout << "data is not exist" << endl;
return -1;
}
VideoCapture cap(0);
if(!cap.isOpened()) // 判断是否打开成功
{
cout << "open camera failed. " << endl;
return -1;
}
Mat frame,gray_src;
while(true)
{
cap >> frame;
if(frame.empty())
{
cout << "frame is empty" << endl;
return -1;
}
flip(frame,frame,1); //画面水平反转
cvtColor(frame,gray_src,COLOR_BGR2GRAY);
equalizeHist(gray_src,gray_src);
vector<Rect> faces;
Mat faceimg = frame.clone();
//识别人脸
face_classifier.detectMultiScale(gray_src,faces,1.1,3,0,Size(30,30));
for(size_t i=0;i<faces.size();i++)
{
rectangle(faceimg,faces[i],Scalar(0,255,0),2);
//缩小眼睛的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 left_rect;
left_rect.x = faces[i].x+offsetx;
left_rect.y = faces[i].y+offsety;
left_rect.width = eyewidth;
left_rect.height = eyeheight;
Mat left_roi = gray_src(left_rect);
//识别左眼
vector<Rect> lefteye;
//小范围识别 识别次数选择一次即可
lefteyes_classifier.detectMultiScale(left_roi,lefteye,1.1,1,0,Size(20,20));
if(lefteye.size())
{
left_rect = lefteye[0] + Point(left_rect.x,left_rect.y);
rectangle(faceimg,left_rect,Scalar(255,0,0),2);
}
//截取右眼区域
Rect right_rect;
right_rect.x = faces[i].x+faces[i].width/2;
right_rect.y = faces[i].y+offsety;
right_rect.width = eyewidth;
right_rect.height = eyeheight;
Mat right_roi = gray_src(right_rect);
//识别右眼
vector<Rect> righteye;
//小范围识别 识别次数选择一次即可
righteyes_classifier.detectMultiScale(left_roi,righteye,1.1,1,0,Size(20,20));
if(righteye.size())
{
right_rect = righteye[0] + Point(right_rect.x,right_rect.y);
rectangle(faceimg,right_rect,Scalar(255,0,0),2);
}
}
imshow("检测画面",faceimg);
if(waitKey(30)>0)
{
break;
}
}
destroyAllWindows();
return 0;
}
眼睛识别效果(缩小识别区域)
代码(模板实现眼睛追踪)
#include <opencv2/opencv.hpp>
#include <iostream>
#include <math.h>
using namespace std;
using namespace cv;
Mat lefttpl,righttpl; //待匹配的源图像
Rect leftEye,rightEye; //匹配后获得的矩形区域
//模板匹配函数
void track_eye(Mat &im,Mat &tpl,Rect &rect)
{
Mat result;
int result_cols = im.cols - tpl.cols +1;
int result_rows = im.rows - tpl.rows +1;
result.create(result_rows,result_cols,CV_32FC1);
matchTemplate(im,tpl,result,TM_CCORR_NORMED);
//寻找位置
double minval,maxval;
Point minloc,maxloc;
minMaxLoc(result,&minval,&maxval,&minloc,&maxloc);
if(maxval > 0.75)
{
rect.x = rect.x + maxloc.x;
rect.y = rect.y + maxloc.y;
}else
{
rect.x = rect.y = rect.width = rect.height = 0;
}
}
int main(void)
{
Mat result;
int result_cols = im.cols - tpl.cols +1;
int result_rows = im.rows - tpl.rows +1;
result.create(result_rows,result_cols,CV_32FC1);
matchTemplate(im,tpl,result,TM_CCORR_NORMED);
//寻找位置
double minval,maxval;
Point minloc,maxloc;
minMaxLoc(result,&minval,&maxval,&minloc,&maxloc);
if(maxval > 0.75)
{
rect.x = rect.x + maxloc.x;
rect.y = rect.y + maxloc.y;
}else
{
rect.x = rect.y = rect.width = rect.height = 0;
}
}
int main(void)
{
string face_data = "/work/opencv_data/haarcascade_frontalface_alt.xml";
string lefteyes_data = "/work/opencv_data/haarcascade_lefteye_2splits.xml";
string righteyes_data = "/work/opencv_data/haarcascade_righteye_2splits.xml";
CascadeClassifier face_classifier;
CascadeClassifier lefteyes_classifier;
CascadeClassifier righteyes_classifier;
if( !face_classifier.load(face_data) ||
!lefteyes_classifier.load(lefteyes_data) ||
!righteyes_classifier.load(righteyes_data) )
{
cout << "data is not exist" << endl;
return -1;
}
VideoCapture cap(0);
if(!cap.isOpened()) // 判断是否打开成功
{
cout << "open camera failed. " << endl;
return -1;
}
Mat frame,gray_src;
while(true)
{
cap >> frame;
if(frame.empty())
{
cout << "frame is empty" << endl;
return -1;
}
flip(frame,frame,1); //画面水平反转
cvtColor(frame,gray_src,COLOR_BGR2GRAY);
equalizeHist(gray_src,gray_src);
vector<Rect> faces;
Mat faceimg = frame.clone();
//识别人脸
face_classifier.detectMultiScale(gray_src,faces,1.1,3,0,Size(30,30));
for(size_t i=0;i<faces.size();i++)
{
rectangle(faceimg,faces[i],Scalar(0,255,0),2);
//缩小眼睛的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 left_rect;
left_rect.x = faces[i].x+offsetx;
left_rect.y = faces[i].y+offsety;
left_rect.width = eyewidth;
left_rect.height = eyeheight;
Mat left_roi = gray_src(left_rect);
//识别左眼
vector<Rect> lefteye;
//小范围识别 识别次数选择一次即可
lefteyes_classifier.detectMultiScale(left_roi,lefteye,1.1,1,0,Size(20,20));
if(lefttpl.empty())
{
if(lefteye.size())
{
left_rect = lefteye[0] + Point(left_rect.x,left_rect.y);
lefttpl = gray_src(left_rect);
rectangle(faceimg,left_rect,Scalar(255,0,0),2);
}
}else
{
leftEye.x = left_rect.x;
leftEye.y = left_rect.y;
track_eye(left_roi,lefttpl,leftEye);
if(leftEye.x > 0 && leftEye.y >0)
{
leftEye.width = lefttpl.cols;
leftEye.height = lefttpl.rows;
rectangle(faceimg,leftEye,Scalar(255,0,0),2);
}
}
//截取右眼区域
Rect right_rect;
right_rect.x = faces[i].x+faces[i].width/2;
right_rect.y = faces[i].y+offsety;
right_rect.width = eyewidth;
right_rect.height = eyeheight;
Mat right_roi = gray_src(right_rect);
//识别右眼
vector<Rect> righteye;
//小范围识别 识别次数选择一次即可
righteyes_classifier.detectMultiScale(left_roi,righteye,1.1,1,0,Size(20,20));
if(righttpl.empty())
{
if(righteye.size())
{
right_rect = righteye[0] + Point(right_rect.x,right_rect.y);
righttpl = gray_src(right_rect);
rectangle(faceimg,right_rect,Scalar(255,0,0),2);
}
}else
{
rightEye.x = right_rect.x;
rightEye.y = right_rect.y;
track_eye(right_roi,righttpl,rightEye);
if(rightEye.x > 0 && rightEye.y >0)
{
rightEye.width = righttpl.cols;
rightEye.height = righttpl.rows;
rectangle(faceimg,rightEye,Scalar(255,0,0),2);
}
}
}
imshow("检测画面",faceimg);
if(waitKey(30)>0)
{
break;
}
}
destroyAllWindows();
return 0;
}