Haar特征原理与icvCreateIntHaarFeatures方法的具体实现附详细注释—— 人脸识别的尝试系列(二)

带着强烈的兴趣,上周开始人脸识别的尝试与学习,并且将具体的操作过程记录了下来

链接如下:http://blog.csdn.net/u011583927/article/details/44627493

这周开始了对于算法的深入学习,下面进入正题。


Haar特征的原理是什么?

Haar特征分为三类:边缘特征、线性特征、中心特征和对角线特征,组合成特征模板。特征模板内有白色和黑色两种矩形,并定义该模板的特征值为白色矩形像素和减去黑色矩形像素和(在opencv实现中为黑色-白色)。Haar特征值反映了图像的灰度变化情况。例如:脸部的一些特征能由矩形特征简单的描述,如:眼睛要比脸颊颜色要深,鼻梁两侧比鼻梁颜色要深,嘴巴比周围颜色要深等。但矩形特征只对一些简单的图形结构,如边缘、线段较敏感,所以只能描述特定走向(水平、垂直、对角)的结构。(本段文字及下面两幅图引用自http://blog.csdn.net/zouxy09/article/details/7929570)

Viola提出的haar特征:

 

Lienhart等牛们提出的Haar-like特征:

 

矩形特征可位于图像任意位置,大小也可以任意改变,所以矩形特征值是矩形模版类别、矩形位置和矩形大小这三个因素的函数,当然对于新提出的有旋转角度的haar特征,还要把旋转的因素考虑进去。

所以一个Haar特征的数据结构应该包含以下内容: 

*haar特征模板类型

*是否有旋转

*矩阵位置及大小

 

CvIntHaarFeatures是如何构成的?

Opencv中,我们用CvTHaarFeatureCvFastHaarFeature作为描述单个特征的数据结构,用CvIntHaarFeatures作为一个封装的类型,通过这个类型中的两个指针(分别是CvTHaarFeature*CvFastHaarFeature*指针)可以间接遍寻到存储的所有的特征。下面来看下它们的具体构造


CvTHaarFeature的数据结构:

//CvTHaarFeature:由(至多三个)矩形表示特征位置

typedef struct CvTHaarFeature

{

    char desc[CV_HAAR_FEATURE_DESC_MAX];   //描述haar特征模板类型的变量

    int  tilted; //标识是否有旋转,通过desc字符数组开头是否为tilted判断

    struct

    {

        CvRect r;

        float weight;

    } rect[CV_HAAR_FEATURE_MAX];            //三个矩形来描述特征位置

} CvTHaarFeature;

 

 

创建一个CvTHaarFeature特征:

/*例:haarFeature = cvHaarFeature("tilted_haar_y2",

                                    x, y, dx,2*dy, -1,

                                    x, y,dx,   dy, +2 );*/

CV_INLINECvTHaarFeature cvHaarFeature(const char* desc,

                            int x0, int y0, int w0,int h0, float wt0,

                            int x1, int y1, int w1,int h1, float wt1,

                            int x2, int y2, int w2,int h2, float wt2 )

{

    CvTHaarFeature hf;

 

    assert( CV_HAAR_FEATURE_MAX >= 3 );

    assert( strlen( desc ) <CV_HAAR_FEATURE_DESC_MAX );

 

    strcpy( &(hf.desc[0]), desc );

    hf.tilted = ( hf.desc[0] == 't' );

 

    hf.rect[0].r.x = x0;

    hf.rect[0].r.y = y0;

    hf.rect[0].r.width  = w0;

    hf.rect[0].r.height = h0;

    hf.rect[0].weight   = wt0;

 

    hf.rect[1].r.x = x1;

    hf.rect[1].r.y = y1;

    hf.rect[1].r.width  = w1;

    hf.rect[1].r.height = h1;

    hf.rect[1].weight   = wt1;

 

    hf.rect[2].r.x = x2;

    hf.rect[2].r.y = y2;

    hf.rect[2].r.width  = w2;

    hf.rect[2].r.height = h2;

    hf.rect[2].weight   = wt2;

 

    return hf;

}

 

 

CvFastHaarFeature的数据结构:

//CvTHaarFeature类似,不同的是通过4个点来描述特征矩形的位置大小信息

typedef struct CvFastHaarFeature

{

    int tilted;

    struct

    {

        int p0, p1, p2, p3;

        float weight;

    } rect[CV_HAAR_FEATURE_MAX];

} CvFastHaarFeature;

 

CvIntHaarFeatures的数据结构:

typedef struct CvIntHaarFeatures

{

    CvSize winsize;

    int count;

    CvTHaarFeature* feature;

    CvFastHaarFeature* fastfeature;

} CvIntHaarFeatures;


了解了如何构成,我们就来创建,icvCreateIntHaarFeatures()方法的具体实现:

接下来就是最重要的一步,如何创建我们想要得到的所有特征信息及CvIntHaarFeatures,下面是icvCreateIntHaarFeatures方法的具体实现和详细注释

由于opencv和C++都是初学,用了很长时间写了大量注释,0基础也绝对能看懂,希望能对大家有帮助


/*
* icvCreateIntHaarFeatures
*
* Create internal representation of haar features
*
* mode:
*  0 - BASIC = Viola提出的原始举行特征
*  1 - CORE  = All upright  所有垂直的haar特征
*  2 - ALL   = All features 所有haar特征
*symmetric: 目标图形是否为垂直对称
*/
static
CvIntHaarFeatures* icvCreateIntHaarFeatures( CvSize winsize,
int mode,
int symmetric )
{
CvIntHaarFeatures* features = NULL;
CvTHaarFeature haarFeature;
/*内存存储器是一个可用来存储诸如序列,轮廓,图形,子划分等动态增长数据结构的底层结构。它是由一系列以同等大小的内存块构成,呈列表型*/
CvMemStorage* storage = NULL;
CvSeq* seq = NULL;
CvSeqWriter writer;
int s0 = 36; /* minimum total area size of basic haar feature     */
int s1 = 12; /* minimum total area size of tilted(倾斜的) haar features 2 */
int s2 = 18; /* minimum total area size of tilted haar features 3 */
int s3 = 24; /* minimum total area size of tilted haar features 4 */
int x  = 0;
int y  = 0;
int dx = 0;
int dy = 0;
#if 0
float factor = 1.0F;
factor = ((float) winsize.width) * winsize.height / (24 * 24);
s0 = (int) (s0 * factor);
s1 = (int) (s1 * factor);
s2 = (int) (s2 * factor);
s3 = (int) (s3 * factor);
#else
//程序必然走这边,为什么这么写?
s0 = 1;
s1 = 1;
s2 = 1;
s3 = 1;
#endif
/* CV_VECTOR_CREATE( vec, CvIntHaarFeature, size, maxsize ) */
storage = cvCreateMemStorage();
//功能:创建新序列,并初始化写入部分
/*我的理解:这里其实是定义了writer工具每次写入数据的大小,以及写入到哪个内存存储器
在之后调用 CV_WRITE_SEQ_ELEM( haarFeature, writer )时就可以自动将一个haarFeature类型的数据写入内存存储器中*/
cvStartWriteSeq( 0, sizeof( CvSeq ), sizeof( haarFeature ), storage, &writer );
/*矩形特征可位于图像任意位置,大小也可以任意改变,所以矩形特征值是矩形模版类别、矩形位置和矩形大小这三个因素的函数*/
for( x = 0; x < winsize.width; x++ )
{
for( y = 0; y < winsize.height; y++ )
{
//x,y确定了特征矩形的左上角坐标
for( dx = 1; dx <= winsize.width; dx++ )
{
for( dy = 1; dy <= winsize.height; dy++ )
{
//dx,dy确定了特征矩形的大小
//下面需要按照不同的特征模板类型分别讨论,在模板不越界的情况下,添
加该特征
// haar_x2   对应上图中的(a)特征模板,黑色为+,白色为-
if ( (x+dx*2 <= winsize.width) && (y+dy <= winsize.height) ) {
if (dx*2*dy < s0) continue;
if (!symmetric || (x+x+dx*2 <=winsize.width)) 
{
//目标图像不为垂直对称或目标垂直对称但满足上式条件
//若目标不垂直对称,显然要计算当前矩形特征的特征值
//若对称,则只计算左半部分全部位于标准样本左半边的矩形特征的特征值
haarFeature = cvHaarFeature( "haar_x2",
x,    y, dx*2, dy, -1,
x+dx, y, dx  , dy, +2 );
/* CV_VECTOR_PUSH( vec, CvIntHaarFeature, haarFeature, size, maxsize, step ) */
CV_WRITE_SEQ_ELEM( haarFeature, writer );
}
}
// haar_y2   对应上图中的(b)特征模板
if ( (x+dx <= winsize.width) && (y+dy*2 <= winsize.height) ) {
if (dx*2*dy < s0) continue;
if (!symmetric || (x+x+dx <= winsize.width)) {
haarFeature = cvHaarFeature( "haar_y2",
x, y,    dx, dy*2, -1,
x, y+dy, dx, dy,   +2 );
CV_WRITE_SEQ_ELEM( haarFeature, writer );
}
}
// haar_x3    对应上图中的(c)特征模板
if ( (x+dx*3 <= winsize.width) && (y+dy <= winsize.height) ) {
if (dx*3*dy < s0) continue;
if (!symmetric || (x+x+dx*3 <=winsize.width)) {
haarFeature = cvHaarFeature( "haar_x3",
x,    y, dx*3, dy, -1,
x+dx, y, dx,   dy, +3 );
CV_WRITE_SEQ_ELEM( haarFeature, writer );
}
}
// haar_y3     对应上图中的(d)特征模板
if ( (x+dx <= winsize.width) && (y+dy*3 <= winsize.height) ) {
if (dx*3*dy < s0) continue;
if (!symmetric || (x+x+dx <= winsize.width)) {
haarFeature = cvHaarFeature( "haar_y3",
x, y,    dx, dy*3, -1,
x, y+dy, dx, dy,   +3 );
CV_WRITE_SEQ_ELEM( haarFeature, writer );
}
}
if( mode != 0 /*BASIC*/ ) {
// haar_x4     对应上图中的(2b)特征模板
if ( (x+dx*4 <= winsize.width) && (y+dy <= winsize.height) ) {
if (dx*4*dy < s0) continue;
if (!symmetric || (x+x+dx*4 <=winsize.width)) {
haarFeature = cvHaarFeature( "haar_x4",
x,    y, dx*4, dy, -1,
x+dx, y, dx*2, dy, +2 );
CV_WRITE_SEQ_ELEM( haarFeature, writer );
}
}
// haar_y4     对应上图中的(2d)特征模板
if ( (x+dx <= winsize.width ) && (y+dy*4 <= winsize.height) ) {
if (dx*4*dy < s0) continue;
if (!symmetric || (x+x+dx   <=winsize.width)) {
haarFeature = cvHaarFeature( "haar_y4",
x, y,    dx, dy*4, -1,
x, y+dy, dx, dy*2, +2 );
CV_WRITE_SEQ_ELEM( haarFeature, writer );
}
}
}
// x2_y2     对应上图中的(e)特征模板
if ( (x+dx*2 <= winsize.width) && (y+dy*2 <= winsize.height) ) {
if (dx*4*dy < s0) continue;
if (!symmetric || (x+x+dx*2 <=winsize.width)) {
haarFeature = cvHaarFeature( "haar_x2_y2",
x   , y,    dx*2, dy*2, -1,
x   , y   , dx  , dy,   +2,
x+dx, y+dy, dx  , dy,   +2 );
CV_WRITE_SEQ_ELEM( haarFeature, writer );
}
}
if (mode != 0 /*BASIC*/) {
// point     对应上图中的(3a)特征模板
if ( (x+dx*3 <= winsize.width) && (y+dy*3 <= winsize.height) ) {
if (dx*9*dy < s0) continue;
if (!symmetric || (x+x+dx*3 <=winsize.width))  {
haarFeature = cvHaarFeature( "haar_point",
x   , y,    dx*3, dy*3, -1,
x+dx, y+dy, dx  , dy  , +9);
CV_WRITE_SEQ_ELEM( haarFeature, writer );
}
}
}
if (mode == 2 /*ALL*/) {
// tilted haar_x2                   (x, y, w, h, b, weight)
//对应上图中的(1c)特征模板
if ( (x+2*dx <= winsize.width) && (y+2*dx+dy <= winsize.height) && (x-dy>= 0) ) {
if (dx*2*dy < s1) continue;
if (!symmetric || (x <= (winsize.width / 2) )) {
haarFeature = cvHaarFeature( "tilted_haar_x2",
x, y, dx*2, dy, -1,
x, y, dx  , dy, +2 );
CV_WRITE_SEQ_ELEM( haarFeature, writer );
}
}
// tilted haar_y2                     (x, y, w, h, b, weight)
//对应上图中的(1d)特征模板
if ( (x+dx <= winsize.width) && (y+dx+2*dy <= winsize.height) && (x-2*dy>= 0) ) {
if (dx*2*dy < s1) continue;
if (!symmetric || (x <= (winsize.width / 2) )) {
haarFeature = cvHaarFeature( "tilted_haar_y2",
x, y, dx, 2*dy, -1,
x, y, dx,   dy, +2 );
CV_WRITE_SEQ_ELEM( haarFeature, writer );
}
}
// tilted haar_x3                                   (x, y, w, h, b, weight)
if ( (x+3*dx <= winsize.width) && (y+3*dx+dy <= winsize.height) && (x-dy>= 0) ) {
if (dx*3*dy < s2) continue;
if (!symmetric || (x <= (winsize.width / 2) )) {
haarFeature = cvHaarFeature( "tilted_haar_x3",
x,    y,    dx*3, dy, -1,
x+dx, y+dx, dx  , dy, +3 );
CV_WRITE_SEQ_ELEM( haarFeature, writer );
}
}
// tilted haar_y3                                      (x, y, w, h, b, weight)
if ( (x+dx <= winsize.width) && (y+dx+3*dy <= winsize.height) && (x-3*dy>= 0) ) {
if (dx*3*dy < s2) continue;
if (!symmetric || (x <= (winsize.width / 2) )) {
haarFeature = cvHaarFeature( "tilted_haar_y3",
x,    y,    dx, 3*dy, -1,
x-dy, y+dy, dx,   dy, +3 );
CV_WRITE_SEQ_ELEM( haarFeature, writer );
}
}
// tilted haar_x4                                   (x, y, w, h, b, weight)
if ( (x+4*dx <= winsize.width) && (y+4*dx+dy <= winsize.height) && (x-dy>= 0) ) {
if (dx*4*dy < s3) continue;
if (!symmetric || (x <= (winsize.width / 2) )) {
haarFeature = cvHaarFeature( "tilted_haar_x4",
x,    y,    dx*4, dy, -1,
x+dx, y+dx, dx*2, dy, +2 );
CV_WRITE_SEQ_ELEM( haarFeature, writer );
}
}
// tilted haar_y4                                      (x, y, w, h, b, weight)
if ( (x+dx <= winsize.width) && (y+dx+4*dy <= winsize.height) && (x-4*dy>= 0) ) {
if (dx*4*dy < s3) continue;
if (!symmetric || (x <= (winsize.width / 2) )) {
haarFeature = cvHaarFeature( "tilted_haar_y4",
x,    y,    dx, 4*dy, -1,
x-dy, y+dy, dx, 2*dy, +2 );
CV_WRITE_SEQ_ELEM( haarFeature, writer );
}
}
/*
// tilted point
if ( (x+dx*3 <= winsize.width - 1) && (y+dy*3 <= winsize.height - 1) && (x-3*dy>= 0)) {
if (dx*9*dy < 36) continue;
if (!symmetric || (x <= (winsize.width / 2) ))  {
haarFeature = cvHaarFeature( "tilted_haar_point",
x, y,    dx*3, dy*3, -1,
x, y+dy, dx  , dy,   +9 );
CV_WRITE_SEQ_ELEM( haarFeature, writer );
}
}
*/
}
}
}
}
}
/*我的理解:当前已经完成了数据的写入,但是是存储在内存存储器中的,调用此方法将存储器中的所有数据转移到cvSeq中*/
seq = cvEndWriteSeq( &writer );
在OpenCV中临时缓存用cvAlloc和cvFree函数分配和回收.函数应注意适当对齐,对未释放的内存保持跟踪,检查溢出。
features = (CvIntHaarFeatures*) cvAlloc( sizeof( CvIntHaarFeatures ) +
( sizeof( CvTHaarFeature ) + sizeof( CvFastHaarFeature ) ) * seq->total );
features->feature = (CvTHaarFeature*) (features + 1);
features->fastfeature = (CvFastHaarFeature*) ( features->feature + seq->total );
features->count = seq->total;
features->winsize = winsize;
cvCvtSeqToArray( seq, (CvArr*) features->feature );
cvReleaseMemStorage( &storage );
//特征的rect由坐标表示转换为由像素索引表示
icvConvertToFastHaarFeature( features->feature, features->fastfeature,
features->count, (winsize.width + 1) );
return features;
}


由于我是小白,感觉上面代码中的最后几行逻辑可能比较绕,卡在那里好久终于理解。

所以做了一张图,可以借助下图帮助理解


<span style="font-family: Arial, Helvetica, sans-serif;">看到这里,对于Haar特征的理论以及实际使用应该理解的不错了吧,希望能和大家一起交流</span>





本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/499577.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

createsamples.cpp中生成vec文件的实现及详细注释、图解——人脸识别的尝试系列(三)

在我们开始训练我们的Haar分类器之前&#xff0c;首先要对样本进行处理。 人脸识别的尝试系列&#xff08;一&#xff09;中&#xff1a;http://blog.csdn.net/u011583927/article/details/44627493 我们已经提到了如何准备我们的样本&#xff0c;在如下图准备好样本之后 需…

设置同时上内外网+文件共享

最近做了这样一个小项目&#xff0c;逻辑是这样的。 需要在某个办公人员的电脑上装个软件&#xff0c;从局域网中的另一台电脑中读取access数据库&#xff0c;然后用apn接入某个系统的内网传输数据。 同时还要保证这个工作人员能够正常的浏览因特网。 这就涉及到了两个内容。…

浅析haartraining方法进行人脸检测

上个月用了两周的时间&#xff0c;学习了用于人脸检测的haartraining算法&#xff0c;今天打算做一总结 首先先为和我一样的初学者推荐几篇博客 http://blog.csdn.net/zouxy09/article/details/7922923真的很感谢写这篇文章的博主&#xff0c;讲解深入浅出。本文中的主要逻辑…

haartraining训练分类器方法cvCreateTreeCascadeClassifier()详解——人脸识别的尝试系列(四)

本文将介绍opencv_haartraining.exe中训练分类器的核心方法cvCreateTreeCascadeClassifier&#xff08;&#xff09;中参数的具体含义&#xff0c;以及具体实现代码附加详细的注释。最后给出运行截图以作代码阅读的参考 我们还是从具体的例子出发&#xff0c;以一些实际的参数帮…

常用知识总结——模板Template

1. 模板的概念。 我们已经学过重载(Overloading)&#xff0c;对重载函数而言,C的检查机制能通过函数参数的不同及所属类的不同。正确的调用重载函数。例如&#xff0c;为求两个数的最大值&#xff0c;我们定义MAX()函数需要对不同的数据类型分别定义不同重载(Overload)版本。 /…

opencv视频读写和视频等间隔采样

今天学习了opencv的HighGUI的内容 总结了两个视频读写demo以备以后进行视频处理和识别用 demo1 视频的读取和写入 按顺序读取视频的每一帧。对于读取的每一帧图像&#xff0c;显示在窗口中&#xff0c;然后转化为灰度图像输出到指定的文件中。 运行期间可以按ESC键退出。 还…

Socket通用TCP通信协议设计及实现(防止粘包,可移植,可靠)

Socket通用TCP通信协议设计及实现&#xff08;防止粘包&#xff0c;可移植&#xff0c;可靠&#xff09; 引文 我们接收Socket字节流数据一般都会定义一个数据包协议。我们每次开发一个软件的通信模块时&#xff0c;尽管具体的数据内容是不尽相同的&#xff0c;但是大体上的框…

浅谈 Adaboost 算法

注&#xff1a;本文全文引用自http://blog.csdn.net/carson2005/article/details/41444289 当然作者也是转载的&#xff0c;原文是http://blog.csdn.net/haidao2009/article/details/7514787 写的很好所以转载过来以便之后再次翻阅。 一 Boosting 算法的起源 boost 算法系列的起…

如何理解离散傅里叶变换(一)实数形式傅里叶变换

如何理解离散傅里叶变换&#xff08;一&#xff09; ——实数形式傅里叶变换 ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- 本文…

快速傅里叶变换(FFT)

快速傅里叶变换&#xff08;FFT&#xff09; ------------------------------------------------------------------------------------------------------------------- 作者&#xff1a;随煜而安 时间&#xff1a;2015/7/21 注&#xff1a;本文为作者原创文章&#xff0c…

风机桨叶故障诊断(一) 样本的获取

风机桨叶故障诊断&#xff08;一&#xff09; 样本的获取今天团队接了个新项目&#xff0c;做一个风机桨叶故障诊断系统。虽然马上就是准备考研的关键期了&#xff0c;可是一想到这是我学习了机器学习后遇到的第一个实际项目&#xff0c;我觉得参与进来&#xff0c;也帮导师分担…

风机桨叶故障诊断(二) 获取图像几何主方向

风机桨叶故障诊断&#xff08;二&#xff09; 获取图像几何主方向 昨天&#xff0c;我将视频资源按帧抽取并筛选得到了可以用来提取样本的图像库。今天还是进行项目的准备工作。当我们拿到一张图片&#xff0c;我们的软件要做的大致可以分为三个步骤&#xff1a;从原图中识别桨…

风机桨叶故障诊断(三) 识别桨叶——初步构建BP神经网络

风机桨叶故障诊断&#xff08;三&#xff09; 识别桨叶——初步构建BP神经网络 新的一天&#xff0c;希望有好的运气。今天开始着手系统的第一个模块&#xff0c;从一幅图像中寻找到桨叶所在的位置。第一直觉我们的识别任务属于难度比较大&#xff0c;干扰因素多的了&#xff…

风机桨叶故障诊断(五) 修改隐含层神经元个数的尝试

风机桨叶故障诊断&#xff08;五&#xff09; 修改隐含层神经元个数的尝试 我们已经为训练一个更为稳健的神经网络做好了样本的准备工作&#xff0c;那么我们开始下一步的工作吧&#xff01; 我们已经有了样本集&#xff0c;目前我筛选出来了247个正样本&#xff0c;652个负样本…

风机桨叶故障诊断(六) 利用自编码器进行特征学习

风机桨叶故障诊断&#xff08;六&#xff09; 利用自编码器进行特征学习 在之前的工作中&#xff0c;我已经初步构建了三层的BP神经网络&#xff0c;并已经从样本集的选取&#xff0c;模型的选择&#xff08;隐含层神经元个数&#xff09;&#xff0c;和输出层神经元阈值选择这…