K-means聚类算法实现图片的压缩,Python实现

ImageCompress

K-means聚类算法实现图片的压缩,Python实现

参数

  • K 全图用多少种颜色表示,默认值 K=16,设置K后将会对所有图像进行聚类,并只使用K种颜色表示全图
  • iter 迭代计算次数, 默认值 iter=3

未实现

  • 未实现压缩后的存储(降低图像文件大小),只能存为一般jpg格式预览压缩后效果

效果

默认参数下的效果

K-means聚类算法实现图片的压缩,Python实现

K-means聚类算法实现图片的压缩,Python实现

K-means聚类算法实现图片的压缩,Python实现

K-means聚类算法实现图片的压缩,Python实现

核心代码:

import numpy as np

from PIL import Image

import random

class KMCompress():

def __init__(self):

self.centroids = None #簇中心 (K, c)

self.data = None #压缩后的数据 (h*w, c)

self.cen_idx = None #压缩后的数据对应簇中心的下标(h*w, 1)

self.shape = None #原图的shape

def compress(self, path, K=16, iter=3):

'''

压缩图像

:param path: 图像路径

:param K: K簇,压缩后图像使用K个像素点表示全图

:param iter: 迭代计算次数

:return: None

'''

X, self.shape = self.__image_to_matrix(path)

centroids = self.__init_random_centroids(X, K)

for i in range(iter):

print(' ====> ' + str((i+1.0)*100 / (iter + 1)) + '%')

cen_idx = self.__find_closest_centroids(X, centroids)

centroids = self.__move_centroids(X, cen_idx, K)

print(' ====> 99%')

cen_idx = self.__find_closest_centroids(X, centroids)

self.cen_idx = cen_idx

self.centroids = centroids

self.__fill_data()

def __fill_data(self):

m = self.cen_idx.shape[0]

channel = self.shape[2]

self.data = np.zeros((m, channel))

for i in range(m):

self.data[i] = self.centroids[int(self.cen_idx[i])]

def __image_to_matrix(self, path):

'''

加载图片

:param path:

:return: 图像矩阵 (h * w, c)

'''

img = Image.open(path)

w, h = img.size

if img.mode == 'RGB':

shape = (h, w, 3)

elif img.mode == 'L':

shape = (h, w, 1)

return np.asmatrix(img.getdata(), dtype='float'), shape

def __init_random_centroids(self, X, K):

'''

初始化簇中心

:param X: 图像矩阵 (h*w, c)

:param K:

:return: 簇中心(K, c)

'''

m, n = X.shape

sh = list(range(m))

random.shuffle(sh)

centroids = []

for i in range(K):

centroids.append(X[sh[i]])

return (np.asarray(centroids))[:,0,:] #list 转换成Matrix

def __find_closest_centroids(self, img, centroids):

'''

计算图像每个像素点对应的簇中心下标

:param img:

:param centroids: (h*w, 1)

:return: 图像每个像素点对应的簇中心下标

'''

m, n = img.shape

K, _ = centroids.shape

cen_idx = np.zeros([m, 1], dtype='int')

for i in range(m):

M = np.full([K, n], img[i])

err = M - centroids

distance = np.multiply(err, err).sum(axis=1)

cen_idx[i] = distance.argmin() #最小列索引

return cen_idx

def __move_centroids(self, img, cen_idx, K):

'''

更新簇中心

:param img:

:param cen_idx: 图像每个像素点对应的簇中心下标

:param K: K簇

:return: 簇中心矩阵 (K, c)

'''

m, n = img.shape

times = np.zeros([K, 1], dtype='int')

centroids = np.zeros([K, n], dtype='float')

for i in range(m):

idx = int(cen_idx[i])

centroids[idx] = centroids[idx] + img[i]

times[idx] = times[idx] + 1

while times.min() == 0: #避免除零异常

times[times.argmin()] = 1

centroids = centroids / times

return centroids

def get_img(self):

'''

返回图像文件

:param M:

:param shape:

:return:

'''

img = np.reshape(np.asarray(self.data), self.shape)

return Image.fromarray(img.astype(np.uint8))

def save(self, path):

'''

#TODO 字典+二进制压缩存储图片

:param path:

:return:

'''

pass

def load(self, path):

'''

TODO 读取压缩后的图片

:param path:

:return:

'''

pass

a = KMCompress()

a.compress('./b.jpg', iter=10)

a.get_img().save('./compress_b.jpg')

print('done...')


代码地址:https://github.com/SherlockUnknowEn/ImageCompress.git


分享到:


相關文章: