christopher choyce -- chris@thechoyce.net
About:
The goal of P4 was to create an image morph from 2 basic photos; 1 from someone famous, and 1 from myself. People used to tell me I looked like Moby (I don't really think I do, at least not without a completly bald head). So I figured I'd take this opportunity to either prove or disprove everyone.

Finding a usable photo of Moby took a little bit of time, but I finally was able to find the one to the left. I figured if I was going to morph into Moby, I mine as well dress the part (pictured on the far right). All it took was a little "Photoshop Magic" and we were set to start the deformation process.



The first and third images are what I used as my base to create the animation. I took those 2 images and made 6 key frames out of them, following the flow pattern outlined below.


Deformations:
To create the Deformations of each photo, I used the turbo morph applet (provided by Professor Jarek Rossignac). I then modified the save/load routines to both output the points into a file format, and to output the current screen render as a JPG image. Also, I decided I would need a consistant facial map to use when creating each keyframe (that way I always had the same number of points, in the same order, making my interpolation step trivial). By lining up these map-points between key facial features in each of the images, I was able to create my key-frames.

moby-deformation moby moby-choyce-map choyce-moby-map choyce choyce-deformation


Facial map used to create the key frames.
To create the morphing in between each of these key-frames, I just ran 30 iterations of my custom interpolations tool:
void animate_step( )
{
  // loop through all the  keyframes
  int frame = startStep;
  int nextFrame = frame+1;
    
  float newX, newY;
  // looping through all the points
  for( int i = 0; i < cn[frame]; i++ )
  {
    // get out starting and ending points
    pt ptStart = C[frame][i].make();
    pt ptEnd   = C[nextFrame][i].make();
      
    newX = ptStart.x + t * (ptEnd.x - ptStart.x)/4;
    newY = ptStart.y + t * (ptEnd.y - ptStart.y)/4;
        
    // now update our starting point.
    C[frame][i].setTo(newX, newY, 0);
    G[I[frame][i]][J[frame][i]].setTo(C[frame][i]); 
  } // end cn loop
      
  t += timeStep;
  animationFrameCount++;
  
  // smooth it!
  smoothing=true; sfairInit(); fstp=0;
      
  imgSaveAnimationFrame();
}

I ended up keeping this as a manual iteration because the timing between to smoothing call and the saving routine would get off, so sometimes the smoothing wouldn't finish before the save was called --not good! I would like to go back and automate this so you could just start the process with your keyframes defined, and it would do all the iteration for you while you sat back, ate some popcorn and watched Veronica Mars.

Blending:
In order to get the middle part of the animation working correctly, I had to blend together the image of Moby with the image of myself. I combined both a the interpolation method above, and an alpha cross-fade between the two images. I choose a function which ramped up quickly and then slowed down as it came to an end (basically the same function we used in P1a: F3).

What I tired to do was map the the moby photo's facial points to about where my facial points were for the moby-choyce blend, and then map my facial points to Moby's for the choyce-moby blend. While this worked, I noticed afterwards: I should have paid more attention to the glasses. His are significantly larger than mine in this photo, and when I mapped the eyes and ears together, I messed up the glasses mapping. His glass get bigger when they should get smaller but, time permitting, I would like to fix this.

void animate_blend( )
{
  String mBase = "../frames/m1-m2/m1-m2-";
  String cBase = "../frames/m3-m4/m3-m4-";
  
  mImage = loadImage( mBase + currentBlendIndex + ".jpg");
  cImage = loadImage( cBase + currentBlendIndex + ".jpg");
  
  alphaChannel = blendRampUp( );
  
  int pixelLength =  mImage.height * mImage.width;
  color mColor, cColor;
  for( int i = 0; i < pixelLength; i++ )
  {
    mColor = color(
     red(mImage.pixels[i]),
     green(mImage.pixels[i]),
     blue(mImage.pixels[i]),
     255-alphaChannel
    );
    cColor = color(
     red(cImage.pixels[i]),
     green(cImage.pixels[i]),
     blue(cImage.pixels[i]),
     alphaChannel
    );

    mImage.pixels[i] = blendColor( mColor, cColor, BLEND );
  }
  imgSaveBlendFrame();
  
  currentBlendIndex++;
}

// changes opacity fast and then slows down
float blendRampUp( )
{
  float retVal = 0;
  float t = (float)currentBlendIndex/30;
  retVal = -cos(3.14159265*t);
  retVal += 1.0;
  retVal /= 2.0;
  
  return retVal*255;
}

1/3 through the moby-choyce blend.
1/2 through the moby-choyce blend.
2/3 through the moby-choyce blend.
Resizing:
Once I had all these images create, I had to resize them all to get a useable images. Rather then running the interpolation and blending over and over each time I wanted a different size animation, I just simple created a simple tool in processing to resize them to any width I would need. This resize process also removed the moby-choyce images and choyce-moby images (which are now combined into a blend) and re-numbered them to make the animation easier.

Click to toggle resize code display.
Animation:
Then came time to animate it all. I could have just imported all of my animation files into Adobe Premiere, but in the spirt of Processing (and because I didn't have access to it at the time) I simply created an applet which loaded up all the files and then played the animation in a loop. This ended up working out nicely since I did a reverse loop so that the animation just cycles back and forth like a pendulum.

Click to toggle animation code display.
Download Source Code:
Here is all the source code I used to generate these images:
* fast is a modified version of that tubro-warp source code. Changes include:
  • IO.pde -- custom functions for loading and saving data.
  • MyImage.pde -- image manipulation and saving functions.
  • animate.pde -- all the functions which generated the animated images.
  • fast.pde -- modifications made to support the animation.
  • /keyframes -- files needed to load up the different keyframes of the animation.
  • output-[0-9].txt, output-[0-9].jpg -- used to load up the different sets of constraints.