【火炉炼AI】机器学习051-视觉词袋模型+极端随机森林建立图像分类器

(本文所使用的Python库和版本号: Python 3.6, Numpy 1.14, scikit-learn 0.19, matplotlib 2.2 )

视觉词袋模型(Bag Of Visual Words,BOVW)来源于自然语言处理中的词袋模型(Bag Of Words, BOW),关于词袋模型,可以参考我的博文【火炉炼AI】机器学习038-NLP创建词袋模型.在NLP中,BOW的核心思想是将一个文档当做一个袋子,里面装着各种各样的单词,根据单词出现的频次或权重来衡量某个单词的重要性。BOW的一个重要特性是不考虑单词出现的顺序,句子语法等因素。

视觉词袋模型BOVW是将BOW的核心思想应用于图像处理领域的一种方法,为了表示一幅图像,我们可以将图像看做文档,即若干个“视觉单词”的集合,和BOW一样,不考虑这些视觉单词出现的顺序,故而BOVW的一个缺点是忽视了像素之间的空间位置信息(当然,针对这个缺点有很多改进版本)。BOVW的核心思想可以从下图中看出一二。

image

有人要问了,提取图像的特征方法有很多,比如SIFT特征提取器,Star特征提取器等,为什么还要使用BOVW模型来表征图像了?因为SIFT,Star这些特征提取器得到的特征矢量是多维的,比如SIFT矢量是128维,而且一幅图像通常会包含成百上千个SIFT矢量,在进行下游机器学习计算时,这个计算量非常大,效率很低,故而通常的做法是用聚类算法对这些特征矢量进行聚类,然后用聚类中的一个簇代表BOVW中的一个视觉单词,将同一幅图像的SIFT矢量映射到视觉视觉单词序列,生成视觉码本,这样,每一幅图像都可以用一个视觉码本矢量来描述,在后续的计算中,效率大大提高,有助于大规模的图像检索。

1. 使用BOVW建立图像数据集

BOVW主要包括三个关键步骤:

1,提取图像特征:提取算法可以使用SIFT,Star,HOG等方法,比如使用SIFT特征提取器,对数据集中的每一幅图像都使用SIFT后,每一个SIFT特征用一个128维描述特征向量表示,假如有M幅图像,一共提取出N个SIFT特征向量。

2,聚类得到视觉单词:最常用的是K-means,当然可以用其他聚类算法,使用聚类对N个SIFT特征向量进行聚类,K-means会将N个特征向量分成K个簇,使得每个簇内部的特征向量都具有非常高的相似度,而簇间的相似度较低,聚类后会得到K个聚类中心(在BOVW中,聚类中心被称为视觉单词)。计算每一幅图像的每一个SIFT特征到这K个视觉单词的距离,并将其映射到距离最近的一个簇中(即该视觉单词的对应词频+1)。这样,每一幅图像都变成了一个与视觉单词相对应的词频矢量。

3,构建视觉码本:因为每一幅图像的SIFT特征个数不相等,所以需要对这些词频矢量进行归一化,将每幅图像的SIFT特征个数变为频数,这样就得到视觉码本。

整个流程可以简单地用下图描述:

image

下面开始准备数据集,首先从Caltech256图像抽取3类,每一类随机抽取20张图片,组成一个小型数据集,每一个类别放在一个文件夹中,且文件夹的命名以数字和“-”开头,数字就表示类别名称。这个小数据集纯粹是验证算法是否能跑通。如下为准备的数据集:

image

首先来看第一步的代码:提取图像特征的代码:

def __img_sift_features(self,image):

'''

提取图片image中的Star特征的关键点,然后用SIFT特征提取器进行计算,

得到N行128列的矩阵,每幅图中提取的Star特征个数不一样,故而N不一样,

但是经过SIFT计算之后,特征的维度都变成128维。

返回该N行128列的矩阵

'''

keypoints=xfeatures2d.StarDetector_create().detect(image)

gray=cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)

_,feature_vectors=xfeatures2d.SIFT_create().compute(gray,keypoints)

return feature_vectors

然后将得到的所有图片的N行128列特征集合起来,组成M行128列特征,构建一个聚类算法,用这个算法来映射得到含有32个聚类中心(视觉单词)的模型,将这128列特征映射到32个视觉单词中(由于此处Kmeans我使用32个簇,故而得到32个视觉单词,越复杂的项目,这个值要调整的越大,从几百到几千不等。),在统计每一个特征出现的频次,组成一个词袋模型,如下代码:

def __map_feature_to_cluster(self,img_path):

'''从单张图片中提取Star特征矩阵(N行128列),

再将该特征矩阵通过K-means聚类算法映射到K个类别中,每一行特征映射到一个簇中,得到N个簇标号的向量,

统计每一个簇中出现的特征向量的个数,相当于统计词袋中某个单词出现的频次。

'''

img_feature_vectors=self.__img_sift_features(self.__get_image(img_path)) # N 行128列

cluster_labels=self.cluster_model.predict(img_feature_vectors)

# 计算这些特征在K个簇中的类别,得到N个数字,每个数字是0-31中的某一个,代表该Star特征属于哪一个簇

# eg [30 30 30 6 30 30 23 25 23 23 30 30 16 17 31 30 30 30 4 25]

# 统计每个簇中特征的个数

vector_nums=np.zeros(self.clusters_num) # 32个元素

for num in cluster_labels:

vector_nums[num]+=1

# 将特征个数归一化处理:得到百分比而非个数

sum_=sum(vector_nums)

return [vector_nums/sum_] if sum_>0 else [vector_nums] # 一行32列,32 个元素组成的list

上面仅仅是用一部分图片来得到聚类中心,没有用全部的图像,因为部分图像完全可以代表全部图像。

第三步:获取多张图片的视觉码本,将这些视觉码本组成一个P行32列的矩阵。

def __calc_imgs_clusters(self,img_path_list):

'''获取多张图片的视觉码本,将这些视觉码本组成一个P行32列的矩阵,P是图片张数,32是聚类的类别数。

返回该P行32列的矩阵'''

img_paths=list(itertools.chain(*img_path_list)) # 将多层list展开

code_books=[]

[code_books.extend(self.__map_feature_to_cluster(img_path)) for img_path in img_paths]

return code_books

完整的准备数据集的代码比较长,如下:

# 准备数据集

import cv2,itertools,pickle,os

from cv2 import xfeatures2d

from glob import glob

class DataSet:

def __init__(self,img_folder,cluster_model_path,img_ext='jpg',max_samples=12,clusters_num=32):

self.img_folder=img_folder

self.cluster_model_path=cluster_model_path

self.img_ext=img_ext

self.max_samples=max_samples

self.clusters_num=clusters_num

self.img_paths=self.__get_img_paths()

self.all_img_paths=[list(item.values())[0] for item in self.img_paths]

self.cluster_model=self.__load_cluster_model()

def __get_img_paths(self):

folders=glob(self.img_folder+'/*-*') # 由于图片文件夹的名称是数字+‘-’开头,故而可以用这个来获取

img_paths=[]

for folder in folders:

class_label=folder.split('\\')[-1]

img_paths.append({class_label:glob(folder+'/*.'+self.img_ext)})

# 每一个元素都是一个dict,key为文件夹名称,value为该文件夹下所有图片的路径组成的list

return img_paths

def __get_image(self,img_path,new_size=200):

def resize_img(image,new_size):

'''将image的长或宽中的最小值调整到new_size'''

h,w=image.shape[:2]

ratio=new_size/min(h,w)

return cv2.resize(image,(int(w*ratio),int(h*ratio)))

image=cv2.imread(img_path)

return resize_img(image,new_size)

def __img_sift_features(self,image):

'''

提取图片image中的Star特征的关键点,然后用SIFT特征提取器进行计算,

得到N行128列的矩阵,每幅图中提取的Star特征个数不一样,故而N不一样,

但是经过SIFT计算之后,特征的维度都变成128维。

返回该N行128列的矩阵

'''

keypoints=xfeatures2d.StarDetector_create().detect(image)

gray=cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)

_,feature_vectors=xfeatures2d.SIFT_create().compute(gray,keypoints)

return feature_vectors

def __calc_imgs_features(self,img_path_list):

'''获取多张图片的特征矢量,这些特征矢量是合并到一起的,最终组成M行128列的矩阵,返回该矩阵.

此处的M是每张图片的特征矢量个数之和,即N1+N2+N3....'''

img_paths=list(itertools.chain(*img_path_list)) # 将多层list展开

feature_vectors=[]

[feature_vectors.extend(self.__img_sift_features(self.__get_image(img_path))) for img_path in img_paths]

return feature_vectors

def __create_save_Cluster(self):

'''由于folders中含有大量图片,故而取一小部分(max_samples)图片来做K-means聚类。

'''

# 获取要进行聚类的小部分图片的路径

cluster_img_paths=[list(item.values())[0][:self.max_samples] for item in self.img_paths]

feature_vectors=self.__calc_imgs_features(cluster_img_paths)

cluster_model = KMeans(self.clusters_num, # 建立聚类模型

n_init=10,

max_iter=10, tol=1.0)

cluster_model.fit(feature_vectors) # 对聚类模型进行训练

# 将聚类模型保存,以后就不需要再训练了。

with open(self.cluster_model_path,'wb+') as file:

pickle.dump(cluster_model,file)

print('cluster model is saved to {}.'.format(self.cluster_model_path))

return cluster_model

def __map_feature_to_cluster(self,img_path):

'''从单张图片中提取Star特征矩阵(N行128列),

再将该特征矩阵通过K-means聚类算法映射到K个类别中,每一行特征映射到一个簇中,得到N个簇标号的向量,

统计每一个簇中出现的特征向量的个数,相当于统计词袋中某个单词出现的频次。

'''

img_feature_vectors=self.__img_sift_features(self.__get_image(img_path)) # N 行128列

cluster_labels=self.cluster_model.predict(img_feature_vectors)

# 计算这些特征在K个簇中的类别,得到N个数字,每个数字是0-31中的某一个,代表该Star特征属于哪一个簇

# eg [30 30 30 6 30 30 23 25 23 23 30 30 16 17 31 30 30 30 4 25]

# 统计每个簇中特征的个数

vector_nums=np.zeros(self.clusters_num) # 32个元素

for num in cluster_labels:

vector_nums[num]+=1

# 将特征个数归一化处理:得到百分比而非个数

sum_=sum(vector_nums)

return [vector_nums/sum_] if sum_>0 else [vector_nums] # 一行32列,32 个元素组成的list

def __calc_imgs_clusters(self,img_path_list):

'''获取多张图片的视觉码本,将这些视觉码本组成一个P行32列的矩阵,P是图片张数,32是聚类的类别数。

返回该P行32列的矩阵'''

img_paths=list(itertools.chain(*img_path_list)) # 将多层list展开

code_books=[]

[code_books.extend(self.__map_feature_to_cluster(img_path)) for img_path in img_paths]

return code_books

def __load_cluster_model(self):

'''从cluster_model_path中加载聚类模型,返回该模型,如果不存在或出错,则调用函数准备聚类模型'''

cluster_model=None

if os.path.exists(self.cluster_model_path):

try:

with open(self.cluster_model_path, 'rb') as f:

cluster_model = pickle.load(f)

except:

pass

if cluster_model is None:

print('No valid model found, start to prepare model...')

cluster_model=self.__create_save_Cluster()

return cluster_model

def get_img_code_book(self,img_path):

'''获取单张图片的视觉码本,即一行32列的list,每个元素都是对应特征出现的频率'''

return self.__map_feature_to_cluster(img_path)

def get_imgs_code_books(self,img_path_list):

'''获取多张图片的视觉码本,即P行32列的list,每个元素都是对应特征出现的频率'''

return self.__calc_imgs_clusters(img_path_list)

def get_all_img_code_books(self):

'''获取img_folder中所有图片的视觉码本'''

return self.__calc_imgs_clusters(self.all_img_paths)

def get_img_labels(self):

'''获取img_folder中所有图片对应的label,可以从文件夹名称中获取'''

img_paths=list(itertools.chain(*self.all_img_paths))

return [img_path.rpartition('-')[0].rpartition('\\')[2] for img_path in img_paths]

def prepare_dataset(self):

'''获取img_folder中所有图片的视觉码本和label,构成数据集'''

features=self.get_all_img_code_books()

labels=self.get_img_labels()

return np.c_[features,labels]

2. 使用极端随机森林建立模型

极端随机森林是随机森林算法的一个提升版本,可以参考我以前的文章【火炉炼AI】机器学习007-用随机森林构建共享单车需求预测模型.使用方法和随机森林几乎一样。

# 极端随机森林分类器

from sklearn.ensemble import ExtraTreesClassifier

class CLF_Model:

def __init__(self,n_estimators=100,max_depth=16):

self.model=ExtraTreesClassifier(n_estimators=n_estimators,

max_depth=max_depth, random_state=12)

def fit(self,train_X,train_y):

self.model.fit(train_X,train_y)

def predict(self,newSample_X):

return self.model.predict(newSample_X)

其实,这个分类器很简单,没必要写成类的形式。

对该分类器进行训练:

dataset_df=pd.read_csv('./prepared_set.txt',index_col=[0])

dataset_X,dataset_y=dataset_df.iloc[:,:-1].values,dataset_df.iloc[:,-1].values

model=CLF_Model()

model.fit(dataset_X,dataset_y)

3. 使用训练后模型预测新样本

如下,我随机测试三张图片,均得到了比较好的结果。

# 用训练好的model预测新图片,看看它属于哪一类

new_img1='E:\PyProjects\DataSet\FireAI/test0.jpg'

img_code_book=dataset.get_img_code_book(new_img1)

predicted=model.predict(img_code_book)

print(predicted)

new_img2='E:\PyProjects\DataSet\FireAI/test1.jpg'

img_code_book=dataset.get_img_code_book(new_img2)

predicted=model.predict(img_code_book)

print(predicted)

new_img3='E:\PyProjects\DataSet\FireAI/test2.jpg'

img_code_book=dataset.get_img_code_book(new_img3)

predicted=model.predict(img_code_book)

print(predicted)

-------------------------------------输---------出--------------------------------

[0]

[1]

[2]

--------------------------------------------完-------------------------------------

########################小**********结###############################

1,这个项目的难点在于视觉词袋模型的理解和数据集准备,所以我将其写成了类的形式,这个类具有一定的通用性,可以用于其他项目数据集的制备。

2,从这个项目可以看出视觉词袋模型相对于原始的Star特征的优势:如果使用原来的Star特征,一张图片会得到N行128列的特征数,而使用了BOVW模型,我们将N行128列的特征数据映射到1行32列的空间中,所以极大的降低了特征数,使得模型简化,训练和预测效率提高。

3,一旦准备好了数据集,就可以用各种常规的机器学习分类器进行分类,也可以用各种方法评估该分类器的优劣,比如性能报告,准确率,召回率等,由于这部分我在前面的文章中已经讲过多次,故而此处省略。

#################################################################

注:本部分代码已经全部上传到(我的github)上,欢迎下载。

参考资料:

1, Python机器学习经典实例,Prateek Joshi著,陶俊杰,陈小莉译

词袋模型 matlab,【火炉炼AI】机器学习051-视觉词袋模型+极端随机森林建立图像分类器...相关推荐

  1. 【火炉炼AI】机器学习044-创建隐马尔科夫模型

    [火炉炼AI]机器学习044-创建隐马尔科夫模型 (本文所使用的Python库和版本号: Python 3.6, Numpy 1.14, scikit-learn 0.19, matplotlib 2 ...

  2. 【火炉炼AI】机器学习007-用随机森林构建共享单车需求预测模型

    [火炉炼AI]机器学习007-用随机森林构建共享单车需求预测模型 (本文所使用的Python库和版本号: Python 3.5, Numpy 1.14, scikit-learn 0.19, matp ...

  3. python用numpy和pil处理图像成灰度图_「火炉炼AI」机器学习047-图像的直方图均衡化操作...

    [火炉炼AI]机器学习047-图像的直方图均衡化操作 [火炉炼AI]机器学习047-图像的直方图均衡化操作 (本文所使用的Python库和版本号: Python 3.6, Numpy 1.14, sc ...

  4. 【火炉炼AI】机器学习013-用朴素贝叶斯分类器估算个人收入阶层

    [火炉炼AI]机器学习013-用朴素贝叶斯分类器估算个人收入阶层 (本文所使用的Python库和版本号: Python 3.5, Numpy 1.14, scikit-learn 0.19, matp ...

  5. 【火炉炼AI】机器学习018-项目案例:根据大楼进出人数预测是否举办活动

    [火炉炼AI]机器学习018-项目案例:根据大楼进出人数预测是否举办活动 (本文所使用的Python库和版本号: Python 3.5, Numpy 1.14, scikit-learn 0.19, ...

  6. 【火炉炼AI】机器学习032-用户之间相似度的计算

    [火炉炼AI]机器学习032-用户之间相似度的计算 (本文所使用的Python库和版本号: Python 3.6, Numpy 1.14, scikit-learn 0.19, matplotlib ...

  7. 【火炉炼AI】机器学习006-用决策树回归器构建房价评估模型

    [火炉炼AI]机器学习006-用决策树回归器构建房价评估模型 (本文所使用的Python库和版本号: Python 3.5, Numpy 1.14, scikit-learn 0.19, matplo ...

  8. 【火炉炼AI】机器学习040-NLP性别判断分类器

    [火炉炼AI]机器学习040-NLP性别判断分类器 (本文所使用的Python库和版本号: Python 3.6, Numpy 1.14, scikit-learn 0.19, matplotlib ...

  9. 【火炉炼AI】机器学习055-使用LBP直方图建立人脸识别器

    [火炉炼AI]机器学习055-使用LBP直方图建立人脸识别器 (本文所使用的Python库和版本号: Python 3.6, Numpy 1.14, scikit-learn 0.19, matplo ...

最新文章

  1. 使用Nomad构建弹性基础架构: 作业生命周期
  2. Android和IOS打开文档
  3. 朴素贝叶斯和贝叶斯估计
  4. [设计模式-行为型]模板方法模式(Template Method)
  5. C语言指针类型 强制转换
  6. 【已解决】报异常:java.lang.ArithmeticException: / by zero
  7. vector中针对自定义类型的排序
  8. Q142:PBRT-V3,交点处各种微分的求解(三角形,3.6章节)
  9. 吴昊品游戏核心算法 Round 5 —— 五子棋(无禁手)后台的判胜负AI(bfs+queue容器)(HDOJ 2699)...
  10. 密码攻略 黑客亲手打造QQ密码破解器(转)
  11. Excel-VBA基础(11):VBA中数组基础知识
  12. python:实现培根密码算法(附完整源码)
  13. 广东英语高考怎么计算机,2019广东高考英语听说考试大纲出炉!附三大题型得分套路!...
  14. java毕业设计校园快递柜存取件系统(附源码、数据库)
  15. 我也是从寒门走出来的,程序员这个职业是我最好的选择!
  16. 如果只定一个指标,市场运营的考核指标应该是什么?
  17. c语言判断a b大小的值,c语言abc条件值的判定
  18. 讲真,你与尖子生之间只有这本书的距离
  19. 我的遗憾 - Mini 迅雷
  20. mysql卜耀华电子书_PHP+MYSQL网站开发与实践教程/卜耀华

热门文章

  1. 网络安全系列之二十六 EFS加密
  2. C7N新增,保存,删除基础模板
  3. [转]拍照怎么搜题?(上)
  4. 解读爱奇艺Q2财报:会员数破亿,其他收入打开新增长空间?
  5. python积木编程软件_积木编程软件手机版下载
  6. R语言 dataframe 取指定行列filter 随机取数
  7. LVGL hal disp(porting display)
  8. 记一次服务器被攻击后的经历
  9. Springboot毕设项目基于SpringBoot的学生宿舍水电费缴费系统的设计与实现2py81(java+VUE+Mybatis+Maven+Mysql)
  10. web3开发课程精选