接上一篇,使用了原始圖像數據進行聚類,現在先提取圖像的特徵然後再進行聚類。
首先使用的是梯度直方圖特徵,代碼如下:主要還是修改地址和聚類數目
#!/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. 從哲學上講:原始圖像數據包含了各個方面的豐富信息。
【我們不可能沒有目的處理圖像而得到滿足我們目的的結果,哪怕結果看起來很舒服。】
以上。圖像的聚類依賴數據的預處理,特別是特徵提取。