Tutorial Discrete Fourier Transform

pull/9406/head
tribta 8 years ago
parent 13317bdfda
commit 954e2f9b9c
  1. 260
      doc/tutorials/core/discrete_fourier_transform/discrete_fourier_transform.markdown
  2. 2
      doc/tutorials/core/table_of_content_core.markdown
  3. 24
      samples/cpp/tutorial_code/core/discrete_fourier_transform/discrete_fourier_transform.cpp
  4. 109
      samples/java/tutorial_code/core/discrete_fourier_transform/DiscreteFourierTransform.java
  5. 80
      samples/python/tutorial_code/core/discrete_fourier_transform/discrete_fourier_transform.py

@ -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
@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
@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
Here's a sample usage of @ref cv::dft() : @add_toggle_java
@include java/tutorial_code/core/discrete_fourier_transform/DiscreteFourierTransform.java
@end_toggle
@includelineno cpp/tutorial_code/core/discrete_fourier_transform/discrete_fourier_transform.cpp @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 The performance of a DFT is dependent of the image
to the image to get a size with such traits. The @ref cv::getOptimalDFTSize() returns this size. It tends to be the fastest for image sizes that are multiple of the numbers two, three and
optimal size and we can use the @ref cv::copyMakeBorder() function to expand the borders of an five. Therefore, to achieve maximal performance it is generally a good idea to pad border values
image: to the image to get a size with such traits. The **getOptimalDFTSize()** returns this
@code{.cpp} optimal size and we can use the **copyMakeBorder()** function to expand the borders of an
Mat padded; //expand input image to optimal size image (the appended pixels are initialized with zero):
int m = getOptimalDFTSize( I.rows );
int n = getOptimalDFTSize( I.cols ); // on the border add zero pixels @add_toggle_cpp
copyMakeBorder(I, padded, 0, m - I.rows, 0, n - I.cols, BORDER_CONSTANT, Scalar::all(0)); @snippet cpp/tutorial_code/core/discrete_fourier_transform/discrete_fourier_transform.cpp expand
@endcode @end_toggle
The appended pixels are initialized with zero.
@add_toggle_java
-# **Make place for both the complex and the real values**. The result of a Fourier Transform is @snippet java/tutorial_code/core/discrete_fourier_transform/DiscreteFourierTransform.java expand
complex. This implies that for each image value the result is two image values (one per @end_toggle
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 @add_toggle_python
input image to this type and expand it with another channel to hold the complex values: @snippet python/tutorial_code/core/discrete_fourier_transform/discrete_fourier_transform.py expand
@code{.cpp} @end_toggle
Mat planes[] = {Mat_<float>(padded), Mat::zeros(padded.size(), CV_32F)};
Mat complexI; #### Make place for both the complex and the real values
merge(planes, 2, complexI); // Add to the expanded another plane with zeros
@endcode The result of a Fourier Transform is
-# **Make the Discrete Fourier Transform**. It's possible an in-place calculation (same input as complex. This implies that for each image value the result is two image values (one per
output): component). Moreover, the frequency domains range is much larger than its spatial counterpart.
@code{.cpp} Therefore, we store these usually at least in a *float* format. Therefore we'll convert our
dft(complexI, complexI); // this way the result may fit in the source matrix input image to this type and expand it with another channel to hold the complex values:
@endcode
-# **Transform the real and complex values to magnitude**. A complex number has a real (*Re*) and a @add_toggle_cpp
complex (imaginary - *Im*) part. The results of a DFT are complex numbers. The magnitude of a @snippet cpp/tutorial_code/core/discrete_fourier_transform/discrete_fourier_transform.cpp complex_and_real
DFT is: @end_toggle
\f[M = \sqrt[2]{ {Re(DFT(I))}^2 + {Im(DFT(I))}^2}\f] @add_toggle_java
@snippet java/tutorial_code/core/discrete_fourier_transform/DiscreteFourierTransform.java complex_and_real
Translated to OpenCV code: @end_toggle
@code{.cpp}
split(complexI, planes); // planes[0] = Re(DFT(I), planes[1] = Im(DFT(I)) @add_toggle_python
magnitude(planes[0], planes[1], planes[0]);// planes[0] = magnitude @snippet python/tutorial_code/core/discrete_fourier_transform/discrete_fourier_transform.py complex_and_real
Mat magI = planes[0]; @end_toggle
@endcode
-# **Switch to a logarithmic scale**. It turns out that the dynamic range of the Fourier #### Make the Discrete Fourier Transform
coefficients is too large to be displayed on the screen. We have some small and some high It's possible an in-place calculation (same input as
changing values that we can't observe like this. Therefore the high values will all turn out as output):
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_cpp
@snippet cpp/tutorial_code/core/discrete_fourier_transform/discrete_fourier_transform.cpp dft
\f[M_1 = \log{(1 + M)}\f] @end_toggle
Translated to OpenCV code: @add_toggle_java
@code{.cpp} @snippet java/tutorial_code/core/discrete_fourier_transform/DiscreteFourierTransform.java dft
magI += Scalar::all(1); // switch to logarithmic scale @end_toggle
log(magI, magI);
@endcode @add_toggle_python
-# **Crop and rearrange**. Remember, that at the first step, we expanded the image? Well, it's time @snippet python/tutorial_code/core/discrete_fourier_transform/discrete_fourier_transform.py dft
to throw away the newly introduced values. For visualization purposes we may also rearrange the @end_toggle
quadrants of the result, so that the origin (zero, zero) corresponds with the image center.
@code{.cpp} #### Transform the real and complex values to magnitude
magI = magI(Rect(0, 0, magI.cols & -2, magI.rows & -2)); A complex number has a real (*Re*) and a
int cx = magI.cols/2; complex (imaginary - *Im*) part. The results of a DFT are complex numbers. The magnitude of a
int cy = magI.rows/2; DFT is:
Mat q0(magI, Rect(0, 0, cx, cy)); // Top-Left - Create a ROI per quadrant \f[M = \sqrt[2]{ {Re(DFT(I))}^2 + {Im(DFT(I))}^2}\f]
Mat q1(magI, Rect(cx, 0, cx, cy)); // Top-Right
Mat q2(magI, Rect(0, cy, cx, cy)); // Bottom-Left Translated to OpenCV code:
Mat q3(magI, Rect(cx, cy, cx, cy)); // Bottom-Right
@add_toggle_cpp
Mat tmp; // swap quadrants (Top-Left with Bottom-Right) @snippet cpp/tutorial_code/core/discrete_fourier_transform/discrete_fourier_transform.cpp magnitude
q0.copyTo(tmp); @end_toggle
q3.copyTo(q0);
tmp.copyTo(q3); @add_toggle_java
@snippet java/tutorial_code/core/discrete_fourier_transform/DiscreteFourierTransform.java magnitude
q1.copyTo(tmp); // swap quadrant (Top-Right with Bottom-Left) @end_toggle
q2.copyTo(q1);
tmp.copyTo(q2); @add_toggle_python
@endcode @snippet python/tutorial_code/core/discrete_fourier_transform/discrete_fourier_transform.py magnitude
-# **Normalize**. This is done again for visualization purposes. We now have the magnitudes, @end_toggle
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. #### Switch to a logarithmic scale
@code{.cpp} It turns out that the dynamic range of the Fourier
normalize(magI, magI, 0, 1, NORM_MINMAX); // Transform the matrix with float values into a coefficients is too large to be displayed on the screen. We have some small and some high
// viewable image form (float between values 0 and 1). changing values that we can't observe like this. Therefore the high values will all turn out as
@endcode 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…
Cancel
Save