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 <fvclassifiers/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 <fvmodels/scanlines/scanlinemodel.h> 00031 #include <fvmodels/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 <fvclassifiers/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 * @param color color to look for 00057 */ 00058 SimpleColorClassifier::SimpleColorClassifier(ScanlineModel *scanline_model, 00059 ColorModel *color_model, 00060 unsigned int min_num_points, 00061 unsigned int box_extent, 00062 bool upward, 00063 unsigned int neighbourhood_min_match, 00064 unsigned int grow_by, 00065 color_t color) 00066 : Classifier("SimpleColorClassifier"), 00067 color(color) 00068 { 00069 if (scanline_model == NULL) { 00070 throw fawkes::NullPointerException("SimpleColorClassifier: scanline_model " 00071 "may not be NULL"); 00072 } 00073 if (color_model == NULL) { 00074 throw fawkes::NullPointerException("SimpleColorClassifier: color_model " 00075 "may not be NULL"); 00076 } 00077 00078 modified = false; 00079 this->scanline_model = scanline_model; 00080 this->color_model = color_model; 00081 this->min_num_points = min_num_points; 00082 this->box_extent = box_extent; 00083 this->upward = upward; 00084 this->grow_by = grow_by; 00085 this->neighbourhood_min_match = neighbourhood_min_match; 00086 } 00087 00088 00089 unsigned int 00090 SimpleColorClassifier::consider_neighbourhood( unsigned int x, unsigned int y , color_t what) 00091 { 00092 color_t c; 00093 unsigned int num_what = 0; 00094 00095 unsigned char yp = 0, up = 0, vp = 0; 00096 int start_x = -2, start_y = -2; 00097 int end_x = 2, end_y = 2; 00098 00099 if (x < (unsigned int)abs(start_x)) { 00100 start_x = 0; 00101 } 00102 if (y < (unsigned int)abs(start_y)) { 00103 start_y = 0; 00104 } 00105 00106 if (x > _width - end_x) { 00107 end_x = 0; 00108 } 00109 if (y == _height - end_y) { 00110 end_y = 0; 00111 } 00112 00113 for (int dx = start_x; dx <= end_x ; dx += 2) { 00114 for (int dy = start_y; dy <= end_y; ++dy) { 00115 if ((dx == 0) && (dy == 0)) { 00116 continue; 00117 } 00118 00119 // cout << "x=" << x << " dx=" << dx << " +=" << x+dx 00120 // << " y=" << y << " dy=" << dy << " +2=" << y+dy << endl; 00121 00122 YUV422_PLANAR_YUV(_src, _width, _height, x+dx, y+dy, yp, up, vp); 00123 c = color_model->determine(yp, up, vp); 00124 00125 if (c == what) { 00126 ++num_what; 00127 } 00128 } 00129 } 00130 00131 return num_what; 00132 } 00133 00134 std::list< ROI > * 00135 SimpleColorClassifier::classify() 00136 { 00137 00138 if (_src == NULL) { 00139 //cout << "SimpleColorClassifier: ERROR, src buffer not set. NOT classifying." << endl; 00140 return new std::list< ROI >; 00141 } 00142 00143 00144 std::list< ROI > *rv = new std::list< ROI >(); 00145 std::list< ROI >::iterator roi_it, roi_it2; 00146 color_t c; 00147 00148 unsigned int x = 0, y = 0; 00149 unsigned char yp = 0, up = 0, vp = 0; 00150 unsigned int num_what = 0; 00151 00152 ROI r; 00153 00154 scanline_model->reset(); 00155 while (! scanline_model->finished()) { 00156 00157 x = (*scanline_model)->x; 00158 y = (*scanline_model)->y; 00159 00160 YUV422_PLANAR_YUV(_src, _width, _height, x, y, yp, up, vp); 00161 00162 c = color_model->determine(yp,up, vp); 00163 00164 if (color == c) { 00165 // Yeah, found a ball, make it big and name it a ROI 00166 // Note that this may throw out a couple of ROIs for just one ball, 00167 // as the name suggests this one is really ABSOLUTELY simple and not 00168 // useful for anything else than quick testing 00169 00170 if (neighbourhood_min_match) { 00171 num_what = 00172 consider_neighbourhood((*scanline_model)->x, (*scanline_model)->y, c); 00173 } 00174 if (num_what >= neighbourhood_min_match) { 00175 bool ok = false; 00176 00177 for (roi_it = rv->begin(); roi_it != rv->end(); ++roi_it) { 00178 if ( (*roi_it).contains(x, y) ) { 00179 // everything is fine, this point is already in another ROI 00180 ok = true; 00181 break; 00182 } 00183 } 00184 if (! ok) { 00185 for (roi_it = rv->begin(); roi_it != rv->end(); ++roi_it) { 00186 if ( (*roi_it).neighbours(x, y, scanline_model->get_margin()) ) { 00187 // ROI is neighbour of this point, extend region 00188 (*roi_it).extend(x, y); 00189 ok = true; 00190 break; 00191 } 00192 } 00193 } 00194 00195 if (! ok) { 00196 if ( upward ) { 00197 if ( x < box_extent ) { 00198 x = 0; 00199 } else { 00200 x -= box_extent; 00201 } 00202 if ( y < box_extent ) { 00203 y = 0; 00204 } else { 00205 y -= box_extent; 00206 } 00207 } 00208 r.start.x = x; 00209 r.start.y = y; 00210 00211 unsigned int to_x = (*scanline_model)->x + box_extent; 00212 unsigned int to_y = (*scanline_model)->y + box_extent; 00213 if (to_x > _width) to_x = _width; 00214 if (to_y > _height) to_y = _height; 00215 r.width = to_x - r.start.x; 00216 r.height = to_y - r.start.y; 00217 r.hint = c; 00218 r.color = c; 00219 00220 r.line_step = _width; 00221 r.pixel_step = 1; 00222 00223 r.image_width = _width; 00224 r.image_height = _height; 00225 00226 if ( (r.start.x + r.width) > _width ) { 00227 r.width -= (r.start.x + r.width) - _width; 00228 } 00229 if ( (r.start.y + r.height) > _height ) { 00230 r.height -= (r.start.y + r.height) - _height; 00231 } 00232 00233 rv->push_back( r ); 00234 } 00235 } // End if enough neighbours 00236 } // end if is orange 00237 00238 ++(*scanline_model); 00239 } 00240 00241 // Grow regions 00242 if (grow_by > 0) { 00243 for (roi_it = rv->begin(); roi_it != rv->end(); ++roi_it) { 00244 (*roi_it).grow( grow_by ); 00245 } 00246 } 00247 00248 // Merge neighbouring regions 00249 for (roi_it = rv->begin(); roi_it != rv->end(); ++roi_it) { 00250 roi_it2 = roi_it; 00251 ++roi_it2; 00252 00253 while ( roi_it2 != rv->end() ) { 00254 if ((roi_it != roi_it2) && 00255 roi_it->neighbours(&(*roi_it2), scanline_model->get_margin())) 00256 { 00257 *roi_it += *roi_it2; 00258 rv->erase(roi_it2); 00259 roi_it2 = rv->begin(); //restart 00260 } else { 00261 ++roi_it2; 00262 } 00263 } 00264 } 00265 00266 // Throw away all ROIs that have not enough classified points 00267 for (roi_it = rv->begin(); roi_it != rv->end(); ++roi_it) { 00268 while ( (roi_it != rv->end()) && 00269 ((*roi_it).num_hint_points < min_num_points )) 00270 { 00271 roi_it = rv->erase( roi_it ); 00272 } 00273 } 00274 00275 // sort ROIs by number of hint points, descending (and thus call reverse) 00276 rv->sort(); 00277 rv->reverse(); 00278 00279 return rv; 00280 } 00281 00282 00283 /** Get mass point of primary color. 00284 * @param roi ROI to consider 00285 * @param massPoint contains mass point upon return 00286 */ 00287 void 00288 SimpleColorClassifier::get_mass_point_of_color( ROI *roi, 00289 fawkes::point_t *massPoint ) 00290 { 00291 unsigned int nrOfOrangePixels; 00292 nrOfOrangePixels = 0; 00293 massPoint->x = 0; 00294 massPoint->y = 0; 00295 00296 // for accessing ROI pixels 00297 register unsigned int h = 0; 00298 register unsigned int w = 0; 00299 // planes 00300 register unsigned char *yp = _src + (roi->start.y * roi->line_step) + (roi->start.x * roi->pixel_step); 00301 register unsigned char *up = YUV422_PLANAR_U_PLANE(_src, roi->image_width, roi->image_height) 00302 + ((roi->start.y * roi->line_step) / 2 + (roi->start.x * roi->pixel_step) / 2) ; 00303 register unsigned char *vp = YUV422_PLANAR_V_PLANE(_src, roi->image_width, roi->image_height) 00304 + ((roi->start.y * roi->line_step) / 2 + (roi->start.x * roi->pixel_step) / 2); 00305 // line starts 00306 unsigned char *lyp = yp; 00307 unsigned char *lup = up; 00308 unsigned char *lvp = vp; 00309 00310 color_t dcolor; 00311 00312 // consider each ROI pixel 00313 for (h = 0; h < roi->height; ++h) { 00314 for (w = 0; w < roi->width; w += 2) { 00315 // classify its color 00316 dcolor = color_model->determine(*yp++, *up++, *vp++); 00317 yp++; 00318 // ball pixel? 00319 if (color == dcolor) { 00320 // take into account its coordinates 00321 massPoint->x += w; 00322 massPoint->y += h; 00323 nrOfOrangePixels++; 00324 } 00325 } 00326 // next line 00327 lyp += roi->line_step; 00328 lup += roi->line_step / 2; 00329 lvp += roi->line_step / 2; 00330 yp = lyp; 00331 up = lup; 00332 vp = lvp; 00333 } 00334 00335 // to obtain mass point, divide by number of pixels that were added up 00336 massPoint->x = (unsigned int) (float(massPoint->x) / float(nrOfOrangePixels)); 00337 massPoint->y = (unsigned int) (float(massPoint->y) / float(nrOfOrangePixels)); 00338 00339 /* shift mass point 00340 such that it is relative to image 00341 (not relative to ROI) */ 00342 massPoint->x += roi->start.x; 00343 massPoint->y += roi->start.y; 00344 } 00345 00346 } // end namespace firevision