【python计算机视觉编程——6.图像聚类】

python计算机视觉编程——6.图像聚类

  • 6.图像聚类
      • 6.1 K-means聚类
      • 6.1.2 图像聚类
      • 6.1.3 在主成分上可视化图像
      • 6.1.4 像素聚类
    • 6.2 层次聚类
    • 6.3 谱聚类

6.图像聚类

6.1 K-means聚类

from scipy.cluster.vq import *
import numpy as np
from pylab import *
matplotlib.rcParams['font.family'] = 'sans-serif'
matplotlib.rcParams['font.sans-serif'] = ['SimHei']  # 黑体字体
matplotlib.rcParams['axes.unicode_minus'] =False  # 显示负号
class1 = 1.5 * np.random.randn(100,2)  # 生成一个100x2的数组,每个元素都是从均值为0、标准差为1.5的正态分布中抽取的。
class2 = np.random.randn(100,2)+np.array([5,5])  # 生成一个100x2的数组,每个元素都是从均值为5、标准差为1的正态分布中抽取的,并且整体上移[5, 5]。
features=np.vstack((class1,class2))  # 将这两个生成的类别数据合并成一个200x2的数组。
# 对features数据集进行k-均值聚类,指定为2个簇。返回簇的质心和每个簇的方差。
# 是一个形状为(2, 2)的数组,其中每一行表示一个簇的质心(中心点)。由于指定了2个簇,centroids会有2行,每行包含2个值(对应每个簇的二维坐标)。
# variance:表示数据点到其对应簇的质心的总方差。方差越小,说明数据点与质心的距离越近,聚类效果越好。
centroids,variance=kmeans(features,2)

在这里插入图片描述

# 将每个数据点分配给离其最近的质心,并计算每个点到其分配的质心的距离。code包含簇的分配信息,distance包含距离。
code,distance=vq(features,centroids)
figure()
ndx=where(code==0)[0]  #找到被分配到簇 0 的数据点的索引。
plot(features[ndx,0],features[ndx,1],'*')
ndx=where(code==1)[0]
plot(features[ndx,0],features[ndx,1],'r.')
plot(centroids[:,0],centroids[:,1],'go')
show()

在这里插入图片描述

6.1.2 图像聚类

import pickle
from scipy.cluster.vq import *
import os
from PIL import Image
from pylab import *
def get_imlist(path):        
    return [os.path.join(path,f) for f in os.listdir(path) if f.endswith('.jpg')]
imlist=get_imlist(r'D:\pyFile\Python计算机视觉编程\data\selectedfontimages\a_selected_thumbs')
imnbr=len(imlist)

with open('a_pca_modes.pkl','rb') as f:
    immean=pickle.load(f,encoding='iso-8859-1')  # 从文件中加载图像的均值向量
    V=pickle.load(f,encoding='iso-8859-1')  # 从文件中加载PCA变换的特征向量(主成分)

    
# 将每个图像打开并展平为一维向量,然后将这些向量堆叠成一个矩阵。每行代表一张图像的特征向量
immatrix=np.array([array(Image.open(im)).flatten()
                  for im in imlist],'f')


# 将图像均值向量展平为一维向量
immean=immean.flatten()
# 将每张图像从原始空间投影到PCA空间。V[:40](取前40行)表示使用前40个主成分来进行投影
# dot(V[:40],immatrix[i]-immean).shape  :  (40,)
# imnbr:66
projected=np.array([dot(V[:40],immatrix[i]-immean) for i in range(imnbr)])
# projected.shape:(66, 40)


# 白化处理是一种数据预处理技术,旨在将数据的特征转换为具有相同方差的标准正态分布(均值为0,方差为1)
projected=whiten(projected)  # 对投影后的数据进行白化处理,使得每个特征的方差为1
centroids,distortion=kmeans(projected,4)

code,distance=vq(projected,centroids)
for k in range(4):
    ind=where(code==k)[0]
    figure()
    gray()
    for i in range(minimum(len(ind),40)): # 对簇中的前 40 张图像进行迭代(如果簇中的图像少于 40 张,则显示所有图像)
        subplot(4,10,i+1)
        imshow(immatrix[ind[i]].reshape((25,25)))
        axis('off')
show()

类1

在这里插入图片描述

类2

在这里插入图片描述

类3

在这里插入图片描述

类4

在这里插入图片描述

6.1.3 在主成分上可视化图像

上述代码中的一句修改如下,将图像投影到两个主成分上,使用 PCA 的前两个主成分(V[[0,2]])将每张图像从原始空间投影到降维后的空间。每个图像的投影是减去均值后的特征向量与主成分的点积。

projected=np.array([dot(V[:2],immatrix[i]-immean) for i in range(imnbr)])

类1
在这里插入图片描述

类2

在这里插入图片描述

类3

在这里插入图片描述

类4

在这里插入图片描述

from PIL import Image,ImageDraw
h,w=1200,1200  # 定义背景图像的高度和宽度为 1200 像素

img=Image.new('RGB',(w,h),(255,255,255))  # 创建一个新的 RGB 图像,背景颜色为白色
draw=ImageDraw.Draw(img)  #创建一个可以在图像上绘制的 ImageDraw 对象

draw.line((0,h/2,w,h/2),fill=(255,0,0))  # 在图像的中间绘制一条水平红线
draw.line((w/2,0,w/2,h),fill=(255,0,0))  # 在图像的中间绘制一条垂直红线

scale=abs(projected).max(0)  # 计算 PCA 投影数据的绝对值的最大值,以用于数据的缩放
# 将 PCA 投影数据按比例缩放,并将其平移到图像的中心。floor 函数将坐标值向下取整,以便图像能正确地绘制在整数像素上
scaled=floor(array([(p/scale)*(w/2-20,h/2-20)+(w/2,h/2) for p in projected]))

for i in range(imnbr):
    nodeim=Image.open(imlist[i])
    nodeim.thumbnail((25,25))
    ns=nodeim.size
    
#    将图像粘贴到背景图的指定位置。位置根据图像的缩放和 PCA 投影位置计算得出
    img.paste(nodeim,(int(scaled[i][0]-ns[0]//2),int(scaled[i][1]-ns[1]//2),
                      int(scaled[i][0]+ns[0]//2+1),int(scaled[i][1]+ns[1]//2+1)))

imshow(img)
img.save('pca_font.jpg')

在这里插入图片描述

6.1.4 像素聚类

from scipy.cluster.vq import *
steps=50
im=array(Image.open('sun.jpg'))
figure()
subplot(121)
imshow(im)
dx=im.shape[0]/steps
dy=im.shape[1]/steps

features=[]
for x in range(steps):
    for y in range(steps):
        R=mean(im[int(x*dx):int((x+1)*dx),int(y*dy):int((y+1)*dy),0])
        G=mean(im[int(x*dx):int((x+1)*dx),int(y*dy):int((y+1)*dy),1])
        B=mean(im[int(x*dx):int((x+1)*dx),int(y*dy):int((y+1)*dy),2])
        features.append([R,G,B])
features=array(features,'f')

centroids,variance=kmeans(features,3)
code,distance=vq(features,centroids)

codeim=code.reshape(steps,steps)
codeim=np.array(Image.fromarray(codeim).resize(im.shape[:2]))

subplot(122)
imshow(codeim)
show()

在这里插入图片描述

6.2 层次聚类

from itertools import combinations
import numpy as np
class ClusterNode(object):
    def __init__(self,vec,left,right,distance=0.0,count=1):
        self.left=left
        self.right=right
        self.vec=vec
        self.distance=distance
        self.count=count
    def extract_clusters(self,dist):
        """ 从层次聚类树中提取距离小于 dist 的子树簇群列表 """
        if self.distance < dist: 
            return [self]
        return self.left.extract_clusters(dist) + self.right.extract_clusters(dist)
    def get_cluster_elements(self):
        """ 在聚类子树中返回元素的 id """
        return self.left.get_cluster_elements() + self.right.get_cluster_elements()
    def get_height(self):
        """ 返回节点的高度,高度是各分支的和 """
        return self.left.get_height() + self.right.get_height()
    def get_depth(self):
        """ 返回节点的深度,深度是每个子节点取最大再加上它的自身距离 """
        return max(self.left.get_depth(), self.right.get_depth()) + self.distance
class ClusterLeafNode(object):
    def __init__(self,vec,id):
        self.vec = vec
        self.id = id
    def extract_clusters(self,dist):
        return [self]
    def get_cluster_elements(self):
        return [self.id]
    def get_height(self):
        return 1
    def get_depth(self):
        return 0
    def L2dist(v1,v2):
        return np.sqrt(sum((v1-v2)**2))
    def L1dist(v1,v2):
        return sum(abs(v1-v2))
    def hcluster(features,distfcn=L2dist):
        """ 用层次聚类对行特征进行聚类 """
        # 用于保存计算出的距离
        distances = {}
        # 每行初始化为一个簇
        node = [ClusterLeafNode(np.array(f),id=i) for i,f in enumerate(features)]
        while len(node)>1:
            closest = float('Inf')
            # 遍历每对,寻找最小距离
            for ni,nj in combinations(node,2):
                if (ni,nj) not in distances:
                    distances[ni,nj] = distfcn(ni.vec,nj.vec)
                d = distances[ni,nj]
                if d<closest:
                    closest = d
                    lowestpair = (ni,nj)
            ni,nj = lowestpair
            # 对两个簇求平均
            new_vec = (ni.vec + nj.vec) / 2.0
            # 创建新的节点
            new_node = ClusterNode(new_vec,left=ni,right=nj,distance=closest)
            node.remove(ni)
            node.remove(nj)
            node.append(new_node)
        return node[0]
class1=1.5*np.random.randn(100,2)
class2=np.random.randn(100,2)+np.array([5,5])
features=np.vstack((class1,class2))
tree=ClusterLeafNode.hcluster(features)
clusters=tree.extract_clusters(5)
print('number of clusters',len(clusters))
for c in clusters:
    print(c.get_cluster_elements())

在这里插入图片描述

import os
from PIL import Image,ImageDraw
from pylab import *
path=r'D:\pyFile\Python计算机视觉编程\data\sunsets\flickr-sunsets-small'
imlist=[os.path.join(path,f) for f in os.listdir(path) if f.endswith('.jpg')]

features=np.zeros([len(imlist),512])
for i,f in enumerate(imlist):
    im=np.array(Image.open(f))
    
    h,edges=np.histogramdd(im.reshape(-1,3),8,density=True,range=[(0,255),(0,255),(0,255)])
    features[i]=h.flatten()
tree=ClusterLeafNode.hcluster(features)
def draw_dendrogram(node,imlist,filename='clusters.jpg'):
    rows=node.get_height()*20
    cols=1200
    s=float(cols-150)/node.get_depth()
    
    im=Image.new('RGB',(cols,rows),(255,255,255))
    draw =ImageDraw.Draw(im)
    
    draw.line((0,rows/2,20,rows/2),fill=(0,0,0))
    
    node.draw(draw,20,(rows/2),s,imlist,im)
    im.save(filename)
    im.show()

添加draw函数到ClusterNode类中

    def draw(self,draw,x,y,s,imlist,im):
        h1=int(self.left.get_height()*20/2)
        h2=int(self.right.get_height()*20/2)
        top=y-(h1+h2)
        bottom=y+(h1+h2)

        draw.line((x,top+h1,x,bottom-h2),fill=(0,0,0))

        l1=self.distance*s
        draw.line((x,top+h1,x+l1,top+h1),fill=(0,0,0))
        draw.line((x,bottom-h2,x+l1,bottom-h2),fill=(0,0,0))

        self.left.draw(draw,x+l1,top+h1,s,imlist,im)
        self.right.draw(draw,x+l1,bottom-h2,s,imlist,im)

添加draw函数到ClusterLeafNode中

    def draw(self,draw,x,y,s,imlist,im):
        nodeim=Image.open(imlist[self.id])
        nodeim.thumbnail([20,20])
        ns=nodeim.size
        im.paste(nodeim,[int(x),int(y-ns[1]//2),int(x+ns[0]),int(y+ns[1]-ns[1]//2)])
draw_dendrogram(tree,imlist,filename='sunset.pdf')

在这里插入图片描述

clusters=tree.extract_clusters(0.23*tree.distance)

for c in clusters:
    elements=c.get_cluster_elements()
    nbr_elements=len(elements)
    if nbr_elements>3:
        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()

在这里插入图片描述

在这里插入图片描述

6.3 谱聚类

from scipy.cluster.vq import *
import pickle
import os
from PIL import Image
from pylab import *
imlist=get_imlist(r'D:\pyFile\Python计算机视觉编程\data\selectedfontimages\a_selected_thumbs')
imnbr=len(imlist)

with open('a_pca_modes.pkl','rb') as f:
    immean=pickle.load(f,encoding='iso-8859-1')
    V=pickle.load(f,encoding='iso-8859-1')

immatrix=np.array([array(Image.open(im)).flatten()
                  for im in imlist],'f')

immean=immean.flatten()
projected=np.array([dot(V[:40],immatrix[i]-immean) for i in range(imnbr)])

projected=whiten(projected)
centroids,distortion=kmeans(projected,4)

code,distance=vq(projected,centroids)
n=len(projected)
S=array([[np.sqrt(sum((projected[i]-projected[j])**2))
         for i in range(n)] for j in range(n)],'f')
rowsum=sum(S,axis=0)
D=diag(1/sqrt(rowsum))
I=identity(n)
L=I-dot(D,dot(S,D))

U,sigma,V=linalg.svd(L)

k=5

features=array(V[:k]).T
features=whiten(features)
centroids,distortion=kmeans(features,k)
code,distance=vq(features,centroids)

for c in range(k):
    ind=where(code==c)[0]
    figure()
    for i in range(minimum(len(ind),39)):
#         print(imlist[ind[i]])
        im=Image.open(imlist[ind[i]])
        subplot(4,10,i+1)
        imshow(array(im))
        axis('equal')
        axis('off')
show()

类1

在这里插入图片描述

类2

在这里插入图片描述

类3

在这里插入图片描述

类4

在这里插入图片描述

类5

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值