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. |