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