The goal of this part is to select pairs of corresponding points on our two images, which will be used to create a triangulation for affine morphing. I used ginput to select the points in a Jupyter notebook. Then, I computed the average of the point sets and used Delauanay triangulation on the average point set.
We can now use the triangles that we computed from the previous part in finding an affine transformation from corresponding triangles between images. For each triangle, we first compute a transformation matrix that maps from our source triangle to our destination triangle. In our case, the destination will always be that of the midway image. Next, we apply the inverse of this transformation to the polygon of points contained within the destination triangle, to obtain the corresponding source polygon. I used RectBivariateSpline to interpolate the pixel values of the source polygon coordinates, and set the pixel values of the destination polygon in my transformed image to take on those values. I used 3 interpolation functions per image, one for each color channel. I do the same process for each triangle I have, for both of my input images. Last, I take the average of the two morphed images to obtain the final mid-way face.
In order to create a morph sequence, we need to introduce two new parameters, warp_frac and dissolve_frac. I varied these parameters such that they increased linearly from 0 to 1 as the frame number progressed. Warp frac handles a weighted average between our selected keypoint images when computing the target keypoints that we will use for triangulation. I use (1-warp_frac) * image1_points + warp_frac * image2_points to get the target points for triangulation. Dissolve frac is responsible for when we take a weighted average of the pixel values of the two morphed images. I use (1-dissolve_frac) * transformed_1 + dissolve_frac * transformed_2 to get my final image in the frame.
In this part, I computed the average face of the Danes dataset and then performed morphing between my face and the average Dane face. In order to compute the average Dane face, I first computed the average of all the labeled keypoints for each image that were provided in the dataset. Then, I used the average keypoints to get a target triangulation. I morphed each image in the dataset to this target triangulation using the morphing methods I implemented in the previous steps. Then, I computed the average of these morphed images to obtain the average Dane face. For morphing my face to Dane, I used the triangulation of the average Dane keypoints, and morphed directly to the Dane face shape. For morphing Dane to my face, I did the same where I directly morphed the average Dane face to my face shape.
In order to create a caricature, I first subtracted the keypoints of my face from the keypoints of the average Dane face. Referring to this differences as delta, I then added the vector alpha * delta to my kyepoints, creating target keypoints. I then morph my image to the target keypoints in a face shape transformation to complete my caricature. I used alpha values of -1, -0.5, 1.5, and 2.0 in order to create different variations on my caricatures. Scroll through the carousel below to see how the caricatures change as the alpha values change!
Alpha = -1.0
Alpha = -0.5
Alpha = 1.5
Alpha = 2.0
For Bells and Whistles, I tried morphing my face onto the average Asian Woman face, in order to see what my features would look like as a female. I also tried morphing myself into the average NBA player face. For each, I tried morphing just the shape, just the appearance, and both shape and appearance.
Me
Average Asian Woman Face
Morphing Shape Only
Morphing Appearance ONly
Morphing Both
Me
Average NBA Player Face
Morphing Shape Only
Morphing Appearance ONly
Morphing Both