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