《OpenCV3编程入门》学习笔记5 Core组件进阶(五)离散傅里叶变换(DFT)
第5章 Core組件進(jìn)階
5.5 離散傅里葉變換(Discrete Fourier Transform,DFT)
5.5.1 離散傅里葉變換原理
1.對一張圖像使用傅里葉變換就是把它分解成正弦和余弦,將圖像從空間域(spatial domain)轉(zhuǎn)換到頻域(frequency domain)
2.理論基礎(chǔ):任一函數(shù)都可以表示成無數(shù)個正弦和余弦函數(shù)的和的形式
3.二維圖像的傅里葉變換數(shù)學(xué)公式表達(dá):
??????????????,??
??f是空間域值(spatial domain),F是頻域值(frequency domain),轉(zhuǎn)換之后的頻域值是復(fù)數(shù),因此,顯示傅里葉變換之后的結(jié)果需要使用實數(shù)圖像(real image)加虛數(shù)圖像(complex image),或幅度圖像(magitude image)加相位圖像(phase image)。
4.頻域里面,高頻部分代表圖像的細(xì)節(jié)、紋理信息;低頻部分代表圖像的輪廓結(jié)構(gòu),如果對一幅精細(xì)的圖像使用低通濾波器,那么濾波后的結(jié)果會只剩下輪廓,如果圖像受到的噪聲正好位于某個特定的“頻率”范圍內(nèi),則可以通過濾波器來恢復(fù)原來的圖像。
5.傅里葉變換在圖像處理中可以做到圖像增強(qiáng)與圖像去噪、圖像分割之邊緣檢測、圖像特征提取、圖像壓縮等。
5.5.2 dft()函數(shù)
1.作用:對一維或二維浮點數(shù)組進(jìn)行正向或反向離散傅里葉變換
2.原型:
void dft(InputArray src,OutputArray dst,int flags=0,int nonzeroRows=0)
3.參數(shù)說明:
(1)輸入矩陣
(2)運算結(jié)果
(3)轉(zhuǎn)換標(biāo)識符(默認(rèn)0)
????????????
(4)參數(shù)設(shè)為非零時,函數(shù)會假設(shè)只有輸入矩陣的第一個非零行包含非零元素(沒有設(shè)置DFT_INVERSE標(biāo)識符)或只有輸出矩陣的第一個非零行包含非零元素(設(shè)置了DFT_INVERSE標(biāo)識符),如此函數(shù)可以對其他行進(jìn)行更高效的處理,以節(jié)省時間開銷,尤其在采用DFT計算矩陣卷積時非常有效。
4.示例:用dft函數(shù)計算兩個二維實矩陣卷積
void consolveDFT(InputArray A, InputArray B, OutputArray C)
{//【1】初始化輸出矩陣C.create(abs(A.rows - B.rows) + 1, abs(A.cols - B.cols) + 1, A.type());Size dftSize;//【2】計算DFT變換尺寸dftSize.width = getOptimalDFTSize(A.cols + B.cols - 1);dftSize.height = getOptimalDFTSize(A.rows + B.rows - 1);//【3】分配臨時緩沖區(qū)并初始化置零Mat tempA(dftSize, A.type(), Scalar::all(0));Mat tempB(dftSize, B.type(), Scalar::all(0));//【4】分別復(fù)制A和B到tempA和tempB的左上角Mat roiA(tempA, Rect(0, 0, A.cols, A.rows));A.copyTo(roiA);Mat roiB(tempB, Rect(0, 0, B.cols, B.rows));B.copyTo(roiB);//【5】就地操作(in_place),進(jìn)行快速傅里葉變換,并將nonzeroRows參數(shù)置為非零,以進(jìn)行更快速的處理dft(tempA, tempA, 0, A.rows);dft(tempB, tempB, 0, B.rows);//【6】將得到的頻譜相乘,結(jié)果存放于tempA中mulSpectrums(tempA, tempB, tempA);//計算兩個傅里葉頻譜的每個元素的乘法//【7】將結(jié)果變換為頻域,且盡管結(jié)果行(result rows)都為非零,我們只需要其中的C.rows的第一行,所以采用nonzeroRows==C.rowsdft(tempA, tempA, DFT_INVERSE + DFT_SCALE, C.rows);//【8】將結(jié)果復(fù)制到C中tempA(Rect(0, 0, C.cols, C.rows)).copyTo(C);//所有的臨時緩沖區(qū)將被自動釋放,所以無須收尾操作
}
5.5.3 返回DFT最優(yōu)尺寸大小:getOptimalDFTSize()函數(shù)
??返回給定向量尺寸的傅里葉最優(yōu)尺寸大小,為了提高離散傅里葉變換的運行速度,需要擴(kuò)充圖像,具體擴(kuò)充多少,由此函數(shù)計算得到:
?????????????????int getOptimalDFTSize(int vecsize)
5.5.4 擴(kuò)充圖像邊界:copyMakeBorder()函數(shù)
1.原型:
void copyMakeBorder(InputArray src,OutputArray dst,int top,int bottom,int left,int right,int borderType,const Scalar&value=Scalar())
2.參數(shù)說明:
(1)源圖像
(2)運算結(jié)果,需和源圖片有一樣的尺寸和類型,Size(src.cols+left+right,src.rows+top+bottom)
(3)top,bottom,left,right,分別表示在源圖像的四個方向上擴(kuò)充多少像素
(4)邊界類型,常見取值為BORDER_CONSTANT
(5)默認(rèn)值Scalar(),可理解為默認(rèn)值為0,borderType取值為BORDER_CONSTANT,這個參數(shù)表示邊界值
5.5.5 計算二維矢量的幅值:magnitude()函數(shù)
1.原型:
void magnitude(InputArray x,InputArray y,OutputArray magnitude)
2.參數(shù)說明:
(1)表示矢量的浮點型X坐標(biāo)值(實部)
(2)表示矢量的浮點型Y坐標(biāo)值(虛部)
(3)輸出的幅值,它和第一個參數(shù)x有著同樣的尺寸和類型
3.原理:
????????????????
5.5.6 計算自然對數(shù):log()函數(shù)
1.作用:計算每個數(shù)組元素絕對值的自然對數(shù)
2.原型:
void log(InputArray src,OutputArray dst)
3.參數(shù)說明:輸入圖像,得到的對數(shù)值
4.原理:
????????????????
5.5.7 矩陣歸一化:normalize()函數(shù)
1.原型:
void normalize(InputArray src,OutputArray dst,double alpha=1,double beta=0,int norm_type=NORM_L2,int dtype=-1,InputArray mask=noArray() )
2.參數(shù)說明:
(1)輸入圖像
(2)運算結(jié)果,和源圖片有一樣的尺寸和類型
(3)歸一化后的最大值,默認(rèn)值1
(4)歸一化后的最大值,默認(rèn)值0
(5)歸一化類型,有NORM_INF、NORM_L1、NORM_L2(默認(rèn))和NORM_MINMAX等參數(shù)可選
(6)默認(rèn)值-1,當(dāng)此參數(shù)取負(fù)值時,輸出矩陣和src有同樣的類型,否則它和src有同樣的通道數(shù),且此時圖像深度為CV_MAT_DEPTH(dtype)
(7)可選的操作掩膜,默認(rèn)值noArray()
5.5.8 示例程序:離散傅里葉變換
#include<opencv2/opencv.hpp>
#include<opencv2/core/core.hpp>
#include<opencv2/imgproc/imgproc.hpp>
#include<opencv2/highgui/highgui.hpp>
#include<iostream>
using namespace cv;
using namespace std;int main()
{//[1]以灰度模式讀取原始圖像并顯示Mat srcImage = imread("love.jpg", 0);if (!srcImage.data) { printf("讀取圖片錯誤,請確定目錄下是否有imread函數(shù)指定圖片存在~!\n"); return false; }imshow("原始圖像", srcImage);//[2]將輸入圖像延擴(kuò)到最佳尺寸,邊界用0擴(kuò)充(擴(kuò)大圖像尺寸可以提高計算速度)int m = getOptimalDFTSize(srcImage.rows);int n = getOptimalDFTSize(srcImage.cols);//將添加的像素初始化為0Mat padded;copyMakeBorder(srcImage, padded, 0, m - srcImage.rows, 0, n - srcImage.cols, BORDER_CONSTANT, Scalar::all(0));//[3]為傅里葉變換的結(jié)果(實部和虛部)分配存儲空間//傅里葉變換結(jié)果是復(fù)數(shù),即每個原圖像值結(jié)果會有兩個圖像值,且頻域值范圍至少存儲在float格式中,所以將輸入圖像轉(zhuǎn)換成浮點類型,并多加一個額外通道存儲復(fù)數(shù)部分//將planes數(shù)組組合合并成一個多通道的數(shù)組complexIMat planes[] = { Mat_<float>(padded),Mat::zeros(padded.size(),CV_32F) };Mat complexI;merge(planes, 2, complexI);//[4]進(jìn)行就地離散傅里葉變換dft(complexI, complexI);//[5]將復(fù)數(shù)轉(zhuǎn)換為幅值,即=>log(1+sqrt(Re(DFT(I)^2+IM(DFT(I))^2))split(complexI, planes); //將多通道數(shù)組complexI分離成幾個單通道數(shù)組,planes[0]=Re(DFT(I),planes[1]=Im(DFT(I))magnitude(planes[0], planes[1], planes[0]);//planes[0]=magnitudeMat magnitudeImage = planes[0];//[6]進(jìn)行對數(shù)尺寸(logarithmic scale)縮放//傅里葉變換的幅度值范圍大到不合適再屏幕顯示,高值在屏幕上顯示為白點,低值為黑點,高低值的變化無法有效分辨,為了在屏幕上凸顯高低變化的連續(xù)性,可以用對數(shù)尺度來替換線性尺度:M'=log(1+M)magnitudeImage += Scalar::all(1);log(magnitudeImage, magnitudeImage);//求自然對數(shù)//[7]剪切和重分布幅度圖象限//因為[2]中進(jìn)行了圖像延擴(kuò),現(xiàn)在需要剔除,為了方便顯示,可以重新分布幅度圖象限位置//若有奇數(shù)行或奇數(shù)列,進(jìn)行頻譜裁剪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區(qū)域的左上Mat q1(magnitudeImage, Rect(cx, 0, cx, cy)); //ROI區(qū)域的右上Mat q2(magnitudeImage, Rect(0, cy, cx, cy)); //ROI區(qū)域的左下Mat q3(magnitudeImage, Rect(cx, cy, cx, cy)); //ROI區(qū)域的右下//交換象限(左上和右下進(jìn)行交換)Mat tmp;q0.copyTo(tmp);q3.copyTo(q0);tmp.copyTo(q3);//交換象限(右上與左下進(jìn)行交換)q1.copyTo(tmp);q2.copyTo(q1);tmp.copyTo(q2);//[8]歸一化,用0到1之間的浮點值將矩陣變換為可視的圖像格式//有了重分布后的幅度圖,但是幅度值仍然超過可顯示范圍[0,1],使用normalize函數(shù)將幅度歸一化到可顯示范圍normalize(magnitudeImage, magnitudeImage, 0, 1, NORM_MINMAX);//[9]顯示效果圖imshow("頻譜幅值", magnitudeImage);waitKey(0);return 0;
}
總結(jié)
以上是生活随笔為你收集整理的《OpenCV3编程入门》学习笔记5 Core组件进阶(五)离散傅里叶变换(DFT)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 一般割包皮要多少钱?
- 下一篇: 《OpenCV3编程入门》学习笔记5 C