梯度直方圖特徵、phash特徵的k-means聚類以及層次聚類

接上一篇,使用了原始圖像數據進行聚類,現在先提取圖像的特徵然後再進行聚類。

首先使用的是梯度直方圖特徵,代碼如下:主要還是修改地址和聚類數目

#!/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. 從哲學上講:原始圖像數據包含了各個方面的豐富信息。

我們不可能沒有目的處理圖像而得到滿足我們目的的結果,哪怕結果看起來很舒服。

以上。圖像的聚類依賴數據的預處理,特別是特徵提取。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章