《将图片转化为向量并使用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. 从哲学上讲:原始图像数据包含了各个方面的丰富信息。
【我们不可能没有目的处理图像而得到满足我们目的的结果,哪怕结果看起来很舒服。】
以上。图像的聚类依赖数据的预处理,特别是特征提取。