1.简介

离散余弦变换(Discrete Cosine Transform),简称DCT变换,能够将空域的信号转换到频域上,在这个专题中就是将二维的像素值转换为二维的频率信号。

2.公式

2.1 二维离散余弦变换(2D DCT):

给定一个N×M(通常M等于N N表示水平长度)的输入矩阵F,其离散余弦变换结果G可通过以下公式计算得出:

一维离散余弦变换 python 二维离散余弦变换指令_算法



其中,

一维离散余弦变换 python 二维离散余弦变换指令_二维_02

为原始图像中像素点\((x,y)\)处的灰度值;

一维离散余弦变换 python 二维离散余弦变换指令_一维离散余弦变换 python_03

为变换后系数矩阵中的元素,表示不同频率分量的权值;


一维离散余弦变换 python 二维离散余弦变换指令_频域_04

为正交归一化系数,


一维离散余弦变换 python 二维离散余弦变换指令_算法_05

时,

一维离散余弦变换 python 二维离散余弦变换指令_一维离散余弦变换 python_06

,否则

一维离散余弦变换 python 二维离散余弦变换指令_二维_07


二维离散余弦逆变换(IDCT)的公式:

一维离散余弦变换 python 二维离散余弦变换指令_算法_08

这些公式描述了二维离散余弦变换和逆变换的原理,用于将输入图像从空域转换到频域,并通过调整系数来实现信号的压缩或特征提取。在实际应用中,可以使用相应的库函数或算法来进行离散余弦变换和逆变换操作。

2.2 公式解读

一维离散余弦变换 python 二维离散余弦变换指令_算法_09

2.3公式使用

由于cos函数的精度问题,使用上述公式进行正变换和逆变换得不到原来的矩阵,相关转换代码会附在文末。所以采用矩阵的方法对其进行转换。相关数学原理不在说明。

一维离散余弦变换 python 二维离散余弦变换指令_二维_10

以此方式在进行idct变换。

一维离散余弦变换 python 二维离散余弦变换指令_一维离散余弦变换 python_11

以此方式进行dct逆变换。

3.直观展示变化


初始8*8矩阵如下(表示图片的像素值):

一维离散余弦变换 python 二维离散余弦变换指令_二维_12

进行dct变换 得到频域信号 再进行dct逆变换得到原始图像想信号,发现并没有多大损失。

一维离散余弦变换 python 二维离散余弦变换指令_算法_13

4.频域数字意义

一维离散余弦变换 python 二维离散余弦变换指令_频域_14

所有的8*8的黑白图可以看做这64个基本余弦波构成的色块进行的叠加, 每个块的上频率是确定的,对应的数字表示其影响。可以类比傅里叶变换每个频率前对应的振幅,相应的越大表示影响越大,保留低频的信号就可以保留图片的主体信息。
参考视频:【jpeg-dct】https://www.bilibili.com/video/BV17v411p7gV?vd_source=53f17186c8480e141f43fa1db7910b6c

5.代码分享

DCT变换与IDCT变换

import numpy as np

def dct(matrix):
    N = matrix.shape[0]
    M = matrix.shape[1]

    # 构造系数矩阵
    C = np.ones((N, M))
    for i in range(0, N):
        C[i, 0] = 1 / np.sqrt(N)
    for j in range(1, M):
        for i in range(0, N):
            C[i, j] = np.sqrt(2 / N) * np.cos((np.pi / N) * (i + 0.5) * j)
    # 计算 DCT 变换
    dct_data = np.dot(np.dot(C, matrix), np.transpose(C))
    return np.around(dct_data, 0)


def idct(dct_data):
    N = dct_data.shape[0]
    M = dct_data.shape[1]

    # 构造系数矩阵
    C = np.ones((N, M))
    for i in range(0, N):
        C[i, 0] = 1 / np.sqrt(N)
    for j in range(1, M):
        for i in range(0, N):
            C[i, j] = np.sqrt(2 / N) * np.cos((np.pi / N) * (i + 0.5) * j)

    # 计算逆变换
    idct_data = np.dot(np.dot(np.transpose(C), dct_data), C)

    return np.around(idct_data,0)

量化与反量化(JPEG压缩是使用,与DCT变化无关)
 

def quantify(dct_data):
    N = dct_data.shape[0]
    M = dct_data.shape[1]
    quantify_data =np.zeros(dct_data.shape)
    quantization_matrix = 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]])

    for j in range(0, M):
        for i in range(0, N):
            quantify_data[i][j]=dct_data[i][j]//quantization_matrix[i][j]
    return quantify_data

def i_quantify(dct_data):
    N = dct_data.shape[0]
    M = dct_data.shape[1]
    quantify_data = np.zeros(dct_data.shape)
    quantization_matrix = 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]])

    for j in range(0, M):
        for i in range(0, N):
            quantify_data[i][j] = dct_data[i][j] * quantization_matrix[i][j]
    return quantify_data

滤波函数(可以选择性的过滤到高频滤波)

def fittler(dct_data):
    N = dct_data.shape[0]
    M = dct_data.shape[1]
    for i in range(0, N):
        for j in range(M-i, M):
            if abs(dct_data[i][j]) <= 1:
                dct_data[i][j] = 0
    return dct_data

通过python中的列表切片操作,完成分块的DCT变换与逆变换
 

import cv2 as cv
# img = cv.imread(r"D:\py_code\2023_10\1.jpg",cv.IMREAD_GRAYSCALE)
img = np.array([[-76, -73, -67, -62, -58, -67, -64, -55],
                  [-65, -69, -73, -38, -19, -43, -59, -56],
                  [-66, -69, -60, -15, 16, -24, -62, -55],
                  [-65, -70, -57, -6, 26, -22, -58, -59],
                  [-61, -67, -60, -24, -2, -40, -60, -58],
                  [-49, -63, -68, -58, -51, -60, -70, -53],
                  [-43, -57, -64, -69, -73, -67, -63, -45],
                  [-41, -49, -59, -60, -63, -52, -50, -34]])
size = 8
print(img.shape)
print(img)
dct_data = np.zeros(img.shape)
img_dct = np.zeros(img.shape)
for i in range(img.shape[0]//size):
    for j in range(img.shape[1]//size):
        sub_dct_data = dct(img[i*size:(i+1)*size,j*size:(j+1)*size])
        sub_dct_data = fittler(sub_dct_data)
        for u in range(size):
            for v in range(size):
                dct_data[i*size+u][j*size+v]=sub_dct_data[u][v]
print(dct_data)


for i in range(img.shape[0]//size):
    for j in range(img.shape[1]//size):
        sub_idct_data = idct(dct_data[i*size:(i+1)*size,j*size:(j+1)*size])
        for u in range(size):
            for v in range(size):
                img_dct[i*size+u][j*size+v]=sub_idct_data[u][v]

print(img_dct)

总体代码 文件为黑白lina图

import numpy as np

def dct(matrix):
    N = matrix.shape[0]
    M = matrix.shape[1]

    # 构造系数矩阵
    C = np.ones((N, M))
    for i in range(0, N):
        C[i, 0] = 1 / np.sqrt(N)
    for j in range(1, M):
        for i in range(0, N):
            C[i, j] = np.sqrt(2 / N) * np.cos((np.pi / N) * (i + 0.5) * j)
    # 计算 DCT 变换
    dct_data = np.dot(np.dot(C, matrix), np.transpose(C))
    return np.around(dct_data, 0)


def idct(dct_data):
    N = dct_data.shape[0]
    M = dct_data.shape[1]

    # 构造系数矩阵
    C = np.ones((N, M))
    for i in range(0, N):
        C[i, 0] = 1 / np.sqrt(N)
    for j in range(1, M):
        for i in range(0, N):
            C[i, j] = np.sqrt(2 / N) * np.cos((np.pi / N) * (i + 0.5) * j)

    # 计算逆变换
    idct_data = np.dot(np.dot(np.transpose(C), dct_data), C)

    return np.around(idct_data,0)
def quantify(dct_data):
    N = dct_data.shape[0]
    M = dct_data.shape[1]
    quantify_data =np.zeros(dct_data.shape)
    quantization_matrix = 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]])

    for j in range(0, M):
        for i in range(0, N):
            quantify_data[i][j]=dct_data[i][j]//quantization_matrix[i][j]
    return quantify_data

def i_quantify(dct_data):
    N = dct_data.shape[0]
    M = dct_data.shape[1]
    quantify_data = np.zeros(dct_data.shape)
    quantization_matrix = 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]])

    for j in range(0, M):
        for i in range(0, N):
            quantify_data[i][j] = dct_data[i][j] * quantization_matrix[i][j]
    return quantify_data

def fittler(dct_data):
    N = dct_data.shape[0]
    M = dct_data.shape[1]
    for i in range(0, N):
        for j in range(M-i, M):
            if abs(dct_data[i][j]) <= 1:
                dct_data[i][j] = 0
    return dct_data

import cv2 as cv
# img = cv.imread(r"D:\py_code\2023_10\1.jpg",cv.IMREAD_GRAYSCALE)
img = np.array([[-76, -73, -67, -62, -58, -67, -64, -55],
                  [-65, -69, -73, -38, -19, -43, -59, -56],
                  [-66, -69, -60, -15, 16, -24, -62, -55],
                  [-65, -70, -57, -6, 26, -22, -58, -59],
                  [-61, -67, -60, -24, -2, -40, -60, -58],
                  [-49, -63, -68, -58, -51, -60, -70, -53],
                  [-43, -57, -64, -69, -73, -67, -63, -45],
                  [-41, -49, -59, -60, -63, -52, -50, -34]])
size = 8
print(img.shape)
print(img)
dct_data = np.zeros(img.shape)
img_dct = np.zeros(img.shape)
for i in range(img.shape[0]//size):
    for j in range(img.shape[1]//size):
        sub_dct_data = dct(img[i*size:(i+1)*size,j*size:(j+1)*size])
        sub_dct_data = fittler(sub_dct_data)
        for u in range(size):
            for v in range(size):
                dct_data[i*size+u][j*size+v]=sub_dct_data[u][v]
print(dct_data)


for i in range(img.shape[0]//size):
    for j in range(img.shape[1]//size):
        sub_idct_data = idct(dct_data[i*size:(i+1)*size,j*size:(j+1)*size])
        for u in range(size):
            for v in range(size):
                img_dct[i*size+u][j*size+v]=sub_idct_data[u][v]

print(img_dct)
cv.imwrite("6.jpg",img_dct)

通过滤波器过滤掉一些高频细节,完成对图片的压缩

一维离散余弦变换 python 二维离散余弦变换指令_系数矩阵_15

6.基于DCT变换的信息隐藏的原理

DCT变换矩阵前后,中间过滤掉高频信号,对图片质量的影响很小,我们通过更改高频信号区的系数完成信息隐藏。