利用SIFT/ORB和KMeans方法实现图像的聚类

利用SIFT或ORB和KMeans方法实现图像的聚类

一、使用SIFT或ORB提取特征

    尺度不变特征转换(Scale-invariant feature transform)是一种用来侦测与描述影像中的局部性特征的视觉算法,即通过求一幅图中的特征点及其有关尺度和方向的描述子得到特征,并进行图像特征点匹配。SIFT所查找到的特征点是一些十分突出,不会因光照,仿射变换和噪音等因素而变化的点,如角点、边缘点、暗区的亮点及亮区的暗点等,噪声也保持一定程度的稳定性,除此之外SIFT还具有独特性,多量性,高速性,可扩展性等特点。
    ORB(Oriented FAST and Rotated BRIEF)是一种快速特征点提取和描述的算法。ORB 特征是将 FAST 特征点的检测方法与 BRIEF 特征描述子结合起来,并在它们原来的基础上做了改进与优化。
    ORB算法之前的周报已经总结过,本次选用ORB算法提取特征是因为这是最近所学习的算法,有一定的基础,并且没使用SIFT或SURF算法提取特征是因为经过实验对比后发现ORB效果相对好一点,其他两种提取特征的方法得到的结果不理想。

二、使用KMeans进行聚类

    k-means 算法将一组 N 样本 X 划分成 K 不相交的簇 C, 每个都用该簇中的样本的均值 μ j \mu_j μj 描述。这个均值(means)通常被称为簇的“质心(centroids)”。K-means(K-均值)算法旨在选择一个质心, 能够最小化惯性或簇内平方和的标准:
∑ i = 0 n m i n u j ∈ C ( ∣ ∣ x j − u i ∣ ∣ 2 ) \sum_{i=0}^{n}{min_{u_j \in C}(\mid\mid x_j - u_i\mid\mid^{2})} i=0nminujC(xjui2)
    该算法可分为三个步骤。第一步是选择初始质心,最基本的方法是从 X 数据集中选择 k 个样本。初始化完成后,K-means 由接下来两个步骤之间的循环组成。 第一步将每个样本分配到其最近的质心。第二步通过取分配给每个先前质心的所有样本的平均值来创建新的质心。计算旧的和新的质心之间的差异,并且算法重复这些最后的两个步骤,直到该值小于阈值。

三、自己构建的图像数据集

    我选取了100张图片作为数据集,分别有10个类,每个类各有10张图片,分别为猫,狗,鸭舌帽,书包,飞机,人,热水壶,汽车,球鞋,椅子。
数据集放在了百度网盘,百度网盘链接:https://pan.baidu.com/s/1ldUB3fAIk2ameYvM1W3AsQ
提取码:jbpb

(1)狗示例

(2)猫示例

(3)飞机示例

(4)椅子示例

(5)汽车示例

(6)人示例

(7)水壶示例

(8)球鞋示例

(9)帽子示例

(10)书包示例

四、代码文件

import os
import cv2
import numpy as np
from sklearn.cluster import KMeans #在sklearn.cluster库中有KMeans类可以直接使用。
import skimage.io as io
from PIL import Image
import os.path
import glob
import matplotlib.pyplot as plt


#convertjpg:设置统一图像的尺寸400*400
def convertjpg(jpgfile,outdir,width = 400 ,height = 400):
  img = Image.open(jpgfile)
  try:
    new_img=img.resize((width,height),Image.BILINEAR)  
    new_img.save(os.path.join(outdir,os.path.basename(jpgfile)))
  except Exception as e:
    print(e)
for jpgfile in glob.glob("test\\*.jpg"):
  convertjpg(jpgfile,"image_dataset")

#get_filename:获得图片的路径文件名
def get_filenames(path):
    path_filenames = []
    filenames = os.listdir(path)#  os.listdir() 方法用于返回指定的文件夹包含的文件或文件夹的名字的列表。
    for file in filenames:
        if not file.startswith('.'):
            path_filenames.append(os.path.join(path, file)) #os.path.join路径拼接
    return path_filenames

#image_cluster 使用SIFT和KMEANS算法进行图像聚类,得到图像标签
def image_clusterByKMeans(cluster_nums, path_filenames, randomState = None):
    features = []
    '''
    特征提取的方法分为三种 这里实验了2种。
    一种是SIFT算法,一种是ORB算法
    '''
    #orb = cv2.ORB_create(nfeatures = 40) #使用ORB算法提取图像特征,经过多次试验,发现提取特征点的个数为nfeatures = 100以下时,聚类效果相对较好
    #surf = cv2.xfeatures2d.SURF_create(400)#使用SURF算法提取图像特征
    sift = cv2.xfeatures2d.SIFT_create() #使用SIFT算法提取图像特征

    #count  = 0
    files = path_filenames #特征检测

    for file in files:
        #count = count + 1
        #print(count)
        #print(file)
        img = cv2.imread(file)#读入文件
        #print(file)
        #print(gray.dtype) #打印类型
        gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) #灰度化处理,加快计算速度

        kp,des = sift.detectAndCompute(gray, None) #调用SIFT算法检测并计算描述符
        
        #print(des)
        print(des.shape)
        #我在实验中出现了图片对象是NONETYPE的报错,后来经过检查发现是图片本身的问题,替换相同类型的图片可以解决该问题
        if des is None:
            path_filenames.remove(file)
            continue
        reshape_feature = des.reshape(-1, 1)
        print(reshape_feature.shape)
        '''
        这里也有2种方法进行实验,第一种是我查阅资料看到大部分人取的是reshape_feature[0]即只取第一个元素的值;
        第二种是我在实验过程中觉得可以取平均值mean来算,使用np.mean(reshape_feature)
        但是我觉得2者的结果没有太大的差别,可能平均值的大部分比较好,但是有个别类只有1-2个图片。
        '''
        
        #features.append(reshape_feature[0].tolist()) #第一种取第一个元素
        reshape_feature_Mean = np.array([np.mean(reshape_feature)]) #第二种取平均值
        features.append(reshape_feature_Mean.tolist())
        
        print(reshape_feature[0])
        print(reshape_feature_Mean)
    input_x = np.array(features) #计算关键点 因为KMeans.fix(X[,y) X是需要2D 而不是1D
    print(input_x)
    #print("x的值为:")
    print(input_x.shape)
    kmeans = KMeans(n_clusters = cluster_nums, max_iter = 10000).fit(input_x) #关键点聚类
    return kmeans.labels_, kmeans.cluster_centers_     #返回标签以及聚类中心
    




def main():
    #①调用get_filenames函数获取拼接后的image_datasets文件夹中的图片名字
    path_filenames = get_filenames("image_dataset")
    #print(path_filenames)
    imgs = os.listdir('image_dataset')
    imgnum = len(imgs)  # 文件夹中图片的数量,为100张的图片
    #print(imgnum)
    cluster_nums = 10 #识别k类
    #②调用image_cluster函数实现SIFT提取特征并使用KNN聚类方法,得到图像标签
    labels, cluster_centers = image_clusterByKMeans(cluster_nums, path_filenames)
    #print(labels.tolist())

    res = labels.tolist()
    #print(len(res))
    result = [[] for i in range(10)]
    i = 0
    for i in range(imgnum):
        result[res[i]].append(i + 1)

    for i in range(cluster_nums):
        print('%02d个'%(res.count(i)), end=': ')
        print(result[i])  
    #图像聚类结果可视化,一行内的图像都被分为一类
    for i in range(cluster_nums):
        size = len(result[i]) 
        plt.figure(figsize=(20, 20))
        for j in range(size):
            ax = plt.subplot(1, size, 1 + j)
            plt.xticks([])  
            plt.yticks([]) 
            #plt.subplot(1, size, j + 1)
            file = path_filenames[result[i][j] - 1]
            img = io.imread(file)

            ax.imshow(img)
           
        plt.show()
if __name__ == "__main__": 
    main()



五、实验结果

(1)SIFT+KMEANS

图1 实验结果1

(2)ORB+KMEANS

图2 实验结果2

参考资料:

https://docs.opencv.org/master/db/d95/classcv_1_1ORB.html
https://github.com/Byronnar/image_classfication
https://scikit-learn.org/stable/modules/generated/sklearn.cluster.KMeans.html#sklearn.cluster.KMeans
https://blog.csdn.net/qq_18150255/article/details/88040858
https://blog.csdn.net/weixin_43823854/article/details/102017382
https://blog.csdn.net/dss_dssssd/article/details/82692894

猜你喜欢

转载自blog.csdn.net/weixin_43823854/article/details/102540793