方向梯度直方图(Histogram Of Gradient)详解
特征描述子(Feature Descriptor)
特征描述子就是圖像的表示,抽取了有用的信息,丟掉了不相關的信息。通常特征描述子會把一個w*h*3(寬高3,3個channel)的圖像轉換成一個長度為n的向量/矩陣。比如一副64*128*3的圖像,經過轉換后輸出的圖像向量長度可以是3780。
什么樣子的特征是有用的呢?假設我們想要預測一張圖片里面衣服上面的扣子,扣子通常是圓的,而且上面有幾個洞,那你就可以用邊緣檢測(edge detector),把圖片變成只有邊緣的圖像,然后就可以很容易的分辨了,那么對于這張圖邊緣信息就是有用的,顏色信息就是沒有用的。而且好的特征應該能夠區分紐扣和其它圓形的東西的區別。
方向梯度直方圖(HOG)中,梯度的方向分布被用作特征。沿著一張圖片X和Y軸的方向上的梯度是很有用的,因為在邊緣和角點的梯度值是很大的,我們知道邊緣和角點包含了很多物體的形狀信息。(HOG特征描述子可以不局限于一個長度,也可以用很多其他的長度,這里只記錄一種計算方法。)
怎么計算方向梯度直方圖呢?
我們會先用圖像的一個patch來解釋。
第一步:預處理
Patch可以是任意的尺寸,但是有一個固定的比例,比如當patch長寬比1:2,那patch大小可以是100*200, 128*256或者1000*2000,但不可以是101*205。
這里有張圖是720*475的,我們選100*200大小的patch來計算HOG特征,把這個patch從圖片里面摳出來,然后再把大小調整成64*128。
第二步:計算梯度圖像
首先我們計算水平和垂直方向的梯度,再來計算梯度的直方圖。可以用下面的兩個kernel來計算,也可以直接用OpenCV里面的kernel大小為1的Sobel算子來計算。
horizontal_vertical_gradient_kernel (水平和垂直梯度)
調用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);
#?Python?gradient?calculation?
#?Read?imageim?=?cv2.imread('bolt.png')
im?=?np.float32(im)?/?255.0
#?Calculate?gradient
gx?=?cv2.Sobel(img,?cv2.CV_32F,?1,?0,?ksize=1)
gy?=?cv2.Sobel(img,?cv2.CV_32F,?0,?1,?ksize=1)
接著,用下面的公式來計算梯度的幅值g和方向theta:
gradient_direction_formula(梯度方向計算)
可以用OpenCV的cartToPolar函數來計算:
//?C++?Calculate?gradient?magnitude?and?direction?(in?degrees)
Mat?mag,?angle;
cartToPolar(gx,?gy,?mag,?angle,?1);
#?Python?Calculate?gradient?magnitude?and?direction?(?in?degrees?)
mag,?angle?=?cv2.cartToPolar(gx,?gy,?angleInDegrees=True)
計算得到的gradient圖如下:
左邊:x軸的梯度絕對值? ? ? ? ?中間:y軸的梯度絕對值? ? ? ? ? ? ?右邊:梯度幅值
從上面的圖像中可以看到x軸方向的梯度主要凸顯了垂直方向的線條,y軸方向的梯度凸顯了水平方向的梯度,梯度幅值凸顯了像素值有劇烈變化的地方。(注意:圖像的原點是圖片的左上角,x軸是水平的,y軸是垂直的)
圖像的梯度去掉了很多不必要的信息(比如不變的背景色),加重了輪廓。換句話說,你可以從梯度的圖像中輕而易舉的發現有個人。在每個像素點,都有一個幅值(magnitude)和方向,對于有顏色的圖片,會在3個channel上都計算梯度。那么相應的幅值就是3個channel上最大的幅值,角度(方向)是最大幅值所對應的角。
第三步:在8*8的網格中計算梯度直方圖
在這一步,我們先把整個圖像劃分為若干個8x8的小單元,稱為cell,并計算每個cell的梯度直方圖。這個cell的尺寸也可以是其他值,根據具體的特征而定。
為什么我們要把圖像分成若干個8x8的小單元?
這是因為對于一整張梯度圖,其中的有效特征是非常稀疏的,不但運算量大,而且效果可能還不好。于是我們就使用特征描述符來表示一個更緊湊(compact)的特征。
一個8*8的圖像有8*8*3=192個像素值(彩色圖有3個channel),每個像素的梯度包括兩個值(幅值magnitude和方向direction,magnitude取3個channel中最大值,然后direction取最大magnitude值對應的direction值),因此一個8x8的小單元(cell)就包含了8*8*2=128個值,因為每個像素包括梯度的大小和方向。
現在我們要把這個8x8的小單元用長度為9的數組來表示,這個數組就是梯度直方圖。這種表示方法不僅使得特征更加緊湊,而且對單個像素值的變化不敏感,也就是能夠抗噪聲干擾。
這個patch的大小是64*128,把它分割成若干個8*8的cell,那么一共有(64/8)*(128/8) = 8*16=128個網格,對于64*128的這幅patch來說,8*8的網格已經足夠大來表示有趣的特征比如臉,頭等等。
直方圖是有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的直方圖,上面的網格對應的直方圖如下:
可以看到直方圖中,0度和160附近有很大的權重,說明了大多數像素的梯度向上或者向下,也就是這個cell是個橫向邊緣。
現在我們就可以用這9個數的梯度直方圖來代替原來很大的三維矩陣,即代替了8x8x2個值。
第四步: 16*16塊(block)歸一化
hog-16x16-block-normalization
在前面的步驟中,我們基于圖像的梯度對每個cell創建了一個直方圖。
但是圖像的梯度對整體光照非常敏感,比如通過將所有像素值除以2來使圖像變暗,那么梯度幅值將減小一半,因此直方圖中的值也將減小一半。 理想情況下,我們希望我們的特征描述符不會受到光照變化的影響,那么我們就需要將直方圖“歸一化” 。
在說明如何歸一化直方圖之前,先看看長度為3的向量是如何歸一化的。
假設我們有一個向量?[128,64,32],向量的長度為,這叫做向量的L2范數。將這個向量的每個元素除以146.64就得到了歸一化向量?[0.87, 0.43, 0.22]。
現在有一個新向量,是第一個向量的2倍 [128x2, 64x2, 32x2],也就是?[256, 128, 64],我們將這個向量進行歸一化,你可以看到歸一化后的結果與第一個向量歸一化后的結果相同。所以,對向量進行歸一化可以消除整體光照的影響。
知道了如何歸一化,現在來對block的梯度直方圖進行歸一化(注意不是cell),一個block有4個直方圖,將這4個直方圖拼接成長度為36的向量,然后對這個向量進行歸一化。
因為使用的是滑動窗口,滑動步長為8個像素,所以每滑動一次,就在這個窗口上進行歸一化計算得到長度為36的向量,并重復這個過程
第五步:計算HOG特征向量
為了計算這整個patch的特征向量,需要把36*1的向量全部合并組成一個巨大的向量。向量的大小可以這么計算:
我們有多少個16*16的塊?水平7個,垂直15個,總共有7*15=105次移動。
每個16*16的塊代表了36*1的向量。所以把他們放在一起也就是36*105=3780維向量。
這個得到的長度3780的向量就可以作為整個圖像的特征描述符。
通常HOG特征描述子是畫出8*8網格中9*1歸一化的直方圖,見下圖。你可以發現直方圖的主要方向捕捉了這個人的外形,特別是軀干和腿。
為了顯示效果更明顯,我把cell的尺寸改為(16, 16),對于每一個cell,畫出它歸一化后的梯度直方圖。如下圖所示,我們可以很明顯的看出一個人的輪廓。
參考:
HOG特征詳解
Histogram of Oriented Gradients
?
總結
以上是生活随笔為你收集整理的方向梯度直方图(Histogram Of Gradient)详解的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: windows下的虚拟机中的ubuntu
- 下一篇: 看看这几个版本Linux系统总有一款适合