详细的基于opencv svm hog的描述讲解
(轉自http://blog.csdn.net/zhazhiqiang/ 未經允許請勿用于商業用途)
一、理論
1、HOG特征描述子的定義:
locally normalised histogram of gradient orientation in dense overlapping grids,即局部歸一化的梯度方向直方圖,是一種對圖像局部重疊區域的密集型描述符, 它通過計算局部區域的梯度方向直方圖來構成特征。
2、本質:
Histogram of Oriented Gradient descriptors provide a dense overlapping description of image regions,即統計圖像局部區域的梯度方向信息來作為該局部圖像區域的表征。
3、OpenCV中的HOG算法來源:
Histograms of Oriented Gradients for Human Detection, CVPR 2005。詳細的算法可以參考這個文章。這里是英文和中文的介紹。4、檢測窗口Win、塊Block、單元格Cell的基本信息
(1)大小:
A、檢測窗口:WinSize=12864像素,在圖像中滑動的步長是8像素(水平和垂直都是)
B、塊:BlockSize=1616像素,在檢測窗口中滑動的步長是8像素(水平和垂直都是)
C、單元格:CellSize=88像素
D、梯度方向:一個Cell的梯度方向分為9個方向,在一個單元格內統計9個方向的梯度直方圖
(2)HOG描述子
OpenCV中一個Hog描述子是針對一個檢測窗口而言的,一個檢測窗口有((128-16)/8+1)((64-16)/8+1)=105個Block,一個Block有4個Cell,一個Cell的Hog描述子向量的長度是9,所以一個檢測窗口的Hog描述子的向量長度是10549=3780維。
HOG特征提取是統計梯度直方圖特征。具體來說就是將梯度方向(0->360°)劃分為9個區間,將圖像化為16x16的若干個block,每個block再化為4個cell(8x8)。對每一個cell,算出每一像素點的梯度方向和模,按梯度方向增加對應bin的值,最終綜合N個cell的梯度直方圖形成一個高維描述子向量。實際實現的時候會有各種插值。
5、算法流程:
(1)灰度化
由于顏色信息作用不大,通常轉化為灰度圖。
(2)標準化gamma空間
為了減少光照因素的影響,首先需要將整個圖像進行規范化(歸一化),這種處理能夠有效地降低圖像局部的陰影和光照變化。
Gamma壓縮公式:
(3)計算圖像每個像素的梯度(包括大小和方向)
計算圖像橫坐標和縱坐標方向的梯度,并據此計算每個像素位置的梯度方向值;求導操作不僅能夠捕獲輪廓,人影和一些紋理信息,還能進一步弱化光照的影響。
梯度算子:水平邊緣算子: [-1, 0, 1] ;垂直邊緣算子: [-1, 0, 1]T
圖像中像素點(x,y)的梯度為:
(4)將圖像分割為小的Cell單元格
由于Cell單元格是HOG特征最小的結構單位,而且其塊Block和檢測窗口Win的滑動步長就是一個Cell的寬度或高度,所以,先把整個圖像分割為一個個的Cell單元格(8*8像素)。
(5)為每個單元格構建梯度方向直方圖【重點】
這步的目的是:統計局部圖像梯度信息并進行量化(或稱為編碼),得到局部圖像區域的特征描述向量。同時能夠保持對圖像中人體對象的姿勢和外觀的弱敏感性。
(6)把單元格組合成大的塊(block),塊內歸一化梯度直方圖【重點】
由于局部光照的變化以及前景-背景對比度的變化,使得梯度強度的變化范圍非常大。這就需要對梯度強度做歸一化。歸一化能夠進一步地對光照、陰影和邊緣進行壓縮。
方法:
(6-1)將多個臨近的cell組合成一個block塊,然后求其梯度方向直方圖向量;
(6-2)采用L2-Norm with Hysteresis threshold方式進行歸一化,即將直方圖向量中bin值的最大值限制為0.2以下,然后再重新歸一化一次;
注意:block之間的是“共享”的,也即是說,一個cell會被多個block“共享”。另外,每個“cell”在被歸一化時都是“block”independent的,也就是說每個cell在其所屬的block中都會被歸一化一次,得到一個vector。這就意味著:每一個單元格的特征會以不同的結果多次出現在最后的特征向量中。(6-3)四種歸一化方法:
作者采用了四中不同的方法對區間進行歸一化,并對結果進行了比較。引入v表示一個還沒有被歸一 化的向量,它包含了給定區間(block)的所有直方圖信息。| | vk | |表示v的k階范數,這里的k去1、2。用e表示一個很小的常數。這時,歸一化因子可以表示如下:L2-norm:L1-norm:L1-sqrt:L2-Hys:它可以通過先進行L2-norm,對結果進行截短(clipping)(即值被限制為v - 0.2v之間),然后再重新歸一化得到。作者發現:采用L2- Hys,L2-norm 和 L1-sqrt方式所取得的效果是一樣的,L1-norm稍微表現出一點點不可靠性。但是對于沒有被歸一化的數據來說,這四種方法都表現出來顯著的改進。(6-4)區間(塊)有兩個主要的幾何形狀——矩形區間(R-HOG)和環形區間(C-HOG)。
A、R-HOG區間(blocks):大體上是一些方形的格子,它可以有三個參數來表征:每個區間中細胞單元的數目、每個細胞單元中像素點的數目、每個細胞的直方圖通道數目。例如:行人檢測的最佳參數設置是:3×3細胞/區間、6×6像素/細胞、9個直方圖通道。則一塊的特征數為:3*3*9;作者還發現,對于R-HOG,在對直方圖做處理之前,給每個區間(block)加一個高斯空域窗口(Gaussian spatial window)是非常必要的,因為這樣可以降低邊緣的周圍像素點(pixels around the edge)的權重。R-HOG是各區間被組合起來用于對空域信息進行編碼(are used in conjunction to encode spatial form information)。B、C-HOG區間(blocks):有兩種不同的形式,它們的區別在于:一個的中心細胞是完整的,一個的中心細胞是被分割的。如右圖所示:作者發現C-HOG的這兩種形式都能取得相同的效果。C-HOG區間(blocks)可以用四個參數來表征:角度盒子的個數(number of angular bins)、半徑盒子個數(number of radial bins)、中心盒子的半徑(radius of the center bin)、半徑的伸展因子(expansion factor for the radius)。通過實驗,對于行人檢測,最佳的參數設置為:4個角度盒子、2個半徑盒子、中心盒子半徑為4個像素、伸展因子為2。前面提到過,對于R-HOG,中間加一個高斯空域窗口是非常有必要的,但對于C-HOG,這顯得沒有必要。C-HOG看起來很像基于形狀上下文(Shape Contexts)的方法,但不同之處是:C-HOG的區間中包含的細胞單元有多個方向通道(orientation channels),而基于形狀上下文的方法僅僅只用到了一個單一的邊緣存在數(edge presence count)。
(6-5)HOG描述符(不同于OpenCV定義):我們將歸一化之后的塊描述符(向量)就稱之為HOG描述符。
(6-6)塊劃分帶來的問題:塊與塊之間是相互獨立的嗎?
答:通常的將某個變量范圍固定劃分為幾個區域,由于邊界變量與相鄰區域也有相關性,所以變量只對一個區域進行投影而對相鄰區域完全無關時會對其他區域產生混疊效應。
分塊之間的相關性問題的解決:方案一:塊重疊,重復統計計算
在重疊方式中,塊與塊之間的邊緣點被重復根據權重投影到各自相鄰塊(block)中,從而一定模糊了塊與塊之間的邊界,處于塊邊緣部分的像素點也能夠給相鄰塊中的方向梯度直方圖提供一定貢獻,從而達到關聯塊與塊之間的關系的作用。Datal對于塊和塊之間相互重疊程度對人體目標檢測識別率影響也做了實驗分析。方案二:線性插值權重分配
有些文獻采用的不是塊與塊重疊的方法,而是采用線性插值的方法來削弱混疊效應。這種方法的主要思想是每個Block都對臨近的Block都有影響,這種影響,我們可以以一種加權方式附加上去。基于線性插值的基本思想,對于上圖四個方向(橫縱兩個45度斜角方向)個進行一次線性插值就可以達到權重分配目的。下面介紹一維線性插值。假設x1和x2是x塊相鄰兩塊的中心,且x1<x<x2。對w(即權重,一般可直接采用該block的直方圖值即h(x))進行線性插值的方法如下式: 其中b在橫縱方向取塊間隔,而在斜45度方向則可采用sqrt(2)倍的塊間隔。(7)生成HOG特征描述向量
將所有“block”的HOG描述符組合在一起,形成最終的feature vector,該feature vector就描述了detect window的圖像內容。
二、OpenCV中HOG的參數與函數說明(HOG鏈接為OpenCV英文,這里為網友翻譯)
注:HOG在OpenCV中的幾個模塊中都有,略有差別,可做參考,OpenCV的官方文檔中只有對GPU模塊的HOG,這里前面幾個函數說明是GPU中的,后面兩個objedetect模塊中。其實我們在使用時用的是objedetect模塊中的HOG。
0、HOGDescriptor結構體
HOGDescriptor結構體的注釋點擊這里(里面包括hog.cpp的詳細注釋)。
1、構造函數
(1)作用:創造一個HOG描述子和檢測器
(2)函數原型:
C++: gpu::HOGDescriptor::HOGDescriptor(Size win_size=Size(64, 128),
Size block_size=Size(16, 16),
Size block_stride=Size(8, 8),
Size cell_size=Size(8, 8),
int nbins=9,
double win_sigma=DEFAULT_WIN_SIGMA,
double threshold_L2hys=0.2,
bool gamma_correction=true,
int nlevels=DEFAULT_NLEVELS
)
(3)參數注釋
<1>win_size:檢測窗口大小。
<2>block_size:塊大小,目前只支持Size(16, 16)。
<3>block_stride:塊的滑動步長,大小只支持是單元格cell_size大小的倍數。
<4>cell_size:單元格的大小,目前只支持Size(8, 8)。
<5>nbins:直方圖bin的數量(投票箱的個數),目前每個單元格Cell只支持9個。
<6>win_sigma:高斯濾波窗口的參數。
<7>threshold_L2hys:塊內直方圖歸一化類型L2-Hys的歸一化收縮率
<8>gamma_correction:是否gamma校正
<9>nlevels:檢測窗口的最大數量
2、getDescriptorSize函數
(1)作用:獲取一個檢測窗口的HOG特征向量的維數
(2)函數原型:
C++: size_t gpu::HOGDescriptor::getDescriptorSize() const
3、getBlockHistogramSize函數
(1)作用:獲取塊的直方圖大小
(2)函數原型:
C++: size_t gpu::HOGDescriptor::getBlockHistogramSize() const
4、setSVMDetector 函數
(1)作用:設置線性SVM分類器的系數
(2)函數原型:
C++: void gpu::HOGDescriptor::setSVMDetector(const vector& detector)
5、getDefaultPeopleDetector 函數
(1)作用:獲取行人分類器(默認檢測窗口大小)的系數(獲得3780維檢測算子)
(2)函數原型:
C++: static vector gpu::HOGDescriptor::getDefaultPeopleDetector()
6、getPeopleDetector48x96 函數
(1)作用:獲取行人分類器(48*96檢測窗口大小)的系數
(2)函數原型:
C++: static vector gpu::HOGDescriptor::getPeopleDetector48x96()
7、getPeopleDetector64x128 函數
(1)作用:獲取行人分類器(64*128檢測窗口大小)的系數
(2)函數原型:
C++: static vector gpu::HOGDescriptor::getPeopleDetector64x128()
8、detect 函數
(1)作用:用沒有多尺度的窗口進行物體檢測
(2)函數原型:
C++: void gpu::HOGDescriptor::detect(const GpuMat& img,
vector& found_locations,
double hit_threshold=0,
Size win_stride=Size(),
Size padding=Size()
)
(3)參數注釋
<1>img:源圖像。只支持CV_8UC1和CV_8UC4數據類型。
<2>found_locations:檢測出的物體的邊緣。
<3>hit_threshold:特征向量和SVM劃分超平面的閥值距離。通常它為0,并應由檢測器系數決定。但是,當系數被省略時,可以手動指定它。
<4>win_stride:窗口步長,必須是塊步長的整數倍。
<5>padding:模擬參數,使得CUP能兼容。目前必須是(0,0)。
9、detectMultiScale 函數(需有setSVMDetector)
(1)作用:用多尺度的窗口進行物體檢測
(2)函數原型:
C++: void gpu::HOGDescriptor::detectMultiScale(const GpuMat& img,
vector& found_locations,
double hit_threshold=0,
Size win_stride=Size(),
Size padding=Size(),
double scale0=1.05,
int group_threshold=2
)
(3)參數注釋
<1>img:源圖像。只支持CV_8UC1和CV_8UC4數據類型。
<2>found_locations:檢測出的物體的邊緣。
<3>hit_threshold:特征向量和SVM劃分超平面的閥值距離。通常它為0,并應由檢測器系數決定。但是,當系數被省略時,可以手動指定它。
<4>win_stride:窗口步長,必須是塊步長的整數倍。
<5>padding:模擬參數,使得CUP能兼容。目前必須是(0,0)。
<6>scale0:檢測窗口增長參數。
<7>group_threshold:調節相似性系數的閾值。檢測到時,某些對象可以由許多矩形覆蓋。 0表示不進行分組。
(4)詳細說明
<1> 得到層數levels
某圖片(530,402)為例,lg(402/128)/lg1.05=23.4 則得到層數為24
<2>循環levels次,每次執行內容如下
HOGThreadData& tdata = threadData[getThreadNum()];
Mat smallerImg(sz, img.type(), tdata.smallerImgBuf.data);
<3>循環中調用以下核心函數
detect(smallerImg, tdata.locations, hitThreshold, winStride, padding);
其參數分別為,該比例下圖像、返回結果列表、門檻值、步長、margin
該函數內容如下:
(a)得到補齊圖像尺寸paddedImgSize
(b)創建類的對象HOGCache cache(this, img, padding, padding, nwindows == 0, cacheStride); 在創建過程中,首先初始化HOGCache::init,包括:計算梯度descriptor->computeGradient、得到塊的個數105、每塊參數個數36。
?獲得窗口個數nwindows,以第一層為例,其窗口數為(530+322-64)/8+ (402+322-128)/8+1 =67*43=2881,其中(32,32)為winStride參數, 也可用(24,16)
(d)在每個窗口執行循環,內容如下:
在105個塊中執行循環,每個塊內容為:通過getblock函數計算HOG特征并 歸一化,36個數分別與算子中對應數進行相應運算;判斷105個塊的總和 s >= hitThreshold 則認為檢測到目標
10、getDescriptors 函數
(1)作用:返回整個圖片的塊描述符 (主要用于分類學習)。
(2)函數原型:
C++: void gpu::HOGDescriptor::getDescriptors(const GpuMat& img,
Size win_stride,
GpuMat& descriptors,
int descr_format=DESCR_FORMAT_COL_BY_COL
)
(3)參數注釋
<1>img:源圖像。只支持CV_8UC1和CV_8UC4數據類型。
<2>win_stride:窗口步長,必須是塊步長的整數倍。
<3>descriptors:描述符的2D數組。
<4>descr_format:描述符存儲格式:
DESCR_FORMAT_ROW_BY_ROW - 行存儲。
DESCR_FORMAT_COL_BY_COL - 列存儲。
11、computeGradient 函數
(1)作用:計算img經擴張后的圖像中每個像素的梯度和角度
(2)函數原型:
void HOGDescriptor::computeGradient(const Mat& img,
Mat& grad,
Mat& qangle,
Size paddingTL,
Size paddingBR
) const
(3)參數注釋
<1>img:源圖像。只支持CV_8UC1和CV_8UC4數據類型。
<2>grad:輸出梯度(兩通道),記錄每個像素所屬bin對應的權重的矩陣,為幅值乘以權值。這個權值是關鍵,也很復雜:包括高斯權重,三次插值的權重,在本函數中先值考慮幅值和相鄰bin間的插值權重。
<3>qangle:輸入弧度(兩通道),記錄每個像素角度所屬的bin序號的矩陣,均為2通道,為了線性插值。
<4>paddingTL:Top和Left擴充像素數。
<5>paddingBR:Bottom和Right擴充像素數。
12、compute 函數
(1)作用:計算HOG特征向量
(2)函數原型:
void HOGDescriptor::compute(const Mat& img,
vector& descriptors,
Size winStride,
Size padding,
const vector& locations
) const
(3)參數注釋
<1>img:源圖像。只支持CV_8UC1和CV_8UC4數據類型。
<2>descriptors:返回的HOG特征向量,descriptors.size是HOG特征的維數。
<3>winStride:窗口移動步長。
<4>padding:擴充像素數。
<5>locations:對于正樣本可以直接取(0,0),負樣本為隨機產生合理坐標范圍內的點坐標。
三、HOG算法OpenCV實現流程
四、OpenCV的簡單例子
1、HOG行人檢測
(1)代碼:
復制代碼
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/gpu/gpu.hpp>
#include <stdio.h>
using namespace cv;
int main(int argc, char** argv)
{
Mat img;
vectorfound;
img = imread(argv[1]);
if(argc != 2 || !img.data)
{
printf(“沒有圖片\n”);
return -1;
}
HOGDescriptor defaultHog;
defaultHog.setSVMDetector(HOGDescriptor::getDefaultPeopleDetector());
//進行檢測
defaultHog.detectMultiScale(img, found);
//畫長方形,框出行人
for(int i = 0; i < found.size(); i++)
{
Rect r = found[i];
rectangle(img, r.tl(), r.br(), Scalar(0, 0, 255), 3);
}
namedWindow(“檢測行人”, CV_WINDOW_AUTOSIZE);
imshow(“檢測行人”, img);
waitKey(0);
}
復制代碼
(2)注解
上述過程并沒有像人臉檢測Demo里有Load訓練好的模型的步驟,這個getDefaultPeopleDetector是默認模型,這個模型數據在OpenCV源碼中是一堆常量數字,這些數字是通過原作者提供的行人樣本INRIAPerson.tar訓練得到的。
這里只是用到了HOG的識別模塊,OpenCV把HOG包的內容比較多,既有HOG的特征提取,也有結合SVM的識別,這里的識別只有檢測部分,OpenCV提供默認模型,如果使用新的模型,需要重新訓練。
2、HOG的計算以及SVM從訓練到識別(可參考OpenCV中的HOG+SVM物體分類和利用HOG+SVM訓練自己的XML文件和opencv中的 HOGDescriptor 類)
(1)制作樣本,將其歸一化到一個的尺度。
(2)將樣本圖像的名稱寫到一個TXT文件,方便程序調用。
(3)依次提取每張圖像的HOG特征向量。
對每一張圖片調用
hog.compute(img, descriptors,Size(8,8), Size(0,0));
可以生成hog descriptors,把它保存到文件中
for(int j=0;j<3780;j++)
fprintf(f,"%f,",descriptors[j]);
(4)利用SVM進行訓練。
(5)得到XML文件。
這里識別有兩種用法:
A、一種采用svm.predict來做(參考利用HOG+SVM訓練自己的XML文件)
B、另一種采用hog.setSVMDetector+訓練的模型和hog.detectMultiScale(參考利用Hog特征和SVM分類器進行行人檢測 )
五、總結
1、HOG與SIFT的區別
HOG和SIFT都是描述子,以及由于在具體操作上有很多相似的步驟,所以致使很多人誤認為HOG是SIFT的一種,其實兩者在使用目的和具體處理細節上是有很大的區別的。HOG與SIFT的主要區別如下:
(1)SIFT是基于關鍵點特征向量的描述。
(2)HOG是將圖像均勻的分成相鄰的小塊,然后在所有的小塊內統計梯度直方圖。
(3)SIFT需要對圖像尺度空間下對像素求極值點,而HOG中不需要。
(4)SIFT一般有兩大步驟,第一個步驟對圖像提取特征點,而HOG不會對圖像提取特征點。
2、HOG的優缺點
優點:
(1)HOG表示的是邊緣(梯度)的結構特征,因此可以描述局部的形狀信息;
(2)位置和方向空間的量化一定程度上可以抑制平移和旋轉帶來的影響;
(3)采取在局部區域歸一化直方圖,可以部分抵消光照變化帶來的影響;
(4)由于一定程度忽略了光照顏色對圖像造成的影響,使得圖像所需要的表征數據的維度降低了;
(5)而且由于這種分塊分單元的處理方法,也使得圖像局部像素點之間的關系可以很好得到表征。
缺點:
(1)描述子生成過程冗長,導致速度慢,實時性差;
(2)很難處理遮擋問題;
(3)由于梯度的性質,該描述子對噪點相當敏感。
總結
以上是生活随笔為你收集整理的详细的基于opencv svm hog的描述讲解的全部內容,希望文章能夠幫你解決所遇到的問題。