haar特征分成了四类:边缘特征、线性特征、中心特征和对角线特征,这些特征组成了特征模板。这些模板的特征值就是白色矩形区域像素值之和减去黑色矩形像素和。haar特征值反映了图像的灰度变化情况。
1)用s(i,j)表示行方向的累加和,初始化s(i,-1)=0;
2)用ii(i,j)表示一个积分图像,初始化ii(-1,i)=0;
3)逐行扫描图像,递归计算每个像素(i,j)行方向的累加和s(i,j)和积分图像ii(i,j)的值。
s(i,j)=s(i,j-1)+f(i,j)
ii(i,j)=ii(i-1,j)+s(i,j)
4)扫描图像一遍,当到达图像右下角像素时,积分图像ii就构造好了。
三、选取弱分类器检测过程中,通过不断的调整检测窗口的位置、比例,来找到人脸。
六、OpenCV算法实现
void detect_and_draw(IplImage* img)
{
double scale = 1.2; //缩放因子
static CvScalar colors[] = {
{ { 0, 0, 255 } }, { { 0, 128, 255 } }, { { 0, 255, 255 } }, { { 0, 255, 0 } },
{ { 255, 128, 0 } }, { { 255, 255, 0 } }, { { 255, 0, 0 } }, { { 255, 0, 255 } }
};//Just some pretty colors to draw with
//Image Preparation
//
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);
cvCvtColor(img, gray, CV_BGR2GRAY); //将待检测彩色图像转化成灰度图像
cvResize(gray, small_img, CV_INTER_LINEAR);//缩放灰度图像到small_img
cvEqualizeHist(small_img, small_img); //直方图均衡
//Detect objects if any
//
cvClearMemStorage(storage);
double t = (double)cvGetTickCount(); //opencv中测量运行时间的函数
CvSeq* objects = cvHaarDetectObjects(small_img, //检测人脸的核心函数
cascade,
storage,
1.1,
3,
0/*CV_HAAR_DO_CANNY_PRUNING*/,
cvSize(30, 30),
cvSize(200,200));
t = (double)cvGetTickCount() - t;
printf("detection time = %gms\n", t / ((double)cvGetTickFrequency()*1000.));
//Loop through found objects and draw boxes around them
for (int i = 0; i < (objects ? objects->total : 0); ++i) //画矩形
{
CvRect* r = (CvRect*)cvGetSeqElem(objects, i); //返回检测到的序列中元素指针,并将指针转换成序列中实际存储的元素的类型
cvRectangle(img, cvPoint(r->x*scale, r->y*scale), cvPoint((r->x + r->width)*scale, (r->y + r->height)*scale), colors[i % 8]);
}
for (int i = 0; i < (objects ? objects->total : 0); i++) //画圆
{
CvRect* r = (CvRect*)cvGetSeqElem(objects, 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, colors[i % 8], 3, 8, 0);
}
cvShowImage("result", img);
cvReleaseImage(&gray);
cvReleaseImage(&small_img);
}
关键函数解析:
人脸检测的关键函数:
CvHaarClassifier(Cascade* cascade, CvMemStorage* storage, double scale_factor=1.1, int min_neighbors=3, int flags=0, CvSize min_size=cvSize(0,0) );
image 被检图像
cascade harr 分类器级联的内部标识形式
storage 用来存储检测到的一序列候选目标矩形框的内存区域。
scale_factor 在前后两次相继的扫描中,搜索窗口的比例系数。例如1.1指将搜索窗口依次扩大10%。
min_neighbors 构成检测目标的相邻矩形的最小个数(缺省-1)。如果组成检测目标的小矩形的个数和小于 min_neighbors-1 都会被排除。
如果min_neighbors 为 0, 则函数不做任何操作就返回所有的被检候选矩形框,这种设定值一般用在用户自定义对检测结果的组合程序上。
flags 操作方式。当前唯一可以定义的操作方式是 CV_HAAR_DO_CANNY_PRUNING。如果被设定,函数利用Canny边缘检测器来排除一些边缘很少或者很多的图像区域,因为这样的区域一般不含被检目标。人脸检测中通过设定阈值使用了这种方法,并因此提高了检测速度。
min_size 检测窗口的最小尺寸。缺省的情况下被设为分类器训练时采用的样本尺寸(人脸检测中缺省大小是~20×20)。
函数 cvHaarDetectObjects 使用针对某目标物体训练的级联分类器在图像中找到包含目标物体的矩形区域,并且将这些区域作为一序列的矩形框返回。函数以不同比例大小的扫描窗口对图像进行几次搜索(察看cvSetImagesForHaarClassifierCascade)。 每次都要对图像中的这些重叠区域利用cvRunHaarClassifierCascade进行检测。 有时候也会利用某些继承(heuristics)技术以减少分析的候选区域,例如利用 Canny 裁减 (prunning)方法。 函数在处理和收集到候选的方框(全部通过级联分类器各层的区域)之后,接着对这些区域进行组合并且返回一系列各个足够大的组合中的平均矩形。
调节程序中的缺省参数(scale_factor=1.1, min_neighbors=3, flags=0)用于对目标进行更精确同时也是耗时较长的进一步检测。
为了能对视频图像进行更快的实时检测,参数设置通常是: