Haar特征提取算法的实现

Haar特征提取算法的实现自己动手 丰衣食足 系列 nbsp nbsp nbsp nbsp nbsp nbsp nbsp nbsp Haar 特征是一种很早就被提出的图像特征提取算法 后面还经过了几次改进 Haar 特征能够很好地运用于人脸识别技术 当然很多目标检测技术中对目标图像的特征提取也可以使用 Haar 特征 当我们使用 opencv 自带的 cascade 分类器时可以选择 Haar 特征作为训练样本数据的特征描述子 然后将特征描述子作为样本数据送入 cascade 分类器中 就可以通过 Adab

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

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

网上有太多的Haar特征提取算法的原理介绍,这里推荐一篇:https://blog.csdn.net/lanxuecc/article/details/原理过程已经介绍得非常详细。

个人总结:

  • 理解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; //这里使用Mat::at函数需要注意第一参数为行数对应的y和高度height,第二个参数对应才是列数对应的x和宽度width sum=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(){ //模板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特征提取算法所提出的斜的模板,因此提取来的特征运用于分类器的训练和检测效果应该没有保证,改代码只是基于Haar的原理进行了一次流程的重现。如果我们定义了多种矩形模板并能自动选择一个合适的维度,应该就能将由此提取出来的超高维特征向量送入分类其中进行训练。

 

 

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/233541.html原文链接:https://javaforall.net

(0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • java android实例_Android开发精典案例60个

    java android实例_Android开发精典案例60个【实例简介】Android开发精典案例60个【实例截图】【核心代码】2-1(Activity生命周期)3-1(Button与点击监听器)3-10-1(列表之ArrayAdapter适配)3-10-2(列表之SimpleAdapter适配)3-11(Dialog对话框)3-12-5(Activity跳转与操作)3-12-6(横竖屏切换处理)3-3(ImageButton图片按钮)3-4(EditTe…

    2022年6月17日
    24
  • SpringMVC面试题总结「建议收藏」

    SpringMVC面试题总结「建议收藏」前言:SpringMVC的面试题常见的也就那几种,本文我打算分为两个方向为大家介绍SpringMVC的面试题。第一部分将从源码的执行的角度分析SpringMVC(以后简称MVC)第二部分将从面试官常问的SpringMVC面试题取介绍SpringMVC源码介绍1.http://localhost:8000/hello这个路径的执行流程是怎么走的流程大致分析一下:首先会请求会进入前…

    2022年6月19日
    23
  • 此网站无法提供安全连接(客户端和服务器不支持一般 SSL 协议版本或加密套件。)[通俗易懂]

    此网站无法提供安全连接(客户端和服务器不支持一般 SSL 协议版本或加密套件。)[通俗易懂]最近访问一部分网站时,出现如下图所示“此网站无法提供案例连接,客户端和服务器不支持一般SSL协议版本或加密套件”的问题。注意这里显示了非常关键的一句话,xxx使用了不受支持的协议。从这句话入手,很快就查到了原因。原来是网站不支持HTTPS协议,我将HTTPS改为HTTP访问,问题得到解决。…

    2022年5月5日
    221
  • vue生成二维码并保存图片_vue实现扫描二维码

    vue生成二维码并保存图片_vue实现扫描二维码<template><div><qrcode-vueid=”qrcodeBox”:size=”qrcodeVue.size”:value=”qrcodeVue.value”:logo=”qrcodeVue.logo”:bgColor=”qrcodeVue.bgColor”:fgColor=”qrcodeVue.fgColor”></qrcode-vue&gt

    2022年10月3日
    0
  • 什么叫应用程序域?(zhuan)

    什么叫应用程序域?(zhuan) 一:应用程序域介绍:    "域",就是范围,环境,边界的意思,那么什么是应用程序域,官方给出的是这样的解释:操作系

    2022年7月3日
    15
  • java打印数组_Java中打印数组的三种方式

    java打印数组_Java中打印数组的三种方式说明:System.out.println(array);这样是不行的,这样打印是的是数组的首地址。Arrays.toString(array);一维数组定义一个数组:int[]array={1,2,3,4,5};传统的for循环:for(inti=0;i{System.out.println(a[i]);}foreach增强循环:for(inta:array)Sys…

    2022年6月5日
    31

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号