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

博客:http://blog.csdn.net/qianxin_dh


在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_32SCV_32F, or CV_64F.
        
       积分图求和形式如下
                          
利用这些积分图,可以计算图像的任意直立或“倾斜”的矩形区域之和。例如一个简单的例子,计算一个简单矩形区域的和,这个区域是通过角点(x1,y1),(x2,y2)定位的,这里x2>x1,y2>y1,计算如下:

        

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


三.代码的实现


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

就从main函数开始吧:
//  main.cpp
//  HaarFeature
//  Created by qianxin_dh on 14-9-13.
//  Copyright (c) 2014年 qianxin_dh. All rights reserved.


#include "stdafx.h"
#include 
#include 
#include 

#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!"< 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

HaarFeature类:

头文件:
#include 
#include 

#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 m_rects;
	std::vector m_weights;
	float m_factor;

	Mat _imageIntegral;
};

实现文件:
#include "HaarFeature.h"
#include 

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(yMin, xMin) +
		_imageIntegral.at(yMax, xMax) -
		_imageIntegral.at(yMin, xMax) -
		_imageIntegral.at(yMax, xMin);

	//cout<

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

RECT类的实现:

#pragma once

#include 
#include 

template 
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 
	  Rect(const Rect& 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 IntRect;
typedef Rect FloatRect;


你可能感兴趣的:(opencv)