使用SIFT和RANSAC算法,完成特征点的正确匹配,并求出变换矩阵,通过变换矩阵计算出要识别物体的边界

来源:互联网 发布:python自动化管理 编辑:程序博客网 时间:2024/05/17 08:52

使用SIFT和RANSAC算法,完成特征点的正确匹配,并求出变换矩阵,通过变换矩阵计算出要识别物体的边界

CV_INLINE float cvEvalFastHaarFeature( const CvFastHaarFeature* feature,
                                       const sum_type* sum, const sum_type* tilted )
{
    const sum_type* img = feature->tilted ? tilted : sum;//此处img是判断是否是旋转后的。如果不是,那么这个是已经计算了每个位置的像素积分和的。
    float ret = feature->rect[0].weight*
        (img[feature->rect[0].p0] - img[feature->rect[0].p1] -
         img[feature->rect[0].p2] + img[feature->rect[0].p3]) +
         feature->rect[1].weight*
        (img[feature->rect[1].p0] - img[feature->rect[1].p1] -
         img[feature->rect[1].p2] + img[feature->rect[1].p3]);

    if( feature->rect[2].weight != 0.0f )
        ret += feature->rect[2].weight *
            ( img[feature->rect[2].p0] - img[feature->rect[2].p1] -
              img[feature->rect[2].p2] + img[feature->rect[2].p3] );
    return ret;
}

typedef struct CvTHaarFeature

{

    char desc[CV_HAAR_FEATURE_DESC_MAX];

    int  tilted;

    struct

    {

        CvRect r;

        float weight;

    rect[CV_HAAR_FEATURE_MAX];

CvTHaarFeature;

 

typedef struct CvFastHaarFeature

{

    int tilted;

    struct

    {

        int p0p1p2p3;

        float weight;

    rect[CV_HAAR_FEATURE_MAX];

CvFastHaarFeature;

 

typedef struct CvIntHaarFeatures

{

    CvSize winsize;

    int count;

    CvTHaarFeaturefeature;

    CvFastHaarFeaturefastfeature;

CvIntHaarFeatures;

其中CvTHaarFeature和CvFastHaarFeature的区别在于:CvTHaarFeature是标示特征覆盖的窗口的坐标(Cvrect r),CvFastHaarFeature是将特征覆盖的窗口区域拉直,然后计算cvEvalFastHaarFeature.

二、boost
1、boost过程流程,将各种类型归为一个函数cvBoostStartTraining/cvBoostNextWeakClassifier,
通过参数区分不同类型的boost。都是在最优弱分类器已知,各样本对该分类器估计值已计算存入weakEvalVals
typedef struct CvBoostTrainer
{
CvBoostType type;
int count;
int* idx;//要么null,要么存样本索引号
float* F;//存logiBoost的F
} CvBoostTrainer;

调用顺序:cvBoostStartTraining ———> startTraining[type] ———> icvBoostStartTraining等
//定义函数cvBoostStartTraining
CvBoostTrainer* cvBoostStartTraining( ...,CvBoostType type )
{
return startTraining[type]( trainClasses, weakTrainVals, weights, sampleIdx, type );
}
//定义函数指针类型的数组变量startTraining[4]
CvBoostStartTraining startTraining[4] = {
icvBoostStartTraining,
icvBoostStartTraining,
icvBoostStartTrainingLB,
icvBoostStartTraining
};

调用顺序:cvBoostNextWeakClassifier———> nextWeakClassifier[trainer->type]———> icvBoostNextWeakClassifierLB等

函数                 poscount = icvGetHaarTrainingDataFromVec( training_data, 0, npos,
                    (CvIntHaarClassifier*) tcc, vecfilename, &consumed )负责从正样本集*.vec 文件中载入 count(npos)个正样本。在程序第一次运行到此(即训练第一个分类器之前)时,只要正样本集中有 count 个样本,就一定能取出 count 个正样本。在以后运行到此时,有可能取不到 count 个样本,因为
必须是用前面的级联强分类器((CvIntHaarClassifier*) tcc)分类为正样本(即分类正确的样本)的样本才会被取出作为下一个强分类器训练样本,具体可参考 icvGetHaarTrainingData和icvEvalTreeCascadeClassifierFilter函数。

 

训练负样本,具体可参考icvGetHaarTrainingDataFromBG和icvEvalTreeCascadeClassifierFilter函数。

int icvGetHaarTrainingDataFromBG( CvHaarTrainingData* data, int first, int count,
                                  CvIntHaarClassifier* cascade, double* acceptance_ratio, const char * filename = NULL )
  传递返回值的 acceptance_ratio 参数记录的是实际取出的负样本数与查询过的负样本数(如果通过前面级联stage强分类器的负样本数很少时,那么程序会循环重复读取负样本,并用thread_consumed_count计数)之比(acceptance_ratio = ((double) count) / consumed_count),也就是虚警率,用于判断已训练的级联分类器是否达到指标,若达到指标,则停止训练过程。 

 
  注意函数 icvGetHaarTrainingData中一个主要的 For 循环:
        for( i = first; i < first + count; i++ ) //共读取 count 个负样本,当读取不到
        {                            //这么多负样本时将出现死循环!
 
  对上面代码中的注释有必要进一步说明一下:只有当之前的强分类器对负样本集内的样本全部分类正确时才会出现死循环。因为只要有一个样本会被错分为正样本,那么通过 count次扫描整个负样本集就能得到 count 个负样本,当然这 count 个负样本实际上就是一个负样本的 count 个拷贝。为避免这些情况的发生,负样本集中的样本数需要足够多。
  在负样本图像大小与正样本大小完全一致时,假设最终的分类器虚警率要求是falsealarm,参加训练的负样本要求是 count 个,则需要的负样本总数可计算如下: TotalCount = count / falsealarm
  以 Rainer Lienhart 的文章中的一些参数为例,falsealarm=0.5^20=9.6e-07, count=3000,
则 TotalCount=3000/(0.5^20)= 3,145,728,000=31 亿。

函数 icvGetHaarTrainingDataFromBG ()负责从负样本集中载入 count 个负样本。在程序第一次运行到此(即训练第一个分类器之前)时,只要负样本集中有 count 个样本,就一定能取出 count 个负样本。在以后运行到此时,有可能取不到 count 个样本,因为必须是用前面的级联强分类器分类为正样本的样本(即分类错误的样本)才会被取出作为下一个强分类器的负样本输入。



OpenCV自带的adaboost程序能够根据用户输入的正样本集与负样本集训练分类器,常用于人脸检测,行人检测等。它的默认特征采用了Haar,不支持其它特征。

Adaboost的原理简述:(原文

每个Haar特征对应看一个弱分类器,但并不是任伺一个Haar特征都能较好的描述人脸灰度分布的某一特点,如何从大量的Haar特征中挑选出最优的Haar特征并制作成分类器用于人脸检测,这是AdaBoost算法训练过程所要解决的关键问题。

    Paul Viola和Michael Jones于2001年将Adaboost算法应用于人脸检测中,其基本思想是针对不同的训练集训练同一个分类器(弱分类器),然后把这些不同训练集上的得到的分类器联合起来,构成一个最终的强分类器。Adaboost 算法中不同的训练集是通过调整每个样本对应的权重来实现的。开始时,每个样本对应的权重是相同的,对于h1 分类错误的样本,加大其对应的权重; 而对于分类正确的样本, 降低其权重, 这样分错的样本就被突出出来,从而得到一个新的样本分布 U2 。在新的样本分布下,再次对弱分类器进行训练,得到弱分类器 h2 。依次类推,经过 T 次循环,得到 T 个弱分类器,把这 T 个弱分类器按一定的权重叠加(boost)起来,得到最终想要的强分类器。

     训练系统总体框架,由“ 训练部分”和 “ 补充部分”构成。依据系统框架,本文的训练系统可分为以下几个模块:
     (1)以样本集为输入,在给定的矩形特征原型下,计算并获得矩形特征集;
     (2)以特征集为输入,根据给定的弱学习算法,确定闽值,将特征与弱分类器一一对应,获得弱分类器集;
     (3)以弱分类器集为输入, 在训练检出率和误判率限制下, 使用A d a B o o s t 算法
挑选最优的弱分类器构成强分类器;
     (4)以强分类器集为输入,将其组合为级联分类器;
     (5)以非人脸图片集为输入,组合强分类器为临时的级联分类器,筛选并补充
非人脸样本。

训练样本的选择:
    训练样本要求是面部特写图像,图1是一簇训练样本,大小被归一化为24×24像素,其中正训练样本要求是面部特写图像,但是人脸形态千差万别,所以训练样本选取过程中要考虑到样本的多样性。负训练样本,大小被归一化为24×24像素,其中各样本不完全相同,分别具有一定的代表性。

训练过程分为3个步骤:首先需要提取Haar特征;然后将Haar特征转化成对应的弱分类器;最后从大量的弱分类器中迭代选择出最优弱分类器。
    (1)提取Haar特征

 

常用的Haar特征有4种,如图2所示。当然也可以在这4种特征的基础上设计出更多、更复杂的特征。以大小为24X24像素的训练样本为例,上述4种特征的总个数超过了160000个。这样庞大的数字给后续的迭代训练工作带来了庞大的计算量,直接导致AdaBoost算法训练过程极为费时,这恰恰是算法需要改进的关键问题之一o

    (2)生成弱分类器
    每一个Haar特征都对应着一个弱分类器,每一个弱分类器都是根据它所对应的Haar特征的参数来定义的。利用上述Haar特征的位置信息,对训练样本进行统计就可以得到对应的特征参数。AdaBoost算法中所训练的弱分类器是任何分类器,包括决策树,神经网络,隐马尔科夫模型,如果弱分类器是线性神经网络,那么AdaBoost算法每次将构造多层感知器的一个节点。

    (3)采用AdaBoost算法选取优化的弱分类器
    AdaBoost算法训练过程就是挑选最优弱分类器,并赋予权重过程,图3是AdaBoost算法训练示意图。

1.如果跑到某一个分类器时,几个小时也没有反应,而且显示不出训练百分比,这是因为你的负样本数量太少,或者负样本的尺寸太小,所有的负样本在这个分类器都被reject了,程序进入不了下一个循环,果断放弃吧。解决方法:负样本尽量要大一些,比如我的正样本是40*15,共300个,负样本是640*480,共500个。

2.读取样本时报错:Negative or too large argument of CvAlloc function,网上说这个错误是因为opencv规定单幅iplimage的内存分配不能超过10000,可是我的每个负样本都不会超过这个大小,具体原因不明。后来我把负样本的数量减少,尺寸加大,这个问题就解决了。

3.训练的过程可能经常出错,耐心下来不要着急,我在训练MRI分类器的时候失败了无数次。失败的时候有两件事可以做,第一,调整正负样本的数量,再试。第二,调整负样本的大小,祝大家好运。


三、svm图像分类的基本流程

  1. 数据获取
    比如摄像机或视频头的输出,通过采样获得数据,也可以是一般的统计数据集,其中的数据以向量或矩阵形式表示,或者是已经准备好的待检测的图片
  2. 训练图片特征提取和选择
    特征提取是指从对象本身获取各种对于分类有用的度量或属性。特征选择是指如何从描述对象的多种特征中找出那些对于分类最有效的特征。特征提取我们用到了surf算法。Surf具有比sift快的检测速度。
    对某一类模式的识别,其关键在于对模式特征的描述以及如何去提取这些特征。征描述直接影响到特征提取以及特征向量库的建立,并影响到最后分类识图像的特征提取和分类别精度的高低 从理论上讲,个体的特征是唯一的,这是因为不存在完全相同的两个个体。但是由于客观条件限制的存在,往往使得选取的特征并不是描述个体的特征全集,而只是特征的一个子集。因此,确定物体的本质特征是识别任务成功的关键。为了提高特征提取时计算的鲁棒性,往往又要求用尽可能少的特征来描述物体,这使得在实际应用中特征描述的不完全性是不可避免的。
  3. 将这些feature聚成n类。这n类中的每一类就相当于是图片的“单词”,所有的n个类别构成“词汇表”。我的实现中n取1000,如果训练集很大,应增大取值。
  4. 对训练集中的图片构造bag of words,就是将所有训练图片中的feature归到不同的类中,然后统计每一类的feature的频率。这相当于统计一个文本中每一个单词出现的频率。
  5. 分类器的设计(也就是训练分类器)
    利用样本数据来确定分类器的过程称为分类器设计。训练一个多类分类器,将每张图片的bag of words作为feature vector,将该张图片的类别作为label。支持向量机(Support Vector Machines,SVM)应用发热典型流程是首先提取图形的局部特征所形成的特征单词的直方图来作为特征,最后被通过SVM进行训练得到模型。
    在图像分类中用到了一种模型叫做BOW (bag of words) 模型。Bag of words模型最初被用在文本分类中,将文档表示成特征矢量。它的基本思想是假定对于一个文本,忽略其词序和语法、句法,仅仅将其看做是一些词汇的集合,而文本中的每个词汇都是独立的。简单说就是讲每篇文档都看成一个袋子(因为里面装的都是词汇,所以称为词袋,Bag of words即因此而来),然后看这个袋子里装的都是些什么词汇,将其分类。如果文档中猪、马、牛、羊、山谷、土地、拖拉机这样的词汇多些,而银行、大厦、汽车、公园这样的词汇少些,我们就倾向于判断它是一篇描绘乡村的文档,而不是描述城镇的。
    最后通过训练好的模型,再次读取未经分类的图片,就可以对其分类。
    流程图如图所示:
    这里写图片描述

四、所用到的技术方法

  1. 特征提取算法surf
    有关surf算法的介绍网上有很多,在这里我就不一一介绍,大家也可以参考下面的文章:http://blog.csdn.net/yujiflying/article/details/8203511

  2. 聚类算法:Kmeans算法
    k-means 算法接受参数 k ;然后将事先输入的n个数据对象划分为 k个聚类以便使得所获得的聚类满足:同一聚类中的对象相似度较高;而不同聚类中的对象相似度较小。聚类相似度是利用各聚类中对象的均值所获得一个“中心对象”(引力中心)来进行计算的。
    K-means算法是最为经典的基于划分的聚类方法,是十大经典数据挖掘算法之一。K-means算法的基本思想是:以空间中k个点为中心进行聚类,对最靠近他们的对象归类。通过迭代的方法,逐次更新各聚类中心的值,直至得到最好的聚类结果。
    假设要把样本集分为c个类别,算法描述如下:
    (1)适当选择c个类的初始中心;
    (2)在第k次迭代中,对任意一个样本,求其到c各中心的距离,将该样本归到距离最短的中心所在的类;
    (3)利用均值等方法更新该类的中心值;
    (4)对于所有的c个聚类中心,如果利用(2)(3)的迭代法更新后,值保持不变,则迭代结束,否则继续迭代。
    该算法的最大优势在于简洁和快速。算法的关键在于初始中心的选择和距离公式。
    该算法流程首先从n个数据对象任意选择 k 个对象作为初始聚类中心;而对于所剩下其它对象,则根据它们与这些聚类中心的相似度(距离),分别将它们分配给与其最相似的(聚类中心所代表的)聚类;然后再计算每个所获新聚类的聚类中心(该聚类中所有对象的均值);不断重复这一过程直到标准测度函数开始收敛为止。一般都采用均方差作为标准测度函数. k个聚类具有以下特点:各聚类本身尽可能的紧凑,而各聚类之间尽可能的分开。

  3. Bag of words(BOW)模型
    最初的Bag of words,也叫做“词袋”,在信息检索中,Bag of words model假定对于一个文本,忽略其词序和语法,句法,将其仅仅看做是一个词集合,或者说是词的一个组合,文本中每个词的出现都是独立的,不依赖于其他词 是否出现,或者说当这篇文章的作者在任意一个位置选择一个词汇都不受前面句子的影响而独立选择的。
    现在Computer Vision中的Bag of words来表示图像的特征描述也是很流行的。大体思想是这样的,假设有5类图像,每一类中有10幅图像,这样首先对每一幅图像划分成patch(可以是刚性分割也可以是像Surf基于关键点检测的),这样,每一个图像就由很多个patch表示,每一个patch用一个特征向量来表示,咱就假设用SURF表示的,一幅图像可能会有成百上千个patch,每一个patch特征向量的维数128。
    接下来就要进行构建Bag of words模型了,假设Dictionary词典的Size为1000,即有1000个词。那么咱们可以用K-means算法对所有的patch进行聚类,k=1000,我们知道,等k-means收敛时,我们也得到了每一个cluster最后的质心,那么这1000个质心(维数128)就是词典里的1000个词了,词典构建完毕。
    对于bag of words 和K-means算法这一篇文章也挺容易理解的http://www.cnblogs.com/v-July-v/archive/2011/06/20/2091170.html

  4. svm分类器
    支持向量机 (SVM) 是一个类分类器,正式的定义是一个能够将不同类样本在样本空间分隔的超平面。 换句话说,给定一些标记(label)好的训练样本 (监督式学习), SVM算法输出一个最优化的分隔超平面(分类面)。具体可以参考文章:http://blog.csdn.net/sunanger_wang/article/details/7887218






0 0
原创粉丝点击