# 【C++】Image FFT and IFFT
## 環境
* OS: Windows 10
* IDE: Visual Studio 2017
* 語言與框架: C++/CLI,Windows form
## 結果
* 可開啟Windows File Dialog選擇圖片。
* ![](https://i.imgur.com/SB5uDb2.png)
* 快速傅立葉轉換(FFT)。
* ![](https://i.imgur.com/rtSBo3N.png)
* 逆向快速傅立葉轉換(IFFT)。
* ![](https://i.imgur.com/42bQEPu.png)
## 實作講解
### FFT(Fast Fourier Transform)
- <u>詳細的原理、證明這邊不會細講,只會提概念。</u>
#### 1D
* 我們先來想**一維**的FFT怎麼完成。
* FFT和DFT相比,用了兩個概念來加速:
* DP動態規劃。
* bit-reversal重新排序。
* 我們知道DFT必須得到整個傅立葉轉換矩陣,和原輸入相乘之後才能得到結果;FFT也是一樣,只是我們加速得到矩陣的過程。
* 每個一維都要一個矩陣,展成二維之後N*M個矩陣(行和列都需要做轉換,後面會提到),因此這部分的化簡會提升相當大的速度。
* FFT可以利用divide and conquer,將運算從小推到大(NlogN)。
* 然而這樣會讓陣列的索引變很亂,程式碼不易撰寫。此時利用bit-reversal預先排列,就可以讓DP需要的部分排好,不需要額外做處理。
```C++=1
//bit-reversal重新排序
for (int i = 1, j = 0; i < N; ++i)
{
for (int k = N >> 1; !((j ^= k)&k); k >>= 1);
if (i > j) swap(x[i], x[j]);
}
```
* FFT作完之後還要將圖像做一個大小的縮放,不然值會太大。
```C++=1
//scale
for (int i = 0; i < N; i++)
{
x[i] /= sqrt(N);
}
```
* 合併之後就是以下程式碼。
```C++=1
//N為Size大小,x是輸入的一維資料。
void FFT(int N,std::complex<double>* x)
{
/* bit-reversal permutation */
for (int i = 1, j = 0; i < N; ++i)
{
for (int k = N >> 1; !((j ^= k)&k); k >>= 1);
if (i > j) swap(x[i], x[j]);
}
/* dynamic programming */
for (int k = 2; k <= N; k <<= 1)
{
float theta = -2.0 * 3.14159 / k;
std::complex<float> delta_w(cos(theta), sin(theta));
// 每k個做一次FFT
for (int j = 0; j < N; j += k)
{
// 前k/2個與後k/2的三角函數值恰好對稱,
// 因此兩兩對稱的一起做。
std::complex<double> w(1, 0);
for (int i = j; i < j + k / 2; i++)
{
std::complex<double> a = x[i];
std::complex<double> b = x[i + k / 2] * w;
x[i] = a + b;
x[i + k / 2] = a - b;
w *= delta_w;
}
}
}
//scale
for (int i = 0; i < N; i++)
{
x[i] /= sqrt(N);
}
}
```
#### 2D
* 學會1D之後,我們就可以用1D擴展至2D。
* 概念是將每一橫排都做1D的FFT,再將**作完的結果**的每一直排做1D的FFT。
* 特別注意,不是原圖的橫排結果+原圖的直排結果,直排的資料來源要用橫排的結果。
```C++=1
/*///////////////////////////
// Varables Info
// M = image height
// N = image width
// InputImage** = image source
// FreqReal** = 頻譜的實數
// FreqImag** = 頻譜的虛數
///////////////////////////*/
//each col
std::complex<double> **temp = new std::complex<double>*[M];
for (int i = 0; i < M; i++)
{
temp[i] = new std::complex<double>[N];
}
for (int i = 0; i < M; i++)
{
//將一維的資料抽出來
std::complex<double> *x = new std::complex<double>[N];
for (int j = 0; j < N; j++)
{
//資料來源用原圖
x[j].real(InputImage[i][j]);
x[j].imag(0);
}
FFT(N,x);
for (int j = 0; j < N; j++)
{
//將資料暫存到一個空間
temp[i][j] = x[j];
}
delete[] x;
}
//each row
for (int i = 0; i < N; i++)
{
//將一維的資料抽出來
std::complex<double> *x = new std::complex<double>[M];
for (int j = 0; j < M; j++)
{
//資料來源用剛剛的結果
x[j] = temp[j][i];
}
FFT(M,x);
for (int j = 0; j < M; j++)
{
FreqReal[i][j] = x[j].real();
FreqImag[i][j] = x[j].imag();
}
delete[] x;
}
```
* 得到實數和虛數之後,再將兩者混合到輸出即可。
```C++=1
//實數虛數結合
for (int i = 0; i < M; i++)
{
for (int j = 0; j < N; j++)
{
OutputImage[i][j] = sqrt(pow(FreqReal[i][j], 2) + pow(FreqImag[i][j], 2));
}
}
```
### IFFT(Inverse Fast Fourier Transform)
* 反向傅立葉和正向的非常像,這代表我們幾乎不用重寫任何code。
* 如果去觀察正向和反向所需要的傅立葉矩陣的話,你會發現它們之間只差了一個共軛的關係,也就是說,你在虛數部份加上負號即可。
```C++=14
float theta = 2.0 * 3.14159 / k;
```
## 其他資源
* [演算法筆記 Wave](http://www.csie.ntnu.edu.tw/~u91029/Wave.html#4)
* 我code主要參考的網頁,裡面還有許多圖片可以更容易理解概念。
* [Paul Bourke.net](http://paulbourke.net/miscellaneous/dft/)
* 查資料的時候發現的網頁,1993年的歷史物。雖然Code比較雜亂,不過對1D轉2D的部分比較詳細。
* [我的GitHub](https://github.com/Zero871015/FourierTransform)
* 內含該專案完整程式碼。
* 如要服用最好看一下裡面的ReadMe,不然會踩雷。
## Info
* [我的網頁](https://zero871015.github.io/)
* [name=Zero871015][time=Sat, Jun 8, 2019 11:12 AM]
###### tags: `FFT` `C++`