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