当前位置: 首页 > news >正文

Python |GIF 解析与构建(4):快速量化压缩256色算法

Python |GIF 解析与构建(4):快速量化压缩 256 色算法

目录

Python |GIF 解析与构建(4):快速量化压缩 256 色算法

一、引言

二、核心原理:色彩量化与动态优化

2.1 色彩空间压缩模型

2.2 动态优化策略

三、Python 实现:从图像加载到压缩全流程

3.1 关键代码解析

(1)RGB 量化函数

(2)动态压缩主逻辑

(3)质量评估与压缩率计算

3.2 执行结果示例

四、优化与扩展方向

4.1 性能优化

4.2 质量增强

4.3 GIF 适配

五、总结


一、引言

在 GIF 图像的处理中,颜色压缩是关键技术之一。本文聚焦于256 色快速量化压缩算法,结合 Python 实现,深入解析如何通过动态优化色彩空间,在保证图像质量的前提下实现高效压缩,适用于 GIF 动画、图标等对色彩数敏感的场景。

二、核心原理:色彩量化与动态优化

2.1 色彩空间压缩模型

通过RGB 分量位宽截断实现颜色量化,常见方案包括:

  • 332 模型:R(3 位)、G(3 位)、B(2 位),共 28=256 种颜色
  • 333 模型:R/G/B 各 3 位,共 29=512 种颜色(需进一步压缩至 256 色)
  • 444 模型:R/G/B 各 4 位,共 212=4096 种颜色(需强压缩)

2.2 动态优化策略

  1. 自动选择压缩模型
    遍历["332", "333", "444"]模型,计算量化后颜色数:

    • 若颜色数≤256,直接使用该模型
    • 若超过 256,回退至更低位宽模型(如 333→332)
  2. 哈希映射与颜色表构建

    • 通过set()去重统计实际使用的颜色数
    • 建立原始 RGB 值与量化后索引的映射表,确保像素值精准还原

三、Python 实现:从图像加载到压缩全流程

3.1 关键代码解析

(1)RGB 量化函数
def rgb_deal(data, name="332"):if name == "332":r = data[0] >> 5  # R: 3位(0-7)g = data[1] >> 5  # G: 3位(0-7)b = data[2] >> 6  # B: 2位(0-3)return (r << 5) | (g << 2) | b  # 8位组合值# 其他模型类似位运算处理
(2)动态压缩主逻辑
with Image.open("1.png").convert('RGB') as img:pixels = list(img.getdata())for k in range(3):# 计算当前模型下的颜色数hash_set = len(set([rgb_deal(bytes(i), name=list_name[k]) for i in pixels]))if hash_set > 256:k -= 1  # 回退模型break# 生成量化后的颜色索引列表image_hash = [rgb_deal(bytes(i), name=list_name[k]) for i in pixels]
(3)质量评估与压缩率计算
# PSNR(峰值信噪比)计算
def calculate_psnr(original_img, compressed_img):mse = np.mean((np.array(original_img)-np.array(compressed_img))**2)return 20 * np.log10(255.0 / np.sqrt(mse))# SSIM(结构相似性)计算
from skimage.metrics import structural_similarity as ssim
ssim_value = ssim(original_img, compressed_img, multichannel=True)

3.2 执行结果示例

尺寸压缩模型颜色数PSNR (dB)SSIM压缩率
1600×90033221228.030.913846.80%
300×1604443035.390.873962.36%
1260×4033323628.160.947063.09%

四、优化与扩展方向

4.1 性能优化

  • 并行处理:使用multiprocessing加速像素遍历
  • LUT 查找表:预先生成所有可能的 RGB 量化值映射,减少实时计算开销

4.2 质量增强

  • 误差扩散算法(如 Floyd-Steinberg):通过扩散量化误差提升视觉效果
  • 颜色聚类(如 K-means):替代固定位宽截断,实现更精准的色彩保留

4.3 GIF 适配

  • 将量化后的 256 色转换为 GIF 专用的调色板(ImagePalette
  • 支持动画帧批量处理,保持帧率与色彩一致性

五、总结

本文实现的256 色快速量化压缩算法具有以下特点:

  • 动态适配:自动选择最优压缩模型,平衡压缩率与画质
  • 高效性:基于位运算和哈希表,处理速度适用于实时场景
  • 可扩展性:支持与其他图像优化技术(如 DCT 变换)结合

import os
from PIL import Image"""
此算法压缩256色在gif使用的算法中属于
尺寸: 1600x900
压缩颜色种类: 212
压缩算法类别: 332
PSNR: 28.03 dB
SSIM: 0.9138
原始: 350.89KB | 压缩后: 186.68KB | 压缩率: 46.80%
尺寸: 1600x900
压缩颜色种类: 214
压缩算法类别: 332
PSNR: 27.45 dB
SSIM: 0.8755
原始: 664.39KB | 压缩后: 264.25KB | 压缩率: 60.23%
尺寸: 300x160
压缩颜色种类: 30
压缩算法类别: 444
PSNR: 35.39 dB
SSIM: 0.8739
原始: 32.43KB | 压缩后: 12.21KB | 压缩率: 62.36%
尺寸: 485x450
压缩颜色种类: 59
压缩算法类别: 444
PSNR: 33.41 dB
SSIM: 0.7623
原始: 100.68KB | 压缩后: 27.22KB | 压缩率: 72.97%
尺寸: 300x900
压缩颜色种类: 199
压缩算法类别: 332
PSNR: 26.80 dB
SSIM: 0.9417
原始: 136.93KB | 压缩后: 91.08KB | 压缩率: 33.49%
尺寸: 1260x40
压缩颜色种类: 236
压缩算法类别: 333
PSNR: 28.16 dB
SSIM: 0.9470
原始: 19.71KB | 压缩后: 7.28KB | 压缩率: 63.09%
"""# 动态优化法
def rgb_deal(data, name="332"):if name == "332":r = data[0] >> 5  # 3 bits (0-7)g = data[1] >> 5  # 3 bits (0-7)b = data[2] >> 6  # 2 bits (0-3)return (r << 5) | (g << 2) | belif name == "333":r = data[0] >> 5  # 3 bits (0-7)g = data[1] >> 5  # 3 bits (0-7)b = data[2] >> 5  # 3 bits (0-7)return (r << 6) | (g << 3) | belse:r = data[0] >> 4  # 4 bitsg = data[1] >> 4  # 4 bitsb = data[2] >> 4  # 4 bitsreturn (r << 8) | (g << 4) | b# rgb333方法
with Image.open("1.png").convert('RGB') as img:width, height = img.sizepixels = list(img.getdata())print(f"尺寸: {width}x{height}")# 优化压缩算子
list_name = ["332", "333", "444"]
# 动态处理
for k in range(3):hash_set = len(set([rgb_deal(bytes(i), name=list_name[k]) for i in pixels]))if hash_set > 256:k -= 1breakimage_hash = [rgb_deal(bytes(i), name=list_name[k]) for i in pixels]
image_set = set(image_hash)
# 如果压缩数量大于256则压缩
print("压缩颜色种类:", len(image_set))
print("压缩算法类别:", list_name[k])hash_map = {key: (0, 0, 0) for key in image_set}
# 全部哈希遍历映射字典
for s in image_set:hash_map[s] = pixels[image_hash.index(s)]  # 字典赋值new_image = [hash_map.get(x, x) for x in image_hash]
# RGB模式
new_img = Image.new('RGB', (width, height))
# 按行填充
new_img.putdata(new_image)
# 保存为PNG文件
new_img.save('2.png', 'PNG')import numpy as np
from skimage.metrics import structural_similarity as ssimdef calculate_psnr(original_img: Image.Image, compressed_img: Image.Image) -> float:"""计算两张图像之间的PSNR值"""# 将图像转换为NumPy数组original = np.array(original_img).astype(np.float64)compressed = np.array(compressed_img).astype(np.float64)# 确保图像尺寸相同if original.shape != compressed.shape:raise ValueError("两张图像的尺寸必须相同")# 计算MSE(均方误差)mse = np.mean((original - compressed) ** 2)# 避免除零错误if mse == 0:return float('inf')# 计算PSNR(假设像素值范围为0-255)max_pixel = 255.0psnr = 20 * np.log10(max_pixel / np.sqrt(mse))return psnrdef calculate_ssim(original_img: Image.Image, compressed_img: Image.Image) -> float:"""计算两张图像之间的SSIM值"""# 将图像转换为NumPy数组original = np.array(original_img).astype(np.float64)compressed = np.array(compressed_img).astype(np.float64)# 确保图像尺寸相同if original.shape != compressed.shape:raise ValueError("两张图像的尺寸必须相同")# 如果是彩色图像,分别计算每个通道的SSIM并取平均if len(original.shape) == 3 and original.shape[2] == 3:  # RGB图像ssim_values = []for i in range(3):  # 分别计算R、G、B通道ssim_val = ssim(original[:, :, i], compressed[:, :, i],data_range=compressed[:, :, i].max() - compressed[:, :, i].min())ssim_values.append(ssim_val)return np.mean(ssim_values)else:  # 灰度图像return ssim(original, compressed,data_range=compressed.max() - compressed.min())# 加载图像
original = Image.open("1.png").convert("RGB")
compressed = Image.open("2.png").convert("RGB")# 计算PSNR和SSIM
psnr_value = calculate_psnr(original, compressed)
ssim_value = calculate_ssim(original, compressed)print(f"PSNR: {psnr_value:.2f} dB")
print(f"SSIM: {ssim_value:.4f}")# 替换为你的图片路径
original = "1.png"
compressed = "2.png"
# 获取文件大小(KB)
o_size = os.path.getsize(original) / 1024
c_size = os.path.getsize(compressed) / 1024
# 计算并打印压缩率
ratio = (1 - c_size / o_size) * 100
print(f"原始: {o_size:.2f}KB | 压缩后: {c_size:.2f}KB | 压缩率: {ratio:.2f}%")

import os
from PIL import Image"""
此算法压缩256色在gif使用的算法中属于
尺寸: 1600x900
压缩颜色种类: 212
压缩算法类别: 332
PSNR: 28.03 dB
SSIM: 0.9138
原始: 350.89KB | 压缩后: 186.68KB | 压缩率: 46.80%
尺寸: 1600x900
压缩颜色种类: 214
压缩算法类别: 332
PSNR: 27.45 dB
SSIM: 0.8755
原始: 664.39KB | 压缩后: 264.25KB | 压缩率: 60.23%
尺寸: 300x160
压缩颜色种类: 30
压缩算法类别: 444
PSNR: 35.39 dB
SSIM: 0.8739
原始: 32.43KB | 压缩后: 12.21KB | 压缩率: 62.36%
尺寸: 485x450
压缩颜色种类: 59
压缩算法类别: 444
PSNR: 33.41 dB
SSIM: 0.7623
原始: 100.68KB | 压缩后: 27.22KB | 压缩率: 72.97%
尺寸: 300x900
压缩颜色种类: 199
压缩算法类别: 332
PSNR: 26.80 dB
SSIM: 0.9417
原始: 136.93KB | 压缩后: 91.08KB | 压缩率: 33.49%
尺寸: 1260x40
压缩颜色种类: 236
压缩算法类别: 333
PSNR: 28.16 dB
SSIM: 0.9470
原始: 19.71KB | 压缩后: 7.28KB | 压缩率: 63.09%
"""# 动态优化法
def rgb_deal(data, name="332"):if name == "332":r = data[0] >> 5  # 3 bits (0-7)g = data[1] >> 5  # 3 bits (0-7)b = data[2] >> 6  # 2 bits (0-3)return (r << 5) | (g << 2) | belif name == "333":r = data[0] >> 5  # 3 bits (0-7)g = data[1] >> 5  # 3 bits (0-7)b = data[2] >> 5  # 3 bits (0-7)return (r << 6) | (g << 3) | belse:r = data[0] >> 4  # 4 bitsg = data[1] >> 4  # 4 bitsb = data[2] >> 4  # 4 bitsreturn (r << 8) | (g << 4) | b# rgb333方法
with Image.open("1.png").convert('RGB') as img:width, height = img.sizepixels = list(img.getdata())print(f"尺寸: {width}x{height}")# 优化压缩算子
list_name = ["332", "333", "444"]
# 动态处理
for k in range(3):hash_set = len(set([rgb_deal(bytes(i), name=list_name[k]) for i in pixels]))if hash_set > 256:k -= 1breakimage_hash = [rgb_deal(bytes(i), name=list_name[k]) for i in pixels]
image_set = set(image_hash)
# 如果压缩数量大于256则压缩
print("压缩颜色种类:", len(image_set))
print("压缩算法类别:", list_name[k])hash_map = {key: (0, 0, 0) for key in image_set}
# 全部哈希遍历映射字典
for s in image_set:hash_map[s] = pixels[image_hash.index(s)]  # 字典赋值new_image = [hash_map.get(x, x) for x in image_hash]
# RGB模式
new_img = Image.new('RGB', (width, height))
# 按行填充
new_img.putdata(new_image)
# 保存为PNG文件
new_img.save('2.png', 'PNG')import numpy as np
from skimage.metrics import structural_similarity as ssimdef calculate_psnr(original_img: Image.Image, compressed_img: Image.Image) -> float:"""计算两张图像之间的PSNR值"""# 将图像转换为NumPy数组original = np.array(original_img).astype(np.float64)compressed = np.array(compressed_img).astype(np.float64)# 确保图像尺寸相同if original.shape != compressed.shape:raise ValueError("两张图像的尺寸必须相同")# 计算MSE(均方误差)mse = np.mean((original - compressed) ** 2)# 避免除零错误if mse == 0:return float('inf')# 计算PSNR(假设像素值范围为0-255)max_pixel = 255.0psnr = 20 * np.log10(max_pixel / np.sqrt(mse))return psnrdef calculate_ssim(original_img: Image.Image, compressed_img: Image.Image) -> float:"""计算两张图像之间的SSIM值"""# 将图像转换为NumPy数组original = np.array(original_img).astype(np.float64)compressed = np.array(compressed_img).astype(np.float64)# 确保图像尺寸相同if original.shape != compressed.shape:raise ValueError("两张图像的尺寸必须相同")# 如果是彩色图像,分别计算每个通道的SSIM并取平均if len(original.shape) == 3 and original.shape[2] == 3:  # RGB图像ssim_values = []for i in range(3):  # 分别计算R、G、B通道ssim_val = ssim(original[:, :, i], compressed[:, :, i],data_range=compressed[:, :, i].max() - compressed[:, :, i].min())ssim_values.append(ssim_val)return np.mean(ssim_values)else:  # 灰度图像return ssim(original, compressed,data_range=compressed.max() - compressed.min())# 加载图像
original = Image.open("1.png").convert("RGB")
compressed = Image.open("2.png").convert("RGB")# 计算PSNR和SSIM
psnr_value = calculate_psnr(original, compressed)
ssim_value = calculate_ssim(original, compressed)print(f"PSNR: {psnr_value:.2f} dB")
print(f"SSIM: {ssim_value:.4f}")# 替换为你的图片路径
original = "1.png"
compressed = "2.png"
# 获取文件大小(KB)
o_size = os.path.getsize(original) / 1024
c_size = os.path.getsize(compressed) / 1024
# 计算并打印压缩率
ratio = (1 - c_size / o_size) * 100
print(f"原始: {o_size:.2f}KB | 压缩后: {c_size:.2f}KB | 压缩率: {ratio:.2f}%")

http://www.xdnf.cn/news/692641.html

相关文章:

  • 关于uv 工具的使用总结(uv,conda,pip什么关系)
  • 在 MATLAB 2015a 中如何调用 Python
  • Spring Boot 读取.env文件获取配置
  • 金融全业务场景的系统分层与微服务域架构切分
  • 2025-05-28 Python-List-二分法
  • 实验设计与分析(第6版,Montgomery)第4章随机化区组,拉丁方, 及有关设计4.5节思考题4.26~4.27 R语言解题
  • 【HTML-14】HTML 列表:从基础到高级的完整指南
  • 从SEO到GEO:搜索范式迁移的三大断层
  • 算法分析·回溯法
  • JAX-WS 返回值<return>标签怎么修改
  • 植被监测新范式!Python驱动机器学习反演NDVI/LAI关键技术解析
  • Qwen3大模型本地部署及Python调用指南
  • 数据库管理-第330期 数据库国产化可以顺便做的事情(20250528)
  • SpringBoot使用ffmpeg实现视频压缩
  • 大模型应用开发第五讲:成熟度模型:从ChatGPT(L2)到未来自主Agent(L4)
  • 服务器开机自启动服务
  • css设置动态数值:clamp函数
  • Tailwind CSS 实战,基于 Kooboo 构建 AI 对话框页面(三):实现暗黑模式主题切换
  • kubernate解决 “cni0“ already has an IP address different from 10.244.0.1/24问题
  • FastAPI 依赖注入
  • c++第二章练习题
  • Java数值字符串相加
  • 英飞凌SBC芯片TLE9263QX for STM32的库函数与使用
  • ⭐️⭐️⭐️ 免费的AI Clouder认证 ⭐️⭐️⭐️ 第四弹【课时1:课程概览】for「大模型Clouder认证:基于通义灵码实现高效AI编码」
  • 企业信息管理系统的设计与实现(代码+数据库+LW)
  • 【多线程初阶】初识线程 创建线程
  • 线性回归中标准方程法求逆失败的解法:正则化
  • 三维点云深度学习代码torch-points3d-SiamKPConvVariants复现记录(持续更新中)
  • MAC程序签名遇到的问题
  • 用结构填充平面