【Python】基于动态规划和K聚类的彩色图片压缩算法

引言
当想要压缩一张彩色图像时,彩色图像通常由数百万个颜色值组成,每个颜色值都由红、绿、蓝三个分量组成。因此,如果我们直接对图像的每个像素进行编码,会导致非常大的数据量。为了减少数据量,我们可以尝试减少颜色的数量,从而降低存储需求。
1.主要原理
(一)颜色聚类(Color Clustering):
首先,使用 KMeans 聚类算法将图像中的颜色值聚类为较少数量的颜色簇。聚类的数量由 n_clusters 参数指定。每个像素被归类到与其最接近的聚类中心所代表的颜色簇。颜色聚类的过程大致如下:
- 图像转换: 首先,彩色图像被转换为一个包含所有像素颜色值的数据集。每个像素的颜色通常由红、绿、蓝三个分量组成,因此数据集中的每个样本都是一个三维向量,表示一个像素的颜色。
- 选择聚类数量: 在应用 KMeans 算法之前,需要确定聚类的数量。这个数量通常由用户指定,通过参数 n_clusters 控制。
- 应用 KMeans 算法: 将 KMeans 算法应用于颜色数据集,将颜色值聚类为指定数量的簇。每个簇的质心代表了该簇的平均颜色。
- 像素映射: 每个像素的颜色被映射到最接近的簇的质心所代表的颜色。这样,整个图像被转换为由较少数量的颜色值表示的压缩图像。
通过颜色聚类,彩色图像的颜色数量得以减少,从而实现了数据的压缩。压缩后的图像仍然能够保持视觉上的相似性,同时大大降低了存储空间的需求。
(二)动态规划量化(Dynamic Programming Quantization):
接下来,通过动态规划量化算法对颜色进行压缩。这个算法会进一步减少颜色的数量,并尽可能保持图像的质量。参数 max_colors 指定了压缩后图像中的最大颜色数量。算法会尽量选择与原始图像相似的颜色进行保留,以最大程度地保持图像的质量。而在这部分动态规划量化过程大致如下:
- 初始化: 首先,初始化状态数组,表示不同颜色数量下的最优颜色组合。通常,初始状态可以是一个空数组或者包含少量颜色的数组。
- 状态转移: 根据动态规划的思想,从初始状态开始逐步扩展,计算每个状态下的最优颜色组合。这个过程通常涉及到对每种可能的颜色组合进行评估,并根据优化准则选择最优的组合。
- 选择最优解: 最终,选择最优的颜色组合作为压缩后的图像的颜色集合。这个颜色集合将用于替换原始图像中的颜色,从而实现图像的压缩。
- 压缩数据保存: 压缩后的图像数据以及相关信息(如原始图像的尺寸、选择的颜色集合等)被保存为 NumPy 数组,并通过 np.savez_compressed() 函数保存到指定路径。
通过动态规划量化,我们能够选择一组颜色,使得压缩后的图像在尽可能减少颜色数量的情况下,仍然能够保持与原始图像相似的视觉效果。这样就实现了对图像数据的进一步压缩。
(三)压缩数据保存:
压缩后的图像数据以及相关信息(如原始图像的尺寸、聚类数、最大颜色数、聚类中心颜色等)被保存为 NumPy 数组,并通过 np.savez_compressed() 函数保存到指定路径。
(四)解压缩过程:
解压缩过程与压缩过程相反。首先加载压缩后的图像数据,然后根据聚类中心颜色替换像素颜色,最后将重构后的图像数据重塑为原始形状,并恢复图像的原始尺寸。
2.彩色图像压缩类
(一)类结构介绍
将上面所述的一个彩色图像的压缩功能整合为一个名为’ColorfulImageCompressor’的类,在这个类中有四个函数,它们的函数名称、接受参数以及介绍如下:
ColorfulImageCompressor类
__init__(self, n_clusters, max_colors, resize_factor=0.5): 初始化彩色图像压缩器对象。compress(self, image_path, compressed_file_path): 压缩彩色图像并保存到指定路径。decompress(self, compressed_file_path): 解压缩彩色图像并返回解压缩后的图像对象。_dynamic_programming_quantization(self, image_array): 动态规划量化,将彩色图像颜色量化为指定数量的颜色。
(二)初始化参数
在创建一个彩色图像压缩类的时候需要传入以下三个参数,进行参数的初始化。
n_clusters:聚类数,用于 KMeans 算法,指定图像中的颜色数量。max_colors:最大颜色数,用于动态规划量化,指定压缩后图像中的最大颜色数量。resize_factor:缩放因子,用于调整图像尺寸,默认为 0.5,表示将图像尺寸缩小到原始的一半。
(三)函数介绍
(1)compress(self, image_path, compressed_file_path)
介绍:
该函数的作用是压缩彩色图像并保存到指定路径。参数:
image_path:原始图像文件路径。
compressed_file_path:压缩后的图像文件路径。函数体:
def compress(self, image_path, compressed_file_path):
"""
压缩彩色图像并保存到指定路径。
参数:
- image_path:原始图像文件路径。
- compressed_file_path:压缩后的图像文件路径。
"""
# 打开图像并转换为 RGB 模式
image = Image.open(image_path)
image = image.convert('RGB')
# 根据缩放因子调整图像大小
new_size = (int(image.width * self.resize_factor), int(image.height * self.resize_factor))
image = image.resize(new_size)
# 将图像转换为 NumPy 数组并重塑为二维数组
np_image = np.array(image)
original_shape = np_image.shape
np_image = np_image.reshape(-1, 3)
# 使用动态规划量化对图像进行压缩
compressed_data = self._dynamic_programming_quantization(np_image)
# 保存压缩后的图像数据到指定路径
np.savez_compressed(compressed_file_path, np_image=compressed_data['np_image'], original_shape=original_shape, n_clusters=self.n_clusters, max_colors=self.max_colors, center_colors=compressed_data['center_colors'])
(2)decompress(self, compressed_file_path)
- 介绍:
解压缩彩色图像并返回解压缩后的图像对象。 - 参数:
compressed_file_path:压缩后的图像文件路径。
返回:
reconstructed_image:解压缩后的图像对象。 - 函数体:
def decompress(self, compressed_file_path):
"""
解压缩彩色图像并返回解压缩后的图像对象。
参数:
- compressed_file_path:压缩后的图像文件路径。
返回:
- reconstructed_image:解压缩后的图像对象。
"""
# 加载压缩后的图像数据
compressed_data = np.load(compressed_file_path)
np_image = compressed_data['np_image'].reshape(-1, 3)
center_colors = compressed_data['center_colors']
# 根据聚类中心替换像素颜色
for i in range(self.n_clusters):
np_image[np_image[:, 0] == i] = center_colors[i]
# 将重构后的图像数据重塑为原始形状
original_shape = compressed_data['original_shape']
reconstructed_image = np_image.reshape(*original_shape).astype('uint8')
reconstructed_image = Image.fromarray(reconstructed_image, 'RGB')
# 恢复图像原始尺寸
original_size = (int(reconstructed_image.width / self.resize_factor), int(reconstructed_image.height / self.resize_factor))
reconstructed_image = reconstructed_image.resize(original_size)
return reconstructed_image
(3)_dynamic_programming_quantization(self, image_array)
- 介绍:
动态规划量化,将彩色图像颜色量化为指定数量的颜色。 - 参数:
image_array:图像数据的 NumPy 数组表示。
返回:
compressed_data:包含压缩后图像数据及相关信息的字典。 - 函数体:
def _dynamic_programming_quantization(self, image_array):
"""
动态规划量化,将彩色图像颜色量化为指定数量的颜色。
参数:
- image_array:图像数据的 NumPy 数组表示。
返回:
- compressed_data:包含压缩后图像数据及相关信息的字典。
"""
# 使用 KMeans 进行聚类
kmeans = KMeans(n_clusters=self.n_clusters)
labels = kmeans.fit_predict(image_array)
quantized_image = np.zeros_like(image_array)
# 遍历每个聚类簇
for i in range(self.n_clusters):
# 获取当前簇的像素颜色及其出现次数
cluster_pixels = image_array[labels == i]
unique_colors, color_counts = np.unique(cluster_pixels, axis=0, return_counts=True)
# 选取出现次数最多的前 max_colors 个颜色作为量化后的颜色
color_indices = np.argsort(color_counts)[::-1][:self.max_colors]
quantized_colors = unique_colors[color_indices]
# 计算聚类中像素与量化后颜色的距离
distances = np.linalg.norm(cluster_pixels[:, None] - quantized_colors, axis=2)
quantized_indices = np.argmin(distances, axis=1)
# 使用量化后颜色替换聚类中的像素颜色
quantized_image[labels == i] = quantized_colors[quantized_indices]
# 存储聚类中心颜色
center_colors = kmeans.cluster_centers_.astype('uint8')
return {'np_image': quantized_image, 'n_clusters': self.n_clusters, 'max_colors': self.max_colors, 'center_colors': center_colors}
(四)使用说明
# 创建压缩器对象
compressor = ColorfulImageCompressor(n_clusters=4, max_colors=2, resize_factor=0.5)
# 压缩彩色图像
image_path = "./img/image2.jpg"
compressed_file_path = "./npz/compressed_image2_n4_c2.npz"
compressor.compress(image_path, compressed_file_path)
# 解压缩图像并显示
reconstructed_image = compressor.decompress(compressed_file_path)
reconstructed_image.show()
reconstructed_image.save("./img/reconstructed_image2_n4_c2.jpg")
3.测试结果
测试图片我们使用的采用的一张818*818分辨率,大小为79.49KB的彩色图片。分别使用不同的聚类数量和颜色数量来进行测试。
![]() |
![]() |
|---|---|
| 原始图片 | 聚类数为8,颜色为2的压缩图片 |
详细运行数据如下表(下面文件名中的n为聚类数,而c为颜色数):
| 文件名 | 原始大小(KB) | 压缩后的中间文件大小(KB) | 解压缩后的图片大小 (KB) |
|---|---|---|---|
| reconstructed_image2_n4_c2 | 79.49 | 29.5 | 41.7 |
| reconstructed_image2_n4_c4 | 79.49 | 49.3 | 45.2 |
| reconstructed_image2_n4_c8 | 79.49 | 70.9 | 51.3 |
| reconstructed_image2_n4_c16 | 79.49 | 94.3 | 59.3 |
| reconstructed_image2_n8_c2 | 79.49 | 48.3 | 48.7 |
| reconstructed_image2_n8_c4 | 79.49 | 73.3 | 52.5 |
| reconstructed_image2_n8_c8 | 79.49 | 101 | 59.1 |
| reconstructed_image2_n8_c16 | 79.49 | 125 | 61.1 |
结束语
如果有疑问欢迎大家留言讨论,你如果觉得这篇文章对你有帮助可以给我一个免费的赞吗?你们的认可是我最大的分享动力!
【Python】基于动态规划和K聚类的彩色图片压缩算法的更多相关文章
- [python] 基于词云的关键词提取:wordcloud的使用、源码分析、中文词云生成和代码重写
1. 词云简介 词云,又称文字云.标签云,是对文本数据中出现频率较高的“关键词”在视觉上的突出呈现,形成关键词的渲染形成类似云一样的彩色图片,从而一眼就可以领略文本数据的主要表达意思.常见于博客.微博 ...
- Python基于回溯法解决01背包问题实例
Python基于回溯法解决01背包问题实例 这篇文章主要介绍了Python基于回溯法解决01背包问题,结合实例形式分析了Python回溯法采用深度优先策略搜索解决01背包问题的相关操作技巧,需要的朋友 ...
- 用Python基于Google Bard做一个交互式的聊天机器人
用Python基于Google Bard做一个交互式的聊天机器人 之前已经通过浏览器试过了 Google Bard ,更多细节请看: Try out Google Bard, Will Google ...
- python基于LeanCloud的短信验证
python基于LeanCloud的短信验证 1. 获取LeanCloud的Id.Key 2. 安装Flask框架和Requests库 pip install flask pip install re ...
- Python实现kNN(k邻近算法)
Python实现kNN(k邻近算法) 运行环境 Pyhton3 numpy科学计算模块 计算过程 st=>start: 开始 op1=>operation: 读入数据 op2=>op ...
- 纠错:基于FPGA串口发送彩色图片数据至VGA显示
今天这篇文章是要修改之前的一个错误,前面我写过一篇基于FPGA的串口发送图片数据至VGA显示的文章,最后是显示成功了,但是显示的效果图,看起来确实灰度图,当时我默认我使用的MATLAB代码将图片数据转 ...
- Python基于共现提取《釜山行》人物关系
Python基于共现提取<釜山行>人物关系 一.课程介绍 1. 内容简介 <釜山行>是一部丧尸灾难片,其人物少.关系简单,非常适合我们学习文本处理.这个项目将介绍共现在关系中的 ...
- Python 基于Python实现的ssh兼sftp客户端(上)
基于Python实现的ssh兼sftp客户端 by:授客 QQ:1033553122 实现功能 实现ssh客户端兼ftp客户端:实现远程连接,执行linux命令,上传下载文件 测试环境 Win7 ...
- 聚类-DBSCAN基于密度的空间聚类
1.DBSCAN介绍 DBSCAN(Density-Based Spatial Clustering of Applications with Noise,具有噪声的基于密度的聚类方法)是一种基于密度 ...
- Python基于socket模块实现UDP通信功能示例
Python基于socket模块实现UDP通信功能示例 本文实例讲述了Python基于socket模块实现UDP通信功能.分享给大家供大家参考,具体如下: 一 代码 1.接收端 import ...
随机推荐
- C#使用MX Component实现三菱PLC软元件数据采集的完整步骤(仿真)
前言 本文介绍了如何使用三菱提供的MX Component插件实现对三菱PLC软元件数据的读写,记录了使用计算机仿真,模拟PLC,直至完成测试的详细流程,并重点介绍了在这个过程中的易错点,供参考. 用 ...
- three.js教程2-几何体BufferGeomety顶点
1.网格模型(三角形概念) 网格模型Mesh其实就一个一个三角形(面)拼接构成.使用使用网格模型Mesh渲染几何体geometry,就是几何体所有顶点坐标三个为一组,构成一个三角形,多组顶点构成多个三 ...
- postgresql性能优化2:sql语句和缓存配置
1.看执行计划 EXPLAIN, 此命令用于查看SQL的执行计划 总的来说sql的执行计划是一个树形层次结构, 一般来说阅读上遵从层级越深越优先, 同一层级由上到下的原则. 来跟着铁蛋老师读: 层级越 ...
- CentOS7部署Docker(联网)
一.参考地址 https://blog.csdn.net/m0_49762804/article/details/131398587 二.Docker安装 安装依赖环境,yum-utils ## 检查 ...
- 面试题--mysql的数据库优化
mysql的数据库优化 当有人问你如何对数据库进行优化时,很多人第一反应想到的就是 SQL 优化,如何创建索引,如何改写 SQL,他们把数据库优化与 SQL 优化划上了等号. 当然这不能算是完全错误的 ...
- SpringMVC在处理Ajax请求后返回void导致前台Ajax回调函数不执行
问题简述 在一次SpringMVC项目中,前台通过Ajax请求,准备修改数据库中的数据.因为Ajax请求只是让后台更改数据,所以Controller方法根本不需要返回给前台任何数据,所以我们将Cont ...
- Java JVM——1.JVM与Java体系结构
前言 作为Java工程师的你曾被伤害过吗?你是否也遇到过这些问题? ✘ 运行着的线上系统突然卡死,系统无法访问,甚至直接OOMM! ✘ 想解决线上JVM GC问题,但却无从下手. ✘ 新项目上线,对各 ...
- CSS置顶操作(z-index属性)
z-index使用方法: 1.首先要把position设置为 absolute 或 relative 或 fixed,z-index才能生效 2.设置z-index的值(整数) # 值越大代表越置前, ...
- Prism 基础知识学习(五)发布订阅
Prism 基础知识学习(五)发布订阅 在 ViewBViewModel.cs中 1 public class ViewBViewModel : IDialogAware 2 { 3 /// < ...
- 在 Flask 项目中配置 Session:简明指南
在 Flask 项目中配置 Session:简明指南 本文介绍如何在 Flask 项目中配置会话 1. Flask 内置会话 Flask 自带会话管理功能,使用客户端 Cookie 存储会话数据.默认 ...

