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