The goal of face morphing is to take two pictures of faces and morph them into each other. This includes moving and warping features to match.
The algorithm is suprisingly simple. The input consists of two images of faces and the corresponding points, ie left eye in picture A mapped to left eye in picture B. First we find the vector between every pair of corresponding points. We would like to go from the output pixel backwards to the original image to grab the pixel color, therefore to get from a pixel in a warped image to the original we just use (location - offset) where the offset is some distance along the shift vector. This distance is determined by a warpratio and is an input into the morph function. For example if the warp ratio is 0.2, one image would have an offset of (0.2*shift_vector), the other would meet it by traveling (0.8*-shift_vector). This brings both corresponding points to the same location on the warped images. This allows us to find the offset for a few pixels but we need to find the offset for the rest too. We would like the offsets to not vary to dramatically from pixel to pixel. To do this we simply poisson fill the offsets across the images, diffusing the known value at a few points throughout. We can then get the value of a pixel in the warped image by finding the pixel at (location-offset) in the original image. We do this for both faces, and then use a crossdissolve to blend them into each other. The ratio of the crossdissolve is also an input into the warp.
To find the mean face, i averaged the locations of the corresponding points. I then warped each face to match the mean locations and averaged them with crossdissolve. There was one guy with his face turned at a particularly awkward angle so i threw him out as an outlier.
The algorithm worked surprisingly well on similarly oriented faces. However it blew on faces that were facing the wrong angle. I produced images with crossdissolves and warp ratios from 0.1 to 0.9. These are available in images/ . Below I display the results at 0.5 crossdissolve and 0.5 warp ratio along with the original faces.
For kicks i tried running the algorithm on a sketch of myself and of a friend. Since a sketch depends on sharp boundaries i expected it to look pretty iffy, but it actually turned out quite well.
![]() | ![]() | ![]() |
![]() | ![]() | ![]() |
![]() | ![]() | ![]() |
![]() | ![]() | ![]() |
![]() | ![]() | ![]() |
![]() | ![]() | ![]() |
![]() | ![]() | ![]() |
![]() | ![]() | ![]() |
![]() | ![]() | ![]() |
![]() | ![]() | ![]() |
![]() | ![]() | ![]() |
![]() | ![]() | ![]() |
![]() | ![]() | ![]() |
![]() | ![]() | ![]() |
![]() | ![]() | ![]() |
![]() | ![]() | ![]() |
![]() | ![]() | ![]() |
![]() | ![]() | ![]() |
![]() |
||
![]() | ![]() | ![]() |