一、Haar-like特征
Haar-like特征最早是由Papageorgiou等应用于人脸表示,Viola和Jones在此基础上,使用3种类型4种形式的特征。Haar特征分为四类:边缘特征、线性特征、中心特征和对角线特征【也可以分成三类:边缘特征、线性特征(包含对角线特征)、点特征(中心特征)】,组合成特征模板。特征模板内有白色和黑色两种矩形,并定义该模板的特征值为白色矩形像素和减去黑色矩形像素和。Haar特征值反映了图像的灰度变化情况。例如:脸部的一些特征能由矩形特征简单的描述,如:眼睛要比脸颊颜色要深,鼻梁两侧比鼻梁颜色要深,嘴巴比周围颜色要深等。但矩形特征只对一些简单的图形结构,如边缘、线段较敏感,所以只能描述特定走向(水平、垂直、对角)的结构。
对于图中的A, B和D这类特征,特征数值计算公式为:v=Sum白-Sum黑,而对于图C来说,计算公式如下:v=Sum白-2*Sum黑;之所以将黑色区域像素和乘以2,是为了使两种矩形区域中像素数目一致。
通过改变特征模板的大小和位置,可在图像子窗口中穷举出大量的特征。上图的特征模板称为“特征原型”;特征原型在图像子窗口中扩展(平移伸缩)得到的特征称为“矩形特征”;矩形特征的值称为“特征值”。
矩形特征可位于图像任意位置,大小也可以任意改变,所以矩形特征值是矩形模版类别、矩形位置和矩形大小这三个因素的函数。故类别、大小和位置的变化,使得很小的检测窗口含有非常多的矩形特征,如:在24*24像素大小的检测窗口内矩形特征数量可以达到16万个。这样就有两个问题需要解决了:
(1)如何快速计算那么多的特征?---积分图大显神通;
(2)哪些矩形特征才是对分类器分类最有效的?---如通过AdaBoost算法来训练
二、Haar-like特征的计算方法—积分图
2.1 积分图
积分图就是只遍历一次图像就可以求出图像中所有区域像素和的快速算法,大大的提高了图像特征值计算的效率。
积分图主要的思想是将图像从起点开始到各个点所形成的矩形区域像素之和作为一个数组的元素保存在内存中,当要计算某个区域的像素和时可以直接索引数组的元素,不用重新计算这个区域的像素和,从而加快了计算(这有个相应的称呼,叫做动态规划算法)。积分图能够在多种尺度下,使用相同的时间(常数时间)来计算不同的特征,因此大大提高了检测速度。
我们来看看它是怎么做到的。
积分图是一种能够描述全局信息的矩阵表示方法。积分图的构造方式是位置(i,j)处的值ii(i,j)是原图像(i,j)左上角方向所有像素的和:
2.2 积分图构建算法:
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就构造好了。
2.3 任意矩形区域内像素积分。
由图像的积分图可方便快速地计算图像中任意矩形内所有像素灰度积分。如下图所示,点1的积分图像ii1的值为(其中Sum为求和) : ii1=Sum(A)
同理,点2、点3、点4的积分图像分别为:
ii2=Sum(A)+Sum(B); ii3=Sum(A)+Sum(C); ii4=Sum(A)+Sum(B)+Sum(C)+Sum(D);
矩形区域D内的所有像素灰度积分可由矩形端点的积分图像值得到:
Sum(D)=ii1+ii4-(ii2+ii3) (1)
2.4 特征值计算
矩形特征的特征值是两个不同的矩形区域像素和之差,由(1)式可以计算任意矩形特征的特征值,下面以图2.4中特征原型A为例说明特征值的计算。
如图2.4 所示,该特征原型的特征值定义为:
Sum(A)-Sum(B)
根据(1)式则有:
Sum(A)=ii4+ii1-(ii2+ii3); Sum(B)=ii6+ii3-(ii4+ii5);
所以此类特征原型的特征值为:
(ii4-ii3)-(ii2-ii1)+(ii4-ii3)-(ii6-ii5)
另示:运用积分图可以快速计算给定的矩形之所有象素值之和Sum(r)。假设r=(x,y,w,h),那么此矩形内部所有元素之和等价于下面积分图中下面这个式子:
Sum(r) = ii(x+w,y+h)+ii(x-1,y-1)-ii(x+w,y-1)-ii(x-1,y+h)
而Haar-like特征值无非就是两个矩阵像素和的差,同样可以在常数时间内完成。所以矩形特征的特征值计算,只与此特征矩形的端点的积分图有关,所以不管此特征矩形的尺度变换如何,特征值的计算所消耗的时间都是常量。这样只要遍历图像一次,就可以求得所有子窗口的特征值。
三、Haar-like矩形特征拓展
Lienhart R.等对Haar-like矩形特征库作了进一步扩展:加入了旋转45,角的矩形特征。扩展后的特征大致分为4种类型:边缘特征、线特征环、中心环绕特征和对角线特征:
在特征值的计算过程中,黑色区域的权值为负值,白色区域的权值为正值。而且权值与矩形面积成反比(使两种矩形区域中像素数目一致);
3.1特征值计算
1)竖直矩阵特征值计算:
对于竖直矩阵,与上面2处说的一样。
2)45°旋角的矩形特征计算:(表示此段没太看懂)
对于45°旋角的矩形,我们定义RSAT(x,y)为点(x,y)左上角45°区域和左下角45°区域的像素和。
用公式可以表示为:
为 了节约时间,减少重复计算,可按如下递推公式计算:
RSAT(x,y) = RSAT(x-1,y-1)+RSAT(x-1,y+1)-RSAT(x-2,y)+I(x,y)+I(x-1,y)
而计算矩阵特征的特征值,是位于十字行矩形RSAT(x,y)之差。可参考下图:
四、代码实现
4.1 C++ OpenCV
1)原图:
2)代码:
//Haar人脸检测
void HaarFindFace()
{
//加载Haar特征检测分类器
const string CascadeFileName = "E:\\tools\\opencv\\opencv2.13\\sources\\data\\haarcascades\\haarcascade_frontalface_default.xml";
CascadeClassifier cascade = CascadeClassifier::CascadeClassifier(CascadeFileName);
// 载入图像
const string pstrImageName = "D:\\test\\hog.png";
Mat SrcImage = imread(pstrImageName, CV_LOAD_IMAGE_COLOR);
Mat GrayImage;
cvtColor(SrcImage, GrayImage, CV_BGR2GRAY);
// 人脸识别与标记
if (!cascade.empty())
{
CvScalar FaceCirclecolors[] =
{
{ { 0, 0, 255 } },
{ { 0, 128, 255 } },
{ { 0, 255, 255 } },
{ { 0, 255, 0 } },
{ { 255, 128, 0 } },
{ { 255, 255, 0 } },
{ { 255, 0, 0 } },
{ { 255, 0, 255 } }
};
vector<cv::Rect> faces;
DWORD dwTimeBegin, dwTimeEnd;
dwTimeBegin = GetTickCount();
// 识别
cascade.detectMultiScale(GrayImage, faces);
dwTimeEnd = GetTickCount();
cout << "人脸个数:" << faces.end() - faces.begin()
<< "识别用时:" << dwTimeEnd - dwTimeBegin << "ms\n";
// 标记
int n = 0;
for (vector<cv::Rect>::const_iterator i = faces.begin(); i <faces.end(); i++, n++)
{
Point center;
int radius;
center.x = cvRound((i->x + i->width * 0.5));
center.y = cvRound((i->y + i->height * 0.5));
radius = cvRound((i->width + i->height) * 0.25);
circle(SrcImage, center, radius, FaceCirclecolors[n % 8], 2);
}
}
namedWindow("人脸识别");
imshow("人脸识别", SrcImage);
cvWaitKey(0);
return;
}
//-----开始------
void COpenCVLearningDlg::OnBnClickedStartButton()
{
HaarFindFace();
}
3)代码结果:
4.2 Python OpenCV
1)原图:
2)代码:
import cv2
OPENCV_PATH = r"E:\tools\opencv\opencv3.4.1\opencv\sources"
face_cascade = cv2.CascadeClassifier(OPENCV_PATH + '/data/haarcascades/haarcascade_frontalface_default.xml')
img = cv2.imread('D:/test/haar.jpg')
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
faces = face_cascade.detectMultiScale(gray, 1.7, 2)
for (x,y,w,h) in faces:
cv2.rectangle(img,(x,y),(x+w,y+h),(255,0,0),2)
cv2.imwwrite("D://HarrResult.jpg",img)
# cv2.imshow('image',img)
# cv2.waitKey(0)
# cv2.destroyAllWindows()
3)代码结果:
五、参考文章
[1]https://blog.csdn.net/smf0504/article/details/51322255
[2]https://blog.csdn.net/zouxy09/article/details/7929570
[3]https://blog.csdn.net/u011776903/article/details/73798654
[4]http://lib.csdn.net/article/opencv/33210
[5]https://blog.csdn.net/qq_24946843/article/details/82664548