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

Python |GIF 解析与构建(3):简单哈希压缩256色算法

Python |GIF 解析与构建(3):简单哈希压缩256色算法

目录

Python |GIF 解析与构建(3):简单哈希压缩256色算法

一、算法性能表现

二、算法核心原理与实现

(一)简单哈希算法逻辑

1. 颜色分量压缩

2. 加权灰度值计算

3. 旋转哈希生成

(二)图像压缩流程

三、图像质量评估实现

(一)PSNR 计算

(二)SSIM 计算

四、算法优缺点与应用场景

(一)优势

(二)局限性

(三)适用场景

五、总结与改进方向


在图像处理领域,压缩算法的效率与效果始终是研究的核心方向之一。本文将聚焦于一种适用于 256 色 GIF 的简单哈希压缩算法,通过实际案例分析其压缩性能、技术实现及应用场景。

一、算法性能表现

该算法在不同尺寸、类型的图像上在同类的算法之中展现出了中游水平的压缩能力和处理图像质量能力,在GIF256色压缩可以很容易的构建出来,以下是具体测试数据:

图像类型原始尺寸原始大小压缩后大小压缩率PSNR(dB)SSIM
900P 全屏截图1600×900740KB224KB69.73%24.480.8358
900P 全屏截图1600×900477KB238KB50.1%24.830.8997
小型图片300×16039KB7KB82.05%27.980.8877
任务栏截图1260×4027KB13KB51.8%25.100.9448

关键指标说明

  • 压缩率:文件体积缩减比例,数值越高表示压缩效果越显著(如小型图片压缩率达 82.05%)。
  • PSNR(峰值信噪比):衡量图像失真程度,数值越高表示画质保留越好(任务栏截图 PSNR 为 25.10dB,接近人眼可接受范围)。
  • SSIM(结构相似性):评估图像结构相似度,取值越接近 1 表示画质越接近原图(任务栏截图 SSIM 达 0.9448,视觉效果接近无损)。

二、算法核心原理与实现

(一)简单哈希算法逻辑

1. 颜色分量压缩

通过右移操作降低颜色精度:

def shift_bytes_data(data):return bytes(c >> 5 for c in data)  # 将RGB各分量从8位压缩为3位(0-7)

原理:利用人眼对颜色细节敏感度较低的特性,通过舍弃低位数据减少颜色数量。

2. 加权灰度值计算

结合人眼对 RGB 三通道的敏感度差异(绿光 > 红光 > 蓝光):

r, g, b = data[0], data[1], data[2]
weighted_sum = (r * 299 + g * 587 + b * 114) // 1000  # 近似计算亮度值

作用:将彩色信息转化为亮度特征,为哈希值提供感知层面的鲁棒性。

3. 旋转哈希生成

通过位移与异或运算增强哈希值的扩散性:

rotated_hash = ((r << 5) ^ g ^ (b >> 3)) & 0xFF  # 混合RGB分量并限制值域
hash_value = (weighted_sum + rotated_hash) & 0xFF  # 合并亮度与色彩特征

优势:减少哈希冲突概率,确保相似颜色生成不同哈希值。

(二)图像压缩流程

  1. 像素数据处理
    将图像转换为 RGB 模式,提取像素列表:

    with Image.open("1.png").convert('RGB') as img:pixels = list(img.getdata())  # 获取所有像素的(R, G, B)元组
    
  2. 哈希映射构建
    对每个像素生成哈希值,统计唯一哈希数量(即压缩后颜色数):

    image_set = set([simple_hash_unrolled(bytes(i)) for i in pixels])
    print("压缩数量:", len(image_set))  # 输出唯一哈希值总数
    
  3. 颜色映射与重建
    创建哈希值到原始颜色的映射表,用映射颜色重建图像:

    hash_map = {key: pixels[image_hash.index(s)] for key in image_set}  # 记录每个哈希对应的原始颜色
    new_image = [hash_map.get(x, x) for x in image_hash]  # 用映射颜色替换像素值
    

三、图像质量评估实现

(一)PSNR 计算

基于均方误差(MSE)评估像素级差异:

def calculate_psnr(original_img, compressed_img):original = np.array(original_img).astype(np.float64)compressed = np.array(compressed_img).astype(np.float64)mse = np.mean((original - compressed) ** 2)return 20 * np.log10(255.0 / np.sqrt(mse))  # 假设像素值范围0-255

(二)SSIM 计算

从亮度、对比度、结构三方面评估图像相似性:

def calculate_ssim(original_img, compressed_img):original = np.array(original_img)compressed = np.array(compressed_img)if len(original.shape) == 3:  # 彩色图像分通道计算return np.mean([ssim(original[:, :, i], compressed[:, :, i], data_range=255) for i in range(3)])else:  # 灰度图像直接计算return ssim(original, compressed, data_range=255)

四、算法优缺点与应用场景

(一)优势

  • 速度快:压缩耗时约 5 秒(针对 1600×900 图像),适合实时或批量处理。
  • 实现简单:无需复杂数学变换,仅通过位运算和哈希映射完成压缩。
  • 适应性强:在文字图标(如任务栏)、大面积单色区域图像中表现优异(SSIM>0.9)。

(二)局限性

  • 颜色损失明显:在高色彩丰富度图像中(如自然风景),压缩后可能出现色块化。
  • 压缩率波动大:依赖图像内容,复杂图像压缩率较低(约 50%)。

(三)适用场景

  • UI 界面截图:任务栏、菜单等含大量纯色或重复图案的场景。
  • 简单图标 / 图形:LOGO、线条图等对色彩精度要求不高的图像。
  • 低带宽传输:需要快速压缩传输的简单图像场景(如嵌入式设备)。

五、总结与改进方向

该简单哈希算法为 256 色 GIF 压缩提供了一种轻量级解决方案,平衡了压缩效率与实现复杂度。未来可通过以下方式优化:

  1. 动态颜色量化:结合 K-means 等聚类算法,自适应选择最优颜色子集。
  2. 哈希函数优化:引入更复杂的哈希算法(如感知哈希),提升相似颜色区分度。
  3. 分块处理:对图像分区域应用不同压缩策略,保留高细节区域的色彩精度。

代码如下:

import os
from PIL import Image"""
此算法压缩256色在gif使用的算法中属于中游
尺寸: 1600x900
PSNR: 24.45 dB
SSIM: 0.8676
原始: 666.91KB | 压缩后: 284.01KB | 压缩率: 57.41%尺寸: 1600x900
PSNR: 26.44 dB
SSIM: 0.9225
原始: 434.13KB | 压缩后: 219.29KB | 压缩率: 49.49%尺寸: 300x160
PSNR: 34.48 dB
SSIM: 0.8587
原始: 32.36KB | 压缩后: 9.47KB | 压缩率: 70.75%尺寸: 690x300
PSNR: 23.46 dB
SSIM: 0.9106
原始: 106.14KB | 压缩后: 71.28KB | 压缩率: 32.84%尺寸: 480x480
PSNR: 29.43 dB
SSIM: 0.6616
原始: 107.20KB | 压缩后: 17.50KB | 压缩率: 83.67%
"""# 简单哈希
def simple_hash_unrolled(data):# 通过右移来实现对细节的颜色进行进一步压缩def shift_bytes_data(data):return bytes(c >> 5 for c in data)data = shift_bytes_data(data)# 提取RGB分量r, g, b = data[0], data[1], data[2]# 人眼敏感度计算weighted_sum = (r * 299 + g * 587 + b * 114) // 1000# 旋转哈希:结合位移和异或运算,扩散哈希值分布rotated_hash = ((r << 5) ^ g ^ (b >> 3)) & 0xFF# 亮度brightness_component = max(r, g, b) & 0xFF# 计算哈希hash_value = (weighted_sum + rotated_hash + brightness_component) & 0xFFreturn hash_value# 简单压缩
def rgb332_hash(data):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) | b#  亮度
def perceptual_hash(data):# 转换到YUV空间,提取亮度(更符合人眼敏感度)y = (data[0] * 299 + data[1] * 587 + data[2] * 114) // 1000# 结合色度简化u = data[1] >> 5  # 3-bitv = data[2] >> 5  # 3-bitreturn (y & 0xF8) | (u >> 2) | (v << 3)# 换位
def enhanced_rotated_hash(data):r, g, b = data[0] >> 4, data[1] >> 4, data[2] >> 4  # 4-bit压缩# 多重异或+位移扩散hash_r = (r << 4) ^ (g << 2) ^ bhash_g = (g << 4) ^ (b << 2) ^ rhash_b = (b << 4) ^ (r << 2) ^ greturn (hash_r + hash_g * 3 + hash_b * 5) & 0xFF# 简单哈希法
with Image.open("1.png").convert('RGB') as img:width, height = img.sizepixels = list(img.getdata())print(f"尺寸: {width}x{height}")print(f"像素数据: {pixels[:10]} ...")image_set = set([simple_hash_unrolled(bytes(i)) for i in pixels])
print("压缩数量:", len(image_set))
print("压缩哈希:", image_set)
image_hash = [simple_hash_unrolled(bytes(i)) for i in pixels]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
PSNR: 24.45 dB
SSIM: 0.8676
原始: 666.91KB | 压缩后: 284.01KB | 压缩率: 57.41%尺寸: 1600x900
PSNR: 26.44 dB
SSIM: 0.9225
原始: 434.13KB | 压缩后: 219.29KB | 压缩率: 49.49%尺寸: 300x160
PSNR: 34.48 dB
SSIM: 0.8587
原始: 32.36KB | 压缩后: 9.47KB | 压缩率: 70.75%尺寸: 690x300
PSNR: 23.46 dB
SSIM: 0.9106
原始: 106.14KB | 压缩后: 71.28KB | 压缩率: 32.84%尺寸: 480x480
PSNR: 29.43 dB
SSIM: 0.6616
原始: 107.20KB | 压缩后: 17.50KB | 压缩率: 83.67%
"""# 简单哈希
def simple_hash_unrolled(data):# 通过右移来实现对细节的颜色进行进一步压缩def shift_bytes_data(data):return bytes(c >> 5 for c in data)data = shift_bytes_data(data)# 提取RGB分量r, g, b = data[0], data[1], data[2]# 人眼敏感度计算weighted_sum = (r * 299 + g * 587 + b * 114) // 1000# 旋转哈希:结合位移和异或运算,扩散哈希值分布rotated_hash = ((r << 5) ^ g ^ (b >> 3)) & 0xFF# 亮度brightness_component = max(r, g, b) & 0xFF# 计算哈希hash_value = (weighted_sum + rotated_hash + brightness_component) & 0xFFreturn hash_value# 简单压缩
def rgb332_hash(data):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) | b#  亮度
def perceptual_hash(data):# 转换到YUV空间,提取亮度(更符合人眼敏感度)y = (data[0] * 299 + data[1] * 587 + data[2] * 114) // 1000# 结合色度简化u = data[1] >> 5  # 3-bitv = data[2] >> 5  # 3-bitreturn (y & 0xF8) | (u >> 2) | (v << 3)# 换位
def enhanced_rotated_hash(data):r, g, b = data[0] >> 4, data[1] >> 4, data[2] >> 4  # 4-bit压缩# 多重异或+位移扩散hash_r = (r << 4) ^ (g << 2) ^ bhash_g = (g << 4) ^ (b << 2) ^ rhash_b = (b << 4) ^ (r << 2) ^ greturn (hash_r + hash_g * 3 + hash_b * 5) & 0xFF# 简单哈希法
with Image.open("1.png").convert('RGB') as img:width, height = img.sizepixels = list(img.getdata())print(f"尺寸: {width}x{height}")print(f"像素数据: {pixels[:10]} ...")image_set = set([simple_hash_unrolled(bytes(i)) for i in pixels])
print("压缩数量:", len(image_set))
print("压缩哈希:", image_set)
image_hash = [simple_hash_unrolled(bytes(i)) for i in pixels]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.lryc.cn/news/2393051.html

相关文章:

  • 蓝桥杯2114 李白打酒加强版
  • 基本数据指针的解读-C++
  • Android Studio里的BLE数据接收策略
  • 【Office】Excel两列数据比较方法总结
  • 基于多模态脑电、音频与视觉信号的情感识别算法【Nature核心期刊,EAV:EEG-音频-视频数据集】
  • 【QueryServer】dbeaver使用phoenix连接Hbase(轻客户端方式)
  • 数据湖 (特点+与数据仓库和数据沼泽的对比讲解)
  • 深入链表剖析:从原理到 C 语言实现,涵盖单向、双向及循环链表全解析
  • 编码总结如下
  • 《算力觉醒!ONNX Runtime + DirectML如何点燃Windows ARM设备的AI引擎》
  • [9-1] USART串口协议 江协科技学习笔记(13个知识点)
  • Oracle基础知识(五)——ROWID ROWNUM
  • 简述synchronized和java.util.concurrent.locks.Lock的异同 ?
  • OpenCV CUDA模块直方图计算------在 GPU 上计算图像直方图的函数calcHist()
  • EMS只是快递那个EMS吗?它跟能源有什么关系?
  • 日志技术-LogBack、Logback快速入门、Logback配置文件、Logback日志级别
  • 修改Cinnamon主题
  • 91.评论日记
  • HTML5实现简洁的端午节节日网站源码
  • Window10+ 安装 go环境
  • AWS WebRTC:获取ICE服务地址(part 2): ICE Agent的作用
  • 一、Sqoop历史发展及原理
  • React 编译器 RC
  • PyTorch 中mm和bmm函数的使用详解
  • 关于表连接
  • 【计算机网络】fork()+exec()创建新进程(僵尸进程及孤儿进程)
  • QPS 和 TPS 详解
  • Word表格怎样插入自动序号或编号
  • 数据结构:导论
  • 青少年编程与数学 02-020 C#程序设计基础 13课题、数据访问