使用K均值来减少一张图片的颜色种类,既能压缩图片,也能保留图片的特征!
主要步骤:
1、选取聚类中心的数量(即压缩后图片的颜色种类)
聚类中心是一个1×3的矩阵,3指的是通道数(通常为RGB三通道)。
2、计算每个像素点到聚类中心的距离(像素点之间的差距)
每张图片的每个像素都有一个像素值,三通道需要分开计算,然后求平均。这里使用的是欧氏距离:
通道1:d1 = ( pic[i][0] - centre[j][0] )^2
通道2:d2 = ( pic[i][1] - centre[j][1] )^2
通道3:d3 = ( pic[i][2] - centre[j][2] )^2
Distance = ( d1 + d2 + d3 ) / 3
3、将像素点归入最近的聚类中心
假设 Distance[i] 为像素点 i 到每个聚类中心的距离的集合,则类别 x = argmin( Distance[i] )。
4、得到新的聚类中心
把归入每个聚类中心的像素点求平均,平均值即为新的聚类中心,同样3通道要分开!
5、重复上述步骤直到聚类中心不变
具体代码(Python3):
import numpy as np
from matplotlib import pyplot as plt
from matplotlib import image as img
import random
pic = img.imread('Kaer.png')
#Original Picture
plt.figure()
plt.imshow(pic)
plt.axis('off')
plt.title('Original Picture')
k = 16 #The number of cluster centroids
data = pic.reshape(pic.shape[0]**2,3) #3 means RGB
'''Create empty list to store the points'''
list = []
for i in range(k):
list.append([])
'''Compute min distance'''
def Min_distance(centre,rgb):
min_distance = 1.0e+10
for i in range(len(centre)):
distance = ((centre[i][0]-rgb[0])**2+(centre[i][1]-rgb[1])**2+(centre[i][2]-rgb[2])**2)/3
if distance < min_distance:
min_distance = distance
c_i = i
return c_i
'''Random Initialization'''
centroid = []
rnd = random.sample(range(0,data.shape[0]+1),k)
for i in rnd:
centroid.append(data[i,:])
'''K-means Algorithm'''
while True:
centroid_1 = centroid
for i in range(k):
list[i].clear()
#cluster points to the centroids
for i in range(data.shape[0]):
c_i = Min_distance(centroid,data[i,:]) #c_i represents the index in centroid
list[c_i].append(i)
centroid.clear()
#find new centroids
for i in range(k):
sum = np.array([0.0, 0.0, 0.0])
for j in list[i]:
sum += data[j,:]
mean = sum/len(list[i])
centroid.append(mean)
if centroid == centroid_1: #cluster centroid doesn't change
break
'''Draw the picture'''
#replace the pixels
for i in range(k):
for j in list[i]:
data[j,:] = centroid[i]
#Processed Picture
new_pic = data.reshape(int(data.shape[0]**0.5),int(data.shape[0]**0.5),3)
plt.figure()
plt.imshow(new_pic)
plt.axis('off')
plt.title('Processed Picture')
plt.show()
最后的效果是:
上面是用16种颜色代替原图的效果!
然后两张图的大小分别是: