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