引言
图像分割正如字面意思,就是把一张图分成几个部分,比如下面一张蔬果照片,有香蕉、辣椒、西兰花等,根据颜色、位置将它们分开,这样后续就可以有更多操作比如得到图片里辣椒的数量。这有一些类似数字图像处理中的图像阈值处理,但后者往往只分成黑白两部分,比如一张手写作业的照片,由于光照不均、笔迹太浅等原因看不清楚,于是把灰度低于某一阈值的像素全设为黑,否则设为白,其中这一阈值通常需要人为根据灰度直方图确定。而这里所说的图像分割不需要输入阈值,并且要分割成多个部分。
K-means聚类
图像分割实际上可以看成一个聚类问题。
先小结下聚类,比如我们有一系列数据记为,,,...,,,以二维为例,类似下图,显然如果离得近的点算一类里面的数据点大致可以分成两类,而聚类就是要完成这一分类,由于在聚类之前并不需要人为给定一些已知分类的样本点,因此称作无监督学习机器,以区别于感知器、支持向量机等有监督机器学习。
K-means 是一种简单有效的聚类方法,首先给定分类数,在这个例子里就是两类,之后选择两个分类中心,计算所有数据点到分类中心的距离,并分到离得近的那个。我们先随意选定了一组中心,如下图,三角形和五角星是两个中心:
显然这时候分的效果很差,这时因为中心是随意选的,于是分别将所得两类数据点的中心点作为新的分类中心,再分类,如下图:
再迭代一次,两类点就能很好的区分开:
代码如下:
#k-means 聚类测试
import numpy as np
import matplotlib.pyplot as plt
Ns=50
fig,ax=plt.subplots(figsize=(5,5))
#数据,两类,都是高斯分布的随机数
class1=np.random.normal(0,1,[Ns,2])
class2=np.random.normal(0,1,[Ns,2])
class2[:,0]+=3
class2[:,1]+=4
Data=np.r_[class1,class2]
plt.figure(1)
ax.scatter(Data[:,0],Data[:,1])
ax.axis([-4,9,-4,9])
plt.title('Data')
#选定两个初始中心
cen1=[-2,2]
cen2=[6,-3]
#样本点离中心的距离平方
def d2(x1,y1,x2,y2):
return (x1-x2)**2+(y1-y2)**2
C=np.zeros(2*Ns)
for i in range(5):
#遍历所有点,离中心1近则分到1类,否则分到2类
for j in range(2*Ns):
data=Data[j]
if d2(data[0],data[1],cen1[0],cen1[1])<d2(data[0],data[1],cen2[0],cen2[1]):
C[j]=1
else:
C[j]=2
C1=Data[C==1]
C2=Data[C==2]
#画图
plt.figure(i+1)
fig,ax=plt.subplots(figsize=(5,5))
ax.scatter(C1[:,0],C1[:,1],c='red',label='class 1')
ax.scatter(C2[:,0],C2[:,1],c='blue',label='class 2')
ax.axis([-4,9,-4,9])
ax.legend()
plt.title("step"+str(i))
ax.plot(cen1[0],cen1[1],marker='*',markersize=15,c='black')
ax.plot(cen2[0],cen2[1],marker='^',markersize=15,c='black')
#通过对分出来的两类分别做平均,得到新的中心点
cen1=[np.mean(C1[:,0]),np.mean(C1[:,1])]
cen2=[np.mean(C2[:,0]),np.mean(C2[:,1])]
plt.show()
Mean-shift
K-means聚类有一个明显限制,它要求同一类数据点在参数空间地分布是“球形”的,否则无法成功分类,因此当数据更复杂时,需要推广,其中Mean-shift是一种有效的方法。
首先,在d维空间中有样本点,定义密度f为:
,
其中H是d维对称正定阵(也因此必可逆),称为带宽矩阵,核函数K满足归一等诸多条件,而密度的梯度也可以通过这一表达式计算出。这时使所有样本点沿着密度梯度方向移动(具体步长和表达式参考论文),则该论文证明,若K 为凸函数且单调递减,最终这些样本点将收敛。而实际操作时,也就是使原本分散的样本点都集中到各个中心。因此Mean-shift 并不是直接完成聚类这一任务,对图像来说实际上更接近于滤波。