bag of words classification实现图像分类的原理详解
bag of words模型是信息检索领域常用的文档表示方法。在信息检索时,bag of words模型将一个文档仅看作是若干个词汇的集合,文档中每个单词的出现都是独立的,不依赖于其他单词,语法和词法。bag of words模型应用于图像时,需要从每类图像中提取视觉词汇构造单词表,用单词表中的词汇来表示图像。opencv的例程bag-of-words classification演示了bag of words模型在图像分类中的应用,下面我们根据该例程的执行顺序来学习其如何实现图像分类。
前言
a. bagofwords_classification.cpp位于opencv_contrib\modules\xfeatures2d\samples\bagofwords_classification.cpp
b. bagofwords_classification的一种正确的打开方式记录了例程的编译方法和查看运行结果的方法;
c. 本文中涉及的一些部分原理和图片参考了CSDN著名博主v_JULY_v的博客:SIFT算法的应用-目标识别之用Bag-of-words模型表示一幅图像,感谢原作者的精彩讲解。
下面我们以bagofwords_classification.cpp的执行顺序为主线来讲述此例程实现图像分类的原理。
1. 创建文件夹:bowImageDescriptors,svms, plots
在bagofwords_classification的一种正确的打开方式一文中提到过,在正式运行代码之前,需要修改例程中的变量const string vocPath(将VOC数据的路径赋给该变量)和const string resPath(指定bowImageDescriptors,svms, plots等相关数据的存储路径。)的赋值。
修改完成,代码运行时会在指定路径下创建三个文件夹bowImageDescriptors,svms, plots,它们的用途分别为:
bowImageDescriptors:存储所有训练图像和验证图像的bag of words描述符;
svms:存储PASCAL VOC数据集中20个分类的SVM的训练文件;
plots:存储PASCAL VOC的验证图像集对20个分类的预测结果。下图是PASCAL VOC2010中20个分类以及相应类别的数量分布。
2. params.xml
params.xml的参考内容和格式如下,该文档可以提前准备好,也可以从控制台中输入,为便于测试,建议提前准备好该文档。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
<?xml version="1.0" ?> <opencv_storage> <!--必须包含该语句--> <vocName>VOC2010</vocName> <ddmParams> <detectorType>SURF</detectorType> <descriptorType>SURF</descriptorType> <matcherType>BruteForce</matcherType> </ddmParams> <vocabTrainParams> <trainObjClass>chair</trainObjClass> <vocabSize>1000</vocabSize> <memoryUse>200</memoryUse> <descProportion>0.3</descProportion> </vocabTrainParams> <svmTrainParamsExt> <descPercent>0.5</descPercent> <targetRatio>0.4</targetRatio> <balanceClasses>true</balanceClasses> </svmTrainParamsExt> </opencv_storage> <!--必须包含该语句--> |
params.xml的参数意义如下:
a. DDMParams包含参数detectorType,descriptorType,matcherType。
前面我们提到过“视觉词汇”,对于图像而言,“视觉词汇”是不同图像之间的共同点,即图像中的局部不变特征点。detectorType和descriptorType分别用于设定获取“视觉词汇”时的特征点和特征点描述符类型,单次测试中这两个参数值要求保持一致。在bagofwords_classification中,这两个变量的取值范围包含SIFT, SURF, ORB, BRISK, KAZE,AKAZE。matcherType用于设定特征点匹配描述符,设定范围包含BruteForce, BruteForce-L1, FlannBased, BruteForce-Hamming, BruteForce-HammingLUT。
b. vocabTrainParams包含参数trainObjClass, vocabSize, memoryUse, descProportion。
trainObjClass: 训练对象类别。在VOC中,20个类别的对象使用是相同的训练集和验证集,不同类别之间的train.txt文档中正,负,难度标志不一样。因此这里在获取词汇表时,该参数只是获取训练数据集的一个接口,并不代表当前的词汇表仅针对当前设置对象。
vocabSize: 词汇表中“视觉词汇”的数量。
memoryUse: 获取词汇表时,允许使用的内存。
descProportion: 设置单个图像的特征点用作词汇表的比例。设定比例后,程序中会随机获取相应比例的特征点进入词汇表。
c. svmTrainParamsExt包含参数descPercent, targetRatio, balanceClasses
descPercent: 设定训练图像集中用于做SVM训练的图像比例。
targetRatio:设置SVM训练图像集中正样本占总样本数量的最小比例。
balanceClasses: 是否启用SVM训练中的class weights,默认设置为true,设置为true时,targetRatio设置无效。
3. 获取PASCAL VOC训练图像集的特征–>存放在BOWKMeansTrainer类型变量中–>BOWKMeansTrainer聚类–>生成视觉词汇表
params.xml的参数DDMParams和vocabTrainParams在这一步都会用到,具体用法参考例程即可。不过我有一个疑问:为何设定memoryUse这样的参数?避免用户电脑死机吗?不解。
4. 遍历图像集中的物体类别,根据设定参数对每一种物体类别进行SVM训练
这一步有一点需要强调:并不是每一张图片都可以得到bag of words特征点,有的图片可能无法与词汇表中任意一个词汇匹配。因此如果我们项使用BOW+SVM,在执行SVM训练和验证时,必须确认当前图像的bag of words描述符是否存在,若不存在则需要剔除该图片。
5. 用SVM的训练结果预测PASCAL VOC的测试图像集并输出结果
bagofwords_classification.cpp针对每一个分类有三种形式的结果输出,以tvmonitor类别为例,三个文件分别及其存储的内容分别是:
comp1_cls_val_tvmonitor.txt:该文件存储的是验证图像集SVM predict的结果。该文件中每一行数据包含图像的名称和该图像用predict预测的结果,比较特别的是predict的第三个参数flags需要设置为StatModel::RAW_OUTPUT,其含义为makes the method return the raw results (the sum), not the class label。
scoregt_tvmonitor.txt:该文件每一行数据包含图像名称,SVM predict的结果,当前图像实际针对当前类别而言为正(1)或为负(0)样本。该文件将comp1_cls_val_tvmonitor.txt所述的SVM predict结果进行递减排序后依次存储。
comp1_cls_val_tvmonitor.plt:此文件存储的是绘制验证图像集PR曲线的相关数据,包含AP,Precision和recall。存储顺序按照scoregt_tvmonitor.txt文件中的排序。
6. AP/Precison/recall
AP/Precison/recall是PASCAL VOC衡量算法优劣的主要标准,也是当前SVM预测结果的重要衡量标志(EasyPR作者也提到过这些概念)。我们先来了解如下概念:
a. True Positives,TP:预测为正样本,实际也为正样本的特征数
b. False Positives,FP:预测为正样本,实际为负样本的特征数(错预测为正样本了,所以叫False)
c. True Negatives,TN:预测为负样本,实际也为负样本的特征数
d. False Negatives,FN:预测为负样本,实际为正样本的特征数(错预测为负样本了,所以叫False)
Precision和Recall(有人中文翻译成召回率或查全率)则分别构成了PR曲线的y轴和x轴。
a. Precision=TP/(TP+FP),预测结果为有多少正样本是预测正确了的
b. Recall=TP/(TP+FN),召回率很有意思,相对于Precision只不过参考样本从预测总正样本数结果变成了实际总正样本数。
为了提高精确度,衡量低AP算法间的差异,VOC2010计算AP的算法与VOC2010之前的版本相比有所差异。VOC2010的算法如下:
a. 为每一个不同的召回率找到一个最大的准确率,计算一种准确率单调递减的PR曲线。
b. 用积分计算出当前PR曲线下的面积作为AP。曲线是由分段常数组成,故AP没有近似值。
相比VOC2010之前版本的算法,VOC2010有效采样到了所有不同的recall值。
bagofwords_classification.cpp结构比较复杂,但很有趣。我已尽力将我所理解的描述出来,但可能仍不够清晰,欢迎探留言探讨。
本文到此结果,谢谢阅读!