Fawkes API  Fawkes Development Version
multi_color.cpp
1 
2 /***************************************************************************
3  * multi_color.cpp - Implementation of a MultiColorClassifier
4  *
5  * Created: Sat Apr 02 09:57:14 2011
6  * Copyright 2005-2011 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/multi_color.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 MultiColorClassifier <fvclassifiers/multi_color.h>
43  * Simple multi-color classifier.
44  * @author Tim Niemueller
45  */
46 
47 /** Constructor.
48  * @param scanline_model scanline model
49  * @param color_model color model
50  * @param min_num_points minimum number of points in ROI to be considered
51  * @param box_extent basic extent of a new ROI
52  * @param upward set to true if you have an upward scanline model, this means that
53  * points are traversed from the bottom to the top. In this case the ROIs are initially
54  * extended towards the top instead of the bottom.
55  * @param neighbourhood_min_match minimum number of object pixels to grow neighbourhood
56  * @param grow_by grow region by that many pixels
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  : Classifier("MultiColorClassifier")
66 {
67  if (scanline_model == NULL) {
68  throw fawkes::NullPointerException("MultiColorClassifier: scanline_model "
69  "may not be NULL");
70  }
71  if (color_model == NULL) {
72  throw fawkes::NullPointerException("MultiColorClassifier: color_model "
73  "may not be NULL");
74  }
75 
76  modified = false;
77  this->scanline_model = scanline_model;
78  this->color_model = color_model;
79  this->min_num_points = min_num_points;
80  this->box_extent = box_extent;
81  this->upward = upward;
82  this->grow_by = grow_by;
83  this->neighbourhood_min_match = neighbourhood_min_match;
84 }
85 
86 
87 unsigned int
88 MultiColorClassifier::consider_neighbourhood( unsigned int x, unsigned int y , color_t what)
89 {
90  color_t c;
91  unsigned int num_what = 0;
92 
93  unsigned char yp = 0, up = 0, vp = 0;
94  int start_x = -2, start_y = -2;
95  int end_x = 2, end_y = 2;
96 
97  if (x < (unsigned int)abs(start_x)) {
98  start_x = 0;
99  }
100  if (y < (unsigned int)abs(start_y)) {
101  start_y = 0;
102  }
103 
104  if (x > _width - end_x) {
105  end_x = 0;
106  }
107  if (y == _height - end_y) {
108  end_y = 0;
109  }
110 
111  for (int dx = start_x; dx <= end_x ; dx += 2) {
112  for (int dy = start_y; dy <= end_y; ++dy) {
113  if ((dx == 0) && (dy == 0)) {
114  continue;
115  }
116 
117  // cout << "x=" << x << " dx=" << dx << " +=" << x+dx
118  // << " y=" << y << " dy=" << dy << " +2=" << y+dy << endl;
119 
120  YUV422_PLANAR_YUV(_src, _width, _height, x+dx, y+dy, yp, up, vp);
121  c = color_model->determine(yp, up, vp);
122 
123  if (c == what) {
124  ++num_what;
125  }
126  }
127  }
128 
129  return num_what;
130 }
131 
132 std::list< ROI > *
134 {
135 
136  if (_src == NULL) {
137  return new std::list< ROI >;
138  }
139 
140  std::map<color_t, std::list<ROI> > rois;
141  std::map<color_t, std::list<ROI> >::iterator map_it;
142  std::list<ROI>::iterator roi_it, roi_it2;
143  color_t c;
144 
145  unsigned int x = 0, y = 0;
146  unsigned char yp = 0, up = 0, vp = 0;
147  unsigned int num_what = 0;
148 
149  ROI r;
150 
151  scanline_model->reset();
152  while (! scanline_model->finished()) {
153 
154  x = (*scanline_model)->x;
155  y = (*scanline_model)->y;
156 
157  YUV422_PLANAR_YUV(_src, _width, _height, x, y, yp, up, vp);
158 
159  c = color_model->determine(yp,up, vp);
160 
161  if ((c != C_BACKGROUND) && (c != C_OTHER)) {
162  // Yeah, found something, make it big and name it a ROI
163  // Note that this may throw out a couple of ROIs for just one ball,
164  // as the name suggests this one is really ABSOLUTELY simple and not
165  // useful for anything else than quick testing
166 
167  if (neighbourhood_min_match) {
168  num_what =
169  consider_neighbourhood((*scanline_model)->x, (*scanline_model)->y, c);
170  }
171  if (num_what >= neighbourhood_min_match) {
172  bool ok = false;
173 
174  for (roi_it = rois[c].begin(); roi_it != rois[c].end(); ++roi_it) {
175  if ( (*roi_it).contains(x, y) ) {
176  // everything is fine, this point is already in another ROI
177  ok = true;
178  break;
179  }
180  }
181  if (! ok) {
182  for (roi_it = rois[c].begin(); roi_it != rois[c].end(); ++roi_it) {
183  if ( (*roi_it).neighbours(x, y, scanline_model->get_margin()) ) {
184  // ROI is neighbour of this point, extend region
185  (*roi_it).extend(x, y);
186  ok = true;
187  break;
188  }
189  }
190  }
191 
192  if (! ok) {
193  if ( upward ) {
194  if ( x < box_extent ) {
195  x = 0;
196  } else {
197  x -= box_extent;
198  }
199  if ( y < box_extent ) {
200  y = 0;
201  } else {
202  y -= box_extent;
203  }
204  }
205  r.start.x = x;
206  r.start.y = y;
207 
208  unsigned int to_x = (*scanline_model)->x + box_extent;
209  unsigned int to_y = (*scanline_model)->y + box_extent;
210  if (to_x > _width) to_x = _width;
211  if (to_y > _height) to_y = _height;
212  r.width = to_x - r.start.x;
213  r.height = to_y - r.start.y;
214  r.hint = c;
215  r.color = c;
216 
217  r.line_step = _width;
218  r.pixel_step = 1;
219 
220  r.image_width = _width;
221  r.image_height = _height;
222 
223  if ( (r.start.x + r.width) > _width ) {
224  r.width -= (r.start.x + r.width) - _width;
225  }
226  if ( (r.start.y + r.height) > _height ) {
227  r.height -= (r.start.y + r.height) - _height;
228  }
229 
230  rois[c].push_back( r );
231  }
232  } // End if enough neighbours
233  } // end if is orange
234 
235  ++(*scanline_model);
236  }
237 
238  // Grow regions
239  if (grow_by > 0) {
240  for (map_it = rois.begin(); map_it != rois.end(); ++map_it) {
241  for (roi_it = map_it->second.begin();
242  roi_it != map_it->second.end(); ++roi_it)
243  {
244  (*roi_it).grow( grow_by );
245  }
246  }
247  }
248 
249  // Merge neighbouring regions
250  for (map_it = rois.begin(); map_it != rois.end(); ++map_it) {
251  for (roi_it = map_it->second.begin(); roi_it != map_it->second.end(); ++roi_it)
252  {
253  roi_it2 = roi_it;
254  ++roi_it2;
255 
256  while ( roi_it2 != map_it->second.end() ) {
257  if ((roi_it != roi_it2) &&
258  roi_it->neighbours(&(*roi_it2), scanline_model->get_margin()))
259  {
260  *roi_it += *roi_it2;
261  map_it->second.erase(roi_it2);
262  roi_it2 = map_it->second.begin(); //restart
263  } else {
264  ++roi_it2;
265  }
266  }
267  }
268  }
269 
270  // Throw away all ROIs that have not enough classified points
271  for (map_it = rois.begin(); map_it != rois.end(); ++map_it) {
272  for (roi_it = map_it->second.begin(); roi_it != map_it->second.end(); ++roi_it)
273  {
274  while ( (roi_it != map_it->second.end()) &&
275  ((*roi_it).num_hint_points < min_num_points ))
276  {
277  roi_it = map_it->second.erase(roi_it);
278  }
279  }
280  }
281 
282  // sort ROIs by number of hint points, descending (and thus call reverse)
283 
284  std::list<ROI> *rv = new std::list<ROI>();
285  for (map_it = rois.begin(); map_it != rois.end(); ++map_it) {
286  map_it->second.sort();
287  rv->merge(map_it->second);
288  }
289  rv->reverse();
290  return rv;
291 }
292 
293 
294 /** Get mass point of primary color.
295  * @param roi ROI to consider
296  * @param massPoint contains mass point upon return
297  */
298 void
300  fawkes::upoint_t *massPoint )
301 {
302  unsigned int nrOfOrangePixels;
303  nrOfOrangePixels = 0;
304  massPoint->x = 0;
305  massPoint->y = 0;
306 
307  // for accessing ROI pixels
308  register unsigned int h = 0;
309  register unsigned int w = 0;
310  // planes
311  register unsigned char *yp = _src + (roi->start.y * roi->line_step) + (roi->start.x * roi->pixel_step);
312  register unsigned char *up = YUV422_PLANAR_U_PLANE(_src, roi->image_width, roi->image_height)
313  + ((roi->start.y * roi->line_step) / 2 + (roi->start.x * roi->pixel_step) / 2) ;
314  register unsigned char *vp = YUV422_PLANAR_V_PLANE(_src, roi->image_width, roi->image_height)
315  + ((roi->start.y * roi->line_step) / 2 + (roi->start.x * roi->pixel_step) / 2);
316  // line starts
317  unsigned char *lyp = yp;
318  unsigned char *lup = up;
319  unsigned char *lvp = vp;
320 
321  color_t dcolor;
322 
323  // consider each ROI pixel
324  for (h = 0; h < roi->height; ++h) {
325  for (w = 0; w < roi->width; w += 2) {
326  // classify its color
327  dcolor = color_model->determine(*yp++, *up++, *vp++);
328  yp++;
329  // ball pixel?
330  if (dcolor == roi->color) {
331  // take into account its coordinates
332  massPoint->x += w;
333  massPoint->y += h;
334  nrOfOrangePixels++;
335  }
336  }
337  // next line
338  lyp += roi->line_step;
339  lup += roi->line_step / 2;
340  lvp += roi->line_step / 2;
341  yp = lyp;
342  up = lup;
343  vp = lvp;
344  }
345 
346  // to obtain mass point, divide by number of pixels that were added up
347  massPoint->x = (unsigned int) (float(massPoint->x) / float(nrOfOrangePixels));
348  massPoint->y = (unsigned int) (float(massPoint->y) / float(nrOfOrangePixels));
349 
350  /* shift mass point
351  such that it is relative to image
352  (not relative to ROI) */
353  massPoint->x += roi->start.x;
354  massPoint->y += roi->start.y;
355 }
356 
357 } // end namespace firevision
Color model interface.
Definition: colormodel.h:34
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
MultiColorClassifier(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)
Constructor.
Definition: multi_color.cpp:58
Region of interest.
Definition: roi.h:58
A NULL pointer was supplied where not allowed.
Definition: software.h:34
virtual std::list< ROI > * classify()
Classify image.
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
virtual void get_mass_point_of_color(ROI *roi, fawkes::upoint_t *massPoint)
Get mass point of primary color.
unsigned int image_height
height of image that contains this ROI
Definition: roi.h:127
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.