Project 3: Face Morphing

Overview

In this project, we learn to morph faces. We accomplish this by partitioning the images into geometric shapes and performing affine transformations on those triangles.

Defining Correspondences

In this section, the goal is to triangulate our images using Delaunay triangulation. Delaunay triangulation will give us reasonably sized triangles, which greatly helps with with stability when using them to morph. We want to use the same triangulation for both images, so we need to perform triangulation on an image that is close to both original pictures. We achieve this by using ginput from matplotlib and compute the Delaunay triangulation on the average of the images.

Computing the "Mid-way Face"

Before completing the morph sequence from one face to another, we compute an intermediate result: the midway face. To do this, we compute the average shape, warp both original faces into that, and then average the colors. We do this by performing an inverse affine transformation and then interpolating to fill in the color where the pixel result is "in between" pixels. By doing this for each person and each triangle in our delaunay triangulation, we can create a smooth image for that will combine the two faces

The Morph Sequence

For the morph sequence, we need to generalize the above intermediate result. Instead of calculating the average between the faces, we take a weighted average of the two images to determine what ratio image we want to warp to. Additionally, when reconstructing post-interpolation, we can decide the ratio at which the original images' colors are pulled. Then, to generate the gif, we simply need to play back the slow change from (0, 0) warp fraction and dissolve fraction to (1, 1). We can increment both values slowly to get a smooth morph sequence as seen below.

The "Mean Face" of a Population

For this section, we look at the Danes dataset. We first compute the average face of the whole population. We do this by morphing each of the faces into the average shape (literally averaging the pixels). Then, we can average each person's morph to the average in order to obtain a clearer average face.

Below are examples of some of the Danes morphed into their average face shape.

Now that we have some data about the average face of Danes (based on the dataset), we can apply Danish features to our own face. We simply perform a morph from our face to the Danish face, making sure to retain our face while taking the shape of the average face. We can do the inverse as well to obtain the average Dane's face, but in the shape of my own face.

As you can see, there are some triangulated distortions apparent in these morphs. I hypothesize the reason why these occur is due to a large difference in the base pictures that I try to morph. My portrait picture of myself was fairly offset from the average Dane face, and perhaps the large shift resulted some issues in the clarity of the morphs.

Caricatures: Extrapolating from the Mean

By making the warp greater than 1, we get the caricature of our face via the data from the Danish population. Here, we set our warp fraction to 2.

Bells and Whistles: Changing Ethnicity

Using the average face of a white male found online, I morph myself to have the features of the average white male. Below, we have morphing just the shape (1, 0), the appearance (0, 1), and then both the shape and the appearance (0.5, 0.5). We can achieve each of these pictures by varying the dissolve fraction and the warp fraction!