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