日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

Opencv——DFT变换(实现两个Mat的卷积以及显示Mat的频域图像)

發布時間:2023/12/1 编程问答 49 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Opencv——DFT变换(实现两个Mat的卷积以及显示Mat的频域图像) 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

DFT原理:(單變量離散傅里葉變換)

數學基礎:
任何一個函數都可以轉換成無數個正弦和余弦函數的和的形式。
通常觀察傅里葉變換后的頻域函數可以獲得兩個重要的信息:幅頻曲線和相頻曲線。
在數字圖像處理中的作用:
在數字圖像處理中,對一張圖片進行傅里葉變換后我們獲得的是:實數圖像(幅度圖像)+虛數圖像(相位圖像)
傅里葉變換在數字圖像處理中將空間域信息轉為頻域信息。
如果需要得到圖像中的幾何結構信息,就要用到離散傅里葉變換

在頻域中高頻和低頻意義:

高頻代表了圖像的細節、紋理信息,(噪聲)
低頻代表圖像的輪廓信息。
所以低通濾波器可以得到輪廓。

傅里葉變換可以應用的場景:

圖像的增強與圖像去噪
圖像分割的邊緣檢測
圖像特征提取
圖像壓縮

Opencv中離散傅里葉變換函數:

dft(input,output,flags,nonzeroRows); //flags:標識符 //nonzeroRows:默認值為0,非零時,表示你想處理的那一行C.rows,計算時更加高效

dft函數應用實例

例1:用dft函數計算兩個二維實矩陣卷積

例子包含的小知識點
1、Size類型

CvSize結構表示矩形尺寸的結構,結構體中分別定義了矩形的寬度和高度,具體定義如下:
typedef struct CvSize {
int width; /* 矩形寬度,單位為象素 /
int height; / 矩形高度,單位為象素 */
}CvSize;

2、getOptimalDFTSize()函數

返回DFT最優尺寸大小:getOpimalDFTSize()
函數返回給定向量尺寸的傅里葉最優尺寸大小、
input:向量尺寸,即圖像的rows\cols

3、mulSpectrums()函數

void cvMulSpectrums( const CvArr* src1, const CvArr* src2, CvArr* dst, int flags );
src1
第一輸入數組
src2
第二輸入數組
dst
輸出數組,和輸入數組有相同的類型和大小。
flags
下面列舉的值的組合:
DFT_COMPLEX_OUTPUT- 把數組的每一行視為一個單獨的頻譜 (參見 cvDFT 的參數討論).
DFT_REAL_OUTPUT - 在做乘法之前取第二個輸入數組的共軛.
函數 cvMulSpectrums 執行兩個 CCS-packed 或者實數或復數傅立葉變換的結果復數矩陣的每個元素的乘法。

4、dft只能處理浮點數,所以需要將輸入圖像轉為float類型
全部代碼:

void convolveDFT(Mat& A,Mat& B, Mat& 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】分配臨時緩沖區并初始化置零Mat tempA(dftSize, A.type(), Scalar::all(0));Mat tempB(dftSize, B.type(), Scalar::all(0));//【4】分別復制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】就地操作,進行快速傅里葉變換,并將nonzeroRows參數置為非零,以進行更加快速的處理???為什么dft(tempA, tempA, 0, A.rows);dft(tempB, tempB, 0, B.rows);//【6】將得到的頻譜相乘,結果存放于tempA中mulSpectrums(tempA,tempB,tempA, DFT_COMPLEX_OUTPUT);//DFT_REAL_OUTPUT//【7】將結果變換為頻域且盡管行結果都為非零,我們只需要其中C.rows的第一行,所以采用nonzeroRows==C.rowsdft(tempA, tempA, DFT_INVERSE + DFT_SCALE, C.rows);//【8】將結果復制到C中tempA(Rect(0, 0, C.cols, C.rows)).copyTo(C);//所有的臨時緩沖區將被自動釋放,所以無須收尾操作 } int main() {SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_INTENSITY | FOREGROUND_GREEN); //字體為綠色//載入原圖Mat srcImage = imread("D:\\opencv_picture_test\\形態學操作\\coin_inv.png", 0); //讀取灰度圖Mat kernel = (Mat_<float>(3, 3) << 1, 1, 1, 1, 1, 1, 1, 1, 1);cout << kernel;Mat floatI = Mat_<float>(srcImage);// change image type into floatMat filteredI;convolveDFT(floatI, kernel, filteredI);normalize(filteredI, filteredI, 0, 1,NORM_MINMAX); // Transform the matrix with float values into a// viewable image form (float between values 0 and 1).//imshow("image", srcImage);imshow("filtered", filteredI); //這里顯示報錯,但是可以用ImageWatch查看,暫時不知道原因waitKey(0);return 0; }

參考鏈接:
https://blog.csdn.net/lichengyu/article/details/18848281

利用傅里葉變換卷積和利用核游走整個圖像進行卷積運算的區別

一般求法中,利用核游走整個圖像進行卷積運算,實際上進行的是相關運算,真正意義上的卷積,應該首先把核翻轉180度,再在整個圖像上進行游走。OpenCV中的filter2D實際上做的也只是相關,而非卷積。

例2:顯示一幅圖像傅里葉變換后的頻域圖像

需要用的函數:
1、擴充圖像邊界:copyMakeBorder()

C++: void copyMakeBorder (InputArray src, OutputArray dst, int top, int bottom, int left, int right,int borderType, const Scalar&
value=Scalar () );
top
bottom
left
right
分別表示在四個方向上擴充多少個像素。
第七個參數:邊界類型,常見取值BORDER_CONSTANT;
第八個參數:默認值為0,當邊界類型取值為BORDER_CONSTANT時,這個參數表示邊界值。

2、計算二維矢量的幅值:magnitude()

C++: void magnitude (InputArray X,InputArray Y, OutputArray magnitude)
X:矢量實部
Y:矢量虛部
magnitude:輸出幅值,與x有相同的尺寸和類型

3、log()函數

C++: void 1og (InputArray srC, OutputArray dst)
計算每個數組元素絕對值的自然對數。
傅里葉變換的幅值范圍大到不適合在屏幕上顯示。
為了凸顯出高低變化的連續性,可以用對數尺度來替換線性尺度。

3、矩陣歸一化:normalize()函數

void cv::normalize(InputArry src,InputOutputArray dst,double alpha=1,double beta=0,int norm_type=NORM_L2,int dtype=-1,InputArray mark=noArry())
alpha 用來規范值或者規范范圍,并且是下限;
beta 只用來規范范圍并且是上限,因此只在NORM_MINMAX中起作用;
1.NORM_L1、NORM_INF、NORM_L2模式下歸一化結果與beta無關,只與alpha有關,詳見第4部分的公式說明;
2.NORM_MINMAX中alpha、beta都起作用,同時需要注意的是alpha和beta的取值順序與歸一化結果無關。即alpha=255,beta=0和alpha=0,beta=255最后的歸一化結果是相同的。

歸一化公式:

而其中的dtype為負數時,輸出數組的type與輸入數組的type相同;
否則,輸出數組與輸入數組只是通道數相同,而tpye=CV_MAT_DEPTH(dtype).

參考鏈接

實現流程:
1、載入原圖
2、將圖像擴大到合適的尺寸(當圖像的尺寸是2.3.5的整數倍時,運行速度最快)
//將輸入圖像延擴到最佳尺寸,邊界用0補充
3、為傅里葉變換的結果(實部和虛部)分配存儲空間
4、進行離散傅里葉變化
5、將復數轉化為幅值
6、進行對數尺度縮放(傅里葉變換的幅值范圍大到不適合在屏幕上顯示。為了凸顯出高低變化的連續性,可以用對數尺度來替換線性尺度。)
公式:M1 = log(1+M);
7、剪切和重分布幅度圖像限
因為在第二部中延擴了圖像,現在需要將添加的像素剔除
為了方便顯示,也可以重新分布幅度圖像象限位置(將第五步得到的幅度圖從中間劃開得到4張1/4子圖像,將每張子圖都看成幅度圖的一個象限,重新分布即將4個交點重疊到圖像中心)這樣的話原點(0,0)就為一道圖像中心了。
//剪切和重分布幅度圖像限
//如有奇數行或奇數列,進行頻譜裁剪
//重新排列傅里葉圖像中的象限,使得原點位于圖像中心。
//交換象限(左上與右下進行交換)
//交換象限(左下與右上進行交換)
8、歸一化
這一步仍然是為了顯示?,F在有了重分布后的幅度圖,但是幅度值仍然超過了可顯示范圍【0,1】。這里使用歸一化函數。
9、顯示效果圖
全部代碼:

#include <opencv2/opencv.hpp> #include <iostream> #include "windows.h" #include <stdio.h> //#include "My_ImageProssing_base.h"using namespace cv; using namespace std;void convolveDFT(Mat& A,Mat& B, Mat& 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】分配臨時緩沖區并初始化置零Mat tempA(dftSize, A.type(), Scalar::all(0));Mat tempB(dftSize, B.type(), Scalar::all(0));//【4】分別復制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】就地操作,進行快速傅里葉變換,并將nonzeroRows參數置為非零,以進行更加快速的處理???為什么dft(tempA, tempA, 0, A.rows);dft(tempB, tempB, 0, B.rows);//【6】將得到的頻譜相乘,結果存放于tempA中mulSpectrums(tempA,tempB,tempA, DFT_COMPLEX_OUTPUT);//DFT_REAL_OUTPUT//【7】將結果變換為頻域且盡管行結果都為非零,我們只需要其中C.rows的第一行,所以采用nonzeroRows==C.rowsdft(tempA, tempA, DFT_INVERSE + DFT_SCALE, C.rows);//【8】將結果復制到C中tempA(Rect(0, 0, C.cols, C.rows)).copyTo(C);//所有的臨時緩沖區將被自動釋放,所以無須收尾操作 } int main() {SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_INTENSITY | FOREGROUND_GREEN); //字體為綠色//1、載入原圖Mat srcImage = imread("D:\\opencv_picture_test\\形態學操作\\coin_inv.png",0); //讀取灰度圖//2、將圖像擴大到合適的尺寸(當圖像的尺寸是2.3.5的整數倍時,運行速度最快)//【2】將輸入圖像延擴到最佳尺寸,邊界用0補充int m = getOptimalDFTSize(srcImage.rows);int n = getOptimalDFTSize(srcImage.cols);//將添加的像素初始化為0Mat padded;copyMakeBorder(srcImage, padded, 0, m - srcImage.rows, n - srcImage.cols, BORDER_CONSTANT,0);//3、為傅里葉變換的結果(實部和虛部)分配存儲空間Mat planes[] = { Mat_<float>(padded),Mat::zeros(padded.size(),CV_32F) };Mat complexI;merge(planes, 2, complexI);//4、進行離散傅里葉變化dft(complexI, complexI);//5、將復數轉化為幅值split(complexI, planes);//將多通道數組complexI分離成幾個單通道數//planes[0] = Re(DFT(I));//planes[1] = Im(DFT(I));//計算矢量幅值magnitude(planes[0], planes[1], planes[0]);//將幅值存入planes[0] Mat magnitudeImage = planes[0];//6、進行對數尺度縮放magnitudeImage += Scalar::all(1);log(magnitudeImage, magnitudeImage);//就地操作,求自然對數//7、剪切和重分布幅度圖像限magnitudeImage = magnitudeImage(Rect(0, 0, magnitudeImage.cols & -2, magnitudeImage.rows & -2));//這個&-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); //將q0與q3互換q3.copyTo(q0);tmp.copyTo(q3);//交換象限(左下與右上進行交換)q1.copyTo(tmp); //將q1與q2互換q2.copyTo(q1);tmp.copyTo(q2);//8、歸一化//這一步仍然是為了顯示?,F在有了重分布后的幅度圖,但是幅度值仍然超過了可顯示范圍【0, 1】。這里使用歸一化函數。normalize(magnitudeImage, magnitudeImage, 0, 1, NORM_MINMAX);//9、顯示效果圖imshow("原圖", srcImage);imshow("頻譜幅值", magnitudeImage);waitKey(0);return 0; }

srcImage:

padded:

magnitudeImage:

每個圖像的具體性質:

總結

以上是生活随笔為你收集整理的Opencv——DFT变换(实现两个Mat的卷积以及显示Mat的频域图像)的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。