mirror of
https://github.com/opencv/opencv.git
synced 2025-06-07 17:44:04 +08:00
Tutorial Discrete Fourier Transform
This commit is contained in:
parent
13317bdfda
commit
954e2f9b9c
@ -1,6 +1,9 @@
|
||||
Discrete Fourier Transform {#tutorial_discrete_fourier_transform}
|
||||
==========================
|
||||
|
||||
@prev_tutorial{tutorial_random_generator_and_text}
|
||||
@next_tutorial{tutorial_file_input_output_with_xml_yml}
|
||||
|
||||
Goal
|
||||
----
|
||||
|
||||
@ -8,21 +11,49 @@ We'll seek answers for the following questions:
|
||||
|
||||
- What is a Fourier transform and why use it?
|
||||
- How to do it in OpenCV?
|
||||
- Usage of functions such as: @ref cv::copyMakeBorder() , @ref cv::merge() , @ref cv::dft() , @ref
|
||||
cv::getOptimalDFTSize() , @ref cv::log() and @ref cv::normalize() .
|
||||
- Usage of functions such as: **copyMakeBorder()** , **merge()** , **dft()** ,
|
||||
**getOptimalDFTSize()** , **log()** and **normalize()** .
|
||||
|
||||
Source code
|
||||
-----------
|
||||
|
||||
@add_toggle_cpp
|
||||
You can [download this from here
|
||||
](https://github.com/opencv/opencv/tree/master/samples/cpp/tutorial_code/core/discrete_fourier_transform/discrete_fourier_transform.cpp) or
|
||||
](https://raw.githubusercontent.com/opencv/opencv/master/samples/cpp/tutorial_code/core/discrete_fourier_transform/discrete_fourier_transform.cpp) or
|
||||
find it in the
|
||||
`samples/cpp/tutorial_code/core/discrete_fourier_transform/discrete_fourier_transform.cpp` of the
|
||||
OpenCV source code library.
|
||||
@end_toggle
|
||||
|
||||
Here's a sample usage of @ref cv::dft() :
|
||||
@add_toggle_java
|
||||
You can [download this from here
|
||||
](https://raw.githubusercontent.com/opencv/opencv/master/samples/java/tutorial_code/core/discrete_fourier_transform/DiscreteFourierTransform.java) or
|
||||
find it in the
|
||||
`samples/java/tutorial_code/core/discrete_fourier_transform/DiscreteFourierTransform.java` of the
|
||||
OpenCV source code library.
|
||||
@end_toggle
|
||||
|
||||
@includelineno cpp/tutorial_code/core/discrete_fourier_transform/discrete_fourier_transform.cpp
|
||||
@add_toggle_python
|
||||
You can [download this from here
|
||||
](https://raw.githubusercontent.com/opencv/opencv/master/samples/python/tutorial_code/core/discrete_fourier_transform/discrete_fourier_transform.py) or
|
||||
find it in the
|
||||
`samples/python/tutorial_code/core/discrete_fourier_transform/discrete_fourier_transform.py` of the
|
||||
OpenCV source code library.
|
||||
@end_toggle
|
||||
|
||||
Here's a sample usage of **dft()** :
|
||||
|
||||
@add_toggle_cpp
|
||||
@include cpp/tutorial_code/core/discrete_fourier_transform/discrete_fourier_transform.cpp
|
||||
@end_toggle
|
||||
|
||||
@add_toggle_java
|
||||
@include java/tutorial_code/core/discrete_fourier_transform/DiscreteFourierTransform.java
|
||||
@end_toggle
|
||||
|
||||
@add_toggle_python
|
||||
@include python/tutorial_code/core/discrete_fourier_transform/discrete_fourier_transform.py
|
||||
@end_toggle
|
||||
|
||||
Explanation
|
||||
-----------
|
||||
@ -49,89 +80,140 @@ Fourier Transform too needs to be of a discrete type resulting in a Discrete Fou
|
||||
(*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 image *I*):
|
||||
|
||||
-# **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. The @ref cv::getOptimalDFTSize() returns this
|
||||
optimal size and we can use the @ref cv::copyMakeBorder() function to expand the borders of an
|
||||
image:
|
||||
@code{.cpp}
|
||||
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));
|
||||
@endcode
|
||||
The appended pixels are initialized with zero.
|
||||
#### Expand the image to an optimal size
|
||||
|
||||
-# **Make place for both the complex and the real values**. The result of a Fourier Transform is
|
||||
complex. This implies that for each image value the result is two image values (one per
|
||||
component). Moreover, the frequency domains range is much larger than its spatial counterpart.
|
||||
Therefore, we store these usually at least in a *float* format. Therefore we'll convert our
|
||||
input image to this type and expand it with another channel to hold the complex values:
|
||||
@code{.cpp}
|
||||
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
|
||||
@endcode
|
||||
-# **Make the Discrete Fourier Transform**. It's possible an in-place calculation (same input as
|
||||
output):
|
||||
@code{.cpp}
|
||||
dft(complexI, complexI); // this way the result may fit in the source matrix
|
||||
@endcode
|
||||
-# **Transform the real and complex values to magnitude**. A complex number has a real (*Re*) and a
|
||||
complex (imaginary - *Im*) part. The results of a DFT are complex numbers. The magnitude of a
|
||||
DFT is:
|
||||
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. The **getOptimalDFTSize()** returns this
|
||||
optimal size and we can use the **copyMakeBorder()** function to expand the borders of an
|
||||
image (the appended pixels are initialized with zero):
|
||||
|
||||
\f[M = \sqrt[2]{ {Re(DFT(I))}^2 + {Im(DFT(I))}^2}\f]
|
||||
@add_toggle_cpp
|
||||
@snippet cpp/tutorial_code/core/discrete_fourier_transform/discrete_fourier_transform.cpp expand
|
||||
@end_toggle
|
||||
|
||||
Translated to OpenCV code:
|
||||
@code{.cpp}
|
||||
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];
|
||||
@endcode
|
||||
-# **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:
|
||||
@add_toggle_java
|
||||
@snippet java/tutorial_code/core/discrete_fourier_transform/DiscreteFourierTransform.java expand
|
||||
@end_toggle
|
||||
|
||||
\f[M_1 = \log{(1 + M)}\f]
|
||||
@add_toggle_python
|
||||
@snippet python/tutorial_code/core/discrete_fourier_transform/discrete_fourier_transform.py expand
|
||||
@end_toggle
|
||||
|
||||
Translated to OpenCV code:
|
||||
@code{.cpp}
|
||||
magI += Scalar::all(1); // switch to logarithmic scale
|
||||
log(magI, magI);
|
||||
@endcode
|
||||
-# **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.
|
||||
@code{.cpp}
|
||||
magI = magI(Rect(0, 0, magI.cols & -2, magI.rows & -2));
|
||||
int cx = magI.cols/2;
|
||||
int cy = magI.rows/2;
|
||||
#### Make place for both the complex and the real values
|
||||
|
||||
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
|
||||
The result of a Fourier Transform is
|
||||
complex. This implies that for each image value the result is two image values (one per
|
||||
component). Moreover, the frequency domains range is much larger than its spatial counterpart.
|
||||
Therefore, we store these usually at least in a *float* format. Therefore we'll convert our
|
||||
input image to this type and expand it with another channel to hold the complex values:
|
||||
|
||||
Mat tmp; // swap quadrants (Top-Left with Bottom-Right)
|
||||
q0.copyTo(tmp);
|
||||
q3.copyTo(q0);
|
||||
tmp.copyTo(q3);
|
||||
@add_toggle_cpp
|
||||
@snippet cpp/tutorial_code/core/discrete_fourier_transform/discrete_fourier_transform.cpp complex_and_real
|
||||
@end_toggle
|
||||
|
||||
q1.copyTo(tmp); // swap quadrant (Top-Right with Bottom-Left)
|
||||
q2.copyTo(q1);
|
||||
tmp.copyTo(q2);
|
||||
@endcode
|
||||
-# **Normalize**. This is done again for visualization purposes. We now have the magnitudes,
|
||||
however this are still out of our image display range of zero to one. We normalize our values to
|
||||
this range using the @ref cv::normalize() function.
|
||||
@code{.cpp}
|
||||
normalize(magI, magI, 0, 1, NORM_MINMAX); // Transform the matrix with float values into a
|
||||
// viewable image form (float between values 0 and 1).
|
||||
@endcode
|
||||
@add_toggle_java
|
||||
@snippet java/tutorial_code/core/discrete_fourier_transform/DiscreteFourierTransform.java complex_and_real
|
||||
@end_toggle
|
||||
|
||||
@add_toggle_python
|
||||
@snippet python/tutorial_code/core/discrete_fourier_transform/discrete_fourier_transform.py complex_and_real
|
||||
@end_toggle
|
||||
|
||||
#### Make the Discrete Fourier Transform
|
||||
It's possible an in-place calculation (same input as
|
||||
output):
|
||||
|
||||
@add_toggle_cpp
|
||||
@snippet cpp/tutorial_code/core/discrete_fourier_transform/discrete_fourier_transform.cpp dft
|
||||
@end_toggle
|
||||
|
||||
@add_toggle_java
|
||||
@snippet java/tutorial_code/core/discrete_fourier_transform/DiscreteFourierTransform.java dft
|
||||
@end_toggle
|
||||
|
||||
@add_toggle_python
|
||||
@snippet python/tutorial_code/core/discrete_fourier_transform/discrete_fourier_transform.py dft
|
||||
@end_toggle
|
||||
|
||||
#### Transform the real and complex values to magnitude
|
||||
A complex number has a real (*Re*) and a
|
||||
complex (imaginary - *Im*) part. The results of a DFT are complex numbers. The magnitude of a
|
||||
DFT is:
|
||||
|
||||
\f[M = \sqrt[2]{ {Re(DFT(I))}^2 + {Im(DFT(I))}^2}\f]
|
||||
|
||||
Translated to OpenCV code:
|
||||
|
||||
@add_toggle_cpp
|
||||
@snippet cpp/tutorial_code/core/discrete_fourier_transform/discrete_fourier_transform.cpp magnitude
|
||||
@end_toggle
|
||||
|
||||
@add_toggle_java
|
||||
@snippet java/tutorial_code/core/discrete_fourier_transform/DiscreteFourierTransform.java magnitude
|
||||
@end_toggle
|
||||
|
||||
@add_toggle_python
|
||||
@snippet python/tutorial_code/core/discrete_fourier_transform/discrete_fourier_transform.py magnitude
|
||||
@end_toggle
|
||||
|
||||
#### 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:
|
||||
|
||||
\f[M_1 = \log{(1 + M)}\f]
|
||||
|
||||
Translated to OpenCV code:
|
||||
|
||||
@add_toggle_cpp
|
||||
@snippet cpp/tutorial_code/core/discrete_fourier_transform/discrete_fourier_transform.cpp log
|
||||
@end_toggle
|
||||
|
||||
@add_toggle_java
|
||||
@snippet java/tutorial_code/core/discrete_fourier_transform/DiscreteFourierTransform.java log
|
||||
@end_toggle
|
||||
|
||||
@add_toggle_python
|
||||
@snippet python/tutorial_code/core/discrete_fourier_transform/discrete_fourier_transform.py log
|
||||
@end_toggle
|
||||
|
||||
#### 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.
|
||||
|
||||
@add_toggle_cpp
|
||||
@snippet cpp/tutorial_code/core/discrete_fourier_transform/discrete_fourier_transform.cpp crop_rearrange
|
||||
@end_toggle
|
||||
|
||||
@add_toggle_java
|
||||
@snippet java/tutorial_code/core/discrete_fourier_transform/DiscreteFourierTransform.java crop_rearrange
|
||||
@end_toggle
|
||||
|
||||
@add_toggle_python
|
||||
@snippet python/tutorial_code/core/discrete_fourier_transform/discrete_fourier_transform.py crop_rearrange
|
||||
@end_toggle
|
||||
|
||||
#### Normalize
|
||||
This is done again for visualization purposes. We now have the magnitudes,
|
||||
however this are still out of our image display range of zero to one. We normalize our values to
|
||||
this range using the @ref cv::normalize() function.
|
||||
|
||||
@add_toggle_cpp
|
||||
@snippet cpp/tutorial_code/core/discrete_fourier_transform/discrete_fourier_transform.cpp normalize
|
||||
@end_toggle
|
||||
|
||||
@add_toggle_java
|
||||
@snippet java/tutorial_code/core/discrete_fourier_transform/DiscreteFourierTransform.java normalize
|
||||
@end_toggle
|
||||
|
||||
@add_toggle_python
|
||||
@snippet python/tutorial_code/core/discrete_fourier_transform/discrete_fourier_transform.py normalize
|
||||
@end_toggle
|
||||
|
||||
Result
|
||||
------
|
||||
@ -140,7 +222,7 @@ An application idea would be to determine the geometrical orientation present in
|
||||
example, let us find out if a text is horizontal or not? 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 use
|
||||
[this horizontal ](https://github.com/opencv/opencv/tree/master/samples/data/imageTextN.png) and [this rotated](https://github.com/opencv/opencv/tree/master/samples/data/imageTextR.png)
|
||||
[this horizontal ](https://raw.githubusercontent.com/opencv/opencv/master/samples/data/imageTextN.png) and [this rotated](https://raw.githubusercontent.com/opencv/opencv/master/samples/data/imageTextR.png)
|
||||
image about a text.
|
||||
|
||||
In case of the horizontal text:
|
||||
|
@ -76,6 +76,8 @@ understanding how to manipulate the images on a pixel level.
|
||||
|
||||
- @subpage tutorial_discrete_fourier_transform
|
||||
|
||||
*Languages:* C++, Java, Python
|
||||
|
||||
*Compatibility:* \> OpenCV 2.0
|
||||
|
||||
*Author:* Bernát Gábor
|
||||
|
@ -8,45 +8,58 @@
|
||||
using namespace cv;
|
||||
using namespace std;
|
||||
|
||||
static void help(char* progName)
|
||||
static void help(void)
|
||||
{
|
||||
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;
|
||||
<< "./discrete_fourier_transform [image_name -- default ../data/lena.jpg]" << endl;
|
||||
}
|
||||
|
||||
int main(int argc, char ** argv)
|
||||
{
|
||||
help(argv[0]);
|
||||
help();
|
||||
|
||||
const char* filename = argc >=2 ? argv[1] : "../data/lena.jpg";
|
||||
|
||||
Mat I = imread(filename, IMREAD_GRAYSCALE);
|
||||
if( I.empty())
|
||||
if( I.empty()){
|
||||
cout << "Error opening image" << endl;
|
||||
return -1;
|
||||
}
|
||||
|
||||
//! [expand]
|
||||
Mat padded; //expand input image to optimal size
|
||||
int m = getOptimalDFTSize( I.rows );
|
||||
int n = getOptimalDFTSize( I.cols ); // on the border add zero values
|
||||
copyMakeBorder(I, padded, 0, m - I.rows, 0, n - I.cols, BORDER_CONSTANT, Scalar::all(0));
|
||||
//! [expand]
|
||||
|
||||
//! [complex_and_real]
|
||||
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
|
||||
//! [complex_and_real]
|
||||
|
||||
//! [dft]
|
||||
dft(complexI, complexI); // this way the result may fit in the source matrix
|
||||
//! [dft]
|
||||
|
||||
// compute the magnitude and switch to logarithmic scale
|
||||
// => log(1 + sqrt(Re(DFT(I))^2 + Im(DFT(I))^2))
|
||||
//! [magnitude]
|
||||
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];
|
||||
//! [magnitude]
|
||||
|
||||
//! [log]
|
||||
magI += Scalar::all(1); // switch to logarithmic scale
|
||||
log(magI, magI);
|
||||
//! [log]
|
||||
|
||||
//! [crop_rearrange]
|
||||
// crop the spectrum, if it has an odd number of rows or columns
|
||||
magI = magI(Rect(0, 0, magI.cols & -2, magI.rows & -2));
|
||||
|
||||
@ -67,9 +80,12 @@ int main(int argc, char ** argv)
|
||||
q1.copyTo(tmp); // swap quadrant (Top-Right with Bottom-Left)
|
||||
q2.copyTo(q1);
|
||||
tmp.copyTo(q2);
|
||||
//! [crop_rearrange]
|
||||
|
||||
//! [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).
|
||||
//! [normalize]
|
||||
|
||||
imshow("Input Image" , I ); // Show the result
|
||||
imshow("spectrum magnitude", magI);
|
||||
|
@ -0,0 +1,109 @@
|
||||
import org.opencv.core.*;
|
||||
import org.opencv.highgui.HighGui;
|
||||
import org.opencv.imgcodecs.Imgcodecs;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.*;
|
||||
|
||||
class DiscreteFourierTransformRun{
|
||||
private void help() {
|
||||
System.out.println("" +
|
||||
"This program demonstrated the use of the discrete Fourier transform (DFT). \n" +
|
||||
"The dft of an image is taken and it's power spectrum is displayed.\n" +
|
||||
"Usage:\n" +
|
||||
"./DiscreteFourierTransform [image_name -- default ../data/lena.jpg]");
|
||||
}
|
||||
|
||||
public void run(String[] args){
|
||||
|
||||
help();
|
||||
|
||||
String filename = ((args.length > 0) ? args[0] : "../data/lena.jpg");
|
||||
|
||||
Mat I = Imgcodecs.imread(filename, Imgcodecs.IMREAD_GRAYSCALE);
|
||||
if( I.empty() ) {
|
||||
System.out.println("Error opening image");
|
||||
System.exit(-1);
|
||||
}
|
||||
|
||||
//! [expand]
|
||||
Mat padded = new Mat(); //expand input image to optimal size
|
||||
int m = Core.getOptimalDFTSize( I.rows() );
|
||||
int n = Core.getOptimalDFTSize( I.cols() ); // on the border add zero values
|
||||
Core.copyMakeBorder(I, padded, 0, m - I.rows(), 0, n - I.cols(), Core.BORDER_CONSTANT, Scalar.all(0));
|
||||
//! [expand]
|
||||
|
||||
//! [complex_and_real]
|
||||
List<Mat> planes = new ArrayList<Mat>();
|
||||
padded.convertTo(padded, CvType.CV_32F);
|
||||
planes.add(padded);
|
||||
planes.add(Mat.zeros(padded.size(), CvType.CV_32F));
|
||||
Mat complexI = new Mat();
|
||||
Core.merge(planes, complexI); // Add to the expanded another plane with zeros
|
||||
//! [complex_and_real]
|
||||
|
||||
//! [dft]
|
||||
Core.dft(complexI, complexI); // this way the result may fit in the source matrix
|
||||
//! [dft]
|
||||
|
||||
// compute the magnitude and switch to logarithmic scale
|
||||
// => log(1 + sqrt(Re(DFT(I))^2 + Im(DFT(I))^2))
|
||||
//! [magnitude]
|
||||
Core.split(complexI, planes); // planes.get(0) = Re(DFT(I)
|
||||
// planes.get(1) = Im(DFT(I))
|
||||
Core.magnitude(planes.get(0), planes.get(1), planes.get(0));// planes.get(0) = magnitude
|
||||
Mat magI = planes.get(0);
|
||||
//! [magnitude]
|
||||
|
||||
//! [log]
|
||||
Mat matOfOnes = Mat.ones(magI.size(), magI.type());
|
||||
Core.add(matOfOnes, magI, magI); // switch to logarithmic scale
|
||||
Core.log(magI, magI);
|
||||
//! [log]
|
||||
|
||||
//! [crop_rearrange]
|
||||
// crop the spectrum, if it has an odd number of rows or columns
|
||||
magI = magI.submat(new Rect(0, 0, magI.cols() & -2, magI.rows() & -2));
|
||||
|
||||
// rearrange the quadrants of Fourier image so that the origin is at the image center
|
||||
int cx = magI.cols()/2;
|
||||
int cy = magI.rows()/2;
|
||||
|
||||
Mat q0 = new Mat(magI, new Rect(0, 0, cx, cy)); // Top-Left - Create a ROI per quadrant
|
||||
Mat q1 = new Mat(magI, new Rect(cx, 0, cx, cy)); // Top-Right
|
||||
Mat q2 = new Mat(magI, new Rect(0, cy, cx, cy)); // Bottom-Left
|
||||
Mat q3 = new Mat(magI, new Rect(cx, cy, cx, cy)); // Bottom-Right
|
||||
|
||||
Mat tmp = new Mat(); // 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);
|
||||
//! [crop_rearrange]
|
||||
|
||||
magI.convertTo(magI, CvType.CV_8UC1);
|
||||
//! [normalize]
|
||||
Core.normalize(magI, magI, 0, 255, Core.NORM_MINMAX, CvType.CV_8UC1); // Transform the matrix with float values
|
||||
// into a viewable image form (float between
|
||||
// values 0 and 255).
|
||||
//! [normalize]
|
||||
|
||||
HighGui.imshow("Input Image" , I ); // Show the result
|
||||
HighGui.imshow("Spectrum Magnitude", magI);
|
||||
HighGui.waitKey();
|
||||
|
||||
System.exit(0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public class DiscreteFourierTransform {
|
||||
public static void main(String[] args) {
|
||||
// Load the native library.
|
||||
System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
|
||||
new DiscreteFourierTransformRun().run(args);
|
||||
}
|
||||
}
|
@ -0,0 +1,80 @@
|
||||
from __future__ import print_function
|
||||
import sys
|
||||
|
||||
import cv2
|
||||
import numpy as np
|
||||
|
||||
|
||||
def print_help():
|
||||
print('''
|
||||
This program demonstrated the use of the discrete Fourier transform (DFT).
|
||||
The dft of an image is taken and it's power spectrum is displayed.
|
||||
Usage:
|
||||
discrete_fourier_transform.py [image_name -- default ../../../../data/lena.jpg]''')
|
||||
|
||||
|
||||
def main(argv):
|
||||
|
||||
print_help()
|
||||
|
||||
filename = argv[0] if len(argv) > 0 else "../../../../data/lena.jpg"
|
||||
|
||||
I = cv2.imread(filename, cv2.IMREAD_GRAYSCALE)
|
||||
if I is None:
|
||||
print('Error opening image')
|
||||
return -1
|
||||
## [expand]
|
||||
rows, cols = I.shape
|
||||
m = cv2.getOptimalDFTSize( rows )
|
||||
n = cv2.getOptimalDFTSize( cols )
|
||||
padded = cv2.copyMakeBorder(I, 0, m - rows, 0, n - cols, cv2.BORDER_CONSTANT, value=[0, 0, 0])
|
||||
## [expand]
|
||||
## [complex_and_real]
|
||||
planes = [np.float32(padded), np.zeros(padded.shape, np.float32)]
|
||||
complexI = cv2.merge(planes) # Add to the expanded another plane with zeros
|
||||
## [complex_and_real]
|
||||
## [dft]
|
||||
cv2.dft(complexI, complexI) # this way the result may fit in the source matrix
|
||||
## [dft]
|
||||
# compute the magnitude and switch to logarithmic scale
|
||||
# = > log(1 + sqrt(Re(DFT(I)) ^ 2 + Im(DFT(I)) ^ 2))
|
||||
## [magnitude]
|
||||
cv2.split(complexI, planes) # planes[0] = Re(DFT(I), planes[1] = Im(DFT(I))
|
||||
cv2.magnitude(planes[0], planes[1], planes[0])# planes[0] = magnitude
|
||||
magI = planes[0]
|
||||
## [magnitude]
|
||||
## [log]
|
||||
matOfOnes = np.ones(magI.shape, dtype=magI.dtype)
|
||||
cv2.add(matOfOnes, magI, magI) # switch to logarithmic scale
|
||||
cv2.log(magI, magI)
|
||||
## [log]
|
||||
## [crop_rearrange]
|
||||
magI_rows, magI_cols = magI.shape
|
||||
# crop the spectrum, if it has an odd number of rows or columns
|
||||
magI = magI[0:(magI_rows & -2), 0:(magI_cols & -2)]
|
||||
cx = int(magI_rows/2)
|
||||
cy = int(magI_cols/2)
|
||||
|
||||
q0 = magI[0:cx, 0:cy] # Top-Left - Create a ROI per quadrant
|
||||
q1 = magI[cx:cx+cx, 0:cy] # Top-Right
|
||||
q2 = magI[0:cx, cy:cy+cy] # Bottom-Left
|
||||
q3 = magI[cx:cx+cx, cy:cy+cy] # Bottom-Right
|
||||
|
||||
tmp = np.copy(q0) # swap quadrants (Top-Left with Bottom-Right)
|
||||
magI[0:cx, 0:cy] = q3
|
||||
magI[cx:cx + cx, cy:cy + cy] = tmp
|
||||
|
||||
tmp = np.copy(q1) # swap quadrant (Top-Right with Bottom-Left)
|
||||
magI[cx:cx + cx, 0:cy] = q2
|
||||
magI[0:cx, cy:cy + cy] = tmp
|
||||
## [crop_rearrange]
|
||||
## [normalize]
|
||||
cv2.normalize(magI, magI, 0, 1, cv2.NORM_MINMAX) # Transform the matrix with float values into a
|
||||
## viewable image form(float between values 0 and 1).
|
||||
## [normalize]
|
||||
cv2.imshow("Input Image" , I ) # Show the result
|
||||
cv2.imshow("spectrum magnitude", magI)
|
||||
cv2.waitKey()
|
||||
|
||||
if __name__ == "__main__":
|
||||
main(sys.argv[1:])
|
Loading…
Reference in New Issue
Block a user