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