blob: 856e12bd4ae37514ac4d005a49dfc964ef8ec28f [file] [log] [blame]
Brian Silverman5b3e51e2013-03-29 22:53:44 -07001#include "vision/CameraProcessor.h"
Austin Schuh86bec782013-04-04 05:50:52 +00002#include "aos/common/logging/logging.h"
3
4//#define LOCAL_DEBUG 1
Brian Silverman6ae77dd2013-03-29 22:28:08 -07005
6// create a new target
7Target::Target(std::vector<cv::Point> new_contour,
8 std::vector<cv::Point> new_raw_contour,
9 FullRect new_rect, int new_weight, bool _is_90) {
10 this_contour = new_contour;
11 raw_contour = new_raw_contour;
12 rect = new_rect;
13 weight = new_weight;
14 height = getHeight(_is_90);
15}
16
17// calculate distance to target
18double Target::getHeight(bool is_90) {
19 // The 780.3296 is at full resolution 640x480, and we need
20 // to scale back to 320x240
21 //static const double cam_l = 780.3296 / 2.0;
22 ////static const double cam_d = 20.78096;
23 double height;
24 if (is_90) {
25 height = ((rect.ul.x - rect.ur.x) +
26 (rect.bl.x - rect.br.x)) / 2.0;
27 } else {
28 height = ((rect.ur.y + rect.ul.y) -
29 (rect.br.y + rect.bl.y)) / 2.0;
30 }
31 //return cam_l * 12.0 / height;
32 return height;
33}
34
35void Target::refineTarget() {
36 printf("okay refined\n");
37}
38
39FullRect::FullRect() {
40 ur.x = -1;
41 ur.y = -1;
42 ul.x = -1;
43 ul.y = -1;
44 br.x = -1;
45 br.y = -1;
46 bl.x = -1;
47 bl.y = -1;
48}
49
50// turns a contour into easier to access structure
51FullRect calcFullRect(std::vector<cv::Point> *contour){
52 FullRect rect;
53 for(int i=0; i<4; i++){
54 cv::Point2f pt = (*contour)[i];
55 rect.centroid.x += pt.x;
56 rect.centroid.y += pt.y;
57 }
58 rect.centroid.x /= 4;
59 rect.centroid.y /= 4;
60 for(int i=0; i<4; i++){
61 cv::Point2f pt = (*contour)[i];
62 if(pt.y > rect.centroid.y ){
63 if(pt.x > rect.centroid.x){
64 if (rect.ul.x < 0) {
65 rect.ul = pt;
66 } else {
67 rect.ur = pt;
68 }
69 }else{
70 if (rect.ur.x < 0) {
71 rect.ur = pt;
72 } else {
73 rect.ul = pt;
74 }
75 }
76 if (rect.ul.x > 0 && rect.ur.x > 0) {
77 // both are set, so if we got it wrong correct it here
78 if (rect.ul.x > rect.ur.x) {
79 pt = rect.ur;
80 rect.ur = rect.ul;
81 rect.ul = pt;
82 }
83 }
84 }else{
85 if(pt.x > rect.centroid.x){
86 if (rect.bl.x < 0) {
87 rect.bl = pt;
88 } else {
89 rect.br = pt;
90 }
91 }else{
92 if (rect.br.x < 0) {
93 rect.br = pt;
94 } else {
95 rect.bl = pt;
96 }
97 }
98 if (rect.bl.x > 0 && rect.br.x > 0) {
99 // both are set, so if we got it wrong correct it here
100 if (rect.bl.x > rect.br.x) {
101 pt = rect.br;
102 rect.br = rect.bl;
103 rect.bl = pt;
104 }
105 }
106 }
107 }
108 return rect;
109}
110
111// quickly remove targets that do not fit a very broad set of constraints
112bool cullObvious(FullRect rect, double outside_size){
113 // first check that could see a target this size
114 // Calculated from dave's simulation, shloud be 850 and 72000 if filled
115 if((outside_size < 500) || (outside_size > 90000)){
116 return false;
117 }
118 // Targets on the edge are at best inaccurate.
119 // In this case, we just want to point the right way,
120 // so this is no longer a valid assumption.
121 /*if( rect.ur.x < 2 || rect.ur.y < 2 || rect.ur.x > 637 || rect.ur.y > 477 ||
122 rect.ul.x < 2 || rect.ul.y < 2 || rect.ul.x > 637 || rect.ul.y > 477 ||
123 rect.br.x < 2 || rect.br.y < 2 || rect.br.x > 637 || rect.br.y > 477 ||
124 rect.bl.x < 2 || rect.bl.y < 2 || rect.bl.x > 637 || rect.bl.y > 477){
125 return false;
126 }*/
127 // make sure the sides are close to the right ratio of a rect
128 // some really odd shapes get confusing
129 double ratio = norm(rect.ur-rect.ul)/norm(rect.br-rect.bl);
130 if( ratio < .7 || ratio > 1.4 ) {
131 return false;
132 }
133 ratio = norm(rect.ur-rect.br)/norm(rect.ul-rect.bl);
134 if( ratio < .7 || ratio > 1.4 ) {
135 return false;
136 }
137
138 return true;
139}
140
141// sum over values between these two points and normalize
142// see Bresenham's Line Algorithm for the logic of moving
143// over all the pixels between these two points.
144double ProcessorData::calcHistComponent(
145 cv::Point2i start,
146 cv::Point2i end,
147 cv::Mat thresh_img){
148 int dx = abs(end.x - start.x);
149 int dy = abs(end.y - start.y);
150 int sx = (start.x < end.x) ? 1 : -1;
151 int sy = (start.y < end.y) ? 1 : -1;
152 int error = dx-dy;
153
154 int total = 0;
155 int value = 0;
156 int total_error;
157#if LOCAL_DEBUG
158 IplImage gd = *global_display;
159#endif
160 IplImage ti = thresh_img;
161 while(1){
162 total++;
163
164 uchar* ptr = (uchar*) (ti.imageData + start.y * ti.widthStep + start.x);
165 if((int) *ptr) value++;
166 // draw line checked
167#if LOCAL_DEBUG
168 uchar* ptr2 = (uchar*) (gd.imageData + start.y * gd.widthStep + start.x*3);
169 *ptr2++ = 0;
170 *ptr2++ = 255;
171 *ptr2 = 0;
172#endif
173 if(start.x == end.x && start.y == end.y) break;
174 total_error = 2 * error;
175 if(total_error > -dy){
176 error -= dy;
177 start.x += sx;
178 }
179 if(total_error < dx){
180 error += dx;
181 start.y += sy;
182 }
183 }
184 return (double)value/(double)total;
185}
186
187// just a distance function
188double chiSquared(int length, double* histA, double* histB){
189 double sum = 0;
190 for(int i=0; i<length;i++){
191 double diff = *(histB+i) - *(histA+i);
192 sum += (diff * diff) / *(histA+i);
193 }
194 return sum;
195}
196// euclidiean dist function
197double L2_dist(int length, double* histA, double* histB){
198 double sum = 0;
199 for(int i=0; i<length;i++){
200 double diff = *(histB+i) - *(histA+i);
201 sum += (diff * diff);
202 }
203 return sqrt(sum);
204}
205
206// calc and compare the histograms to the desired
207double ProcessorData::checkHistogram(FullRect rect, cv::Mat thresh_img){
208 // found horiz histogram
209 double hist_lr[HIST_SIZE];
210 // step size along left edge
211 cv::Point2f delta_left = (rect.ul - rect.bl)*HIST_SIZE_F;
212 // step size along right edge
213 cv::Point2f delta_right = (rect.ur - rect.br)*HIST_SIZE_F;
214 // sum each left to right line for the histogram
215 for(int i=0; i<HIST_SIZE; i++){
216 hist_lr[i] = calcHistComponent(rect.bl+i*delta_left,
217 rect.br+i*delta_right,thresh_img);
218 }
219 double check_vert = L2_dist(HIST_SIZE, vert_hist, hist_lr);
220 // found vert histogram
221 double hist_ub[HIST_SIZE];
222 // step size along bottom edge
223 cv::Point2f delta_bottom = (rect.bl - rect.br)*HIST_SIZE_F;
224 // step size along top edge
225 cv::Point2f delta_top = (rect.ul - rect.ur)*HIST_SIZE_F;
226 // sum each top to bottom line for the histogram
227 for(int i=0; i<HIST_SIZE; i++){
228 hist_ub[i] = calcHistComponent(rect.ur+i*delta_top,
229 rect.br+i*delta_bottom,thresh_img);
230 }
231 double check_horz = L2_dist(HIST_SIZE, horz_hist, hist_ub);
232
233 // average the two distances
234 double check = (check_vert + check_horz)/2.0;
235 return check;
236}
237
238// return smallest
239template<class T> inline T Min3(T x, T y, T z) {
240 return y <= z ? (x <= y ? x : y)
241 : (x <= z ? x : z);
242}
243
244// return largest
245template<class T> inline T Max3(T x, T y, T z) {
246 return y >= z ? (x >= y ? x : y)
247 : (x >= z ? x : z);
248}
249
250// transforms the contour
251void makeConvex(std::vector<cv::Point> *contour){
252 std::vector<cv::Point2i> hull;
253 convexHull(*contour, hull, false);
254 *contour = hull;
255}
256
257// basic init
258ProcessorData::ProcessorData(int width, int height, bool is_90_) {
259 is_90 = is_90_;
260 // series b images from nasa
261 h1=79; s1=53; v1=82;
262 h2=200; s2=255; v2=255;
263 // For images from Jerry
264 //h1=79; s1=0; v1=11;
265 //h2=160; s2=255; v2=255;
266 img_width = width;
267 img_height = height;
268 buffer_size = img_height * img_width * 3;
269#if LOCAL_DEBUG
Austin Schuh86bec782013-04-04 05:50:52 +0000270 global_display = cvCreateImage(cvSize(width, height),
271 IPL_DEPTH_8U, 3);
Brian Silverman6ae77dd2013-03-29 22:28:08 -0700272#endif
273 grey_image = cvCreateImage(cvSize(width, height),
Austin Schuh86bec782013-04-04 05:50:52 +0000274 IPL_DEPTH_8U, 1);
Brian Silverman6ae77dd2013-03-29 22:28:08 -0700275 grey_mat = new cv::Mat(grey_image);
276
277 // calculate a desired histogram before we start
278 int j = 0;
279 for(double i=0; j<HIST_SIZE; i+=HIST_SIZE_F){
280 if (is_90) {
281 if(i < 2.0/12.0 || i > (1.0-2.0/12.0) ) horz_hist[j] = 1;
282 else horz_hist[j] = 0.10;
283 if(i < 2.0/24.0 || i > (1.0-2.0/24.0) ) vert_hist[j] = 1;
284 else vert_hist[j] = 4.0/24.0;
285 } else {
286 if(i < 2.0/12.0 || i > (1.0-2.0/12.0) ) vert_hist[j] = 1;
287 else vert_hist[j] = 0.10;
288 if(i < 2.0/24.0 || i > (1.0-2.0/24.0) ) horz_hist[j] = 1;
289 else horz_hist[j] = 4.0/24.0;
290 }
291 j++;
292 }
Brian Silvermanaf535942013-03-31 19:07:35 -0700293
Austin Schuh86bec782013-04-04 05:50:52 +0000294 src_header_image = cvCreateImage(cvSize(width, height),
295 IPL_DEPTH_8U, 3);
Brian Silverman6ae77dd2013-03-29 22:28:08 -0700296}
297
298// throw stuff away
299ProcessorData::~ProcessorData() {
300 cvReleaseImage(&grey_image);
301 cvReleaseImage(&src_header_image);
302 delete(grey_mat);
303}
304
305// reset processor data freeing as little as possible.
306void ProcessorData::clear() {
307 target_list.clear();
308 contour_pairs.clear();
309 hierarchy.clear();
310}
311
312
313// r,g,b values are from 0 to 255
314// h = [0,255], s = [0,255], v = [0,255]
315// if s == 0, then h = 0 (undefined)
316void ProcessorData::RGBtoHSV(uchar r, uchar g, uchar b,
317 uchar *h, uchar *s, uchar *v ) {
318 uchar min, max, delta;
319 min = Min3( r, g, b );
320 max = Max3( r, g, b );
321 *v = max;
322 delta = max - min;
323 if (max == 0 || delta == 0) {
324 *s = 0;
325 *h = 0;
326 return;
327 }
328 *s = (255 * long(delta))/max;
329 if (max == r) {
330 *h = 0 + 43*(g - b)/(delta);
331 } else if (max == g) {
332 *h = 85 + 43*(b - r)/(delta);
333 } else {
334 *h = 171 + 43*(r - g)/(delta);
335 }
336}
337
338// keep anything that is in our HVS wedge
339// by far the longest running function in this code
340void ProcessorData::threshold(uchar* buffer) {
341#if LOCAL_DEBUG
Austin Schuh86bec782013-04-04 05:50:52 +0000342 uchar * draw_buffer = (uchar *) global_display->imageData;
Brian Silverman6ae77dd2013-03-29 22:28:08 -0700343#endif
Austin Schuh86bec782013-04-04 05:50:52 +0000344 for (int i = 0, j = 0; i < (buffer_size); i+= 3, j++) {
345 uchar r = buffer[i + 2];
346 uchar g = buffer[i + 1];
347 uchar b = buffer[i + 0];
348 uchar h, s, v;
Brian Silverman6ae77dd2013-03-29 22:28:08 -0700349
Austin Schuh86bec782013-04-04 05:50:52 +0000350 RGBtoHSV(r, g, b, &h, &s, &v);
Brian Silverman6ae77dd2013-03-29 22:28:08 -0700351
Austin Schuh86bec782013-04-04 05:50:52 +0000352 if (g > 128) {
Brian Silverman6ae77dd2013-03-29 22:28:08 -0700353#if LOCAL_DEBUG
Austin Schuh86bec782013-04-04 05:50:52 +0000354 draw_buffer[i + 0] = 120;
355 draw_buffer[i + 1] = 80;
356 draw_buffer[i + 2] = 70;
Brian Silverman6ae77dd2013-03-29 22:28:08 -0700357#endif
Austin Schuh86bec782013-04-04 05:50:52 +0000358 grey_image->imageData[j] = 255;
359 } else if (h == 0 && s == 0 && v >= v1 && v <= v2) {
360 // Value thresholds invalid pixels.
Brian Silverman6ae77dd2013-03-29 22:28:08 -0700361#if LOCAL_DEBUG
Austin Schuh86bec782013-04-04 05:50:52 +0000362 draw_buffer[i + 0] = 200;
363 draw_buffer[i + 1] = 50;
364 draw_buffer[i + 2] = 100;
Brian Silverman6ae77dd2013-03-29 22:28:08 -0700365#endif
Austin Schuh86bec782013-04-04 05:50:52 +0000366 grey_image->imageData[j] = 255;
367 } else if (h >= h1 && h <= h2 && v >= v1 &&
368 v <= v2 && s >= s1 && s <= s2){
369 // HSV Thresholded image.
Brian Silverman6ae77dd2013-03-29 22:28:08 -0700370#if LOCAL_DEBUG
Austin Schuh86bec782013-04-04 05:50:52 +0000371 draw_buffer[i + 0] = 255;
372 draw_buffer[i + 1] = 0;
373 draw_buffer[i + 2] = 0;
Brian Silverman6ae77dd2013-03-29 22:28:08 -0700374#endif
Austin Schuh86bec782013-04-04 05:50:52 +0000375 grey_image->imageData[j] = 255;
376 } else {
377 // Display the unmodified image.
378#if LOCAL_DEBUG
379 draw_buffer[i + 0] = buffer[i + 0];
380 draw_buffer[i + 1] = buffer[i + 1];
381 draw_buffer[i + 2] = buffer[i + 2];
382#endif
383 grey_image->imageData[j] = 0;
384 }
385
386 }
387
Brian Silverman6ae77dd2013-03-29 22:28:08 -0700388}
389
390// run find contours and try to make them squares
391void ProcessorData::getContours() {
392 std::vector<std::vector<cv::Point> > raw_contours;
393 //cv::findContours(*grey_mat, raw_contours, hierarchy, CV_RETR_LIST,
394 // CV_CHAIN_APPROX_SIMPLE);
395 cv::findContours(*grey_mat, raw_contours, hierarchy, CV_RETR_EXTERNAL,
396 CV_CHAIN_APPROX_SIMPLE);
397#if LOCAL_DEBUG
398 cv::Mat global_mat(global_display);
399 cv::Scalar color(255,0,0);
400 drawContours(global_mat,raw_contours,-1,color,1);
401
402 std::vector<std::vector<cv::Point> > p_contours;
403#endif
404 if (!raw_contours.empty()) {
405 std::vector<std::vector<cv::Point2i> >::iterator contour_it;
406 for (contour_it=raw_contours.begin();
407 contour_it != raw_contours.end();
408 contour_it++) {
409 // make the contours convex
410 makeConvex(&(*contour_it));
411 std::vector<cv::Point> contour;
412 //then make them rectangle
413 approxPolyDP(*contour_it, contour, 9, true);
414 // stick the raw and processed contours together
415 std::pair<std::vector<cv::Point>,
416 std::vector<cv::Point> > a_pair(
417 *contour_it, contour);
418#if LOCAL_DEBUG
419 p_contours.push_back(contour);
420#endif
421 // and put them in a list
422 contour_pairs.push_back(a_pair);
423 }
424 }
425#if LOCAL_DEBUG
426 cv::Scalar color2(0,0,255);
427 drawContours(global_mat,p_contours,-1,color2,CV_FILLED);
428#endif
429}
430
431// filter the contours down to a list of targets
432void ProcessorData::filterToTargets() {
Austin Schuh86bec782013-04-04 05:50:52 +0000433 std::vector<std::pair<
434 std::vector<cv::Point>,
435 std::vector<cv::Point> > >::iterator contour_it;
436 for(contour_it=contour_pairs.begin();
437 contour_it != contour_pairs.end();
438 contour_it++){
439 double check = 0;
440 std::vector<cv::Point> raw_contour = std::get<0>(*contour_it);
441 std::vector<cv::Point> contour = std::get<1>(*contour_it);
442 FullRect rect = calcFullRect(&contour);
443 if(contour.size() == 4 &&
444 cullObvious(rect, contourArea(contour)) &&
445 (check = checkHistogram(rect, *grey_mat)) <= HIST_MATCH){
446 // now we have a target, try to improve the square
Brian Silverman6ae77dd2013-03-29 22:28:08 -0700447#if LOCAL_DEBUG
Austin Schuh86bec782013-04-04 05:50:52 +0000448 /* printf("________\n");
449 printf("\tcont= %d raw= %d\n",
450 (int)contour.size(), (int)raw_contour.size());
451 std::vector<cv::Point2i>::iterator point_it;
452 for(point_it=raw_contour.begin();
453 point_it != raw_contour.end(); point_it++){
454 printf("(%d,%d)", point_it->x, point_it->y);
455 }
456 printf("\n");*/
Brian Silverman6ae77dd2013-03-29 22:28:08 -0700457#endif
Austin Schuh86bec782013-04-04 05:50:52 +0000458 target_list.push_back(Target(contour,
459 raw_contour, rect, check, is_90));
460 }
461 if (contour.size() == 4 && cullObvious(rect, contourArea(contour))) {
Brian Silverman6742a442013-11-03 12:58:42 -0800462 LOG(DEBUG, "check= %.2f\n", check);
Austin Schuh86bec782013-04-04 05:50:52 +0000463 }
464 }
Brian Silverman6ae77dd2013-03-29 22:28:08 -0700465}
466