关于Opencv实现人脸检测的阐述
最近用Opencv开发一个人脸检测的小程序,结构并不复杂,但对于Opencv初学者来说还是具有一定的引导意义。接下来对于程序开发中出现的一些问题进行简单讨论。
一、图像采集。图像既可以从摄像头设备中读取,也可以从磁盘中加载,两者方法大同小异。以摄像头为例,Opencv对于摄像头的操作同matlab中一样,是通过一个简单的API函数实现的,具体如下:
CvCapture* m_pCapture;
m_pCapture = cvCreateCameraCapture(0);
IplImage* m_pFrameImage;
m_pFrameImage = cvQueryFrame(m_pCapture);
首先是创建一个视频流结构体指针m_pCapture,之后调用cvCreateCameraCapture(0)函数将结构体与相应视频输入设备关联,若只有一个视频输入设备(如笔记本摄像头),建议参数给0,若有多个视频设备则应给对应的ID号。关联完成后,调用cvQueryFrame(m_pCapture);得到m_pCapture结构体中视频流的下一帧图像,存储在图像指针对应的区域,至此,完成摄像头图像采集。
从磁盘中读取图像过程相对复杂一点,需要用到MFC中关于文件及文件夹读取的知识。具体讲用两条途径,一是定位指定文件夹,继而读取文件夹下的所有图像文件;二是直接定位文件,继而读取相应文件。读取文件夹的具体代码如下:
BROWSEINFO bi;//用来存储用户选中的目录信息
TCHAR name[MAX_PATH];//存储路径
name[0]='d';
ZeroMemory(&bi,sizeof(BROWSEINFO));//清空目录对应的内存
bi.hwndOwner=GetSafeHwnd();//得到窗口句柄
bi.pszDisplayName=name;
BIF_BROWSEINCLUDEFILES;//这句话是什么意思
bi.lpszTitle=_T("Selectfolder");//对话框标题
bi.ulFlags=0x80;//设置对话框形式
LPITEMIDLISTidl=SHBrowseForFolder(&bi);//返回所选中文件夹的ID
if(idl==NULL)
return;
SHGetPathFromIDList(idl,str.GetBuffer(MAX_PATH));//将文件信息格式化存储到对应缓冲区中
str.ReleaseBuffer();//与GerBuffer配合使用,清空内存
m_Path=str;//将路径存储在m_path中
if(str.GetAt(str.GetLength()-1)!='\\')
m_Path+="\\";
UpdateData(FALSE);
文件夹读取过程中关键函数为SHBrowseForFolder,这个函数有什么样作用以及具体用法网上都有具体的帖子和博客进行说明,这里不做赘述,最终文件夹路径存储在变量m_Path中。
相比之下,直接读取文件要简单得多,主要是运用MFC中的CFileDialog类,具体代码如下:
CFileDialog FDlg(TRUE);
if(FDlg.DoModal() == IDOK)
{
m_Path = FDlg.GetPathName();
UpdateData(false);
}
同样,接下来只需调用IplImage* image= cvLoadImage(m_Path)将图像加载到内存中即可。
二、人脸检测
首先基于Opencv的人脸检测很重要的一类方法就是AdaBoost,这也是目前检测效率比价高、速度比较快的一类方法。在Opencv中使用AdaBoost方法的优势在于它已经将算法高度集成化,只需开发者调用一些API函数即可,甚至对AdaBoost算法的本质原理不太明白也没关系,这里对AdaBoost算法的原理不在赘述,网上有很多分析透彻的帖子。
在进行AdaBoost人脸检测的过程中第一步是训练过程,即通过对已知样本的训练得到强分类器。由于成熟的AdaBoost训练算法往往需要大量的人脸和非人脸样本,训练时间也往往长达数周之久,因此在Opencv中已经将训练好的弱分类器存储在Opencv安装目录下的data\\haarcascades文件夹下的XML文件中里面有训练好的各种各样的分类器,包括人脸、眼睛、嘴、鼻子等等,依据需要检测的目标来加载对应的强分类器即可,强分类器的加载方法如下:
const char*cascade_name= "对应XML文件的全路径"
static CvMemStorage*storage = 0;
static CvHaarClassifierCascade*cascade = 0;
cascade =cvLoadHaarClassifierCascade(cascade_name,cvSize(30,30));//用这个函数加载
storage =cvCreateMemStorage(0);//内存置零
强调,在加载分类器是一定要对应申请内存块storage,这在后面通过强分类器检测人脸的过程中将会用到。可见,在AdaBoost算法实现过程总Opencv替我们做了很多工作,强分类器的训练这一工作量很大的步骤就可以省略了。当然我们也可以通过自己的训练样本来训练出自己的强分类,具体方法在以后的文章中会介绍。
在完成弱分类的加载之后即开始对图像进行检测,用到的核心检测函数如下:
CvSeq* objects =cvHaarDetectObjects(small_img,//待检测图像
cascade,//分类器标识
storage,//存储检测到的候选矩形
1.2,//相邻两次检测中窗口扩大的比例
3,//认为是人脸的最小矩形数(阈值)
0/*CV_HAAR_DO_CANNY_PRUNING*/,
cvSize(30,30));//初始检测窗口大小
这段代码是取自一段人脸检测程序中,因此对cvHaarDetectObjects这个函数的各个参数都进行了赋值,其中small_img为待检测的图像指针,cascade为已经加载的强分类器,storage为开辟好的内存区域,scale_factor= 1.2代表相邻两次检测中窗口扩大的比例,一般来说这个参数要大于1,且它的值越大检测速度越快,但检测的准确度将会下降。min_neighbors=3
表示能够构成人脸的相邻最小矩形数,大于等于1且为整数,一般需要取一个合适的值才能达到较好的检测效果flags = 0代表检测方式当前唯一可以定义的操作方式是 CV_HAAR_DO_CANNY_PRUNING。如果被设定,函数利用Canny边缘检测器来排除一些边缘很少或者很多的图像区域,因为这样的区域一般不含被检目标。min_size = cvSize(30,30)代表 检测窗口的最小尺寸,一般取训练样本的尺寸即可。
这个函数的返回值是一些的矩形元素,存储在可动态增长序列结构体CvSeq* objects中,在显示时可直接从中取出元素进行矩形框的绘制,具体代码如下
if(objects->total> 0)//如果人脸检测成功
{
doublescale1=2 - scale;
CvRect*r=(CvRect*)cvGetSeqElem(objects,i);//从结果序列中提取第i个矩形元素
CvScalar(0,0,255))
}
强调,这里人脸检测算法是可以检测多个人脸的,如果需要只检测图中目标最大的人脸,只需将这些得到的矩形分别求面积,取面积最大的矩形即可。