图像处理基础 - DCT 变换
回顾
我们学习的传统 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 比傅立叶好的地方是没有虚部,是无损变换。
例子
这里 lena 做 DCT 变换得到的频谱图频率集中在左上角,观察到低频信号对图像特征贡献较大;噪声当然会集中在高频(为滤去它创造了条件);考虑 (0, 0) 处是什么?这里是频率 u=0, v=0 对应的值,也就是信号的直流分量
图像压缩
DCT 变换得到了一组 DCT 系数,量化表试图把它映射到离散级别以减小数据精度;量化原理:
编码时:
- 给定量化表,其中每个元素是这里的量化步长
- 将 RGB 转换为 YUV
- DCT 变换
- DCT 系数和量化表逐元素相除,再四舍五入,这样将 DCT 映射到最接近的离散级别;我们可以对高频分量赋值较大的步长来保留更少高频成分
- 这会是一个很稀疏的矩阵,jpeg 可以节俭地编码他们
解码时:
- 反量化:将数据再乘上量化表
- 反 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