利用二維的傅里葉變換,咱們能夠將圖像信號從空間域(spatial domain)變換到其對應的頻域(frequency domain)中進行分析,這與咱們平常觀察世界的視角是大相徑庭的。dom
所謂傅里葉變換實際上是正交變換的一種,其原理是「週期與非週期信號均可用正弦函數的加權積分表示」。在圖像處理中,咱們通常用到的是二維離散傅里葉變換,具體公式以下。函數
在具體的編碼實現時,咱們經常利用歐拉變換將公式中的實部與虛部分離(以下圖)。優化
能夠發現, 經過上述變換,咱們將二維離散傅里葉變換的公式由\(F(u,v)=|F(u,v)|e^{jφ(u,v)}\)的形式轉化成了\(F(u,v)=R(u,v)+jI(u,v)\)的形式,因此有下式。編碼
上式中的 \(φ(u,v)\)爲圖像信號在\((u,v)\)點的幅度值,\(|F(u,v)|\)爲信號在\((u,v)\)點的相位值。經過上面的拆分,咱們能夠輕鬆地編寫程序進行計算,獲得所謂的幅度圖像和相位圖像。其中幅度圖像包含了咱們所須要的圖像幾何結構的全部信息,在圖像分析和處理中應用最爲普遍。spa
餘弦變換也是正交變換的一種。由前面提到的傅里葉變換公式能夠知道,偶函數的傅立葉變換的虛部爲零,於是不須要計算,只計算餘弦項變換,這就是餘弦變換 。顯然,餘弦變換的變換核爲實數的餘弦函數(公式以下),其計算速度相比傅里葉變換要快得多,因此餘弦變換被普遍應用於圖像有損壓縮和語音信號處理等衆多領域。.net
能夠證實,傅里葉變換與餘弦變換都是可逆的,其逆變換公式能夠經過矩陣的逆運算性質求出,具體公式以下。code
不管是快速傅里葉變換仍是快速餘弦變換,其實都是利用了變換的可分離性,藉助動態規劃的思想,將二維離散變換的複雜度從 \(O(n^2)\) 優化到 \(O(nlogn)\) 。基本思路以下圖所示(圖源博客)。blog
此處僅貼出了快速傅里葉正逆變換(FFT&IFFT)與快速餘弦正逆變換(FCT&IFCT)等關鍵代碼,其餘代碼如簡單離散傅里葉變換(DFT)與餘弦變換(DCT)的代碼請見附件。圖片
constexpr auto AMAX = 100; //調整幅度值可視化參數 constexpr auto CMAX = 50; //調整餘弦變換可視化參數 //可選的照片:"photo" "Gundam" "lena" static String name = "Gundam"; //將圖像存儲容器中的值映射到[0,255]的灰度區間內 enum StandardType { AMPLITUDE, //幅度圖 PHASE, //相位圖 COSINE, //餘弦變換圖 SIFFT, //傅里葉逆變換 SIFCT //餘弦逆變換 }; int Shift(Mat& src, Mat& dst); //中心化 int Standard(Container src, Mat& dst, int type); //映射到[0,255]區間 int DFT(Mat src, Container& A, Container& φ); //二維離散傅里葉變換 int DCT(Mat src, Container& C); //二維離散餘弦變換 int FFT(Mat src, Container& R, Container& I, Container& A, Container& φ); //快速傅里葉變換 int FCT(Mat src, Container& C); //快速餘弦變換 int IFFT(Container R, Container I, Mat& dst); //快速傅里葉逆變換 int IFCT(Container& C, Mat& dst); //快速餘弦逆變換
int FFT(Mat src, Container& R, Container& I, Container& A, Container& φ) { double M = src.rows; double N = src.cols; for (int v = 0; v < N; v++) { vector<double> listR, listI; for (int x = 0; x < M; x++) { double r = 0.0, i = 0.0; for (int y = 0; y < N; y++) { double t = -2 * PI * v * y / N; r += src.ptr<uchar>(x)[y] * cos(t); i += src.ptr<uchar>(x)[y] * sin(t); } listR.push_back(r); listI.push_back(i); } for (int u = 0; u < M; u++) { double r = 0.0, i = 0.0; for (int x = 0; x < M; x++) { double t = -2 * PI * u * x / M; r += listR[x] * cos(t) - listI[x] * sin(t); i += listR[x] * sin(t) + listI[x] * cos(t); } R[u].push_back(r); I[u].push_back(i); A[u].push_back(sqrt(r * r + i * i)); φ[u].push_back(atan(i / r)); } } return 0; }
int FCT(Mat src, Container& C) { for (int v = 0; v < N; v++) { vector<double> list; for (int x = 0; x < M; x++) { double c = 0.0; for (int y = 0; y < N; y++) { double t = (2.0 * y + 1.0) * v * PI / (double)(2.0 * N); t = (double)src.ptr<uchar>(x)[y] * cos(t); if (v == 0) t /= sqrt(2); c += t; } list.push_back(c); } for (int u = 0; u < M; u++) { double c = 0.0; for (int x = 0; x < M; x++) { double t = (2.0 * x + 1.0) * u * PI / (double)(2.0 * M); t = list[x] * cos(t); if (u == 0) t /= sqrt(2); c += t; } C[u].push_back(c * 2 / sqrt(M * N)); } } return 0; }
int IFFT(Container R, Container I, Mat& dst) { double M = dst.rows; double N = dst.cols; Container Cdst(M); for (int v = 0; v < N; v++) { vector<double> listR, listI; for (int x = 0; x < M; x++) { double r = 0.0, i = 0.0; for (int y = 0; y < N; y++) { double t = 2 * PI * v * y / N; r += R[x][y] * cos(t) - I[x][y] * sin(t); i += R[x][y] * sin(t) + I[x][y] * cos(t); } listR.push_back(r); listI.push_back(i); } for (int u = 0; u < M; u++) { double ifft = 0.0; for (int x = 0; x < M; x++) { double t = 2 * PI * u * x / M; ifft += listR[x] * cos(t) - listI[x] * sin(t); } Cdst[u].push_back(ifft / (M * N)); } } Standard(Cdst, dst, SIFFT); return 0; }
int IFCT(Container& C, Mat& dst) { double M = dst.rows; double N = dst.cols; Container Cdst(M); for (int v = 0; v < N; v++) { vector<double> list; for (int x = 0; x < M; x++) { double c = 0.0; for (int y = 0; y < N; y++) { double t = (2.0 * y + 1.0) * v * PI / (double)(2.0 * N); t = C[x][y] * cos(t); if (v == 0) t /= sqrt(2); c += t; } list.push_back(c); } for (int u = 0; u < M; u++) { double c = 0.0; for (int x = 0; x < M; x++) { double t = (2.0 * x + 1.0) * u * PI / (double)(2.0 * M); t = list[x] * cos(t); if (u == 0) t /= sqrt(2); c += t; } Cdst[u].push_back(c * 2.0 / sqrt(M * N)); } } Standard(Cdst, dst, SIFCT); return 0; }
從左到右依次爲 原圖 - 幅度圖 - 餘弦變換圖 - 相位圖get
爲了更好的展現兩種變換的特色,避免將數據映射到[0,255]灰度區間所帶來的數據丟失,我在三維空間座標系中從新繪製了幅度圖與相位圖,以下圖所示。
從下圖咱們能夠看出,餘弦變換後圖像信號的能量集中於一角,這是餘弦變換最顯著的特色。