www.pudn.com > ReadingPeopleTracker-1.28.rar > Image.cc
/* * Image.cc * general Image class * * author : A M Baumberg */ #ifdef _SVID_SOURCE #include// for System V style 48-bit random number generator // functions void srand48(long int seed_val), double drand48(void) // (_SVID_SOURCE must be defined for this to work --- available under Linux) #endif #include #include #include #include #include #include "Image.h" #include "Point2.h" #include "Kernel.h" #include "text_output.h" #include "tracker_defines_types_and_helpers.h" namespace ReadingPeopleTracker { // definition and initialisation of static member variables const int Image::CIRCLE_PRECISION = 8; Point2 *Image::pt = NULL; #ifndef NO_DISPLAY EnvIntParameter *Image::display_zoom = new EnvIntParameter("IMAGE_ZOOM", 1); #endif // ifndef NO_DISPLAY // Global and constant variables that determine Image Addressing: // image_addressing_mode: determines the orientation of the v axis in the image. // This should always be BOTTOM_TO_TOP, like axes in mathematics and physics. // TOP_TO_BOTTOM: line 0 is the top line, BOTTOM_TO_TOP: line 0 is the bottom line const ImageAddressing Image::image_addressing_mode = IA_BOTTOM_TO_TOP; // image_storage_mode: determines the orientation of the image in memory. This has an // influence only on functions which operate in the image memory, e.g. JPEGSource. // This should be set to the way your graphics library or display (e.g. X11) handles it. // TOP_TO_BOTTOM: a lower memory address is closer to the top line of the image, // BOTTOM_TO_TOP: a higher memory address is closer to the top line of the image. #ifdef LINUX #ifdef USE_GL // Warning: this is unlike X11, which makes displaying images slower... const ImageAddressing Image::image_storage_mode = IA_BOTTOM_TO_TOP; #else const ImageAddressing Image::image_storage_mode = IA_TOP_TO_BOTTOM; #endif // USE_GL #else // ie not LINUX #error " Set both image addressing variables to correct values. " ImageAddressing Image::image_storage_mode = IA_TOP_TO_BOTTOM; #endif // ifdef LINUX // please check usage of this varable below when changing this! #ifndef NO_DISPLAY #ifndef USE_GL #ifdef USE_VOGL extern "C" { // these are vogl void ginit(); void vo_xt_window(Display*, Window, int, int); void vo_xt_drawable(Display*, Window, Drawable, GC, int, int); void vo_array(unsigned char*, int, int, int); typedef unsigned short Colorindex; extern void color(Colorindex); } int Image::vogl_init_flag = 0; #endif // ifdef USE_VOGL #endif // ifndef USE_GL #endif // ifndef NO_DISPLAY void Image::set_colour(unsigned int col) { #ifndef NO_DISPLAY #ifndef USE_GL // ie use VOGL #ifdef USE_VOGL ::color(col); #endif // ifdef USE_VOGL #else // FIXME: we are assuming we have an RGB (true colour?) display. // get r,g,b colour from table defined in Image.h ... ::RGBcolor( draw_colour_table[col % NUM_DRAW_COLOURS][0], draw_colour_table[col % NUM_DRAW_COLOURS][1], draw_colour_table[col % NUM_DRAW_COLOURS][2] ); #endif #endif // #ifndef NO_DISPLAY } Image::Image(unsigned int the_width, unsigned int the_height, frame_id_t the_frame_id, frame_time_t the_frame_time_in_ms, unsigned int the_bpp, unsigned char *the_data) { assert (the_width > 0); assert (the_height > 0); assert (the_bpp > 0); width = the_width; height = the_height; frame_id = the_frame_id; frame_time_in_ms = the_frame_time_in_ms; image_type = BASE; // change in derived classes! switch (bytes_per_pixel = the_bpp) { case 1: bpp_shift = 0; break; case 2: bpp_shift = 1; break; case 4: bpp_shift = 2; break; default: cerror << " Image::Image: Bad call to Image constructor (unknown #bpp)" << endl; exit(1); } #ifndef NO_DISPLAY window_title[0] = 0; #endif // #ifndef NO_DISPLAY // length (size) of an image line in memory line_length = width * bytes_per_pixel; if (the_data == NULL) { data = new unsigned char[height * line_length]; own_data = true; } else { data = the_data; own_data = false; } end_data = (data + height * line_length); ypoints = new unsigned int[height]; // make sure that ypoints can be used as the user wants (image_addressing_mode) // while respecting the image's layout in memory (image_storage_mode): if (image_storage_mode == image_addressing_mode) { for (unsigned int y = 0; y < height; y++) ypoints[y] = y * line_length; } else { // flip ypoints to hide the discrepancy from other methods for (unsigned int y = 0; y < height; y++) ypoints[y] = (height - (y+1)) * line_length; } #ifndef NO_DISPLAY #ifdef USE_GL glwin = NULLWIN; #else mydisplay = NULL; mywindow = NULLWIN; #endif #endif // #ifndef NO_DISPLAY } Image::~Image() { // de-allocate image memory if ((own_data) && (data != NULL)) delete [] data; delete [] ypoints; // close window if Image is currently displayed #ifndef NO_DISPLAY #ifdef USE_GL // GL interface if (glwin != NULLWIN) winclose(glwin); #else // X interface if (mydisplay != NULL) { XFreeGC(mydisplay, mygc); XDestroyWindow(mydisplay, mywindow); if (myimage->data != (char *) data) delete [] (myimage->data); myimage->data = None; XFree((char *) myimage); } #endif #endif // #ifndef NO_DISPLAY } inline void Image::clear(unsigned char fill_char) { memset(data, (int) fill_char, (end_data - data)); } // given a pointer to a pixel within the Image, return x and y ccordinates void Image::get_pixel_coords(unsigned char *pnt, unsigned int &x, unsigned int &y) const { assert(pnt > data); assert(pnt <= end_data); register unsigned int offset = pnt - data; x = (offset % line_length) >> bpp_shift; // this is dependent on image addressing and storage modes: if (image_addressing_mode == image_storage_mode) y = offset / line_length; else y = height - ((offset / line_length) + 1); } void Image::draw_in_image() { #ifndef NO_DISPLAY #ifdef USE_GL if (glwin != NULLWIN) { winset(glwin); if (image_type == GREY8) cmode(); else RGBmode(); gconfig(); // NB we have to do this after cmode / RGBmode } #else if (vogl_init_flag == 0) { if (mydisplay == NULL) // draw directly into image data { // use amb's extension to vogl // vo_array(data, width, height, bytes_per_pixel); ginit(); vogl_init_flag = 1; } else // draw into backing pixmap or into window { // vo_xt_drawable(mydisplay, mywindow, mywindow, mygc, width, // height); vogl_init_flag = 3; ginit(); } } #endif #endif // #ifndef NO_DISPLAY } #ifdef USE_GL inline #endif void Image::read_image() { #ifndef NO_DISPLAY #ifdef USE_GL winset(glwin); lrectread(0,0,width-1,height-1, (Int32 *) data); #else // do nothing if drawing directly into image data if (vogl_init_flag == 1) return; XImage *xi; if (vogl_init_flag == 2) xi = XGetImage(mydisplay, mypixmap, 0,0, width, height, AllPlanes, ZPixmap); else if ((vogl_init_flag == 3) || (vogl_init_flag == 0)) xi = XGetImage(mydisplay, mywindow, 0,0, width, height, AllPlanes, ZPixmap); else memcpy(data, xi->data, (end_data - data)); XDestroyImage(xi); flip_vertically(this); #endif #endif // #ifndef NO_DISPLAY } Image *Image::flip_vertically(Image *r) { if (r == NULL) r = copy_type(); unsigned char *tmp_store = new unsigned char[line_length]; int endloop; if ((height % 2) == 0) endloop = height / 2; else { endloop = (height - 1) / 2; memcpy(r->get_pixel(0,endloop), get_pixel(0,endloop), line_length); } int y1; int y2; // could use register here? but then, get_pixel is inline for (y1 = 0; y1 < endloop; y1++) { y2 = height - y1 - 1; memcpy(tmp_store, get_pixel(0,y2), line_length); memcpy(r->get_pixel(0,y2), get_pixel(0,y1), line_length); memcpy(r->get_pixel(0,y1), tmp_store, line_length); } delete [] tmp_store; return r; } Image *Image::extract_subimage(int xmin, int xmax, int ymin, int ymax, Image *r) { // new implementation for base class // should be reasonably efficient if (r == NULL) r = copy_type(xmax-xmin+1, ymax-ymin+1); for (int y = ymin; y <= ymax; y++) memcpy(r->get_pixel(0,y-ymin), get_pixel(xmin,y), r->line_length); return r; } void Image::paste_subimage(unsigned int xmin, unsigned int xmax, unsigned int ymin, unsigned int ymax, Image *subimg) { // sanity checks assert (xmin < xmax); assert (ymin < ymax); assert (subimg != NULL); assert (check_coords(xmin,ymin) == true); assert (check_coords(xmax,ymax) == true); assert (get_image_type() == subimg->get_image_type()); // if ((check_pixel(xmin,ymin) == NULL ) || // (check_pixel(xmax,ymax) == NULL) || // (xmin > xmax) || (ymin > ymax) || (get_image_type() != subimg->get_image_type())) // { // cerror << " bad parameters to paste_subimage because " // << (void*) check_pixel(xmin,ymin) << " == NULL or " // << (void*) check_pixel(xmax,ymax) << " == NULL or " // << xmin << " > " << xmax << " or " << ymin << " > " << ymax // << " or " << get_image_type() << " != " << subimg->get_image_type() // << endl; // exit(1); // } int nbytes = (xmax - xmin + 1) * bytes_per_pixel; for (unsigned int y = ymin; y <= ymax; y++) memcpy(get_pixel(xmin,y),subimg->get_pixel(0,y-ymin), nbytes); } Image *Image::get_subimage(unsigned int xmin, unsigned int xmax, unsigned int ymin, unsigned int ymax, Image *subimg) { if (subimg == NULL) subimg = copy_type(xmax - xmin + 1, ymax - ymin + 1); else assert (get_image_type() == subimg->get_image_type()); assert (xmin < xmax); assert (ymin < ymax); assert (check_coords(xmin,ymin) == true); assert (check_coords(xmax,ymax) == true); // if ((check_pixel(xmin,ymin) == NULL ) || // (check_pixel(xmax,ymax) == NULL) || // (xmin > xmax) || (ymin > ymax) || (image_type != subimg->get_image_type())) // { // cerror << " Image::get_subimage: bad parameters. " << endl; // exit(1); // } int nbytes = (xmax - xmin + 1) * bytes_per_pixel; for (unsigned int y = ymin; y <= ymax; y++) memcpy(subimg->get_pixel(0,y-ymin),get_pixel(xmin,y), nbytes); return subimg; } unsigned int Image::measure_contrast(void *p1, void *p2) { unsigned char *pix1 = (unsigned char*) p1; unsigned char *pix2 = (unsigned char*) p2; if ((pix1 == NULL) || (pix2 == NULL)) return 0; unsigned int val1 = (unsigned int) *pix1; unsigned int val2 = (unsigned int) *pix2; return abs(val1 - val2); } // the following VIRTUAL function implementations // do nothing but return error messages if not overridden/redefined RGB32Image *Image::rgb_read_image(RGB32Image *res) { cerror << " illegal call to Image::rgb_read_image " << endl << " res = " << res << endl; abort(); } Image *Image::fcombine(Image *image2, int (*func) (void*, void*), Image *r) { cerror << " illegal call to Image::fcombine. " << endl << " image2 = " << image2 << ", func = " << func << ", r = " << r << endl; abort(); } Image *Image::diff_stats(Image *img2, realno &mean, realno &variance, Image *r) { cerror << "illegal call to Image::diff_stats " << endl; mean = variance = 0; abort(); } Image *Image::image_blend(Image *image2, int a, int b, Image *r) { cerror << "illegal call to Image::image_blend " << endl; abort(); } Image *Image::map_intensities(PntGreyMap gmap, Image *r) { cerror << "illegal call to Image::gamma_correct " << endl; abort(); } // Image *Image::neighbour_order(unsigned int n, Image *r) // { // cerror << "illegal call to Image::neighbour_order " << endl; // abort(); // } Image *Image::fmed(Image *r) { cerror << "illegal call to Image::fmed " << endl; abort(); } Image *Image::half_blur( Image *r) { cerror << "illegal call to Image::half_blur " << endl; abort(); } Image *Image::ColourFilter(Image *res, ColourFilteringMethod method) { cerror << "illegal call to Image::ColourFilter " << endl; abort(); } Image *Image::ColourDistance(Image *res, ColourDistanceMethod) { cerror << "illegal call to Image::ColourDistance " << endl; abort(); } #ifdef HSV Image *Image::to_HSV32(Image *res) { cerror << "illegal call to Image::to_HSV32 " << endl; abort(); } #endif Image *Image::to_rgb(Image *r) { cerror << "illegal call to Image::to_rgb " << endl; abort(); } void Image::draw_rectangle(int xmin, int xmax, int ymin, int ymax, int val) { cerror << "illegal call to Image::draw_rectangle " << endl; abort(); } Image *Image::transform(int centre_x, int centre_y, realno angle, realno scale, Image *r) { cerror << "illegal call to Image::transform " << endl; abort(); } // base class implementation assumes noise is to be added // to each byte (i.e. each field is stored in a byte) #ifdef _SVID_SOURCE // the following uses SVID 48-bit random numbers... static int set_seed = 0; static int iset = 0; static realno gset; Image *Image::add_gaussian_noise(realno sd, realno &noise, Image *res) { if (res == NULL) res = copy_type(); if (set_seed == 0) { long rseed = time(NULL); srand48(rseed); set_seed = 1; } noise = 0; unsigned char *in_pnt = data; unsigned char *out_pnt = res->data; while (in_pnt < end_data) { realno fac, rsq, v1, v2, corrupt; if (iset == 0) { do { v1 = 2.0 * drand48() - 1.0; v2 = 2.0 * drand48() - 1.0; rsq = (v1 * v1) + (v2 * v2); } while ((rsq >= 1.0 )|| (rsq == 0.0)); fac = sqrt(-2.0 * log(rsq)/rsq); gset = v1 * fac; iset = 1; corrupt = v2 * fac; } else { iset = 0; corrupt = gset; } int icorrupt = (int) (sd * corrupt + 0.5); int ipix = (int) *in_pnt++; int new_ipix = ipix + icorrupt; if (new_ipix > 255) new_ipix = 255; if (new_ipix < 0) new_ipix = 0; *out_pnt++ = new_ipix; noise += (new_ipix - ipix) * (new_ipix - ipix); } return res; } #endif // ifdef _SVID_SOURCE // sum pixel values // assume each field is stored in a byte // all fields are set inline realno Image::sum_pixels() { realno total = 0; unsigned char *pix_field = data; while (pix_field < end_data) total += (realno) (*pix_field++); return total; } // sum [I(x,y) - zero]^2 realno Image::sum_square_pixels(int zero) { realno total = 0; unsigned char *pix_field = data; while (pix_field < end_data) { int ival = *pix_field++ - zero; total += (realno) (ival * ival); } return total; } void Image::median_img_array(Image **arr, int i) { cerror << "illegal call to Image::median_img_array " << endl; } Image *Image::plane_project(const realno m00, const realno m01, const realno m02, const realno m10, const realno m11, const realno m12, const realno m20, const realno m21, const realno m22, Image *res) { if (res == NULL) res = copy_type(); realno depth, back_x, back_y; int rw = res->get_width(); int rh = res->get_height(); for (int x = 0; x < rw; x++) for (int y = 0; y < rh; y++) { depth = m20 * x + m21 * y + m22; back_x = (m00 * x + m01 * y + m02) / depth; back_y = (m10 * x + m11 * y + m12) / depth; unsigned char *p1 = nearest_pixel(back_x, back_y); if (p1 != NULL) memcpy(res->get_pixel(x,y), p1, bytes_per_pixel); else memset(res->get_pixel(x,y), 0, bytes_per_pixel); } return res; } void Image::poly_delete(int i, int &nact, Edge *active) { int j; for (j=0; j =nact) return; nact--; memcpy(&active[j], &active[j+1], (nact-j)*sizeof(active[0])); } void Image::poly_insert(int i, int y, int nvert, int &nact, Edge *active) { int j; double dx; Point2 *p, *q; j = i x-p->x)/(q->y-p->y); active[nact].x = dx*(y+.5-p->y)+p->x; active[nact].i = i; nact++; } void Image::draw_filled_polygon(int n, Point2 *vertices) { // // Concave Polygon Scan Conversion // by Paul Heckbert // from "Graphics Gems", Academic Press, 1990 // // modified by amb pt = vertices; Edge *active = new Edge[n]; int win_x0 = 0, win_x1 = width-1; int win_y0 = 0, win_y1 = height-1; int y0, y1, i, j, xl, xr; int *ind = new int[n]; int k; if (n<=0) return; for (k=0; k 0) ? i-1 : n-1; // vertex previous to i if (pt[j].y <= y-.5) // old edge, remove from active list poly_delete(j, nact, active); else if (pt[j].y > y+.5) // new edge, add to active list poly_insert(j, y, n, nact, active); j = (i y+.5) // new edge, add to active list poly_insert(i, y, n, nact, active); } // NB: nact at this point is usually 2, sometimes 0 // if no edges, simply continue if (nact == 0) continue; // sort active edge list by active[j].x qsort(active, nact, sizeof(active[0]), &compare_active); // draw horizontal segments for scanline y for (j=0; j win_x1) xr = win_x1; if (xl<=xr) draw_horizontal(y, xl, xr); active[j].x += active[j].dx; // increment edge coords active[j+1].x += active[j+1].dx; } } delete [] ind; delete [] active; } void Image::draw_filled_circle(int x0, int y0, int r) { int limit = 0; int sigma; int x = 0; int y = r; int delta = (1 - r) << 1; while (y >= limit) { if (delta < 0) { sigma = ((delta + y) << 1) - 1; if (sigma > 0) { draw_horizontal(y0+y, x0-x, x0+x); draw_horizontal(y0-y, x0-x, x0+x); x++;y--; delta += (x - y + 1) << 1; } else { x++; delta += (x << 1) + 1; } } else if (delta > 0) { sigma = ((delta - x) << 1) - 1; if (sigma > 0) { draw_horizontal(y+y0, x0-x, x0+x); draw_horizontal(y0-y, x0-x, x0+x); y--; delta += 1 - (y << 1); } else { draw_horizontal(y+y0, x0-x, x0+x); draw_horizontal(y0-y, x0-x, x0+x); x++; y--; delta += (x - y + 1) << 1; } } else { draw_horizontal(y+y0, x0-x, x0+x); draw_horizontal(y0-y, x0-x, x0+x); x++;y--; delta += (x - y + 1) << 1; } } } void Image::draw_line(int x0, int y0, int x1, int y1) { if (DrawLineWidth > 1) { Point2 plist[4]; realno delta_x, delta_y, dlength; delta_x = (realno) (x1 - x0); delta_y = (realno) (y1 - y0); dlength = sqrt((delta_x * delta_x) + (delta_y * delta_y)); int r = (int) ((DrawLineWidth - 0.99) / 2); if (dlength > 0) { realno dx = delta_x / dlength; realno dy = delta_y / dlength; realno nx = dy; realno ny = -dx; plist[0].x = x0 + 0.5 + ((r + 0.5) * nx) - (dx * 0.5); plist[0].y = y0 + 0.5 + ((r + 0.5) * ny) - (dy * 0.5); plist[1].x = x0 + 0.5 - ((r + 0.5) * nx) - (dx * 0.5); plist[1].y = y0 + 0.5 - ((r + 0.5) * ny) - (dy * 0.5); plist[2].x = x1 + 0.5 - ((r + 0.5) * nx) + (dx * 0.5); plist[2].y = y1 + 0.5 - ((r + 0.5) * ny) + (dy * 0.5); plist[3].x = x1 + 0.5 + ((r + 0.5) * nx) + (dx * 0.5); plist[3].y = y1 + 0.5 + ((r + 0.5) * ny) + (dy * 0.5); draw_filled_polygon(4, plist); } draw_filled_circle(x0,y0,r); draw_filled_circle(x1,y1,r); return; } int d; int x; int y; int ax; int ay; int sx; int sy; int dx; int dy; dx = x1-x0; ax = ABS(dx)<<1; sx = SGN(dx); dy = y1-y0; ay = ABS(dy)<<1; sy = SGN(dy); if ((dx == 0) && (dy == 0)) return; x = x0; y = y0; if (ax>ay) { // x dominant d = ay-(ax>>1); for (;;) { plot_pixel(x,y); if (x==x1) return; if (d>=0) { y += sy; d -= ax; } x += sx; d += ay; } } else { // y dominant d = ax-(ay>>1); for (;;) { plot_pixel(x,y); if (y==y1) return; if (d>=0) { x += sx; d -= ay; } y += sy; d += ax; } } } void Image::draw_circle(int x0, int y0, int r) { static const realno cth = cos(M_PI_2 / ((realno) CIRCLE_PRECISION)); static const realno sth = sin(M_PI_2 / ((realno) CIRCLE_PRECISION)); int i; if (DrawLineWidth > 1) { Point2 *pnt_data = new Point2[CIRCLE_PRECISION*8+2]; Point2 *big_circle = pnt_data; Point2 *small_circle = &pnt_data[CIRCLE_PRECISION*4+1]; realno x_big = 0; realno y_big = r + 0.5; realno x_small = 0; realno y_small = (r - DrawLineWidth); Point2 origin(x0+0.5, y0+0.5); for (i = 0; i < CIRCLE_PRECISION; i++) { big_circle[i] = Point2(x_big, y_big); big_circle[i+CIRCLE_PRECISION] = Point2(y_big, -x_big); big_circle[i+2*CIRCLE_PRECISION] = Point2(-x_big, -y_big); big_circle[i+3*CIRCLE_PRECISION] = Point2(-y_big, x_big); small_circle[i] = Point2(x_small, y_small); small_circle[i+CIRCLE_PRECISION] = Point2(y_small, -x_small); small_circle[i+2*CIRCLE_PRECISION] = Point2(-x_small, -y_small); small_circle[i+3*CIRCLE_PRECISION] = Point2(-y_small, x_small); realno new_x_big = cth * x_big + sth * y_big; realno new_y_big = -sth * x_big + cth * y_big; x_big = new_x_big; y_big = new_y_big; realno new_x_small = cth * x_small + sth * y_small; realno new_y_small = -sth * x_small + cth * y_small; x_small = new_x_small; y_small = new_y_small; } big_circle[4*CIRCLE_PRECISION] = big_circle[0]; small_circle[4*CIRCLE_PRECISION] = small_circle[0]; for (i = 0; i < (8 * CIRCLE_PRECISION + 2); i++) pnt_data[i] = pnt_data[i] + origin; draw_filled_polygon(8*CIRCLE_PRECISION+2, pnt_data); delete [] pnt_data; return; } register int x = 0; register int y = r; register int delta = ( 1 - r) << 1; int limit = 0; int sigma; while (y >= limit) { plot_pixel(x0-x,y0+y); plot_pixel(x0+x,y0+y); plot_pixel(x0-x,y0-y); plot_pixel(x0+x,y0-y); if (delta < 0) { sigma = ((delta + y) << 1) - 1; if (sigma > 0) { x++;y--; delta += (x - y + 1) << 1; } else { x++; delta += (x << 1) + 1; } } else if (delta > 0) { sigma = ((delta - x) << 1) - 1; if (sigma > 0) { y--; delta += 1 - (y << 1); } else { x++; y--; delta += (x - y + 1) << 1; } } else { x++; y--; delta += (x - y + 1) << 1; } } } } // namespace ReadingPeopleTracker