圖像學習-HOG特徵

特徵描述子(Feature Descriptor)

特徵描述子就是圖像的表示,抽取了有用的信息丟掉了不相關的信息。通常特徵描述子會把一個wh3(寬3,3個channel)的圖像轉換成一個長度爲n的向量/矩陣。比如一副641283的圖像,經過轉換後輸出的圖像向量長度可以是3780。

什麼樣子的特徵是有用的呢?假設我們想要預測一張圖片裏面衣服上面的扣子,釦子通常是圓的,而且上面有幾個洞,那你就可以用邊緣檢測(edge detector),把圖片變成只有邊緣的圖像,然後就可以很容易的分辨了,那麼對於這張圖邊緣信息就是有用的,顏色信息就是沒有用的。而且好的特徵應該能夠區分鈕釦和其它圓形的東西的區別。

方向梯度直方圖(HOG)中,梯度的方向分佈被用作特徵。沿着一張圖片X和Y軸的方向上的梯度是很有用的,因爲在邊緣和角點的梯度值是很大的,我們知道邊緣和角點包含了很多物體的形狀信息。

(HOG特徵描述子可以不侷限於一個長度的,也可以用很多其他的長度,這裏只記錄一種計算方法。)

怎麼計算方向梯度直方圖呢?

我們會先用圖像的一個patch來解釋。

第一步:預處理
Patch可以是任意的尺寸,但是有一個固定的比列,比如當patch長寬比1:2,那patch大小可以是100200, 128256或者10002000但不可以是101205。

這裏有張圖是720475的,我們選100200大小的patch來計算HOG特徵,把這個patch從圖片裏面摳出來,然後再把大小調整成64*128。


第二步:計算梯度圖像
首相我們計算水平和垂直方向的梯度,再來計算梯度的直方圖。可以用下面的兩個kernel來計算,也可以直接用OpenCV裏面的kernel大小爲1的Sobel算子來計算。
在這裏插入圖片描述
調用OpenCV代碼如下:

// C++ gradient calculation.
// Read image
Mat img = imread("bolt.png");
img.convertTo(img, CV_32F, 1/255.0);
 
// Calculate gradients gx, gy
Mat gx, gy; 
Sobel(img, gx, CV_32F, 1, 0, 1);
Sobel(img, gy, CV_32F, 0, 1, 1);

接着,用下面的公式來計算梯度的幅值g和方向theta:

可以用OpenCV的cartToPolar函數來計算:

// C++ Calculate gradient magnitude and direction (in degrees)
Mat mag, angle; 
cartToPolar(gx, gy, mag, angle, 1)

在這裏插入圖片描述
從上面的圖像中可以看到x軸方向的梯度主要凸顯了垂直方向的線條,y軸方向的梯度凸顯了水平方向的梯度,梯度幅值凸顯了像素值有劇烈變化的地方。(注意:圖像的原點是圖片的左上角,x軸是水平的,y軸是垂直的)

圖像的梯度去掉了很多不必要的信息(比如不變的背景色),加重了輪廓。換句話說,你可以從梯度的圖像中還是可以輕而易舉的發現有個人。

在每個像素點,都有一個幅值(magnitude)和方向,對於有顏色的圖片,會在三個channel上都計算梯度。那麼相應的幅值就是三個channel上最大的幅值,角度(方向)是最大幅值所對應的角。

第三步:在8*8的網格中計算梯度直方圖
在這一步,上面的patch圖像會被分割成88大小的網格(如下圖),每個網格都會計算一個梯度直方圖。那爲什麼要分成88的呢?用特徵描述子的一個主要原因是它提供了一個緊湊(compact)/壓縮的表示。一個88的圖像有883=192個像素值,每個像素有兩個值(幅值magnitude和方向direction,三個channel取最大magnitude那個),加起來就是88*2=128,後面我們會看到這128個數如何用一個9個bin的直方圖來表示成9個數的數組。不僅僅是可以有緊湊的表示,用直方圖來表示一個patch也可以更加抗噪,一個gradient可能會有噪音,但是用直方圖來表示後就不會對噪音那麼敏感了。

在這裏插入圖片描述
對於64128的這幅patch來說,88的網格已經足夠大來表示有趣的特徵比如臉,頭等等。
直方圖是有9個bin的向量,代表的是角度0,20,40,60…160。

我們先來看看每個8*8的cell的梯度都是什麼樣子:

在這裏插入圖片描述
中間這個圖的箭頭是梯度的方向,長度是梯度的大小,可以發現箭頭的指向方向是像素強度都變化方向,幅值是強度變化的大小。

右邊的梯度方向矩陣中可以看到角度是0-180度,不是0-360度,這種被稱之爲"無符號"梯度(“unsigned” gradients)因爲一個梯度和它的負數是用同一個數字表示的,也就是說一個梯度的箭頭以及它旋轉180度之後的箭頭方向被認爲是一樣的。那爲什麼不用0-360度的表示呢?在事件中發現unsigned gradients比signed gradients在行人檢測任務中效果更好。一些HOG的實現中可以讓你指定signed gradients。

下一步就是爲這些8*8的網格創建直方圖,直方圖包含了9個bin來對應0,20,40,…160這些角度。

下面這張圖解釋了這個過程。我們用了上一張圖裏面的那個網格的梯度幅值和方向。根據方向選擇用哪個bin, 根據副值來確定這個bin的大小。先來看藍色圈圈出來的像素點,它的角度是80,副值是2,所以它在第五個bin裏面加了2,再來看紅色的圈圈出來的像素點,它的角度是10,副值是4,因爲角度10介於0-20度的中間(正好一半),所以把幅值一分爲二地放到0和20兩個bin裏面去。

在這裏插入圖片描述
這裏有個細節要注意,如果一個角度大於160度,也就是在160-180度之間,我們知道這裏角度0,180度是一樣的,所以在下面這個例子裏,像素的角度爲165度的時候,要把幅值按照比例放到0和160的bin裏面去。

在這裏插入圖片描述
把這8*8的cell裏面所有的像素點都分別加到這9個bin裏面去,就構建了一個9-bin的直方圖,上面的網格對應的直方圖如下:
在這裏插入圖片描述
這裏,在我們的表示中,Y軸是0度(從上往下)。你可以看到有很多值分佈在0,180的bin裏面,這其實也就是說明這個網格中的梯度方向很多都是要麼朝上,要麼朝下。

第四步: 16*16塊歸一化
上面的步驟中,我們創建了基於圖片的梯度直方圖,但是一個圖片的梯度對於整張圖片的光線會很敏感。如果你把所有的像素點都除以2,那麼梯度的幅值也會減半,那麼直方圖裏面的值也會減半,所以這樣並不能消除光線的影響。所以理想情況下,我們希望我們的特徵描述子可以和光線變換無關,所以我們就想讓我們的直方圖歸一化從而不受光線變化影響。

先考慮對向量用l2歸一化的步驟是:
v = [128, 64, 32]
[(128^2) + (64^2) + (32^2) ]^0.5=146.64
把v中每一個元素除以146.64得到[0.87,0.43,0.22]
考慮另一個向量2*v,歸一化後可以得到向量依舊是[0.87, 0.43, 0.22]。你可以明白歸一化是把scale給移除了。

你也許想到直接在我們得到的91的直方圖上面做歸一化,這也可以,但是更好的方法是從一個1616的塊上做歸一化,也就是4個91的直方圖組合成一個361的向量,然後做歸一化,接着,窗口再朝後面挪8個像素(看動圖)。重複這個過程把整張圖遍歷一邊
在這裏插入圖片描述
第五步:計算HOG特徵向量
爲了計算這整個patch的特徵向量,需要把36*1的向量全部合併組成一個巨大的向量。向量的大小可以這麼計算:

我們有多少個1616的塊?水平7個,垂直15個,總共有715=105次移動。
每個1616的塊代表了361的向量。所以把他們放在一起也就是36105=3780維向量。
可視化HOG
通常HOG特徵描述子是畫出8
8網格中9*1歸一化的直方圖,見下圖。你可以發現直方圖的主要方向捕捉了這個人的外形,特別是軀幹和腿。

在這裏插入圖片描述
轉載原網址https://www.jianshu.com/p/395f0582c5f7

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章