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

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坐标为0void 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);//将特征值保存至descriptorif(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_rectvector<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;
//这里使用Mat::at函数需要注意第一参数为行数对应的y和高度height,第二个参数对应才是列数对应的x和宽度widthsum=integral_img.at<double>(y,x)+integral_img.at<double>(y+height,x+width)-integral_img.at<double>(y+height,x)-integral_img.at<double>(y,x+width);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(){//模板verticalRect 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;//模板horizontalr=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;//模板centerr=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的原理进行了一次流程的重现。如果我们定义了多种矩形模板并能自动选择一个合适的维度,应该就能将由此提取出来的超高维特征向量送入分类其中进行训练。

Haar特征提取算法的实现相关推荐

  1. 基于MATLAB的图像压缩感知 算法的实现

    摘要 获取项目源文件,联系Q:1415736481,可指导毕设,课设 数据压缩技术是提高无线数据传输速度的有效措施之一.传统的数据压缩技术是基于奈奎斯特采样定律进行采样,并根据数据本身的特性降低其冗余 ...

  2. 计算机图形学 区域填充,计算机图形学 区域填充算法的实现

    . '. 实验四区域填充算法的实现班级 08信计学号 58 姓名陈瑞雪分数 一.实验目的和要求: 1.掌握区域填充算法基本知识 2.理解区域的表示和类型,能正确区分四连通和八连通的区域 3.了解区域填 ...

  3. OpenCV中图像旋转(warpAffine)算法的实现过程

    在OpenCV中,目前并没有现成的函数直接用来实现图像旋转,它是用仿射变换函数cv::warpAffine来实现的,此函数目前支持4种插值算法,最近邻.双线性.双三次.兰索斯插值,如果传进去的参数为基 ...

  4. JAVA实现中点画线_实验1-中点画线和Bresenham画线算法的实现

    <实验1-中点画线和Bresenham画线算法的实现>由会员分享,可在线阅读,更多相关<实验1-中点画线和Bresenham画线算法的实现(9页珍藏版)>请在人人文库网上搜索. ...

  5. python边缘检测代码_python Canny边缘检测算法的实现

    图像边缘信息主要集中在高频段,通常说图像锐化或检测边缘,实质就是高频滤波.我们知道微分运算是求信号的变化率,具有加强高频分量的作用.在空域运算中来说,对图像的锐化就是计算微分.对于数字图像的离散信号, ...

  6. 干货回顾丨TensorFlow四种Cross Entropy算法的实现和应用

    交叉熵介绍 交叉熵(Cross Entropy)是Loss函数的一种(也称为损失函数或代价函数),用于描述模型预测值与真实值的差距大小,常见的Loss函数就是均方平方差(Mean Squared Er ...

  7. C++基础代码--20余种数据结构和算法的实现

    C++基础代码--20余种数据结构和算法的实现 过年了,闲来无事,翻阅起以前写的代码,无意间找到了大学时写的一套C++工具集,主要是关于数据结构和算法.以及语言层面的工具类.过去好几年了,现在几乎已经 ...

  8. 选择性模糊及其算法的实现。

    选择性模糊及其算法的实现. 我们常见的模糊算法比如均值模糊.高斯模糊等其基本的过程都是计算一个像素周边的的某个领域内,相关像素的某个特征值的累加和及对应的权重,然后得到结果值.比如均值模糊的各像素的权 ...

  9. ML之SVM:基于Js代码利用SVM算法的实现根据Kaggle数据集预测泰坦尼克号生存人员

    ML之SVM:基于Js代码利用SVM算法的实现根据Kaggle数据集预测泰坦尼克号生存人员 目录 实验数据 设计思路​ 实现代码(部分代码) 实验数据 设计思路 实现代码(部分代码) /**js代码实 ...

  10. RSA算法和RSA数字签名算法的实现

    RSA算法和RSA数字签名算法的实现 http://blog.chinaunix.net/uid-21880738-id-1813146.html 顾婷婷 李涛 (四川大学计算机系(西区) 成都 61 ...

最新文章

  1. 【pytorch】nn.conv1d的使用
  2. linux 配置 java 环境变量
  3. 大佬教你Android如何实现时间线效果
  4. 最常用git命令汇总(参考列表)
  5. java 排序stackoverflow_JAVA开发知识点
  6. vue对象拼接_vue 俩个数组对象合并成一个
  7. 这一年里,你一共写了34万行代码
  8. linux addr2line
  9. PAT-甲级之树遍历问题的总结
  10. 【BOI2007】【BZOJ1176】Mokia
  11. C语言指针类型 强制转换
  12. 程序运行中(BSS段、数据段、代码段、堆栈)
  13. Ajax学习笔记-get请求参数-3
  14. Win 7 安装office visio
  15. [4G+5G专题-137]: 终端 - 射频前端(滤波器、功率放大器)详解
  16. Synthesys:语音合成和视频生成平台
  17. 快手和抖音怎么打开微信小程序
  18. mo和po文件相互转化
  19. 获取某一帧、某一秒、某几秒画面
  20. 小学计算机面试试讲题目,小学数学教师资格证面试试讲题目

热门文章

  1. “什么?中东的土豪给我们 App 充了大钱!”
  2. win10计算机管理被阻止,Win10家庭版安装程序提示系统管理员已阻止你运行此应用的解决办法...
  3. 高频电子线路实验箱QY-JXSY25
  4. 零基础java学习笔记
  5. GD32 startup.s
  6. 线性代数学习指导与MATLAB编程实践,21世纪高等理工科重点课程辅导丛书:线性代数学习指导与MATLAB编程实践...
  7. 《你的灯亮着吗?》 读后感
  8. c语言图形编程.pdf,C语言图形编程
  9. 【开源微信】Java实现基于Redis公众号模板消息队列
  10. 阿里巴巴Java开发手册代码规范