Haar特征描述子及其代码实现

引用:http://blog.csdn.net/qianxin_dh/article/details/39268113

     在2001年, ViolaJones两位大牛发表了经典的<<Rapid Object Detection using a Boosted Cascade of Simple Features>>和<<Robust Real-Time Face Detection>>,文中提出使用Haar特征和积分图方法进行人脸检测,并对AdaBoost训练出的强分类器进行级联,实现了很好的人脸检测效果。那么究竟什么是Haar特征?怎么计算Haar特征?又该如何利用Haar特征呢?

一.什么是Haar特征?


         Harr特征,是计算机视觉领域中常用的一种特征描述子,用来表示我们感兴趣目标的特征,帮助我们找到目标。举例来说, 假设在人脸检测时,我们可以利用一个子窗口在待检测的图片帧中进行滑动检测(也就是将图像的指定位置转化为特征,通常情况下是采用滑动窗口技术),计算出每一位置的特征,然后用预先训练好的级连分类器对该特征进行筛选,一旦该特征通过了所有强分类器的筛选,则判定该区域为人脸。
        目前常用的Haar特征有:

         其中,由Viola等牛人提出的Haar特征主要是:
                                                        
          这这些黑白方格表示什么意义呢?这就是所谓的Haar特征计算了,将白色区域的像素和减去黑色区域的像素和,既为所求的特征值。也就是说,这些黑白方格能够将检测的目标进行量化,设想将其中一个特征模板覆盖在图像帧中,则目标区域计算出的像素特征值和非目标的像素特征值是不一样的,差值越大,分类的效果越明显,目标找的越准确。

二.积分图


        上面提到了Haar特征如何进行计算,我们可以想到不同大小,不同位置的特征模板在不同图像帧中都会面临着计算特征值的问题,一般来说, 在一个24×24像素的窗口中任意排列至少可以产生数以10万计的特征,计算量是十分巨大的,因此,如何提高计算效率是将Haar特征从理论应用到实践的关键步骤。
         积分图是一个数据结构,可实现子区域的快速求和。这样的求和在很多应用中是很有用的,最显著的就是在人脸识别及相关算法中应用的Haar小波。
        

        Opencv函数: void integral ( InputArray  image , OutputArray  sum , int  sdepth )
        参           数:
                             image:输入图像大小为W*H
                             sum:   输出图像大小为(W+1)*(H+1);
                            sdepth:根据需要可以提供,是指积分图像的深度和类型,如 CV_32S CV_32F , or  CV_64F .
        
       积分图求和形式如下
                          
利用这些积分图,可以计算图像的任意直立或“倾斜”的矩形区域之和。例如一个简单的例子,计算一个简单矩形区域的和,这个区域是通过角点(x1,y1),(x2,y2)定位的,这里x2>x1,y2>y1,计算如下:

        

在这种方式下,就可以为各种窗口大小执行快速的可变窗块相关计算。


三.代码的实现


     这里我是在一副灰度图像内随便选取一个框,计算其haar特征,当然具体应用时由于haar特征对于人脸检测效果尤其好,感兴趣的朋友可以把程序进行更改,加入手动画框模块,读取视频,计算得到每帧目标框内的特征值。测试环境:vs2008+opencv2.3.1

就从main函数开始吧:
[cpp]  view plain  copy
  1. //  main.cpp  
  2. //  HaarFeature  
  3. //  Created by qianxin_dh on 14-9-13.  
  4. //  Copyright (c) 2014年 qianxin_dh. All rights reserved.  
  5.   
  6.   
  7. #include "stdafx.h"  
  8. #include <opencv2/core/core.hpp>  
  9. #include <opencv2/highgui/highgui.hpp>  
  10. #include <opencv2/imgproc/imgproc.hpp>  
  11.   
  12. #include "HaarFeature.h"  
  13.   
  14. using namespace cv;  
  15. using namespace std;  
  16.   
  17. const int featureNUM=192;  
  18.   
  19.   
  20. int main()  
  21. {  
  22.     Mat image=imread("lena.bmp");  
  23.   
  24.     //cvtColor(image,image,CV_RGB2GRAY);  
  25.   
  26.     if (image.empty())  
  27.     {  
  28.         cout<<"Load the image error!"<<endl;  
  29.         return -1;  
  30.     }  
  31.   
  32.     vector<HaarFeature> m_features;     
  33.   
  34.     //用于生成特征模板  
  35.     float x[] = {0.2f, 0.4f, 0.6f, 0.8f};  
  36.     float y[] = {0.2f, 0.4f, 0.6f, 0.8f};  
  37.     float s[] = {0.2f, 0.4f};  
  38.     for (int iy = 0; iy < 4; ++iy)  
  39.     {  
  40.         for (int ix = 0; ix < 4; ++ix)  
  41.         {  
  42.             for (int is = 0; is < 2; ++is)  
  43.             {  
  44.                 FloatRect r(x[ix]-s[is]/2, y[iy]-s[is]/2, s[is], s[is]);  //32种尺寸  
  45.   
  46.                 for (int it = 0; it < 6; ++it)        //这里主要实现6种hair特征,32*6=192种特征模板  
  47.                 {  
  48.                     m_features.push_back(HaarFeature(r, it));     
  49.                 }  
  50.             }  
  51.         }  
  52.     }  
  53.   
  54.     float m_feat;  
  55.   
  56.     FloatRect rect(10,10,100,50);  
  57.   
  58.     for(int i=0;i<featureNUM;i++){  
  59.         m_feat= m_features[i].caluHf(image,rect);   
  60.   
  61.         cout<<m_feat<<" "<<endl;    
  62.     }  
  63.   
  64.     return 0;  
  65. }  

HaarFeature类:

头文件:
[cpp]  view plain  copy
  1. #include <opencv2/opencv.hpp>  
  2. #include <vector>  
  3.   
  4. #include "RECT.h"  
  5.   
  6. using namespace cv;  
  7.   
  8. class HaarFeature  
  9. {  
  10. public:  
  11.     HaarFeature(FloatRect& bb, int type);  
  12.     ~HaarFeature();  
  13.   
  14.     float caluHf(Mat& _image,FloatRect& _rect);  //计算haar特征值  
  15.   
  16. private:  
  17.     float sum(Mat& _image,IntRect& _rect);  
  18.   
  19. private:  
  20.     FloatRect m_box;  
  21.     std::vector<FloatRect> m_rects;  
  22.     std::vector<float> m_weights;  
  23.     float m_factor;  
  24.   
  25.     Mat _imageIntegral;  
  26. };  

实现文件:
[cpp]  view plain  copy
  1. #include "HaarFeature.h"  
  2. #include <iostream>  
  3.   
  4. using namespace std;  
  5.   
  6. HaarFeature::HaarFeature(FloatRect& bb, int type) :  
  7. m_box(bb)  
  8. {  
  9.     assert(type < 6);    //分别实现六种haar特征,可以参照文章开头时引用的图片进行对应  
  10.   
  11.     switch (type)  
  12.     {  
  13.     case 0:  
  14.         {  
  15.             m_rects.push_back(FloatRect(bb.XMin(), bb.YMin(), bb.Width(), bb.Height()/2));  
  16.             m_rects.push_back(FloatRect(bb.XMin(), bb.YMin()+bb.Height()/2, bb.Width(), bb.Height()/2));  
  17.             m_weights.push_back(1.f);  
  18.             m_weights.push_back(-1.f);  
  19.             m_factor = 255*1.f/2;  
  20.             break;  
  21.         }  
  22.     case 1:  
  23.         {  
  24.             m_rects.push_back(FloatRect(bb.XMin(), bb.YMin(), bb.Width()/2, bb.Height()));  
  25.             m_rects.push_back(FloatRect(bb.XMin()+bb.Width()/2, bb.YMin(), bb.Width()/2, bb.Height()));  
  26.             m_weights.push_back(1.f);  
  27.             m_weights.push_back(-1.f);  
  28.             m_factor = 255*1.f/2;  
  29.             break;  
  30.         }  
  31.     case 2:  
  32.         {  
  33.             m_rects.push_back(FloatRect(bb.XMin(), bb.YMin(), bb.Width()/3, bb.Height()));  
  34.             m_rects.push_back(FloatRect(bb.XMin()+bb.Width()/3, bb.YMin(), bb.Width()/3, bb.Height()));  
  35.             m_rects.push_back(FloatRect(bb.XMin()+2*bb.Width()/3, bb.YMin(), bb.Width()/3, bb.Height()));  
  36.             m_weights.push_back(1.f);  
  37.             m_weights.push_back(-2.f);  
  38.             m_weights.push_back(1.f);  
  39.             m_factor = 255*2.f/3;  
  40.             break;  
  41.         }  
  42.     case 3:  
  43.         {  
  44.             m_rects.push_back(FloatRect(bb.XMin(), bb.YMin(), bb.Width(), bb.Height()/3));  
  45.             m_rects.push_back(FloatRect(bb.XMin(), bb.YMin()+bb.Height()/3, bb.Width(), bb.Height()/3));  
  46.             m_rects.push_back(FloatRect(bb.XMin(), bb.YMin()+2*bb.Height()/3, bb.Width(), bb.Height()/3));  
  47.             m_weights.push_back(1.f);  
  48.             m_weights.push_back(-2.f);  
  49.             m_weights.push_back(1.f);  
  50.             m_factor = 255*2.f/3;  
  51.             break;  
  52.         }  
  53.     case 4:  
  54.         {  
  55.             m_rects.push_back(FloatRect(bb.XMin(), bb.YMin(), bb.Width()/2, bb.Height()/2));  
  56.             m_rects.push_back(FloatRect(bb.XMin()+bb.Width()/2, bb.YMin()+bb.Height()/2, bb.Width()/2, bb.Height()/2));  
  57.             m_rects.push_back(FloatRect(bb.XMin(), bb.YMin()+bb.Height()/2, bb.Width()/2, bb.Height()/2));  
  58.             m_rects.push_back(FloatRect(bb.XMin()+bb.Width()/2, bb.YMin(), bb.Width()/2, bb.Height()/2));  
  59.             m_weights.push_back(1.f);  
  60.             m_weights.push_back(1.f);  
  61.             m_weights.push_back(-1.f);  
  62.             m_weights.push_back(-1.f);  
  63.             m_factor = 255*1.f/2;  
  64.             break;  
  65.         }  
  66.     case 5:  
  67.         {  
  68.             m_rects.push_back(FloatRect(bb.XMin(), bb.YMin(), bb.Width(), bb.Height()));  
  69.             m_rects.push_back(FloatRect(bb.XMin()+bb.Width()/4, bb.YMin()+bb.Height()/4, bb.Width()/2, bb.Height()/2));  
  70.             m_weights.push_back(1.f);  
  71.             m_weights.push_back(-4.f);  
  72.             m_factor = 255*3.f/4;  
  73.             break;  
  74.         }                 
  75.     }  
  76. }  
  77.   
  78. HaarFeature::~HaarFeature()  
  79. {  
  80. }  
  81.   
  82. float HaarFeature::sum(Mat& _image,IntRect& _rect)  
  83. {  
  84.     int xMin=_rect.XMin();  
  85.     int yMin=_rect.YMin();  
  86.     int xMax=_rect.XMin()+_rect.Width();  
  87.     int yMax=_rect.YMin()+_rect.Height();  
  88.   
  89.     int tempValue=0;  
  90.   
  91.     tempValue +=    
  92.         _imageIntegral.at<int>(yMin, xMin) +  
  93.         _imageIntegral.at<int>(yMax, xMax) -  
  94.         _imageIntegral.at<int>(yMin, xMax) -  
  95.         _imageIntegral.at<int>(yMax, xMin);  
  96.   
  97.     //cout<<weight<<endl;  
  98.   
  99.     //cout<<tempValue<<endl;  
  100.   
  101.     return tempValue;  
  102. }  
  103.   
  104. float HaarFeature::caluHf(Mat& _image,FloatRect& _rect)    
  105. {  
  106.     int value = 0;  
  107.   
  108.     integral(_image, _imageIntegral, CV_32F);  
  109.   
  110.     //cout<<_imageIntegral<<" "<<endl;  
  111.   
  112.     for (int i = 0; i < (int)m_rects.size(); ++i)      //m_rects.size()=2;  
  113.     {  
  114.         FloatRect& r = m_rects[i];  
  115.   
  116.         IntRect sampleRect((int)(_rect.XMin()+r.XMin()*_rect.Width()+0.5f), (int)(_rect.YMin()+r.YMin()*_rect.Height()+0.5f),  
  117.             (int)(r.Width()*_rect.Width()), (int)(r.Height()*_rect.Height()));  
  118.   
  119.         value +=m_weights[i]*sum(_image,sampleRect);   //sum函数返回的是积分图像对应的数值  
  120.     }  
  121.     return value / (m_factor*(_rect.Area())*(m_box.Area()));     
  122. }  

这里大家应该都看到了 HaarFeature类头文件中还加入了一个RECT头文件,这个头文件的作用定义两种矩形框:IntRect和FloatRect。之所以要定义这两种矩形框,是因为我们所选取的用于生成特征模板的矩形框r的值过小(r在main.cpp中),而opencv里自带的rect原型为typedef Rect_ <int> Rect,如果使用它会导致r的值为(0,0,0,0)。

RECT类的实现:

[cpp]  view plain  copy
  1. #pragma once  
  2.   
  3. #include <iostream>  
  4. #include <algorithm>  
  5.   
  6. template <typename T>  
  7. class Rect  
  8. {  
  9. public:  
  10.     Rect() :  
  11.           m_xMin(0),  
  12.           m_yMin(0),  
  13.           m_width(0),  
  14.           m_height(0)  
  15.       {  
  16.       }  
  17.   
  18.       Rect(T xMin, T yMin, T width, T height) :  
  19.       m_xMin(xMin),  
  20.           m_yMin(yMin),  
  21.           m_width(width),  
  22.           m_height(height)  
  23.       {  
  24.       }  
  25.   
  26.       template <typename T2>  
  27.       Rect(const Rect<T2>& rOther) :  
  28.       m_xMin((T)rOther.XMin()),  
  29.           m_yMin((T)rOther.YMin()),  
  30.           m_width((T)rOther.Width()),  
  31.           m_height((T)rOther.Height())  
  32.       {  
  33.       }  
  34.   
  35.       inline T XMin() const { return m_xMin; }  
  36.       inline T YMin() const { return m_yMin; }  
  37.       inline T Width() const { return m_width; }  
  38.       inline T Height() const { return m_height; }  
  39.       inline T Area() const { return m_width * m_height; }  
  40.   
  41. private:  
  42.     T m_xMin;  
  43.     T m_yMin;  
  44.     T m_width;  
  45.     T m_height;  
  46. };  
  47.   
  48. typedef Rect<int> IntRect;  
  49. typedef Rect<float> FloatRect;  

  • 0
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值