DCT变换与图像压缩、去燥

May 26,2018   4820 words   18 min


1.DCT变换简介

所谓DCT变换,全称是Discrete Cosine Transform,中文名称是离散余弦变换。 是与傅里叶变换相关的一种变换,它类似于离散傅里叶变换(DFT for Discrete Fourier Transform),但是只使用实数。 离散傅里叶变换需要进行复数运算,尽管有FFT可以提高运算速度,但在图像编码、特别是在实时处理中非常不便。离散傅里叶变换在实际的图像通信系统中很少使用,但它具有理论的指导意义。根据离散傅里叶变换的性质,实偶函数的傅里叶变换只含实的余弦项,因此构造了一种实数域的变换——离散余弦变换(DCT)。通过研究发现,DCT除了具有一般的正交变换性质外,其变换阵的基向量很近似于Toeplitz矩阵的特征向量,后者体现了人类的语言、图像信号的相关特性。因此,在对语音、图像信号变换的确定的变换矩阵正交变换中,DCT变换被认为是一种准最佳变换。在近年颁布的一系列视频压缩编码的国际标准建议中,都把DCT作为其中的一个基本处理模块。

离散余弦变换,经常被信号处理和图像处理使用,用于对信号和图像(包括静止图像和运动图像)进行有损数据压缩。这是由于离散余弦变换具有很强的”能量集中”特性:大多数的自然信号(包括声音和图像)的能量都集中在离散余弦变换后的低频部分,而且当信号具有接近马尔科夫过程(Markov processes)的统计特性时,离散余弦变换的去相关性接近于K-L变换(Karhunen-Loève 变换–它具有最优的去相关性)的性能。

2.DCT变换原理

(1)一维DCT变换

一维DCT一共有8种形式,下面这种为最实用的公式:

\[F(u)=c(u)\sum_{i=0}^{N-1}f(i)cos(\frac{(2i+1)\pi}{2N}u)\]

其中,c(u)如下:

\[c(u)=\left\{\begin{matrix} \sqrt{\frac{1}{N}}\quad u=0\\ \sqrt{\frac{2}{N}}\quad u\neq 0 \end{matrix}\right.\]

N是一维数据的元素总数,c(u)系数使得DCT变换矩阵成为正交矩阵,正交特性在二维DCT变换中更能体现其优势。

(2)二维DCT变换(FDCT)

公式如下。

\[F(u,v)=c(u)c(v)\sum_{i=0}^{N-1}\sum_{j=0}^{N-1}f(i,j)cos[\frac{(2i+1)\pi}{2N}u]cos[\frac{(2j+1)\pi}{2N}v]\]

其中c(u)、c(v)如下。

\[c(u)=\left\{\begin{matrix} \sqrt{\frac{1}{N}}\quad u=0\\ \sqrt{\frac{2}{N}}\quad u\neq 0 \end{matrix}\right., c(v)=\left\{\begin{matrix} \sqrt{\frac{1}{N}}\quad v=0\\ \sqrt{\frac{2}{N}}\quad v\neq 0 \end{matrix}\right.\]

如果将上面的式子改写一下,可以发现二维DCT变换其实是在一维DCT变换的基础上,再做一次一维DCT变换。二维DCT变换的复杂度达到O(n^4),所以进行DCT变换的矩阵不宜过大。在实际处理图片的过程中,需要先把矩阵分块,一般分为8×8或16×16大小,这样DCT变换不至于耗费过多的时间。

(3)DCT反变换(IDCT)
\[f(i,j)=\sum_{u=0}^{N-1}\sum_{v=0}^{N-1}c(u)c(v)F(u,v)cos[\frac{(2i+1)\pi}{2N}u]cos[\frac{(2j+1)\pi}{2N}v]\]

其中c(u)、c(v)与上面说到的二维变换相同。

3.DCT变换与图像压缩

DCT在图像领域的应用之一就是图像压缩,用于对信号和图像(包括静止图像和运动图像)进行有损压缩。在压缩算法中,现将输入图像划分为8×8或16×16的图像块,对每个图像块作DCT变换;然后舍弃高频的系数,并对余下的系数进行量化以进一步减少数据量;最后使用无失真编码来完成压缩任务。解压缩时首先对每个图像块作DCT反变换,然后将图像拼接成一副完整的图像。

大多数自然信号(包括声音和图像)的能量都集中在余弦变换后的低频部分。由于人眼对于细节信息不是很敏感,因此信息含量更少的高频部分可以直接去掉,从而在后续的压缩操作中获得较高的压缩比。

评价一幅图像压缩后与原图的差异,从数学角度上而言就是计算均方差(Mean Square Error,MSE)。MSE越小,压缩后的图像与原图越相近,认为质量越好。想要获得最小均方差的转换,可以使用K-L转换。K-L转换(Karhunen-Loève Transform)是建立在统计特性基础上的一种转换,它是均方差意义下的最佳转换,因此在资料压缩技术中占有重要的地位。K-L转换是对输入的向量x,做一个正交变换,使得输出的向量得以去除数据的相关性。但一般效果好的方法都伴随着计算量的增加,K-L转换也不例外。因此DCT变换应运而生。“变换”就是换个角度看问题,将函数从“时域”投影到“频域”,于是就能从频率的角度分析问题。

对二维灰度图像进行DCT变换,就能得到图像的频谱图:低阶(变化幅度小)的部分反映在DCT的左上方,高阶(变化幅度大)的部分反映在DCT的右下方。由于人眼对高阶部分不敏感,依靠低阶部分就能基本识别出图像内容,所以JPEG进行压缩的时候,基本上只存储DCT变换后的左上部分,而右下部分则直接丢弃。

DCT跟DFT都是将二维空间图像转换到频域,为什么JPEG会采用DCT而非DFT呢?一个重要的原因,是子图之间的连续性。DFT变换没有考虑到子图之间的边界,整幅大图变换出来会得到许多独立的波形,波形不连续容易导致Gibbs phenomenon;而DCT得到子图间的波形则是连续的。

(1)压缩步骤
a.图像分块

一般情况下,将图像分成8×8的许多小块用于后续处理,每块64个像素。

b.转换颜色系统

将表示像素的RGB系统转换成YUV系统(Y-明度,U-色调,V-饱和度)。

c.进行DCT变换

对每个小块进行DCT离散余弦变换。由第二部分所介绍的公式,公式可以具体写成如下形式。这里N=8,因为二维DCT变换可以看做是由两个一维DCT合成的,而N表示的意义是一维数据的长度,图像小块每一维都是8,所以N=8。

\[F(u,v)=c(u)c(v)\sum_{i=0}^{7}\sum_{j=0}^{7}f(i,j)cos[\frac{(2i+1)\pi}{16}u]cos[\frac{(2j+1)\pi}{16}v]\]

f(i,j)是图像块中(i,j)位置上的灰度值,F(u,v)是变换后的(u,v)位置上的值。0<=u, v<8。c(u)、c(v)如下。

\[c(u)=\left\{\begin{matrix} \sqrt{\frac{1}{8}}\quad u=0\\ \sqrt{\frac{2}{8}}\quad u\neq 0 \end{matrix}\right., c(v)=\left\{\begin{matrix} \sqrt{\frac{1}{8}}\quad v=0\\ \sqrt{\frac{2}{8}}\quad v\neq 0 \end{matrix}\right.\]

最后会输出一个大小同样是8×8的矩阵。F(0,0)称为直流系数。

d.量化

将DCT变换后的临时结果,除以各自量化步长并四舍五入后取整,得到量化系数。 经过DCT后,左上方都是大数值,右下方都是小数值。 在术语里,左上方称为低频数据,右下方称为高频数据。 JPEG系统分别规定了亮度分量和色度分量的量化表,色度分量相应的量化步长比亮度分量大。

直流系数:通常相邻8*8图象块的DC分量很接近,因此JPEG对量化后的直流分量采用无失真DPCM编码。通常JPEG要保存所需比特数和实际差值。

交流系数:经过量化后,AC分量出现较多的0。JPEG采用对0系数的行程长度编码。而对非0值,则要保存所需数和实际值。

ZIG-ZAG排序:为使连续的0个数增多,采用Z形编码。

JPEG压缩是有损失的,DCT的输出结果并不是完全等于输入。 更多DCT应用于图像压缩的步骤与例子可以参考这篇博客,写得很详细了。

4.DCT变换与图像降噪

利用DCT进行降噪的主要思路其实比较简单,对图像进行DCT变换,然后在变换后的频率域通过设置阈值,过滤掉高频部分(通常认为噪声都是高频部分),然后再用DCT反变换变换为图像。这样就完成了基于DCT变换的去燥。相关论文网页是这个。 这里不仔细讨论具体的算法原理,而是通过使用OpenCV提供的函数实现图像的降噪。在OpenCV中DCT降噪放在了xphoto模块中,这个模块包含了一些与相片处理有关的函数,调节白平衡、相片修复等等。这个模块是contrib里的,所以要想使用需要先安装contrib版的OpenCV。如何安装,在这篇博客里也已经介绍了。下面直接贴出使用的代码。

# coding=utf-8
import cv2
import numpy as np


def denoising(img, sigma, psize=8):
    img_out = np.zeros(img.shape, np.uint8)
    # @param src source image
    # @param dst destination image
    # @param sigma expected noise standard deviation
    # @param psize size of block side where dct is computed
    cv2.xphoto.dctDenoising(img, img_out, sigma, psize)
    return img_out


img = cv2.imread("file.png")
img_denoise = denoising(img, 45, 8)
cv2.imshow("img", img)
cv2.imshow("denoise", img_denoise)
cv2.waitKey(0)

运行结果如下所示,这是一幅有很大噪声的遥感影像,直接在这样的影像上提取特征等十分困难而且很容易受到噪声干扰。因此需要先对影像进行预处理,如灰度拉伸、去噪等操作。经过DCT变换去噪后,效果还是不错的,边缘也没有明显变得模糊。 当然还有其它许多去噪的手段,如先采用平滑滤波(如双边滤波),然后再锐化的手段,或者采用腐蚀的手段去噪声等等。相比之下,DCT在这个场景下的效果是最好的。

5.参考资料

  • https://baike.baidu.com/item/%E7%A6%BB%E6%95%A3%E4%BD%99%E5%BC%A6%E5%8F%98%E6%8D%A2/7118270
  • https://zhuanlan.zhihu.com/p/33845296
  • https://blog.csdn.net/qq_20613513/article/details/78744101
  • https://feichashao.com/image_dct/
  • https://blog.csdn.net/newchenxf/article/details/51719597/
  • https://www.cnblogs.com/helloforworld/p/5283641.html
  • https://www.cnblogs.com/Imageshop/p/4965192.html

本文作者原创,未经许可不得转载,谢谢配合

返回顶部