程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> VC >> 關於VC++ >> DCT快速變換

DCT快速變換

編輯:關於VC++

一、引言

DCT變換是數字圖像處理中重要的變換,很多重要的圖像算法、圖像應用都是基於DCT變換的,如JPEG圖像編碼方式。對於大尺寸的二維數值矩陣,倘若采用普通的DCT變換來進行,其所花費的時間將是讓人難以忍受甚至無法達到實用。而要克服這一難點,DCT變換的快速算法無非是非常吸引人的。

就目前而言,DCT變換的快速算法無非有以下兩種方式:

1.由於FFT算法的普便采用,直接利用FFT來實現DCT變換的快速算法相比來說就相對容易。但是此種方法也有不足:計算過程會涉及到復數的運算。由於DCT變換前後的數據都是實數,計算過程中引入復數,而一對復數的加法相當於兩對實數的加法,一對復數的乘法相當於四對實數的乘法和兩對實數的加法,顯然是增加了運算量,也給硬件存儲提出了更高的要求。

2.直接在實數域進行DCT快速變換。顯然,這種方法相比於前一種而言,計算量和硬件要求都要優於前者。

鑒於此,本文采用第二種方法來實現DCT變換的快速算法。

二、理論推導

限於篇幅,在此不能羅列,具體推導過程可參見《DCT快速新算法及濾波器結構研究與子波變換域圖像降噪研究》華南理工大學博士論文。

三、程序實現

DCT快速變換

考慮到DCT變換中的系數要重復計算,可使用查找表來提高運行的效率,只要開始DCT變換之前計算一次,DCT變換中就可以只查找而無需計算系數。

void initDCTParam(int deg)
{
   // deg 為DCT變換數據長度的冪
   if(bHasInit)
   {
       return; //不用再計算查找表
   }
   int total, halftotal, i, group, endstart, factor;
   total = 1 << deg;
   if(C != NULL) delete []C;
   C = (double *)new double[total];
   halftotal = total >> 1;
   for(i=0; i < halftotal; i++)
       C[total-i-1]=(double)(2*i+1);
   for(group=0; group < deg-1; group++)
   {
       endstart=1 << (deg-1-group);
       int len = endstart >> 1;
       factor=1 << (group+1);
       for(int j = 0;j < len; j++)
          C[endstart-j-1] = factor*C[total-j-1];
   }
   for(i=1; i < total; i++)
       C[i] = 2.0*cos(C[i]*PI/(total << 1)); ///C[0]空著,沒使用
   bHasInit=true;
}

DCT變換過程可分為兩部分:前向追底和後向回根

前向追底:

void dct_forward(double *f,int deg)
{
   // f中存儲DCT數據
   int i_deg, i_halfwing, total, wing, wings, winglen, halfwing;
   double temp1,temp2;
   total = 1 << deg;
   for(i_deg = 0; i_deg < deg; i_deg++)
   {
       wings = 1 << i_deg;
       winglen = total >> i_deg;
       halfwing = winglen >> 1;
       for(wing = 0; wing < wings; wing++)
       {
          for(i_halfwing = 0; i_halfwing < halfwing; i_halfwing++)
          {
              temp1 = f[wing*winglen+i_halfwing];
              temp2 = f[(wing+1)*winglen-1-i_halfwing];
              if(wing%2)
                 swap(temp1,temp2); // 交換temp1與temp2的值
              f[wing*winglen+i_halfwing] = temp1+temp2;
              f[(wing+1)*winglen-1-i_halfwing] =
                (temp1-temp2)*C[winglen-1-i_halfwing];
          }
       }
   }
}

後向回根:

void dct_backward(double *f,int deg)
{
   // f中存儲DCT數據
   int total,i_deg,wing,wings,halfwing,winglen,i_halfwing,temp1,temp2;
   total = 1 << deg;
   for(i_deg = deg-1; i_deg >= 0; i_deg--)
   {
       wings = 1 << i_deg;
       winglen = 1 << (deg-i_deg);
       halfwing = winglen >> 1;
       for(wing = 0; wing < wings; wing++)
       {
          for(i_halfwing = 0; i_halfwing < halfwing; i_halfwing++)
          {
              //f[i_halfwing+wing*winglen] = f[i_halfwing+wing*winglen];
              if(i_halfwing == 0)
              {
                  f[halfwing+wing*winglen+i_halfwing] =
                    0.5*f[halfwing+wing*winglen+i_halfwing];
              }
              else
              {
                 temp1=bitrev(i_halfwing,deg-i_deg-1);  // bitrev為位反序
                 temp2=bitrev(i_halfwing-1,deg-i_deg-1); // 第一參數為要變換的數
                 // 第二參數為二進制長度
                 f[halfwing+wing*winglen+temp1] =
                    f[halfwing+wing*winglen+temp1]-f[halfwing+wing*winglen+temp2];
              }
          }
       }
   }
}

位反序函數如下:

int bitrev(int bi,int deg)
{
   int j = 1, temp = 0, degnum, halfnum;
   degnum = deg;
   //if(deg<0) return 0;
   if(deg == 0) return bi;
   halfnum = 1 << (deg-1);
   while(halfnum)
   {
       if(halfnum&bi)
          temp += j;
       j<<=1;
       halfnum >>= 1;
   }
   return temp;
}

完整實現一維DCT變換:

void fdct_1D_no_param(double *f,int deg)
{
   initDCTParam(deg);
   dct_forward(f,deg);
   dct_backward(f,deg);
   fbitrev(f,deg);   // 實現位反序排列
   f[0] = 1/(sqrt(2.0))*f[0];
}
void fdct_1D(double *f,int deg)
{
   fdct_1D_no_param(f,deg);
   int total = 1 << deg;
   double param = sqrt(2.0/total);
   for(int i = 0; i < total; i++)
       f[i] = param*f[i];
}

利用一維DCT變換來實現二維DCT變換:

void fdct_2D(double *f,int deg_row,int deg_col)
{
   int rows,cols,i_row,i_col;
   double two_div_sqrtcolrow;
   rows=1 << deg_row;
   cols=1 << deg_col;
   init2D_Param(rows,cols);
   two_div_sqrtcolrow = 2.0/(sqrt(double(rows*cols)));
   for(i_row = 0; i_row < rows; i_row++)
   {
       fdct_1D_no_param(f+i_row*cols,deg_col);
   }
   for(i_col = 0; i_col < cols; i_col++)
   {
       for(i_row = 0; i_row < rows; i_row++)
       {
          temp_2D[i_row] = f[i_row*cols+i_col];
       }
       fdct_1D_no_param(temp_2D, deg_row);
       for(i_row = 0; i_row < rows; i_row++)
       {
          f[i_row*cols+i_col] = temp_2D[i_row]*two_div_sqrtcolrow;
       }
   }
   bHasInit = false;
}

IDCT快速變換

初始化查找表:

void initIDCTParam(int deg)
{
   if(bHasInit)
       return;  //不用再計算查找表
   int total, halftotal, i, group, endstart, factor;
   total = 1 << deg;
   // if(C!=NULL) delete []C;
   // C=(double *)new double[total];
   // 由於正變換已經為C申請了空間,所以逆變換就需再申請空間了!
   halftotal = total >> 1;
   for(i = 0; i < halftotal; i++)
       C[total-i-1] = (double)(2*i+1);
   for(group = 0; group < deg-1; group++)
   {
       endstart = 1 << (deg-1-group);
       int len = endstart>>1;
       factor = 1 << (group+1);
       for(int j = 0; j < len; j++)
          C[endstart-j-1] = factor*C[total-j-1];
   }
   for(i = 1; i < total; i++)
       C[i] = 1.0/(2.0*cos(C[i]*PI/(total << 1)));    // C[0]空著沒用
   bHasInit=true;
}

IDCT變換過程也可分為兩部分:前向追底和後向回根

前向追底

void idct_forward(double *F,int deg)
{
   int total,i_deg,wing,wings,halfwing,winglen,i_halfwing,temp1,temp2;
   total = 1 << deg;
   for(i_deg = 0; i_deg < deg; i_deg++)
   {
       wings = 1 << i_deg;
       winglen = 1 << (deg-i_deg);
       halfwing = winglen >> 1;
       for(wing = 0; wing < wings; wing++)
       {
          for(i_halfwing = halfwing-1; i_halfwing >= 0; i_halfwing--)
          {
              if(i_halfwing == 0)
              {
                 F[halfwing+wing*winglen+i_halfwing] =
                  2.0*F[halfwing+wing*winglen+i_halfwing];
              }
              else
              {
                 temp1 = bitrev(i_halfwing,deg-i_deg-1);
                 temp2 = bitrev(i_halfwing-1,deg-i_deg-1);
                 F[halfwing+wing*winglen+temp1] = F[halfwing+wing*winglen+temp1]
                     +F[halfwing+wing*winglen+temp2];
              }
          }
       }
   }
}

後向回根

void idct_backward(double *F, int deg)
{
   int i_deg,i_halfwing,total,wing,wings,winglen,halfwing;
   double temp1, temp2;
   total = 1 << deg;
   for(i_deg = deg-1; i_deg >= 0; i_deg--)
   {
       wings = 1 << i_deg;
       winglen = total >> i_deg;
       halfwing = winglen >> 1;
       for(wing = 0; wing < wings; wing++)
       {
          for(i_halfwing = 0; i_halfwing < halfwing; i_halfwing++)
          {
              temp1 = F[wing*winglen+i_halfwing];
              temp2 = F[(wing+1)*winglen-1-i_halfwing]*C[winglen-1-i_halfwing];
              if(wing % 2)
              {
                 F[wing*winglen+i_halfwing] = (temp1-temp2)*0.5;
                 F[(wing+1)*winglen-1-i_halfwing] = (temp1+temp2)*0.5;
              }
              else
              {
                 F[wing*winglen+i_halfwing] = (temp1+temp2)*0.5;
                 F[(wing+1)*winglen-1-i_halfwing] = (temp1-temp2)*0.5;
              }
          }
       }
   }
}

完整實現一維IDCT變換:

void fidct_1D_no_param(double *F, int deg)
{
   initIDCTParam(deg);
   F[0] = F[0]*sqrt(2.0);
   fbitrev(F, deg);
   idct_forward(F, deg);
   idct_backward(F, deg);
}
void fdct_1D(double *f, int deg)
{
   fdct_1D_no_param(f, deg);
   int total = 1 << deg;
   double param = sqrt(2.0/total);
   for(int i = 0; i < total; i++)
       f[i] = param*f[i];
}

利用一維IDCT變換來實現二維IDCT變換:

void fidct_2D(double *F, int deg_row, int deg_col)
{
   int rows,cols,i_row,i_col;
   double   sqrtcolrow_div_two;
   rows = 1 << deg_row;
   cols = 1 << deg_col;
   init2D_Param(rows,cols);
   sqrtcolrow_div_two = (sqrt(double(rows*cols)))/2.0;
   for(i_row = 0; i_row < rows; i_row++)
   {
       fidct_1D_no_param(F+i_row*cols,deg_col);
   }
   for(i_col = 0; i_col < cols; i_col++)
   {
       for(i_row = 0; i_row < rows; i_row++)
       {
          temp_2D[i_row] = F[i_row*cols+i_col];
       }
       fidct_1D_no_param(temp_2D, deg_row);
       for(i_row = 0; i_row < rows; i_row++)
       {
          F[i_row*cols+i_col] = temp_2D[i_row]*sqrtcolrow_div_two;
       }
   }
   bHasInit=false;
}

多線程的考量由於DCT變換要花費一定的時間,特別是在數據矩陣尺寸比較大的時候。此時,如果沒有增加一個線程來執行DCT變換,操作界面可能因程序忙於DCT變換的計算而失去響應,所以,增加一個用來進行DCT變換的線程是十分必要的。

首先定義一個結構

typedef struct
{
   int row;
   int col;
   double *data;
   //double *data2;
   //double *data3; // 在計算彩色圖象的數據矩陣時,彩色圖象用RGB三個分量
   bool m_bfinished;
   DWORD m_start,m_end; //以毫秒計,用來計算DCT變換所用的時間;
}RUNINFO;

DCT變換進程函數:

UINT ThreadProcfastDct(LPVOID pParam)
{
   RUNINFO *pinfo = (RUNINFO*)pParam;
   pinfo->m_start = ::GetTickCount();
   fdct_2D((double *)pinfo->data, GetTwoIndex(pinfo->row), GetTwoIndex(pinfo->col));
   pinfo->m_end = ::GetTickCount();
   pinfo->m_bfinished = true;
   return 1;
}

IDCT變換進程函數:

UINT ThreadProcfastIDct(LPVOID pParam)
{
   RUNINFO *pinfo = (RUNINFO*)pParam;
   pinfo->m_start = ::GetTickCount();
   fidct_2D((double *)pinfo->data, GetTwoIndex(pinfo->row), GetTwoIndex(pinfo->col));
   pinfo->m_end = ::GetTickCount();
   pinfo->m_bfinished = true;
   return 1;
}

四、程序運行

圖1 普通DCT變換

圖2 快速DCT變換

圖3 快速IDCT變換

從以上可以看出,采用上述快速DCT變換對一幅256灰度的256*256的圖像進行DCT正變換只需94ms,IDCT逆變換也只需94ms,而如果采用普通DCT變換,所需時間要575172ms。由此可見,DCT快速變換的巨大的優勢,計算速度快,效率高。

本文配套源碼

  1. 上一頁:
  2. 下一頁:
欄目導航
Copyright © 程式師世界 All Rights Reserved