欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

二维DCT变换的实现

程序员文章站 2022-07-04 16:00:32
...

DCT原理参考

离散余弦变换原理与应用
JPEG压缩原理
JPEG压缩原理与DCT离散变换
JPEG图像压缩算法流程详解

JPEG压缩算法流程

二维DCT变换的实现

二维DCT变换实现代码

#include <iostream>
#include <memory.h>
#include <stdio.h>
#include <math.h>
#include <time.h>
#include <iomanip>
using namespace std;

/************************************************************************/
/* 
    project:二维DCT变换的实现 / 2D DCT Test

    author:桑来
    date:2017/10/31
*/
/************************************************************************/
#define PI (3.1415926)  
#define N (8)   /* 当前切成8x8的块进行DCT变换 */
const int width = 256, height = 256;
const int frame_size = height*width;

/*
** 读取yuv文件
** 取其y分量
*/
void read_yuv(const char *file_name_yuv, unsigned char y_buff[height][width]){
    FILE *fin = fopen(file_name_yuv, "rb+");
    unsigned char *yuv_buff = (unsigned char*)malloc(frame_size * 3 / 2);
    fread(yuv_buff, 1, frame_size * 3 / 2, fin);

    for (int j = 0; j < height; j++){
        for (int i = 0; i < width; i++){
            y_buff[j][i] = yuv_buff[j*width + i];
        }
    }
    fclose(fin);
}

/*
** 输出某个块的DCT变换的矩阵
** DCT_block:指针,指向[N][N]的某个块
*/
void visit(const double(*DCT_block)[N][N]){
    for (int i = 0; i < N; i++){
        for (int j = 0; j < N; j++){
            printf("%lf\t", (*DCT_block)[i][j]);
        }
        cout << endl;
    }
}

void visit(const unsigned char(*block)[N][N]){
    for (int i = 0; i < N; i++){
        for (int j = 0; j < N; j++){
            printf("%d\t", (*block)[i][j]);
        }
        cout << endl;
    }
}



/*
** 二维IDCT变换
** block:指针,指向DCT变换后的[N][N]的某个块
** IDCT_block:指针,指向反变换后的块
*/
void IDCT(const double(*block)[N][N], double(*IDCT_block)[N][N]){

    // 申请临时空间
    double *tmp = new double[N*N];
    double *coff = new double[N*N];
    memset(tmp, 0, sizeof(double)*N*N);
    // 设置IDCT系数
    coff[0] = 1.0 / sqrt((double)N);
    for (int m = 1; m < N; m++){
        coff[m] = sqrt((double)2) / sqrt((double)N);
    }

    // 实现IDCT变换
    for (int k = 0; k < N; k++){
        for (int n = 0; n < N; n++){
            for (int x = 0; x < N; x++){
                tmp[k*N + n] += coff[x] * (*block)[k][x] * cos((2 * n + 1)*x*PI / 2 / N);
            }
        }
    }
    for (int m = 0; m < N; m++){
        for (int n = 0; n < N; n++){
            for (int x = 0; x < N; x++){
                (*IDCT_block)[m][n] += coff[x] * tmp[x*N + n] * cos((2 * m + 1)*x*PI / 2 / N);
            }
        }
    }

    // 销毁分配的空间
    delete[]tmp;
    delete[]coff;
}


/* 
** 二维DCT变换
** block:指针,指向[N][N]的某个块
** DCT_block:指针,指向变换后的块
*/
void DCT(const unsigned char (*block)[N][N],double (*DCT_block)[N][N]){

    // 申请临时空间
    double *tmp = new double[N*N];
    double *coff = new double[N*N];
    memset(tmp, 0, sizeof(double)*N*N);
    // 设置DCT系数
    coff[0] = 1.0 / sqrt((double)N);
    for (int m = 1; m < N; m++){
        coff[m] = sqrt((double)2) / sqrt((double)N);
    }

    // 实现DCT变换
    for (int m = 0; m < N; m++){
        for (int l = 0; l < N; l++){
            for (int x = 0; x < N; x++){
                tmp[m*N + l] += coff[l] * (*block)[m][x] * cos((2 * x + 1)*PI*l / (2 * N));
            }
        }
    }
    for (int k = 0; k < N; k++){
        for (int l = 0; l < N; l++){
            for (int x = 0; x < N; x++){
                (*DCT_block)[k][l] += coff[k] * tmp[x*N + l] * cos((2 * x + 1)*PI*k / (2 * N));
            }
        }
    }

    // 销毁分配的空间
    delete[]tmp;
    delete[]coff;
}

int main(){

    // 计算能分解成多少个8x8的块
    // 考虑当width或height不能被N整除,填充 
    const unsigned int num_of_NxN = (height%N ? (height / N) + 1 : (height / N)) * (width%N ? (width / N + 1) : (width / N));
    //printf("一共将%dx%d分辨率的图片切成%d个8x8的小块\n", width, height, num_of_NxN);

    const char *file_name_yuv = "E:\\sample\\lenna_256x256_yuv420p.yuv";
    unsigned char y_buff[height][width];
    read_yuv(file_name_yuv, y_buff);

    // 将一幅图片切成8*8块 存储在block中.
    unsigned char block[num_of_NxN][N][N];
    memcpy(block, y_buff, frame_size);

    // DCT_block存放DCT变换后的系数
    double DCT_block[num_of_NxN][N][N] = { 0 };

    // 对每一个8x8块进行DCT变换
    for (int i = 0; i < 1; i++){
        DCT(&block[i], &DCT_block[i]);
    }

    // 打印变换前的某个块:
    printf("\n打印进行DCT变换之前的块:\n");
    visit(&block[0]);

    // 打印某个变换后的块
    printf("\n打印进行DCT变换之后的块:\n");
    visit(&DCT_block[0]);

    // IDT_block存放IDCT变换后的系数
    // 这里只开辟了一个块的大小
    double IDCT_block[1][N][N] = { 0 };
    IDCT(&DCT_block[0], &IDCT_block[0]);
    // 打印逆变换后的块
    printf("\n打印进行IDCT变换之后的块:\n");
    visit(&IDCT_block[0]);

    printf("\n");
    system("pause");

    return 0;
}

实验测试结果:

二维DCT变换的实现

to be continued

  • 图像的8x8分块
  • 量化(量化表)
  • ZigZag扫描
  • 行程编码(run-level-coding)
  • huffman编码
相关标签: 压缩