理论部分见博客:SIFT+词袋+SVM的深入理解,感谢博主herr_kun,这个真是写的太好了,通俗易懂,尤其是那张自动笔画的图,真好https://blog.csdn.net/herr_kun/article/details/80220389
使用的代码来源于https://blog.csdn.net/Yan456jie/article/details/52313317
感谢博主Yan456jie
在跑代码的时候,发现该代码有一些函数在新版本的python上跑不通,有一些错误,所以调了一下
主要遇到的错误是
1前期报了一堆错,我查博客做了一堆,结果发现主要是改了文件读写的代码,结果读不到文件了,这个只能具体问题具体分析了,就不记录了。
2 路径中最好不要有中文!哪怕已经可以读取中文路径下的东西了,可是我在调用im = cv2.cvtColor(im, cv2.COLOR_BGR2GRAY)
时依然报错,而且是报说opencv 崩溃了。。。果真是玄学。
3 由于版本更新,sift特征的函数改变了,下列关键语句不能用了
#关键点检测对象
fea_det = cv2.FeatureDetector_create("SIFT")
#特征提取对象
des_ext = cv2.DescriptorExtractor_create("SIFT")
解决方法
参考该博客,使用新的函数提取sift,感谢博主
StevenGerrad,https://blog.csdn.net/qq_40690815/article/details/104946216
4由于版本更新,cross_validation
不能使用了
解决方法:参考该博客,感谢博主ronaldo2018
https://blog.csdn.net/sinat_17697111/article/details/84835873
代码中的cross_validation都改成model_selection
代码如下
#coding=utf-8
#-*- coding: utf-8 -*-
import argparse as ap
import cv2
import imutils
import numpy as np
import os
from sklearn.externals import joblib
from scipy.cluster.vq import *
from sklearn import preprocessing
import math
import sys
import numpy
from sklearn import metrics
from sklearn.feature_extraction.text import HashingVectorizer
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.multiclass import OneVsRestClassifier
from sklearn.naive_bayes import MultinomialNB
from sklearn.feature_extraction.text import CountVectorizer, TfidfTransformer
from sklearn.neighbors import KNeighborsClassifier
from sklearn.preprocessing import MultiLabelBinarizer, StandardScaler
from sklearn.svm import SVC
from sklearn.svm import LinearSVC
import codecs
from sklearn.feature_selection import SelectPercentile, f_classif
from sklearn.feature_selection import SelectKBest
from sklearn.feature_selection import chi2
# from sklearn import cross_validation #报错
#ImportError: cannot import name 'cross_validation' from 'sklearn' (C:\Users\wyh\Anaconda3\lib\site-packages\sklearn\__init__.py)
# cross_validation路径换了,现在放在model_selection,改成下面这句
from sklearn import model_selection
from sklearn.utils import shuffle
import sys
if sys.getdefaultencoding()!='utf-8':
reload(sys)
sys.setdefaultencoding('utf-8')
'''
使用词袋模型对图像进行分类:
1、数据格式,文件夹名即类别,每个文件夹下是一类图像
2、提取sift特征,将所有图像的sift特征放在一起,进行聚类,聚出n个视觉词
3、计算每幅图像有哪些视觉词,统计出词频矩阵
4、根据矩阵计算idf,进而得到tfidf矩阵,并对其进行L2归一化(向量中每个元素除以向量的L2范数->x/平方和开根号)
5、使用一般分类模型对其进行分类,计算P,R,F
'''
def load_data(path):
'''
每个文件夹下是一种图片
:param path:种类文件夹路径
:return: 图片路径列表和标签列表
'''
categories = os.listdir(path)
img_pathes = []
labels = []
for mypath, dirs, files in os.walk(path):
for file in files:
mypath1=mypath+'/'
mytmp = os.path.join(mypath1, file)
img_pathes.append(mytmp) # 不能用expend 要用append
# print(img_pathes)
if len(files)>0:
# print('hello')
labels.extend([mytmp.split('/')[-2]] )
# labels.extend([mytmp.split('/')[-2]] * len(files))
# print('labels:', labels)
# print('img_pathes:',img_pathes)
# print('labels:',labels)
#print len(labels),labels
return img_pathes,labels
# def load_data(path):
# '''
# 每个文件夹下是一种图片
# :param path:种类文件夹路径
# :return: 图片路径列表和标签列表
# '''
# categories = os.listdir(path)
# img_pathes = []
# labels = []
# for mypath,dirs, files in os.walk(path):#root(根目录) dir(文件夹是list) file(文件是list)
# for file in files:
# mypath1=mypath+"/" #要加/,不然会变成前面都是/,后面的file是\
# mytmp=os.path.join(mypath1, file)
#
# img_pathes.append(mytmp) #不能用expend 要用append
# print(img_pathes)
#
# if len(files) > 0:
# labels.append([img_pathes.split('/')[-2]] * len(files)) #类名就是子文件夹的名字,有多少个文件就有多少个这一类的标签,所以乘上files的数量
# # print([path.split('/')[-2]])
# # print len(img_pathes),img_pathes
# # print len(labels),labels
# print('load_data 里的label: ',labels)
# return img_pathes, labels
def cal_bow(image_paths, numWords):
'''
使用bag of word方法提取图像特征
:param image_paths:
:return:
'''
# # numWords = 100
#这几行会报错cv.cv没有这个属性FeatureDetector_create
# # 关键点检测对象
# fea_det = cv2.FeatureDetector_create("SIFT")
# # 特征提取对象
# des_ext = cv2.DescriptorExtractor_create("SIFT")
# 参考博客 https://stackoverflow.com/questions/35588570/cv2-featuredetector-createsift-causes-segmentation-fault
# ima = cv2.imread('image.jpg')
# gray = cv2.cvtColor(ima, cv2.COLOR_BGR2GRAY)
# detector = cv2.SIFT()
# kpts, des = detector.detectAndCompute(gray, None)
# AttributeError: module 'cv2.cv2' has no attribute 'FeatureDetector_create'
# List where all the descriptors are stored
des_list = []
kps_list= []
for i, image_path in enumerate(image_paths):
im = cv2.imread(image_path) #路径有中文这一句不报错
# print("所读取的图片是:%s\n" %image_path)
# im = cv2.cvtColor(im, cv2.COLOR_BGR2GRAY) #路径不能有中文,否则这一句会报错
# print (kpts[0].pt[0])
# print("Extract SIFT of %s image, %d of %d images" %(image_paths[i],i,len(image_paths)))
# kpts = fea_det.detect(im)
# 可能存在没有检测出特征点的情况
# 参考博客 https://blog.csdn.net/qq_40690815/article/details/104946216
# 参考博客 https://www.it1352.com/2128021.html
detector = cv2.xfeatures2d.SIFT_create()
kpts, des = detector.detectAndCompute(im, None) # des是描述子 kpts是关键点,每张图关键点不一样多,每个关键点是128维
# print('kpts :', len(kpts))
# des有k行m列,每行代表一个特征,m是固定的特征维数
# kpts, des = des_ext.compute(im, kpts)
kps_list.append(kpts)
des_list.append((image_path, des))
# Stack all the descriptors vertically in a numpy array
# image_path为图片路径,descriptor为对应图片的特征
# 将所有特征纵向堆叠起来,每行当做一个特征词
descriptors = des_list[0][1]
for image_path, descriptor in des_list[1:]:
# vstack对矩阵进行拼接,将所有的特征word拼接到一起
# print descriptor.shape, descriptors.shape
# if descriptor != None:
descriptors = np.vstack((descriptors, descriptor))
# 对特征词使用k-menas算法进行聚类
print("Start k-means: %d words, %d key points" % (numWords, descriptors.shape[0]))
# "Start k-means: %d words, %d key points" % (numWords, descriptors.shape[0])
# 最后输出的结果其实是两维的,第一维是聚类中心,第二维是损失distortion
voc, variance = kmeans(descriptors, numWords, iter=1)
# 初始化一个bag of word矩阵,每行表示一副图像,每列表示一个视觉词,下面统计每副图像中视觉词的个数
im_features = np.zeros((len(image_paths), numWords), "float32")
for i in range(len(image_paths)):
# 计算每副图片的所有特征向量和voc中每个特征word的距离,返回为匹配上的word
descriptor = des_list[i][1]
# if descriptor != None:
# 根据聚类中心将所有数据进行分类des_list[i][1]为数据, voc则是kmeans产生的聚类中心.
# vq输出有两个:一是各个数据属于哪一类的label,二是distortion
words, distance = vq(des_list[i][1], voc)
for w in words:
im_features[i][w] += 1
# Perform Tf-Idf vectorization
nbr_occurences = np.sum((im_features > 0) * 1, axis=0)
idf = np.array(np.log((1.0 * len(image_paths) + 1) / (1.0 * nbr_occurences + 1)), 'float32')
# L2归一化
im_features = im_features * idf
im_features = preprocessing.normalize(im_features, norm='l2')
print('cal_bow 结束了')
return im_features
def train_clf2(train_data, train_tags):
# print('训练标签: ',train_tags)
clf = SVC(kernel = 'linear',C=1000)#default with 'rbf'
print(clf)
# clf = LinearSVC(C=1100.0) # default with 'rbf'
# clf = LinearSVC(C=1000.0) # default with 'rbf'
# new_train_data=MultiLabelBinarizer().fit_transform(train_data)#要改格式再传参
# new_train_tags=MultiLabelBinarizer().fit_transform(train_tags)#要改格式再传参
# print('类别: ', new_train_tags)
# clf.fit(new_train_data, new_train_tags)
clf.fit(train_data, train_tags)
# clf = OneVsRestClassifier(estimator=SVC(random_state=0))
# clf.fit(train_data, train_tags).predict(train_data)
# clf.fit(train_data, train_tags)
#报错ValueError: You appear to be using a legacy multi-label data representation. 因为scikit-learn版本不一样,所以多标签的写法不同
return clf
def evaluate(actual, pred):
m_precision = metrics.precision_score(actual, pred, average="macro")
m_recall = metrics.recall_score(actual, pred, average="macro")
print('m_precision: ',m_precision,'\n')
# print
# 'precision:{0:.3f}'.format(m_precision)
print('m_recall: ', m_recall, '\n')
# print
# 'recall:{0:0.3f}'.format(m_recall)
print('f1-score: ',metrics.f1_score(actual, pred, average="macro"),'\n')
# print
# 'f1-score:{0:.8f}'.format(metrics.f1_score(actual, pred, average="macro"))
'''
'''
# 提取图片特征并保存 图像大小为20*20
# path = 'D:/data/Caltech_101/101_part/'
path = 'G:/wenxian/28cba52f/101_part/'
# path='G:/Caltech101/101_ObjectCategories/101_ObjectCategories/'#Caltech101数据集
# path='G:/wenxian/28cba52f/Caltech101/101_ObjectCategories/101_ObjectCategories'#Caltech101数据集除掉背景那一类共101类
img_pathes, labels = load_data(path)
im_features = cal_bow(img_pathes, numWords=500)
joblib.dump((im_features, labels), "bof.pkl", compress=3)
# 训练并测试
im_features, labels = joblib.load("bof.pkl")
# print('特征的长度:',len(im_features))
# print('标签的长度:',len(labels))
X_train, X_test, y_train, y_test = \
model_selection.train_test_split(im_features, labels, test_size=0.3, random_state=0)
# cross_validation.train_test_split(im_features, labels, test_size=0.3, random_state=0) #报错 NameError: name 'cross_validation' is not defined
clf = train_clf2(X_train, y_train)
# new_X_test=MultiLabelBinarizer().fit_transform(X_test)#要改格式再传参
pred = clf.predict(X_test)
# print(pred)
# print('测试集:',y_test,'\n')
evaluate(y_test, pred)
数据集也是来自原博客,地址为https://download.csdn.net/detail/yan456jie/9614133就是该数据集取前4个类的一个小型的数据集。
然后我好奇查了一下这个Caltech101数据集,喜闻乐见地是官网地址又下不了了。。。我又想起了oulu大学那个进不去的官网。。。忧伤,Caltech101官网链接如下http://www.vision.caltech.edu/Image_Datasets/Caltech101/
于是我在网上找了这个链接,Calttech101: https://hyper.ai/datasets/5258,下载方式如下,在页面中点击数据集下载,下载的是一个torent文件,到时候用迅雷下载就行
Caltech256也一并附上:https://hyper.ai/datasets/5261
因为担心链接挂了,此处百度网盘链接
Caltech101:链接:https://pan.baidu.com/s/1I90Tgm1kYj79wzDHUcFU1w
提取码:pbvz
Caltech256:https://pan.baidu.com/s/1-xwSETF1UfdkBhPVg0vqWA
提取码:of7k
发现一个别的东西,101数据集里面有个叫show_annotation的函数,不能直接执行,调用语句如下所示,嗯,所以说把这个分类数据集还做了标注是吗
show_annotation('G:\迅雷下载\Caltech-101\data\Caltech 101\Caltech 101\101_ObjectCategories\101_ObjectCategories\wrench\image_0039.jpg','G:\迅雷下载\Caltech-101\data\Caltech 101\Caltech 101\Annotations\Annotations\wrench\annotation_0039.mat')
效果不佳,而且这个是不可重复的,数据集划分和svc分类器都没有设置随机数种子,但奇怪的是即使设置了随机数,结果还是不能重复,所以只能说马马虎虎调通了这个代码吧,还是有很多地方不了解。
感谢以上所有博主,另外求oulu大学的Outex纹理数据集,官网进不去了。。。嗯,然而我并没有钱。。。
另外,如何使结果可重复呢?是不是还有哪里的random_state是我不知道的