二维DCT变换的实现
程序员文章站
2022-07-04 16:00:32
...
DCT原理参考
离散余弦变换原理与应用
JPEG压缩原理
JPEG压缩原理与DCT离散变换
JPEG图像压缩算法流程详解
JPEG压缩算法流程
二维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;
}
实验测试结果:
to be continued
- 图像的8x8分块
- 量化(量化表)
- ZigZag扫描
- 行程编码(run-level-coding)
- huffman编码