1. 程式人生 > >opencv學習實現簡單的影象離散傅立葉變換

opencv學習實現簡單的影象離散傅立葉變換

離散傅立葉變換就是將影象從空間域轉換到頻域,這一轉換基本原理為:

任一函式都可以表示成無數個正弦和餘弦函式的和的形式,二維影象的傅立葉變換可用公式表示為:

其中,f是空間域,F是頻域,轉換之後的頻域值是複數,因此顯示傅立葉變換之後的結果需要使用實物影象加虛數影象或者幅度影象加相點陣圖像的形式。

示例;

#include"stdafx.h"
#include <opencv2/core/utility.hpp>
#include "opencv2/imgproc.hpp"
#include "opencv2/imgcodecs.hpp"
#include "opencv2/highgui.hpp"
#include"opencv2/core/core.hpp"
#include <iostream>

using namespace cv;
using namespace std;

void DFT_Change(Mat &, string);
// 定義全域性變數


int main() {
	system("color 5E");
	//讀取圖片
	Mat image1 = imread("E:\\pictures\\For_Project\\Opencv_Test\\哈爾施特塔\\05.jpg", 0);
	namedWindow("【原始影象1】", 1);
	imshow("【原始影象1】", image1);

	Mat image2 = imread("E:\\pictures\\For_Project\\Opencv_Test\\哈爾施特塔\\b4740bd7f5ee276803fdaa95bfe128d6.jpg", 0);
	namedWindow("【原始影象2】", 1);
	imshow("【原始影象2】", image2);

	Mat image3 = imread("E:\\pictures\\For_Project\\Opencv_Test\\哈爾施特塔\\cbf4d74061b713bb5da0bdafedbf1178.jpg", 0);
	namedWindow("【原始影象3】", 1);
	imshow("【原始影象3】", image3);
	
	double time1 = static_cast<double>(getTickCount());
	//進行DFT傅立葉變換操作 
	string windows_name1 = "頻譜幅值1";
	DFT_Change(image1,windows_name1);
	//計算執行時間並輸出  
	time1 = ((double)getTickCount() - time1) / getTickFrequency();
	cout << "1.該方法執行時間為:" << time1 << "秒" << endl;

	double time2 = static_cast<double>(getTickCount());
	//進行DFT傅立葉變換操作 
	string windows_name2 = "頻譜幅值2";
	DFT_Change(image2,windows_name2);
	//計算執行時間並輸出  
	time2 = ((double)getTickCount() - time2) / getTickFrequency();
	cout << "2.該方法執行時間為:" << time2 << "秒" << endl;

	double time3 = static_cast<double>(getTickCount());
	//進行DFT傅立葉變換操作 
	string windows_name3 = "頻譜幅值3";
	DFT_Change(image3,windows_name3);
	//計算執行時間並輸出  
	time3 = ((double)getTickCount() - time3) / getTickFrequency();
	cout << "3.該方法執行時間為:" << time3 << "秒" << endl;

	//等待鍵盤按鍵‘q’退出
	while (char(waitKey(1)) != 'q') {}
	return 0;
}

void DFT_Change(Mat &image,string windows_name) {
	
	Mat srcImage = image.clone();
	// ShowHelpText();

	// 將輸入影象延擴到最佳的尺寸,邊界用0補充q
	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));

	//為傅立葉變換的結果(實部和虛部)分配儲存空間
	//講planes陣列組合成一個多通道的陣列complexI
	Mat planes[] = { Mat_<float>(padded),Mat::zeros(padded.size(),CV_32F) };
	Mat complexI;
	merge(planes, 2, complexI);

	//就地進行離散傅立葉變換
	dft(complexI, complexI);

	//將複數轉換為幅值
	//講多通道的陣列complexI分離成幾個單通道陣列
	split(complexI, planes);
	magnitude(planes[0], planes[1], planes[0]);
	Mat magnitudeImage = planes[0];

	//進行對數尺度縮放
	magnitudeImage += Scalar::all(1);
	log(magnitudeImage, magnitudeImage);

	//剪下和重分佈幅度影象象限
	//若有奇數或奇數行,進行頻譜裁剪
	magnitudeImage = magnitudeImage(Rect(0, 0, magnitudeImage.cols&-2, magnitudeImage.rows&-2));

	//重新排列傅立葉影象中的象限,使得原點在影象中心
	int cx = magnitudeImage.cols / 2;
	int cy = magnitudeImage.rows / 2;
	//ROI區域的左上
	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));

	//交換象限,左上與右下交換,右上和左下交換
	Mat tmp1, tmp2;
	q0.copyTo(tmp1);
	q3.copyTo(q0);
	tmp1.copyTo(q3);

	q1.copyTo(tmp2);
	q2.copyTo(q1);
	tmp2.copyTo(q2);

	//歸一化處理,用0-1之間的浮點值將矩陣變換為可視的影象格式
	normalize(magnitudeImage, magnitudeImage, 0, 1, NORM_MINMAX);

	imshow(windows_name, magnitudeImage);
}