目标检测的图像特征提取—Haar特征

2024-06-17 04:08

本文主要是介绍目标检测的图像特征提取—Haar特征,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

1Haar-like特征

       Haar-like特征最早是由Papageorgiou等应用于人脸表示,ViolaJones在此基础上,使用3种类型4种形式的特征。

Haar特征分为三类:边缘特征、线性特征、中心特征和对角线特征,组合成特征模板。特征模板内有白色和黑色两种矩形,并定义该模板的特征值为白色矩形像素和减去黑色矩形像素和。Haar特征值反映了图像的灰度变化情况。例如:脸部的一些特征能由矩形特征简单的描述,如:眼睛要比脸颊颜色要深,鼻梁两侧比鼻梁颜色要深,嘴巴比周围颜色要深等。但矩形特征只对一些简单的图形结构,如边缘、线段较敏感,所以只能描述特定走向(水平、垂直、对角)的结构。

     对于图中的A, BD这类特征,特征数值计算公式为:v=Sum-Sum黑,而对于C来说,计算公式如下:v=Sum-2*Sum黑;之所以将黑色区域像素和乘以2,是为了使两种矩形区域中像素数目一致。

     通过改变特征模板的大小和位置,可在图像子窗口中穷举出大量的特征。上图的特征模板称为“特征原型”;特征原型在图像子窗口中扩展(平移伸缩)得到的特征称为“矩形特征”;矩形特征的值称为“特征值”。

      矩形特征可位于图像任意位置,大小也可以任意改变,所以矩形特征值是矩形模版类别、矩形位置和矩形大小这三个因素的函数。故类别、大小和位置的变化,使得很小的检测窗口含有非常多的矩形特征,如:在24*24像素大小的检测窗口内矩形特征数量可以达到16万个。这样就有两个问题需要解决了:(1)如何快速计算那么多的特征?---积分图大显神通;(2)哪些矩形特征才是对分类器分类最有效的?---如通过AdaBoost算法来训练(这一块这里不讨论,具体见http://blog.csdn.net/zouxy09/article/details/7922923

 

2Haar-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)

    [转载]Haar-like矩形特征的特征值的快速计算方法

    同理,点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为例说明特征值的计算。

    [转载]Haar-like矩形特征的特征值的快速计算方法

    如图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特征值无非就是两个矩阵像素和的差,同样可以在常数时间内完成。所以矩形特征的特征值计算,只与此特征矩形的端点的积分图有关,所以不管此特征矩形的尺度变换如何,特征值的计算所消耗的时间都是常量。这样只要遍历图像一次,就可以求得所有子窗口的特征值。

3Haar-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特征的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1068436

相关文章

Python脚本轻松实现检测麦克风功能

《Python脚本轻松实现检测麦克风功能》在进行音频处理或开发需要使用麦克风的应用程序时,确保麦克风功能正常是非常重要的,本文将介绍一个简单的Python脚本,能够帮助我们检测本地麦克风的功能,需要的... 目录轻松检测麦克风功能脚本介绍一、python环境准备二、代码解析三、使用方法四、知识扩展轻松检测麦

基于Python开发一个图像水印批量添加工具

《基于Python开发一个图像水印批量添加工具》在当今数字化内容爆炸式增长的时代,图像版权保护已成为创作者和企业的核心需求,本方案将详细介绍一个基于PythonPIL库的工业级图像水印解决方案,有需要... 目录一、系统架构设计1.1 整体处理流程1.2 类结构设计(扩展版本)二、核心算法深入解析2.1 自

Linux系统性能检测命令详解

《Linux系统性能检测命令详解》本文介绍了Linux系统常用的监控命令(如top、vmstat、iostat、htop等)及其参数功能,涵盖进程状态、内存使用、磁盘I/O、系统负载等多维度资源监控,... 目录toppsuptimevmstatIOStatiotopslabtophtopdstatnmon

C++ 检测文件大小和文件传输的方法示例详解

《C++检测文件大小和文件传输的方法示例详解》文章介绍了在C/C++中获取文件大小的三种方法,推荐使用stat()函数,并详细说明了如何设计一次性发送压缩包的结构体及传输流程,包含CRC校验和自动解... 目录检测文件的大小✅ 方法一:使用 stat() 函数(推荐)✅ 用法示例:✅ 方法二:使用 fsee

HTML5 中的<button>标签用法和特征

《HTML5中的<button>标签用法和特征》在HTML5中,button标签用于定义一个可点击的按钮,它是创建交互式网页的重要元素之一,本文将深入解析HTML5中的button标签,详细介绍其属... 目录引言<button> 标签的基本用法<button> 标签的属性typevaluedisabled

OpenCV实现实时颜色检测的示例

《OpenCV实现实时颜色检测的示例》本文主要介绍了OpenCV实现实时颜色检测的示例,通过HSV色彩空间转换和色调范围判断实现红黄绿蓝颜色检测,包含视频捕捉、区域标记、颜色分析等功能,具有一定的参考... 目录一、引言二、系统概述三、代码解析1. 导入库2. 颜色识别函数3. 主程序循环四、HSV色彩空间

Python中OpenCV与Matplotlib的图像操作入门指南

《Python中OpenCV与Matplotlib的图像操作入门指南》:本文主要介绍Python中OpenCV与Matplotlib的图像操作指南,本文通过实例代码给大家介绍的非常详细,对大家的学... 目录一、环境准备二、图像的基本操作1. 图像读取、显示与保存 使用OpenCV操作2. 像素级操作3.

C/C++的OpenCV 进行图像梯度提取的几种实现

《C/C++的OpenCV进行图像梯度提取的几种实现》本文主要介绍了C/C++的OpenCV进行图像梯度提取的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的... 目录预www.chinasem.cn备知识1. 图像加载与预处理2. Sobel 算子计算 X 和 Y

c/c++的opencv图像金字塔缩放实现

《c/c++的opencv图像金字塔缩放实现》本文主要介绍了c/c++的opencv图像金字塔缩放实现,通过对原始图像进行连续的下采样或上采样操作,生成一系列不同分辨率的图像,具有一定的参考价值,感兴... 目录图像金字塔简介图像下采样 (cv::pyrDown)图像上采样 (cv::pyrUp)C++ O

Python+wxPython构建图像编辑器

《Python+wxPython构建图像编辑器》图像编辑应用是学习GUI编程和图像处理的绝佳项目,本教程中,我们将使用wxPython,一个跨平台的PythonGUI工具包,构建一个简单的... 目录引言环境设置创建主窗口加载和显示图像实现绘制工具矩形绘制箭头绘制文字绘制临时绘制处理缩放和旋转缩放旋转保存编