梯度直方图特征、phash特征的k-means聚类以及层次聚类

《将图片转化为向量并使用K-means进行聚类》

https://blog.csdn.net/gusui7202/article/details/88081072

使用了原始图像数据进行聚类,现在先提取图像的特征然后再进行聚类。

首先使用的是梯度直方图特征,代码如下:主要还是修改地址和聚类数目

#!/usr/bin/python
# coding=utf-8
'''
基于直方图特征的图片聚类实现
'''
import numpy as np
import os
from PIL import Image
#coding=utf-8
from numpy import *

def loadDataSet(fileName):
    dataMat = []
    fr = open(fileName)
    for line in fr.readlines():
        curLine = line.strip().split('\t')
        fltLine = map(float, curLine)
        dataMat.append(fltLine)
    return dataMat
    
#计算两个向量的距离,用的是欧几里得距离
def distEclud(vecA, vecB):
    return np.sqrt(sum(np.power(vecA - vecB, 2)))

#随机生成初始的质心(ng的课说的初始方式是随机选K个点)    
def randCent(dataSet, k):
    n = np.shape(dataSet)[1]
    centroids = np.mat(np.zeros((k,n)))
    for j in range(n):
        minJ = min(dataSet[:,j])
        rangeJ = float(max(np.array(dataSet)[:,j]) - minJ)
        centroids[:,j] = minJ + rangeJ * np.random.rand(k,1)
    return centroids
    
def kMeans(dataSet, k, distMeas=distEclud, createCent=randCent):
    m =np.shape(dataSet)[0]
    clusterAssment = np.mat(np.zeros((m,2)))#create mat to assign data points 
                                      #to a centroid, also holds SE of each point
    centroids = createCent(dataSet, k)
    clusterChanged = True
    while clusterChanged:
        clusterChanged = False
        for i in range(m):#for each data point assign it to the closest centroid
            minDist = np.inf
            minIndex = -1
            for j in range(k):
                distJI = distMeas(centroids[j,:],dataSet[i,:])
                if distJI < minDist:
                    minDist = distJI; minIndex = j
            if clusterAssment[i,0] != minIndex: 
                clusterChanged = True
            clusterAssment[i,:] = minIndex,minDist**2
        #print centroids
        for cent in range(k):#recalculate centroids
            ptsInClust = dataSet[nonzero(clusterAssment[:,0].A==cent)[0]]#get all the point in this cluster
            centroids[cent,:] = mean(ptsInClust, axis=0) #assign centroid to mean 
    return centroids, clusterAssment
    
def show(dataSet, k, centroids, clusterAssment):
    from matplotlib import pyplot as plt  
    numSamples, dim = dataSet.shape  
    mark = ['or', 'ob', 'og', 'ok', '^r', '+r', 'sr', 'dr', '<r', 'pr']  
    for i in xrange(numSamples):  
        markIndex = int(clusterAssment[i, 0])  
        plt.plot(dataSet[i, 0], dataSet[i, 1], mark[markIndex])  
    mark = ['Dr', 'Db', 'Dg', 'Dk', '^b', '+b', 'sb', 'db', '<b', 'pb']  
    for i in range(k):  
        plt.plot(centroids[i, 0], centroids[i, 1], mark[i], markersize = 12)  
    plt.show()
      
def main():
    dataMat = aa()
    myCentroids, clustAssing= kMeans(dataMat,2)
    print dataMat
    show(dataMat, 2, myCentroids, clustAssing)  
    

def aa():
 path = 'C:/Users/nansbas/Desktop/julei/'
 imlist = [os.path.join(path, f) for f in os.listdir(path) if f.endswith('.jpg')]
 # extract feature vector (8 bins per color channel)
 features = zeros([len(imlist), 512])#特征长度512
 for i, f in enumerate(imlist):
     im = array(Image.open(f))#Image不是image包,是PIL里的Image模块
     # multi-dimensional histogram
     h, edges = histogramdd(im.reshape(-1, 3), 8, normed=True, range=[(0, 255), (0, 255), (0, 255)])#reshape函数要导入Numpy 用其进行平整,将图像拉成一个三维数据。
 #print(len(h))=8
 #print(edges)=三个数组,每个数组8个数值,一共24个数据
     features[i] = h.flatten()
 path = 'C:/Users/nansbas/Desktop/julei/'
 imlist = [os.path.join(path, f) for f in os.listdir(path) if f.endswith('.jpg')]
 # extract feature vector (8 bins per color channel)
 features = zeros([len(imlist), 512])#特征长度512
 for i, f in enumerate(imlist):
     im = array(Image.open(f))#Image不是image包,是PIL里的Image模块
     # multi-dimensional histogram
     h, edges = histogramdd(im.reshape(-1, 3), 8, normed=True, range=[(0, 255), (0, 255), (0, 255)])#reshape函数要导入Numpy 用其进行平整,将图像拉成一个三维数据。
 #print(len(h))=8
 #print(edges)=三个数组,每个数组8个数值,一共24个数据
     features[i] = h.flatten()
 #data = np.loadtxt('K-means_data')
 return features
 
if __name__ == '__main__':
    main()
	

然后是层次聚类方法,使用了PCV的clustering包,里面有现成的层次聚类算法可以使用。

 # -*- coding: utf-8 -*-
import os
from PIL import Image
from PCV.clustering import hcluster
from matplotlib.pyplot import *
from numpy import *
 
# create a list of images
path = 'C:/Users/nansbas/Desktop/julei/'
imlist = [os.path.join(path, f) for f in os.listdir(path) if f.endswith('.jpg')]
# extract feature vector (8 bins per color channel)
features = zeros([len(imlist), 512])#特征长度512
for i, f in enumerate(imlist):
    im = array(Image.open(f))#Image不是image包,是PIL里的Image模块
    # multi-dimensional histogram
    h, edges = histogramdd(im.reshape(-1, 3), 8, normed=True, range=[(0, 255), (0, 255), (0, 255)])#reshape函数要导入Numpy 用其进行平整,将图像拉成一个三维数据。
#print(len(h))=8
#print(edges)=三个数组,每个数组8个数值,一共24个数据
    features[i] = h.flatten()
tree = hcluster.hcluster(features)#聚类分析
# visualize clusters with some (arbitrary) threshold
clusters = tree.extract_clusters(20 * tree.distance)
print(len(clusters))
# plot images for clusters with more than 3 elements
for c in clusters:
    elements = c.get_cluster_elements()
    nbr_elements = len(elements)
    if nbr_elements > 50:
        figure()
        for p in range(minimum(nbr_elements,20)):
            subplot(4, 5, p + 1)
            im = array(Image.open(imlist[elements[p]]))
            imshow(im)
            axis('off')
show()
 
hcluster.draw_dendrogram(tree,imlist,filename='sunset.pdf')

最后,就是phash特征的聚类了。我用的都是k-means,代码如下:

import cv2
import numpy as np
import matplotlib.pyplot as plt
from sklearn.cluster import KMeans  
from math import log ,sqrt,pow
import math
import random
 
 
"""
数据预处理
"""
datanumber=1000 #图像个数
print('读入图片')
def unpickle(file):
    import pickle
    with open(file,'rb')as fo:
        dict =pickle.load(fo,encoding='bytes')
    return dict
path='C:/Users/nansbas/Desktop/julei/'#路径可以自己定义
a=unpickle(path+'data_batch_1')
temp=np.zeros((10000,3072))
data_label=np.zeros(50000)
data_array=np.zeros((50000,3072))
for i in range(5):
    cur_dict=unpickle(path+'data_batch_'+str(i+1))
    for j in range(10000):
        data_array[i*10000+j]=cur_dict[b'data'][j][:]
        data_label[i*10000+j]=cur_dict[b'labels'][j]
data_array=data_array.reshape(50000,3,32,32).transpose(0,2,3,1).astype('float32')
data_label=data_label.astype('float32')
 
    
"""
phash图像特征
"""
def pHash(img):
    gray=cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
#创建二维列表
    h,w=gray.shape[:2]
    vis0=np.zeros((h,w),np.float32)
    vis0[:h,:w]=gray
    
#二维DCT变换
    vis1=cv2.dct(cv2.dct(vis0))
    img_list=vis1.flatten()
 
# 计算均值
    avg=sum(img_list)*1./len(img_list)
    avg_list=['0' if i <avg else '1' for i in img_list]
 
#得到哈希值
    hash=''.join(['%x '% int(''.join(avg_list[x:x+4]),2)for x in range(0,32*32,4)])
    hash1=[int(x,16) for x in hash.split()]
    return hash1
 
 
"""
分类图像phash特征提取
"""
true_labels=[]#cifar10真实标签
print('提取phash特征')
feature=[]
for i in range(datanumber):
    img=data_array[i,:,:,:].astype('uint8')
    true_labels.append(data_label[i].astype('uint8'))
    feature.append(pHash(img))
print('特征提取结束')
 
 
 
"""
Kmeans分类
"""
#计算欧几里得距离
def distance(vecA,vecB):
    return np.sqrt(np.sum(np.square(vecA-vecB)))
 
#取K个随机质心
def randcent(data,k):
    return random.sample(data,k)
 
#KMeans实现
def kmeans(dataset,k):
    m=len(dataset)
    clustterassment=np.zeros((m,2)).astype('float32')#二维向量,第一维存放类别 第二维存放距离本类别质心距离
    centnoids=randcent(dataset,k) #随机取K个质心
    clusterchanged=True #用来判断收敛,质心不再变化时,算法停止
    time=1
    while clusterchanged:
        clusterchanged=False
        for i in range(m):
            minDist=float("inf")        
            minIndex=-1
            for j in range(k):
                vec1=np.array(centnoids[j])
                vec2=np.array(dataset[i])
                distji=distance(vec1,vec2)
                if distji<minDist:
                    minDist=distji
                    minIndex=j
            if clustterassment[i,0]!=minIndex:
                clusterchanged=True
            clustterassment[i,:]=minIndex,minDist**2
#更新K个质心        
        for cent in range(k):
            pointsincluster=[]
            for num in range(m):
                if clustterassment[num][0]==cent:
                    pointsincluster.append(dataset[num])#取出同一类别的数据
            centtemp=np.mean(np.array(pointsincluster),axis=0)#计算均值
            centnoids[cent]=centtemp.tolist()
        print("kmeans 第%d次 迭代"%time)
        time+=1
    print("kmeans结束")
    predict_labels=clustterassment[:,0].astype('uint8')
    return predict_labels
 
 
"""
性能评估 熵
"""
#n_clusters=10  
#cls=KMeans(n_clusters).fit(feature)
#predict_labels=cls.labels_
predict_labels=kmeans(feature,10)
entropy=np.zeros(10).astype('float32')
Eall=0
cluster=np.zeros((10,10)).astype('float32')#10个真实标签 10个预测标签 组成10*10的矩阵 存放个数
print("分类图像总数 %d"%datanumber)
for i in range(datanumber):
    cluster[predict_labels[i],true_labels[i]]+=1
for i in range(10):
    Esum=sum(cluster[i,:])#每一类图像总数
    print (cluster[i,:])   
    print("第%d类共%d"%(i,Esum))
#计算熵    
    for j in range(10):
        p1=cluster[i][j]/Esum 
        if p1!=0:
            entropy[i]+=-(p1*log(p1))
    Eall+=Esum*entropy[i]
Eall=Eall/datanumber
print('评估矩阵完成,熵为%.5f'%Eall)   
 
 
 
"""
图像聚类可视化
"""
#256维特征feature 通过PCA降维 方便画图
from sklearn.decomposition import PCA  
pca = PCA(n_components=2)             #输出两维  
newData = pca.fit_transform(feature)   #载入N维  
x1=[]
y1=[]
x2=[]
y2=[]
x3=[]
y3=[]
x4=[]
y4=[]
x5=[]
y5=[]
x6=[]
y6=[]
x7=[]
y7=[]
x8=[]
y8=[]
x9=[]
y9=[]
x10=[]
y10=[]
for i in range(datanumber):
    if predict_labels[i]==0:
        x10.append(newData[i][0])
        y10.append(newData[i][1])
    elif predict_labels[i]==1:
        x1.append(newData[i][0])
        y1.append(newData[i][1])
    elif predict_labels[i]==2:
        x2.append(newData[i][0])
        y2.append(newData[i][1])
    elif predict_labels[i]==3:
        x3.append(newData[i][0])
        y3.append(newData[i][1])
    elif predict_labels[i]==4:
        x4.append(newData[i][0])
        y4.append(newData[i][1])
    elif predict_labels[i]==5:
        x5.append(newData[i][0])
        y5.append(newData[i][1])
    elif predict_labels[i]==6:
        x6.append(newData[i][0])
        y6.append(newData[i][1])
    elif predict_labels[i]==7:
        x7.append(newData[i][0])
        y7.append(newData[i][1])
#只取了6个类别给予显示
plt.plot(x1, y1, 'or')  
plt.plot(x2, y2, 'og')   
plt.plot(x3, y3, 'ob')  
plt.plot(x4, y4, 'ok')
plt.plot(x5, y5, 'oy')
plt.plot(x6, y6, 'oc')  
plt.show() 

因为这几个算法的结果我都没有保存,但都做了可视化模块,都是可以用的。

放一个结论:

为什么图像的聚类要提取特征?难道就是因为直接使用图像来进行聚类的实验效果不好?

理论上这个问题就和为什么要进行特征提取一样:

为什么要特征提取?有多个方面的因素:

1.我们的目的是找到某一种特点,从原图到这个特点的对应关系,是一种有特点的模式,所以其实我们需要去提取图片中的模式对应的数据内容并进行考量。比如,颜色:图片是什么颜色?颜色就是图片的一种模式。在数据上的体现为图像三个通道数值的大小。那么这个图片里面有什么几何图像?这就和图像数值的结构有关系了。是另一种模式。

即:我们需要的结果,是图像相关的一个方面,我们要得到这个结果,就要从图像的这个方面来考虑。

计算机是如何实现从图像的某个方面考虑的?特征。

2.冗余和干扰:在上面一点知道特征是图像的一个方面,既然他本来就有,那么我们为什么一定要提取出来?不提取出来就它就没有了吗?对。它还真的没有了。我们定义了是哪个方面,而图像本身是不会定义自己有多少个方面的,分析的量,都是我们定义好的量,那么不提取出来,图像这个方面就不成型,混在原始图像丰富的信息中,收到其他信息的干扰。

3.滤波器:特征提取就是一个过滤的过程。过滤到上面第2点说的干扰,也是特征形成的过程,表达的过程。而我们定义了滤波器的形状。

4. 从哲学上讲:原始图像数据包含了各个方面的丰富信息。

我们不可能没有目的处理图像而得到满足我们目的的结果,哪怕结果看起来很舒服。

以上。图像的聚类依赖数据的预处理,特别是特征提取。

猜你喜欢

转载自blog.csdn.net/gusui7202/article/details/88081259