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
34namespace 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
80unsigned int
81MultiColorClassifier::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
125std::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
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 */
281void
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
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.
virtual std::list< ROI > * classify()
Classify image.
virtual void get_mass_point_of_color(ROI *roi, fawkes::upoint_t *massPoint)
Get mass point of primary color.
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
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
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