python-sift-kmeans-svm图片分类

文章目录

    • 1理论
    • 2代码
      • 2.1debug记录
      • 2.2代码
    • 3 数据集
    • 4结果
    • 写在最后

1理论

理论部分见博客:SIFT+词袋+SVM的深入理解,感谢博主herr_kun,这个真是写的太好了,通俗易懂,尤其是那张自动笔画的图,真好https://blog.csdn.net/herr_kun/article/details/80220389

2代码

使用的代码来源于https://blog.csdn.net/Yan456jie/article/details/52313317
感谢博主Yan456jie
在跑代码的时候,发现该代码有一些函数在新版本的python上跑不通,有一些错误,所以调了一下

2.1debug记录

主要遇到的错误是
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

2.2代码

代码如下

#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)

3 数据集

数据集也是来自原博客,地址为https://download.csdn.net/detail/yan456jie/9614133就是该数据集取前4个类的一个小型的数据集。
python-sift-kmeans-svm图片分类_第1张图片
然后我好奇查了一下这个Caltech101数据集,喜闻乐见地是官网地址又下不了了。。。我又想起了oulu大学那个进不去的官网。。。忧伤,Caltech101官网链接如下http://www.vision.caltech.edu/Image_Datasets/Caltech101/
于是我在网上找了这个链接,Calttech101: https://hyper.ai/datasets/5258,下载方式如下,在页面中点击数据集下载,下载的是一个torent文件,到时候用迅雷下载就行
python-sift-kmeans-svm图片分类_第2张图片
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')

显示结果
python-sift-kmeans-svm图片分类_第3张图片

4结果

效果不佳,而且这个是不可重复的,数据集划分和svc分类器都没有设置随机数种子,但奇怪的是即使设置了随机数,结果还是不能重复,所以只能说马马虎虎调通了这个代码吧,还是有很多地方不了解。
python-sift-kmeans-svm图片分类_第4张图片

写在最后

感谢以上所有博主,另外求oulu大学的Outex纹理数据集,官网进不去了。。。嗯,然而我并没有钱。。。
另外,如何使结果可重复呢?是不是还有哪里的random_state是我不知道的

你可能感兴趣的:(Python,烟酒僧,Caltech101,Caltech256)