You might recall this image being shared on social media some time ago.

Source: http://cvcl.mit.edu/hybrid_gallery/monroe_einstein.html

Look closely and you see Albert Einstein. However, if you move further away (or make the image smaller), you see Marilyn Monroe.

To create the image, the high spatial frequency data from an image of Albert Einstein was added to the low spatial frequency data from an image of Marilyn Monroe. This approach was pioneered by Oliva et al. (2006) and is influenced by the multiscale processing of human vision.

  • When we view objects near us, we see fine detail (that is, higher spatial frequencies dominate).

  • However, when we view objects at a distance, the broad outline has greater influence (that is, lower spatial frequencies dominate).

I thought I'd try to create a similar image in Maple (get the complete application here).

Here's an overview of the approach (as outlined in Oliva et al., 2006). I used different source images of Einstein and Monroe.

Let's start by loading some packages and defining a few procedures.

restart:
with(ImageTools):
with(SignalProcessing):

fft_shift := proc(M)
   local nRows, nCols, quad_1, quad_2, quad_3, quad_4, cRows, cCols;
   nRows, nCols := LinearAlgebra:-Dimensions(M):
   cRows, cCols := ceil(nRows/2), ceil(nCols/2):
   quad_1 := M[1..cRows,      1..cCols]:
   quad_2 := M[1..cRows,      cCols + 1..-1]:  
   quad_3 := M[cRows + 1..-1, cCols + 1..-1]:
   quad_4 := M[cRows + 1..-1, 1..cCols]:
   return <<quad_3, quad_2 |quad_4, quad_1>>:
end proc:

PowerSpectrum2D := proc(M)
   return sqrt~(abs~(M))
end proc:

gaussian_filter := (a, b, sigma) -> Matrix(2 * a, 2 * b, (i, j) -> evalf(exp(-((i - a)^2 + (j - b)^2) / (2 * sigma^2))), datatype = float[8]):

fft_shift() swaps quadrants of a 2D Fourier transform around so that the zero frequency components are in the center.

PowerSpectrum2D() returns the spectra of a 2D Fourier transform

gaussian_filter() will be used to apply a high or low-pass filter in the frequency domain (a and b are the number of rows and columns in the 2D Fourier transform, and sigma is the cut-off frequency.

Now let's import and display the original Einstein and Monroe images (both are the same size).

einstein_img := Read("einstein.png")[..,..,1]:
Embed(einstein_img)

marilyn_img  := Read("monroe.png")[..,..,1]:
Embed(monroe_img)

Let's convert both images to the spatial frequency domain (not many people know that SignalProcessing:-FFT can calculate the Fourier transform of matrices).

einstein_fourier := fft_shift(FFT(einstein_img)):
monroe_fourier   := fft_shift(FFT(monroe_img)):

Visualizing the power spectra of the unfiltered and filtered images isn't necessary, but helps illustrate what we're doing in the frequency domain.

First the spectra of the Einstein image. Lower frequency data is near the center, while higher frequency data is further away from the center.

Embed(Create(PowerSpectrum2D(einstein_fourier)))

Now the spectra of the Monroe image.

Embed(Create(PowerSpectrum2D(monroe_fourier)))

Now we need to filter the frequency content of both images.

First, define the cutoff frequencies for the high and low pass Gaussian filters.

sigma_einstein := 25:
sigma_monroe   := 10:

In the frequency domain, apply a high pass filter to the Einstein image, and a low pass filter to the Monroe image.

nRows, nCols := LinearAlgebra:-Dimension(einstein_img):

einstein_fourier_high_pass := einstein_fourier *~ (1 -~ gaussian_filter(nRows/2, nCols/2, sigma_einstein)):
monroe_fourier_low_pass    := monroe_fourier   *~ gaussian_filter(nRows/2, nCols/2, sigma_monroe):

Here's the spectra of the Einstein and Monroe images after the filtering (compare these to the pre-filtered spectra above).

Embed(Create(PowerSpectrum2D(einstein_fourier_high_pass)))

Embed(Create(PowerSpectrum2D(monroe_fourier_low_pass)))

Before combining both images in the Fourier domain, let's look the individual filtered images.

einstein_high_pass_img := Re~(InverseFFT(fft_shift(einstein_fourier_high_pass))):
monroe_low_pass_img    := Re~(InverseFFT(fft_shift(monroe_fourier_low_pass))):

We're left with sharp detail in the Einstein image.

Embed(FitIntensity(Create(einstein_high_pass_img)))

But the Monroe image is blurry, with only lower spatial frequency data.

Embed(FitIntensity(Create(monroe_low_pass_img)))

For the final image, we're simply going to add the Fourier transforms of both filtered images, and invert to the spatial domain.

hybrid_image := Create(Re~(InverseFFT(fft_shift(monroe_fourier_low_pass + einstein_fourier_high_pass)))):
Embed(hybrid_image)

So that's our final image, and has a similar property to the hybrid image at the top of this post.

  • Move close to the computer monitor and you see Albert Einstein.
  • Move to the other side of the room, and Marilyn Monroe swims into vision (if you're myopic, just take off your glasses and don't move back as much).

To simulate this, here, I've successively reduced the size of the hybrid image

And just because I can, here's a hybrid image of a cat and a dog, generated by the same worksheet.

Please Wait...