OpenCV——图像傅里叶变换
1、OpenCV傅里葉變換相關函數
首先我要說明的是,在使用OpenCV寫代碼做圖像傅里葉變換的時候,并僅僅是調用dft函數做一個傅里葉變換這么簡單的,而是先要對圖像進行一些變換之后,才能得到正確的傅里葉變換結果。因此,第一部分我想先列出幾個OpenCV提供的與傅里葉變換相關的函數,在了解這些函數功能的基礎上,我們再進行具體的圖像傅里葉變換的過程。
1.1 dft()
首先,OpenCV提供的傅里葉變換函數dft。其定義如下:
void dft(InputArray src, OutputArray, dst, int flags, int nonzeroRows); /*** 參數解釋:* src : 輸入圖像* dst : 輸出圖像,傅里葉變換結果。默認情況下返回值有兩個通道,第一個通達是實部,第二個通道是虛部。* flags : 轉換的標識符,有默認值0,暫時不用理會這個參數*/1.2 magnitude()
計算二維矢量的幅值。其定義如下:
void magnitude(InputArray x, InputArray y, OutputArray magnitude); /*** 參數解釋:* x : 實部* y : 虛部* magnitude : 幅值結果*/其計算公式如下:
dst(I)=x(I)2+y(I)2dst(I) = \sqrt{x(I)^2 + y(I)^2} dst(I)=x(I)2+y(I)2?
1.3 phase()
void phase(InputArray x, InputArray y, OutputArray dst, bool angleIndegrees=false); /*** 參數解釋:* x : 實部* y : 虛部* dst : x和y的反正切值結果*/其計算公式如下:
dst=arctan?yxdst = \arctan \frac{y}{x} dst=arctanxy?
1.4 getOptimalDFTSize()
返回給定向量尺寸經過DFT變換后結果的最優尺寸大小。其定義如下:
int getOptimalDFTSize(int vecsize); /*** 參數解釋:* vecsize: 輸入向量尺寸大小(vector size)* DFT變換在一個向量尺寸上不是一個單調函數,當計算兩個數組卷積或對一個數組進行光學分析,它常常會用0擴充一些數組來得到稍微大點的數組以達到比原來數組計算更快的目的。一個尺寸是2階指數(2,4,8,16,32…)的數組計算速度最快,一個數組尺寸是2、3、5的倍數(例如:300 = 5*5*3*2*2)同樣有很高的處理效率。 getOptimalDFTSize()函數返回大于或等于vecsize的最小數值N,這樣尺寸為N的向量進行DFT變換能得到更高的處理效率。在當前N通過p,q,r等一些整數得出N = 2^p*3^q*5^r.*/1.5 copyMakeBorder()
擴充圖像邊界,其函數定義如下:
void copyMakeBorder(InputArray src, OutputArray dst, int top, int bottom, int left, int right, int borderType, const Scalar& value=Scalar() ); /*** 參數解釋:* src: 輸入圖像* dst: 輸出圖像,與src圖像有相同的類型,其尺寸應為Size(src.cols+left+right, src.rows+top+bottom)* int類型的top、bottom、left、right: 在圖像的四個方向上擴充像素的值* borderType: 邊界類型,由borderInterpolate()來定義,常見的取值為BORDER_CONSTANT* const Scalar& value = Scalar(): 如果邊界類型為BORDER_CONSTANT則表示為邊界值*/1.6 normalize()
歸一化就是把要處理的數據經過某種算法的處理限制在所需要的范圍內。首先歸一化是為了后面數據處理的方便,其次歸一化能夠保證程序運行時收斂加快。歸一化的具體作用是歸納同意樣本的統計分布性,歸一化在0-1之間是統計的概率分布,歸一化在某個區間上是統計的坐標分布,在機器學習算法的數據預處理階段,歸一化也是非常重要的步驟。其定義如下:
void normalize(InputArray src, OutputArray dst, double alpha=1, double beta=0, int norm_type=NORM_L2, int dtype=-1, InputArray mask=noArray() ); /*** 參數解釋:* src: 輸入圖像* dst: 輸出圖像,尺寸大小和src相同* alpha = 1: range normalization模式的最小值* beta = 0: range normalization模式的最大值,不用于norm normalization(范數歸一化)模式* norm_type = NORM_L2: 歸一化的類型,主要有 * NORM_INF: 歸一化數組的C-范數(絕對值的最大值)* NORM_L1: 歸一化數組的L1-范數(絕對值的和) * NORM_L2: 歸一化數組的L2-范數(歐幾里得)* NORM_MINMAX: 數組的數值被平移或縮放到一個指定的范圍,線性歸一化,一般較常用。* int dtype = -1: 當該參數為負數時,輸出數組的類型與輸入數組的類型相同,否則輸出數組與輸入數組只是通道數相同,而depth = * CV_MAT_DEPTH(dtype)* mask = noArray(): 操作掩膜版,用于指示函數是否僅僅對指定的元素進行操作。*/1.2 OpenCV傅里葉變換步驟
傅里葉變換
- getOptimalDFTSize()函數得到DFT變換后結果的最優尺寸大小
- 根據得到的尺寸大小,使用copyMakeBorder()函數填充圖像,得到填充后的Mat
- 根據新生成的Mat,使用merge()函數得到一個雙通道的Mat,命名為planes
- 使用dft()函數進行傅里葉變換,得到通道1為實部,通道2為虛部
顯示傅里葉變換結果
- 使用magnitude()函數得到傅里葉變換結果幅度譜
- 將幅度值轉換到對數尺度,取對數的目的是使那些振幅較低的成分相對高振幅成分得以拉高,以便觀察掩蓋在低幅噪聲中的周期信號
- 使用normalize()函數進行歸一化處理,用0-1之間的浮點數將矩陣變換為可視的圖像格式
- 顯示結果
1.3 具體代碼
在這里我只展示了關鍵的代碼片段,而不是具體的可直接運行的代碼。這部分完整的代碼可以訪問我的gitee(碼云)項目,該項目記錄了我自學數字圖像處理所寫的全部代碼,當然也包括OpenCV傅里葉變換的部分。項目鏈接:https://gitee.com/lucasnan/Digital-Image-Process
傅里葉變換
cv::Mat frequencyfilter::frequencyFiltering::fourierTransform(const cv::Mat& originImage){cv::Mat paddingImage;//得到適合傅里葉變換的最優尺寸int m = cv::getOptimalDFTSize(originImage.rows);int n = cv::getOptimalDFTSize(originImage.cols);//填充,上側和左側不填充cv::copyMakeBorder(originImage, paddingImage, 0, m-originImage.rows, 0, n-originImage.cols, cv::BORDER_CONSTANT, cv::Scalar(0));//雙通道Matcv::Mat planes[] = {cv::Mat_<float>(paddingImage), cv::Mat::zeros(paddingImage.size(), CV_32FC1)};cv::Mat mergeImage;cv::merge(planes, 2, mergeImage);//進行傅里葉變換cv::dft(mergeImage, mergeImage, cv::DFT_COMPLEX_OUTPUT);return mergeImage; }獲得傅里葉變換結果幅度值
cv::Mat frequencyfilter::frequencyFiltering::getMagnitudeImage(const cv::Mat& fourierImage){cv::Mat planes[] = {cv::Mat::zeros(fourierImage.size(), CV_32FC1), cv::Mat::zeros(fourierImage.size(), CV_32FC1)};cv::Mat magImage = planes[0].clone();cv::split(fourierImage, planes);cv::magnitude(planes[0], planes[1], magImage);//cv::log(magImage+1, magImage);//如果有奇數行或列,則轉換為偶數magImage = magImage(cv::Rect(0, 0, magImage.cols-(magImage.cols%2), magImage.rows-(magImage.rows%2)));return magImage; }傅里葉變換結果中心化
cv::Mat frequencyfilter::frequencyFiltering::changeCenter(const cv::Mat& magImage){//重新排布傅里葉變換后的圖像,使得原點位于圖像中心int centerX = magImage.cols / 2;int centerY = magImage.rows / 2;cv::Mat magImageCopy = magImage.clone();cv::Mat planes[] = {cv::Mat::zeros(magImageCopy.size(), CV_32FC1), cv::Mat::zeros(magImageCopy.size(), CV_32FC1)};cv::Mat mat1(magImageCopy, cv::Rect(0, 0, centerX, centerY)); //左上cv::Mat mat2(magImageCopy, cv::Rect(0, centerY, centerX, centerY)); //右上cv::Mat mat3(magImageCopy, cv::Rect(centerX, 0, centerX, centerY)); //左下cv::Mat mat4(magImageCopy, cv::Rect(centerX, centerY, centerX, centerY)); //右下//互換左上和右下cv::Mat tempImage;mat1.copyTo(tempImage);mat4.copyTo(mat1);tempImage.copyTo(mat4);//互換左下和右上mat2.copyTo(tempImage);mat3.copyTo(mat2);tempImage.copyTo(mat3);return magImageCopy; }獲得傅里葉變化結果相位譜
cv::Mat frequencyfilter::frequencyFiltering::getPhaseImage(const cv::Mat& fourierImage){cv::Mat planes[] = {cv::Mat::zeros(fourierImage.size(), CV_32FC1), cv::Mat::zeros(fourierImage.size(), CV_32FC1)};cv::Mat phaseImage = planes[0].clone();cv::split(fourierImage, planes);cv::phase(planes[0], planes[1], phaseImage);//imshow("phase", phaseImage);return phaseImage; }顯示傅里葉變換結果
frequencyfilter::frequencyFiltering::frequencyFiltering(cv::Mat image){this->inputImage = image.clone();fourierTransformImage = fourierTransform(inputImage);cv::Mat magImage = getMagnitudeImage(fourierTransformImage);magImage = changeCenter(magImage);//計算幅值,并轉換到對數尺度//取對數的目的是使那些振幅較低的成分相對高振幅成分得以拉高,以便觀察掩蓋在低幅噪聲中的周期信號。cv::log(magImage+1, magImage);//歸一化處理,用0-1之間的浮點數將矩陣變換為可視的圖像格式cv::normalize(magImage, magImage, 0, 1, CV_MINMAX);cv::imshow("test", inputImage);cv::imshow("test2", magImage);inverseFourierTransform(fourierTransformImage); }傅里葉逆變換
cv::Mat frequencyfilter::frequencyFiltering::inverseFourierTransform(const cv::Mat& fourierImage){cv::Mat dealtImage = fourierImage.clone();cv::Mat iDFT[] = {cv::Mat::zeros(dealtImage.size(), CV_32FC1), cv::Mat::zeros(dealtImage.size(), CV_32FC1)};cv::idft(dealtImage, dealtImage);cv::split(dealtImage, iDFT);normalize(iDFT[0], iDFT[0], 0, 1, CV_MINMAX);return iDFT[0]; }結果如下:
最后,再說明一下,以上代碼片段都是從我的數字圖像處理項目代碼中截取的,如果你對OpenCV傅里葉變換有一定的了解,那么結和以上代碼可以應該可以解決你的問題,知道怎么用OpenCV來做傅里葉變換,但是如果你不能理解以上代碼,我推薦你去看我在碼云上傳的項目,里面有完整的項目代碼和結構,應該可以幫助你更好地理解。地址:https://gitee.com/lucasnan/Digital-Image-Process/tree/master/Frequency_Filter
總結
以上是生活随笔為你收集整理的OpenCV——图像傅里叶变换的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 武汉轻工大学计算机考研资料汇总
- 下一篇: 图像坐标空间变换:透视变换(Perspe