Haar特征提取算法的实现

原创 2018年04月17日 16:45:12

【自己动手,丰衣食足】系列

        Haar特征是一种很早就被提出的图像特征提取算法,后面还经过了几次改进。Haar特征能够很好地运用于人脸识别技术,当然很多目标检测技术中对目标图像的特征提取也可以使用Haar特征。当我们使用opencv自带的cascade分类器时可以选择Haar特征作为训练样本数据的特征描述子,然后将特征描述子作为样本数据送入cascade分类器中,就可以通过Adaboost级联分类算法来训练用于图像识别和目标检测的分类器。我是在使用opencv自带的cascade分类器时候接触到了Haar特征提取,当时使用的时候,我是调用的是opencv库中Haar特征提取的接口,Haar特征提取的算法原理我并没有深究,因为计算机视觉课程布置一项手动实现一种图像特征提取算法的作业。。。。。我顺势就把Haar特征提取的算法原理学习了一下,并将其实现。

网上有太多的Haar特征提取算法的原理介绍,这里推荐一篇:

https://blog.csdn.net/lanxuecc/article/details/52222369原理过程已经介绍得非常详细。

  • 理解Haar特征提取的关键点就在于对积分图的理解以及如何利用积分图去计算给定矩形模板的特征值。Haar特征提取算法在提取某一张图片的特征的时候,首先会计算该图片的积分图,一次性计算完积分图并保存下来为后面的像素值加和计算提供了直接的计算结果,这里运用到了动态规划的算法思想,是一种典型的用空间换时间的做法。计算完积分图之后,每次对图片中任意位置的矩形内像素之和的计算都由原本的O(n^2)计算复杂度变成了O(1),不得不说对于Haar特征提取这种特征提取机制,积分图的运用是一种非常聪明的做法。
  • 关于Haar特征的特征维数,其实这个问题很多介绍Haar特征的博客里面都没有提到。如果对任意一种尺寸的模板(还没有算模板的种类)出现在图片上任意位置的特征值都进行计算,在一张仅仅25*25的图片上都能提取出上万维的特征向量。可想而知Haar特征的大小控制得当非常重要,如果要计算某一个尺寸模板的特征值,那么为了保证多个尺度的模板都能够充分地反应该图片在该尺度模板下的特征,就应该让多个尺度的模板下在图像创厚重进行充分地滑动。既要控制特征向量的维数,又要保证特征模板的充分滑动,因此控制滑动步长和模板的尺度伸缩速度非常重要。

代码如下:

定义的MyHaar类的头文件myhaar.h

#ifndef MY_HAAR
#define MY_HAAR

#include <opencv2/opencv.hpp>
#include <iostream>

//定义多种模板
enum MODEL_TYPE{
    VERTICAL=1,HORIZONTAL=2,CENTER=3
};

struct model{
    std::vector<cv::Rect> rects;//保存一个模板里面的多个矩形
    std::vector<char> flag;//记录每个矩形颜色,1代表白色,-1代表黑色
    MODEL_TYPE type;
};

//定义HAAR类
class MyHaar{
public:
    MyHaar();
    //计算Haar特征的接口,参数:原图、特征模板、步长、缩放速度
    void compute(cv::Mat &src,std::vector<double> &descriptor,model m,int step_x=4,int step_y=4,float mutil=1.5);

private:
    cv::Mat integral_img;//积分图
    void generate_integral_image(cv::Mat &image);//生成积分图
    double compute_sum_of_rect(cv::Rect r);//计算矩形内像素值之和
    void mutil_transform(model &m,float mutil);//对模板进行伸缩变换
    void model_move(model &m,int bios_x,int bios_y);//模板平移
    void x_reset(model &m);//重置模板的x坐标为0
    void y_reset(model &m);//重置模板的y坐标为0
};


#endif // MY_HAAR

定义的MyHaar类的源文件myhaar.cpp

#include "my_haar.h"

using namespace std;
using namespace cv;

MyHaar::MyHaar(){}

void MyHaar::compute(cv::Mat &src,vector<double> &descriptor,model m,int step_x,int step_y,float mutil){
    //生成积分图
    generate_integral_image(src);

    vector<Rect>::iterator iter=m.rects.begin();
    Rect total_model=*iter;
    for(iter=m.rects.begin()+1;iter!=m.rects.end();iter++)
        total_model=total_model|*iter;
   // cout<<total_model.width<<" "<<total_model.height<<endl;
    while((total_model.width<=src.cols&&total_model.width>=1)&&(total_model.height<=src.rows&&total_model.height>=1)){
        //当前模板在目标窗口中进行滑动
        for(y_reset(m);m.rects[0].y+total_model.height<=src.rows;model_move(m,0,step_y)){

            for(x_reset(m);m.rects[0].x+total_model.width<=src.cols;model_move(m,step_x,0)){
                //计算当前模板特征值
                double sum=0;
                for(int i=0;i<m.rects.size();i++)
                    sum+=m.flag[i]*compute_sum_of_rect(m.rects[i]);
                descriptor.push_back(sum);//将特征值保存至descriptor
                if(m.rects[0].x+total_model.width+step_x>src.cols)
                    break;
            }
            if(m.rects[0].y+total_model.height+step_y>src.rows)
                break;
        }
        //伸缩变换
        mutil_transform(m,mutil);

        //重新计算total_rect
        vector<Rect>::iterator iter=m.rects.begin();
        total_model=*iter;
        for(iter=m.rects.begin()+1;iter!=m.rects.end();iter++)
            total_model=total_model|*iter;
    }

}

//生成积分图
void MyHaar::generate_integral_image(Mat &img){
    cv::integral(img,integral_img,CV_64F);
}

//计算矩形像素值之和
double MyHaar::compute_sum_of_rect(Rect r){
    int x=r.x;
    int y=r.y;
    int width=r.width;
    int height=r.height;
    double sum;

    sum=integral_img.at<double>(x,y)+integral_img.at<double>(x+width,y+height)
            -integral_img.at<double>(x,y+height)-integral_img.at<double>(x+width,y);

    return sum;
}

//模板进行伸缩变换
void MyHaar::mutil_transform(model &m, float mutil){
    //坐标归零
    m.rects[0].x=0;
    m.rects[0].y=0;
    //宽高伸缩
    for(vector<Rect>::iterator iter=m.rects.begin();iter!=m.rects.end();iter++)
        iter->width=iter->width*mutil,iter->height=iter->height*mutil;
    //矩形位置重定位
    switch(m.type){
        case 1:
            m.rects[1].x=m.rects[0].x+m.rects[0].width;
        break;
        case 2:
            m.rects[1].y=m.rects[0].y+m.rects[0].height;
        break;
        case 3:
            m.rects[1].x=m.rects[0].x+m.rects[0].width;
            m.rects[2].x=m.rects[1].x+m.rects[1].width;
        break;
        default:break;
    }
}

//模板平移
void MyHaar::model_move(model &m, int bios_x, int bios_y){
    for(vector<Rect>::iterator iter=m.rects.begin();iter!=m.rects.end();iter++){
        iter->x+=bios_x;
        iter->y+=bios_y;
    }
}

//重置模板的x坐标为0
void MyHaar::x_reset(model &m){
    m.rects[0].x=0;
    switch(m.type){
        case 1:
            m.rects[1].x=m.rects[0].x+m.rects[0].width;
        break;
        case 2:
            m.rects[1].y=m.rects[0].y+m.rects[0].height;
        break;
        case 3:
            m.rects[1].x=m.rects[0].x+m.rects[0].width;
            m.rects[2].x=m.rects[1].x+m.rects[1].width;
        break;
        default:break;
    }
}

//重置模板的y坐标为0
void MyHaar::y_reset(model &m){
    m.rects[0].y=0;
    switch(m.type){
        case 1:
            m.rects[1].x=m.rects[0].x+m.rects[0].width;
        break;
        case 2:
            m.rects[1].y=m.rects[0].y+m.rects[0].height;
        break;
        case 3:
            m.rects[1].x=m.rects[0].x+m.rects[0].width;
            m.rects[2].x=m.rects[1].x+m.rects[1].width;
        break;
        default:break;
    }
}

main函数main.cpp:

#include <iostream>
#include <opencv2/opencv.hpp>
#include "my_haar.h"

using namespace std;
using namespace cv;


//Mat image2=(Mat_<unsigned char> << );

model m_vertical;
model m_horizontal;
model m_center;

void init_model(){
    //模板vertical
    Rect r=Rect(0,0,2,4);
    m_vertical.rects.push_back(r);
    m_vertical.flag.push_back(1);
    r=Rect(2,0,2,4);
    m_vertical.rects.push_back(r);
    m_vertical.flag.push_back(-1);
    m_vertical.type=VERTICAL;

    //模板horizontal
    r=Rect(0,0,4,2);
    m_horizontal.rects.push_back(r);
    m_horizontal.flag.push_back(-1);
    r=Rect(0,2,4,2);
    m_horizontal.rects.push_back(r);
    m_horizontal.flag.push_back(1);
    m_horizontal.type=HORIZONTAL;

    //模板center
    r=Rect(0,0,2,4);
    m_center.rects.push_back(r);
    m_center.flag.push_back(1);
    r=Rect(2,0,4,4);
    m_center.rects.push_back(r);
    m_center.flag.push_back(-1);
    r=Rect(6,0,2,4);
    m_center.rects.push_back(r);
    m_center.flag.push_back(1);

}

int main(){
    init_model();

    MyHaar mh;
    Mat image=imread("lena.jpg");
    //imshow("1",image);
    cvtColor(image,image,CV_RGB2GRAY);
    Mat src;
    cout<<"OK"<<endl;
    vector<double> descriptor;
    mh.compute(image,descriptor,m_vertical);
//    cout<<descriptor.size();
    for(vector<double>::iterator iter=descriptor.begin();iter!=descriptor.end();iter++)
        cout<<*iter<<' ';
    waitKey(0);
    return 0;
}

        main.cpp中只定义了三种模板,如果要添加其他类型的模板,可以在main.cpp中添加并在init()函数中初始化。代码中步长默认值为4,伸缩速度为1.5,运行代码得到三种模板中某一个模板的特征值的维度都有几千维。


        其中的每一个数值都代表了一个模板在某一个尺度在图片中的某一个位置计算得到的特征值,该代码中的模板种类很少也没有实现后来Haar特征提取算法所提出的斜的模板,因此提取来的特征运用于分类器的训练和检测效果应该没有保证,改代码只是基于Haar的原理进行了一次流程的重现。如果我们定义了多种矩形模板并能自动选择一个合适的维度,应该就能将由此提取出来的超高维特征向量送入分类其中进行训练。

通过 scikit-learn 实现文本特征提取

Python 在机器学习领域应用是非常广泛的,比如,我们可以使用机器学习进行验证码识别,使用机器学习实现计算机视觉项目,或者,我们也可以使用机器学习技术实现网页分类、文本挖掘、情感分析等等各种各样的事情。机器学习的重点在于算法,而算法的学习相对来说是比较枯燥的,所以,只有在学习的时候让算法跟实例结合,才能够让算法的学习变得不枯燥,并且也才能够更好的将理论运用与实践。
  • 2017年05月31日 15:56

图像算法之八:特征提取算法之Haar

一、AdaBoost算法原理     AdaBoost算法是一种迭代的算法,对于一组训练集,通过改变其中每个样本的分布概率,而得到不同的训练集Si,对于每一个Si进行训练从而得到一个弱分类器Hi,再...
  • SoaringLee_fighting
  • SoaringLee_fighting
  • 2016-09-30 09:13:30
  • 1956

特征提取之Haar特征

特征提取之Haar特征一、前言(废话)很久没有写博客了,一晃几年就过去了,为了总结一下自己看的一些论文,以后打算写一些自己读完论文的总结。那么,今天就谈一谈人脸检测最为经典的算法Haar-like特征...
  • xizero00
  • xizero00
  • 2015-08-06 00:02:58
  • 18036

haar特征提取 matlab

  • 2016年03月04日 19:38
  • 2KB
  • 下载

目标检测算法-特征提取之(一)Haar特征

目标检测的图像特征提取之(一)Haar特征 转载博客:http://blog.csdn.net/zy1034092330/article/details/488504371、Haar-like特征 ...
  • u013403054
  • u013403054
  • 2017-11-06 20:14:27
  • 307

haar特征提取代码 MATLAB版

  • 2014年12月26日 14:34
  • 10KB
  • 下载

全局Haar-Like特征图像识别的C++实现

 cvdirectcascade.h:#include "cv.h"struct TWeakClassifier...{    CvRect Rectangles[2];};struct PtrWea...
  • thirdapple
  • thirdapple
  • 2006-06-29 19:29:00
  • 6081

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

一.Haar-like特征
  • qianxin_dh
  • qianxin_dh
  • 2014-09-14 13:40:16
  • 2441

OpenCV源码中Haar训练及特征提取的代码说明

本文转自:http://www.cnblogs.com/YCwavelet/p/3545525.html。本文虽然是转载的,但是我稍微调整了一下版面,所以看着是比原来的舒服多了。,大家如有疑问还是请参...
  • ding977921830
  • ding977921830
  • 2015-06-24 16:08:19
  • 2262

手势识别之-----Haar特征提取

1、Haar-like特征        Haar-like特征最早是由Papageorgiou等应用于人脸表示,Viola和Jones在此基础上,使用3种类型4种形式的特征。 Haar特征分为三类:...
  • nameix
  • nameix
  • 2016-07-22 14:45:56
  • 1274
收藏助手
不良信息举报
您举报文章:Haar特征提取算法的实现
举报原因:
原因补充:

(最多只允许输入30个字)