1、简介
HOG(Histograms of Oriented Gradients,方向梯度直方图)最初是在2005年的CVPR会议上由法国研究员Dala提出来的,主要用来进行行人检测。目前HOG+SVM已经被广泛应用到目标检测和分类等领域。博主主要用HOG+SVM方法进行手势分类,原文请参考:“Histograms of Oriented Gradients for Human Detection“。
2、算法实现
核心思想:
①将图像检测窗口分成许多空间区域单元(cells),在每一个cell中计算一维梯度方向或者边缘方向直方图;
②若干个cells组成一个block,一个block中的梯度方向直方图是由这若干个cells的梯度方向直方图组成,为了
对光照、阴影等鲁棒,需要对block进行对比度归一化处理;
③实际上所有的block之间是有重叠的(重叠的区域为一个cell,后面会具体讲道),将检测窗口的所有blocks的
梯度直方图连接起来就形成了整个检测窗口的梯度方向直方图(HOG)描述子。
具体过程:
(1)Gamma/Colour Normalization
Gamma校正方式为 I(x,y) = [ I(x,y) ] ^Gamma。基于人眼处理图像的方式,一般取Gamma=0.5。此过程主要是为了调节图像对比度、减少光照影响、抑制噪声干扰。
原文提到:在灰度图像、RGB图像和LAB色彩空间进行的Gamma校正对结果只有少许的影响,是因为在后续阶段对block进行了归一化处理。对每一个颜色通道进行的颜色归一化处理结果并不理想,所以一般对灰度图像进行Gamma校正即可。
(2)Gradient Computation
为了降低灰度图像的早点,线采用离散高斯平滑模板进行平滑,高斯函数在不同平滑的尺度下进行对灰度图像进行平滑操作,然后采用[-1 ,0 ,1]的差分模板进行梯度计算。求导操作不仅能够捕获轮廓,人影和一些纹理信息,还能进一步弱化光照的影响。水平和垂直方向上梯度计算公式如下:
原文提到:人体检测效果最佳(即不做高斯平滑),使得错误率缩小了约一倍。不做平滑操作,可能原因:图像是基于边缘的,平滑会降低边缘信息的对比度,从而减少图像中的信号信息。对于彩色图像,分别计算每个颜色通道的梯度,然后选择最大范数的梯度作为该像素的梯度向量。
(3)Spatial / Orientation Binning
方向角范围可以为[0° 180°],也可以为[0° 360°],方向角计算公式为:
原文提到:权重计算可以为幅值本身,幅值平方,幅值平方根和二值边缘表述。文中实验结果显示为梯度幅值本身时效果最好(然而在实际使用中与采用上式的权重计算公式并没有卵的差别)。为了减少走样,相邻的bins的投票在方向和位置上采取双线性插值方法。而且方向角为[0° 180°]是效果最好。
那么将180°分为多少个bins时效果最好呢?
实验表明:将180°分为9个bins时效果最好,也就是说每个bin包括20°的角度范围。
原文提到:对于行人检测来说,由于衣服和背景的颜色变化范围较大,可能会导致[0° 360°]的效果差些。或许在目标任务识别(轿车,摩托车)等方面,[0° 360°]范围的效果较好。
(4)Normalization and Descriptor Blocks
由于局部光照和前后景的对比,梯度强度变化范围会很大,所以有效的对比度归一化可以提高效果。选取的block之间有重叠,所以每一个标量cell响应都会对最终的描述子向量有所贡献,不同的blocks需要归一化。
block的选取哪种方式好呢?
原文提到:选取正方形的cell结果较好,而且当每个cell包含6×6~8×8,每个bolck包含2×2~3×3时效果最好。
bolck该选择什么方式归一化呢?
常见的归一化形式有:
该算子的关键在于行人轮廓与背景之间的对比,而不是内部边缘或者轮廓与前景的对比,所以行人穿着或者姿势变化时对该算子影响不大。
测试代码如下:
im = imread('1(1).jpg');
if size(im,3) == 3
im = rgb2gray(im);
end
im = double(im);
rows = size(im,1);
cols = size(im,2);
%h = fspecial('gaussian');
%im = imfilter(im,h);
%figure,imshow(im,[]);
im = im.^0.5; % gamma calibration
%figure,imshow(im,[]);
Ix = im;
Iy = im;
for i = 1:rows-2 % Calculate the gradient in X and Y direction
Iy(i,:) = im(i+2,:)-im(i,:);
end
Iy(rows-1,:) = Iy(rows-2,:);
Iy(rows,:) = Iy(rows-2,:);
for j = 1:cols-2
Ix(:,j) = im(:,j+2)-im(:,j);
end
Ix(:,cols-1) = Ix(:,cols-2);
Ix(:,cols) = Ix(:,cols-2);
angle = atand(Iy./Ix);% Calculate the angle and magnitude of
angle = imadd(angle,90);
magtu = sqrt(Ix.^2+Iy.^2);
angle(isnan(angle)) = 0;% Remove NaN
magtu(isnan(magtu)) = 0;
%figure,imshow(angle,[]);
%figure,imshow(magtu,[]);
hog_feature = [];
%Find every block
for i = 1:rows/8-1
for j = 1:cols/8-1
angle_block = angle((i-1)*8+1:(i-1)*8+16,(j-1)*8+1:(j-1)*8+16);
magtu_block = magtu((i-1)*8+1:(i-1)*8+16,(j-1)*8+1:(j-1)*8+16);
%magtu_block = imfilter(mag_block,gaussian);
block_feature = [];
%Find every cell in a block
for ii = 1:2
for jj = 1:2
angle_cell = angle_block((ii-1)*8+1:(ii-1)*8+8,(jj-1)*8+1:(jj-1)*8+8);
magtu_cell = magtu_block((ii-1)*8+1:(ii-1)*8+8,(jj-1)*8+1:(jj-1)*8+8);
%Find every pixel's belonged bins
hist_cell = zeros(1,9);
for p = 1:8
for q = 1:8
% Bi_linear Interpolation
if angle_cell(p,q)>10 && angle_cell(p,q)<=30
hist_cell(1)=hist_cell(1)+magtu_cell(p,q)*(30-angle_cell(p,q))/20;
hist_cell(2)=hist_cell(2)+magtu_cell(p,q)*(angle_cell(p,q)-10)/20;
elseif angle_cell(p,q)>30 && angle_cell(p,q)<=50
hist_cell(2)=hist_cell(2)+magtu_cell(p,q)*(50-angle_cell(p,q))/20;
hist_cell(3)=hist_cell(3)+magtu_cell(p,q)*(angle_cell(p,q)-30)/20;
elseif angle_cell(p,q)>50 && angle_cell(p,q)<=70
hist_cell(3)=hist_cell(3)+magtu_cell(p,q)*(70-angle_cell(p,q))/20;
hist_cell(4)=hist_cell(4)+magtu_cell(p,q)*(angle_cell(p,q)-50)/20;
elseif angle_cell(p,q)>70 && angle_cell(p,q)<=90
hist_cell(4)=hist_cell(4)+magtu_cell(p,q)*(90-angle_cell(p,q))/20;
hist_cell(5)=hist_cell(5)+magtu_cell(p,q)*(angle_cell(p,q)-70)/20;
elseif angle_cell(p,q)>90 && angle_cell(p,q)<=110
hist_cell(5)=hist_cell(5)+magtu_cell(p,q)*(110-angle_cell(p,q))/20;
hist_cell(6)=hist_cell(6)+magtu_cell(p,q)*(angle_cell(p,q)-90)/20;
elseif angle_cell(p,q)>110 && angle_cell(p,q)<=130
hist_cell(6)=hist_cell(6)+magtu_cell(p,q)*(130-angle_cell(p,q))/20;
hist_cell(7)=hist_cell(7)+magtu_cell(p,q)*(angle_cell(p,q)-110)/20;
elseif angle_cell(p,q)>130 && angle_cell(p,q)<=150
hist_cell(7)=hist_cell(7)+magtu_cell(p,q)*(150-angle_cell(p,q))/20;
hist_cell(8)=hist_cell(8)+magtu_cell(p,q)*(angle_cell(p,q)-130)/20;
elseif angle_cell(p,q)>150 && angle_cell(p,q)<=170
hist_cell(8)=hist_cell(8)+magtu_cell(p,q)*(170-angle_cell(p,q))/20;
hist_cell(9)=hist_cell(9)+magtu_cell(p,q)*(angle_cell(p,q)-150)/20;
elseif angle_cell(p,q)>170 && angle_cell(p,q)<=180
hist_cell(9)=hist_cell(9)+magtu_cell(p,q)*(190-angle_cell(p,q))/20;
hist_cell(1)=hist_cell(1)+magtu_cell(p,q)*(angle_cell(p,q)-170)/20;
elseif angle_cell(p,q)>0 && angle_cell(p,q)<=10
hist_cell(1)=hist_cell(1)+magtu_cell(p,q)*(angle_cell(p,q)+10)/20;
hist_cell(9)=hist_cell(9)+magtu_cell(p,q)*(10-angle_cell(p,q))/20;
end
end
end
block_feature = [block_feature hist_cell];
end
end
% Normalize block_feature using L2-norm
block_feature = block_feature/sqrt(norm(block_feature)^2+0.00001);
hog_feature = [hog_feature block_feature];
end
end
hog_feature(isnan(hog_feature)) = 0;
%Normalize the hog_feature using L2-Hys
hog_feature = hog_feature/sqrt(norm(hog_feature)^2+0.00001);
for z = 1:length(hog_feature)
if hog_feature(z)>0.2
hog_feature(z)=0.2
end
end
hog_feature = hog_feature/sqrt(norm(hog_feature)^2+0.00001);