Project 3 - Face Morphing

By Ethan Chen

Introduction

This project aims to morph one face with another and creating a caricature from a population of faces. Disclaimer: some images, especially triangulations, are not the same dimensions as the raw image because the former was screenshotted vs. saved through code.

Part 1: Defining Correspondences

First, I took a picture of my own face in a way that mimics the structure of the photo of George Clooney (taken by Martin Schoeller). Then, I aligned the faces using code from Project 2 to ensure the images are the same shape. Then, with some cv2 code I wrote myself, I selected the facial keypoints on the images of both George and my face to be the correspondence points, including the 4 corner ones (I wrote some code to adjust the selection of corner points).

Raw Image of George

george_small.jpg

Raw Image of Ethan

ethan.jpg

After aligning

Aligned Image of George

aligned_george.jpg

Aligned Image of Ethan

aligned_ethan.jpg

Then I compute the facial keypoints, which I then call scipy.spatial.Delaunay on to get the Delaunay triangulation for the mesh, which is how we set up the task of converting source triangles to destination ones for the image warping.

Triangulation of George

george_triangulation.jpg

Triangulation of Ethan

ethan_triangulation.jpg

Now, we can get the average of the correspondnece points and display that triangulation

Triangulation of George using Mean of Points

george_mean_triangulation.jpg

Triangulation of Ethan using Mean of Points

ethan_mean_triangulation.jpg

Part 2: Computing the "Mid-Way Face"

To find the "mid-way face" between images A and B, we can use the average shape by warping A and B into that and then average their colors together.

This is the "mid-way face" of George's face with mine.

Original George (Aligned)

george_aligned.jpg

"Mid-Way face" of George's Face with Ethan's

mid_way_face.jpg

Original Ethan (Aligned)

ethan_aligned.jpg

Part 3: The Morph Sequence

We take a few steps. We need to pad the last row of matrix A with 0 0 1 and the last element on the B vector with 1 in order to accomodate translations. Without this pad, we would only be able to allow rotation, scaling, and shearing transformations.

GIF of George's Face Morphing into Ethan's Face

converted_morph_sequence_fps_30_num_points_41.gif

Part 4: The "Mean Face" of a Population

I sourced my images from the FEI database. I chose to use the dataset of spatially normalized images (not cropped) – 2 subsets of 100 non-smiiling images and 100 smiling images - because we are also given 46 annotated points for each of the 200 images. None of the 46 points were corners so I needed to manually add the corner points, which was simple to do through code since each face took up the entire image so we could just programmatically append the corners directly to the list of 46. Without adding corner points, we wouldn't account for the Delaunay triangles of everything outside the region of the face, which would be black in the resulting image.

Then, I computed the average face shape by subsets of non-smiling and smiling.

Next, I morphed each of the faces in the dataset to the corresponding subset's average shape. Here are the raw images shown in the first and third row and the warped images on the second and fourth row.

Raw 25a (Non-smiling)

25a.jpg

Raw 50a (Non-smiling)

50a.jpg

Raw 75a (Non-smiling)

75a.jpg

Raw 100a (Non-smiling)

100a.jpg

Warped 25a

25a_warped_to_avg_shape.jpg

Warped 50a

50a_warped_to_avg_shape.jpg

Warped 75a

75a_warped_to_avg_shape.jpg

Warped 100a

100a_warped_to_avg_shape.jpg

Raw 25a (Smiling)

25b.jpg

Raw 50a (Smiling)

50b.jpg

Raw 75a (Smiling)

75b.jpg

Raw 100a (Smiling)

100b.jpg

Warped 25b

25b_warped_to_avg_shape.jpg

Warped 50b

50b_warped_to_avg_shape.jpg

Warped 75b

75b_warped_to_avg_shape.jpg

Warped 100b

100b_warped_to_avg_shape.jpg

In these images above, we can note a few key changes: both faces in 25a and 25b are more aligned so that this person looks more towards the center. Their faces in the raw images are slightly angled toward the left of the audience. The faces in the raw 50a and 50b are also slightly angled upwards. Warping them to the average shape adjust it so that that person's faces are more front-facing. Some changes are more subtle and stand out less like the faces of 75a and 75b taking up more space in the warped images.

This is the mean image for each subset.

Mean Face of Non-Smiling Population

non_smiling_mean_face.jpg

Mean Face of Smiling Population

smiling_mean_face.jpg

Now, we can warp my face into the average geometry - we take the non-smiling population to show as an example - and warp the average face of the non-smiling subet into my geometry.

My Face Warped into the Average Geometry

ethan_part5_warped_to_non_smiling_avg_shape.jpg

Average Face Warped into the Average Geometry

non_smiling_avg_face_warped_ethan_part5_shape.jpg

Part 5: Caricatures: Extrapolation from the Mean

To get a caricature of my face by extrapolating from the population mean in part 4, I first cropped the image of myself to better suit the style of the images in the FEI datasets I used, specifically, removing a big chunk of the top part of the image so less of my hair shows. Most of the FEI database images don't fully show the person's forehead, which makes sense since our dataset doesn't have annotated keypoints close to that area of the image.

This is the image cropped and downscaled to get the same shape of (300, 250, 3) as the FEI ones.

Cropped and Downscaled Ethan

ethan_downscaled_cropped.jpg

Triangulation of Cropped and Downscaled Ethan

triangulation_ethan_downscaled_cropped.jpg

To get the caricature points, we first annotate the points on my face by going in the same order as the annotated points from each image in the FEI dataset - the positions of these points are chosen independnently of those in the dataset. Then, we take a scaled (by alpha) version of the difference between the points on my face with the mean points of the subset. Then, we can warp my face using the same method as before to get the caricature image.

$\alpha = -4$

ethan_caricature_alpha_neg2.jpg

$\alpha = -2$

ethan_caricature_alpha_neg2.jpg

$\alpha = -1$

ethan_caricature_alpha_neg1.jpg

$\alpha = -0.5$

ethan_caricature_alpha_neg0.5.jpg

$\alpha = 1.5$

ethan_caricature_alpha_pos1.5.jpg

$\alpha = 2$

ethan_caricature_alpha_pos2.jpg

$\alpha = 4$

ethan_caricature_alpha_pos4.jpg

$\alpha = 6$

ethan_caricature_alpha_pos6.jpg

We can see that the lower alphas make my face vertically shorter and wider (compared to the downscaled and cropped input image). This makes sense since my face also looks longer than most of the faces in the FEI dataset. As expected, increasing $\alpha$ past 1 exaggerates the length of my face, stretching it out even more. Large magnitudes of alpha has a greater distortion effect on the image.

Bells and Whistles

Bells and Whistles #1: Change age/gender/ethnicity/smile/etc of your (or your friend's) face...

Average Chinese Male Face

I chose to warp and morph my face to and with an average Southeast Asian male face that I got from Pinterest here who looks older than I do.

Here are the input images. I got the middle image by manually cropping and downscaling like I did with my face in Part 5.

Raw Average Asian Male Face

avg_asian_male_face.jpg

Cropped and Downscaled Average Asian Male Face

avg_asian_male_face_downscaled_cropped.jpg

Cropped and Downscaled Ethan

ethan_downscaled_cropped.jpg

Shape Only

warped_asian_male_to_ethan_geometry.jpg

Appearance Only

warped_ethan_to_asian_male_geometry.jpg

Shape and Appearance

morphed_ethan_and_asian_male.jpg

This morphed result above isn't too different from my own face. Let's observe a larger change/difference in the morph by using an average Chinese female vs. male face. I found this face at this link on Pinterest, too.

Average Chinese Female Face

Using the same code, here are the input images and the generated warps and morph.

Raw Average Asian Female Face

avg_asian_male_face.jpg

Cropped and Downscaled Average Asian Female Face

avg_asian_female_face_downscaled_cropped.jpg

Cropped and Downscaled Ethan

ethan_downscaled_cropped.jpg

Shape Only

warped_asian_female_to_ethan_geometry.jpg

Appearance Only

warped_ethan_to_asian_female_geometry.jpg

Shape and Appearance

morphed_ethan_and_asian_female.jpg

Here are the triangulations of both average Chinese male and female faces.

Triangulation of Cropped and Downscaled Average Chinese Male Face

triangulation_avg_asian_male_face.jpg

Triangulation of Cropped and Downscaled Average Chinese Female Face

triangulation_avg_asian_female_face.jpg

Bells and Whistles #2: Use one of the datasets to compute a PCA basis for the face space...

Using sklearn.decomposition.PCA with n_components=0.95, I computed a PCA basis for the face space of the FEI non-smiling dataset. Here are the eigenfaces with the top 10 largest singular values.

Top 10 Eigenvalues

eigenfaces_fei_non_smiling.jpg

Now using the top 32 PCA components, we can compare the caricatures of raw images projected to the PCA basis vs. those in the original basis. Here, we can see that PCA performs well by capturing the unique facial features, specifically the eys, and changes of color intensity among features and including less noise. The caricature in the original basis lacks eye and hair color, as both colors look the same as the person's facial skin color. Using the original basis also makes us amplify each pixel without any filtering, so the exaggeration doesn't produce images with appealing or with contrast as noticeable as the images produced by using the PCA basis.

25a

comparison_pca_idx_25a.jpg

50a

comparison_pca_idx_50a.jpg

75a

comparison_pca_idx_75a.jpg

100a

comparison_pca_idx_100a.jpg

Lastly, we can generate random faces with the following process. First, we randomly sample weights from $[-1.0, 1.0]$ and use them to scale the top 32 principal components to reconstruct images. Then, we take the sum of this weighted average. From the beginning, we reshape images before we display them since projecting onto the PCA basis gives us the flattened 1D vector representation of our images. Some random faces can be seen below.

4 Random Faces Generated with PCA

pca_4_random_faces.jpg