Face Morphing

CS 194-26 Image Manipulation and Computational Photography – Project 3, Fall 2021

Adnaan Sachidanandan

Defining Correspondences

I first began by defining correspondence points on the two images I used, one of myself and one of George. To do this, I directly mapped out key points on both images. Then I averaged the two correspondence point sets to create a mid-way triangulation, which will better distribute the sections of the image when I merge the two faces together. The triangulation is computed with Scipy's implementation of Delaunay triangulation, producing the results below

Adnaan
George
Adnaan Points and Triangulation
George Points and Triangulation

Computing the "Mid-Way Face"

With the triangulation, we can now morph the two images into the averaged shape to merge the faces together. To do this morph, we take two steps:

  1. For each triangle, find the transform matrix from points in the original images to the averaged points.
  2. For each pixel in the averaged/mid-way image, apply the inverse of the transformation matrix to get the corresponding pixels in the original images to pull values from. Take the average of the pixel values from the two images.
After these steps, we have effectively created a mid-way image with each pixel having a value that is half from each original image, creating a strong "mid-way" hybrid image as shown below.

Adnaan
George
Midway Face of Adnaan and George

Morph Sequence

I then generalize the previous method to instead of averaging correspondence points between the two images, compute a weighted average of the points based on an input warp fraction. Additionally, I modify the inverse transform part to, for each pixel, instead of taking the average of the corresponding pixels in the original images it takes a weighted average of their pixel values based on an input dissolve fraction. With these parameters, I can now create progressive morphs from low warp and dissolve fractions (more heavily weighing one image) to high fractions (more heavily weighing the other image), as shown in the gif below.

Morph Sequence of Adnaan and George

The "Mean Face" of a Population

From the earlier morphing, I generalized the code to take any image and its correspondence points and morph the image to a new set of inputted correspondence points, based on a given triangulation and cross-dissolve ratio. To do this, I take the same inverse transform logic from the mid-way calculation, but instead of moving to an averaged shape, I morph the shape to the target correspondence points that are passed in. After that, I multiply each pixel value by the cross-dissolve fraction.

With these generalized morphing methods, I can now combine any set of faces into a stronger merged face, more specifically a "mean face" for a population. I computed the "mean face" of the male Danes from this dataset. This mean face is shown below.

Mean Male Dane Face from Dataset

With this mean face, I could now morph any individual Dane in the dataset to the mean shape, and even morph my face to that shape as well. I also warped the average Dane face to the shape of my face. Examples of these warps are below.

Original Dane 1
Original Dane 2
Original Dane 3
Original Dane 4
Warped Dane 1
Warped Dane 2
Warped Dane 3
Warped Dane 4
Adnaan Warped to Average Dane
Average Dane Warped to Adnaan

Caricatures: Extrapolating from the Mean

With average faces now, we can think of the differences in faces that make us unique as the vector difference between our faces and the average face. With this in mind, I can take the difference vector between my face and the mean Dane face and add a scalar multiple of that difference back to my face to create a caricature, which amplifies the unique characteristics of my face. As shown below, the caricatures get extreme with fairly small multiples.

Original Adnaan
Adnaan Caricature (multiplicative factor = 0.5)
Adnaan Caricature (multiplicative factor = 1)
Adnaan Caricature (multiplicative factor = 1.5)

Bells and Whistles

Changing Ethnicity with Average Images

With my morph methods as tools, I explored creating morphs between ethnicities utilizing the average Indian and Korean male images I found online, displayed below.

Average Indian Male Face
Average Korean Male Face

With these images, I tried morphing just the shape, just the appearance, and both. For morphing just the shape, I tried morphing my shape with by adding a difference vector between the average Korean and average Indian face to my face's shape and morphing to that, as well as directly morphing my face's shape to the Korean face's shape. For the appearance morph, I directly morphed the Korean face's shape to my face's shape and cross dissolved the two. For the combined morphing of face and appearance, I tried both shape morphs mentioned above and cross-dissolved my moprhed face with the Korean face.

Original Adnaan
Shape Morph with Difference Vector
Direct Shape Morph
Appearance Morph
Combined Morph with Difference Vector
Combined Direct Morph

In this project I had a great time exploring the intricacies of facial transformations and morphing. The inverse transformations were challenging, but rewarding when they started working! I plan to continue experimenting with this code in the future.