Fawkes API  Fawkes Development Version
simple.cpp
1 
2 /***************************************************************************
3  * simple.cpp - Implementation of a SimpleColorClassifier
4  *
5  * Created: Thu May 16 00:00:00 2005
6  * Copyright 2005-2007 Tim Niemueller [www.niemueller.de]
7  *
8  ****************************************************************************/
9 
10 /* This program is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation; either version 2 of the License, or
13  * (at your option) any later version. A runtime exception applies to
14  * this software (see LICENSE.GPL_WRE file mentioned below for details).
15  *
16  * This program is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19  * GNU Library General Public License for more details.
20  *
21  * Read the full text in the LICENSE.GPL_WRE file in the doc directory.
22  */
23 
24 #include <fvclassifiers/simple.h>
25 
26 #include <fvutils/color/colorspaces.h>
27 #include <fvutils/color/yuv.h>
28 #include <fvutils/color/color_object_map.h>
29 
30 #include <fvmodels/scanlines/scanlinemodel.h>
31 #include <fvmodels/color/colormodel.h>
32 
33 #include <core/exceptions/software.h>
34 
35 #include <cstdlib>
36 
37 namespace firevision {
38 #if 0 /* just to make Emacs auto-indent happy */
39 }
40 #endif
41 
42 /** @class SimpleColorClassifier <fvclassifiers/simple.h>
43  * Simple classifier.
44  */
45 
46 /** Constructor.
47  * @param scanline_model scanline model
48  * @param color_model color model
49  * @param min_num_points minimum number of points in ROI to be considered
50  * @param box_extent basic extent of a new ROI
51  * @param upward set to true if you have an upward scanline model, this means that
52  * points are traversed from the bottom to the top. In this case the ROIs are initially
53  * extended towards the top instead of the bottom.
54  * @param neighbourhood_min_match minimum number of object pixels to grow neighbourhood
55  * @param grow_by grow region by that many pixels
56  * @param color color to look for
57  */
59  ColorModel *color_model,
60  unsigned int min_num_points,
61  unsigned int box_extent,
62  bool upward,
63  unsigned int neighbourhood_min_match,
64  unsigned int grow_by,
65  color_t color)
66  : Classifier("SimpleColorClassifier"),
67  color(color)
68 {
69  if (scanline_model == NULL) {
70  throw fawkes::NullPointerException("SimpleColorClassifier: scanline_model "
71  "may not be NULL");
72  }
73  if (color_model == NULL) {
74  throw fawkes::NullPointerException("SimpleColorClassifier: color_model "
75  "may not be NULL");
76  }
77 
78  modified = false;
79  this->scanline_model = scanline_model;
80  this->color_model = color_model;
81  this->min_num_points = min_num_points;
82  this->box_extent = box_extent;
83  this->upward = upward;
84  this->grow_by = grow_by;
85  this->neighbourhood_min_match = neighbourhood_min_match;
86 }
87 
88 
89 unsigned int
90 SimpleColorClassifier::consider_neighbourhood( unsigned int x, unsigned int y , color_t what)
91 {
92  color_t c;
93  unsigned int num_what = 0;
94 
95  unsigned char yp = 0, up = 0, vp = 0;
96  int start_x = -2, start_y = -2;
97  int end_x = 2, end_y = 2;
98 
99  if (x < (unsigned int)abs(start_x)) {
100  start_x = 0;
101  }
102  if (y < (unsigned int)abs(start_y)) {
103  start_y = 0;
104  }
105 
106  if (x > _width - end_x) {
107  end_x = 0;
108  }
109  if (y == _height - end_y) {
110  end_y = 0;
111  }
112 
113  for (int dx = start_x; dx <= end_x ; dx += 2) {
114  for (int dy = start_y; dy <= end_y; ++dy) {
115  if ((dx == 0) && (dy == 0)) {
116  continue;
117  }
118 
119  // cout << "x=" << x << " dx=" << dx << " +=" << x+dx
120  // << " y=" << y << " dy=" << dy << " +2=" << y+dy << endl;
121 
122  YUV422_PLANAR_YUV(_src, _width, _height, x+dx, y+dy, yp, up, vp);
123  c = color_model->determine(yp, up, vp);
124 
125  if (c == what) {
126  ++num_what;
127  }
128  }
129  }
130 
131  return num_what;
132 }
133 
134 std::list< ROI > *
136 {
137 
138  if (_src == NULL) {
139  //cout << "SimpleColorClassifier: ERROR, src buffer not set. NOT classifying." << endl;
140  return new std::list< ROI >;
141  }
142 
143 
144  std::list< ROI > *rv = new std::list< ROI >();
145  std::list< ROI >::iterator roi_it, roi_it2;
146  color_t c;
147 
148  unsigned int x = 0, y = 0;
149  unsigned char yp = 0, up = 0, vp = 0;
150  unsigned int num_what = 0;
151 
152  ROI r;
153 
154  scanline_model->reset();
155  while (! scanline_model->finished()) {
156 
157  x = (*scanline_model)->x;
158  y = (*scanline_model)->y;
159 
160  YUV422_PLANAR_YUV(_src, _width, _height, x, y, yp, up, vp);
161 
162  c = color_model->determine(yp,up, vp);
163 
164  if (color == c) {
165  // Yeah, found a ball, make it big and name it a ROI
166  // Note that this may throw out a couple of ROIs for just one ball,
167  // as the name suggests this one is really ABSOLUTELY simple and not
168  // useful for anything else than quick testing
169 
170  if (neighbourhood_min_match) {
171  num_what =
172  consider_neighbourhood((*scanline_model)->x, (*scanline_model)->y, c);
173  }
174  if (num_what >= neighbourhood_min_match) {
175  bool ok = false;
176 
177  for (roi_it = rv->begin(); roi_it != rv->end(); ++roi_it) {
178  if ( (*roi_it).contains(x, y) ) {
179  // everything is fine, this point is already in another ROI
180  ok = true;
181  break;
182  }
183  }
184  if (! ok) {
185  for (roi_it = rv->begin(); roi_it != rv->end(); ++roi_it) {
186  if ( (*roi_it).neighbours(x, y, scanline_model->get_margin()) ) {
187  // ROI is neighbour of this point, extend region
188  (*roi_it).extend(x, y);
189  ok = true;
190  break;
191  }
192  }
193  }
194 
195  if (! ok) {
196  if ( upward ) {
197  if ( x < box_extent ) {
198  x = 0;
199  } else {
200  x -= box_extent;
201  }
202  if ( y < box_extent ) {
203  y = 0;
204  } else {
205  y -= box_extent;
206  }
207  }
208  r.start.x = x;
209  r.start.y = y;
210 
211  unsigned int to_x = (*scanline_model)->x + box_extent;
212  unsigned int to_y = (*scanline_model)->y + box_extent;
213  if (to_x > _width) to_x = _width;
214  if (to_y > _height) to_y = _height;
215  r.width = to_x - r.start.x;
216  r.height = to_y - r.start.y;
217  r.hint = c;
218  r.color = c;
219 
220  r.line_step = _width;
221  r.pixel_step = 1;
222 
223  r.image_width = _width;
224  r.image_height = _height;
225 
226  if ( (r.start.x + r.width) > _width ) {
227  r.width -= (r.start.x + r.width) - _width;
228  }
229  if ( (r.start.y + r.height) > _height ) {
230  r.height -= (r.start.y + r.height) - _height;
231  }
232 
233  rv->push_back( r );
234  }
235  } // End if enough neighbours
236  } // end if is orange
237 
238  ++(*scanline_model);
239  }
240 
241  // Grow regions
242  if (grow_by > 0) {
243  for (roi_it = rv->begin(); roi_it != rv->end(); ++roi_it) {
244  (*roi_it).grow( grow_by );
245  }
246  }
247 
248  // Merge neighbouring regions
249  for (roi_it = rv->begin(); roi_it != rv->end(); ++roi_it) {
250  roi_it2 = roi_it;
251  ++roi_it2;
252 
253  while ( roi_it2 != rv->end() ) {
254  if ((roi_it != roi_it2) &&
255  roi_it->neighbours(&(*roi_it2), scanline_model->get_margin()))
256  {
257  *roi_it += *roi_it2;
258  rv->erase(roi_it2);
259  roi_it2 = rv->begin(); //restart
260  } else {
261  ++roi_it2;
262  }
263  }
264  }
265 
266  // Throw away all ROIs that have not enough classified points
267  for (roi_it = rv->begin(); roi_it != rv->end(); ++roi_it) {
268  while ( (roi_it != rv->end()) &&
269  ((*roi_it).num_hint_points < min_num_points ))
270  {
271  roi_it = rv->erase( roi_it );
272  }
273  }
274 
275  // sort ROIs by number of hint points, descending (and thus call reverse)
276  rv->sort();
277  rv->reverse();
278 
279  return rv;
280 }
281 
282 
283 /** Get mass point of primary color.
284  * @param roi ROI to consider
285  * @param massPoint contains mass point upon return
286  */
287 void
289  fawkes::upoint_t *massPoint )
290 {
291  unsigned int nrOfOrangePixels;
292  nrOfOrangePixels = 0;
293  massPoint->x = 0;
294  massPoint->y = 0;
295 
296  // for accessing ROI pixels
297  register unsigned int h = 0;
298  register unsigned int w = 0;
299  // planes
300  register unsigned char *yp = _src + (roi->start.y * roi->line_step) + (roi->start.x * roi->pixel_step);
301  register unsigned char *up = YUV422_PLANAR_U_PLANE(_src, roi->image_width, roi->image_height)
302  + ((roi->start.y * roi->line_step) / 2 + (roi->start.x * roi->pixel_step) / 2) ;
303  register unsigned char *vp = YUV422_PLANAR_V_PLANE(_src, roi->image_width, roi->image_height)
304  + ((roi->start.y * roi->line_step) / 2 + (roi->start.x * roi->pixel_step) / 2);
305  // line starts
306  unsigned char *lyp = yp;
307  unsigned char *lup = up;
308  unsigned char *lvp = vp;
309 
310  color_t dcolor;
311 
312  // consider each ROI pixel
313  for (h = 0; h < roi->height; ++h) {
314  for (w = 0; w < roi->width; w += 2) {
315  // classify its color
316  dcolor = color_model->determine(*yp++, *up++, *vp++);
317  yp++;
318  // ball pixel?
319  if (color == dcolor) {
320  // take into account its coordinates
321  massPoint->x += w;
322  massPoint->y += h;
323  nrOfOrangePixels++;
324  }
325  }
326  // next line
327  lyp += roi->line_step;
328  lup += roi->line_step / 2;
329  lvp += roi->line_step / 2;
330  yp = lyp;
331  up = lup;
332  vp = lvp;
333  }
334 
335  // to obtain mass point, divide by number of pixels that were added up
336  massPoint->x = (unsigned int) (float(massPoint->x) / float(nrOfOrangePixels));
337  massPoint->y = (unsigned int) (float(massPoint->y) / float(nrOfOrangePixels));
338 
339  /* shift mass point
340  such that it is relative to image
341  (not relative to ROI) */
342  massPoint->x += roi->start.x;
343  massPoint->y += roi->start.y;
344 }
345 
346 } // end namespace firevision
Color model interface.
Definition: colormodel.h:34
virtual void get_mass_point_of_color(ROI *roi, fawkes::upoint_t *massPoint)
Get mass point of primary color.
Definition: simple.cpp:288
Scanline model interface.
Definition: scanlinemodel.h:55
fawkes::upoint_t start
ROI start.
Definition: roi.h:119
unsigned int _width
Width in pixels of _src buffer.
Definition: classifier.h:54
unsigned int y
y coordinate
Definition: types.h:36
unsigned int x
x coordinate
Definition: types.h:35
unsigned int width
ROI width.
Definition: roi.h:121
Region of interest.
Definition: roi.h:58
A NULL pointer was supplied where not allowed.
Definition: software.h:34
SimpleColorClassifier(ScanlineModel *scanline_model, ColorModel *color_model, unsigned int min_num_points=6, unsigned int box_extent=50, bool upward=false, unsigned int neighbourhood_min_match=8, unsigned int grow_by=10, color_t color=C_ORANGE)
Constructor.
Definition: simple.cpp:58
unsigned int _height
Height in pixels of _src buffer.
Definition: classifier.h:56
unsigned int image_width
width of image that contains this ROI
Definition: roi.h:125
unsigned int image_height
height of image that contains this ROI
Definition: roi.h:127
virtual std::list< ROI > * classify()
Classify image.
Definition: simple.cpp:135
Point with cartesian coordinates as unsigned integers.
Definition: types.h:34
unsigned int hint
ROI hint.
Definition: roi.h:133
unsigned int height
ROI height.
Definition: roi.h:123
unsigned int line_step
line step
Definition: roi.h:129
Classifier to extract regions of interest.
Definition: classifier.h:37
unsigned int pixel_step
pixel step
Definition: roi.h:131
unsigned char * _src
Source buffer, encoded as YUV422_PLANAR.
Definition: classifier.h:52
color_t color
ROI primary color.
Definition: roi.h:136
virtual color_t determine(unsigned int y, unsigned int u, unsigned int v) const =0
Determine classification of YUV pixel.