Program in text version.

/*###===-- Motion tracker for low poly facial animation --===###*/

/* James Answer - u9702646 */
/* Portions Copyright 1999 Tom Box */

/*-------------------------------------------------------------*/
/*---------------------[ Header files ]------------------------*/
/*-------------------------------------------------------------*/

#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <ctype.h>
#include <string.h>
#include "jpeglib.h"

/*-------------------------------------------------------------*/
/*-----------------------[ Typedefs ]--------------------------*/
/*-------------------------------------------------------------*/

typedef unsigned char Pixel_component;

typedef struct PIXEL_STRUCT
{
  Pixel_component r : 8;
  Pixel_component g : 8;
  Pixel_component b : 8;

} PIXEL_STRUCT;

typedef struct JPEG_INFO_STRUCT
{
  // the most simple image structure in the world... ever.

  int width;
  int height;

  PIXEL_STRUCT *image;

} JPEG_INFO_STRUCT;

typedef struct TRACKER_DATA_STRUCT
{
  int out_width;
  int out_height;

 int in_width;
 int in_height;

 int x_pos;
 int y_pos;

  PIXEL_STRUCT *image;

} TRACKER_DATA_STRUCT;

/*--------------------------------------------------------------*/
/*---------------------[ Program Code ]-------------------------*/
/*--------------------------------------------------------------*/

int SaveJPEG(JPEG_INFO_STRUCT *jpeg_data, char *filename, int quality)
{
  JSAMPARRAY buffer;  /* Output row buffer */
  JSAMPROW   row;
  struct jpeg_compress_struct cinfo;
  struct jpeg_error_mgr jerr;
  FILE * outfile;  /* destination file */
  int n;
  boolean force_baseline = FALSE;
  PIXEL_STRUCT *pixel;
  JSAMPLE      *jsample_row;//[FRAME_WIDTH*3];

  if ((outfile = fopen(filename, "wb")) == NULL) {
    fprintf(stderr, "ERROR: Can't open file '%s'\n", filename);
    return(0);
  }

  jsample_row = (JSAMPLE *)malloc(jpeg_data->width * sizeof(jsample_row));
  if(jsample_row == NULL)
  {
    fprintf(stderr, "ERROR: Can't allocate memory.\n");
    return(0);
  }
 

  /* Initialize the JPEG compression object with default error handling. */
   cinfo.err = jpeg_std_error(&jerr);
  jpeg_create_compress(&cinfo);

  /* Initialize JPEG parameters.
   * Much of this may be overridden later.
   * In particular, we don't yet know the input file's color space,
   * but we need to provide some value for jpeg_set_defaults() to work.
   */

  cinfo.in_color_space = JCS_RGB;
 
  jpeg_set_defaults(&cinfo);

  cinfo.input_components = 3;
  cinfo.data_precision = 8;
  cinfo.image_width = (JDIMENSION)jpeg_data->width;
  cinfo.image_height = (JDIMENSION)jpeg_data->height;

  /* Scan command line to find file names.
   * It is convenient to use just one switch-parsing routine, but the switch
   * values read here are ignored; we will rescan the switches after opening
   * the input file.
   */

  //file_index = parse_switches(&cinfo, argc, argv, 0, FALSE);

  /* Now that we know input colorspace, fix colorspace-dependent defaults */
  jpeg_default_colorspace(&cinfo);
  jpeg_set_quality(&cinfo, quality, force_baseline);

  /* Specify data destination for compression */
  jpeg_stdio_dest(&cinfo, outfile);

  /* Start compressor */
  jpeg_start_compress(&cinfo, TRUE);

  /* Process data */

 if (jpeg_data->image == NULL || jpeg_data->width < 1 || jpeg_data->height < 1) 
 {
    fprintf(stderr, "ERROR: Invalid JPEG data.\n");
    return(0);
  }
 

  row = jsample_row;
  buffer = &row;
  pixel = jpeg_data->image;

  while (cinfo.next_scanline < cinfo.image_height) 
  {
 for(n = 0; n < jpeg_data->width; n++)
 {
    jsample_row[n*3]   = pixel->r;
   jsample_row[n*3+1] = pixel->g;
   jsample_row[n*3+2] = pixel->b;
   pixel++;
 }
 
    (void) jpeg_write_scanlines(&cinfo, buffer, 1);
  }

  /* Finish compression and release memory */

  jpeg_finish_compress(&cinfo);
  jpeg_destroy_compress(&cinfo);

  /* Free resources */

  fclose(outfile);
  free(jsample_row);

  return(TRUE);
}

/*--------------------------------------------------------------*/
/*--------------------------------------------------------------*/

void FreeJPEG(JPEG_INFO_STRUCT *jpeg_data)
{
  if(jpeg_data != NULL && jpeg_data->image != NULL)
   free(jpeg_data->image);

  if(jpeg_data != NULL)
   free(jpeg_data);

  return;
}

/*--------------------------------------------------------------*/
/*--------------------------------------------------------------*/

JPEG_INFO_STRUCT *LoadJPEG(char *filename)
{
  struct jpeg_decompress_struct cinfo;
  struct jpeg_error_mgr jerr;
  FILE * infile;  /* source file */
  JSAMPARRAY buffer;  /* Output row buffer */
  JSAMPROW   row;
  int row_stride,n;
  PIXEL_STRUCT *pixel;
  JPEG_INFO_STRUCT *jpeg_data = NULL;

  printf("Loading ");

  printf("%s\n",filename);

  jpeg_data = (JPEG_INFO_STRUCT *)malloc(sizeof(JPEG_INFO_STRUCT));
  if(jpeg_data == NULL)
  {
    fprintf(stderr, "ERROR: Can't allocate memory for image.\n", filename);
    return(0);
  }

  if ((infile = fopen(filename, "rb")) == NULL) {
    fprintf(stderr, "ERROR: Can't open file '%s'\n", filename);
    return(0);
  }

  /* We set up the normal JPEG error routines, then override error_exit. */
  cinfo.err = jpeg_std_error(&jerr);

  /* Now we can initialize the JPEG decompression object. */
  jpeg_create_decompress(&cinfo);

  /* Step 2: specify data source (eg, a file) */
  jpeg_stdio_src(&cinfo, infile);

  /* Step 3: read file parameters with jpeg_read_header() */
  (void) jpeg_read_header(&cinfo, TRUE);

  jpeg_data->width = cinfo.image_width;
  jpeg_data->height = cinfo.image_height;

  /* JSAMPLEs per row in output buffer */
  row_stride = cinfo.image_width * cinfo.num_components;

  /* Allocate image row buffer, and image data array */
  buffer = (*cinfo.mem->alloc_sarray)
  ((j_common_ptr) &cinfo, JPOOL_IMAGE, row_stride, 1);

  jpeg_data->image = (PIXEL_STRUCT *)malloc(sizeof(PIXEL_STRUCT) * cinfo.image_width * cinfo.image_height);
  if(jpeg_data->image == NULL)
  {
    fprintf(stderr, "ERROR: Can't allocate memory for image.\n", filename);
    return(0);
  }
 

  /* Here we use the library's state variable cinfo.output_scanline as the
   * loop counter, so that we don't have to keep track ourselves.
   */
  (void) jpeg_start_decompress(&cinfo);

  pixel  = jpeg_data->image;

  while (cinfo.output_scanline < cinfo.output_height) 
  {
    /* jpeg_read_scanlines expects an array of pointers to scanlines.
     * Here the array is only one element long, but you could ask for
     * more than one scanline at a time if that's more convenient.
     */
    /*num_scanlines = jpeg_read_scanlines(&cinfo, buffer,
     row_stride);*/
    (void) jpeg_read_scanlines(&cinfo, buffer, 1);
    /* Assume put_scanline_someplace wants a pointer and sample count. */
    /*put_scanline_someplace(buffer[0], row_stride);*/

   row = buffer[0];
 

    for(n = 0 ; n < jpeg_data->width; n++)
    {
      //printf("[%d,%d,%d]",row[n],row[n+1],row[n+2]);
      pixel->r = (Pixel_component)row[n*3];
      pixel->g = (Pixel_component)row[n*3+1];
      pixel->b = (Pixel_component)row[n*3+2];
      pixel++;
    }
 //printf("ELine %d\n",cinfo.output_scanline);
  }

  /* Step 7: Finish decompression */
  (void) jpeg_finish_decompress(&cinfo);

  /* Step 8: Release JPEG decompression object */
  jpeg_destroy_decompress(&cinfo);

  fclose(infile);

  return(jpeg_data);
}

/*--------------------------------------------------------------*/
/*----------------------[ Tracker init ]------------------------*/
/*--------------------------------------------------------------*/

int Tracker_Init(TRACKER_DATA_STRUCT *tracker_data, int outbox_size, int inbox_size)
{
  tracker_data->image = (PIXEL_STRUCT *)malloc(sizeof(PIXEL_STRUCT) * inbox_size * inbox_size);
  if(tracker_data->image == NULL)
  {
    fprintf(stderr, "ERROR: No memory available to initialise tracker.\n");
    return(0);    
  }

  tracker_data->out_width = tracker_data->out_height = outbox_size;
  tracker_data->in_width  = tracker_data->in_height  = inbox_size;

 return(1);
}

/*--------------------------------------------------------------*/
/*--------------------------------------------------------------*/

int Tracker_GetImageData(TRACKER_DATA_STRUCT *tracker, 
JPEG_INFO_STRUCT *jpeg, int x_pos, int y_pos)

{
  int min_x,max_x,min_y,max_y,x,y;
  PIXEL_STRUCT *tracker_pixel;
  PIXEL_STRUCT *jpeg_pixel;

 min_x = x_pos - (tracker->in_width  >> 1);
 max_x = x_pos + (tracker->in_width  >> 1);
 min_y = y_pos - (tracker->in_height >> 1);
 max_y = y_pos + (tracker->in_height >> 1);

 if(min_x < 0 || max_x > jpeg->width || min_y < 0 || max_y > jpeg->height)
 {
   fprintf(stderr, "ERROR: Tracker point out of range.\n");
    return(0);    
  }

  for(y = 0; y < tracker->in_height; y++)
 {
  for(x = 0; x < tracker->in_width; x++)
  {
   tracker_pixel = &tracker->image[y*tracker->in_height + x];
   jpeg_pixel = &jpeg->image[jpeg->width*(min_y+y) + (min_x+x)];

    tracker_pixel->r = jpeg_pixel->r;
    tracker_pixel->g = jpeg_pixel->g;
    tracker_pixel->b = jpeg_pixel->b;
  }
 }

 tracker->x_pos = x_pos;
 tracker->y_pos = y_pos;

 return(1);
}

/*--------------------------------------------------------------*/
/*--------------Tracker jitter to compare data------------------*/
/*--------------------------------------------------------------*/

int Tracker_Jitter(TRACKER_DATA_STRUCT *tracker, TRACKER_DATA_STRUCT *jitter,
       JPEG_INFO_STRUCT *jpeg, int x_pos, int y_pos)

{
  int min_x,max_x,min_y,max_y,x,y,jitter_value,final_jitter_value,
   outmin_x,outmax_x,outmin_y,outmax_y,outx,outy;

  PIXEL_STRUCT *jitter_pixel;
  PIXEL_STRUCT *tracker_pixel;
  PIXEL_STRUCT *jpeg_pixel;

 final_jitter_value = 999999999;

 min_x = x_pos - (tracker->in_width  >> 1);
 max_x = x_pos + (tracker->in_width  >> 1);
 min_y = y_pos - (tracker->in_height >> 1);
 max_y = y_pos + (tracker->in_height >> 1);

 outmin_x = x_pos - (tracker->out_width  >> 1);
 outmax_x = x_pos + (tracker->out_width  >> 1);
 outmin_y = y_pos - (tracker->out_height >> 1);
 outmax_y = y_pos + (tracker->out_height >> 1);
 

 if(outmin_x < 0 || outmax_x > jpeg->width || outmin_y < 0 || outmax_y > jpeg->height)
 {
   fprintf(stderr, "ERROR: Jitter tracker point out of range.\n");
    return(0);    
  }

 for(outy = 0; outy <= (tracker->out_height - tracker->in_height); outy++)
  {
   for(outx = 0; outx <= (tracker->out_width - tracker->in_width); outx++)
   {
    jitter_value = 0;

   for(y = 0; y < tracker->in_height; y++)
    {
     for(x = 0; x < tracker->in_width; x++)
     {
      tracker_pixel = &tracker->image[y*tracker->in_height + x];
      jitter_pixel = &jitter->image[y*tracker->in_height + x];
      jpeg_pixel = &jpeg->image[jpeg->width*(outmin_y+y+outy) + (outmin_x+x+outx)];

     jitter_pixel->r = abs(jpeg_pixel->r - tracker_pixel->r);
     jitter_pixel->g = abs(jpeg_pixel->g - tracker_pixel->g);
     jitter_pixel->b = abs(jpeg_pixel->b - tracker_pixel->b);

     jitter_value += (jitter_pixel->r + jitter_pixel->g + jitter_pixel->b);
         // printf ("%d\n",jitter_value);
     }
    }

    if (jitter_value < final_jitter_value) 
    {
     final_jitter_value=jitter_value;
//     printf ("%d\n",final_jitter_value);
     x_pos = outmin_x + (tracker->in_width  >> 1) + outx;
     y_pos = outmin_y + (tracker->in_width  >> 1) + outy;
    }
   }
 }

// printf ("%d,%d",x_pos,y_pos);

 tracker->x_pos = x_pos;
 tracker->y_pos = y_pos;

 return(1);
}
 

/*--------------------------------------------------------------*/
/*--------------------------------------------------------------*/

void JPEG_HighlightArea(JPEG_INFO_STRUCT *jpeg, int x_pos, int y_pos, 
int size, float r, float g, float b)

{
  int min_x,max_x,min_y,max_y,x,y;
  PIXEL_STRUCT *pixel;

 min_x = x_pos - (size  >> 1);
 max_x = x_pos + (size  >> 1);
 min_y = y_pos - (size  >> 1);
 max_y = y_pos + (size  >> 1);

//   printf ("min_x: %d\nmax_x: %d\nmin_y: %d\nmax_y: %d\n",min_x,max_x,min_y,max_y);
//   printf ("Image size: %dx%d\n",jpeg->width, jpeg->height);

 if(min_x < 0 || max_x > jpeg->width || min_y < 0 || max_y > jpeg->height)
 {
   fprintf(stderr, "ERROR: Highlight tracker point out of range.\n");
  }

  for(y = 0; y <= size; y++)
 {
  for(x = 0; x < size; x++)
  {
       pixel = &jpeg->image[jpeg->width*(min_y+y) + (min_x+x)];

    pixel->r = (pixel->r * r);
    pixel->g = (pixel->g * g);
    pixel->b = (pixel->b * b);
  }
 }
}

/*--------------------------------------------------------------*/
/*---------------------[ Main Function ]------------------------*/
/*--------------------------------------------------------------*/

int main(int argc, char *argv[])
{

   char filename[50];
  TRACKER_DATA_STRUCT tracker_data,jitter_data;
  JPEG_INFO_STRUCT *jpeg_main;
  int n = 1,start_x,start_y,start_frame,end_frame,frame,xkey,ykey;
  int keyframes[300][2] = {{0},{0}};
  FILE *ofPtr; /* Pointer to output file */
  FILE *ifPtr; /* Pointer to input file */

  if(argc != 5)
  {
 printf("Usage: mocap X Y Startframe Endframe filenameprefix");
 return 1;
  }

  start_x = atoi(argv[1]); // convert first command line arg from string to integer
  start_y = atoi(argv[2]);
  start_frame = atoi(argv[3]);
  end_frame = atoi(argv[4]);
 

  // Initialise tracker data

    if(Tracker_Init(&tracker_data,40,20) == 0)
 return(0);

  if(Tracker_Init(&jitter_data,40,20) == 0)
 return(0);
 

  // Open files for input and output

  if ((ofPtr = fopen("output.txt", "w")) == NULL)
   printf("Error opening output.txt");

  if ((ifPtr = fopen("keyframes.txt", "r")) == NULL)
   printf("Error opening keyframes.txt");

  fprintf(ofPtr, "======Output for point n======\n");

  while (!feof(ifPtr)) {
   fscanf(ifPtr, "%d%d%d",&frame,&xkey,&ykey);
   keyframes[frame][1] = xkey;
   keyframes[frame][2] = ykey;

  };
 

  // Load in JPEG and read first tracker block

 sprintf(filename,"%s%d.jpg",argv[5],start_frame);

     if((jpeg_main = LoadJPEG(filename)) == NULL)
 return(0);

  if(Tracker_GetImageData(&tracker_data,jpeg_main, start_x, start_y) == 0)
 return(0);

   for(n = start_frame; n <= end_frame; n++)
  {

 sprintf(filename,"%s%d.jpg",argv[5],n);

     if((jpeg_main = LoadJPEG(filename)) == NULL)
 return(0);

  // Jitter the tracking point to determine where the new point is

  if(Tracker_Jitter(&tracker_data, &jitter_data, jpeg_main, tracker_data.x_pos, tracker_data.y_pos) == 0)
 return(0);

  // If keyframe data exists for current frame, then overwrite the value.

  if (keyframes[n][1] != 0)
   tracker_data.x_pos = keyframes[n][1];

  if (keyframes[n][2] != 0)
   tracker_data.y_pos = keyframes[n][2];

  fprintf(ofPtr, "%d %d %d\n",n, tracker_data.x_pos, tracker_data.y_pos);
  
  // Read in the image for the new point

  if(Tracker_GetImageData(&tracker_data,jpeg_main, tracker_data.x_pos, tracker_data.y_pos) == 0)
 return(0);

  // Highlight tracker point and output image

 JPEG_HighlightArea(jpeg_main, tracker_data.x_pos, tracker_data.y_pos, 
                 tracker_data.in_width, 0.0, 1.0, 0.5);

 JPEG_HighlightArea(jpeg_main, tracker_data.x_pos, tracker_data.y_pos, 
                 tracker_data.out_width, 1.0, 1.0, 0.5);

 sprintf(filename,"output%d.jpg",n);
      SaveJPEG(jpeg_main,filename,95);
 

   }

  FreeJPEG(jpeg_main);
  fclose(ofPtr);
  fclose(ifPtr);
  return 0;
 
}

Return to Motion Capture.
Home.