mirror of
https://github.com/opencv/opencv.git
synced 2025-06-08 01:53:19 +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}
|
Discrete Fourier Transform {#tutorial_discrete_fourier_transform}
|
||||||
==========================
|
==========================
|
||||||
|
|
||||||
|
@prev_tutorial{tutorial_random_generator_and_text}
|
||||||
|
@next_tutorial{tutorial_file_input_output_with_xml_yml}
|
||||||
|
|
||||||
Goal
|
Goal
|
||||||
----
|
----
|
||||||
|
|
||||||
@ -8,21 +11,49 @@ We'll seek answers for the following questions:
|
|||||||
|
|
||||||
- What is a Fourier transform and why use it?
|
- What is a Fourier transform and why use it?
|
||||||
- How to do it in OpenCV?
|
- How to do it in OpenCV?
|
||||||
- Usage of functions such as: @ref cv::copyMakeBorder() , @ref cv::merge() , @ref cv::dft() , @ref
|
- Usage of functions such as: **copyMakeBorder()** , **merge()** , **dft()** ,
|
||||||
cv::getOptimalDFTSize() , @ref cv::log() and @ref cv::normalize() .
|
**getOptimalDFTSize()** , **log()** and **normalize()** .
|
||||||
|
|
||||||
Source code
|
Source code
|
||||||
-----------
|
-----------
|
||||||
|
|
||||||
|
@add_toggle_cpp
|
||||||
You can [download this from here
|
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
|
find it in the
|
||||||
`samples/cpp/tutorial_code/core/discrete_fourier_transform/discrete_fourier_transform.cpp` of the
|
`samples/cpp/tutorial_code/core/discrete_fourier_transform/discrete_fourier_transform.cpp` of the
|
||||||
OpenCV source code library.
|
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
|
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
|
(*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*):
|
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
|
#### Expand the image to an optimal size
|
||||||
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.
|
|
||||||
|
|
||||||
-# **Make place for both the complex and the real values**. The result of a Fourier Transform is
|
The performance of a DFT is dependent of the image
|
||||||
complex. This implies that for each image value the result is two image values (one per
|
size. It tends to be the fastest for image sizes that are multiple of the numbers two, three and
|
||||||
component). Moreover, the frequency domains range is much larger than its spatial counterpart.
|
five. Therefore, to achieve maximal performance it is generally a good idea to pad border values
|
||||||
Therefore, we store these usually at least in a *float* format. Therefore we'll convert our
|
to the image to get a size with such traits. The **getOptimalDFTSize()** returns this
|
||||||
input image to this type and expand it with another channel to hold the complex values:
|
optimal size and we can use the **copyMakeBorder()** function to expand the borders of an
|
||||||
@code{.cpp}
|
image (the appended pixels are initialized with zero):
|
||||||
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:
|
|
||||||
|
|
||||||
\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:
|
@add_toggle_java
|
||||||
@code{.cpp}
|
@snippet java/tutorial_code/core/discrete_fourier_transform/DiscreteFourierTransform.java expand
|
||||||
split(complexI, planes); // planes[0] = Re(DFT(I), planes[1] = Im(DFT(I))
|
@end_toggle
|
||||||
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:
|
|
||||||
|
|
||||||
\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:
|
#### Make place for both the complex and the real values
|
||||||
@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;
|
|
||||||
|
|
||||||
Mat q0(magI, Rect(0, 0, cx, cy)); // Top-Left - Create a ROI per quadrant
|
The result of a Fourier Transform is
|
||||||
Mat q1(magI, Rect(cx, 0, cx, cy)); // Top-Right
|
complex. This implies that for each image value the result is two image values (one per
|
||||||
Mat q2(magI, Rect(0, cy, cx, cy)); // Bottom-Left
|
component). Moreover, the frequency domains range is much larger than its spatial counterpart.
|
||||||
Mat q3(magI, Rect(cx, cy, cx, cy)); // Bottom-Right
|
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)
|
@add_toggle_cpp
|
||||||
q0.copyTo(tmp);
|
@snippet cpp/tutorial_code/core/discrete_fourier_transform/discrete_fourier_transform.cpp complex_and_real
|
||||||
q3.copyTo(q0);
|
@end_toggle
|
||||||
tmp.copyTo(q3);
|
|
||||||
|
|
||||||
q1.copyTo(tmp); // swap quadrant (Top-Right with Bottom-Left)
|
@add_toggle_java
|
||||||
q2.copyTo(q1);
|
@snippet java/tutorial_code/core/discrete_fourier_transform/DiscreteFourierTransform.java complex_and_real
|
||||||
tmp.copyTo(q2);
|
@end_toggle
|
||||||
@endcode
|
|
||||||
-# **Normalize**. This is done again for visualization purposes. We now have the magnitudes,
|
@add_toggle_python
|
||||||
however this are still out of our image display range of zero to one. We normalize our values to
|
@snippet python/tutorial_code/core/discrete_fourier_transform/discrete_fourier_transform.py complex_and_real
|
||||||
this range using the @ref cv::normalize() function.
|
@end_toggle
|
||||||
@code{.cpp}
|
|
||||||
normalize(magI, magI, 0, 1, NORM_MINMAX); // Transform the matrix with float values into a
|
#### Make the Discrete Fourier Transform
|
||||||
// viewable image form (float between values 0 and 1).
|
It's possible an in-place calculation (same input as
|
||||||
@endcode
|
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
|
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
|
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
|
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
|
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.
|
image about a text.
|
||||||
|
|
||||||
In case of the horizontal 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
|
- @subpage tutorial_discrete_fourier_transform
|
||||||
|
|
||||||
|
*Languages:* C++, Java, Python
|
||||||
|
|
||||||
*Compatibility:* \> OpenCV 2.0
|
*Compatibility:* \> OpenCV 2.0
|
||||||
|
|
||||||
*Author:* Bernát Gábor
|
*Author:* Bernát Gábor
|
||||||
|
@ -8,45 +8,58 @@
|
|||||||
using namespace cv;
|
using namespace cv;
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
|
||||||
static void help(char* progName)
|
static void help(void)
|
||||||
{
|
{
|
||||||
cout << endl
|
cout << endl
|
||||||
<< "This program demonstrated the use of the discrete Fourier transform (DFT). " << 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
|
<< "The dft of an image is taken and it's power spectrum is displayed." << endl
|
||||||
<< "Usage:" << 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)
|
int main(int argc, char ** argv)
|
||||||
{
|
{
|
||||||
help(argv[0]);
|
help();
|
||||||
|
|
||||||
const char* filename = argc >=2 ? argv[1] : "../data/lena.jpg";
|
const char* filename = argc >=2 ? argv[1] : "../data/lena.jpg";
|
||||||
|
|
||||||
Mat I = imread(filename, IMREAD_GRAYSCALE);
|
Mat I = imread(filename, IMREAD_GRAYSCALE);
|
||||||
if( I.empty())
|
if( I.empty()){
|
||||||
|
cout << "Error opening image" << endl;
|
||||||
return -1;
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
//! [expand]
|
||||||
Mat padded; //expand input image to optimal size
|
Mat padded; //expand input image to optimal size
|
||||||
int m = getOptimalDFTSize( I.rows );
|
int m = getOptimalDFTSize( I.rows );
|
||||||
int n = getOptimalDFTSize( I.cols ); // on the border add zero values
|
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));
|
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 planes[] = {Mat_<float>(padded), Mat::zeros(padded.size(), CV_32F)};
|
||||||
Mat complexI;
|
Mat complexI;
|
||||||
merge(planes, 2, complexI); // Add to the expanded another plane with zeros
|
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(complexI, complexI); // this way the result may fit in the source matrix
|
||||||
|
//! [dft]
|
||||||
|
|
||||||
// compute the magnitude and switch to logarithmic scale
|
// compute the magnitude and switch to logarithmic scale
|
||||||
// => log(1 + sqrt(Re(DFT(I))^2 + Im(DFT(I))^2))
|
// => 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))
|
split(complexI, planes); // planes[0] = Re(DFT(I), planes[1] = Im(DFT(I))
|
||||||
magnitude(planes[0], planes[1], planes[0]);// planes[0] = magnitude
|
magnitude(planes[0], planes[1], planes[0]);// planes[0] = magnitude
|
||||||
Mat magI = planes[0];
|
Mat magI = planes[0];
|
||||||
|
//! [magnitude]
|
||||||
|
|
||||||
|
//! [log]
|
||||||
magI += Scalar::all(1); // switch to logarithmic scale
|
magI += Scalar::all(1); // switch to logarithmic scale
|
||||||
log(magI, magI);
|
log(magI, magI);
|
||||||
|
//! [log]
|
||||||
|
|
||||||
|
//! [crop_rearrange]
|
||||||
// crop the spectrum, if it has an odd number of rows or columns
|
// crop the spectrum, if it has an odd number of rows or columns
|
||||||
magI = magI(Rect(0, 0, magI.cols & -2, magI.rows & -2));
|
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)
|
q1.copyTo(tmp); // swap quadrant (Top-Right with Bottom-Left)
|
||||||
q2.copyTo(q1);
|
q2.copyTo(q1);
|
||||||
tmp.copyTo(q2);
|
tmp.copyTo(q2);
|
||||||
|
//! [crop_rearrange]
|
||||||
|
|
||||||
|
//! [normalize]
|
||||||
normalize(magI, magI, 0, 1, NORM_MINMAX); // Transform the matrix with float values into a
|
normalize(magI, magI, 0, 1, NORM_MINMAX); // Transform the matrix with float values into a
|
||||||
// viewable image form (float between values 0 and 1).
|
// viewable image form (float between values 0 and 1).
|
||||||
|
//! [normalize]
|
||||||
|
|
||||||
imshow("Input Image" , I ); // Show the result
|
imshow("Input Image" , I ); // Show the result
|
||||||
imshow("spectrum magnitude", magI);
|
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