开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第27天,点击查看活动详情
前言
在离散余弦变换一节中,我们已经学习了如何实现 2D-DCT 和 IDCT,在本节中,我们将使用这两种技术执行图像压缩操作,完成 JPEG 格式图像压缩。JPEG 压缩基本操作步骤如下所示:
- 将图像分割为
8x8的图像块 - 在每个图像块上(从左到右,从上到下)应用
DCT - 对每个图像块使用量化压缩
- 将压缩后的图像块存储在较小的空间中,从而达到较高的压缩效果
下图显示了在 8x8 图像块的 64 个 DCT 基函数,对于图像中的每个块,64 个 DCT 系数是基函数的线性组合以生成图像块系数:
接下来,我们将实现简化版 JPEG 压缩,以确保仅需少数 DCT 系数可以表示图像,而不会导致图像质量有过多的视觉失真。在本节中,我们将直接在输入图像像素上计算 8x8 图像块的 DCT。
此外,我们将使用全局阈值来清零大部分 DCT 系数,而不是通过用量化矩阵(对于给定的压缩级别)分割 DCT 矩阵来进行量化。
最后,我们将跳过霍夫曼 (Huffman) 编码,并存储(压缩)/读取(解压缩)图像。
JPEG 压缩
(1) 导入所需库,读取输入图像,并将图像分割为 8x8 的 DCT 块:
import scipy.fftpack as fp
from skimage.io import imread
from skimage.color import rgb2gray, gray2rgb
from skimage.draw import rectangle_perimeter
import numpy as np
import matplotlib.pylab as plt
from mpl_toolkits.mplot3d import Axes3D # noqa: F401 unused import
from mpl_toolkits.axes_grid1 import make_axes_locatable
from matplotlib.ticker import LinearLocator, FormatStrFormatter
from scipy.fftpack import dct, idct
def dct2(a):
return dct(dct(a, axis=0, norm='ortho'), axis=1, norm='ortho')
def idct2(a):
return idct(idct(a, axis=0, norm='ortho'), axis=1, norm='ortho')
im = rgb2gray(imread('1.png'))
dct_coeffs = np.zeros(im.shape)
for i in range(0, im.shape[0], 8):
for j in range(0, im.shape[1], 8):
dct_coeffs[i:(i+8),j:(j+8)] = dct2(im[i:(i+8),j:(j+8)])
(2) 从图像中提取一个图像块,并显示该块的 DCT 系数。
index = 112
plt.figure(figsize=(10,6))
plt.gray()
plt.subplot(121), plt.imshow(im[index:index+8,index:index+8]), plt.title( "An 8x8 Image block", size=10)
plt.subplot(122), plt.imshow(dct_coeffs[index:index+8,index:index+8], vmax= np.max(dct_coeffs)*0.01, vmin = 0, extent=[0, np.pi, np.pi, 0])
plt.title("An 8x8 DCT block", size=10)
plt.show()
输出结果如下所示:
(3) 使用全局阈值对 DCT 系数进行阈值设置,将值小于阈值乘以最大系数值的所有 DCT 系数置为 0,打印应用阈值后剩余的非零 DCT系数的百分比。
thresh = 0.03
dct_thresh = dct_coeffs * (abs(dct_coeffs) > (thresh*np.max(dct_coeffs)))
percent_nonzeros = np.sum( dct_thresh != 0.0 ) / (im.shape[0]*im.shape[1])
print ("Keeping only {}% of the DCT coefficients".format(percent_nonzeros*100.0))
# Keeping only 3.45875% of the DCT coefficients
从以上输出可以看出,应用阈值后,只有约 3.5% 的 DCT 系数为非零值。
(4) 可视化原始 DCT 系数以及阈值处理后获得的系数:
plt.figure(figsize=(12,7))
plt.gray()
plt.subplot(121), plt.imshow(dct_coeffs,cmap='gray',vmax = np.max(dct_coeffs)*0.01,vmin = 0), plt.axis('off')
plt.title("8x8 DCTs of the image", size=10)
plt.subplot(122), plt.imshow(dct_thresh, vmax = np.max(dct_coeffs)*0.01, vmin = 0), plt.axis('off')
plt.title("Thresholded 8x8 DCTs of the image", size=10)
plt.tight_layout()
plt.show()
(5) 最后,仅使用 3.5% 非零 DCT 系数,使用 8x8 块 IDCT 重建压缩图像:
im_out = np.zeros(im.shape)
for i in range(0, im.shape[0], 8):
for j in range(0, im.shape[1], 8):
im_out[i:(i+8),j:(j+8)] = idct2( dct_thresh[i:(i+8),j:(j+8)])
(6) 绘制原始图像和 DCT 压缩图像,并对比原始图像和重建(压缩)图像:
plt.figure(figsize=(15,7))
plt.gray()
plt.subplot(121), plt.imshow(im), plt.axis('off'), plt.title('original image', size=10)
plt.subplot(122), plt.imshow(im_out), plt.axis('off'), plt.title('DCT compressed image', size=10)
plt.tight_layout()
plt.show()
从以上图像可以看到,仅通过有效地存储大约 3.5% 的 DCT 系数,我们便可以高图像质量的重建图像,而不会过多的降低图像质量上,从而实现较高压缩比,并计算 MSE/PSNR 以进行定量测量图像间的差距。