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