Saturday, March 29, 2014

Frequent Faces

Frequent Faces

There was an interesting article a while ago (it looks like 2006 to be exact) where an image of Einstein was combined with an image of Marilyn Monroe. The resultant hybrid image contained high-frequency information from the Einstein image, and low-frequency information from the Marilyn image:

When you look at the composite image above, it looks like Einstein up close (where you can see the high frequency information, and it dominates) or Marilyn from a distance (or, if you're lucky enough to be short-sighted, without glasses) where the high-frequency information cannot be resolved and only the low frequency info gets through.

Creating Hybrid Images

The actual process for creating images like this is relatively simple. You take a Fourier transform of both images, remove (filter) the high frequency parts from one image (which are just the pixels away from the center) and the low frequency (pixels near the center) from the other, then add the images together. So all you need is a math library with a Fourier transform function. Like NumPy and Python.

The hardest part is actually finding two images that are similar enough to each other that they don't clash too much when combined (who knew Einstein and Marilyn looked so similar?).

I decided to try and combine Patrick Stewart and John Lennon, I found some photos online, lined them up and made them the same size using a paint program.

Next we need a way to filter the Fourier transforms. There's a lot of different ways to do this (eg Butterworth) but I decided to keep it simple and use a Gaussian filter. The code in python looks like:

def gauss_mask(shape, low):
    Return a mask suitable for use on a reduced real FT
    IE zero frequency at 0,0 next at 0,1 1,0 and h-1,0
    h, w = shape
    outw=w/2+1 # if reduced
    # we have full heightfreqs:
    irow = np.fft.fftfreq(h).reshape(h,1)
    # cols are halfed
    icol = np.fft.fftfreq(w)[:outw].reshape(1,outw)
    r = np.exp(-(icol*icol+irow*irow)/(low*low))
    return r

Then it's just a case of applying this to the source images with an appropriate cutoff:

lowf = np.array("stewart_edit.jpg"))
highf = np.array("lennon_edit.jpg"))
h,w,_ = lowf.shape

hipass = 1-gauss_mask((w,h), 15.0/768)
lowpass = gauss_mask((w,h), 15.0/768)
imsave("patrick_low.png", lowout, cmap='gray')
imsave("lennon_high.png", hiout, cmap='gray')

The resulting images look like:

When we add the together (with some suitable weighting) to get:

Which, apart from looking like Harry Potter, has the properties we want -- looks mostly like John Lennon up close and then Patrick Stewart from a distance.

I was using the iPython Notebook for doing this, as it allowed me to easily play around with the parameters and see the intermediate results as I was going along. I've put the both the ipynb file and the plain python code on github (requires NumPy, and iPython for showing images).