本样例比较kmeans和svd做图像压缩后的图片效果
原图如下:
代码如下:
#!/usr/bin/python
# -*- coding:utf-8 -*-
import numpy as np
import os
from PIL import Image
import matplotlib.pyplot as plt
import matplotlib as mpl
from pprint import pprint
from sklearn.cluster import KMeans
def restore1(sigma, u, v, K): # 奇异值、左特征向量、右特征向量
m = len(u)
n = len(v[0])
a = np.zeros((m, n))
for k in range(K):
uk = u[:, k].reshape(m, 1)
vk = v[k].reshape(1, n)
a += sigma[k] * np.dot(uk, vk)
a[a < 0] = 0
a[a > 255] = 255
# a = a.clip(0, 255)
return np.rint(a).astype('uint8')
if __name__ == "__main__":
A = Image.open("timg.png", 'r')
a = np.array(A)
pixel = a.reshape((-1, 3))
K = 12
u_r, sigma_r, v_r = np.linalg.svd(a[:, :, 0])
u_g, sigma_g, v_g = np.linalg.svd(a[:, :, 1])
u_b, sigma_b, v_b = np.linalg.svd(a[:, :, 2])
plt.figure(figsize=(11, 9), facecolor='w')
mpl.rcParams['font.sans-serif'] = ['simHei']
mpl.rcParams['axes.unicode_minus'] = False
for k in range(1, K+1):
print(k)
R = restore1(sigma_r, u_r, v_r, k)
G = restore1(sigma_g, u_g, v_g, k)
B = restore1(sigma_b, u_b, v_b, k)
I = np.stack((R, G, B), axis=2)
model = KMeans(n_clusters=k, init='k-means++', n_init=10)
labels = model.fit_predict(pixel) # 每个点颜色分类
colors = model.cluster_centers_ # 聚类中心,颜色值
new_img = colors[labels]
# 然后用聚类中心的颜色代替原来的颜色值。
new_img = new_img.reshape(a.shape)
new_img = new_img.astype(np.uint8)
plt.subplot(3, 8, 2*k-1)
plt.imshow(I)
plt.axis('off')
plt.title('奇异值个数:%d' % k)
plt.subplot(3, 8, 2*k)
plt.imshow(new_img)
plt.axis('off')
plt.title('簇个数:%d' % k)
plt.suptitle('SVD与Kmeans做图像分解', fontsize=20)
plt.tight_layout(0.3, rect=(0, 0, 1, 0.92))
plt.show()
从图片的效果我们可以看出,运用kmeans会直接按照簇的个数使用像素个数,svd则是选择特征值的个数,即使在特征值为1时得到的也是多像素的图片但比较模糊,看不到轮廓。发现其实kmeans的只用少量的像素得到的效果并不差,当簇个数为12时基本与原图效果一直,较奇异值分解得到的图片更清晰一些。