一、 Haar特征定义
Haar特征是基于“块”的特征,也被称为矩形特征。Haar特征(模板)分为三类:边缘特征、线性特征、中心特征和对角线特征。特征模板内有白色和黑色两种矩形,并定义该模板的特征值为白色矩形像素和减去黑色矩形像素和。Haar特征值反映了图像的灰度变化情况。例如:脸部的一些特征能由矩形特征简单的描述,如:眼睛要比脸颊颜色要深,鼻梁两侧比鼻梁颜色要深,嘴巴比周围颜色要深等。但矩形特征只对一些简单的图形结构,如边缘、线段较敏感,所以只能描述特定走向(水平、垂直、对角)的结构。
对于图中的1.a这类特征,特征数值计算公式为:v=Sum白-Sum黑,而对于2.c这类来说,计算公式如下:v=Sum白-2*Sum黑;之所以将黑色区域像素和乘以2,是为了使两种矩形区域中像素数目一致。
Haar特征数量:
通过改变特征模板的大小和位置,可在图像子窗口中穷举出大量的特征。上图的特征模板称为“特征原型”;特征原型在图像子窗口中扩展(平移伸缩)得到的特征称为“矩形特征”;矩形特征的值称为“特征值”。
矩形特征可位于图像任意位置,大小也可以任意改变,所以矩形特征值是矩形模版类别、矩形位置和矩形大小这三个因素的函数。故类别、大小和位置的变化,使得很小的检测窗口含有非常多的矩形特征。对于一个给定的24X24的窗口,根据不同的位置,以及不同的缩放,可以产生超过160,000个特征,因此计算一幅图像的所有Haar特征是一件工作量很大的事。
二、Haar特征的快速计算-积分图
图中的大框是积分图,为了讲解如何计算任意矩形内的像素值,我们画出四个区域A、B、C、D,并且图中有四个位置分别为1、2、3、4。我们要计算D区域内部的像素和该怎么计算?
我们记位置4的左上的所有像素为rectsum(4),那么
D位置的像素之和就是rectsum(4)−rectsum(2)−rectsum(3)+rectsum(1)。
matlab实现积分图像:
src = imread('2.jpg');
imshow(src);
src = rgb2gray(src);
src = double(src);
[m,n]=size(src);
I=zeros(m,n);
for i=1:m
for j=1:n
if i==1 && j==1 %积分图像左上角
I(i,j)=src(i,j);
elseif i==1 && j~=1 %积分图像第一行
I(i,j)=I(i,j-1)+src(i,j);
elseif i~=1 && j==1 %积分图像第一列
I(i,j)=I(i-1,j)+src(i,j);
else %积分图像其它像素
I(i,j)=src(i,j)+I(i-1,j)+I(i,j-1)-I(i-1,j-1);
end
end
end
figure,imshow(I,[]);
c++实现积分图像:
#include
using namespace cv;
using namespace std;
int main(int arc, char** argv) {
Mat src = imread("2.jpg",IMREAD_GRAYSCALE);
namedWindow("input");
imshow("input", src);
Mat result = Mat::zeros(src.size(), CV_32FC1);
for (int row = 0; row < src.rows; row++) {
for (int col = 0; col < src.cols; col++) {
if ((row == 0) && (col == 0)) {
result.at(row, col) = src.at(row, col);
}
else if (row == 0 && col != 0) {
result.at(row, col) = src.at(row, col) + result.at(row, col - 1);
}
else if (row != 0 && col == 0) {
result.at(row, col) = src.at(row, col) + result.at(row - 1, col);
}
else {
result.at(row, col) = src.at(row, col) + result.at(row - 1, col)
+ result.at(row, col - 1) - result.at(row - 1, col - 1);
}
}
}
normalize(result, result, 0, 255, NORM_MINMAX,CV_8UC1);
imshow("output", result);
waitKey(0);
return 0;
}
opencv实现积分图像:
#include
using namespace cv;
using namespace std;
int main(int arc, char** argv) {
Mat src;
src = imread("2.jpg",IMREAD_GRAYSCALE);
namedWindow("input", CV_WINDOW_AUTOSIZE);
imshow("input", src);
//opencv生成的积分图尺寸比原图尺寸行列各要大一个像素,用opencv的API遵循它的规则即可,不然也可自己写
Mat sum_img = Mat::zeros(src.rows + 1, src.cols + 1, CV_32FC1);
integral(src, sum_img, CV_32FC1);
normalize(sum_img, sum_img, 0, 255, NORM_MINMAX,CV_8UC1);
imshow("integral_img", sum_img);
waitKey(0);
return 0;
}
参考文献:https://blog.csdn.net/shuzfan/article/details/44676735
https://blog.csdn.net/xizero00/article/details/46929261