🎩

CS180 Project 3: Face Morphing

https://inst.eecs.berkeley.edu/~cs180/fa24/hw/proj3/index.html

Author: Nicolas Rault-Wang (nraultwang at berkeley.edu)

Credit to Notion for this template.

Part 1: Defining Correspondences

I used the suggested tool (link here) to create my correspondence point pairs. Each pair represents a pair of similar features in both images. For example, the centers of the left eye could be one such feature. Using these points, we can smoothly interpolate the local features from one image into the corresponding set of local features in another image.

Facial keypoints
Delaunay Triangulation on the facial keypoints.

Part 2: Computing the "Mid-way Face"

Step 2.1: Derivation of the Affine Transformation

To compute the affine warp between two arbitrary 2D triangles we will derive the affine warp from a standard triangle (1,0),(0,1),(0,0)(1, 0), (0, 1), (0,0) into an arbitrary 2D triangle (x0,y0),(x1,y1),(x2,y2)(x_0, y_0), (x_1, y_1), (x_2, y_2). Then, given two affine warps A1A_1 and A2A_2 from the standard triangle into two arbitrary triangles, the composition A2A11A_2A_1^{-1} will warp the first arbitrary triangle into the second arbitrary triangle.

Since an affine warp has 6 degrees of freedom, it requires 6 constraints to fully specify the transformation. The following 3 correspondences will provide us exactly 6 such constraints:

(1,0)(x0,y0)(0,1)(x1,y1)(0,0)(x2,y2)(1, 0)\leftrightarrow (x_0, y_0)\newline (0, 1)\leftrightarrow (x_1, y_1) \newline (0, 0)\leftrightarrow (x_2, y_2)

To simplify, we will lift these heterogeneous triangle coordinates (x0,y0),(x1,y1),(x2,y2)(x_0, y_0), (x_1, y_1), (x_2, y_2) into homogeneous coordinates (x0,y0,1),(x1,y1,1),(x2,y2,1)(x_0, y_0, 1), (x_1, y_1, 1), (x_2, y_2, 1).

Then, can simultaneously solve the affine transformation

[xiyi1]=[abcdef001][1[i=0]1[i=1]1]\begin{align*} \begin{bmatrix} x_i \\ y_i \\ 1 \end{bmatrix} = \begin{bmatrix} a & b & c \\ d & e & f \\ 0 & 0 & 1 \end{bmatrix} \begin{bmatrix} \mathbb{1}[i = 0] \\ \mathbb{1}[i = 1] \\ 1 \end{bmatrix} \end{align*}

for each the correspondence with the system:

[x0x1x2y0y1y2111]=[abcdef001][100010111]\begin{align*} \begin{bmatrix} x_0 & x_1 & x_2\\ y_0 & y_1 & y_2\\ 1 & 1 & 1\\ \end{bmatrix} &= \begin{bmatrix} a & b & c \\ d & e & f \\ 0 & 0 & 1 \end{bmatrix} \begin{bmatrix} 1 & 0 & 0 \\ 0 & 1 & 0 \\ 1 & 1 & 1 \end{bmatrix} \end{align*}

Thus, we have the affine warp from the standard to arbitrary triangle:

A[abcdef001]=[x0x1x2y0y1y2111][100010111]1A\equiv\begin{align*} \begin{bmatrix} a & b & c \\ d & e & f \\ 0 & 0 & 1 \end{bmatrix} = \begin{bmatrix} x_0 & x_1 & x_2\\ y_0 & y_1 & y_2\\ 1 & 1 & 1\\ \end{bmatrix} \begin{bmatrix} 1 & 0 & 0 \\ 0 & 1 & 0 \\ 1 & 1 & 1 \end{bmatrix}^{-1} \end{align*}

Step 2.2: Backwards warp

Left: image A; Middle: average image; Right: image B.

The following steps describe our backwards warp methodology for computing an average frame between AA and BB weighted by a given 0t10\leq t\leq 1. For the steps below, let ai\vec a_i and bi\vec b_i respectively be points in the source image AA and destination image BB forming the iith correspondence pair (ai,bi)(\vec a_i, \vec b_i).

  1. Transform images AA and BB into the same shape then manually create correspondence pairs (ai,bi)(\vec a_i, \vec b_i) for i=0,1,,Ni=0, 1, \cdots, N.
  1. Form a set of interpolated correspondence points M={mi}i=0NM = \{\vec m_i\}_{i=0}^N by mi(1t)ai+tbi\vec m_i \equiv (1-t)\cdot \vec a_i +t\cdot\vec b_i
  1. Call scipy.spatial.Delaunay to create a Delaunay triangulation of MM.
    1. Each triangle TMT_M in the average frame is defined by an array of three vertices TM=[mi,mj,mk]T_M = [\vec m_i, \vec m_j, \vec m_k].
    1. Thus, from the vector indices, we see that TMT_M corresponds to the triangles TA=[ai,aj,ak]T_A =[\vec a_i, \vec a_j, \vec a_k] in image AA and triangles TB=[bi,bj,bk]T_B = [\vec b_i, \vec b_j, \vec b_k] in image BB.
  1. Use the Affine Transformation described in the previous section to compute the affine warps WAW_A from TMT_M to TAT_A and WBW_B from TMT_M to TBT_B.
  1. Call scipy.draw.polygon on the vertices of TMT_M to obtain the set SMS_M of pixel coordinates forming the interior and boundary of TMT_M in
  1. Apply WAW_A to SS to obtain SAS_A, a set of points in image AA.
    1. Note that while the coordinates of each point in SMS_M are the all integers, coordinates in SAS_A not necessarily at integer coordinates.
    1. Thus, we must interpolate the color values at points in SAS_A
  1. Use scipy.interpolate.RegularGridInterpolator to interpolate the color value each aSA\vec a \in S_A from the pixels in the image matrix for AA (at integer coordinates).
  1. Repeat steps 6-7 for image BB in a similar way using WBW_B and SBS_B.
  1. Compute the color value at each point mSM\vec m\in S_M from a weighted average of the interpolated colors values at aSA\vec a \in S_A and bSB\vec b\in S_B.
  1. Repeat steps 2-9 for every triangle in the triangulation computed in step 3.

Part 3: The Morph Sequence

Step 3.1: Frame Creation

Each frame in the morph sequence is created with the procedure described in Part 2. For a smooth warp effect, frame i[0..N]i \in [0..N] is created with t=i/Nt = i / N, where NN is the total number of frames.

Step 3.2: Gif Creation

A call to ffmpeg can create a .gif from a sequence of images.

# Format string for my ffmpeg commands:
cmd = f'yes | ffmpeg -framerate {framerate} -i "{tmpdir}/%0{n}d.png" {save_dir}/{movie_name}.gif'
 
# Example of complete command:
“yes | ffmpeg -framerate 30 -i "tmp_frames/%08d.png"  movies/part_6.fnameA_pt1-me.png.fnameB_pt1-jer.png.nframes_90.framerate_30.gif"

Part 4: The "Mean face " of a population

4.1: Computing the Mean Faces

I used the IMM Face Database for parts 4-5, which consists of 6 different expressions from 40 subjects; each expression has 58 manually labeled keypoints.

M. B. Stegmann, B. K. Ersb¿ll, and R. Larsen. FAME – a flexible appearance modelling

environment. IEEE Trans. on Medical Imaging, 22(10):1319{1331, 2003

To create the “mean face” for a particular expression type, I first extracted and cleaned the keypoints for each subjects. Then, I computed the average keypoint positions and applied my part 2 to warp each subject into this average shape. Finally, I averaged the color values from each subject to obtain the mean facial expression. The results are pictured below.

Mean faces of the population. Each image is the mean of a different facial expression type.

4.1.1: Examples of Warping Individuals into the Mean Geometry

4.2: Warping Myself into the Mean Faces

4.2.1: Me Warped in the Mean Geometry

4.2.2: Mean Face Warped into My Geometry

Part 5. Caricatures: Extrapolating from the Mean

To extrapolate from the mean shape, I created morphed images of myself with t<0t < 0 and t>1t > 1. The resulting extrapolated shapes rapidly deteriorate in quality with large magnitudes of tt. (Note: in the plots below, please interpret “alpha” as tt.)

Bells & Whistles

I chose to implement the morph sequence task. To do this, I started with my function make_animation_frames(imgA, imgB), which creates a morph animation from imgA to imgB. Then, I applied it to create a smooth morph effect across consecutive images in a sequence img_0, img_1, im_2, ..., img_N. This required labeling correspondence pairs between each img_k, img_(k+1). Also, by setting img_N = img_0, my animations could loop infinitely and seamlessly.