淘先锋技术网

首页 1 2 3 4 5 6 7

这学期正好上了数据挖掘这门课,本周的作业是实现 K-means的两个实例,分别是实现对waveform.data文件数据的聚类分析,还有一个就是对图像的 K-means 聚类分割。下面我分别对两个例子进行说明。

首先先来介绍一下 K-means实现原理,这里有一篇写的非常通俗易懂的文章,原文链接如下;

http://www.aboutyun.com/thread-18178-1-1.html

废话我就不多说了,简而言之 K-means 算法实现的就是通过定义聚类数目,随机选取聚类点,通过计算其余所有点到聚类点的距离,判断最近距离,将其归入聚类点,之后计算各聚类平均値,将其定义为新的聚类点,不断迭代,直到不再产生新的聚类点。

优点:算法简单,当结果簇是密集的且区别明显,效果较好。

缺点:只有在簇的平均值(mean)被定义的情况下才能使用,可能不适用于某些应用, 例如涉及有分类属性的数据

需要预先指顶簇的数目k,不能处理噪音数据和孤立点(outliers)。不适合用来发现具有非凸形状(non-convex shapes)的簇。

下面上代码,先是对waveform.data数据的聚类代码,这里我划分了三个聚类,具体的各位可以按需设置。

import random 
import pandas as pd
import numpy as np
import tool

#设置精度小数点后两位
np.set_printoptions(precision=2)

#读取文件
df=pd.read_csv("waveform.data",header = None)

#随机选择三个中心点作为初始质心
i1 = random.randint(0,21)     #列标0-21
j1 = random.randint(0,4998)   #行标0-4999
cent1 = df[i1][j1]

i2 = random.randint(0,21)     
j2 = random.randint(0,4998)   
cent2 = df[i2][j2]

i3 = random.randint(0,21)     #列标0-21
j3 = random.randint(0,4998)   #行标0-4999
cent3 = df[i3][j3]

#聚类存储列表
list1 = []
list2 = []
list3 = []

result = 1

while result:
	list1.clear
	list2.clear
	list3.clear
	result = tool.circle (df,cent1,cent2,cent3,list1,list2,list3,result)
	

print("聚类1:前100个数:",list1[:100],'\n')
print("聚类2:前100个数:",list2[:100],'\n')
print("聚类2:前100个数:",list3[:100],'\n')

waveform.data数据格式比较简单,可以看成是一个一维数组,求距离使用平方欧式距离,但这里只需要求距离差的绝对值即可。

在这里吐槽一下,之前由于导师比较偏爱C语言,项目编程全是用C,突然用python有点不适应,由于才开始学习编写python,在使用pandas时想要实现遍历,本以为有专门接口,或者本人暂时没找到,最后还是只能用for循环,如果哪位大神知道的可以顺便留言告诉我一下。

这里本人封装了一个circle借口,用来循环判断各点的聚类距离并分类,最后判断是否会生成新的聚类点,返回result。当result为0则停止循环。具体接口代码和waveform数据文件各位按需下载。

 

接着是对图像分割的代码实现。

这里本人分割的图像为一个较为简单的图像,原图和分割图像如下:

原图:

本人将其划分成四个聚类,分别是天空,森林,地面,狗狗,分割图如下:

        

 

可以看出图像分割结果并不理想,在地面与天空的分割过程中有较多混杂像素点,狗狗和森林也有混杂的像素点,而且这个分割结果是本人运行了大概六次之后得出的结果。

先上主代码:

import random 
import pandas as pd
import numpy as np
import tool

from PIL import Image

#图像大小为98*69
#print(im.size,im.format,im.mode) 显示图片信息

img = Image.open("dog.jpg","r")
img_array=img.load()

#随机选择四个中心点作为初始质心
i1 = random.randint(0,97)     
j1 = random.randint(0,68)   
cent1 = img_array[i1,j1]

i2 = random.randint(0,97)     
j2 = random.randint(0,68)   
cent2 = img_array[i2,j2]

i3 = random.randint(0,97)     
j3 = random.randint(0,68)   
cent3 = img_array[i3,j3]

i4 = random.randint(0,97)     
j4 = random.randint(0,68)   
cent4 = img_array[i4,j4]

print(i1,j1,"\n",i2,j2,"\n",i3,j3,"\n",i4,j4)

#聚类存储列表
list1 = []
list2 = []
list3 = []
list4 = []

result = 1

while result:
	list1.clear
	list2.clear
	list3.clear
	list4.clear
	result = tool.pic(img,img_array,cent1,cent2,cent3,cent4,list1,list2,list3,list4,result)
	
imgnew1 = Image.new("RGB",(img.size[0],img.size[1]))
imgnew2 = Image.new("RGB",(img.size[0],img.size[1]))
imgnew3 = Image.new("RGB",(img.size[0],img.size[1]))
imgnew4 = Image.new("RGB",(img.size[0],img.size[1]))

#三个参数依次为R,G,B,A   R:红 G:绿 B:蓝 A:透明度
#白色(225,255,255) 黑色(0,0,0)
pixTuple = (255,255,255)
for i in range(img.size[0]):
	for j in range(img.size[1]):
		if img_array[i,j] in list1:
			imgnew1.putpixel((i,j),img_array[i,j])
		else:
			imgnew1.putpixel((i,j),pixTuple)

		if img_array[i,j] in list2:
			imgnew2.putpixel((i,j),img_array[i,j])
		else:
			imgnew2.putpixel((i,j),pixTuple)

		if img_array[i,j] in list3:
			imgnew3.putpixel((i,j),img_array[i,j])
		else:
			imgnew3.putpixel((i,j),pixTuple)

		if img_array[i,j] in list4:
			imgnew4.putpixel((i,j),img_array[i,j])
		else:
			imgnew4.putpixel((i,j),pixTuple)

imgnew1.save('list1.jpg')
imgnew2.save('list2.jpg')
imgnew3.save('list3.jpg')
imgnew4.save('list4.jpg')

print("打印图片\n")




分析一下聚类效果较差的原因。首先聚类点为通过随机数生成选取的,因此本人猜测在随机数生成过程中,选取的不同位置的像素位置对聚类结果或许有一定影响。其次,由于图像的画质较低,因此存在个别像素点的RGB数值有较大偏差,使聚类点产生了较大的偏差。改进方法可以采用K-medoids方法。

编程思想:图像可以理解为具有三个值的像素点组成的矩阵或者多维列表。那么同理还是利用平方欧式距离,计算每个像素点(R,G,B)距离聚类点的距离,并根据距离远近进行聚类,并迭代直到未产生新的聚类点。

在这里本人遇到了一个小的算法问题,就是聚类之后如何计算各类的新聚类点。我的思路是利用局针转置,因为我需要计算的是各个像素点的R,G,B分别的平均値,[[R1,G1,B1],[R2,G2,B2],[R3,G3,B3],[R4,G4,B4]........[Rn,Gn,Bn]],可以看到如果我将这个多维列表进行一次转置,那么就变成了[[R1,R2,R3,R4,.......Rn],[G1,G2,G3,G4,.......Gn],[B1,B2,B3,B4,.......Bn]],那么这样就讲问题简单化了,转制后直接利用numpy提供的接口求每一行的平均値,就是分别对应新的聚类点各RGB三个点的值。

最终通过判断,按照需要对其进行图形分割即可。

代码下载地址:https://download.csdn.net/download/u011403848/10730195