opencv中的SVM图像分类(二)
版权声明:本文为博主原创文章,未经博主允许不得转载。
原创作品 转载请注明出http://blog.csdn.net/always2015/article/details/47107129
上一篇博文对图像分类理论部分做了比较详细的讲解,这一篇主要是对图像分类代码的实现进行分析。理论部分我们谈到了使用BOW模型,但是BOW模型如何构建以及整个步骤是怎么样的呢?可以参考下面的博客http://www.cnblogs.com/yxy8023ustc/p/3369867.html,这一篇博客很详细讲解了BOW模型的步骤了,主要包含以下四个步骤:
- 提取训练集中图片的feature
- 将这些feature聚成n类。这n类中的每一类就相当于是图片的“单词”,所有的n个类别构成“词汇表”。我的实现中n取1000,如果训练集很大,应增大取值。
- 对训练集中的图片构造bag of words,就是将图片中的feature归到不同的类中,然后统计每一类的feature的频率。这相当于统计一个文本中每一个单词出现的频率
- 训练一个多类分类器,将每张图片的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
我训练图片特征提取的示例代码如下:
Mat vocab_descriptors;
// 对于每一幅模板,提取SURF算子,存入到vocab_descriptors中
multimap<string,Mat> ::iterator i=train_set.begin();
for(;i!=train_set.end();i++)
{
vector<KeyPoint>kp;
Mat templ=(*i).second;
Mat descrip;
featureDecter->detect(templ,kp);
descriptorExtractor->compute(templ,kp,descrip);
//push_back(Mat);在原来的Mat的最后一行后再加几行,元素为Mat时, 其类型和列的数目 必须和矩阵容器是相同的
vocab_descriptors.push_back(descrip);
}
注意:上述代码只是工程的一个很小的部分,有些变量在类中已经定义,在这里没有贴出来,例如上述的train_set训练图片的映射,定义为:
//从类目名称到训练图集的映射,关键字可以重复出现
multimap<string,Mat> train_set;
将每张图片的特征描述符存储起来vocab_descriptors,然后为后面聚类和构造训练图片词典做准备。
NO.2、feature聚类
由于opencv封装了一个类BOWKMeansExtractor[2],这一步非常简单,将所有图片的feature vector丢给这个类,然后调用cluster()就可以训练(使用KMeans方法)出指定数量(步骤介绍中提到的n)的类别。输入vocab_descriptors就是第1步计算得到的结果,返回的vocab是一千个向量,每个向量是某个类别的feature的中心点。
示例代码如下:
//将每一副图的Surf特征利用add函数加入到bowTraining中去,就可以进行聚类训练了
bowtrainer->add(vocab_descriptors);
// 对SURF描述子进行聚类
vocab=bowtrainer->cluster();
bowtrainer的定义如下:
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。
//对每张图片的特征点,统计这张图片各个类别出现的频率,作为这张图片的bag of words
bowDescriptorExtractor->setVocabulary(vocab);
}
// 对于每一幅模板,提取SURF算子,存入到vocab_descriptors中
multimap<string,Mat> ::iterator i=train_set.begin();
for(;i!=train_set.end();i++)
{
vector<KeyPoint>kp;
string cate_nam=(*i).first;
Mat tem_image=(*i).second;
Mat imageDescriptor;
featureDecter->detect(tem_image,kp);
bowDescriptorExtractor->compute(tem_image,kp,imageDescriptor);
//push_back(Mat);在原来的Mat的最后一行后再加几行,元素为Mat时, 其类型和列的数目 必须和矩阵容器是相同的
allsamples_bow[cate_nam].push_back(imageDescriptor);
}
上面部分变量的定义如下:
//存放所有训练图片的BOW
map<string,Mat> allsamples_bow;
//特征检测器detectors与描述子提取器extractors 泛型句柄类Ptr
Ptr<FeatureDetector> featureDecter;
Ptr<DescriptorExtractor> descriptorExtractor;
Ptr<BOWKMeansTrainer> bowtrainer;
Ptr<BOWImgDescriptorExtractor> bowDescriptorExtractor;
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方法就可以了。
示例代码如下:
stor_svms=new CvSVM[categories_size];
//设置训练参数
SVMParams svmParams;
svmParams.svm_type = CvSVM::C_SVC;
svmParams.kernel_type = CvSVM::LINEAR;
svmParams.term_crit = cvTermCriteria(CV_TERMCRIT_ITER, 100, 1e-6);
cout<<"训练分类器..."<<endl;
for(int i=0;i<categories_size;i++)
{
Mat tem_Samples( 0, allsamples_bow.at( category_name[i] ).cols, allsamples_bow.at( category_name[i] ).type() );
Mat responses( 0, 1, CV_32SC1 );
tem_Samples.push_back( allsamples_bow.at( category_name[i] ) );
Mat posResponses( allsamples_bow.at( category_name[i]).rows, 1, CV_32SC1, Scalar::all(1) );
responses.push_back( posResponses );
for ( auto itr = allsamples_bow.begin(); itr != allsamples_bow.end(); ++itr )
{
if ( itr -> first == category_name[i] ) {
continue;
}
tem_Samples.push_back( itr -> second );
Mat response( itr -> second.rows, 1, CV_32SC1, Scalar::all( -1 ) );
responses.push_back( response );
}
stor_svms[i].train( tem_Samples, responses, Mat(), Mat(), svmParams );
//存储svm
string svm_filename=string(DATA_FOLDER) + category_name[i] + string("SVM.xml");
stor_svms[i].save(svm_filename.c_str());
}
对于SVM的参数以及函数调用的介绍可以参考中文官网http://www.opencv.org.cn/opencvdoc/2.3.2/html/doc/tutorials/ml/introduction_to_svm/introduction_to_svm.html#introductiontosvms
部分变量的定义如下:
// 训练得到的SVM
CvSVM *stor_svms;
//类目名称,也就是TRAIN_FOLDER设置的目录名
vector<string> category_name;
//类目数目
int categories_size;
NO.5、对未知图片进行分类
分类
使用某张待分类图片的bag of words作为feature vector输入,使用每一类的分类器计算判为该类的可能性,然后使用可能性最高的那个类别作为这张图片的类别。
prediction_category就是结果,test就是某张待分类图片的bag of words。示例代码如下:
Mat input_pic=imread(train_pic_path);
imshow("输入图片:",input_pic);
cvtColor(input_pic,gray_pic,CV_BGR2GRAY);
// 提取BOW描述子
vector<KeyPoint>kp;
Mat test;
featureDecter->detect(gray_pic,kp);
bowDescriptorExtractor->compute(gray_pic,kp,test);
float scoreValue = stor_svms[i].predict( test, true );
float classValue = stor_svms[i].predict( test, false );
sign = ( scoreValue < 0.0f ) == ( classValue < 0.0f )? 1 : -1;
curConfidence = sign * stor_svms[i].predict( test, true );
if(curConfidence>best_score)
{
best_score=curConfidence;
prediction_category=cate_na;
}
上面就是四个主要步骤的部分示例代码,很多其他部分代码没有贴出来,比如说如何遍历文件夹下面的所有不同类别的图片,因为训练图片的样本比较多的话,训练图片是一个时间比较长久的,那么如何在对一张待测图片进行分类的时候,不需要每次都重复训练样本,而是直接读取之前已经训练好的BOW。。。。很多很多。
我的main函数实现如下:
int main(void)
{
int clusters=1000;
//初始化
categorizer c(clusters);
//特征聚类
c.bulid_vacab();
//构造BOW
c.compute_bow_image();
//训练分类器
c.trainSvm();
//将测试图片分类
c.category_By_svm();
return 0;
}
下面来看看我的工程部分运行结果如下:
部分分类下图所示:
左边为输入图片,右边为所匹配的类别模型。准确率为百分之八九十。
我的整个工程文件以及我的所有训练的图片存放在这里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图像分类(二)的更多相关文章
- OpenCV中的SVM參数优化
SVM(支持向量机)是机器学习算法里用得最多的一种算法.SVM最经常使用的是用于分类,只是SVM也能够用于回归,我的实验中就是用SVM来实现SVR(支持向量回归). 对于功能这么强的算法,opencv ...
- OpenCV中的SVM参数优化
OpenCV中的SVM参数优化 svm参数优化opencv SVMSVR参数优化CvSVMopencv CvSVM SVM(支持向量机)是机器学习算法里用得最多的一种算法.SVM最常用的 ...
- OpenCV中对图像进行二值化的关键函数——cvThreshold()。
函数功能:采用Canny方法对图像进行边缘检测 函数原型: void cvThreshold( const CvArr* src, CvArr* dst, double threshold, doub ...
- OpenCV中使用SVM简介
下面这是opencv官方文档中的代码,我加了一部分注释: #include "stdafx.h" #include "opencv2/core/core.hpp" ...
- opencv中对图片的二值化操作并提取特定颜色区域
一.最近因为所在的实习公司要求用opencv视觉库来写一个对图片识别并提取指定区域的程序.看了很多资料,只学会了皮毛,下面附上简单的代码.运行程序之前需要安装opencv库,官网地址为:https:/ ...
- Opencv中SVM样本训练、归类流程及实现
支持向量机(SVM)中最核心的是什么?个人理解就是前4个字--"支持向量",一旦在两类或多累样本集中定位到某些特定的点作为支持向量,就可以依据这些支持向量计算出来分类超平面,再依据 ...
- OpenCV机器学习库函数--SVM
svm分类算法在opencv3中有了很大的变动,取消了CvSVMParams这个类,因此在参数设定上会有些改变. opencv中的svm分类代码,来源于libsvm. #include "o ...
- 【OpenCV】opencv3.0中的SVM训练 mnist 手写字体识别
前言: SVM(支持向量机)一种训练分类器的学习方法 mnist 是一个手写字体图像数据库,训练样本有60000个,测试样本有10000个 LibSVM 一个常用的SVM框架 OpenCV3.0 中的 ...
- [OpenCV-Python] OpenCV 中的图像处理 部分 IV (二)
部分 IVOpenCV 中的图像处理 OpenCV-Python 中文教程(搬运)目录 16 图像平滑 目标 • 学习使用不同的低通滤波器对图像进行模糊 • 使用自定义的滤波器对图像进行卷积(2D 卷 ...
随机推荐
- 【Luogu】P3745期末考试(三分)
题目链接 我是怎么把“期末考试”在本地写成“假期计划”的 qwq???? 本题把学生和卷子都排个序,按出成绩最晚时间三分. 三分之后可以O(n)的时间统计答案,因为修改卷子出成绩的时间可以贪心计划. ...
- 最短路---dijsktra--邻接矩阵
; ; //点的个数 int dist[MAXNUM]; int prev[MAXNUM]; int A[MAXUNM][MAXNUM]; void Dijkstra(int v0) { bool S ...
- bzoj 1069 凸包+旋转卡壳
题目大意 在某块平面土地上有N个点,你可以选择其中的任意四个点,将这片土地围起来,当然,你希望这四个点围成 的多边形面积最大. 分析 枚举对角线的一个端点 另一个端点开始转 转的时候求出对角线左边面积 ...
- APUE 学习笔记(二) 文件I/O
1. 文件I/O 对于内核而言,所有打开的文件都通过文件描述符引用,内核不区分文本文件和二进制文件 open函数:O_RDONLY O_WRONLY O_RDWR create函数: close函 ...
- net5:自定义验证控件服务器端验证与客户端验证的使用
原文发布时间为:2008-07-29 -- 来源于本人的百度文章 [由搬家工具导入] using System;using System.Data;using System.Configuration ...
- 转 手把手教你最简单的开源项目托管GitHub入门教程
传送门 自从google code关闭了下载服务了之后,GitHub作为了目前最好用的免费开源项目托管站点,众多开源项目都托管在github,其中不乏著名的播放器MPC-HC. 不习惯于英文的朋友,难 ...
- 慕课 python 操作数据库
test_connection import MySQLdb conn = MySQLdb.Connect( host = '127.0.0.1', port = 3306, user = '**** ...
- 十六进制字符串jpg图片互转
十六制字符串jpg图片互转(格式:FFD8FFE000104A******)如:FFD8FFE000104A46494600010100000100010000FFDB0043000806060706 ...
- 推荐!手把手教你使用Git(转)
原文出处: 涂根华的博客 欢迎分享原创到伯乐头条 一:Git是什么? Git是目前世界上最先进的分布式版本控制系统. 二:SVN与Git的最主要的区别? SVN是集中式版本控制系统,版本库是集中放 ...
- (入门SpringBoot)SpringBoot结合redis(四)
SpringBoot整合redis: 1.引入jar <!-- 引入redis依赖 --><dependency> <groupId>org.springf ...