First I had to find the faces to use. I selected faces from the Olivetti Research Laboratory in Italy. This link shows the faces that I used. Next I had to select the feature points to use. I decided to use the 186 feature points that Susan Brennan used in her studies at MIT. The only difference was that I left out the points for the ears. At 7 points per ear, that means that I used 172 data points per face. Brennan's faces look like this face (note that this face has ears).

My data file consisted only of the list of 172 points in "x y" format. The coordinates are floating point values, although integers are permissible. In some cases, I magnified an image to get subpixel accuracy on my feature points. My data file format permitted blank lines and lines beginning with "#" for comments. This turned out to be a wise decision when I accidently left out a feature point on a face and had to "debug" which point I had left out. [Note that this technique relies on the assumption of a static number of feature points and that the order of feature points is important.] An example data file looks like this.

So the "pipeline" of data creation looks like this:

After this step, I had many data files. I had four female data files (data.f1 through data.f4) and five male data files (data.m1 through data.m5). At this stage, lots of "behind-the-scenes" work went on. I had to translate pgm images to ppm images to im images to do warping, then from im images back to gif to show things on the web page. The edgewarp code from Fred Green requires images in tiff format, while the Silicon Graphics Indycam grabs files in rgb format. I'll save you the monotony of all those conversions.

I also had to write a "behind-the-scenes" C++ class to handle sets of feature points. The code and header file is given below. This code provides methods to read data files, print in GNUPLOT format, print in interleaved format for the KUIM warping, translate/rotate/scale points, and compute the centroid of the set. These latter functions are essential to implementing the procrustes metric. I'll abridge some detail of normalizing the faces--see the code below for the exact details. Basically, each face needs to have its centroid translated to the origin, then each face has to be scaled and rotated to give the best match to a canonical feature set.

Next I had to create an "average" face for males and females. I picked a canonical face for each class and normalized everything to those faces, then averaged feature points. To normalize, I used my normalize.c program with a command like "cat data.canonical data.f1 | grep -v "#" | normalize", where "grep -v "#"" would remove the comments in the data files. Once I had normalized faces, I would concatenate them all into one file like "cat > female", then edit the file "female" and put the number "4" at the top of the file to tell my averaging program that 4 faces were to be averaged. To average the faces, I used a command like "cat female | average > data.f.avg". The averaging program is given below.

By this time, I had face data, normalized face data, and averaged face points for all my faces. I created an average female face, and average male face, and an average overall (androgynous) face. These average faces had the same data format as the original faces points.

I could do all sorts of fun things with my new data. I created a method for my pointset class to spit out the points in GNUPlot format so I could do a PostScript plot of feature points. I also created an interleave method for my class to take two faces and produce a point format for warping two images. An example program which does this, interleave.c, is given below. Now I could warp any face image to any other face image (based on feature landmarks). In particular, I could warp females to the female average and males to the male average. Finally, I used "ppmblend" to combine the warped faces together to get average images (not just feature points) for females, males, and overall. Since I could also do math on point sets using my class, I could also interpolate between faces or amplify differences from the average face. Once the data reaches this stage, creativity is the only limitation.

  • C & C++ code that I used or wrote
  • This site about entomology and insects tickled my fancy.
    I also like Daddy long legs spiders.
    And of course beetles are cute as well.
    Maybe not as cute as these roly poly little fellows though.
    These guys have two hundred left feet.
    Therefore these suckers must have two deci-lef t feet, maybe?
    If you see one of these ugly ticks, I'd steer clear of them. I didn't realize that scorpions belonged to the Arc hnida class with some of these other critters.
    There's only one scorpion, but lots of spiders that live in Kentucky.