#include<iostream>
#include<map>
#include<opencv2/core/core.hpp>
#include<opencv2/highgui/highgui.hpp>
#include<string>
#include<opencv2/ml/ml.hpp>
#include<opencv2/features2d/features2d.hpp>
#include<opencv2/nonfree/features2d.hpp>
#include<opencv2/imgproc/imgproc.hpp>
#include<fstream>
//boost 库
#include<boost/filesystem.hpp>
#include"Config.h"
using namespace cv;
using namespace std;
//定义一个boost库的命名空间
namespace fs=boost::filesystem;
using namespace fs;
class categorizer
{
private :
//从类目名称到数据的map映射
map<string,Mat> result_objects;
//存放所有训练图片的BOW
map<string,Mat> allsamples_bow;
//从类目名称到训练图集的映射,关键字可以重复出现
multimap<string,Mat> train_set;
// 训练得到的SVM
CvSVM *stor_svms;
//类目名称,也就是TRAIN_FOLDER设置的目录名
vector<string> category_name;
//类目数目
int categories_size;
//用SURF特征构造视觉词库的聚类数目
int clusters;
//存放训练图片词典
Mat vocab;
//特征检测器detectors与描述子提取器extractors 泛型句柄类Ptr
Ptr<FeatureDetector> featureDecter;
Ptr<DescriptorExtractor> descriptorExtractor;
Ptr<BOWKMeansTrainer> bowtrainer;
Ptr<BOWImgDescriptorExtractor> bowDescriptorExtractor;
Ptr<FlannBasedMatcher> descriptorMacher;
//构造训练集合
void make_train_set();
// 移除扩展名,用来讲模板组织成类目
string remove_extention(string);
public:
//构造函数
categorizer(int);
// 聚类得出词典
void bulid_vacab();
//构造BOW
void compute_bow_image();
//训练分类器
void trainSvm();
//将测试图片分类
void category_By_svm();
};
// 移除扩展名,用来讲模板组织成类目
string categorizer::remove_extention(string full_name)
{
//find_last_of找出字符最后一次出现的地方
int last_index=full_name.find_last_of(".");
string name=full_name.substr(0,last_index);
return name;
}
// 构造函数
categorizer::categorizer(int _clusters)
{
cout<<"开始初始化..."<<endl;
clusters=_clusters;
//初始化指针
featureDecter=new SurfFeatureDetector();
descriptorExtractor=new SurfDescriptorExtractor();
bowtrainer=new BOWKMeansTrainer(clusters);
descriptorMacher=new FlannBasedMatcher();
bowDescriptorExtractor=new BOWImgDescriptorExtractor(descriptorExtractor,descriptorMacher);
//boost库文件 遍历数据文件夹 directory_iterator(p)就是迭代器的起点,无参数的directory_iterator()就是迭代器的终点。
directory_iterator begin_iter(TEMPLATE_FOLDER);
directory_iterator end_iter;
//获取该目录下的所有文件名
for(;begin_iter!=end_iter;++begin_iter)
{
string filename=string(TEMPLATE_FOLDER)+begin_iter->path().filename().string();
string sub_category =remove_extention(begin_iter->path().filename().string());
//读入模板图片
Mat image=imread(filename);
Mat templ_image;
//存储原图模板
result_objects[sub_category]=image;
}
cout<<"初始化完毕..."<<endl;
//读取训练集
make_train_set();
}
//构造训练集合
void categorizer::make_train_set()
{
cout<<"读取训练集..."<<endl;
string categor;
//递归迭代rescursive 直接定义两个迭代器:i为迭代起点(有参数),end_iter迭代终点
for(recursive_directory_iterator i(TRAIN_FOLDER),end_iter;i!=end_iter;i++)
{
// level == 0即为目录,因为TRAIN__FOLDER中设置如此
if(i.level()==0)
{
// 将类目名称设置为目录的名称
categor=(i->path()).filename().string();
category_name.push_back(categor);
}else
{
// 读取文件夹下的文件。level 1表示这是一副训练图,通过multimap容器来建立由类目名称到训练图的一对多的映射
string filename=string(TRAIN_FOLDER)+categor+string("/")+(i->path()).filename().string();
Mat temp=imread(filename,CV_LOAD_IMAGE_GRAYSCALE);
pair<string,Mat> p(categor,temp);
//得到训练集
train_set.insert(p);
}
}
categories_size=category_name.size();
cout<<"发现 "<<categories_size<<"种类别物体..."<<endl;
}
// 训练图片feature聚类,得出词典
void categorizer::bulid_vacab()
{
FileStorage vacab_fs(DATA_FOLDER "vocab.xml",FileStorage::READ);
//如果之前已经生成好,就不需要重新聚类生成词典
if(vacab_fs.isOpened())
{
cout<<"图片已经聚类,词典已经存在.."<<endl;
vacab_fs.release();
}else
{
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);
}
cout << "训练图片开始聚类..." << endl;
//将每一副图的Surf特征利用add函数加入到bowTraining中去,就可以进行聚类训练了
bowtrainer->add(vocab_descriptors);
// 对SURF描述子进行聚类
vocab=bowtrainer->cluster();
cout<<"聚类完毕,得出词典..."<<endl;
//以文件格式保存词典
FileStorage file_stor(DATA_FOLDER "vocab.xml",FileStorage::WRITE);
file_stor<<"vocabulary"<<vocab;
file_stor.release();
}
}
//构造bag of words
void categorizer::compute_bow_image()
{
cout<<"构造bag of words..."<<endl;
FileStorage va_fs(DATA_FOLDER "vocab.xml",FileStorage::READ);
//如果词典存在则直接读取
if(va_fs.isOpened())
{
Mat temp_vacab;
va_fs["vocabulary"] >> temp_vacab;
bowDescriptorExtractor->setVocabulary(temp_vacab);
va_fs.release();
}else
{
//对每张图片的特征点,统计这张图片各个类别出现的频率,作为这张图片的bag of words
bowDescriptorExtractor->setVocabulary(vocab);
}
//如果bow.txt已经存在说明之前已经训练过了,下面就不用重新构造BOW
string bow_path=string(DATA_FOLDER)+string("bow.txt");
ifstream read_file(bow_path);
//如BOW已经存在,则不需要构造
if(read_file!=0)
{
cout<<"BOW 已经准备好..."<<endl;
}
else{
// 对于每一幅模板,提取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);
}
//简单输出一个文本,为后面判断做准备
ofstream ous(bow_path);
ous<<"flag";
cout<<"bag of words构造完毕..."<<endl;
}
}
//训练分类器
void categorizer::trainSvm()
{
int flag=0;
for(int k=0;k<categories_size;k++)
{
string svm_file_path=string(DATA_FOLDER) + category_name[k] + string("SVM.xml");
FileStorage svm_fil(svm_file_path,FileStorage::READ);
//判断训练结果是否存在
if(svm_fil.isOpened())
{
svm_fil.release();
continue;
}
else
{
flag=-1;
break;
}
}
//如果训练结果已经存在则不需要重新训练
if(flag!=-1)
{
cout<<"分类器已经训练完毕..."<<endl;
}else
{
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( resp
- 1
- 2
- 3
- 4
- 5
- 6
前往页