Thursday, July 21, 2022

Playing with Phase and Magnitude

 

In this series dedicated to the Fourier Transform, it is important to understand the role played by magnitude (or amplitude) and phase in an image...

1- Real and Imaginary Values in FFT

First, we need a sample, because the ImageJ FFT implementation only works with power-of-2 images, the Boats image (File > Open Samples... > Boats) is cropped in 256x256 and converted in 32-bit. Additionally, a normalization at 0.0% could be done.


Fig. 1: Cropped image of size 256x256 from the boats sample (in File > Open Samples... > Boats).

By default, running the command FFT in ImageJ displays the power spectrum of the image (see post about FFT) corresponding to the intensities of the different frequencies. For this purpose, we need to display all the information contained in the result of FFT (complex numbers).

In Process > FFT > FFT Options..., check the option, then re-run the FFT. Now, a stack composed of the real and imaginary parts is displayed (Fig. 2).

Fig.2: Stack comprising the real and imaginary parts of the FFT in slices 1 and 2, respectively.

2- Magnitude and Phase

By default, there is no way to get the amplitude and phase in the FFT options of ImageJ. Thus, we need to  write a small script...

2-1- Magnitude and Phase
The script is rather simple.

+++ JavaScript snippet +++

/*
* Calculating Magnitude and Phase from the IJ complex FFT
* Jean-Christophe Taveau
* 2022/07/20
*/
const imp = IJ.getImage();
IJ.run("FFT Options...", "fft complex");
IJ.run(imp, "FFT", "");
const power = FFT.forward(imp);
//IJ.run("Swap Quadrants", "");
const fft = IJ.getImage();
const w = fft.getWidth();
const h = fft.getHeight();
print(fft.getNSlices());
print(w + h);
// Create Output
const mag = new FloatProcessor(w,h);
const phase = new FloatProcessor(w,h);
// Calc Magnitude and Phase
const re = fft.getImageStack().getProcessor(1);
const im = fft.getImageStack().getProcessor(2);
for (let i = 0; i < w*h;i++) {
const magi = Math.sqrt(re.getf(i)*re.getf(i) + im.getf(i)*im.getf(i) );
const phasei = Math.atan2(im.getf(i), re.getf(i) );
mag.setf(i,magi);
phase.setf(i,phasei);
}
const stack = new ImageStack();
stack.addSlice(mag);
stack.addSlice(phase);
const output = new ImagePlus("Magnitude and Phase",stack);
output.show();
+++ end of JavaScript snippet +++ 

2-2- Running the script
  1.  Open the image of Fig.1
  2. Run the script fft_mag_phase.js
  3. A stack is created composed of the magnitude and the phase, respectively
  4. You need to rescale (Process > Enhance Contrast...) to get the montage of Fig.2.
Fig. 3: Magnitude and Phase of the image in Fig. 1.



3- Mixing Phase and Magnitude

Now, it could be interesting to see what is the influence of the amplitude and the phase in an image. To explore this question, we can try to mix the amplitude of a first image and the phase of another one and then, compute the inverse FFT to see what is produced.

The pipeline is something like this:

  1. Compute the amplitude and phase of the first image (here, the Boats)
  2. Compute the amplitude and phase of the second image (here, the Clown)
  3. Copy the phase of the Clown into the Boats (second slice).
  4. Convert the amplitude/phase mixed stack into a complex stack. (second JS Script).
  5. Compute the Inverse FFT from the complex stack.
3-1- Second Image

On one hand, we apply the same pipeline to a 256x256 cropped, 32-bit clown image (Fig. 4) i.e. compute the FFT followed by amplitude/phase extractions (Fig.5).

Fig. 4: Input cropped image of size 256x256 from the Clowns sample.
 

Fig. 5: Magnitude and Phase of the Clown image (Fig. 4).
 

From the two stacks of magnitude/phase of the Boats and Clown, copy the phase of the Clown and paste it in the phase of the Boats (Fig. 6).


Fig. 6: Montage of Magnitude of the Boats + Phase of the Clown

3-2- Conversion Magnitude/Phase to Complex values

To compute the Inverse FFT, ImageJ requires a stack composed of the real and imaginary values. Thus, we have to convert the Magnitude and Phase to Complex numbers. The script below does the job by applying these formulas:

 

+++ JavaScript snippet +++

/*
* Calc Real and Imaginary Parts of a FFT
* Jean-Christophe Taveau
* 2022/07/20
*/
const imp = IJ.getImage();
const w = imp.getWidth();
const h = imp.getHeight();
// Create Output
const re = new FloatProcessor(w,h);
const im = new FloatProcessor(w,h);
// Calc Real and Imaginary Parts
const mag = imp.getImageStack().getProcessor(1);
const phase = imp.getImageStack().getProcessor(2);
for (let i = 0; i < w*h;i++) {
const rei = mag.getf(i) * Math.cos(phase.getf(i) );
const imi = mag.getf(i) * Math.sin(phase.getf(i) );
re.setf(i,rei);
im.setf(i,imi);
}
const stack = new ImageStack();
stack.addSlice("Real",re);
stack.addSlice("Imaginary",im);
const output = new ImagePlus("Complex of Signal",stack);
output.show();
+++ end of JavaScript snippet +++

3-3- Result
Fig. 7: Result image of the mixture of phase and amplitude after inverse FFT.

4. Conclusion

Even though, most of the time, we are only interested in the amplitudes via the display of the power spectrum. The phases contain the most important information.

[...] The amplitude of an image represents the intensity of the different frequencies in the image. Therefore, it holds the geometrical structure of features in the image (i.e. changes in the spatial domain).

The phase on the other hand, represents the locations of these features (which helps our human eye to better comprehend the image).[...] (Source: Nitzan [Link] ).

5. Other crazybiocomputing posts

Further readings are available in ...
  • Processing Series  [Link]
  • Image Processing TOC [Link]

 



No comments:

Post a Comment