学习视频编码理论知识的进度太慢,目前才看到变换编码这一块,对DCT的内容进行了简单的了解,根据我的理解,DCT在视频编码中的作用应该是:对预测后的残差进行DCT变换,使得空间域相对分散的能量在变换域变的相对集中,来进一步减少空间冗余。 我就按照H.265/HEVC原理标准与实现(万帅、杨付正)这本书中的第145页的公式来写一段代码帮助自己加深理解,截图如下:
一开始我使用指针的方法去实现,出现了一个错误,在调试的时候发现无法在调试窗口观察到多级指针内部的值,于是改用vector容器的方法来实现,最终检查到了错误,使用容器的有点就是:减少代码量,方便调试,减少了内存泄漏的问题,增加了可读性。下面我把两种实现的方法都放上来,欢迎指正。
两个程序的作用完全一样,都是将一个二维数组经过DCT变换并输出。并且代码很简单,耐心看下去即可。
1.用vector容器来实现:
#include<stdlib.h>
#include<iostream>
#include<math.h>
#include<vector>
#define PI acos(-1)//定义圆周率
using namespace std;
void DCT(vector<vector<float>>& ori, vector<vector<float>>& dct, float N);
int main(void)
{
const int N = 4;//二维数组的阶数
float start[N][N] = { 12,27,3,4,55,6,7,28,9,10,11,12,123,14,65,16 };//二维数组的初始值,可以自己任意设置
vector<vector<float>> oriMatrix;//原始的二维数组,用容器的方法来定义,方便调试
vector<vector<float>> dctMatrix;//用来存储经过DCT变换后的二维数组
vector<float> temp;//temp仅仅是为了用来填充oriMatrix
//填充oriMatrix
for (int i = 0; i < N; i++)
{
temp.push_back(0);
}
for (int i = 0; i < N; i++)
{
oriMatrix.push_back(temp);
}
dctMatrix = oriMatrix;//初始化dctMatrix
//初始化oriMatrix
for (int i = 0; i < N; i++)
{
for (int j = 0; j < N; j++)
{
oriMatrix[i][j] = start[i][j];
}
}
//输出oriMatrix
cout << "原始二维数组矩阵:" << endl;
for (int i = 0; i < N; i++)
{
for (int j = 0; j < N; j++)
{
printf("%-10.4f", oriMatrix[i][j]);//每个输出占10个字符,左对齐,保留4位小数
}
cout << endl;
}
cout << endl;
DCT(oriMatrix, dctMatrix, N);//调用自己定义的函数
//输出dctMatrix
cout << "经过DCT变换后的二维数组矩阵:" << endl;
for (int i = 0; i < N; i++)
{
for (int j = 0; j < N; j++)
{
printf("%-10.4f", dctMatrix[i][j]);
}
cout << endl;
}
return 0;
}
//注意下面这个自定义的函数的第三个参数必须要是float型,否则会出错(自己换成int型试一下就知道了)
void DCT(vector<vector<float>>& ori, vector<vector<float>>& dct, float N)//第一个形参是原始二维数组的引用,第二个形参矩阵是经过DCT变换的二维数组的引用,第三个形参是阶数
{
//计算dct[0][0]的值
for (int m = 0; m < N; m++)
{
for (int n = 0; n < N; n++)
{
dct[0][0] += (1 / N) * ori[m][n];
}
}
//计算dct[0][1]
for (int m = 0; m < N; m++)
{
for (int n = 0; n < N; n++)
{
dct[0][1] += (sqrt(2) / N) * (ori[m][n]) * cos((2 * n + 1) * PI / (2 * N));
}
}
//计算dct[1][0]
for (int m = 0; m < N; m++)
{
for (int n = 0; n < N; n++)
{
dct[1][0] += (sqrt(2) / N) * (ori[m][n]) * cos((2 * m + 1) * PI / (2 * N));
}
}
//从dct[1][1]一直计算到dct[N-1][N-1],根据公式来看,需要四个for循环
for (int k = 1; k < N; k++)
{
for (int l = 1; l < N; l++)//注意区分字母L和数字1
{
for (int m = 0; m < N; m++)
{
for (int n = 0; n < N; n++)
{
dct[k][l] += (2 / N) * (ori[m][n]) * (cos((2 * m + 1) * k * PI / (2 * N))) * (cos((2 * n + 1) * l * PI / (2 * N)));
}
}
}
}
}
2.用指针的方法实现:
#include<stdlib.h>
#include<iostream>
#include<math.h>
#define PI acos(-1)//定义圆周率
using namespace std;
void DCT(float*** ori, float*** dct, float N);
int main(void)
{
const int N = 4;//矩阵的阶数
float start[N][N] = { 13,2,3,4,20,6,7,8,9,10,11,12,35,14,15,16 };//二维数组的初始值,可以自己任意设置
//为多级指针申请内存时遵循这条原则:申请的时候从外层往里层,逐层申请;释放的时候从里层往外层,逐层释放
float** oriMatrix = NULL;//用指针的形式定义二维数组,方便作为函数参数来传递
float** dctMatrix = NULL;//用来接收经过dct函数变换后的二维数组
//为oriMatrix申请内存
oriMatrix = (float**)malloc(sizeof(float*) * N);
if (oriMatrix == NULL)
{
cout << "func malloc() err." << endl;
exit(-1);
}
for (int s = 0; s < N; s++)
{
oriMatrix[s] = (float*)malloc(sizeof(float) * N);
if (oriMatrix[s] == NULL)
{
cout << "func malloc() err." << endl;
exit(-1);
}
}
//为dctMatrix申请内存
dctMatrix = (float**)malloc(sizeof(float*) * N);
if (dctMatrix == NULL)
{
cout << "func malloc() err." << endl;
exit(-1);
}
for (int s = 0; s < N; s++)
{
dctMatrix[s] = (float*)malloc(sizeof(float) * N);
if (dctMatrix[s] == NULL)
{
cout << "func malloc() err." << endl;
exit(-1);
}
}
for (int i = 0; i < N; i++)
{
for (int j = 0; j < N; j++)
{
oriMatrix[i][j] = start[i][j];
}
}
//初始化dctMatrix
for (int i = 0; i < N; i++)
{
for (int j = 0; j < N; j++)
{
dctMatrix[i][j] = 0;
}
}
//输出oriMatrix
cout << "原始二维数组矩阵:" << endl;
for (int i = 0; i < N; i++)
{
for (int j = 0; j < N; j++)
{
printf("%-10.4f", oriMatrix[i][j]);//每个输出占10个字符,左对齐,保留4位小数
}
cout << endl;
}
DCT(&oriMatrix, &dctMatrix, N);//调用自己定义的函数
cout << endl;
cout << "经过DCT变换后的二维数组矩阵:" << endl;
for (int i = 0; i < N; i++)
{
for (int j = 0; j < N; j++)
{
printf("%-10.4f", dctMatrix[i][j]);
}
cout << endl;
}
//为oriMatrix释放内存
for (int s = 0; s < N; s++)
{
free(oriMatrix[s]);
}
free(oriMatrix);
//为dctMatrix释放内存
for (int s = 0; s < N; s++)
{
free(dctMatrix[s]);
}
free(dctMatrix);
return 0;
}
void DCT(float*** ori, float*** dct, float N)//第一个形参是原始二维数组的地址,第二个形参矩阵是经过DCT变换的二维数组的地址,第三个形参是阶数
{
//计算dct[0][0]的值
for (int m = 0; m < N; m++)
{
for (int n = 0; n < N; n++)
{
(*dct)[0][0] += (1 / N) * (*ori)[m][n];
}
}
//计算dct[0][1]
for (int m = 0; m < N; m++)
{
for (int n = 0; n < N; n++)
{
(*dct)[0][1] += (sqrt(2) / N) * ((*ori)[m][n]) * cos((2 * n + 1) * PI / (2 * N));
}
}
//计算dct[1][0]
for (int m = 0; m < N; m++)
{
for (int n = 0; n < N; n++)
{
(*dct)[1][0] += (sqrt(2) / N) * ((*ori)[m][n]) * cos((2 * m + 1) * PI / (2 * N));
}
}
//从dct[1][1]一直计算到dct[N-1][N-1],根据公式来看,需要四个for循环
for (int k = 1; k < N; k++)
{
for (int l = 1; l < N; l++)//注意区分字母L和数字1
{
for (int m = 0; m < N; m++)
{
for (int n = 0; n < N; n++)
{
(*dct)[k][l] += (2 / N) * ((*ori)[m][n]) * (cos((2 * m + 1) * k * PI / (2 * N))) * (cos((2 * n + 1) * l * PI / (2 * N)));
}
}
}
}
}