Python计算机视觉——第六章 图像聚类

引言

本章将介绍聚类方法,用图像进行聚类,寻找相似的图像组。聚类可用于识别、划分图像数据集,组织与导航。此外,会对聚类后的图像进行相似性的可视化。

6.1 K-means聚类

K-means是一种将输入数据划分为K个簇的简单聚类算法,反复提炼初始评估的类中心,步骤如下:

  • 随机方式初始化类中心 u i , i ∈ [ 1 , k ] u_i,i\in [1,k] ui,i[1,k];
  • 将每个数据点归并到离它距离最近的类中心所属类 c i c_i ci;
  • 对所有属于该类的数据点求平均,将平均值作为心的 u i u_i ui;
  • 重复步骤2、3,直到收敛;

K-means聚类算法的意义在于试图使类内总方差最小:

V = ∑ i = 1 k ∑ x j ∈ c i ( x i − μ i ) 2 V = \sum _{i=1} ^{k} \sum_{x_j \in c_i} (x_i-\mu_i)^2 V=i=1kxjci(xiμi)2

注:

  • 为了避免初始化类中心没选取好初值所造成的影响,该算法通常会初始化不同的类中心进行多次运算,然后选择方差最小的结果。
  • K-means算法最大的缺陷使必须预先设定聚类数K,选择不恰当会导致聚类结果很差。

6.1.1 SciPy聚类包

SciPy包中有直接实现K-means的方法,下面的代码时实现过程:

生成二维正态分布数据(两类),用k=2进行聚类

from numpy import *
from scipy.cluster.vq import *
class1 = 1.5*random.randn(100,2)
class2 = random.randn(100,2)+array([5,5])
features = vstack((class1,class2))

centroids,variance = kmeans(features,2)

但返回的方差并不是我们真正需要的,用SciPy包中的矢量量化函数对每个数据点进行归类。

code是分类号,K=2,所以被分为1和0,distance是点离类中心的距离。

code,distance = vq(features,centroids)

将其可视化,我们画出数据点和最终的聚类中心。

from pylab import *
figure(figsize=(10,10))
ndx = where(code==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')
axis('off')
show()

在这里插入图片描述

6.1.2 图像聚类

用K-means对字体图像进行聚类,获取66幅字体数据集,并且利用之前计算过的pca主成分进行投影,用投影系数作为每幅图像的向量描述符,用下面的方法进行聚类。

import imtools
import pickle
from scipy.cluster.vq import *
from PIL import Image
from numpy import *
from pylab import *
# 获取 selected-fontimages 文件下图像文件名,并保存在列表中
imlist = imtools.get_imlist('selectedfontimages')
imnbr = len(imlist)

# 载入模型文件
with open('font_pca_modes.pkl','rb') as f:
    immean = pickle.load(f)
    V = pickle.load(f)
    
# 创建矩阵,存储所有拉成一组形式后的图像
immatrix = array([array(Image.open(im)).flatten()
 for im in imlist],'f')
    
immean=immean.flatten()


# 投影系数
projected = array([dot(V[:40],immatrix[i]-immean) for i in range(imnbr)])

# 进行 k-means 聚类,作归一化处理
projected = whiten(projected)
centroids,distortion = kmeans(projected,4)

# 绘制聚类簇
for k in range(4):
    ind = where(code==k)[0]
    figure()
    gray()
    for i in range(minimum(len(ind),40)):
        subplot(4,10,i+1)
        imshow(immatrix[ind[i]].reshape((25,25)))
        axis('off')
show()

第一行是第一类,第二行是第二类,第三行是第三类,之后是第四类。
在这里插入图片描述

6.1.3 在主成分上可视化图像

为了观察上面是如何利用主成分进行聚类的,在一对主成分方向的坐标上可视化这些图像,将图像投影到两个主成分上。

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

V[0,2]分别是第一个和第三个主成分。

使用PIL中的ImageDraw模块进行可视化

from PIL import Image, ImageDraw
# 高和宽
h,w = 1200,1200
# 创建一幅白色背景图
img = Image.new('RGB',(w,h),(255,255,255))
draw = ImageDraw.Draw(img)
# 绘制坐标轴
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)
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
    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)))

在这里插入图片描述
成对主成分上投影的字体图像,第一个和第三个主成分,二维投影后相似的字体图像距离较近。

6.1.4 像素聚类

来看一个对单幅图像中的像素而非全部图像进行聚类的例子。将图像区域或像素合并成有意义的部分称为图像分割。现在,我们仅会在 RGB 三通道的像素值上运用 K-means 进行聚类。

用一个步长为 steps 的方形网格在图像中滑动,每滑一次对网格中图像区域像素求平均值,将其作为新生成的低分辨率图像对应位置
处的像素值,并用 K-means 进行聚类:

from scipy.cluster.vq import *

from pylab import *
from PIL import Image
steps = 50 # 图像被划分成 steps×steps 的区域
im = array(Image.open('pic/empire.jpg'))

dx = im.shape[0] // steps
dy = im.shape[1] // steps
# 计算每个区域的颜色特征
features = []
for x in range(steps):
    for y in range(steps):
        R = mean(im[x*dx:(x+1)*dx,y*dy:(y+1)*dy,0])
        G = mean(im[x*dx:(x+1)*dx,y*dy:(y+1)*dy,1])
        B = mean(im[x*dx:(x+1)*dx,y*dy:(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)

figure()
subplot(121)
imshow(im)
axis('off')


subplot(122)
imshow(codeim)
axis('off')
show()

50 × 50 , K = 3 50\times50, K=3 50×50,K=3
在这里插入图片描述
100 × 100 , K = 3 100\times100,K=3 100×100,K=3
在这里插入图片描述

6.2 谱聚类

谱聚类和K-means方法截然不同,对于n个元素(n幅图像),相似矩阵是一个 n × n n\times n n×n的矩阵,矩阵的两个元素表示两两之间的相似性分数,谱聚类是由于相似性矩阵的建谱矩阵而得名,对该谱矩阵进行特征分解得到的特征向量可以用于降维,然后聚类。

谱聚类的优点之一是仅需要输入相似性矩阵,采用任意度量方法构建该相似性矩阵,特征或描述子没有类别限制,只要有一个相似性或距离的概念即可。

给定 n × n n\times n n×n的相似矩阵S, s i j s_{ij} sij为相似性分数(矩阵的元素),可以创建矩阵,称为拉普拉斯矩阵:
L = I − D − 0.5 S D − 0.5 L=I-D^{-0.5}SD^{-0.5} L=ID0.5SD0.5

I是单位矩阵,D是对角矩阵,对角元素是S对于应行元素之和, d i = ∑ j s i j d_i=\sum _{j} s_{ij} di=jsij,我们要求相似性分数较小且大等于0。

计算L(拉普拉斯矩阵)的特征向量,并使用K个最大特征值对于的k个特征向量,构建出特征向量集,各列是由之前的k个特征向量构成,每一行可看作一个新的特征向量,长度为k(尺度k*k),而新的特征向量可以用K-means方法进行聚类,形成最终的聚类簇。

import imtools
import pickle
from scipy.cluster.vq import *
from PIL import Image
from numpy import *
from pylab import *
import math
from scipy import linalg
# 获取 selected-fontimages 文件下图像文件名,并保存在列表中
imlist = imtools.get_imlist('selectedfontimages')
imnbr = len(imlist)

# 载入模型文件
with open('font_pca_modes.pkl','rb') as f:
    immean = pickle.load(f)
    V = pickle.load(f)
    
# 创建矩阵,存储所有拉成一组形式后的图像
immatrix = array([array(Image.open(im)).flatten()
 for im in imlist],'f')
    
immean=immean.flatten()

# 投影到前40个主成分上
immean = immean.flatten()
projected = array([dot(V[:40], immatrix[i] - immean) for i in range(imnbr)])

n = len(projected)
# 欧式距离
S = array([[ sqrt(sum((projected[i]-projected[j])**2))
 for i in range(n) ] for j in range(n)], 'f')

#拉普拉斯矩阵
# 应行元素之和
rowsum = sum(S,axis = 1)
D = diag(1/sqrt(rowsum))
I = identity(n)
L = I-dot(D,dot(S,D))

# 计算特征向量
U,sigma,V = linalg.svd(L)

k = 5 # 前5个特征向量

features = array(V[:5].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)):
        im = Image.open(imlist[ind[i]])
        subplot(4,10,i+1)
        imshow(array(im))
        axis('equal')
        axis('off')
show()

在这里插入图片描述

  • 4
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值