目标检测的图像特征提取—Haar特征
1、Haar-like特征
?????? Haar-like特征最早是由Papageorgiou等應用于人臉表示,Viola和Jones在此基礎上,使用3種類型4種形式的特征。
Haar特征分為三類:邊緣特征、線性特征、中心特征和對角線特征,組合成特征模板。特征模板內有白色和黑色兩種矩形,并定義該模板的特征值為白色矩形像素和減去黑色矩形像素和。Haar特征值反映了圖像的灰度變化情況。例如:臉部的一些特征能由矩形特征簡單的描述,如:眼睛要比臉頰顏色要深,鼻梁兩側比鼻梁顏色要深,嘴巴比周圍顏色要深等。但矩形特征只對一些簡單的圖形結構,如邊緣、線段較敏感,所以只能描述特定走向(水平、垂直、對角)的結構。
?????對于圖中的A, B和D這類特征,特征數值計算公式為:v=Sum白-Sum黑,而對于C來說,計算公式如下:v=Sum白-2*Sum黑;之所以將黑色區域像素和乘以2,是為了使兩種矩形區域中像素數目一致。
???? 通過改變特征模板的大小和位置,可在圖像子窗口中窮舉出大量的特征。上圖的特征模板稱為“特征原型”;特征原型在圖像子窗口中擴展(平移伸縮)得到的特征稱為“矩形特征”;矩形特征的值稱為“特征值”。
????? 矩形特征可位于圖像任意位置,大小也可以任意改變,所以矩形特征值是矩形模版類別、矩形位置和矩形大小這三個因素的函數。故類別、大小和位置的變化,使得很小的檢測窗口含有非常多的矩形特征,如:在24*24像素大小的檢測窗口內矩形特征數量可以達到16萬個。這樣就有兩個問題需要解決了:(1)如何快速計算那么多的特征?---積分圖大顯神通;(2)哪些矩形特征才是對分類器分類最有效的?---如通過AdaBoost算法來訓練(這一塊這里不討論,具體見http://blog.csdn.net/zouxy09/article/details/7922923)
?
2、Haar-like特征的計算—積分圖
?????? 積分圖就是只遍歷一次圖像就可以求出圖像中所有區域像素和的快速算法,大大的提高了圖像特征值計算的效率。
?????? 積分圖主要的思想是將圖像從起點開始到各個點所形成的矩形區域像素之和作為一個數組的元素保存在內存中,當要計算某個區域的像素和時可以直接索引數組的元素,不用重新計算這個區域的像素和,從而加快了計算(這有個相應的稱呼,叫做動態規劃算法)。積分圖能夠在多種尺度下,使用相同的時間(常數時間)來計算不同的特征,因此大大提高了檢測速度。
???????我們來看看它是怎么做到的。
?????? 積分圖是一種能夠描述全局信息的矩陣表示方法。積分圖的構造方式是位置(i,j)處的值ii(i,j)是原圖像(i,j)左上角方向所有像素的和:
??
????????
積分圖構建算法:
1)用s(i,j)表示行方向的累加和,初始化s(i,-1)=0;
2)用ii(i,j)表示一個積分圖像,初始化ii(-1,i)=0;
3)逐行掃描圖像,遞歸計算每個像素(i,j)行方向的累加和s(i,j)和積分圖像ii(i,j)的值
s(i,j)=s(i,j-1)+f(i,j)
ii(i,j)=ii(i-1,j)+s(i,j)
4)掃描圖像一遍,當到達圖像右下角像素時,積分圖像ii就構造好了。? ? ?
? ? 任意矩形區域內像素積分。由圖像的積分圖可方便快速地計算圖像中任意矩形內所有像素灰度積分。如下圖2.3所示,點1的積分圖像ii1的值為(其中Sum為求和) :?ii1=Sum(A)
????
? ? 同理,點2、點3、點4的積分圖像分別為:
????ii2=Sum(A)+Sum(B);??????ii3=Sum(A)+Sum(C);????ii4=Sum(A)+Sum(B)+Sum(C)+Sum(D);
????矩形區域D內的所有像素灰度積分可由矩形端點的積分圖像值得到:
?????????????????????Sum(D)=ii1+ii4-(ii2+ii3)????????????(1)
????(2) 特征值計算
????矩形特征的特征值是兩個不同的矩形區域像素和之差,由(1)式可以計算任意矩形特征的特征值,下面以圖2.1中特征原型A為例說明特征值的計算。
????
? ? 如圖2.4 所示,該特征原型的特征值定義為:
????Sum(A)-Sum(B)
????根據(1)式則有:Sum(A)=ii4+ii1-(ii2+ii3);????Sum(B)=ii6+ii3-(ii4+ii5);
????所以此類特征原型的特征值為:
?????????????????(ii4-ii3)-(ii2-ii1)+(ii4-ii3)-(ii6-ii5)
????另示:運用積分圖可以快速計算給定的矩形之所有象素值之和Sum(r)。假設r=(x,y,w,h),那么此矩形內部所有元素之和等價于下面積分圖中下面這個式子:
?????????????????Sum(r) = ii(x+w,y+h)+ii(x-1,y-1)-ii(x+w,y-1)-ii(x-1,y+h)
? ??而Haar-like特征值無非就是兩個矩陣像素和的差,同樣可以在常數時間內完成。所以矩形特征的特征值計算,只與此特征矩形的端點的積分圖有關,所以不管此特征矩形的尺度變換如何,特征值的計算所消耗的時間都是常量。這樣只要遍歷圖像一次,就可以求得所有子窗口的特征值。
3、Haar-like矩形特征拓展
???????? Lienhart R.等對Haar-like矩形特征庫作了進一步擴展,加入了旋轉45。角的矩形特征。擴展后的特征大致分為4種類型:邊緣特征、線特征環、中心環繞特征和對角線特征:
?????? 在特征值的計算過程中,黑色區域的權值為負值,白色區域的權值為正值。而且權值與矩形面積成反比(使兩種矩形區域中像素數目一致);
豎直矩陣特征值計算:
???? 對于豎直矩陣,與上面2處說的一樣。
45°旋角的矩形特征計算:
????? 對于45°旋角的矩形,我們定義RSAT(x,y)為點(x,y)左上角45°區域和左下角45°區域的像素和。
???????
用公式可以表示為:
為了節約時間,減少重復計算,可按如下遞推公式計算:
而計算矩陣特征的特征值,是位于十字行矩形RSAT(x,y)之差。可參考下圖:
4、代碼實現
? ?這里我是在一副灰度圖像內隨便選取一個框,計算其haar特征,當然具體應用時由于haar特征對于人臉檢測效果尤其好,感興趣的朋友可以把程序進行更改,加入手動畫框模塊,讀取視頻,計算得到每幀目標框內的特征值。
main函數: // main.cpp // HaarFeature #include <opencv2/core/core.hpp> #include <opencv2/highgui/highgui.hpp> #include <opencv2/imgproc/imgproc.hpp> #include "HaarFeature.h" using namespace cv; using namespace std; const int featureNUM=192; int main() { Mat image=imread("lena.bmp"); //cvtColor(image,image,CV_RGB2GRAY); if (image.empty()) { cout<<"Load the image error!"<<endl; return -1; } vector<HaarFeature> m_features; //用于生成特征模板 float x[] = {0.2f, 0.4f, 0.6f, 0.8f}; float y[] = {0.2f, 0.4f, 0.6f, 0.8f}; float s[] = {0.2f, 0.4f}; for (int iy = 0; iy < 4; ++iy) { for (int ix = 0; ix < 4; ++ix) { for (int is = 0; is < 2; ++is) { FloatRect r(x[ix]-s[is]/2, y[iy]-s[is]/2, s[is], s[is]); //32種尺寸 for (int it = 0; it < 6; ++it) //這里主要實現6種hair特征,32*6=192種特征模板 { m_features.push_back(HaarFeature(r, it)); } } } } float m_feat; FloatRect rect(10,10,100,50); for(int i=0;i<featureNUM;i++){ m_feat= m_features[i].caluHf(image,rect); cout<<m_feat<<" "<<endl; } return 0; }
HaarFeature類: 頭文件: #include <opencv2/opencv.hpp> #include <vector> #include "RECT.h" using namespace cv; class HaarFeature { public: HaarFeature(FloatRect& bb, int type); ~HaarFeature(); float caluHf(Mat& _image,FloatRect& _rect); //計算haar特征值 private: float sum(Mat& _image,IntRect& _rect); private: FloatRect m_box; std::vector<FloatRect> m_rects; std::vector<float> m_weights; float m_factor; Mat _imageIntegral; };
源文件: #include "HaarFeature.h" #include <iostream> using namespace std; HaarFeature::HaarFeature(FloatRect& bb, int type) : m_box(bb) { assert(type < 6); //分別實現六種haar特征,可以參照文章開頭時引用的圖片進行對應 switch (type) { case 0: { m_rects.push_back(FloatRect(bb.XMin(), bb.YMin(), bb.Width(), bb.Height()/2)); m_rects.push_back(FloatRect(bb.XMin(), bb.YMin()+bb.Height()/2, bb.Width(), bb.Height()/2)); m_weights.push_back(1.f); m_weights.push_back(-1.f); m_factor = 255*1.f/2; break; } case 1: { m_rects.push_back(FloatRect(bb.XMin(), bb.YMin(), bb.Width()/2, bb.Height())); m_rects.push_back(FloatRect(bb.XMin()+bb.Width()/2, bb.YMin(), bb.Width()/2, bb.Height())); m_weights.push_back(1.f); m_weights.push_back(-1.f); m_factor = 255*1.f/2; break; } case 2: { m_rects.push_back(FloatRect(bb.XMin(), bb.YMin(), bb.Width()/3, bb.Height())); m_rects.push_back(FloatRect(bb.XMin()+bb.Width()/3, bb.YMin(), bb.Width()/3, bb.Height())); m_rects.push_back(FloatRect(bb.XMin()+2*bb.Width()/3, bb.YMin(), bb.Width()/3, bb.Height())); m_weights.push_back(1.f); m_weights.push_back(-2.f); m_weights.push_back(1.f); m_factor = 255*2.f/3; break; } case 3: { m_rects.push_back(FloatRect(bb.XMin(), bb.YMin(), bb.Width(), bb.Height()/3)); m_rects.push_back(FloatRect(bb.XMin(), bb.YMin()+bb.Height()/3, bb.Width(), bb.Height()/3)); m_rects.push_back(FloatRect(bb.XMin(), bb.YMin()+2*bb.Height()/3, bb.Width(), bb.Height()/3)); m_weights.push_back(1.f); m_weights.push_back(-2.f); m_weights.push_back(1.f); m_factor = 255*2.f/3; break; } case 4: { m_rects.push_back(FloatRect(bb.XMin(), bb.YMin(), bb.Width()/2, bb.Height()/2)); m_rects.push_back(FloatRect(bb.XMin()+bb.Width()/2, bb.YMin()+bb.Height()/2, bb.Width()/2, bb.Height()/2)); m_rects.push_back(FloatRect(bb.XMin(), bb.YMin()+bb.Height()/2, bb.Width()/2, bb.Height()/2)); m_rects.push_back(FloatRect(bb.XMin()+bb.Width()/2, bb.YMin(), bb.Width()/2, bb.Height()/2)); m_weights.push_back(1.f); m_weights.push_back(1.f); m_weights.push_back(-1.f); m_weights.push_back(-1.f); m_factor = 255*1.f/2; break; } case 5: { m_rects.push_back(FloatRect(bb.XMin(), bb.YMin(), bb.Width(), bb.Height())); m_rects.push_back(FloatRect(bb.XMin()+bb.Width()/4, bb.YMin()+bb.Height()/4, bb.Width()/2, bb.Height()/2)); m_weights.push_back(1.f); m_weights.push_back(-4.f); m_factor = 255*3.f/4; break; } } } HaarFeature::~HaarFeature() { } float HaarFeature::sum(Mat& _image,IntRect& _rect) { int xMin=_rect.XMin(); int yMin=_rect.YMin(); int xMax=_rect.XMin()+_rect.Width(); int yMax=_rect.YMin()+_rect.Height(); int tempValue=0; tempValue += _imageIntegral.at<int>(yMin, xMin) + _imageIntegral.at<int>(yMax, xMax) - _imageIntegral.at<int>(yMin, xMax) - _imageIntegral.at<int>(yMax, xMin); //cout<<weight<<endl; //cout<<tempValue<<endl; return tempValue; } float HaarFeature::caluHf(Mat& _image,FloatRect& _rect) { int value = 0; integral(_image, _imageIntegral, CV_32F); //cout<<_imageIntegral<<" "<<endl; for (int i = 0; i < (int)m_rects.size(); ++i) //m_rects.size()=2; { FloatRect& r = m_rects[i]; IntRect sampleRect((int)(_rect.XMin()+r.XMin()*_rect.Width()+0.5f), (int)(_rect.YMin()+r.YMin()*_rect.Height()+0.5f), (int)(r.Width()*_rect.Width()), (int)(r.Height()*_rect.Height())); value +=m_weights[i]*sum(_image,sampleRect); //sum函數返回的是積分圖像對應的數值 } return value / (m_factor*(_rect.Area())*(m_box.Area())); }
? ? 這里大家應該都看到了HaarFeature類頭文件中還加入了一個RECT頭文件,這個頭文件的作用定義兩種矩形框:IntRect和FloatRect。之所以要定義這兩種矩形框,是因為我們所選取的用于生成特征模板的矩形框r的值過小(r在main.cpp中),而opencv里自帶的rect原型為typedef Rect_ <int> Rect,如果使用它會導致r的值為(0,0,0,0)。
RECT源文件: #pragma once #include <iostream> #include <algorithm> template <typename T> class Rect { public: Rect() : m_xMin(0), m_yMin(0), m_width(0), m_height(0) { } Rect(T xMin, T yMin, T width, T height) : m_xMin(xMin), m_yMin(yMin), m_width(width), m_height(height) { } template <typename T2> Rect(const Rect<T2>& rOther) : m_xMin((T)rOther.XMin()), m_yMin((T)rOther.YMin()), m_width((T)rOther.Width()), m_height((T)rOther.Height()) { } inline T XMin() const { return m_xMin; } inline T YMin() const { return m_yMin; } inline T Width() const { return m_width; } inline T Height() const { return m_height; } inline T Area() const { return m_width * m_height; } private: T m_xMin; T m_yMin; T m_width; T m_height; }; typedef Rect<int> IntRect; typedef Rect<float> FloatRect;
[1]?http://blog.csdn.net/smf0504/article/details/51322255 [2]?http://blog.csdn.net/zouxy09/article/details/7929570
總結
以上是生活随笔為你收集整理的目标检测的图像特征提取—Haar特征的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: pp助手正版激活失败怎么办
- 下一篇: Pedestrian Identific