二維離散傅里葉變換後的圖像的意義:
低頻部分反映圖像的概貌,高頻反映圖像的細節,改變換建立了圖像在空間域的像素特性與其頻率域上的信息強度之間的聯繫。
——《數字圖像處理》賈永紅
來自博客 如何理解 圖像傅里葉變換的頻譜圖:
很多人都不瞭解圖像(二維)頻譜中的每一點究竟代表了什麼,有什麼意義?
一句話解釋爲: 二維頻譜中的每一個點都是一個與之一 一對應的二維正弦/餘弦波。
代碼效果展示
幾個圖像的頻譜圖像
代碼
我給書上《opencv3編程入門》代碼增加了更詳細的註釋
/*
《opencv3編程入門》p139.離散傅里葉變換
以輸入圖像爲單通道的灰度圖像I爲例。
☆相關概念:
通道的概念(參考 http://www.xuebuyuan.com/1681143.html) :
圖像通道在RGB色彩模式下就是指在下就是指那單獨的紅色R、綠色G、藍色B部分。
也就是說,一幅完整的圖像,是由紅色綠色藍色三個通道組成的。他們共同作用產生了完整的圖像。
同樣在HSV色系中指的是色調H,飽和度S,亮度V三個通道。
多通道模式是把含有通道的圖像分割成單個的通道。
灰度模式(參考 http://www.xuebuyuan.com/1681143.html) :
灰度模式是8位深度的圖像模式。也就是28,28=256,在全黑和全白之間插有254個灰度等級的顏色來描繪灰度模式的圖像。
所有模式的圖像都能換成灰度模式,甚至位圖(深度爲1,只有黑白兩色)也可轉換爲灰度模式。
*/
#include "opencv2/core/core.hpp"
#include "opencv2/imgproc/imgproc.hpp"
#include "opencv2/highgui/highgui.hpp"
#include <iostream>
using namespace cv;
int main( )
{
//【1】以灰度模式讀取原始圖像並顯示,imread("圖片",0)//實參0就是灰度模式
Mat srcImage = imread("1.jpg", 0);
if(!srcImage.data ) { printf("讀取圖片錯誤,請確定目錄下是否有imread函數指定圖片存在~! \n"); return false; }
imshow("原始圖像", srcImage);
//【2】將輸入圖像延擴到最佳的尺寸,邊界用0補充
/*
傅里葉變換的速度和圖像尺寸有關,當尺寸是2、3、5的整數倍,計算速度較快。
於是進行添湊新的邊緣像素。getOptimalDFTSize()用於返回最佳尺寸,copyMakeBorder()用於填充邊緣像素
*/
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));
//【3】爲傅立葉變換的結果(實部和虛部)分配存儲空間。
//將planes數組組合合併成一個多通道的數組complexI
Mat planes[] = {Mat_<float>(padded), Mat::zeros(padded.size(), CV_32F)};
Mat complexI;
//merge()使幾個單通道數組合併成多通道數組
merge(planes, 2, complexI);
//【4】進行就地離散傅里葉變換
/*
就地(in-place)含義:
輸入輸出爲同一圖像
*/
dft(complexI, complexI);
//【5】將複數轉換爲幅值,即=> log(1 + sqrt(Re(DFT(I))^2 + Im(DFT(I))^2))
split(complexI, planes); // 將多通道數組complexI分離成幾個單通道數組,planes[0] = Re(DFT(I), planes[1] = Im(DFT(I))
magnitude(planes[0], planes[1], planes[0]);// planes[0] = magnitude
Mat magnitudeImage = planes[0];
//【6】進行對數尺度(logarithmic scale)縮放
/*
由於幅度範圍太大,不適合在屏幕顯示。爲了在屏幕顯示,
用對數尺度來替換線性尺度
M1=log(1+M)
*/
magnitudeImage += Scalar::all(1);
log(magnitudeImage, magnitudeImage);//求自然對數
//【7】剪切和重分佈幅度圖象限
/*
剔除第二步添加的像素。重分佈是把四個象限的四張圖像拼接到一起
*/
//若有奇數行或奇數列,進行頻譜裁剪
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區域的左上
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);
q3.copyTo(q0);
tmp.copyTo(q3);
//交換象限(右上與左下進行交換)
q1.copyTo(tmp);
q2.copyTo(q1);
tmp.copyTo(q2);
//【8】歸一化,用0到1之間的浮點值將矩陣變換爲可視的圖像格式
/*
幅度值仍然超過可顯示範圍[0,1],normalize()歸一化後可以顯示
*/
//此句代碼的OpenCV2版爲:
normalize(magnitudeImage, magnitudeImage, 0, 1, CV_MINMAX);
//此句代碼的OpenCV3版爲:
//normalize(magnitudeImage, magnitudeImage, 0, 1, NORM_MINMAX);
//【9】顯示效果圖
imshow("頻譜幅值", magnitudeImage);
//任意鍵按下,程序關閉
waitKey();
return 0;
}