www.pudn.com > ReadingPeopleTracker-1.28.rar > JPEGSource.cc


///////////////////////////////////////////////////////////////////////////////
//                                                                           //
//  JPEGSource.cc (structure copied from MpegStream.cc by Adam Baumberg)     //
//                (based on our new (JPEG) MovieStore and IJG's example.c)   //
//                                                                           //
//  This class reads individual, numbered JPEG files as an ImageSource       //
//                                                                           //
//  This code is written with full colour images in mind (24/32-bit).        //
//                                                                           //
//  Author    : Nils T Siebel (nts)                                          //
//  Created   : Thu Oct 26 18:52:41 GMT 2000                                 //
//  Revision  : 1.5 of Tue Jul 24 15:55:06 BST 2001                          //
//  Copyright : The University of Reading                                    //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

#include 
#include 
#include   // for strncpy() etc
#include    // for isdigit()
#include 
#include 
#include 
#include 

#include "JPEGSource.h"

#include "Grey8Image.h"
#include "RGB32Image.h"
#include "tracker_defines_types_and_helpers.h"
#include "text_output.h"

namespace ReadingPeopleTracker
{

static const char *JPEGSource_Revision = "@(#) JPEGSource.cc, rev 1.5 of Tue Jul 24 15:55:06 BST 2001, Author Nils T Siebel, Copyright (c) 2000--2001 The University of Reading";

JPEGSource::JPEGSource(char *first_filename,
		       bool the_skip_non_existing,
		       unsigned int the_max_skip_non_existing)
{
    skip_non_existing = the_skip_non_existing;
    max_skip_non_existing = the_max_skip_non_existing;
    
    signed int pos;
    
    strncpy(JPEGfilename, first_filename, 256+16+64-1);
    JPEGfilename[256+16+64-1] = (char) 0x00;  // avoid overrun
    
    // set up file name(s)...
    for (pos = strlen(JPEGfilename)-1; pos >= 0; pos--)
	if (isdigit(JPEGfilename[pos]))
	    break;
    
    if (pos < 0)
    {
	cerror << "JPEGSource::JPEGSource: To use individual JPEG images as input, please give first filename, like »input017.jpg« " << endl;
	exit(1);
    }
    
    strncpy(JPEGfilename_ext, &JPEGfilename[pos+1], 63);
    JPEGfilename_ext[63] = (char) 0x00;
    JPEGfilename[pos+1] = 0;  // for atol
    
    for ( ; pos >= 0; pos--)
	if (! (isdigit(JPEGfilename[pos])))
	    break;
    
    if (! (JPEGfilename_num_digits = strlen(&JPEGfilename[pos+1])))
    {
	cerror << "JPEGSource::JPEGSource: To use individual JPEG images as input, please give first filename, like »input023.jpg«\n" << endl;
	exit(1);
    }
    
    strncpy(JPEGfilename_base, JPEGfilename, MIN(pos+1, 255));
    JPEGfilename_base[MIN(pos+1, 255)] = 0;
    
    JPEGstart_frame_number = atol(&JPEGfilename[pos+1]);  // set start frame number.
    JPEGnum_frames = 0;             // set frame count to 0 (no frames read so far).
    
    cinfo.err = jpeg_std_error(&jerr);
    jpeg_create_decompress(&cinfo);
    
    // We need the image dimensions.  We take it from the 1st image:
    
    // create first file name and open file for reading
    sprintf(JPEGfilename, "%s%0*i%s", JPEGfilename_base,
	    JPEGfilename_num_digits, JPEGstart_frame_number,
	    JPEGfilename_ext);
    
    if (!(JPEGinfile = fopen(JPEGfilename,"r")))
    {
	cerror << "JPEGSource::JPEGSource: Could not open numbered input file »"
	       << JPEGfilename << "« " << endl;
	exit(1);
    }
    
    // initialise JPEG decompression and start reading JPEG file (header)
    jpeg_stdio_src(&cinfo, JPEGinfile);
    
    (void) jpeg_read_header(&cinfo, true);
    // We can ignore the return value from jpeg_read_header since
    //   (a) suspension is not possible with the stdio data source, and
    //   (b) we passed true to reject a tables-only JPEG file as an error.
    // See libjpeg.doc for more info.
    
    // now we can use and store these values...
    JPEGxdim = cinfo.image_width;
    JPEGydim = cinfo.image_height;
    is_grey_image = (cinfo.jpeg_color_space == JCS_GRAYSCALE);
    
    jpeg_destroy_decompress(&cinfo);
    cinfo.err = jpeg_std_error(&jerr);
    jpeg_create_decompress(&cinfo);
    
    fclose(JPEGinfile);  // finished for now.  we'll get back to this file later.
    
    current = NULL;
    ImageSource::source_type = IST_NUMBERED_FILES;

    // reset error message count
    header_warning_count = 0;
}

JPEGSource::~JPEGSource()
{
    jpeg_destroy_decompress(&cinfo);
}





//////////////////////////////////////////////////////////////////////////////
//                                                                          //
//  JPEGSource::get_next: Decompress next JPEG file to get next input frame //
//                                                                          //
//////////////////////////////////////////////////////////////////////////////

Image *JPEGSource::get_next()
{
    frame_id_t new_frame_id;
    frame_time_t new_frame_time_in_ms;

   if (JPEGnum_frames == 0) // first frame
	JPEGcurrent_frame_number = JPEGstart_frame_number;
    else 
	JPEGcurrent_frame_number++;
    
    sprintf(JPEGfilename, "%s%0*i%s", JPEGfilename_base,
	    JPEGfilename_num_digits,
	    JPEGcurrent_frame_number,
	    JPEGfilename_ext);
    
    unsigned int number_of_tries = 1;
    while ((JPEGinfile = fopen(JPEGfilename,"r")) == NULL)
    {
	if (JPEGnum_frames == 0)   //  we have never read a file
	{
	    // We should not get here unless the file has been removed after
	    // our object was instantiated.  Well, better safe than sorry...
	    cerror << "JPEGSource::get_next: Could not open numbered input file »"
		   << JPEGfilename << "« " << endl;
	    exit(1);
	}
	if ((skip_non_existing == true) &&
	    (number_of_tries <= max_skip_non_existing))
	{
	    // try the next numbered image:
	    JPEGcurrent_frame_number++;
	    sprintf(JPEGfilename, "%s%0*i%s", JPEGfilename_base,
		    JPEGfilename_num_digits,
		    JPEGcurrent_frame_number,
		    JPEGfilename_ext);
	    number_of_tries++;
	}
        else
        {
	    current = NULL;
	    return current;
        }
    }
    
    // read JPEG Time Stamp Header (for the ADVISOR System)
    unsigned char marker[3] = {0x00,0x00};
    unsigned char max_length[3] = {0x00,0x00};
    
    char year[5] = "0000";
    char month[3] = "00";
    char day[3] = "00";
    char hour[3] = "00";
    char min[3] = "00";
    char sec[3] = "00";
    char ms[4] = "000";
    char id[7] = "000000";
    
    fseek(JPEGinfile,2,SEEK_SET);
    
    fread(marker,sizeof(unsigned char),2,JPEGinfile);
    fread(max_length,sizeof(unsigned char),2,JPEGinfile);
    
    fread(year,sizeof(char),4,JPEGinfile);
    fread(month,sizeof(char),2,JPEGinfile);
    fread(day,sizeof(char),2,JPEGinfile);
    fread(hour,sizeof(char),2,JPEGinfile);
    fread(min,sizeof(char),2,JPEGinfile);
    fread(sec,sizeof(char),2,JPEGinfile);
    fread(ms,sizeof(char),3,JPEGinfile);
    fread(id,sizeof(char),6,JPEGinfile);
    
    have_valid_header = true;
    
    if ((marker[0] != 0xFF) || (marker[1] != 0xE0))
    {
        cdebug << "JPEGSource::get_next: Error in file : " << JPEGfilename << endl
	       << "  Bad Application Marker Segment." << endl;
	have_valid_header = false;
    }
    else
	if ((max_length[0] != 0x00) || (max_length[1] != 0x3E))
	{
	    if (header_warning_count < 3) // limit number of error messages to 3
	    {
		cdebug << "JPEGSource::get_next: Error in file : " << JPEGfilename << endl
		       << "  Invalid Time Stamp Header ignored." <<  endl;

		// count error messages in order to limit them
		header_warning_count++;

		if (header_warning_count == 3)
		    cdebug << "JPEGSource::get_next: This error message will now be disabled."
			   <<  endl;
	    }

	    have_valid_header = false;
	}
    
    tm *f_time = new tm;
    if (have_valid_header)
    {
	f_time->tm_sec = atoi(sec);
	f_time->tm_min = atoi(min);
	f_time->tm_hour = atoi(hour);
	f_time->tm_mday = atoi(day);
	f_time->tm_mon = atoi(month);
	f_time->tm_year = atoi(year) - 1900;   //  OK, see mktime(3) or ctime(3)
	f_time->tm_isdst = 1;    // FIXME: not necessarily.  or is it?
	
	// extra check: invalid year?
	if ((f_time->tm_year < 70) || (f_time->tm_year > 169))
	{
	    if (header_warning_count < 3) // limit number of error messages to 3
	    {
		cdebug << "JPEGSource::get_next: Warning: strange year encountered in time stamp: "
		       << atoi(year) << endl
		       << "  Invalid Time Stamp Header ignored." <<  endl;
		
		// count error messages in order to limit them
		header_warning_count++;
	    
		if (header_warning_count == 3)
		    cdebug << "JPEGSource::get_next: This error message will now be disabled."
			   <<  endl;
	    }
	    
	    have_valid_header = false;
	}
    }
    
    if (have_valid_header)
    {
        // remember frame id and frame time for later use (once we actually have an Image)
        new_frame_id = atol(id);
        
	time_t new_frame_time_in_s = mktime(f_time);
	
	new_frame_time_in_ms =
            ((frame_time_t) new_frame_time_in_s)   // this is in seconds
            * 1000 + atoi(ms);                     // make milliseconds and add delta
        
    }
    else
    {
	// assume starting time 0 at frame 0
        new_frame_id = JPEGnum_frames;
        new_frame_time_in_ms = (frame_time_t)
            (JPEGnum_frames * default_frame_time_in_ms);
    }
    delete f_time;
   
    fseek(JPEGinfile,0,SEEK_SET);
    
    // initialise JPEG decompression and start reading JPEG file (header)
    jpeg_stdio_src(&cinfo, JPEGinfile);
    
    (void) jpeg_read_header(&cinfo, true);
    // We can ignore the return value from jpeg_read_header since
    //   (a) suspension is return value from jpeg_read_header since
    //   (a) suspension  not possible with the stdio data source, and
    //   (b) we passed true to reject a tables-only JPEG file as an error.
    // See libjpeg.doc for more info.
    
    if (is_grey_image)
    {
	// Set up JPEG decompression parameters to hopefully give b/w images
	// (the only method guaranteeing that would be to give a b/w colourmap which
	//  only works in RGB mode.  That is too complicated and slow --- nts.)
	cinfo.actual_number_of_colors = cinfo.desired_number_of_colors = 2;
	cinfo.dither_mode = JDITHER_NONE;
	cinfo.two_pass_quantize = false;
    }
    
    (void) jpeg_start_decompress(&cinfo);
    
    // We can ignore the return value since suspension is not possible
    // with the stdio data source.
    
    // We need to do some setup of our own at this point before reading
    // the data.  After jpeg_start_decompress() we have the correct scaled
    // output image dimensions available, as well as the output colourmap
    // if we asked for colour quantization.
    
    // save previous values so we can detect changes here...
    int old_JPEGxdim = JPEGxdim;
    int old_JPEGydim = JPEGydim;
    bool old_is_grey_image = is_grey_image;
    
    JPEGydim = cinfo.output_height;
    JPEGxdim = cinfo.output_width;
    
    switch (cinfo.jpeg_color_space)
    {
    case JCS_GRAYSCALE:
	// create resulting image if it doesn't exist
	if (current == NULL)
	    current = new Grey8Image(JPEGxdim,JPEGydim);
	else
	    if ((current->get_image_type() != GREY8)
		|| (current->get_width() != JPEGxdim) || (current->get_height() != JPEGydim))
	    {
		cerror <<  "JPEGSource::get_next: input images have different types/dimensions "
		       << endl;
		exit(1);
	    }
	is_grey_image = true;
	break;
	
    case JCS_RGB:
    case JCS_YCbCr:
	// create resulting image if it does not exist
	if (current == NULL)
	    current = new RGB32Image(JPEGxdim,JPEGydim);
	else
	    if ((current->get_image_type() != RGB32)
		|| (current->get_width() != JPEGxdim) || (current->get_height() != JPEGydim))
	    {
		cerror << "JPEGSource::get_next: input images have different types/dimensions "
		       << endl;
		exit(1);
	    }
	is_grey_image = false;
	break;
	
    default:
	cerror << "JPEGSource::get_next: input image file »"
	       << JPEGfilename << "« has an unsupported image format " << endl;
	exit(1);
    }
    
    row_stride = JPEGxdim * cinfo.output_components;  // we need this many bytes per line
    
    //  set up JPEGimage_buffer and pointers to...
    if (JPEGnum_frames == 0) // this image is the first one in a sequence
    {
	if (! is_grey_image)  // we will write grey images directly to the Image
	{
	    // set up colour JPEGimage_buffer and row pointers for decompression
	    
	    // check size of image for buffering
	    if ((JPEGydim > 1200) || (row_stride > 1600*3)) // too large for our buffer?
	    {
		cerror << "JPEGSource::get_next: Dimensions of image in file »"
		       << JPEGfilename << "« too large. " << endl;
		exit(1);
	    }
	    
	    // set up row pointers
	    register unsigned int r_row;
	    register unsigned char *buffer_pointer = JPEGimage_buffer;
	    
	    for (r_row = 0; r_row < JPEGydim; r_row++)
	    {
		row_pointer[r_row] = buffer_pointer;
		buffer_pointer += row_stride;  // assuming xdim%4 == 0 so rows are 32-bit aligned
	    }
	}
    }
    else  // this is the second or later image, check consistency:
	if ((old_JPEGxdim != JPEGxdim) || (old_JPEGydim != JPEGydim)
	    || (old_is_grey_image != is_grey_image))
	{
	    // this image has a different size/colour space than the one(s) before!
	    cerror << "JPEGSource::get_next: Warning: numbered input file »"
		   << JPEGfilename << "«" << endl
		   << " has different dimensions/colours than the one before,"
		   << " assuming end of sequence." << endl;
	    return NULL;    //  indicate end of input sequence
	}
    
    // file is open.  now we decompress it into our local image_buffer
    // or (if is_grey_image) directly to the Image
    
    // jpeg_read_scanlines expects an array of pointers to scanlines.
    // it returns the number of scanlines actually read.
    
    // we would like to read all lines of the image into our image buffer in one
    // go.  libjpeg, however, reads a maximum number of 2 scanlines at a time :-(
    // nevertheless, we request it to read all scanlines until we have them all
    
    unsigned int scanlines_read;
    unsigned int scanlines_read_sum;
    
    if (is_grey_image)
    {
	// jpeg_lib returns image addressed top to bottom.
	// see if we need to flip it for the result (*current)
	if (Image::image_storage_mode == IA_TOP_TO_BOTTOM)
	{
	    for (scanlines_read_sum = 0; scanlines_read_sum < JPEGydim;
		 scanlines_read_sum += scanlines_read)
	    {
		// only read one scanline at a time so we have fewer problems
		row_pointer[0] = current->get_pixel(0,JPEGydim - scanlines_read_sum-1);
		
		scanlines_read = jpeg_read_scanlines (&cinfo, &row_pointer[0] ,1);
		
		if (scanlines_read == 0)
		{
		    cerror << "JPEGSource::get_next: Warning: numbered input file »"
			   << JPEGfilename << "« has an error. " << endl
			   << "Could not read any more scanlines (read 0 now, "
			   << scanlines_read_sum << " altogether, image has "
			   << JPEGydim << ") " << endl;
		    // let jpeg_finish_decompress exit for us if necessary...
		}
	    }
	}
	else
	{
	    // since we store images BOTTOM_TO_TOP, we must flip the image (y lines)
	    for (scanlines_read_sum = 0; scanlines_read_sum < JPEGydim;
		 scanlines_read_sum += scanlines_read)
	    {
		// only read one scanline at a time, because we flip the image
		row_pointer[0] = current->get_pixel(0,JPEGydim - scanlines_read_sum-1);
		
		scanlines_read = jpeg_read_scanlines (&cinfo, &row_pointer[0], 1);
		
		if (scanlines_read == 0)
		{
		    cerror << "JPEGSource::get_next: Warning: numbered input file »"
			   << JPEGfilename << "« has an error. " << endl
			   << "Could not read any more scanlines (read 0 now, "
			   << scanlines_read_sum << " altogether, image has "
			   << JPEGydim << ") " << endl;
		    // let jpeg_finish_decompress exit for us if necessary...
		}
	    }
	}
    }
    else  // ie colour image with 3 components r,b,g
    {
	// Put colour images in a temporary buffer because the format RGBRGB... is
	// incompatible with ours RGBaRGBa... Colour images will be flipped according
	// yo Image::image_storage_mode below if this is found to be necessary
	for (scanlines_read_sum = 0; scanlines_read_sum < JPEGydim;
	     scanlines_read_sum += scanlines_read)
	{
	    scanlines_read = jpeg_read_scanlines
		(&cinfo, &row_pointer[scanlines_read_sum], JPEGydim-scanlines_read_sum);
	    
	    if (scanlines_read == 0)
	    {
		cerror << "JPEGSource::get_next: Warning: numbered input file »"
		       << JPEGfilename << "« has an error. " << endl
		       << "Could not read any more scanlines (read 0 now, "
		       << scanlines_read_sum << " altogether, image has "
		       << JPEGydim << ") " << endl;
		// let jpeg_finish_decompress exit for us if necessary...
	    }
	}
    }
    
    (void) jpeg_finish_decompress(&cinfo);
    // We can ignore the return value since suspension is not possible
    // with the stdio data source.
    
    fclose(JPEGinfile);
    
    JPEGnum_frames++;  // increase frame count    

    // set time and id of new image, according to data calculated above
    current->set_frame_id (new_frame_id);
    current->set_frame_time_in_ms (new_frame_time_in_ms);

    frame_count++;

    if (is_grey_image)  // we are finished because we wrote data directly to Image
	return current;


    //
    //  now we have the COLOUR image in JPEGimage_buffer but it is RGBRGBRGB...
    //  we have to convert to RGBaRGBa, no matter how slow
    //
    
    // copy result to image, converting to 32-bit, flipping if necessary
    unsigned int row;
    register unsigned int col;
    register unsigned char *rgb_ptr;
    register RGB32pixel *rgba_ptr;
    
    // jpeg_lib returns image addressed top to bottom.
    // see if we need to flip it for the result (*current)
    if (Image::image_addressing_mode == IA_TOP_TO_BOTTOM)
    {
	// no need to flip the image while converting to 32-bit
	for (row = 0; row < JPEGydim; row++)
	{
	    rgba_ptr = (RGB32pixel *) current->get_pixel(0,row);
	    rgb_ptr = row_pointer[row];
	    
	    for (col = 0; col < JPEGxdim; col++)
	    {
		// see definition of RGB32pixel in RGB32Image.h
		rgba_ptr->red = *(rgb_ptr++);    // R
		rgba_ptr->green = *(rgb_ptr++);  // G
		rgba_ptr->blue = *(rgb_ptr++);   // B
		// not necessary to assign alpha, or is it?
		rgba_ptr->alpha = (unsigned char) 0x00;
		
		rgba_ptr++;
	    }
	}
    }
    else
    {
	// we need to flip the image (y lines) while converting to 32-bit
	for (row = 0; row < JPEGydim; row++)
	{
	    rgba_ptr = (RGB32pixel *) current->get_pixel(0,JPEGydim-row-1);
	    rgb_ptr = row_pointer[row];
	    
	    for (col = 0; col < JPEGxdim; col++)
	    {
		// see definition of RGB32pixel in RGB32Image.h
		rgba_ptr->red = *(rgb_ptr++);    // R
		rgba_ptr->green = *(rgb_ptr++);  // G
		rgba_ptr->blue = *(rgb_ptr++);   // B
		// not necessary to assign alpha, or is it?
		rgba_ptr->alpha = (unsigned char) 0x00;
		
		rgba_ptr++;
	    }
	}
    }
    
    return current;
}

} // namespace ReadingPeopleTracker