You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Python实现JPEG的8x8窗口DCT与量化后图像损坏问题求助

简易JPEG压缩流程(仅DCT+量化)失真问题解决

问题背景

尝试用Python实现不含霍夫曼编码的简易JPEG压缩,仅完成DCT(离散余弦变换)、量化及对应逆过程,但运行后量化图像损坏,逆处理后图像大部分变白、严重失真,测试图像为包含文字与图案的灰度图。

核心问题分析

  • DCT调用方式错误:scipy的dct默认是一维变换,直接对8x8矩阵调用仅处理每行,且未做正交归一化,不符合JPEG标准DCT要求。
  • 量化数据保存丢失精度:量化结果为浮点数(或应取整的整数),直接用图像格式保存会被当作8位灰度值截断/错误映射,丢失原始数据。
  • 缺少JPEG预处理步骤:未将像素值从[0,255]转换到[-128,127],导致DCT后直流分量过大,量化数据范围异常。
  • 逆DCT未做正交归一化:逆变换缺少norm='ortho'参数,无法正确还原图像。

修正后的完整代码

import cv2 as cv
from scipy.fftpack import dct, idct
import numpy as np
from PIL import Image

# ---------------------- 压缩流程 ----------------------
image = cv.imread("test.png")
gray = cv.cvtColor(image, cv.COLOR_BGR2GRAY)
# 预处理:将像素值从[0,255]转为[-128,127],符合JPEG标准
arr = np.asarray(gray, float) - 128
image_quant = np.zeros_like(arr)

# JPEG标准50%质量量化矩阵
qm = 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]
])

ws = 8  # 8x8分块大小

def quantize(mat, qm):
    # 量化后取整,符合JPEG整数量化标准
    return np.round(mat / qm)

# 分块执行DCT与量化
for r in range(0, arr.shape[0] - ws + 1, ws):
    for c in range(0, arr.shape[1] - ws + 1, ws):
        window = arr[r:r+ws, c:c+ws]
        # 二维正交DCT变换(转置实现二维处理)
        dct_block = dct(dct(window.T, norm='ortho').T, norm='ortho')
        image_quant[r:r+ws, c:c+ws] = quantize(dct_block, qm)

# 用NPY格式保存量化数据,避免图像格式的精度丢失
np.save("quantized_data.npy", image_quant)

# ---------------------- 解压缩流程 ----------------------
# 从NPY加载原始量化数据,而非读取损坏的PNG图像
image_quant = np.load("quantized_data.npy")
image_dequant = np.zeros_like(image_quant)

def dequantize(mat, qm):
    return mat * qm

# 分块执行反量化与逆DCT
for r in range(0, image_quant.shape[0] - ws + 1, ws):
    for c in range(0, image_quant.shape[1] - ws + 1, ws):
        window = image_quant[r:r+ws, c:c+ws]
        dequant_block = dequantize(window, qm)
        # 二维正交逆DCT变换
        idct_block = idct(idct(dequant_block.T, norm='ortho').T, norm='ortho')
        image_dequant[r:r+ws, c:c+ws] = idct_block

# 后处理:将像素值转回[0,255],并截断超出范围的数值
image_dequant = np.clip(image_dequant + 128, 0, 255).astype(np.uint8)

# 保存并显示还原后的图像
result = Image.fromarray(image_dequant)
result.save('test_dequant_dct.png')
result.show()

关键修正说明

  1. 像素值偏移:添加arr = np.asarray(gray, float) - 128,将像素映射到JPEG标准的[-128,127]范围,避免DCT直流分量过大。
  2. 二维正交DCT:通过转置实现二维正交变换,添加norm='ortho'保证变换的可逆性。
  3. 量化数据保存:用np.save保存量化数组,避免图像格式的精度损失。
  4. 量化取整:量化时执行np.round,符合JPEG整数量化的标准做法。
  5. 后处理截断:还原后将像素值加128并截断到[0,255],确保输出为合法的8位灰度图像。

内容的提问来源于stack exchange,提问作者markman8

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.08 22:45:38