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);