C++实现离散余弦变换(参数为二维指针)

http://www.cnblogs.com/scut-linmaojiang/p/5013590.html

写在前面

到目前为止已经阅读了相当一部分的网格水印等方面的论文了,但是论文的实现进度还没有更上,这个月准备挑选一些较为经典的论文,将其中的算法实现。在实现论文的过程中,发现论文中有用到一些空域转频率域的算法。因此也就想到了实现一下离散余弦变换。虽然本文的代码和网上很多已有的代码很类似,思路都没有太多的差别,但是本文有一个比较重要的改进。具体的说,网上现有DCT算法输入的是一个固定的二维数组。当二维数组作为函数参数进行传递时,至少需要给出第二个维度的大小,否则编译器会报错。但是在图形图像处理中,当我们希望使用DCT变换把某个图形或者图像转换到频域时,可能我在调用DCT函数之前事先并不知道图形或者图像的规模,因此这种传参方式较大限制了代码的使用。本文的一个改进就是,DCT函数的参数不再是二维数组,而是传入一个二维指针,通过手动寻址来实现函数功能。

离散余弦变换理论基础

我想大家比较熟知的是傅立叶变换。其实离散余弦变换跟傅立叶变换差不多。二维离散余弦变换的定义由下式表示:

enter description here

反变换表示如下:
enter description here

代码实现

根据上面的公式,很容易就能写出代码

// DCT - Discrete Cosine Transform
void DCT( double ** input, double ** output, int row, int col )
{
    cout<<"Test in DCT"<<endl;
    double ALPHA, BETA;
    int u = 0;
    int v = 0;
    int i = 0;
    int j = 0;

    for(u = 0; u < row; u++)
    {
        for(v = 0; v < col; v++)
        {
            if(u == 0)
            {
                ALPHA = sqrt(1.0 / row);
            }
            else
            {
                ALPHA = sqrt(2.0 / row);
            }

            if(v == 0)
            {
                BETA = sqrt(1.0 / col);
            }
            else
            {
                BETA = sqrt(2.0 / col);
            }

            double tmp = 0.0;
            for(i = 0; i < row; i++)
            {
                for(j = 0; j < col; j++)
                {
                    tmp += *((double*) input + col*i + j ) * cos((2*i+1)*u*PI/(2.0 * row)) * cos((2*j+1)*v*PI/(2.0 * col));
                }
            }
            *((double*) output + col*u + v) = ALPHA * BETA * tmp;
        }
    }

    cout << "The result of DCT:" << endl;
    for(int m  = 0; m < row; m++)
    {
        for(int n= 0; n < col; n++)
        {
            cout <<setw(8)<< *((double*) output + col*m + n)<<" 	";
        }
        cout << endl;
    }
}

注意代码中的函数参数不是二维数组,而是一个指向二维数组的指针。相应的反变换的代码如下:

// Inverse DCT
void IDCT( double ** input, double ** output, int row, int col )
{
    cout<<"Test in IDCT"<<endl;
    double ALPHA, BETA;
    int u = 0;
    int v = 0;
    int i = 0;
    int j = 0;

    for(i = 0; i < row; i++)
    {
        for( j = 0; j < col; j++)
        {
            double tmp = 0.0;
            for(u = 0; u < row; u++)
            {
                for(v = 0; v < col; v++)
                {
                    if(u == 0)
                    {
                        ALPHA = sqrt(1.0 / row);
                    }
                    else
                    {
                        ALPHA = sqrt(2.0 / row);
                    }
                    if(v == 0)
                    {
                        BETA = sqrt(1.0 / col);
                    }
                    else
                    {
                        BETA = sqrt(2.0 / col);
                    }
                    tmp += ALPHA * BETA * *((double*) input + col*u + v )* cos((2*i+1)*u*PI/(2.0 * row)) * cos((2*j+1)*v*PI/(2.0 * col));
                }
            }
            *((double*) output + col*i + j) = tmp;
        }
    }

    cout << "The result of IDCT:" << endl;
    for(int m  = 0; m < row; m++)
    {
        for(int n= 0; n < col; n++)
        {
            cout <<setw(8)<< *((double*) output + col*m + n)<<"	";
        }
        cout << endl;
    }
}

测试代码

#include <iostream>
#include <math.h>
#include<cstdio>
#include <iomanip>
#include<algorithm>

using namespace std;
#define PI 3.1415926
int main()
{
    int i = 0;
    int j = 0;
    int u = 0;
    int v = 0;

    const int rows = 3;
    const int cols = 3;

    double inputdata[rows][cols] = {
        {89,101,114},
        {97,115,131},
        {114,134,159},
    };

    double outputdata[rows][cols];

    DCT( (double**)inputdata, (double**)outputdata,rows, cols );
    IDCT( (double**)outputdata, (double**)inputdata,rows,cols );

    system("pause");
    return 0;
}

程序运行结果如下:
enter description here

小结

之后可以对代码进行升级,不再使用二维数组作为参数传递,而是使用Eigen类型作为参数,这样代码更加清晰,并且内存管理等方面也更加方便。

转载请注明出处:http://www.cnblogs.com/scut-linmaojiang/p/5013590.html

原文地址:https://www.cnblogs.com/jukan/p/7133379.html