目录
一、题目
二、原理
(BOW+SVM+K-means)
我们把图像的特征当做单词,把图像“文字化”之后,有助于大规模的图像检索。
(1)BOW基本步骤
特征提取:提取数据集中每幅图像的特征点,然后提取特征描述符,利用sift方法形成特征数据。
学习词袋:把处理好的特征数据全部合并,利用聚类把特征词分为若干类,此若干类的数目由自己设定,每一类相当于一个视觉词汇;
利用视觉词袋量化图像特征:每一张图像由很多视觉词汇组成,我们利用统计的词频直方图,可以表示图像属于哪一类;
这个过程需要获取视觉词汇(visual word)字典,从一定程度上来说,词汇越多越好,因此我们需要的数据集也相应的越大越好;
(2)K-means
利用sift方法形成特征数据后,我们将这些特征数据进行分类,形成40个类,每一个类均是一个视觉词汇。
(3)SVM
将40个特征词汇再次进行分类,通过SVM将其分为两个类别:cat & dog。
三、数据集的处理
25000张图片(12500狗+12500猫)
处理前的数据:
猫和狗的图片在homework3/data一个文件夹中(部分截图)
处理后的数据:
split_dataset.py文件的功能,是在homework3/data文件中建立了两个文件,分别是cat、dog,存储猫和狗的图片,进行copy,防止分类过程中出现错误。
将猫的图片放进homework3/data/cat文件中(部分截图),
将狗的图片放进homework3/data/dog文件中(部分截图)
四、代码
1、split_dataset.py
import os
import os.path as osp
import shutil
#图片路径
IMAGE_PATH = 'E:/Pycharm_project/homework3/data'
#将数据中的猫、狗按照文件名进行分类
DOG_DIR = 'E:/Pycharm_project/homework3/data/dog'
CAT_DIR = 'E:/Pycharm_project/homework3/data/cat'
if not osp.exists(DOG_DIR):
os.makedirs(DOG_DIR)
if not osp.exists(CAT_DIR):
os.makedirs(CAT_DIR)
# dog
dog_fnames = ['dog.{}.jpg'.format(i) for i in range(12500)]
i = 0
for dog_fname in dog_fnames:
src = osp.join(IMAGE_PATH, dog_fname)
dat = osp.join(DOG_DIR, dog_fname)
shutil.copy(src, dat)
i += 1
print("dog {} has been coped.".format(i))
# cat
cat_fnames = ['cat.{}.jpg'.format(i) for i in range(12500)]
i = 0
for cat_fname in cat_fnames:
src = osp.join(IMAGE_PATH, cat_fname)
dat = osp.join(CAT_DIR, cat_fname)
shutil.copy(src, dat)
i += 1
print("cat{} has been coped.".format(i))
2、demo.py
import numpy as np
import cv2
import argparse
import os
parser = argparse.ArgumentParser()
parser.add_argument('--train_test_pair', type=str, default='2000_100', help="\'400_100\' or \'500_100\'")
args = parser.parse_args()
class BOW(object):
def __init__(self, train_sample_num):
self.train_sample_num = train_sample_num
# 创建一个SIFT对象 用于关键点提取
self.feature_detector = cv2.xfeatures2d.SIFT_create()
# 创建一个SIFT对象 用于关键点描述符提取
self.descriptor_extractor = cv2.xfeatures2d.SIFT_create()
def path(self, cls, i):
'''
用于获取图片的全路径
'''
# print('%s/%s/%s.%d.jpg' % (self.train_path, cls, cls, i + 1))
return '%s/%s/%s.%d.jpg' % (self.train_path, cls, cls, i + 1)
def fit(self, train_path, k):
self.train_path = train_path
flann_params = dict(algorithm=1, tree=5)
flann = cv2.FlannBasedMatcher(flann_params, {})
# 创建BOW训练器,指定k-means参数k 把处理好的特征数据全部合并,利用聚类把特征词分为若干类,此若干类的数目由自己设定,每一类相当于一个视觉词汇
bow_kmeans_trainer = cv2.BOWKMeansTrainer(k)
pos = 'dog'
neg = 'cat'
# 指定用于提取词汇字典的样本数
length = 10
# 合并特征数据 每个类从数据集中读取length张图片(length个狗,length个猫),通过聚类创建视觉词汇
for i in range(length):
bow_kmeans_trainer.add(self.sift_descriptor_extractor(self.path(pos, i)))
bow_kmeans_trainer.add(self.sift_descriptor_extractor(self.path(neg, i)))
# 进行k-means聚类,返回词汇字典 也就是聚类中心
voc = bow_kmeans_trainer.cluster()
# 输出词汇字典 <class 'numpy.ndarray'> (40, 128)
print(type(voc), voc.shape)
# 初始化bow提取器(设置词汇字典),用于提取每一张图像的BOW特征描述
self.bow_img_descriptor_extractor = cv2.BOWImgDescriptorExtractor(self.descriptor_extractor, flann)
self.bow_img_descriptor_extractor.setVocabulary(voc)
# 创建两个数组,分别对应训练数据和标签,并用BOWImgDescriptorExtractor产生的描述符填充
# 按照下面的方法生成相应的正负样本图片的标签 1:正匹配 -1:负匹配
traindata, trainlabels = [], []
for i in range(self.train_sample_num): # 这里取200张图像做训练
traindata.extend(self.bow_descriptor_extractor(self.path(pos, i)))
trainlabels.append(1)
traindata.extend(self.bow_descriptor_extractor(self.path(neg, i)))
trainlabels.append(-1)
# 创建一个SVM对象
self.svm = cv2.ml.SVM_create()
# 使用训练数据和标签进行训练
self.svm.train(np.array(traindata), cv2.ml.ROW_SAMPLE, np.array(trainlabels))
def predict(self, img_path):
# 提取图片的BOW特征描述
data = self.bow_descriptor_extractor(img_path)
res = self.svm.predict(data)
print(img_path, '\t', res[1][0][0])
# 如果是狗 返回True
if res[1][0][0] == 1.0:
return True
# 如果是猫,返回False
else:
return False
def sift_descriptor_extractor(self, img_path):
'''
特征提取:提取数据集中每幅图像的特征点,然后提取特征描述符,形成特征数据(如:SIFT或者SURF方法);
'''
im = cv2.imread(img_path, 0)
return self.descriptor_extractor.compute(im, self.feature_detector.detect(im))[1]
def bow_descriptor_extractor(self, img_path):
'''
提取图像的BOW特征描述(即利用视觉词袋量化图像特征)
'''
im = cv2.imread(img_path, 0)
return self.bow_img_descriptor_extractor.compute(im, self.feature_detector.detect(im))
if __name__ == '__main__':
test_samples = str(args.train_test_pair).split("_")[1]
print("test samples:", test_samples)
test_samples = int(test_samples)
test_results = np.zeros(test_samples, dtype=np.bool)
# 训练集图片路径 狗和猫两类 进行训练
train_path = 'E:/Pycharm_project/homework3/data'
train_samples = str(args.train_test_pair).split("_")[0]
train_samples = int(train_samples)
print("train samples:", train_samples)
bow = BOW(train_sample_num=train_samples)
bow.fit(train_path, 40)
acc_dir = "./" + str(train_samples) + "_" + str(test_samples)
if not os.path.exists(acc_dir):
os.makedirs(acc_dir)
print("acc_path has been created:", acc_dir)
acc_path = acc_dir + "/acc.txt"
with open(acc_path, "w") as acc_f:
# 指定测试图像路径
for index in range(12400,12500):
dog = 'E:/Pycharm_project/homework3/data/dog/dog.{0}.jpg'.format(index)
print(dog)
dog_img = cv2.imread(dog)
# 预测
dog_predict = bow.predict(dog)
test_results[index - 12400] = dog_predict
# 计算准确率
accuracy = np.mean(test_results.astype(dtype=np.float32))
print('测试准确率为:', accuracy)
acc_f.write("测试准确率为: {}".format(accuracy))
acc_f.write('\n')
acc_f.flush()
acc_f.close()