图像处理基础 - DCT 变换
#data-science #图像处理基础和传统 CV 4

回顾

我们学习的传统 CV 方法几乎都集中于使用卷积:

  • 直方图提取
  • Canny Edge Detection
  • Harris Corner Detection
  • 高斯模糊
  • LoG
  • SIFT
  • HOG

这一节我们还要简单接触一种基于频域的图像处理方法:离散余弦变换(Discrete cosine transform, DCT),它主要适合进行图像压缩、去噪、增强算法,音频信号处理,因为我们通过 DCT 变换能掏出图像里更重要的信息。

定义

我们已经在 Classic Vision I 里见过傅里叶变换,我们也知道它将信号从时域变换到频域;DCT 是特殊的 DFT,只有实数部分,是对离散信号进行偶延拓再作傅里叶变换得到的;图像中用到的二维 DCT 将信号从空间域变换到频域:

\begin{align*} {X}(k_{1}, k_{2}) &= \frac{2}{\sqrt{N_{1}, N_{2}} } \sum_{n_{1}=0}^{N_{1}-1} \sum_{n_{2}=0}^{N_{2}-1} x[n_{1}, n_{2}] \\ & \cdot a_{k_{1}} a_{k_{2}} \mathrm{cos} [k_{1} \frac{2 \pi}{N_{1}} (n_{1} + \frac{1}{2}) ] \mathrm{cos} [k_{2} \frac{2 \pi}{N_{2}} (n_{2} + \frac{1}{2}) ] \end{align*}

DCT 比傅立叶好的地方是没有虚部,是无损变换。

例子

image-JQOS.png
这里 lena 做 DCT 变换得到的频谱图频率集中在左上角,观察到低频信号对图像特征贡献较大;噪声当然会集中在高频(为滤去它创造了条件);考虑 (0, 0) 处是什么?这里是频率 u=0, v=0 对应的值,也就是信号的直流分量

图像压缩

DCT 变换得到了一组 DCT 系数,量化表试图把它映射到离散级别以减小数据精度;量化原理:

编码时:

  1. 给定量化表,其中每个元素是这里的量化步长
  2. 将 RGB 转换为 YUV
  3. DCT 变换
  4. DCT 系数和量化表逐元素相除,再四舍五入,这样将 DCT 映射到最接近的离散级别;我们可以对高频分量赋值较大的步长来保留更少高频成分
  5. 这会是一个很稀疏的矩阵,jpeg 可以节俭地编码他们

解码时:

  1. 反量化:将数据再乘上量化表
  2. 反 DCT 变换

实现

import numpy as np
import cv2
from matplotlib import pyplot as plt

class dct:
    def __init__(self):
        self.luminance_quantization_table = 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.int32)
        self.chrominance_quantization_table = np.array([
            [17, 18, 24, 47, 99, 99, 99, 99],
            [18, 21, 26, 66, 99, 99, 99, 99],
            [24, 26, 56, 99, 99, 99, 99, 99],
            [47, 66, 99, 99, 99, 99, 99, 99],
            [99, 99, 99, 99, 99, 99, 99, 99],
            [99, 99, 99, 99, 99, 99, 99, 99],
            [99, 99, 99, 99, 99, 99, 99, 99],
            [99, 99, 99, 99, 99, 99, 99, 99]
        ], dtype=np.int32)
    
    def encode(self, img, rho):
        img = np.float32(img)
        y = img[:, :, 0]
        u = img[:, :, 1]
        v = img[:, :, 2]
        y -= 128
        u -= 128
        v -= 128
        y_dct = cv2.dct(y)
        u_dct = cv2.dct(u)
        v_dct = cv2.dct(v)

        lq = cv2.resize(self.luminance_quantization_table * rho,
                        y.shape,
                        interpolation=cv2.INTER_NEAREST)
        cq = cv2.resize(self.chrominance_quantization_table * rho,
                        y.shape,
                        interpolation=cv2.INTER_NEAREST)
        y_dct /= lq
        u_dct /= cq
        v_dct /= cq

        y_dct = np.round(y_dct)
        u_dct = np.round(u_dct)
        v_dct = np.round(v_dct)
        return y_dct, u_dct, v_dct
    
    def decode(self, y, u, v, rho):
        lq = cv2.resize(self.luminance_quantization_table * rho,
                        y.shape,
                        interpolation=cv2.INTER_NEAREST)
        cq = cv2.resize(self.chrominance_quantization_table * rho,
                        y.shape,
                        interpolation=cv2.INTER_NEAREST)
        y *= lq
        u *= cq
        v *= cq
        y = cv2.idct(y)
        u = cv2.idct(u)
        v = cv2.idct(v)
        y += 128
        u += 128
        v += 128
        img = np.dstack((y, u, v))
        return img

if __name__ == "__main__":
    img = cv2.imread('lena.jpg')
    img = cv2.resize(img, (8, 8))
    img_yuv = cv2.cvtColor(img, cv2.COLOR_BGR2YUV)
    model = dct()
    y, u, v = model.encode(img_yuv, 1)
    img = model.decode(y, u, v, 1)
    img = cv2.cvtColor(img, cv2.COLOR_YUV2RGB)
    
    plt.imshow(img)
    plt.show()

有 bug,但我还没有改好它

图像处理基础 - DCT 变换
http://localhost:8090/archives/Gl4IxUVp
作者
酱紫瑞
发布于
更新于
许可协议