标签: svm图像
2015-07-30 08:45 8296人阅读 评论(35) 收藏 举报
 分类:
【opencv应用】(5) 

版权声明:本文为博主原创文章,未经博主允许不得转载。

 

目录(?)[+]

 

原创作品 转载请注明出http://blog.csdn.net/always2015/article/details/47107129

上一篇博文对图像分类理论部分做了比较详细的讲解,这一篇主要是对图像分类代码的实现进行分析。理论部分我们谈到了使用BOW模型,但是BOW模型如何构建以及整个步骤是怎么样的呢?可以参考下面的博客http://www.cnblogs.com/yxy8023ustc/p/3369867.html,这一篇博客很详细讲解了BOW模型的步骤了,主要包含以下四个步骤:

  1. 提取训练集中图片的feature
  2. 将这些feature聚成n类。这n类中的每一类就相当于是图片的“单词”,所有的n个类别构成“词汇表”。我的实现中n取1000,如果训练集很大,应增大取值。
  3. 对训练集中的图片构造bag of words,就是将图片中的feature归到不同的类中,然后统计每一类的feature的频率。这相当于统计一个文本中每一个单词出现的频率
  4. 训练一个多类分类器,将每张图片的bag of words作为feature vector,将该张图片的类别作为label。

对于未知类别的图片,计算它的bag of words,使用训练的分类器进行分类。 
上面整个工程步骤所涉及到的函数,我都放在一个类categorizer里, 
下面按步骤说明具体实现,程序示例有所省略,完整的程序可看工程源码。

NO.1、特征提取

对图片特征的提取包括对每张训练图片的特征提取和每张待检测图片特征的提取,我使用的是surf,所以使用OpenCV的SurfFeatureDetector检测特征点,然后再用SurfDescriptorExtractor抽取特征点描述符。对于特征点的检测和特征描述符的讲解可以参考中文opencv中文官网http://www.opencv.org.cn/opencvdoc/2.3.2/html/doc/tutorials/features2d/feature_detection/feature_detection.html#feature-detection以及http://www.opencv.org.cn/opencvdoc/2.3.2/html/doc/tutorials/features2d/feature_description/feature_description.html#feature-description 
我训练图片特征提取的示例代码如下:

  1. Mat vocab_descriptors;
  2. // 对于每一幅模板,提取SURF算子,存入到vocab_descriptors中
  3. multimap<string,Mat> ::iterator i=train_set.begin();
  4. for(;i!=train_set.end();i++)
  5. {
  6. vector<KeyPoint>kp;
  7. Mat templ=(*i).second;
  8. Mat descrip;
  9. featureDecter->detect(templ,kp);
  10. descriptorExtractor->compute(templ,kp,descrip);
  11. //push_back(Mat);在原来的Mat的最后一行后再加几行,元素为Mat时, 其类型和列的数目 必须和矩阵容器是相同的
  12. vocab_descriptors.push_back(descrip);
  13. }

注意:上述代码只是工程的一个很小的部分,有些变量在类中已经定义,在这里没有贴出来,例如上述的train_set训练图片的映射,定义为:

  1. //从类目名称到训练图集的映射,关键字可以重复出现
  2. multimap<string,Mat> train_set;

将每张图片的特征描述符存储起来vocab_descriptors,然后为后面聚类和构造训练图片词典做准备。

NO.2、feature聚类

由于opencv封装了一个类BOWKMeansExtractor[2],这一步非常简单,将所有图片的feature vector丢给这个类,然后调用cluster()就可以训练(使用KMeans方法)出指定数量(步骤介绍中提到的n)的类别。输入vocab_descriptors就是第1步计算得到的结果,返回的vocab是一千个向量,每个向量是某个类别的feature的中心点。 
示例代码如下:

  1. //将每一副图的Surf特征利用add函数加入到bowTraining中去,就可以进行聚类训练了
  2. bowtrainer->add(vocab_descriptors);
  3. // 对SURF描述子进行聚类
  4. vocab=bowtrainer->cluster();

bowtrainer的定义如下:

  1. bowtrainer=new BOWKMeansTrainer(clusters);

NO.3、构造bag of words

对每张图片的特征点,将其归到前面计算的类别中,统计这张图片各个类别出现的频率,作为这张图片的bag of words。由于opencv封装了BOWImgDescriptorExtractor[2]这个类,这一步也走得十分轻松,只需要把上面计算的vocab丢给它,然后用一张图片的特征点作为输入,它就会计算每一类的特征点的频率。

allsamples_bow这个map的key就是某个类别,value就是这个类别中所有图片的bag of words,即Mat中每一行都表示一张图片的bag of words。

  1. //对每张图片的特征点,统计这张图片各个类别出现的频率,作为这张图片的bag of words
  2. bowDescriptorExtractor->setVocabulary(vocab);
  3. }
  4. // 对于每一幅模板,提取SURF算子,存入到vocab_descriptors中
  5. multimap<string,Mat> ::iterator i=train_set.begin();
  6. for(;i!=train_set.end();i++)
  7. {
  8. vector<KeyPoint>kp;
  9. string cate_nam=(*i).first;
  10. Mat tem_image=(*i).second;
  11. Mat imageDescriptor;
  12. featureDecter->detect(tem_image,kp);
  13. bowDescriptorExtractor->compute(tem_image,kp,imageDescriptor);
  14. //push_back(Mat);在原来的Mat的最后一行后再加几行,元素为Mat时, 其类型和列的数目 必须和矩阵容器是相同的
  15. allsamples_bow[cate_nam].push_back(imageDescriptor);
  16. }

上面部分变量的定义如下:

  1. //存放所有训练图片的BOW
  2. map<string,Mat> allsamples_bow;
  3. //特征检测器detectors与描述子提取器extractors 泛型句柄类Ptr
  4. Ptr<FeatureDetector> featureDecter;
  5. Ptr<DescriptorExtractor> descriptorExtractor;
  6. Ptr<BOWKMeansTrainer> bowtrainer;
  7. Ptr<BOWImgDescriptorExtractor> bowDescriptorExtractor;
  8. Ptr<FlannBasedMatcher> descriptorMacher;

NO.4、训练分类器

我使用的分类器是svm,用经典的1 vs all方法实现多类分类。对每一个类别都训练一个二元分类器。训练好后,对于待分类的feature vector,使用每一个分类器计算分在该类的可能性,然后选择那个可能性最高的类别作为这个feature vector的类别。

训练二元分类器

allsamples_bow:第3步中得到的结果。 
category_name:针对哪个类别训练分类器。 
svmParams:训练svm使用的参数。 
stor_svms:针对category_name的分类器。 
属于category_name的样本,label为1;不属于的为-1。准备好每个样本及其对应的label之后,调用CvSvm的train方法就可以了。

示例代码如下:

  1. stor_svms=new CvSVM[categories_size];
  2. //设置训练参数
  3. SVMParams svmParams;
  4. svmParams.svm_type = CvSVM::C_SVC;
  5. svmParams.kernel_type = CvSVM::LINEAR;
  6. svmParams.term_crit = cvTermCriteria(CV_TERMCRIT_ITER, 100, 1e-6);
  7. cout<<"训练分类器..."<<endl;
  8. for(int i=0;i<categories_size;i++)
  9. {
  10. Mat tem_Samples( 0, allsamples_bow.at( category_name[i] ).cols, allsamples_bow.at( category_name[i] ).type() );
  11. Mat responses( 0, 1, CV_32SC1 );
  12. tem_Samples.push_back( allsamples_bow.at( category_name[i] ) );
  13. Mat posResponses( allsamples_bow.at( category_name[i]).rows, 1, CV_32SC1, Scalar::all(1) );
  14. responses.push_back( posResponses );
  15. for ( auto itr = allsamples_bow.begin(); itr != allsamples_bow.end(); ++itr )
  16. {
  17. if ( itr -> first == category_name[i] ) {
  18. continue;
  19. }
  20. tem_Samples.push_back( itr -> second );
  21. Mat response( itr -> second.rows, 1, CV_32SC1, Scalar::all( -1 ) );
  22. responses.push_back( response );
  23. }
  24. stor_svms[i].train( tem_Samples, responses, Mat(), Mat(), svmParams );
  25. //存储svm
  26. string svm_filename=string(DATA_FOLDER) + category_name[i] + string("SVM.xml");
  27. stor_svms[i].save(svm_filename.c_str());
  28. }

对于SVM的参数以及函数调用的介绍可以参考中文官网http://www.opencv.org.cn/opencvdoc/2.3.2/html/doc/tutorials/ml/introduction_to_svm/introduction_to_svm.html#introductiontosvms

部分变量的定义如下:

  1. // 训练得到的SVM
  2. CvSVM *stor_svms;
  3. //类目名称,也就是TRAIN_FOLDER设置的目录名
  4. vector<string> category_name;
  5. //类目数目
  6. int categories_size;

NO.5、对未知图片进行分类

分类

使用某张待分类图片的bag of words作为feature vector输入,使用每一类的分类器计算判为该类的可能性,然后使用可能性最高的那个类别作为这张图片的类别。

prediction_category就是结果,test就是某张待分类图片的bag of words。示例代码如下:

  1. Mat input_pic=imread(train_pic_path);
  2. imshow("输入图片:",input_pic);
  3. cvtColor(input_pic,gray_pic,CV_BGR2GRAY);
  4. // 提取BOW描述子
  5. vector<KeyPoint>kp;
  6. Mat test;
  7. featureDecter->detect(gray_pic,kp);
  8. bowDescriptorExtractor->compute(gray_pic,kp,test);
  9. float scoreValue = stor_svms[i].predict( test, true );
  10. float classValue = stor_svms[i].predict( test, false );
  11. sign = ( scoreValue < 0.0f ) == ( classValue < 0.0f )? 1 : -1;
  12. curConfidence = sign * stor_svms[i].predict( test, true );
  13. if(curConfidence>best_score)
  14. {
  15. best_score=curConfidence;
  16. prediction_category=cate_na;
  17. }

上面就是四个主要步骤的部分示例代码,很多其他部分代码没有贴出来,比如说如何遍历文件夹下面的所有不同类别的图片,因为训练图片的样本比较多的话,训练图片是一个时间比较长久的,那么如何在对一张待测图片进行分类的时候,不需要每次都重复训练样本,而是直接读取之前已经训练好的BOW。。。。很多很多。

我的main函数实现如下:

  1. int main(void)
  2. {
  3. int clusters=1000;
  4. //初始化
  5. categorizer c(clusters);
  6. //特征聚类
  7. c.bulid_vacab();
  8. //构造BOW
  9. c.compute_bow_image();
  10. //训练分类器
  11. c.trainSvm();
  12. //将测试图片分类
  13. c.category_By_svm();
  14. return 0;
  15. }

下面来看看我的工程部分运行结果如下: 

部分分类下图所示: 

 
 

左边为输入图片,右边为所匹配的类别模型。准确率为百分之八九十。

我的整个工程文件以及我的所有训练的图片存放在这里http://download.csdn.net/detail/always2015/8944973以及http://download.csdn.net/detail/always2015/8944959,需要的可以下载,自己在找训练图片写代码花了很多时间,下载完后自行解压,project data文件夹直接放在D盘就行,里面存放训练的图片和待测试图片,以及训练过程中生成的中间文件,另一个文件夹object_classfication_end则是工程文件,我用的是vs2010打开即可,下面工程里有几个要注意的地方:

1、在这个模块中使用到了c++的boost库,但是在这里有一个版本的限制。这个模块的代码只能在boost版本1.46以上使用,这个版本以下的就不能用了,直接运行就会出错,这是最需要注意的。因为在1.46版本以上中对比CsSVM这个类一些成员函数做了一些私有化的修改,所以在使用该类初始化对象时候需要注意。

2、我的模块所使用到的函数和产生的中间结果都是在一个categorizer类中声明的,由于不同的执行阶段中间结果有很多个,例如:训练图片聚类后所得到单词表矩阵,svm分类器的训练的结果等,中间结果的产生是相当耗时的,所以在刚开始就考虑到第一次运行时候把他以文件XML的格式保存下来,下次使用到的时候在读取。将一个矩阵存入文本的时候可以直接用输出流的方式将一个矩阵存入,但是读取时候如果用输入流直接一个矩阵变量的形式读取,那就肯定报错,因为输入流不支持直接对矩阵的操作,所以这时候只能对矩阵的元素一个一个进行读取了。

3、在测试的时候,如果输入的图片太小,或者全为黑色,当经过特征提取和单词构造完成使用svm进行分类时候会出现错误。经过调试代码,发现上述图片在生成该图片的单词的时候所得到的单词矩阵会是一个空矩阵,即该矩阵的行列数都为0,所以在使用svm分类器时候就出错。所以在使用每个输入图片的单词矩阵的时候先做一个判断,如果该矩阵行列数都为0,那么该图片直接跳过。

opencv中的SVM图像分类(二)的更多相关文章

  1. OpenCV中的SVM參数优化

    SVM(支持向量机)是机器学习算法里用得最多的一种算法.SVM最经常使用的是用于分类,只是SVM也能够用于回归,我的实验中就是用SVM来实现SVR(支持向量回归). 对于功能这么强的算法,opencv ...

  2. OpenCV中的SVM参数优化

    OpenCV中的SVM参数优化 svm参数优化opencv SVMSVR参数优化CvSVMopencv CvSVM        SVM(支持向量机)是机器学习算法里用得最多的一种算法.SVM最常用的 ...

  3. OpenCV中对图像进行二值化的关键函数——cvThreshold()。

    函数功能:采用Canny方法对图像进行边缘检测 函数原型: void cvThreshold( const CvArr* src, CvArr* dst, double threshold, doub ...

  4. OpenCV中使用SVM简介

    下面这是opencv官方文档中的代码,我加了一部分注释: #include "stdafx.h" #include "opencv2/core/core.hpp" ...

  5. opencv中对图片的二值化操作并提取特定颜色区域

    一.最近因为所在的实习公司要求用opencv视觉库来写一个对图片识别并提取指定区域的程序.看了很多资料,只学会了皮毛,下面附上简单的代码.运行程序之前需要安装opencv库,官网地址为:https:/ ...

  6. Opencv中SVM样本训练、归类流程及实现

    支持向量机(SVM)中最核心的是什么?个人理解就是前4个字--"支持向量",一旦在两类或多累样本集中定位到某些特定的点作为支持向量,就可以依据这些支持向量计算出来分类超平面,再依据 ...

  7. OpenCV机器学习库函数--SVM

    svm分类算法在opencv3中有了很大的变动,取消了CvSVMParams这个类,因此在参数设定上会有些改变. opencv中的svm分类代码,来源于libsvm. #include "o ...

  8. 【OpenCV】opencv3.0中的SVM训练 mnist 手写字体识别

    前言: SVM(支持向量机)一种训练分类器的学习方法 mnist 是一个手写字体图像数据库,训练样本有60000个,测试样本有10000个 LibSVM 一个常用的SVM框架 OpenCV3.0 中的 ...

  9. [OpenCV-Python] OpenCV 中的图像处理 部分 IV (二)

    部分 IVOpenCV 中的图像处理 OpenCV-Python 中文教程(搬运)目录 16 图像平滑 目标 • 学习使用不同的低通滤波器对图像进行模糊 • 使用自定义的滤波器对图像进行卷积(2D 卷 ...

随机推荐

  1. linux--bash: redis-server: 未找到命令

    linux 安装redis过程中出现了异常,make不通过,异常如下: [root@localhost redis-2.8.3]# make cd src && make all ma ...

  2. [转] Makefile 基础 (9) —— Makefile 使用make更新函数库文件

    该篇文章为转载,是对原作者系列文章的总汇加上标注. 支持原创,请移步陈浩大神博客:(最原始版本) http://blog.csdn.net/haoel/article/details/2886 我转自 ...

  3. NIO系列1:框架拆解

    最近一年用NIO写了不少网络程序,也研究了一些开源NIO网络框架netty.mina等,总结了一下NIO的架构特点. 无论是netty还是mina它们都在java原生NIO的基础上进行了完善的封装,虽 ...

  4. 浅谈Linux下的五种I/O模型 两篇别人的博客

     http://blog.csdn.net/sinat_34990639/article/details/52778562  http://www.cnblogs.com/chy2055/p/5220 ...

  5. vim的复制粘贴(包括系统剪贴板)

    1.vim常用复制粘贴命令 Vim的复制粘贴命令无疑是y (yank),p(paster),加上yy,P PS: vim有个很有意思的约定(我觉得是一种约定),就是某个命令的大小写都是实现某种功能,只 ...

  6. Linq技巧4——怎么在.NET 3.5 SP1中伪造一个外键属性

    在.NET 4.0 的EF 中,增加了FK Associations 的功能,但是在.NET 3.5 SP1 中,仅仅支持独立的关联,这意味着FK 栏位不能作为实体的属性来使用,也就是说在使用的时候, ...

  7. 58同城职位分类数据 json

    { "level0": {"0": "销售", "1": "客服", "2": ...

  8. Error querying database找不到数据库的错误可能发生的原因..

    这个问题纠结了大概两个小时.原因是这样的,我刚刚换了一台新的电脑,准备把以前电脑上自己搭建的小项目放到新电脑上面,用myeclipse引入项目之后,启动项目在浏览器跑起来.然后输入账号密码登录主页,报 ...

  9. 标准C程序设计七---50

    Linux应用             编程深入            语言编程 标准C程序设计七---经典C11程序设计    以下内容为阅读:    <标准C程序设计>(第7版) 作者 ...

  10. AC日记——[USACO11DEC]牧草种植Grass Planting 洛谷 P3038

    题目描述 Farmer John has N barren pastures (2 <= N <= 100,000) connected by N-1 bidirectional road ...