【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.shapenp_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聚类的彩色图片压缩算法
引言 当想要压缩一张彩色图像时,彩色图像通常由数百万个颜色值组成,每个颜色值都由红、绿、蓝三个分量组成。因此,如果我们直接对图像的每个像素进行编码,会导致非常大的数据量。为了减少数据量,我们可以尝试减少颜色…...
【做一道算一道】和为 K 的子数组
给你一个整数数组 nums 和一个整数 k ,请你统计并返回 该数组中和为 k 的子数组的个数 。 子数组是数组中元素的连续非空序列。 示例 1: 输入:nums [1,1,1], k 2 输出:2 示例 2: 输入:nums [1,2,3],…...
Facebook应用开发:认证与授权登录流程详解
Facebook作为全球最大的社交平台之一,提供了强大的认证与授权机制,允许第三方应用通过Facebook登录来简化用户的注册和登录流程。本文将详细介绍Facebook应用开发中的认证和授权登录流程,以及如何在应用中实现这一功能。 关键词 Facebook登…...
实战:搭建一款属于自己的个人知识库~docusaurus(强大且丝滑)-2024.7.7(测试成功)
目录 文章目录 目录docusaurus简介效果专题链接👏环境源码1、安装基础环境2、拉取代码3、安装坚果云并同步md核心文件4、构建运行5、配置脚本环境1.配置vscode终端到ecs的免密2.配置win10 vscode终端环境变量 6、构建并推送静态文件到ecs关于我最后最后 docusaurus简…...
Java教程之IO模式精讲,NIO+BIO
第一章 BIO、NIO、AIO介绍 背景 在java的软件设计开发中,通信架构是不可避免的,我们在进行不同系统或者不同进程之间的数据交互,或 者在高并发下的通信场景下都需要用到网络通信相关的技术,对于一些经验丰富的程序员来说&#x…...
如何让代码兼容 Python 2 和 Python 3?Future 库助你一臂之力
目录 01Future 是什么? 为什么选择 Future? 安装与配置 02Future 的基本用法 1、兼容 print 函数 2、兼容整数除法 3、兼容 Unicode 字符串 03Future 的高级功能 1. 处理字符串与字节 2. 统一异常处理…...
AI让大龄程序员重新焕发活力
AI是在帮助开发者还是取代他们? 在软件开发领域,生成式人工智能(AIGC)正在改变开发者的工作方式。无论是代码生成、错误检测还是自动化测试,AI工具正在成为开发者的得力助手。然而,这也引发了对开发者职业…...
Python在现代办公自动化中的应用:会不会被裁?就看你的效率了!
Python在现代办公自动化中的应用:提升效率的艺术 Python,作为一门简洁而强大的编程语言,已经成为许多办公室英雄优化日常工作的秘密武器。本文将带你探索Python如何在办公自动化领域大放异彩,并且会巧妙融入开源神器PlugLink&…...
Laravel5+mycat 报错 “Packets out of order”
背景 近期对负责项目,配置了一套 主从复制的 MySQL 集群 使用了中间件 mycat 但测试发现,替换了原来的数据连接后,会出现 Packets out of order 的报错 同时注意到,有的框架代码中竟然也会失效,比如 controller 类中&…...
使用androidx.appcompat:appcompat:1.7.0无法运行的问题
问题: 使用 implementation ‘androidx.appcompat:appcompat:1.7.0’ 出现无法运行的问题,编译都没有问题 AGPBI: {“kind”:“error”,“text”:“java.lang.NullPointerException”,“sources”:[{“file”:“C:\Users\10557\.gradle\caches\transfor…...
基于Java的水果商品销售网站
1 水果商品销售网站概述 1.1 课题简介 随着电子商务在当今社会的迅猛发展,水果在线销售已逐渐演变为一种极为便捷的购物方式,日益受到人们的青睐。本系统的设计初衷便是构建一个功能完备、用户体验友好的水果销售平台,致力于为用户提供优质、…...
Redis 线程模型
0、参考 【Redis线程模型】 【big key 排查和解决思路】 1、 Redis 单线程的理解 为什么单线程:CPU不是性能瓶颈(内存和网络),单线程能够达到业务要求 网络IO和键值对读写都是由一个线程完成的 2、 Redis 多线程的理解 持久化…...
栈和队列---循环队列
1.循环队列的出现 (1)上面的这个就是一个普通的数据的入队和出队的过程我们正常情况下去实现这个入队和出队的过程,就是这个数据从这个队尾进入,从队头离开,但是这个加入的时候肯定是没有其他的问题的,直接…...
打卡第4天----链表
通过学习基础,发现我的基本功还得需要再练练,思路得再更加清晰明了,这样子做算法题才能驾轻就熟。每天记录自己的进步。 一、两两交换 题目编号:24 题目描述: 给你一个链表,两两交换其中相邻的节点&#x…...
07-7.1.1 查找的基本概念
👋 Hi, I’m Beast Cheng 👀 I’m interested in photography, hiking, landscape… 🌱 I’m currently learning python, javascript, kotlin… 📫 How to reach me --> 458290771qq.com 喜欢《数据结构》部分笔记的小伙伴可以…...
【数据结构与算法】快速排序双指针法
💓 博客主页:倔强的石头的CSDN主页 📝Gitee主页:倔强的石头的gitee主页 ⏩ 文章专栏:《数据结构与算法》 期待您的关注 ...
GESP C++一级真题
PDF图片1-7 点赞❤️关注😍收藏⭐️ 互粉必回🙏🙏🙏...
短信验证码实现
一、设置AccessKey 创建用户并配置使用权限,使我们拥有调用 aliyunAPI 的权限,之后会生成 AccessKeyID 和 AccessKey密码,后面我们会使用到。需要注意的是 AccessKeyID 和 AccessKey密码生成后我们需要将他保存起来,否则后期无法查…...
pnpm的坑
请问pnpm的两个坑怎么解决: 第一个坑:没有节省磁盘空间 我已经配置了依赖的存储位置, 但我在项目里pnpm install以后,发现依赖包还是很大, 然后发现里面的链接并不是指向先前配置的依赖存储位置,而是指…...
如何监控和分析 PostgreSQL 中的查询执行计划?
文章目录 一、为什么监控和分析查询执行计划很重要二、PostgreSQL 中用于获取查询执行计划的方法三、理解查询执行计划的关键元素四、通过示例分析查询执行计划五、优化查询执行计划的常见策略六、使用工具辅助分析七、结合实际案例的详细分析八、总结 在 PostgreSQL 数据库中&…...
Java 语言特性(面试系列2)
一、SQL 基础 1. 复杂查询 (1)连接查询(JOIN) 内连接(INNER JOIN):返回两表匹配的记录。 SELECT e.name, d.dept_name FROM employees e INNER JOIN departments d ON e.dept_id d.dept_id; 左…...
SCAU期末笔记 - 数据分析与数据挖掘题库解析
这门怎么题库答案不全啊日 来简单学一下子来 一、选择题(可多选) 将原始数据进行集成、变换、维度规约、数值规约是在以下哪个步骤的任务?(C) A. 频繁模式挖掘 B.分类和预测 C.数据预处理 D.数据流挖掘 A. 频繁模式挖掘:专注于发现数据中…...
Matlab | matlab常用命令总结
常用命令 一、 基础操作与环境二、 矩阵与数组操作(核心)三、 绘图与可视化四、 编程与控制流五、 符号计算 (Symbolic Math Toolbox)六、 文件与数据 I/O七、 常用函数类别重要提示这是一份 MATLAB 常用命令和功能的总结,涵盖了基础操作、矩阵运算、绘图、编程和文件处理等…...
【C语言练习】080. 使用C语言实现简单的数据库操作
080. 使用C语言实现简单的数据库操作 080. 使用C语言实现简单的数据库操作使用原生APIODBC接口第三方库ORM框架文件模拟1. 安装SQLite2. 示例代码:使用SQLite创建数据库、表和插入数据3. 编译和运行4. 示例运行输出:5. 注意事项6. 总结080. 使用C语言实现简单的数据库操作 在…...
vue3+vite项目中使用.env文件环境变量方法
vue3vite项目中使用.env文件环境变量方法 .env文件作用命名规则常用的配置项示例使用方法注意事项在vite.config.js文件中读取环境变量方法 .env文件作用 .env 文件用于定义环境变量,这些变量可以在项目中通过 import.meta.env 进行访问。Vite 会自动加载这些环境变…...
保姆级教程:在无网络无显卡的Windows电脑的vscode本地部署deepseek
文章目录 1 前言2 部署流程2.1 准备工作2.2 Ollama2.2.1 使用有网络的电脑下载Ollama2.2.2 安装Ollama(有网络的电脑)2.2.3 安装Ollama(无网络的电脑)2.2.4 安装验证2.2.5 修改大模型安装位置2.2.6 下载Deepseek模型 2.3 将deepse…...
Linux 内存管理实战精讲:核心原理与面试常考点全解析
Linux 内存管理实战精讲:核心原理与面试常考点全解析 Linux 内核内存管理是系统设计中最复杂但也最核心的模块之一。它不仅支撑着虚拟内存机制、物理内存分配、进程隔离与资源复用,还直接决定系统运行的性能与稳定性。无论你是嵌入式开发者、内核调试工…...
如何更改默认 Crontab 编辑器 ?
在 Linux 领域中,crontab 是您可能经常遇到的一个术语。这个实用程序在类 unix 操作系统上可用,用于调度在预定义时间和间隔自动执行的任务。这对管理员和高级用户非常有益,允许他们自动执行各种系统任务。 编辑 Crontab 文件通常使用文本编…...
关于easyexcel动态下拉选问题处理
前些日子突然碰到一个问题,说是客户的导入文件模版想支持部分导入内容的下拉选,于是我就找了easyexcel官网寻找解决方案,并没有找到合适的方案,没办法只能自己动手并分享出来,针对Java生成Excel下拉菜单时因选项过多导…...
LangChain 中的文档加载器(Loader)与文本切分器(Splitter)详解《二》
🧠 LangChain 中 TextSplitter 的使用详解:从基础到进阶(附代码) 一、前言 在处理大规模文本数据时,特别是在构建知识库或进行大模型训练与推理时,文本切分(Text Splitting) 是一个…...

