基于OpenCV完成离散傅里叶变换
基于OpenCV完成離散傅里葉變換
目標(biāo)
學(xué)會(huì)使用函數(shù): cv::copyMakeBorder() , cv::merge() , cv::dft() , cv::getOptimalDFTSize() , cv::log() 和 cv::normalize() . 源代碼如下:
使用的 cv::dft() 的代碼示例:
#include "opencv2/core/core.hpp" #include "opencv2/imgproc/imgproc.hpp" #include "opencv2/imgcodecs.hpp" #include "opencv2/highgui/highgui.hpp"#include <iostream>using namespace cv; using namespace std;static void help(char* progName) {cout << endl<< "This program demonstrated the use of the discrete Fourier transform (DFT). " << endl<< "The dft of an image is taken and it's power spectrum is displayed." << endl<< "Usage:" << endl<< progName << " [image_name -- default ../data/lena.jpg] " << endl << endl; }int main(int argc, char ** argv) {help(argv[0]);const char* filename = argc >=2 ? argv[1] : "../data/lena.jpg";Mat I = imread(filename, IMREAD_GRAYSCALE);if( I.empty())return -1;Mat padded; //expand input image to optimal sizeint m = getOptimalDFTSize( I.rows );int n = getOptimalDFTSize( I.cols ); // on the border add zero valuescopyMakeBorder(I, padded, 0, m - I.rows, 0, n - I.cols, BORDER_CONSTANT, Scalar::all(0));Mat planes[] = {Mat_<float>(padded), Mat::zeros(padded.size(), CV_32F)};Mat complexI;merge(planes, 2, complexI); // Add to the expanded another plane with zerosdft(complexI, complexI); // this way the result may fit in the source matrix// compute the magnitude and switch to logarithmic scale// => log(1 + sqrt(Re(DFT(I))^2 + Im(DFT(I))^2))split(complexI, planes); // planes[0] = Re(DFT(I), planes[1] = Im(DFT(I))magnitude(planes[0], planes[1], planes[0]);// planes[0] = magnitudeMat magI = planes[0];magI += Scalar::all(1); // switch to logarithmic scalelog(magI, magI);// crop the spectrum, if it has an odd number of rows or columnsmagI = magI(Rect(0, 0, magI.cols & -2, magI.rows & -2));// rearrange the quadrants of Fourier image so that the origin is at the image centerint cx = magI.cols/2;int cy = magI.rows/2;Mat q0(magI, Rect(0, 0, cx, cy)); // Top-Left - Create a ROI per quadrantMat q1(magI, Rect(cx, 0, cx, cy)); // Top-RightMat q2(magI, Rect(0, cy, cx, cy)); // Bottom-LeftMat q3(magI, Rect(cx, cy, cx, cy)); // Bottom-RightMat tmp; // swap quadrants (Top-Left with Bottom-Right)q0.copyTo(tmp);q3.copyTo(q0);tmp.copyTo(q3);q1.copyTo(tmp); // swap quadrant (Top-Right with Bottom-Left)q2.copyTo(q1);tmp.copyTo(q2);normalize(magI, magI, 0, 1, NORM_MINMAX); // Transform the matrix with float values into a// viewable image form (float between values 0 and 1).imshow("Input Image" , I ); // Show the resultimshow("spectrum magnitude", magI);waitKey();return 0; }解釋如下
離散傅里葉變換將會(huì)把一副圖像分解為其正弦和余弦分量,也就是把一副圖像從空間域轉(zhuǎn)換到頻率域。主要思想為任何函數(shù)都可以近似的精確地用無(wú)限的正弦和余弦函數(shù)的和表示。在數(shù)學(xué)上的一個(gè)二維圖像的傅立葉變換為:
這里, f 是圖像在空間域的值, F為頻率域的值. 變換的結(jié)果為復(fù)數(shù).
Displaying this is possible either via a real image and a complex image or via a magnitude and a phase image. However, throughout the image processing algorithms only the magnitude image is interesting as this contains all the information we need about the images geometric structure. Nevertheless, if you intend to make some modifications of the image in these forms and then you need to retransform it you'll need to preserve both of these.
In this sample I'll show how to calculate and show the magnitude image of a Fourier Transform. In case of digital images are discrete. This means they may take up a value from a given domain value. For example in a basic gray scale image values usually are between zero and 255. Therefore the Fourier Transform too needs to be of a discrete type resulting in a Discrete Fourier Transform (DFT). You'll want to use this whenever you need to determine the structure of an image from a geometrical point of view. Here are the steps to follow (in case of a gray scale input imageI):
1 Expand the image to an optimal size. The performance of a DFT is dependent of the image size. It tends to be the fastest for image sizes that are multiple of the numbers two, three and five. Therefore, to achieve maximal performance it is generally a good idea to pad border values to the image to get a size with such traits. Thecv::getOptimalDFTSize() returns this optimal size and we can use the cv::copyMakeBorder() function to expand the borders of an image:
Mat padded; //expand input image to optimal size int m = getOptimalDFTSize( I.rows ); int n = getOptimalDFTSize( I.cols ); // on the border add zero pixels copyMakeBorder(I, padded, 0, m - I.rows, 0, n - I.cols, BORDER_CONSTANT, Scalar::all(0));2 Make place for both the complex and the real values. 傅里葉變換的結(jié)果為復(fù)數(shù)。這意味著對(duì)于圖像的每一個(gè)像素值變換的結(jié)果位兩個(gè)值(一個(gè)對(duì)應(yīng)復(fù)數(shù)的實(shí)部一個(gè)對(duì)應(yīng)虛部). 另外,頻率閾值的范圍相對(duì)空間閾是非常大的. 所以,數(shù)據(jù)的存儲(chǔ)至少要用實(shí)數(shù)類型. 所以將會(huì)把輸入圖像轉(zhuǎn)換為用實(shí)數(shù)表示 并擴(kuò)展一個(gè)通道來(lái)保存復(fù)數(shù): Mat planes[] = {Mat_<float>(padded), Mat::zeros(padded.size(), CV_32F)}; Mat complexI; merge(planes, 2, complexI); // Add to the expanded another plane with zeros
3 進(jìn)行離散傅里葉.計(jì)算中用輸入存儲(chǔ)輸出結(jié)果
dft(complexI, complexI); // this way the result may fit in the source matrix
4 轉(zhuǎn)換虛部(complex)和實(shí)部(real)的值為級(jí)數(shù)(magnitude). 一個(gè)復(fù)數(shù)有一個(gè)real (Re)(實(shí)部)和一個(gè) complex (imaginary -Im) 虛部. DFT變換的結(jié)果為一組復(fù)數(shù).DFT變換的級(jí)數(shù)為:?
M=Re(DFT(I))2+Im(DFT(I))2 ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄√2
?OpenCV 代碼如下:
split(complexI, planes); // planes[0] = Re(DFT(I), planes[1] = Im(DFT(I)) magnitude(planes[0], planes[1], planes[0]);// planes[0] = magnitude Mat magI = planes[0];
5 Switch to a logarithmic scale. It turns out that the dynamic range of the Fourier coefficients is too large to be displayed on the screen. We have some small and some high changing values that we can't observe like this. Therefore the high values will all turn out as white points, while the small ones as black. To use the gray scale values to for visualization we can transform our linear scale to a logarithmic one:M1=log(1+M)
用 OpenCV 形式的代碼實(shí)現(xiàn)為:
magI += Scalar::all(1); // switch to logarithmic scale log(magI, magI);
6 Crop and rearrange. Remember, that at the first step, we expanded the image? Well, it's time to throw away the newly introduced values. For visualization purposes we may also rearrange the quadrants of the result, so that the origin (zero, zero) corresponds with the image center.
magI = magI(Rect(0, 0, magI.cols & -2, magI.rows & -2)); int cx = magI.cols/2; int cy = magI.rows/2; Mat q0(magI, Rect(0, 0, cx, cy)); // Top-Left - Create a ROI per quadrant Mat q1(magI, Rect(cx, 0, cx, cy)); // Top-Right Mat q2(magI, Rect(0, cy, cx, cy)); // Bottom-Left Mat q3(magI, Rect(cx, cy, cx, cy)); // Bottom-Right Mat tmp; // swap quadrants (Top-Left with Bottom-Right) q0.copyTo(tmp); q3.copyTo(q0); tmp.copyTo(q3); q1.copyTo(tmp); // swap quadrant (Top-Right with Bottom-Left) q2.copyTo(q1); tmp.copyTo(q2);
7 歸一化. 目的是為了可視化. 現(xiàn)在已經(jīng)有了一個(gè)級(jí)數(shù)圖像,但是數(shù)據(jù)的范圍超出了0到1的范圍.進(jìn)行歸一化使用的函數(shù)為cv::normalize().
normalize(magI, magI, 0, 1, NORM_MINMAX); // Transform the matrix with float values into a// viewable image form (float between values 0 and 1).
結(jié)果(使用DFT檢測(cè)文本的方向)
一個(gè)應(yīng)用是在圖像中存在的幾何方向。例如,判斷文字的方向是否是橫向的?Looking at some text you'll notice that the text lines sort of form also horizontal lines and the letters form sort of vertical lines. These two main components of a text snippet may be also seen in case of the Fourier transform。 Let us usethis horizontal andthis rotated image about a text.
In case of the horizontal text(文字行在水平方向上排成一行):
In case of a rotated text(文字行被旋轉(zhuǎn)了的情況):
You can see that the most influential components of the frequency domain (brightest dots on the magnitude image) follow the geometric rotation of objects on the image. From this we may calculate the offset and perform an image rotation to correct eventual miss alignments.lisanful
總結(jié)
以上是生活随笔為你收集整理的基于OpenCV完成离散傅里叶变换的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: OpenCV中XML文件和YAML文件的
- 下一篇: OpenCV实现基于傅里叶变换(FFT)