OpenCV C++離散傅裡葉變換
- 離散傅裡葉變換
- 程式說明
- 代碼
- 運作效果
- 傅裡葉變換的頻譜圖的含義
離散傅裡葉變換
傅裡葉變換是什麼?一看就懂,寫的超級棒!https://blog.csdn.net/m0_51233386/article/details/114764782
離散傅裡葉變換(Discrete Fourier Transform,DFT)傅裡葉分析方法是信号分析的最基本方法,傅裡葉變換是傅裡葉分析的核心,通過它把信号從時間域變換到頻率域,進而研究信号的頻譜結構和變化規律。
離散傅裡葉變換(DFT),**是傅裡葉變換在時域和頻域上都呈現離散的形式,将時域信号的采樣變換為在離散時間傅裡葉變換(DTFT)頻域的采樣。**在形式上,變換兩端(時域和頻域上)的序列是有限長的,而實際上這兩組序列都應當被認為是離散周期信号的主值序列。即使對有限長的離散信号作DFT,也應當将其看作經過周期延拓成為周期信号再作變換。在實際應用中通常采用快速傅裡葉變換以高效計算DFT。
程式說明
// 程式描述:OpenCV C++離散傅裡葉變換
// 參 考:毛星雲《OpenCV3程式設計入門》
// 作業系統: Windows 10 64bit
// 開發語言: C++
// IDE 版 本:Visual Studio 2019
// OpenCV版本:4.20
代碼
#include "opencv2/core/core.hpp"
#include "opencv2/imgproc/imgproc.hpp"
#include "opencv2/highgui/highgui.hpp"
#include <iostream>
using namespace cv;
//--------------------------------------【main( )函數】-----------------------------------------
// 描述:控制台應用程式的入口函數,我們的程式從這裡開始執行
//-------------------------------------------------------------------------------------------------
int main()
{
//【1】以灰階模式讀取原始圖像并顯示
Mat srcImage = imread("23.jpg",0);
if (!srcImage.data) { printf("讀取圖檔錯誤,請确定目錄下是否有imread函數指定圖檔存在~! \n"); return false; }
imshow("原始灰階圖", srcImage);
//【2】将輸入圖像延擴到最佳的尺寸,邊界用0補充
int m = getOptimalDFTSize(srcImage.rows);
int n = getOptimalDFTSize(srcImage.cols);
//将添加的像素初始化為0.
Mat padded;
copyMakeBorder(srcImage, padded, 0, m - srcImage.rows, 0, n - srcImage.cols, BORDER_CONSTANT, Scalar::all(0));
//【3】為傅立葉變換的結果(實部和虛部)配置設定存儲空間。
//将planes數組組合合并成一個多通道的數組complexI
Mat planes[] = { Mat_<float>(padded), Mat::zeros(padded.size(), CV_32F) };
Mat complexI;
merge(planes, 2, complexI);
//【4】進行就地離散傅裡葉變換
dft(complexI, complexI);
//【5】将複數轉換為幅值,即=> log(1 + sqrt(Re(DFT(I))^2 + Im(DFT(I))^2))
split(complexI, planes); // 将多通道數組complexI分離成幾個單通道數組,planes[0] = Re(DFT(I), planes[1] = Im(DFT(I))
magnitude(planes[0], planes[1], planes[0]);// planes[0] = magnitude
Mat magnitudeImage = planes[0];
//【6】進行對數尺度(logarithmic scale)縮放
magnitudeImage += Scalar::all(1);
log(magnitudeImage, magnitudeImage);//求自然對數
//【7】剪切和重分布幅度圖象限
//若有奇數行或奇數列,進行頻譜裁剪
magnitudeImage = magnitudeImage(Rect(0, 0, magnitudeImage.cols & -2, magnitudeImage.rows & -2));
//重新排列傅立葉圖像中的象限,使得原點位于圖像中心
int cx = magnitudeImage.cols / 2;
int cy = magnitudeImage.rows / 2;
Mat q0(magnitudeImage, Rect(0, 0, cx, cy)); // ROI區域的左上
Mat q1(magnitudeImage, Rect(cx, 0, cx, cy)); // ROI區域的右上
Mat q2(magnitudeImage, Rect(0, cy, cx, cy)); // ROI區域的左下
Mat q3(magnitudeImage, Rect(cx, cy, cx, cy)); // ROI區域的右下
//交換象限(左上與右下進行交換)
Mat tmp;
q0.copyTo(tmp);
q3.copyTo(q0);
tmp.copyTo(q3);
//交換象限(右上與左下進行交換)
q1.copyTo(tmp);
q2.copyTo(q1);
tmp.copyTo(q2);
//【8】歸一化,用0到1之間的浮點值将矩陣變換為可視的圖像格式
//此句代碼的OpenCV2版為:
//normalize(magnitudeImage, magnitudeImage, 0, 1, CV_MINMAX);
//此句代碼的OpenCV3版為:
normalize(magnitudeImage, magnitudeImage, 0, 1, NORM_MINMAX);
//【9】顯示效果圖
imshow("頻譜幅值", magnitudeImage);
waitKey();
return 0;
}
運作效果
示例(1)
示例(2)
為了便于了解變換過程,我将中間過程的圖檔也輸出了,其實我們隻需要得到最後一幅頻譜圖。
離散傅裡葉變換的運作速度與圖檔尺寸有很大關系,當圖像的尺寸的2,3, 5的整數倍時,計算速度最快。是以,為了達到快速計算的目的,經常通過添加新的邊緣像素的方法擷取最佳圖像尺寸。
将輸入圖像延擴到最佳的尺寸,邊界用0補充,可以看到圖檔下方多出了寬度大概0.5厘米的黑條。
示例(3)
離散傅裡葉變換的運作速度與圖檔尺寸有很大關系,當圖像的尺寸的2,3, 5的整數倍時,計算速度最快。是以,為了達到快速計算的目的,經常通過添加新的邊緣像素的方法擷取最佳圖像尺寸。
将輸入圖像延擴到最佳的尺寸,邊界用0補充,可以看到圖檔下方多出了寬度大概0.9厘米的黑條。
示例(4)
此圖尺寸剛好合适,邊緣就沒有明顯的新增像素。
傅裡葉變換的頻譜圖的含義
詳情參考:https://blog.csdn.net/m0_51233386/article/details/115134101
二維頻譜中的每一個點都是一個與之一一對應的二維正弦/餘弦波。
頻譜圖除了周期性之外,還表示高頻低頻的分布。在經過頻譜居中後的頻譜中,中間最亮的點是最低頻率,屬于直流分量(DC分量)。越往邊外走,頻率越高。是以,頻譜圖中的四個角和X,Y軸的盡頭都是高頻。