25. 使用 K-Means 完成图像压缩#
25.1. 介绍#
本次挑战将针对一张成都著名景点:锦里的图片,通过 Mini Batch K-Means 的方法将相近的像素点聚合后用同一像素点代替,以达到图像压缩的效果。
25.2. 知识点#
图像压缩
Mini Batch K-Means 聚类
首先,我们下载并导入示例图片,图片名为
challenge-7-chengdu.png
。
wget -nc "https://cdn.aibydoing.com/aibydoing/files/challenge-7-chengdu.png"
--2023-11-13 17:16:21-- https://cdn.aibydoing.com/aibydoing/files/challenge-7-chengdu.png
正在解析主机 cdn.aibydoing.com (cdn.aibydoing.com)... 198.18.7.59
正在连接 cdn.aibydoing.com (cdn.aibydoing.com)|198.18.7.59|:443... 已连接。
已发出 HTTP 请求,正在等待回应... 200 OK
长度:1057505 (1.0M) [image/png]
正在保存至: “challenge-7-chengdu.png”
challenge-7-chengdu 100%[===================>] 1.01M 1.54MB/s 用时 0.7s
2023-11-13 17:16:23 (1.54 MB/s) - 已保存 “challenge-7-chengdu.png” [1057505/1057505])
使用 Matplotlib 可视化示例图片。
import matplotlib.pyplot as plt
import matplotlib.image as mpimg
%matplotlib inline
chengdu = mpimg.imread('challenge-7-chengdu.png') # 将图片加载为 ndarray 数组
plt.imshow(chengdu) # 将数组还原成图像
chengdu.shape
(516, 819, 3)
在使用
mpimg.imread
函数读取图片后,实际上返回的是一个
numpy.array
类型的数组,该数组表示的是一个像素点的矩阵,包含长,宽,高三个要素。如成都锦里这张图片,总共包含了
\(516\)
行,\(819\)
列共
\(516*819=422604\)
个像素点,每一个像素点的高度对应着计算机颜色中的三原色
RGB(红,绿,蓝),共 3 个要素构成。
25.3. 数据预处理#
为方便后期的数据处理,需要对数据进行降维。
挑战:将形状为 \((516, 819, 3)\) 的数据转换为 \((422604, 3)\) 形状的数据。
提示:使用
np.reshape
进行数据格式的变换。
"""数据格式变换
"""
## 代码开始 ### (≈ 1 行代码)
data = None
## 代码结束 ###
参考答案 Exercise 25.1
"""数据格式变换
"""
### 代码开始 ###(≈ 1 行代码)
data = chengdu.reshape(516 * 819, 3)
### 代码结束 ###
运行测试
data.shape, data[10]
期望输出
((422604, 3), array([0.12941177, 0.13333334, 0.14901961], dtype=float32))
25.4. 像素点种类个数计算#
尽管有
422604
个像素点,但其中仍然有许多相同的像素点。在此我们定义:RGB
值相同的点为一个种类,其中任意值不同的点为不同种类。
挑战:计算
422604
个像素点中种类的个数。
提示:可以将数据转化为 list 类型,然后将每一个元素转换为
tuple 类型,最后利用
set()
和
len()
函数进行计算。也可以按照自己的想法完成。
"""计算像素点种类个数
"""
def get_variety(data):
"""
参数:
预处理后像素点集合
返回:
num_variety -- 像素点种类个数
"""
### 代码开始 ### (≈ 3 行代码)
num_variety=None
### 代码结束 ###
return num_variety
参考答案 Exercise 25.2
"""计算像素点种类个数
"""
def get_variety(data):
"""
参数:
预处理后像素点集合
返回:
num_variety -- 像素点种类个数
"""
### 代码开始 ### (≈ 3 行代码)
temp=data.tolist()
num_variety=len(set([tuple(t) for t in temp]))
### 代码结束 ###
return num_variety
运行测试
get_variety(data), data[20]
期望输出
(100109, array([0.24705882, 0.23529412, 0.2627451 ], dtype=float32))
25.5. Mini Batch K-Means 聚类#
像素点种类的数量是决定图片大小的主要因素之一,在此使用 Mini Batch K-Means 的方式将图片的像素点进行聚类,将相似的像素点用同一像素点值来代替,从而降低像素点种类的数量,以达到压缩图片的效果。
挑战:使用 Mini Batch K-Means 聚类方法对像素点进行聚类,并用每一个中心的像素点代替属于该类别的像素点。
规定:聚类簇数量设置为 10 类。
提示:使用
MiniBatchKMeans
中
fit()
和
predict()
函数进行聚类,使用
cluster_centers_()
函数进行替换,本次挑战基本使用默认参数。阅读官方文档
from sklearn.cluster import MiniBatchKMeans
## 代码开始 ### (≈ 4 行代码)
predict=None
## 代码结束 ###
new_colors = model.cluster_centers_[predict]
参考答案 Exercise 25.3
from sklearn.cluster import MiniBatchKMeans
### 代码开始 ###(≈ 4 行代码)
model = MiniBatchKMeans(10)
model.fit(data)
predict=model.predict(data)
### 代码结束 ###
new_colors = model.cluster_centers_[predict]
运行测试
# 调用前面实现计算像素点种类的函数,计算像素点更新后种类的个数
get_variety(new_colors)
期望输出
10
25.6. 图像压缩前后对比#
挑战:将聚类后并替换为类别中心点值的像素点,变换为数据处理前的格式,并绘制出图片进行对比展示。
提示:使用
reshape()
函数进行格式变换,使用
imshow()
函数进行绘图。
fig, ax = plt.subplots(1, 2, figsize=(16, 6))
## 代码开始 ### (≈ 3 行代码)
## 代码结束 ###
参考答案 Exercise 25.4
fig, ax = plt.subplots(1, 2, figsize=(16, 6))
### 代码开始 ### (≈ 3 行代码)
new_chengdu = new_colors.reshape(chengdu.shape)
ax[0].imshow(chengdu)
ax[1].imshow(new_chengdu)
### 代码结束 ###
期望输出
通过图片对比,可以十分容易发现画质被压缩了。其实,因为使用了聚类,压缩后的图片颜色就变为了 10 种。
接下来,使用
mpimg.imsave()
函数将压缩好的文件进行存储,并对比压缩前后图像的体积变化。
# 运行对比
mpimg.imsave("new_chengdu.png", new_chengdu)
!du -h new_chengdu.png
!du -h challenge-7-chengdu.png
可以看到,使用 Mini Batch K-Means 聚类方法对图像压缩之后,体积明显缩小。
○ 欢迎分享本文链接到你的社交账号、博客、论坛等。更多的外链会增加搜索引擎对本站收录的权重,从而让更多人看到这些内容。