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

【图像处理基石】如何入门图像压缩编码技术?

在这里插入图片描述

一、先搞懂“痛点”:为什么需要图像压缩?

我们平时拍的一张4000×3000分辨率的RGB888格式照片,原始体积约34.3MB。10张图就超300MB,视频更是“存储杀手”——这就是压缩编码的意义:在尽量保留视觉质量的前提下,减少数据量,解决“存不下、传不动”的问题。

二、压缩的根本:图像里的3类“冗余”

图像能被压缩,核心是存在大量冗余信息,去掉后不影响视觉效果:

  • 空间冗余:相邻像素颜色相似(比如天空、墙面);
  • 视觉冗余:人眼对高频细节、部分颜色不敏感;
  • 信息熵冗余:部分像素值出现频率极高,用等长编码浪费空间。

三、核心分类:无损压缩 vs 有损压缩

类型核心特点适用场景典型例子
无损压缩解压后与原图完全一致截图、图标、医学图像PNG、GIF、DEFLATE算法
有损压缩牺牲少量画质换高压缩比照片、网页图、视频帧JPEG、WebP(有损模式)

接下来,直接上代码实战!

四、实战1:OpenCV实现常见图像格式压缩(最常用)

这是开发中最频繁的操作——将图像保存为JPEG/PNG/WebP,调整压缩参数,对比效果和体积。

环境准备

先安装依赖库:

pip install opencv-python pillow  # opencv处理图像,pillow辅助格式转换

代码实现:格式转换+压缩参数调整

import cv2
import os
from PIL import Imagedef image_compress_demo(input_path, output_dir):# 1. 读取图像(OpenCV默认BGR格式,后续保存会自动转RGB)img = cv2.imread(input_path)if img is None:print(f"无法读取图像:{input_path}")return# 创建输出目录os.makedirs(output_dir, exist_ok=True)filename = os.path.splitext(os.path.basename(input_path))[0]# 2. JPEG有损压缩(质量0-100,越高画质越好、体积越大)jpeg_quality = 70  # 常用中间值,平衡画质和体积jpeg_output = os.path.join(output_dir, f"{filename}_jpeg_q{jpeg_quality}.jpg")cv2.imwrite(jpeg_output, img, [int(cv2.IMWRITE_JPEG_QUALITY), jpeg_quality])# 3. PNG无损压缩(压缩级别0-9,越高压缩率越好、速度越慢)png_level = 6  # 默认推荐级别png_output = os.path.join(output_dir, f"{filename}_png_l{png_level}.png")cv2.imwrite(png_output, img, [int(cv2.IMWRITE_PNG_COMPRESSION), png_level])# 4. WebP压缩(支持有损/无损,质量0-100)webp_quality = 70  # 有损模式webp_output = os.path.join(output_dir, f"{filename}_webp_q{webp_quality}.webp")# OpenCV部分版本不直接支持WebP,用Pillow兼容img_rgb = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)  # BGR转RGBImage.fromarray(img_rgb).save(webp_output, "WebP", quality=webp_quality)# 5. 计算并打印各文件体积print("压缩完成,文件体积对比:")for file in [jpeg_output, png_output, webp_output]:size = os.path.getsize(file) / 1024  # 转KBprint(f"{os.path.basename(file)}: {size:.2f} KB")# 运行示例
if __name__ == "__main__":input_image = "test.jpg"  # 你的输入图像路径output_folder = "compressed_images"image_compress_demo(input_image, output_folder)

关键说明

  • JPEG:IMWRITE_JPEG_QUALITY控制质量,推荐50-80,适合照片;
  • PNG:IMWRITE_PNG_COMPRESSION控制压缩级别,无损模式下,级别越高体积越小(但速度变慢);
  • WebP:相同画质下,体积比JPEG小30%左右,比PNG小50%,是网页/APP的优选。

五、实战2:简化版哈夫曼编码(无损压缩核心)

哈夫曼编码是无损压缩的基础,核心是“频率高的字符用短码”。下面实现一个简化版,用于理解原理(实际工程中用现成库即可)。

代码实现:哈夫曼编码与解码

import heapq
from collections import defaultdict# 1. 构建哈夫曼树节点
class HuffmanNode:def __init__(self, value, freq):self.value = value  # 像素值(或字符)self.freq = freq    # 出现频率self.left = None    # 左子树self.right = None   # 右子树# 重载比较运算符,用于堆排序def __lt__(self, other):return self.freq < other.freq# 2. 生成哈夫曼编码表
def build_huffman_code(img_flat):# 统计每个像素值的频率freq_dict = defaultdict(int)for pixel in img_flat:freq_dict[pixel] += 1# 构建最小堆(优先队列)heap = [HuffmanNode(val, freq) for val, freq in freq_dict.items()]heapq.heapify(heap)# 合并节点构建哈夫曼树while len(heap) > 1:left = heapq.heappop(heap)right = heapq.heappop(heap)# 新节点频率为左右子节点之和,值设为None(内部节点)merged = HuffmanNode(None, left.freq + right.freq)merged.left = leftmerged.right = rightheapq.heappush(heap, merged)# 生成编码表(递归遍历哈夫曼树)huffman_code = {}def traverse(node, current_code):if node is None:return# 叶子节点(对应像素值)if node.value is not None:huffman_code[node.value] = current_codereturntraverse(node.left, current_code + "0")traverse(node.right, current_code + "1")root = heapq.heappop(heap)traverse(root, "")return huffman_code# 3. 编码与解码
def huffman_compress(img_flat, huffman_code):# 编码:将像素序列转为二进制字符串compressed_bits = "".join([huffman_code[pixel] for pixel in img_flat])return compressed_bitsdef huffman_decompress(compressed_bits, huffman_code):# 解码:二进制字符串转回像素序列reverse_code = {code: val for val, code in huffman_code.items()}current_code = ""decompressed = []for bit in compressed_bits:current_code += bitif current_code in reverse_code:decompressed.append(reverse_code[current_code])current_code = ""return decompressed# 4. 测试:用灰度图验证
if __name__ == "__main__":# 读取灰度图(简化计算,单通道像素值0-255)img = cv2.imread("test.jpg", cv2.IMREAD_GRAYSCALE)img_flat = img.flatten()  # 转为一维数组(方便处理)# 编码huffman_code = build_huffman_code(img_flat)compressed = huffman_compress(img_flat, huffman_code)# 解码decompressed = huffman_decompress(compressed, huffman_code)decompressed_img = np.array(decompressed).reshape(img.shape)# 验证:解压后与原图是否一致(无损)is_same = np.array_equal(img, decompressed_img)print(f"解压后与原图一致?{is_same}")# 计算压缩比(原始字节数 vs 压缩后比特数/8)original_size = len(img_flat)  # 每个像素1字节(灰度图)compressed_size = len(compressed) / 8compression_ratio = compressed_size / original_sizeprint(f"哈夫曼压缩比:{compression_ratio:.2f}(越小越好)")

关键说明

  • 实际工程中,哈夫曼编码会结合其他算法(如LZ77)使用(比如PNG的DEFLATE算法);
  • 该简化版仅用于理解原理,未处理二进制对齐、文件存储等细节,生产环境建议用zlib等成熟库。

六、实战3:DCT变换可视化(JPEG核心原理)

JPEG的核心是DCT变换(离散余弦变换),将图像从“空间域”转为“频率域”,再通过量化丢弃高频信息(无损压缩)。下面用代码可视化这个过程。

代码实现:DCT变换+量化

import cv2
import numpy as np
import matplotlib.pyplot as pltdef dct_visualization(input_path):# 1. 读取灰度图并预处理(8×8分块,JPEG标准)img = cv2.imread(input_path, cv2.IMREAD_GRAYSCALE)img = img.astype(np.float32) - 128  # 减去128,使像素值范围为[-128, 127](优化DCT效果)# 2. 定义JPEG标准量化表(高频系数量化步长更大,更容易归0)jpeg_quant_matrix = np.array([[16, 11, 10, 16, 24, 40, 51, 61],[12, 12, 14, 19, 26, 58, 60, 55],[14, 13, 16, 24, 40, 57, 69, 56],[14, 17, 22, 29, 51, 87, 80, 62],[18, 22, 37, 56, 68, 109, 103, 77],[24, 35, 55, 64, 81, 104, 113, 92],[49, 64, 78, 87, 103, 121, 120, 101],[72, 92, 95, 98, 112, 100, 103, 99]], dtype=np.float32)# 3. 对每个8×8块执行DCT+量化h, w = img.shapedct_img = np.zeros_like(img)quant_img = np.zeros_like(img)for i in range(0, h, 8):for j in range(0, w, 8):# DCT变换(cv2.dct默认只处理单通道、浮点型)dct_block = cv2.dct(img[i:i+8, j:j+8])# 量化(核心:高频系数被大幅压缩)quant_block = np.round(dct_block / jpeg_quant_matrix)# 逆量化+逆DCT(恢复图像)idct_block = cv2.idct(quant_block * jpeg_quant_matrix)# 保存结果dct_img[i:i+8, j:j+8] = dct_blockquant_img[i:i+8, j:j+8] = idct_block + 128  # 加回128恢复原始亮度范围# 4. 可视化结果(原图 vs DCT频率图 vs 量化后恢复图)plt.figure(figsize=(15, 5))# 原图plt.subplot(1, 3, 1)plt.imshow(img + 128, cmap="gray")plt.title("原图")plt.axis("off")# DCT频率图(取对数增强可视化,高频系数值很小)plt.subplot(1, 3, 2)plt.imshow(np.log1p(np.abs(dct_img)), cmap="gray")plt.title("DCT频率域(亮处为低频)")plt.axis("off")# 量化后恢复图plt.subplot(1, 3, 3)plt.imshow(quant_img.astype(np.uint8), cmap="gray")plt.title("DCT+量化后恢复图")plt.axis("off")plt.tight_layout()plt.show()# 运行可视化
if __name__ == "__main__":dct_visualization("test.jpg")

关键说明

  • DCT变换后,矩阵左上角是低频系数(对应图像轮廓),右下角是高频系数(对应细节);
  • 量化过程中,高频系数被大的量化步长“抹平”(归0),这是JPEG有损压缩的核心;
  • 逆变换后,图像会有轻微损失,但肉眼几乎无法察觉(除非量化步长过大)。

七、实战4:压缩效果量化评估(PSNR+SSIM)

光看主观画质不够,用PSNR(峰值信噪比)和SSIM(结构相似性)客观评估压缩质量。

代码实现:计算PSNR和SSIM

import cv2
import numpy as npdef calculate_psnr(img1, img2):# PSNR:越高越好(>30dB肉眼无明显差异)mse = np.mean((img1.astype(np.float32) - img2.astype(np.float32)) ** 2)if mse == 0:return float("inf")  # 完全一致max_pixel = 255.0psnr = 20 * np.log10(max_pixel / np.sqrt(mse))return psnrdef calculate_ssim(img1, img2):# SSIM:越接近1越好(0-1)C1 = (0.01 * 255) ** 2C2 = (0.03 * 255) ** 2img1 = img1.astype(np.float32)img2 = img2.astype(np.float32)mu1 = cv2.GaussianBlur(img1, (11, 11), 1.5)mu2 = cv2.GaussianBlur(img2, (11, 11), 1.5)mu1_sq = mu1 ** 2mu2_sq = mu2 ** 2mu1_mu2 = mu1 * mu2sigma1_sq = cv2.GaussianBlur(img1 ** 2, (11, 11), 1.5) - mu1_sqsigma2_sq = cv2.GaussianBlur(img2 ** 2, (11, 11), 1.5) - mu2_sqsigma12 = cv2.GaussianBlur(img1 * img2, (11, 11), 1.5) - mu1_mu2ssim_map = ((2 * mu1_mu2 + C1) * (2 * sigma12 + C2)) / ((mu1_sq + mu2_sq + C1) * (sigma1_sq + sigma2_sq + C2))return np.mean(ssim_map)# 测试:对比JPEG压缩前后的效果
if __name__ == "__main__":img_original = cv2.imread("test.jpg")# 保存JPEG压缩图cv2.imwrite("compressed_test.jpg", img_original, [int(cv2.IMWRITE_JPEG_QUALITY), 50])img_compressed = cv2.imread("compressed_test.jpg")# 计算指标psnr = calculate_psnr(img_original, img_compressed)ssim = calculate_ssim(img_original, img_compressed)print(f"PSNR: {psnr:.2f} dB")print(f"SSIM: {ssim:.4f}")

关键说明

  • PSNR > 30dB:肉眼无法区分压缩图和原图;
  • SSIM > 0.9:结构相似性极高,压缩质量优秀;
  • 实际开发中,可根据这两个指标调整压缩参数(比如JPEG质量)。

八、总结与进阶建议

  1. 基础应用:开发中优先用OpenCV/Pillow的现成接口,调整压缩参数即可满足大部分需求;
  2. 原理理解:哈夫曼编码(无损)、DCT变换(有损)是核心,通过简化代码能快速掌握逻辑;
  3. 进阶方向:学习新一代编码(VVC/AVIF)、基于深度学习的图像压缩(如TensorFlow/PyTorch实现)。
http://www.dtcms.com/a/597576.html

相关文章:

  • 网站建设与管理实施方案企业建设网站怎么做账
  • 网站制作与网站建设实际报告文案发布平台
  • 开源项目合并新分支和本地修改指南
  • GJOI 11.5 题解
  • 网站建设是什么语言wan网站建设
  • 个人网站域名怎么起企业官方网站建设的流程
  • CTFHub Web进阶-Json Web Token通关5:修改签名算法
  • 华为OD机试 双机位A卷 - 上班之路 (JAVA Python C++ JS GO)
  • CEVA-DSP开发初识(一)
  • 峰均比降低技术(CFR)
  • 如何删除网站备案号房地产政策
  • 盐城网盐城网站建设站建设wordpress视频解析接口
  • 【CPKCOR-RA8D1】Home Assistant 物联网 ADC 电压温度计
  • STM32外设学习--DMA直接存储器读取(AD扫描程序,DMA搬运)--学习笔记。
  • 贵州网站开发制作公司开发公司各部门职责
  • FreeRTOS 学习:(十八)FreeRTOS 中断管理
  • 做外贸网站 怎么收钱池州哪里做网站
  • 介绍一下 机器人坐标转换的 RT 矩阵
  • 网站备案换公司吗盐山县网站建设
  • 为智能制造护航:SASE如何重塑制造业网络安全与连接
  • 品牌授权网站什么网站可以做软件有哪些东西
  • h5网站系统企业网站制作排名
  • ZeroNews 场景案例 | 结合小皮面板实现公网web服务发布
  • 本地的赣州网站建设电商网站支付接口
  • C# 记录类型(record)全面解析:从概念到最佳实践
  • 广西响应式网站制作怎么修改自己网站内容
  • 万网网站建设教程免费做网站的网站
  • Meta Omnilingual ASR:一个支持超1600种语言的语音识别系统解析
  • 9、prometheus-PromQL-3-偏移量修改器
  • 【题解】洛谷 P2471 [SCOI2007] 降雨量 [线段树 + 逻辑]