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

实验六 基于Python的数字图像压缩算法

在这里插入图片描述
在这里插入图片描述

一、实验目的

 掌握图像压缩的必要性;
 掌握常见的图像压缩标准;
 掌握常见的图像压缩方法分类;
 掌握常见的图像压缩方法原理与实现(包括哈夫曼编码、算术编码、行程编码方法等);
 了解我国音视频编解码标准的发展。

二、实验内容

  1. 学习图像压缩章节内容。
  2. 读取灰度图像cameraman.jpg和barbara.jpg,从以下方法中选择两种实现对图像的压缩,并显示压缩比和压缩前后对比图像,试分析所选算法的优缺点。
    A.哈夫曼编码; B. 算术编码; C. 行程编码;D. 小波图像编码。

三、完整实验程序、结果与分析

  1. 图像压缩是通过减少图像数据的冗余性来实现高效存储与传输的关键技术,其核心目标是在保证视觉质量的前提下降低数据量。根据压缩后信息是否完整保留,可分为无损压缩和有损压缩两类。
    无损压缩基于统计冗余(如哈夫曼编码、行程编码),通过构建最优编码表将高频像素值用短码表示,实现精确还原但压缩比有限(通常2:1~3:1),适用于医学影像、工程制图等对精度要求严格的场景。
    有损压缩则利用人类视觉特性(如小波编码、JPEG),通过傅里叶变换、小波变换等频域分析分离高频细节与低频主体,结合量化和阈值处理舍弃次要信息,可获得10:1以上的高压缩比,广泛用于网络图像传输和视频流媒体。
    其中,哈夫曼编码通过贪心算法构建最优前缀码树,实现信息熵极限压缩,但需额外存储码表;小波编码通过多分辨率分析实现图像分层压缩,在保留主体特征的同时显著减少数据量,但会引入振铃效应等失真。不同算法在压缩效率、重建质量和计算复杂度上存在显著差异,实际应用需根据场景需求权衡选择。
  2. 选择A.哈夫曼编码和D. 小波图像编码
    代码

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

显示压缩比和压缩前后对比图像,试分析所选算法的优缺点。

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

哈夫曼编码
压缩比: 通常较低(约1.2-1.5倍),因为自然图像像素分布均匀,且需存储编码表。
优点: 无损压缩,精确还原图像。
缺点: 压缩比低,不适合高压缩需求场景,存储编码表增加额外开销。
小波编码
压缩比: 较高(可达5-10倍),通过去除高频细节实现。
优点: 高压缩比,保留主要视觉特征。
缺点: 有损压缩,高频细节丢失,图像可能出现模糊。
对比结论
哈夫曼编码适用于需要无损压缩的场景(如医学图像),但压缩效率有限。
小波编码适用于允许有损压缩的高效存储(如网络传输),但会损失部分细节。

四、问题及心得

在本次实验中,主要遇到图像读取失败及压缩算法实现问题。读取barbara.jpg时因路径格式错误导致加载失败,经排查发现路径需使用原始字符串(r"...")避免转义,并通过os.path.exists验证路径存在性。此外,文件实际格式与扩展名不符时,OpenCV无法读取,改用PIL库并添加文件头校验后解决。压缩算法方面,哈夫曼编码因像素分布均匀导致压缩比低(约1.3倍),且编码表存储开销大;小波编码通过阈值量化高频分量实现高压缩比(约5倍),但重构图像出现边缘模糊。实验表明,无损压缩(哈夫曼)适合精度要求高的场景,而有损压缩(小波)更适用于存储优化。此次实践深化了对路径处理、文件格式验证及压缩算法特性的理解,同时认识到错误隔离和模块化测试的重要性。

完整代码

import cv2
import numpy as np
import matplotlib.pyplot as plt
import pywt
from heapq import heappush, heappop, heapifyimport os
from PIL import Imageclass HuffmanCoder:def __init__(self):self.codes = {}def _build_heap(self, freq):heap = [[weight, [pixel, ""]] for pixel, weight in freq.items()]heapify(heap)while len(heap) > 1:lo = heappop(heap)hi = heappop(heap)for pair in lo[1:]:pair[1] = '0' + pair[1]for pair in hi[1:]:pair[1] = '1' + pair[1]heappush(heap, [lo[0] + hi[0]] + lo[1:] + hi[1:])return heap[0] if heap else []def _build_codes(self, heap_entry):codes = {}for pair in heap_entry[1:]:pixel, code = paircodes[pixel] = codereturn codesdef encode(self, data):freq = {}for pixel in data.flatten():freq[pixel] = freq.get(pixel, 0) + 1if not freq:return ""heap_entry = self._build_heap(freq)if not heap_entry:return ""self.codes = self._build_codes(heap_entry)encoded_data = ''.join([self.codes[pixel] for pixel in data.flatten()])return encoded_datadef decode(self, encoded_data, shape):inv_codes = {v: k for k, v in self.codes.items()}current_code = ""decoded_pixels = []for bit in encoded_data:current_code += bitif current_code in inv_codes:decoded_pixels.append(inv_codes[current_code])current_code = ""return np.array(decoded_pixels, dtype=np.uint8).reshape(shape)def compress_huffman(img):coder = HuffmanCoder()encoded_data = coder.encode(img)data_bits = len(encoded_data)table_bits = 0for pixel, code in coder.codes.items():table_bits += 8 + 4 + len(code)total_bits = data_bits + table_bitsdecoded_img = coder.decode(encoded_data, img.shape)return decoded_img, total_bitsdef compress_wavelet(img, threshold=10.0, quant_factor=20):coeffs = pywt.wavedec2(img, 'haar', level=1)coeff_arr, coeff_slices = pywt.coeffs_to_array(coeffs)coeff_arr_thresh = coeff_arr.copy()coeff_arr_thresh[np.abs(coeff_arr_thresh) < threshold] = 0quantized = np.round(coeff_arr_thresh * quant_factor).astype(np.int32)non_zero = quantized != 0non_zero_coords = np.transpose(np.nonzero(non_zero))non_zero_values = quantized[non_zero]num_non_zero = len(non_zero_values)total_bits = num_non_zero * (16 + 32)dequantized = non_zero_values.astype(np.float32) / quant_factorrecon_coeff_arr = np.zeros_like(coeff_arr, dtype=np.float32)recon_coeff_arr[non_zero] = dequantizedrecon_coeffs = pywt.array_to_coeffs(recon_coeff_arr, coeff_slices, output_format='wavedec2')reconstructed = pywt.waverec2(recon_coeffs, 'haar')reconstructed = np.clip(reconstructed, 0, 255).astype(np.uint8)return reconstructed, total_bitsdef plot_images(original, reconstructed, title, compression_ratio):plt.figure(figsize=(10, 5))plt.subplot(1, 2, 1)plt.imshow(original, cmap='gray')plt.title('Original Image')plt.axis('off')plt.subplot(1, 2, 2)plt.imshow(reconstructed, cmap='gray')plt.title(f'{title}\nCR: {compression_ratio:.2f}')plt.axis('off')plt.show()def safe_imread(path):# 基础路径验证if not os.path.exists(path):print(f"❌ 路径不存在: {path}")print("可能原因:")print(f"1. 请检查D盘是否存在")print(f"2. 确认完整路径:{os.path.abspath(path)}")return None# 文件权限检查if not os.access(path, os.R_OK):print(f"⛔ 无读取权限: {path}")return None# 文件大小检查file_size = os.path.getsize(path)if file_size < 1024:print(f"⚠️ 文件过小({file_size}字节),可能已损坏: {path}")# 尝试用OpenCV读取img = cv2.imread(path, cv2.IMREAD_GRAYSCALE)if img is not None:return imgtry:print(f"⚠️ OpenCV读取失败,尝试PIL读取: {path}")pil_img = Image.open(path).convert('L')return np.array(pil_img, dtype=np.uint8)except Exception as e:print(f"❌ 双重读取均失败: {path}")print(f"错误详情: {str(e)}")# 文件头验证with open(path, 'rb') as f:header = f.read(4).hex()print(f"📄 文件头(十六进制): {header}")print("正常JPEG应以 ffd8ff 开头")return Nonedef main():img_paths = [r"D:\tuxiang\cameraman.jpg",  # 使用原始字符串r"D:\tuxiang\barbara.jpg",]for path in img_paths:print(f"\n{'=' * 40}\n处理图像: {path}")# 安全读取图像img = safe_imread(path)if img is None:print("跳过处理...")continue# 显示基本信息print(f"✅ 成功读取 | 尺寸: {img.shape} | 数据类型: {img.dtype}")# 哈夫曼编码try:huff_img, huff_bits = compress_huffman(img)cr_huff = (img.size * 8) / huff_bits if huff_bits != 0 else 0plot_images(img, huff_img, 'Huffman Encoding', cr_huff)except Exception as e:print(f"哈夫曼编码错误: {str(e)}")# 小波编码try:wavelet_img, wavelet_bits = compress_wavelet(img)cr_wavelet = (img.size * 8) / wavelet_bits if wavelet_bits != 0 else 0plot_images(img, wavelet_img, 'Wavelet Encoding', cr_wavelet)except Exception as e:print(f"小波编码错误: {str(e)}")if __name__ == "__main__":main()
http://www.xdnf.cn/news/318619.html

相关文章:

  • 全自动舆情监控系统实现方案
  • 在地震资料柯希霍夫积分法深度偏移大规模成像中,五维旅行时表高效处理策略
  • Spring MVC中Controller是如何把数据传递给View的?
  • 自由浮动时间和总浮动时间对比
  • 学习整理使用php将SimpleXMLElement 对象解析成数组格式的方法
  • 『深夜_MySQL』数据库操作 字符集与检验规则
  • 桥接模式(Bridge)
  • 从 “机器人 +“ 到 “+ 机器人“:算力政策撬动的产业生态革命
  • 针对Mkdocs部署到Githubpages加速访问速度的一些心得
  • Flutter TabBar / TabBarView 详解
  • 蓝桥杯青少 图形化编程——“星星”点灯
  • hadoop中的序列化和反序列化(2)
  • 软件开发各阶段的自动化测试技术详解
  • 好的软件系统
  • 3、Kafka 核心架构拆解和总结
  • 线程池技术
  • mongodb 学习笔记
  • Java泛型
  • PyTorch 中如何针对 GPU 和 TPU 使用不同的处理方式
  • CPU的基本认识
  • 【PostgreSQL数据分析实战:从数据清洗到可视化全流程】7.2 PostgreSQL与Python数据交互(psycopg2库使用)
  • 解决HomeAssistant 无法安装 samba share问题
  • C++ set和map系列(关联式容器)的介绍及使用
  • 如何有效防御服务器DDoS攻击
  • GoFly企业版框架升级2.6.6版本说明(框架在2025-05-06发布了)
  • 【macOS】iTerm2介绍
  • 21. LangChain金融领域:合同审查与风险预警自动化
  • 直线最小二乘法线性拟合-- points点集合
  • 【JS逆向基础】并发爬虫
  • 01Introduction