o365邮箱登录-365bet提款速度-外勤365官方网站

【图像处理基石】图像压缩有哪些经典算法?

【图像处理基石】图像压缩有哪些经典算法?

图像压缩是减少图像数据量的技术,主要分为无损压缩 (不丢失信息)和有损压缩(允许一定信息损失以换取更高压缩率)。以下是几种经典算法及其Python实现:

一、经典图像压缩算法简介

行程长度编码(RLE)

无损压缩,适用于存在连续重复像素的图像(如线条图、图标)。原理是记录"像素值+连续出现次数"替代重复像素。

哈夫曼编码(Huffman Coding)

无损压缩,基于像素值出现频率构建变长编码(高频值用短码),广泛用于JPEG、PNG等格式。

离散余弦变换(DCT)

有损压缩,JPEG核心算法。将图像分块后转换到频率域,保留低频分量(视觉重要),丢弃高频分量(细节)。

小波变换(Wavelet Transform)

有损/无损均可,JPEG 2000采用。相比DCT能更好保留边缘信息,压缩率更高。

二、Python实现示例

1. 行程长度编码(RLE)

适用于简单图像(如二值图),实现简单且压缩效果直观。

python

复制代码

import numpy as np

from PIL import Image

def rle_compress(image):

"""将灰度图像压缩为RLE编码(列表形式:[(值, 长度), ...])"""

# 将二维图像转为一维数组

flat = image.flatten()

if len(flat) == 0:

return []

compressed = []

current_val = flat[0]

count = 1

for val in flat[1:]:

if val == current_val:

count += 1

else:

compressed.append((current_val, count))

current_val = val

count = 1

# 处理最后一组

compressed.append((current_val, count))

return compressed

def rle_decompress(compressed, shape):

"""将RLE编码解压为图像"""

flat = []

for val, count in compressed:

flat.extend([val] * count)

# 恢复为原始形状

return np.array(flat).reshape(shape)

# 测试

if __name__ == "__main__":

# 读取图像并转为灰度图

img = Image.open("test_image.png").convert("L") # 替换为你的图像路径

img_array = np.array(img)

print("原始图像形状:", img_array.shape)

print("原始数据量:", img_array.size, "像素")

# 压缩

compressed = rle_compress(img_array)

print("RLE压缩后数据量:", len(compressed), "组")

# 解压

decompressed = rle_decompress(compressed, img_array.shape)

# 验证是否完全恢复(无损压缩)

print("解压后与原始是否一致:", np.array_equal(img_array, decompressed))

# 保存解压后的图像

Image.fromarray(decompressed).save("rle_decompressed.png")

2. 哈夫曼编码(Huffman Coding)

基于频率的变长编码,适合像素值分布不均匀的图像。

python

复制代码

import numpy as np

from PIL import Image

import heapq

from collections import defaultdict

# 构建哈夫曼树节点

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

# 构建哈夫曼树

def build_huffman_tree(freq_dict):

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 = left

merged.right = right

heapq.heappush(heap, merged)

return heap[0] if heap else None

# 生成哈夫曼编码表

def generate_code_table(root):

code_table = {}

def traverse(node, current_code=""):

if node is None:

return

# 叶子节点(有像素值)

if node.value is not None:

code_table[node.value] = current_code if current_code else "0"

return

# 左子树加"0",右子树加"1"

traverse(node.left, current_code + "0")

traverse(node.right, current_code + "1")

traverse(root)

return code_table

# 哈夫曼压缩

def huffman_compress(image):

flat = image.flatten()

# 统计频率

freq_dict = defaultdict(int)

for val in flat:

freq_dict[val] += 1

# 构建树和编码表

root = build_huffman_tree(freq_dict)

code_table = generate_code_table(root)

# 编码数据(转为二进制字符串)

encoded_bits = "".join([code_table[val] for val in flat])

return encoded_bits, code_table, image.shape

# 哈夫曼解压

def huffman_decompress(encoded_bits, code_table, shape):

# 反转编码表(编码->值)

reverse_table = {code: val for val, code in code_table.items()}

current_code = ""

flat = []

for bit in encoded_bits:

current_code += bit

if current_code in reverse_table:

flat.append(reverse_table[current_code])

current_code = ""

return np.array(flat).reshape(shape)

# 测试

if __name__ == "__main__":

img = Image.open("test_image.png").convert("L")

img_array = np.array(img)

print("原始图像形状:", img_array.shape)

print("原始数据量:", img_array.size * 8, "比特(假设8位灰度)")

# 压缩

encoded_bits, code_table, shape = huffman_compress(img_array)

print("哈夫曼压缩后数据量:", len(encoded_bits), "比特")

print("压缩率:", len(encoded_bits)/(img_array.size * 8))

# 解压

decompressed = huffman_decompress(encoded_bits, code_table, shape)

# 验证无损性

print("解压后与原始是否一致:", np.array_equal(img_array, decompressed))

Image.fromarray(decompressed).save("huffman_decompressed.png")

3. 离散余弦变换(DCT)

JPEG核心算法,有损压缩,通过丢弃高频分量减少数据量。

python

复制代码

import numpy as np

from PIL import Image

from scipy.fftpack import dct, idct

# JPEG标准亮度量化矩阵(用于有损压缩)

QUANTIZATION_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)

def dct_compress(image, quant_matrix=QUANTIZATION_MATRIX):

"""DCT压缩:分块->DCT->量化"""

h, w = image.shape

# 确保尺寸是8的倍数(JPEG分块大小为8x8)

h_padded = ((h + 7) // 8) * 8

w_padded = ((w + 7) // 8) * 8

padded = np.pad(image, ((0, h_padded - h), (0, w_padded - w)), mode='constant')

compressed = np.zeros_like(padded, dtype=np.int32)

# 对每个8x8块进行处理

for i in range(0, h_padded, 8):

for j in range(0, w_padded, 8):

block = padded[i:i+8, j:j+8]

# 转换为[-128, 127](DCT通常基于零均值)

block_shifted = block - 128

# 2D DCT变换

dct_block = dct(dct(block_shifted, axis=0, norm='ortho'), axis=1, norm='ortho')

# 量化(除以量化矩阵并取整,核心压缩步骤)

quant_block = np.round(dct_block / quant_matrix).astype(np.int32)

compressed[i:i+8, j:j+8] = quant_block

return compressed, (h, w) # 保留原始尺寸用于解压

def dct_decompress(compressed, original_shape, quant_matrix=QUANTIZATION_MATRIX):

"""DCT解压:反量化->IDCT->拼接"""

h_padded, w_padded = compressed.shape

h, w = original_shape

decompressed = np.zeros_like(compressed, dtype=np.float32)

# 对每个8x8块处理

for i in range(0, h_padded, 8):

for j in range(0, w_padded, 8):

quant_block = compressed[i:i+8, j:j+8]

# 反量化

dct_block = quant_block * quant_matrix

# 逆DCT变换

block_shifted = idct(idct(dct_block, axis=0, norm='ortho'), axis=1, norm='ortho')

# 恢复到[0, 255]

block = np.round(block_shifted + 128).clip(0, 255).astype(np.uint8)

decompressed[i:i+8, j:j+8] = block

# 裁剪回原始尺寸

return decompressed[:h, :w].astype(np.uint8)

# 测试

if __name__ == "__main__":

img = Image.open("test_image.png").convert("L")

img_array = np.array(img)

print("原始图像形状:", img_array.shape)

# 压缩

compressed, original_shape = dct_compress(img_array)

# 解压(会有损失)

decompressed = dct_decompress(compressed, original_shape)

# 保存结果

Image.fromarray(decompressed).save("dct_decompressed.png")

print("DCT压缩解压完成(有损压缩,可能有画质损失)")

三、算法对比与应用场景

算法

类型

压缩率

适用场景

典型应用

RLE

无损

连续重复像素多的图像(图标)

BMP、Fax格式

哈夫曼编码

无损

像素分布不均匀的图像

JPEG、PNG、ZIP

DCT

有损

自然图像(人眼对细节不敏感)

JPEG

小波变换

有损/无损

需要高压缩率+保留细节的场景

JPEG 2000、医学图像

以上实现均为简化版本,实际工业级压缩(如JPEG)会结合多种算法(如DCT+哈夫曼编码)并优化细节(如色彩空间转换、熵编码)。

相关推荐
外国也有海参,可是外国人为什么不吃海参?
365bet提款速度

外国也有海参,可是外国人为什么不吃海参?

📅 12-21 🔥 686
逗女友开心笑话
外勤365官方网站

逗女友开心笑话

📅 09-16 🔥 599
肾切除术(肾脏切除)
o365邮箱登录

肾切除术(肾脏切除)

📅 07-27 🔥 995
弓字旁的字有哪些
外勤365官方网站

弓字旁的字有哪些

📅 10-04 🔥 167