我们使用opencv级联多级分类器进行解读:
1.基本概念
opencv中的人脸检测使用基于Harr的级联分类和基于LBP的级联分类。
Harr是在2001年,由Viola和Jones等人提出的,它的脸部检测的基本思想是:对于面部正面的大部分区域而言,会有眼睛所在的区域比前额和脸颊更暗,嘴巴应该比脸颊更暗等情况。和这样类似的比较大约有20个,通过这样的比较决定该区域是否为人脸。
LBP是在2006年由Ahonen等人提出的,相比于Harr,LBP有更快的速度。通过比较想读亮度直方图来确定是否为人脸。但是对于稳定性,LBP要弱于前者。
Opencv自带训练好的人脸检测模型,存储在sources/data/haarcascades文件夹和sources/data/lbpcascades文件夹下。其中几个.xml文件如下:
人脸检测器(默认):haarcascade_frontalface_default.xml
人脸检测器(快速Harr):haarcascade_frontalface_alt2.xml
人脸检测器(侧视):haarcascade_profileface.xml
眼部检测器(左眼):haarcascade_lefteye_2splits.xml
眼部检测器(右眼):haarcascade_righteye_2splits.xml
嘴部检测器:haarcascade_mcs_mouth.xml
鼻子检测器:haarcascade_mcs_nose.xml
身体检测器:haarcascade_fullbody.xml
人脸检测器(快速LBP):lbpcascade_frontalface.xml
---------------------
2.如何使用
2.1 加载一个检测器:应用CascadeClassifier并实例化,写入文件路径并加载。
CascadeClassifier face_cascade;
String face_cascade_name = "C:\\Program Files\\OpenCV2.4.11\\opencv\\sources\\data\\lbpcascades\\lbpcascade_frontalface.xml";
face_cascade.load( face_cascade_name );
2.2 图像预处理:灰度变换,收缩尺寸和直方图均衡化
cvtColor( frame, frame_gray, CV_BGR2GRAY );
const int DETECTION_WIDTH = 320;
Mat smallImg;
float scale = frame_gray.cols/(float)DETECTION_WIDTH;
if (frame_gray.cols>DETECTION_WIDTH)
{
int scaledHeight = cvRound(frame_gray.rows/scale);
resize(frame_gray,smallImg.size(DETECTION_WIDTH,scaledHeight));
}
else
{
smallImg = frame_gray;
}
Mat equalizedImg;
equalizeHist( smallImg , equalizedImg);
2.3人脸检测
std::vector faces;
face_cascade.detectMultiScale( equalizedImg, faces, 1.1, 2, 0, Size(80, 80) );
此时检测结果保存在faces这个vector容器中。
3.几个使用技巧
3.1 在加载检测器时通常由于路径问题容易产生错误,最好有一个提醒或异常捕获机制。
face_cascade.load( face_cascade_name );
if( !face_cascade.load( face_cascade_name ) )
{ printf("--(!)Error loading\n"); return -1; };
或者
try {
face_cascade.load( face_cascade_name );
} catch (cv::Exception &e) {}
if ( face_cascade_name.empty() ) {
printf("--(!)Error loading\n"); return -1;
exit(1);
}
3.2根据情况设置参数
detectMultiScale函数原型如下:
CV_WRAP virtual void detectMultiScale( const Mat& image,
CV_OUT vector& objects,
double scaleFactor=1.1,
int minNeighbors=3, int flags=0,
Size minSize=Size(),
Size maxSize=Size() );
其中:
minSize=Size()和maxSize=Size(): 决定了检测到的最小和最大的人脸大小,如果图片中人脸距离相机较远,把minSize参数设置为=Size(20,20)
scaleFactor:参数决定由多少不同大小的人脸要搜索,通常为1.1
minNeighbors: 决定着人脸检测器如何确定人脸已经被找到,默认值是3,如果改为4的话,将会使检测的正确率增加,但是漏检率也可能增加,可以理解为参数越大,判断的条件越苛刻。
flags:是否要检测所有人脸。
---------------------
作者:chaibubble
来源:CSDN
原文:https://blog.csdn.net/chaipp0607/article/details/54234663
眼球中心定位
现在截取出了眼部ROI图像,就到了之前提到的那个论文的方法了,《Accurate eye centre localisation by means of gradients》。其实它用到的方法很好理解,一副图像,每个像素点都可以计算出梯度,而梯度包含了幅值和方向。对于眼部图像而言,越是靠近眼球中心的位置,灰度值就越低,就会有更多的梯度方向的连线交于那个点。
眼球中心定位跟踪算法—eyelike
// 1-Face_Eye.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//
#include "pch.h"
#include "opencv.hpp"
#include "highgui.h"
#include
#include
using namespace cv;
CascadeClassifier face_cascade;
CascadeClassifier eye_cascade;
// 人眼检测
int detectEye(cv::Mat& im, cv::Mat& tpl, cv::Rect& rect)
{
std::vector faces, eyes;
// 多尺度人脸检测
face_cascade.detectMultiScale(im, faces,
1.1, 2, 0 | CV_HAAR_SCALE_IMAGE, cv::Size(30, 30));
// 遍历人脸检测结果查找人眼目标
for (int i = 0; i < faces.size(); i++)
{
cv::Mat face = im(faces[i]);
// 多尺度人眼检测
eye_cascade.detectMultiScale(face, eyes,
1.1, 2, 0 | CV_HAAR_SCALE_IMAGE, cv::Size(20, 20));
// 人眼检测区域输出
if (eyes.size())
{
rect = eyes[0] + cv::Point(faces[i].x, faces[i].y);
tpl = im(rect);
}
}
return eyes.size();
}
// 人眼跟踪
void trackEye(cv::Mat& im, cv::Mat& tpl, cv::Rect& rect)
{
// 人眼位置
cv::Size pSize(rect.width * 2, rect.height * 2);
// 矩形区域
cv::Rect tRect(rect + pSize -
cv::Point(pSize.width / 2, pSize.height / 2));
tRect &= cv::Rect(0, 0, im.cols, im.rows);
// 匹配模板生成
cv::Mat tempMat(tRect.width - tpl.rows + 1,
tRect.height - tpl.cols + 1, CV_32FC1);
// 模板匹配
cv::matchTemplate(im(tRect), tpl, tempMat,
CV_TM_SQDIFF_NORMED);
// 计算最小最大值
double minval, maxval;
cv::Point minloc, maxloc;
cv::minMaxLoc(tempMat, &minval, &maxval,
&minloc, &maxloc);
// 区域检测判断
if (minval <= 0.2)
{
rect.x = tRect.x + minloc.x;
rect.y = tRect.y + minloc.y;
}
else
rect.x = rect.y = rect.width = rect.height = 0;
}
int main()
{
// 初始化摄像头读取视频流
cv::VideoCapture cap(0);
// 宽高设置为320*256
cap.set(CV_CAP_PROP_FRAME_WIDTH, 420);
cap.set(CV_CAP_PROP_FRAME_HEIGHT, 400);
// 读取级联分类器
// 文件存放在opencv\sources\data\haarcascades
bool flagGlasses = false;
if (flagGlasses)
{
face_cascade.load("haarcascade_frontalface_alt2.xml");
eye_cascade.load("haarcascade_eye.xml");
}
else
{
face_cascade.load("haarcascade_frontalface_alt2.xml");
eye_cascade.load("haarcascade_eye_tree_eyeglasses.xml");
}
// 判断初始化设置是否正常
if (face_cascade.empty() || eye_cascade.empty()
|| !cap.isOpened())
return 1;
// 视频流操作
cv::Mat frame, eyeMat;
cv::Rect eyeRect;
while (cv::waitKey(10) != 'q')
{
cap >> frame;
if (frame.empty())
break;
// 水平翻转
cv::flip(frame, frame, 1);
// 灰度转换
cv::Mat gray;
cv::cvtColor(frame, gray, CV_BGR2GRAY);
// 人眼检测尺寸判断 若不符合则需要重新检测
if (eyeRect.width <= 2 || eyeRect.height <= 2)
{
// 人眼检测
detectEye(gray, eyeMat, eyeRect);
}
else // 符合则进行人眼跟踪
{
// 人眼跟踪
trackEye(gray, eyeMat, eyeRect);
// 人眼结果绘制
cv::rectangle(frame, eyeRect, CV_RGB(0, 255, 0));
}
cv::imshow("video", frame);
}
}
发现无法调用摄像头驱动,出现错误:
解决办法:
点击:选项——>调试——>常规——>启用源服务支持
点击:选项——>调试——>符号——>Microsoft服务符号器
https://blog.csdn.net/gywtzh0889/article/details/53337989
这是载入图像后再通过图像识别,找到人脸,画出人脸区域的红圈后的效果。
人脸识别程序编写。
1.首先创建一个机遇对话框的MFC工程FaceDetection2。
文件如图:
2.将对话框修改成如图所示
3.按照如下步骤配置用到的lib
4.然后编辑FaceDetection2Dlg.cpp文件,源码如下
// FaceDetection2Dlg.cpp : implementation file
//
#include "stdafx.h"
#include "FaceDetection2.h"
#include "FaceDetection2Dlg.h"
#include
#include
#ifdef _DEBUG
#define new DEBUG_NEW
#endif
using namespace std;
const char* cascade_name="haarcascade_frontalface_alt2.xml";//分类器的名称
const char* cascade_name1="haarcascade_eye_tree_eyeglasses.xml";//分类器的名称
const char* cascade_name2="haarcascade_frontalface_alt_tree.xml";//分类器的名称
const char* cascade_name3="haarcascade_mcs_mouth.xml";//分类器的名称
const char* cascade_name4="haarcascade_mcs_nose.xml";//分类器的名称
// CAboutDlg dialog used for App About
class CAboutDlg : public CDialog
{
public:
CAboutDlg();
// Dialog Data
enum { IDD = IDD_ABOUTBOX };
protected:
virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support
// Implementation
protected:
DECLARE_MESSAGE_MAP()
};
CAboutDlg::CAboutDlg() : CDialog(CAboutDlg::IDD)
{
}
void CAboutDlg::DoDataExchange(CDataExchange* pDX)
{
CDialog::DoDataExchange(pDX);
}
BEGIN_MESSAGE_MAP(CAboutDlg, CDialog)
END_MESSAGE_MAP()
// CFaceDetection2Dlg dialog
CFaceDetection2Dlg::CFaceDetection2Dlg(CWnd* pParent /*=NULL*/)
: CDialog(CFaceDetection2Dlg::IDD, pParent)
{
m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
}
void CFaceDetection2Dlg::DoDataExchange(CDataExchange* pDX)
{
CDialog::DoDataExchange(pDX);
}
BEGIN_MESSAGE_MAP(CFaceDetection2Dlg, CDialog)
ON_WM_SYSCOMMAND()
ON_WM_PAINT()
ON_WM_QUERYDRAGICON()
//}}AFX_MSG_MAP
ON_BN_CLICKED(ID_FaceDetected, &CFaceDetection2Dlg::OnBnClickedFacedetected)
END_MESSAGE_MAP()
// CFaceDetection2Dlg message handlers
BOOL CFaceDetection2Dlg::OnInitDialog()
{
CDialog::OnInitDialog();
// Add "About..." menu item to system menu.
// IDM_ABOUTBOX must be in the system command range.
ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);
ASSERT(IDM_ABOUTBOX < 0xF000);
CMenu* pSysMenu = GetSystemMenu(FALSE);
if (pSysMenu != NULL)
{
CString strAboutMenu;
strAboutMenu.LoadString(IDS_ABOUTBOX);
if (!strAboutMenu.IsEmpty())
{
pSysMenu->AppendMenu(MF_SEPARATOR);
pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);
}
}
// Set the icon for this dialog. The framework does this automatically
// when the application's main window is not a dialog
SetIcon(m_hIcon, TRUE); // Set big icon
SetIcon(m_hIcon, FALSE); // Set small icon
// TODO: Add extra initialization here
return TRUE; // return TRUE unless you set the focus to a control
}
void CFaceDetection2Dlg::OnSysCommand(UINT nID, LPARAM lParam)
{
if ((nID & 0xFFF0) == IDM_ABOUTBOX)
{
CAboutDlg dlgAbout;
dlgAbout.DoModal();
}
else
{
CDialog::OnSysCommand(nID, lParam);
}
}
// If you add a minimize button to your dialog, you will need the code below
// to draw the icon. For MFC applications using the document/view model,
// this is automatically done for you by the framework.
void CFaceDetection2Dlg::OnPaint()
{
if (IsIconic())
{
CPaintDC dc(this); // device context for painting
SendMessage(WM_ICONERASEBKGND, reinterpret_cast
// Center icon in client rectangle
int cxIcon = GetSystemMetrics(SM_CXICON);
int cyIcon = GetSystemMetrics(SM_CYICON);
CRect rect;
GetClientRect(&rect);
int x = (rect.Width() - cxIcon + 1) / 2;
int y = (rect.Height() - cyIcon + 1) / 2;
// Draw the icon
dc.DrawIcon(x, y, m_hIcon);
}
else
{
CDialog::OnPaint();
}
}
// The system calls this function to obtain the cursor to display while the user drags
// the minimized window.
HCURSOR CFaceDetection2Dlg::OnQueryDragIcon()
{
return static_cast
}
void CFaceDetection2Dlg::OnBnClickedFacedetected()
{
// TODO: 在此添加命令处理程序代码
CString fileName;
//打开文件对话窗口
CFileDialog OpenDlg( TRUE, NULL, NULL, OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT | OFN_NOCHANGEDIR, L"图像文件格式JPG file format (*.jpg)|*.jpg|(*.bmp) |*.bmp|", NULL);
//从文件对话窗口中打开图像
if(OpenDlg.DoModal()!=IDOK)
return ;
//获得文件名
fileName = OpenDlg.GetPathName();
//必要的类型转换
std::string tempName = (LPCSTR)CStringA(fileName);
const char* tmp = tempName.c_str();
//打开文件,若失败则返回
if((src=cvLoadImage(tmp,CV_LOAD_IMAGE_ANYCOLOR))==0)
return ;
//加载(分类器层叠)训练库
cascade = (CvHaarClassifierCascade*)cvLoad( cascade_name2, 0, 0, 0 );
//加载不成功则显示错误讯息,并退出
if(cascade)
{
storage = cvCreateMemStorage(0);
cvNamedWindow( "人脸检测", CV_WINDOW_AUTOSIZE ); //创建窗口
//如果图片存在则分析并显示结果,否则退出程序
if(src)
detect_and_draw(src);//调用人脸检与标示事件
cvReleaseImage(&src);
cvReleaseMemStorage( &storage );
}else{
AfxMessageBox(L"无法加载分类器,请确认后重试!");
}
cvReleaseHaarClassifierCascade( &cascade );
}
void CFaceDetection2Dlg::detect_and_draw(IplImage *img)
{
static CvScalar color[] = {{0,0,255},{0,128,255},{0,255,255},{0,255,0},{0,128,255},{255,128,0},{255,255,0},{255,0,0},{255,0,255}};//用于设置标示图像中人脸的颜色
double scale = 1.3;
IplImage* gray = cvCreateImage(cvSize(img->width,img->height),8,1);
IplImage* small_img = cvCreateImage( cvSize( cvRound (img->width/scale),cvRound (img->height/scale)),8,1 );
int i;
cvCvtColor( img, gray, CV_BGR2GRAY );
cvResize( gray, small_img, CV_INTER_LINEAR );
cvEqualizeHist( small_img, small_img );
cvClearMemStorage( storage );
if( cascade )
{
//检测人脸
CvSeq* faces = cvHaarDetectObjects( small_img, cascade, storage, 1.1, 2, 0, cvSize(30, 30) );
for( i = 0; i < (faces ? faces->total : 0); i++ )
{
CvRect* r = (CvRect*)cvGetSeqElem( faces, i );
CvPoint center;
int radius;
center.x = cvRound((r->x + r->width*0.5)*scale);
center.y = cvRound((r->y + r->height*0.5)*scale);
radius = cvRound((r->width + r->height)*0.25*scale);
cvCircle( img, center, radius, color[i], 3, 8, 0 );
}
}
cvShowImage( "人脸检测", img );
cvReleaseImage( &gray );
cvReleaseImage( &small_img );
}
其中OnBnClickedFacedetected()为按钮FaceDetect的监听方法
detect_and_draw(IplImage *img)方法用于检测人脸
5.运行观察结果
三、总结
在源码中,
这是不同的分类器,你可以在你安装的OpenCV中找到。如D:\Program Files\OpenCV2.0\vs2008\data\haarcascades
不同分类器能够帮助你识别不同的部分,如眼睛,鼻子和嘴,更多的需要自己去探索吧。
选择框架:caffe、tensorflow等
选择模板:(待考虑)
选择语言:C++、opencv、python
整理数据:待检测目标图像的数据集收集、整理、标注;
或:直接找出相关的人脸识别的数据集,拿出一部分来标注;
60%:training;(标注)
20%:testing;(不标)
20%:matching;(不标)
每次写一点,下次整合……
参考文章:
1,以SVM和HOG特性实现实时人脸检测和识别
2,[机器学习]基于级联分类器的多目标检测
3,使用OpenCV自带的级联分类器进行目标检测
4,Opencv目标检测之级联分类器训练与测试
5,级联分级器目标检测objdect
6,OpenCV学习笔记(二十六)——小试SVM算法ml OpenCV学习笔记(二十七)——基于级联分类器的目标检测objdect OpenCV学习笔记(二十八)——光流法对运动目标跟踪Video Ope
7,人脸、人眼检测与跟踪
8,眼球中心定位跟踪算法—eyelike
9,OpenCV 人脸检测级联分类器解读