simple.cpp

00001
00002 /***************************************************************************
00003  *  simple.cpp - Implementation of a SimpleColorClassifier
00004  *
00005  *  Created: Thu May 16 2005
00006  *  Copyright  2005-2007  Tim Niemueller [www.niemueller.de]
00007  *
00008  ****************************************************************************/
00009
00010 /*  This program is free software; you can redistribute it and/or modify
00011  *  it under the terms of the GNU General Public License as published by
00012  *  the Free Software Foundation; either version 2 of the License, or
00013  *  (at your option) any later version. A runtime exception applies to
00014  *  this software (see LICENSE.GPL_WRE file mentioned below for details).
00015  *
00016  *  This program is distributed in the hope that it will be useful,
00017  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00018  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00019  *  GNU Library General Public License for more details.
00020  *
00021  *  Read the full text in the LICENSE.GPL_WRE file in the doc directory.
00022  */
00023
00024 #include <classifiers/simple.h>
00025
00026 #include <fvutils/color/colorspaces.h>
00027 #include <fvutils/color/yuv.h>
00028 #include <fvutils/color/color_object_map.h>
00029
00030 #include <models/scanlines/scanlinemodel.h>
00031 #include <models/color/colormodel.h>
00032
00033 #include <core/exceptions/software.h>
00034
00035 #include <cstdlib>
00036 
00037 /** @class SimpleColorClassifier <classifiers/simple.h>
00038  * Simple classifier.
00039  */
00040 
00041 /** Constructor.
00042  * @param scanline_model scanline model
00043  * @param color_model color model
00044  * @param min_num_points minimum number of points in ROI to be considered
00045  * @param box_extent basic extent of a new ROI
00046  * @param upward set to true if you have an upward scanline model, this means that
00047  * points are traversed from the bottom to the top. In this case the ROIs are initially
00048  * extended towards the top instead of the bottom.
00049  * @param neighbourhood_min_match minimum number of object pixels to grow neighbourhood
00050  * @param grow_by grow region by that many pixels
00051  */
00052 SimpleColorClassifier::SimpleColorClassifier(ScanlineModel *scanline_model,
00053                                              ColorModel *color_model,
00054                                              unsigned int min_num_points,
00055                                              unsigned int box_extent,
00056                                              bool upward,
00057                                              unsigned int neighbourhood_min_match,
00058                                              unsigned int grow_by)
00059 : Classifier("SimpleColorClassifier")
00060 {
00061   if (!scanline_model)
00062     throw fawkes::NullPointerException("SimpleColorClassifier: scanline_model may not be NULL");
00063   if (!color_model)
00064     throw fawkes::NullPointerException("SimpleColorClassifier: color_model may not be NULL");
00065
00066   modified = false;
00067   this->scanline_model = scanline_model;
00068   this->color_model = color_model;
00069   this->min_num_points = min_num_points;
00070   this->box_extent = box_extent;
00071   this->upward = upward;
00072   this->grow_by = grow_by;
00073   this->neighbourhood_min_match = neighbourhood_min_match;
00074   set_hint(H_BALL);
00075 }
00076
00077 
00078 /** Sets the object of interest (hint_t)
00079  * This function clears the current list of objects of interests
00080  * @param hint Object of interest
00081  */
00082 void
00083 SimpleColorClassifier::set_hint (hint_t hint)
00084 {
00085   colors_of_interest.clear();
00086   colors_of_interest.push_back(ColorObjectMap::get_instance().get(hint));
00087 }
00088 
00089 /** Adds another object of interest (hint_t)
00090  * @param hint Object of interest
00091  */
00092 void
00093 SimpleColorClassifier::add_hint (hint_t hint)
00094 {
00095   colors_of_interest.push_back(ColorObjectMap::get_instance().get(hint));
00096   colors_of_interest.unique();
00097 }
00098
00099
00100 unsigned int
00101 SimpleColorClassifier::consider_neighbourhood( unsigned int x, unsigned int y , color_t what)
00102 {
00103   color_t c;
00104   unsigned int num_what = 0;
00105
00106   unsigned char yp = 0, up = 0, vp = 0;
00107   int start_x = -2, start_y = -2;
00108   int end_x = 2, end_y = 2;
00109
00110   if (x < (unsigned int)abs(start_x)) {
00111     start_x = 0;
00112   }
00113   if (y < (unsigned int)abs(start_y)) {
00114     start_y = 0;
00115   }
00116
00117   if (x > _width - end_x) {
00118     end_x = 0;
00119   }
00120   if (y == _height - end_y) {
00121     end_y = 0;
00122   }
00123
00124   for (int dx = start_x; dx <= end_x ; dx += 2) {
00125     for (int dy = start_y; dy <= end_y; ++dy) {
00126       if ((dx == 0) && (dy == 0)) {
00127         continue;
00128       }
00129
00130       //      cout << "x=" << x << "  dx=" << dx << "  +=" << x+dx
00131       //   << "  y=" << y << "  dy=" << dy << "  +2=" << y+dy << endl;
00132
00133       YUV422_PLANAR_YUV(_src, _width, _height, x+dx, y+dy, yp, up, vp);
00134       c = color_model->determine(yp, up, vp);
00135
00136       if (c == what) {
00137         ++num_what;
00138       }
00139     }
00140   }
00141
00142   return num_what;
00143 }
00144
00145 std::list< ROI > *
00146 SimpleColorClassifier::classify()
00147 {
00148
00149   if (_src == NULL) {
00150     //cout << "SimpleColorClassifier: ERROR, src buffer not set. NOT classifying." << endl;
00151     return new std::list< ROI >;
00152   }
00153
00154
00155   std::list< ROI > *rv;
00156   std::list< ROI >::iterator roi_it, roi_it2;
00157   color_t c;
00158   std::map<color_t, std::list< ROI > > to_be_considered;
00159   std::map<color_t, std::list< ROI > >::iterator tbc_it;
00160
00161   for (std::list<color_t>::const_iterator it = colors_of_interest.begin(); it != colors_of_interest.end(); ++it) {
00162     to_be_considered[*it];
00163   }
00164
00165   unsigned int  x = 0, y = 0;
00166   unsigned char yp = 0, up = 0, vp = 0;
00167   unsigned int num_what = 0;
00168
00169   ROI r;
00170
00171   scanline_model->reset();
00172   while (! scanline_model->finished()) {
00173
00174     x = (*scanline_model)->x;
00175     y = (*scanline_model)->y;
00176
00177     YUV422_PLANAR_YUV(_src, _width, _height, x, y, yp, up, vp);
00178
00179     /*
00180      cout << "SimpleColorClassifier: Checking at ("
00181                                                   << x
00182                                                   << ","
00183                                                   << y
00184                                                   << ") "
00185                                                     << " with color Y=" << (int)macro_pixel[y_pos]
00186                                                     << "  U=" << (int)macro_pixel[0]
00187                                                     << "  V=" << (int)macro_pixel[2]
00188                                                     << endl;
00189                                                     */
00190
00191     c = color_model->determine(yp,up, vp);
00192
00193     if ((tbc_it = to_be_considered.find(c)) != to_be_considered.end()) {
00194       rv = &tbc_it->second;
00195       // Yeah, found a ball, make it big and name it a ROI
00196       // Note that this may throw out a couple of ROIs for just one ball,
00197       // as the name suggests this one is really ABSOLUTELY simple and not
00198       // useful for anything else than quick testing
00199
00200       if (neighbourhood_min_match) {
00201         num_what = consider_neighbourhood((*scanline_model)->x, (*scanline_model)->y, c);
00202       }
00203       if (num_what >= neighbourhood_min_match) {
00204
00205         bool ok = false;
00206
00207         for (roi_it = rv->begin(); roi_it != rv->end(); ++roi_it) {
00208           if ( (*roi_it).contains(x, y) ) {
00209             // everything is fine, this point is already in another ROI
00210             ok = true;
00211             break;
00212           }
00213         }
00214         if (! ok) {
00215           for (roi_it = rv->begin(); roi_it != rv->end(); ++roi_it) {
00216             if ( (*roi_it).neighbours(x, y, scanline_model->get_margin()) ) {
00217               // ROI is neighbour of this point, extend region
00218               (*roi_it).extend(x, y);
00219               ok = true;
00220               break;
00221             }
00222           }
00223         }
00224
00225         if (! ok) {
00226           if ( upward ) {
00227             if ( x < box_extent ) {
00228               x = 0;
00229             } else {
00230               x -= box_extent;
00231             }
00232             if ( y < box_extent ) {
00233               y = 0;
00234             } else {
00235               y -= box_extent;
00236             }
00237           }
00238           r.start.x = x;
00239           r.start.y = y;
00240
00241           unsigned int to_x = (*scanline_model)->x + box_extent;
00242           unsigned int to_y = (*scanline_model)->y + box_extent;
00243           if (to_x > _width)  to_x = _width;
00244           if (to_y > _height) to_y = _height;
00245           r.width = to_x - r.start.x;
00246           r.height = to_y - r.start.y;
00247           r.hint = ColorObjectMap::get_instance().get(c);
00248
00249           r.line_step = _width;
00250           r.pixel_step = 1;
00251
00252           r.image_width  = _width;
00253           r.image_height = _height;
00254
00255           if ( (r.start.x + r.width) > _width ) {
00256             r.width -= (r.start.x + r.width) - _width;
00257           }
00258           if ( (r.start.y + r.height) > _height ) {
00259             r.height -= (r.start.y + r.height) - _height;
00260           }
00261
00262           rv->push_back( r );
00263         }
00264       } // End if enough neighbours
00265     } // end if is orange
00266
00267     ++(*scanline_model);
00268   }
00269
00270   // Grow regions
00271   if (grow_by > 0) {
00272     for (tbc_it = to_be_considered.begin(); tbc_it != to_be_considered.end(); ++tbc_it) {
00273       rv = &tbc_it->second;
00274
00275       for (roi_it = rv->begin(); roi_it != rv->end(); ++roi_it) {
00276         (*roi_it).grow( grow_by );
00277       }
00278     }
00279   }
00280
00281   //bool restart = false;
00282   // Merge neighbouring regions
00283   for (tbc_it = to_be_considered.begin(); tbc_it != to_be_considered.end(); ++tbc_it) {
00284     rv = &tbc_it->second;
00285
00286     for (roi_it = rv->begin(); roi_it != rv->end(); ++roi_it) {
00287       roi_it2 = roi_it;//rv->begin();
00288       ++roi_it2;
00289
00290       while ( roi_it2 != rv->end() ) {
00291         if ((roi_it != roi_it2) && roi_it->neighbours(&(*roi_it2), scanline_model->get_margin())) {
00292           *roi_it += *roi_it2;
00293           rv->erase(roi_it2);
00294           roi_it2 = rv->begin(); //restart
00295         } else {
00296           ++roi_it2;
00297         }
00298       }
00299     }
00300   }
00301
00302   std::list< ROI > *result = new std::list< ROI >;
00303   for (tbc_it = to_be_considered.begin(); tbc_it != to_be_considered.end(); ++tbc_it) {
00304     rv = &tbc_it->second;
00305
00306     // Throw away all ROIs that have not enough classified points
00307     for (roi_it = rv->begin(); roi_it != rv->end(); ++roi_it) {
00308       while ( (roi_it != rv->end()) &&
00309              ((*roi_it).num_hint_points < min_num_points )) {
00310                roi_it = rv->erase( roi_it );
00311              }
00312     }
00313
00314     // sort ROIs by number of hint points, descending (and thus call reverse)
00315     rv->sort();
00316     rv->reverse();
00317     result->merge(*rv);
00318   }
00319
00320   return result;
00321 }
00322
00323 
00324 /** Get mass point of ball.
00325  * @param roi ROI to consider
00326  * @param massPoint contains mass point upon return
00327  */
00328 void
00329 SimpleColorClassifier::get_mass_point_of_ball( ROI *roi, fawkes::point_t *massPoint )
00330 {
00331   unsigned int nrOfOrangePixels;
00332   nrOfOrangePixels = 0;
00333   massPoint->x     = 0;
00334   massPoint->y     = 0;
00335
00336   // for accessing ROI pixels
00337   register unsigned int h = 0;
00338   register unsigned int w = 0;
00339   // planes
00340   register unsigned char *yp   = _src + (roi->start.y * roi->line_step) + (roi->start.x * roi->pixel_step);
00341   register unsigned char *up   = YUV422_PLANAR_U_PLANE(_src, roi->image_width, roi->image_height)
00342     + ((roi->start.y * roi->line_step) / 2 + (roi->start.x * roi->pixel_step) / 2) ;
00343   register unsigned char *vp   = YUV422_PLANAR_V_PLANE(_src, roi->image_width, roi->image_height)
00344     + ((roi->start.y * roi->line_step) / 2 + (roi->start.x * roi->pixel_step) / 2);
00345   // line starts
00346   unsigned char *lyp  = yp;
00347   unsigned char *lup  = up;
00348   unsigned char *lvp  = vp;
00349
00350   color_t color;
00351
00352   // consider each ROI pixel
00353   for (h = 0; h < roi->height; ++h) {
00354     for (w = 0; w < roi->width; w += 2) {
00355       // classify its color
00356       color = color_model->determine(*yp++, *up++, *vp++);
00357       *yp++;
00358       // ball pixel?
00359       if (color == ColorObjectMap::get_instance().get(roi->hint)) {
00360         // take into account its coordinates
00361         massPoint->x += w;
00362         massPoint->y += h;
00363         nrOfOrangePixels++;
00364       }
00365     }
00366     // next line
00367     lyp  += roi->line_step;
00368     lup  += roi->line_step / 2;
00369     lvp  += roi->line_step / 2;
00370     yp    = lyp;
00371     up    = lup;
00372     vp    = lvp;
00373   }
00374
00375   // to obtain mass point, divide by number of pixels that were added up
00376   massPoint->x = (unsigned int) (float(massPoint->x) / float(nrOfOrangePixels));
00377   massPoint->y = (unsigned int) (float(massPoint->y) / float(nrOfOrangePixels));
00378
00379   /* shift mass point
00380    such that it is relative to image
00381    (not relative to ROI) */
00382   massPoint->x += roi->start.x;
00383   massPoint->y += roi->start.y;
00384 }