Face Morphing and Modelling a Photo Collection

CS 180 Project 3

Jiayang Wang | jiayang.wang@berkeley.edu

Introduction

This project explores ways that morph images of faces into each other, a two-step process including warping the image shapes and cross-dissolving image colors. I explored methods including Delaunay triangulation, affine transformation, and inverse warping that can be used to produce more natural and believable face morphings.

Defining Correspondences

Approach

For this project, I used the face images of myself and my friend Eric. To warp the two faces into the same shape, such that the facial features and face shapes in the two images align with each other, I defined pairs of corresponding points on the two images using ginput in a consistent order, and added the four corners of the image such that each image has 72 corresponding points. To generate trianges within each set of corresponding points, I first calculated the mid-way shape (the average value of the two point sets), and then generated a Delaunary triangulation for each image using scipy.spatial.Delaunay.

Results

Jiayang_triangle.png

Jiayang (me)
Correspondences + Triangulation

Eric_triangle.png

Eric
Correspondences + Triangulation

From the results, we can see that Delaunary triangulation does not produce overly skinny triangles, and computing the midway shape helps in avoiding potential triangle deformations.

Computing the "Mid-way Face"

Approach

The mid-way face can be computed by warping both shapes into the mid-way shape calculated in the previous part, and then cross-dissolve the colors.

Warping triangles can be represented as multiplying the triangles with an affine transformation matrix A. Therefore, I calculated A for each pair of triangles and took the inverse of the matrices, multiplying the inverses with the target triangles (the mid-way shape) to get the coordinates that I needed to interpolate from the original images. I first tried scipy.interpolate.griddata with linear interpolation but the efficiency is too low. Therefore, I switched to nearest neighbor interpolation and reduced the processing time to under 2 minutes.

After warping both image shapes into the mid-way shape, the cross-dissolving step is just simply averaging the pixel intensities between the two warped images.

Results

Jiayang.jpg

Jiayang

Jeric.jpg

Jiayang + Eric = Jeric

Eric.jpg

Eric

The Morph Sequence

Approach

Expanding the morph function in the last part, I modified it so it can take any two images as input, and added the parameters warp_frac and dissolve_frac, bounded at [0, 1], that control the extent of warping and cross-dissolving respectively, such that the starting frame has both parameters set to 0, and the ending frame has both parameters set to 1.

To create an animated morphing sequence with 45 frames and 30 fps, I repeatedly called the morph function, increasing both warp_frac and dissolve_frac by 1/45 every frame, and then merge the computed 45 morphed images into a gif.

Results

Jeric.gif

Jeric morphing

The "Mean face" of a population

Approach

For this part of the project, I used the Danes dataset with annotated correspondences. I computed the average shape of the whole population, which is the mean value of each corresponding point across the whole population, and warped each of the faces in the dataset into the average shape. Then I cross-dissolved all warped images to compute the average face of the whole population.

Results

08-1f.bmp

Example 1 (08-1f.bmp)

08-1f.bmp_warped.jpg

Warped

34-1m.bmp

Example 2 (34-1m.bmp)

34-1m.bmp_warped.jpg

Warped

35-1f.bmp

Example 3 (35-1f.bmp)

35-1f.bmp_warped.jpg

Warped

36-1m.bmp

Example 4 (36-1m.bmp)

36-1m.bmp_warped.jpg

Warped

danes_average.jpg

Average Face

danes_average_female.jpg

Average Female Face

danes_average_male.jpg

Average Male Face

Using the calculated average shape of the whole population, and my face with re-defined correspondences that matched the keypoints used in the Danes dataset, I warped my face into the average geometry, and also warped the average face into my geometry.

Jiayang_to_danes.jpg

Jiayang to Average Danes

danes_to_Jiayang.jpg

Average Danes to Jiayang

Caricatures: Extrapolating from the mean

Approach

I calculated caricatures of my face by extrapolating from the the average shape of the Danes dataset, setting warp_frac to values greater than 1. I tried 1.5 and 3, and the result images are quite funny.

Results

Jiayang_to_danes_extrapolated_1.5.jpg

warp_frac = 1.5

Jiayang_to_danes_extrapolated_3.jpg

warp_frac = 3

Bells and Whistles

Gender Change

I used the average Chinese woman image off the web, and morphed it with my face image with various parameter settings. The result images below showed morphing just the shape (only warping), just the appearance (only cross-dissolving), and both.

Jiayang.png

Jiayang

female_b&w.png

Reference

Jiayang_gender_shape.jpg

Shape only

Jiayang_gender_appearance.jpg

Appearance only

Jiayang_gender_both.jpg

Both

PCA Basis

With the Danes dataset warped into the average shape, I computed the dataset's PCA basis for the face space. Below are the first 12 "eigenfaces" - the principal components with the most variance, sorted in decreasing order.

eigenface_0.jpg

Eigenface 1

eigenface_1.jpg

Eigenface 2

eigenface_2.jpg

Eigenface 3

eigenface_3.jpg

Eigenface 4

eigenface_4.jpg

Eigenface 5

eigenface_5.jpg

Eigenface 6

eigenface_6.jpg

Eigenface 7

eigenface_7.jpg

Eigenface 8

eigenface_8.jpg

Eigenface 9

eigenface_9.jpg

Eigenface 10

eigenface_10.jpg

Eigenface 11

eigenface_11.jpg

Eigenface 12

Then, I tried to reconstruct the faces in the warped Danes dataset with only the first 12 eigenfaces as the face space. The reconstructed faces are already similar to the original faces, and easily distinguishable, which supports the idea that the first few principal components contains the most variance, thus details that distinguish each face from other faces.

01-1m.bmp_warped.jpg

Example 1 (01-1m.bmp)

01-1m.bmp_reconstructed.jpg

Reconstructed

07-1m.bmp_warped.jpg

Example 2 (07-1m.bmp)

07-1m.bmp_reconstructed.jpg

Reconstructed

35-1f.bmp_warped.jpg

Example 3 (35-1f.bmp)

35-1f.bmp_reconstructed.jpg

Reconstructed

I also tried transforming my own face image, without any modification, into this face space of 37 eigenfaces, and then reconstruct it. The result is kinda disturbing, and also does not look like my face at all, probably because my face is not in the dataset that originally established this face space, and that the background color of my face image is too different from the dark green background of the Danes dataset. I changed the background color of my face image to dark green, and the reconstructed result is significantly better.

Jiayang_danes.png

Jiayang

Jiayang_reconstructed.jpg

Reconstructed

Jiayang_danes_backgroun.png

Jiayang with Background Change

Jiayang_reconstructed_background.jpg

Reconstructed with Background Change

A Different Morphing Algorithm Using PCA and Face Space

In this face space computed from the warped Danes dataset, I tried a different morphing algorithm that produce a smooth transition between faces. Instead of the morphing algorithm that consists of a warping step and a cross-dissoling step that is discussed in the parts above, my new morphing algorithm in this face space morph faces by changing the weights of each of the 37 eigenface during reconstructing the face image. For example, I want to morph face A into face B, which are both in the Danes dataset, in 37 frames. The first frame is a reconstruction of face A using all 37 eigenfaces and corresponding weights, which is represented by the V^T matrix of face A calculated using PCA. For all frames after this, the n-th frame is a reconstructed face also using all 37 eigenfaces and weights, but have the first n weights of face A replaced with the first n weights of face B. Therefore, the 37th frame will have all weights replaced with the weights (V^T) of face B, and the reconstructed face is exactly face B. This process will successfully create a smooth morphing sequence from face A to face B.

Using this new morphing algorithm, I morphed between the first 5 faces of the warped Danes dataset, and merged the result image sequence into a gif loop.

eigenface_morph.gif

The First 5 Faces in Danes Dataset
Produced Using a Different Morphing Algorithm

While the original morphing algorithm produces a smooth transition of shapes and colors, this new morphing algorithm produces a unique transition animation that consists of "ghosts" that are the weighted combinitions of eigenfaces. This algorithm also requires less computation as each frame is just a weighted linear combination of eigenfaces. An interesting thing to note is that the first few frames of each morphing sequence contains the most changes, while the frames toward the end of each morphing contains less changes and converge to the target face. This is another representation of the property of principal components and eigenfaces, that first few principal components contains the most variance.