www.pudn.com > ReadingPeopleTracker-1.28.rar > HSV32Image.cc
/////////////////////////////////////////////////////////////////////////////// // // // HSV32Image.cc --- most of this was copied from RGB32Image.cc // // // // HSV image class derived from RGB32Image class // // // // Author : Nils T Siebel (nts), RGB32Image.cc by Adam M Baumberg // // Created : Thu Apr 19 11:59:19 BST 2001 // // Revision : 0.0 of Thu Apr 19 11:59:19 BST 2001 // // Copyright : The University of Reading // // // /////////////////////////////////////////////////////////////////////////////// static const char *HSV32Image_Revision = "@(#) HSV32Image.cc, rev 0.0 of Thu Apr 19 11:59:19 BST 2001, Authors Adam M Baumberg (RGB32 basis) and Nils T Siebel (all HSV stuff), Copyright (c) 2001 The University of Reading"; #include// for fmin, fmax #include #include "HSV32Image.h" #include "Grey8Image.h" #include "text_output.h" #ifndef NO_DISPLAY #ifdef USE_GL #include #else #include #include #include #include #endif #endif #include "tracker_defines_types_and_helpers.h" namespace ReadingPeopleTracker { // definition and initialisation of static member variables const char *HSV32Image::RGB32_to_HSV32_lookup_table_filename = "/advisor/nils/bin/RGB32_to_HSV32_lookup_table"; // 64 MB lookup table for RGB->HSV. set up using member function defined below HSV32pixel *HSV32Image::RGB32_to_HSV32_lookup_table = NULL; // write out an HSV32pixel as (h,s,v) triplet ostream &operator<< (ostream &target, const HSV32pixel &HSVpix) { target << "("; u_int32_t AX = 0; *((unsigned char*) &AX) = HSVpix.H; target << AX << ","; AX = 0; *((unsigned char*) &AX) = HSVpix.S; target << AX << ","; AX = 0; *((unsigned char*) &AX) = HSVpix.V; target << AX << ")"; return target; } // FIXME: This implementation is very slow. HSV32pixel HSV32Image::to_HSV32 (const RGB32pixel *RGBpix) { HSV32pixel res; // calculate HSV (after D Travis) float R = ((float) (unsigned int) RGBpix->red) / 255; float G = ((float) (unsigned int) RGBpix->green) / 255; float B = ((float) (unsigned int) RGBpix->blue) / 255; float min_RGB = fmin(fmin(R,G),B); float max_RGB = fmax(fmax(R,G),B); float S = (max_RGB == 0) ? 1 : (max_RGB - min_RGB) / max_RGB; float V = max_RGB; float H; if (S == 0) H = 0; // in fact, undefined (colour has no hue) else { float Rdash = (max_RGB - R) / (max_RGB - min_RGB); float Gdash = (max_RGB - G) / (max_RGB - min_RGB); float Bdash = (max_RGB - B) / (max_RGB - min_RGB); if ((R == max_RGB) && (G == min_RGB)) H = 5 + Bdash; else if (R == max_RGB) H = 1 - Gdash; else if ((G == max_RGB) && (B == min_RGB)) H = 1 + Rdash; else if (G == max_RGB) H = 3 - Bdash; else if (R == max_RGB) H = 3 + Gdash; else H = 5 - Rdash; } // gives H \in [0..6] H = H / 6; res.H = (int) (255 * H); res.S = (int) (255 * S); res.V = (int) (255 * V); return res; } // This lookup version uses a 64 MB lookup table (but in spite of guaranteed // cache misses it is still faster than the calculation) --- nts. HSV32pixel HSV32Image::to_HSV32_using_lookup (const RGB32pixel *RGBpix) { if (RGB32_to_HSV32_lookup_table == NULL) // better check... { cerror << " Warning: to_HSV32_using_lookup: NULL table! " << endl; return to_HSV32 (RGBpix); } else { return RGB32_to_HSV32_lookup_table [(((RGBpix->red << 8) + RGBpix->blue) << 8) + RGBpix->green]; } } inline unsigned int HSV32Image::to_uint(const HSV32pixel colour) { return * ((unsigned int*) &colour); } // set up the lookup table for to_HSV32_using_lookup void HSV32Image::setup_HSV32_to_RGB32_lookup_table() { if (RGB32_to_HSV32_lookup_table != NULL) // don't allocate 64 MB twice! { cerror << " setup_HSV32_to_RGB32_lookup_table(): Warning: alloc requested" << " when table was already set up? " << endl; return; // further operation is possible assuming table is ok. } RGB32_to_HSV32_lookup_table = new HSV32pixel [256 * 256 * 256]; if (RGB32_to_HSV32_lookup_table == NULL) // better check { cerror << " setup_HSV32_to_RGB32_lookup_table(): Warning: could not alloc" << " 64 MB for the lookup table. " << endl; return; // further operation is possible but will be slow w/o lookup } cdebug << " Setting up RGB to HSV lookup table ..."; FILE *infile; // to read a pre-computed version of the table from disk: if ((infile = fopen(RGB32_to_HSV32_lookup_table_filename,"rb")) != NULL) { // try reading the a cached version of the table from file: if (fread(RGB32_to_HSV32_lookup_table, sizeof(HSV32pixel), 256*256*256, infile) == 256*256*256) // reading went ok { cdebug << " (cached)" << endl << endl; return; } } // use this and the 2 lines below if you want to write a new table... // FILE *outfile = fopen(RGB32_to_HSV32_lookup_table,"wb"); RGB32pixel colour; colour.alpha = 0x00; HSV32pixel *colourindex = & (RGB32_to_HSV32_lookup_table[0]); for (unsigned int red = 0; red < 256; red++) { colour.red = red; for (unsigned int green = 0; green < 256; green++) { colour.green = green; for (unsigned int blue = 0; blue < 256; blue++) { colour.blue = blue; *colourindex++ = to_HSV32(&colour); } } } // do this to write out the table (once) // fwrite(RGB32_to_HSV32_lookup_table,sizeof(HSV32pixel),256*256*256,outfile); // fclose(outfile); cdebug << " done." << endl << endl; } Image *HSV32Image::copy(Image *res) { if (res == NULL) res = new HSV32Image(width, height, frame_id, frame_time_in_ms); else { // check destination image format and dimensions if ((res->get_image_type() != HSV32) || (res->get_width() != width) || (res->get_height() != height)) { cerror << " HSV32Image::copy: cannot copy to resulting image of different type/dimensions " << endl; exit(1); } // use same frame id and time since it is the same image res->set_frame_id(frame_id); res->set_frame_time_in_ms(frame_time_in_ms); } res->set_timestamp(×tamp); void *src = data; void *dest = res->get_data(); size_t n = (width * height) << 2; memcpy(dest, src, n); return res; } //// Threshold to grey (values MARK and CLEAR_MARK) //// FIXME: uses only V for now. Image *HSV32Image::threshold(unsigned int threshold, Image *res, unsigned int *no_marked) { if (res == NULL) res = new Grey8Image(width, height, frame_id, frame_time_in_ms); // row and column counters... unsigned int row; unsigned int col; // pointers to data: columns of image and result HSV32pixel *image_col; unsigned char *result_col; register unsigned int sum; unsigned int colour_threshold = threshold; unsigned int mark_count = 0; for (row = 0; row < height; row++) { image_col = (HSV32pixel*) get_pixel(0,row); result_col = res -> get_pixel(0,row); for (col = 0; col < width; col++) { sum = (unsigned int) image_col++ -> V; if (sum <= colour_threshold) *result_col++ = CLEAR_MARK; else { *result_col++ = MARK; mark_count++; } } } if (no_marked != NULL) *no_marked = mark_count; return res; } //// "ColourFilter" to grey //// for experiments Image *HSV32Image::ColourFilter (Image *res, ColourFilteringMethod method) { if (res == NULL) res = new Grey8Image(width, height, frame_id, frame_time_in_ms); // row and column counters... unsigned int row; unsigned int col; // pointers to data: columns of image and result HSV32pixel *image_col; unsigned char *result_col; unsigned int grey; // FIXME: should put switch out of loop or use several inline // functions (pointed to) or something. No time. for (row = 0; row < height; row++) { image_col = (HSV32pixel*) get_pixel(0,row); result_col = res -> get_pixel(0,row); for (col = 0; col < width; col++) { switch (method) { // Simple norm methods case CF_METHOD_1_NORM: // 1-norm of (H,S,V) { grey = ((unsigned int) image_col->H + (unsigned int) image_col->S + (unsigned int) image_col->V + 1) / 3; break; } case CF_METHOD_2_NORM: // 2-norm of (H,S,V) { grey = (unsigned int) (sqrt( ((float) ((SQUARE ((unsigned int) image_col->H)) +(SQUARE ((unsigned int) image_col->S)) +(SQUARE ((unsigned int) image_col->V)))) / 3)); break; } case CF_METHOD_MAX_NORM: // max-norm of (H,S,V) { grey = MAX(MAX((unsigned int) image_col->H, (unsigned int) image_col->S), (unsigned int) image_col->V); break; } // HSV after D Travis. This is easy with this image class... case CF_METHOD_H: { grey = image_col->H; break; } case CF_METHOD_S: { grey = image_col->S; break; } case CF_METHOD_V: { grey = image_col->V; break; } // case CF_METHOD_Y_709: // Y from YCbCr after CIE recommendation 709 // { // // source: C Poynton's Colour FAQ page 6 // grey = (218 * (unsigned int) image_col->H // + 732 * (unsigned int) image_col->S // + 74 * (unsigned int) image_col->V) // >> 10; // break; // } default: { cerror << " HSV32Image::ColourFilter: unknown filtering method requested " << endl; exit(1); } } if (grey > 255) *result_col++ = 255; else *result_col++ = (unsigned char) grey; image_col++; } } return res; } //// "ColourDistance" to grey Image *HSV32Image::ColourDistance (Image *res, ColourDistanceMethod method) { if (res == NULL) res = new Grey8Image(width, height, frame_id, frame_time_in_ms); // row and column counters... unsigned int row; unsigned int col; // pointers to data: columns of image and result HSV32pixel *image_col; unsigned char *result_col; unsigned int grey; // FIXME: should put switch out of loop or use several inline // functions (pointed to) or something. No time. for (row = 0; row < height; row++) { image_col = (HSV32pixel*) get_pixel(0,row); result_col = res -> get_pixel(0,row); for (col = 0; col < width; col++) { switch (method) { case CD_METHOD_V_WEIGHTED_HS: // distance in H/S circle, weighted by V { grey = ((unsigned int) image_col->H + (unsigned int) image_col->S + (unsigned int) image_col->V + 1) / 3; break; } default: { cerror << " HSV32Image::ColourDistance: unknown distance measure requested " << endl; exit(1); } } if (grey > 255) *result_col++ = 255; else *result_col++ = (unsigned char) grey; image_col++; } } return res; } //// colour differencing (no filtering or thresholding), using simple abs/diff Image *HSV32Image::difference(Image *image2, Image *res) { if ((this == NULL) || (image2 == NULL)) return NULL; if (image2->get_image_type() != HSV32) { if (image2->get_image_type() == GREY8) return HSV32Image::difference((Grey8Image*) image2, res); cerror << "HSV32Image::difference(): cannot difference different image types. " << endl; exit(1); } if (res == NULL) res = new HSV32Image(width, height, frame_id, frame_time_in_ms); // row and column counters... unsigned int row; unsigned int col; // pointers to data: columns in both images, and the result HSV32pixel *image1_col; HSV32pixel *image2_col; HSV32pixel *result_col; HSV32pixel temp_result; temp_result.alpha = 0x00; for (row = 0; row < height; row++) { image1_col = (HSV32pixel*) get_pixel(0,row); image2_col = (HSV32pixel*) image2 -> get_pixel(0,row); result_col = (HSV32pixel*) res -> get_pixel(0,row); for (col = 0; col < width; col++) { temp_result.H = (image1_col->H > image2_col->H) ? (image1_col->H - image2_col->H) : (image2_col->H - image1_col->H); if (temp_result.H > 127) // wrap around difference temp_result.H = 255 - temp_result.H; temp_result.H <<= 2; temp_result.S = (image1_col->S > image2_col->S) ? (image1_col->S - image2_col->S) : (image2_col->S - image1_col->S); temp_result.V = (image1_col->V > image2_col->V) ? (image1_col->V - image2_col->V) : (image2_col->V - image1_col->V); *result_col++ = temp_result; image1_col++; image2_col++; } } return res; } //// colour differencing --- version for HSV32-GREY8: at the moment, only uses V Image *HSV32Image::difference(Grey8Image *image2, Image *res) { if ((this == NULL) || (image2 == NULL)) return NULL; if (image2->get_image_type() != GREY8) { cerror << "HSV32Image::difference(): cannot difference these image types. " << endl; exit(1); } if (res == NULL) res = new HSV32Image(width, height, frame_id, frame_time_in_ms); // row and column counters... int row; int col; // pointers to data: columns in both images, and the result HSV32pixel *image1_col; unsigned char *image2_col; HSV32pixel *result_col; HSV32pixel temp_result; temp_result.alpha = 0x00; for (row = 0; row < height; row++) { image1_col = (HSV32pixel*) get_pixel(0,row); image2_col = image2 -> get_pixel(0,row); result_col = (HSV32pixel*) res -> get_pixel(0,row); for (col = 0; col < width; col++) { temp_result.H = image1_col->H; temp_result.S = image1_col->S; temp_result.V = (image1_col->V > *image2_col) ? (image1_col->V - *image2_col) : (*image2_col - image1_col->V); *result_col++ = temp_result; image1_col++; image2_col++; } } return res; } Image *HSV32Image::resample(int xstep , int ystep , Image *resampled) { int new_width = (width / xstep); if (resampled == NULL) resampled = (Image *) new HSV32Image(new_width, height / ystep, frame_id, frame_time_in_ms); HSV32pixel *idat; HSV32pixel *odat; int outy = 0; for (int y = 0; y < height; y += ystep) { idat = (HSV32pixel*) get_pixel(0,y); odat = (HSV32pixel*) resampled->get_pixel(0, outy); for (int x = 0; x < new_width; x ++) { *odat = *idat; odat++; idat = idat + xstep; } outy++; } return resampled; } int HSV32Image::compar_char(const void *c1, const void *c2) { return (int) (*((unsigned char *) c1) - *((unsigned char *) c2)); } inline void HSV32Image::clear(HSV32pixel fill_colour) { HSV32pixel *current; HSV32pixel *end = (HSV32pixel *) end_data; for (current = (HSV32pixel *) data; current < end; *current++ = fill_colour) ; } void HSV32Image::clear_border() { HSV32pixel HSV_CLEAR_MARK = {0,0,0,0}; register int w1 = width-1; for (register int y = 0; y < height; y++) { *((HSV32pixel*) get_pixel( 0,y)) = HSV_CLEAR_MARK; *((HSV32pixel*) get_pixel(w1,y)) = HSV_CLEAR_MARK; } register int h1 = height-1; for (register int x = 0; x < width; x++) { *((HSV32pixel*) get_pixel(x, 0)) = HSV_CLEAR_MARK; *((HSV32pixel*) get_pixel(x,h1)) = HSV_CLEAR_MARK; } } Image *HSV32Image::mask(Image *mask, Image *res) { if (mask == NULL) return NULL; if (res == NULL) res = new HSV32Image(width, height, frame_id, frame_time_in_ms); if ((mask->get_width() != width) || (mask->get_height() != height)) { register unsigned long *mask_pix; register unsigned int x; unsigned int y; for (y = 0; y < height; y++) for (x = 0; x < width; x++) { // FIXME: Should use HSV32pixel here, as well. if (mask->check_coords(x,y)) { mask_pix = (unsigned long*) mask->get_pixel(x,y); if (*mask_pix != 0x00000000) *((unsigned long*) res->get_pixel(x,y)) = *((unsigned long*) get_pixel(x,y)); } } } else { // FIXME: Should use HSV32pixel here, as well. unsigned long *dat1 = (unsigned long*) data; unsigned long *dat2 = (unsigned long*) mask->get_data(); unsigned long *edat = (unsigned long*) get_end_data(); register unsigned long *rdat = (unsigned long*) res->get_data(); while (dat1 < edat) *rdat++ = *dat1++ & *dat2++; } return res; } void HSV32Image::draw_rectangle(int xmin, int xmax, int ymin, int ymax, int val) { if ((xmin > xmax) || (ymin > ymax)) { cerror << " HSV32Image::draw_rectangle(" << xmin << "," << xmax << "," << ymin << "," << ymax << ", " << val << ") : cannot draw! " << endl; return; } if (xmin < 0) xmin = 0; if (ymin < 0) ymin = 0; if (xmax >= width) xmax = (width-1); if (ymax >= height) ymax = (height-1); int nbytes = (xmax - xmin + 1) * bytes_per_pixel; for (int y = ymin; y <= ymax; y++) memset(get_pixel(xmin,y),val, nbytes); // just paint grey values all over } } // namespace ReadingPeopleTracker