blob: e186cbf44f14e3a26f2e8faf0f18b7f199fd5b0f [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 }
Brian Silvermanaf535942013-03-31 19:07:35 -0700290
291 src_header_image = cvCreateImage(cvSize(width, height),
292 IPL_DEPTH_8U, 3);
Brian Silverman6ae77dd2013-03-29 22:28:08 -0700293}
294
295// throw stuff away
296ProcessorData::~ProcessorData() {
297 cvReleaseImage(&grey_image);
298 cvReleaseImage(&src_header_image);
299 delete(grey_mat);
300}
301
302// reset processor data freeing as little as possible.
303void ProcessorData::clear() {
304 target_list.clear();
305 contour_pairs.clear();
306 hierarchy.clear();
307}
308
309
310// r,g,b values are from 0 to 255
311// h = [0,255], s = [0,255], v = [0,255]
312// if s == 0, then h = 0 (undefined)
313void ProcessorData::RGBtoHSV(uchar r, uchar g, uchar b,
314 uchar *h, uchar *s, uchar *v ) {
315 uchar min, max, delta;
316 min = Min3( r, g, b );
317 max = Max3( r, g, b );
318 *v = max;
319 delta = max - min;
320 if (max == 0 || delta == 0) {
321 *s = 0;
322 *h = 0;
323 return;
324 }
325 *s = (255 * long(delta))/max;
326 if (max == r) {
327 *h = 0 + 43*(g - b)/(delta);
328 } else if (max == g) {
329 *h = 85 + 43*(b - r)/(delta);
330 } else {
331 *h = 171 + 43*(r - g)/(delta);
332 }
333}
334
335// keep anything that is in our HVS wedge
336// by far the longest running function in this code
337void ProcessorData::threshold(uchar* buffer) {
338#if LOCAL_DEBUG
339 uchar * draw_buffer = (uchar *) global_display->imageData;
340#endif
341 for (int i = 0,j = 0;
342 i < (buffer_size);
343 i+= 3,j++) {
344 uchar r = buffer[i + 2];
345 uchar g = buffer[i + 1];
346 uchar b = buffer[i + 0];
347 uchar h, s, v;
348
349 RGBtoHSV(r, g, b, &h, &s, &v);
350
351 /* if (h == 0 && s == 0 && v >= v1 && v <= v2) {
352#if LOCAL_DEBUG
353 draw_buffer[i + 0] = 200;
354 draw_buffer[i + 1] = 50;
355 draw_buffer[i + 2] = 100;
356#endif
357 grey_image->imageData[j] = 255;
358 } else */if(h >= h1 && h <= h2 && v >= v1 && v <= v2 && s >= s1 && s <= s2){
359#if LOCAL_DEBUG
360/* draw_buffer[i + 0] = 255;
361 draw_buffer[i + 1] = 0;
362 draw_buffer[i + 2] = 0;*/
363#endif
364 grey_image->imageData[j] = 255;
365 }else{
366#if LOCAL_DEBUG
367/* draw_buffer[i + 0] = buffer[i + 0];
368 draw_buffer[i + 1] = buffer[i + 1];
369 draw_buffer[i + 2] = buffer[i + 2];*/
370#endif
371 grey_image->imageData[j] = 0;
372 }
373 }
374}
375
376// run find contours and try to make them squares
377void ProcessorData::getContours() {
378 std::vector<std::vector<cv::Point> > raw_contours;
379 //cv::findContours(*grey_mat, raw_contours, hierarchy, CV_RETR_LIST,
380 // CV_CHAIN_APPROX_SIMPLE);
381 cv::findContours(*grey_mat, raw_contours, hierarchy, CV_RETR_EXTERNAL,
382 CV_CHAIN_APPROX_SIMPLE);
383#if LOCAL_DEBUG
384 cv::Mat global_mat(global_display);
385 cv::Scalar color(255,0,0);
386 drawContours(global_mat,raw_contours,-1,color,1);
387
388 std::vector<std::vector<cv::Point> > p_contours;
389#endif
390 if (!raw_contours.empty()) {
391 std::vector<std::vector<cv::Point2i> >::iterator contour_it;
392 for (contour_it=raw_contours.begin();
393 contour_it != raw_contours.end();
394 contour_it++) {
395 // make the contours convex
396 makeConvex(&(*contour_it));
397 std::vector<cv::Point> contour;
398 //then make them rectangle
399 approxPolyDP(*contour_it, contour, 9, true);
400 // stick the raw and processed contours together
401 std::pair<std::vector<cv::Point>,
402 std::vector<cv::Point> > a_pair(
403 *contour_it, contour);
404#if LOCAL_DEBUG
405 p_contours.push_back(contour);
406#endif
407 // and put them in a list
408 contour_pairs.push_back(a_pair);
409 }
410 }
411#if LOCAL_DEBUG
412 cv::Scalar color2(0,0,255);
413 drawContours(global_mat,p_contours,-1,color2,CV_FILLED);
414#endif
415}
416
417// filter the contours down to a list of targets
418void ProcessorData::filterToTargets() {
419 std::vector<std::pair<
420 std::vector<cv::Point>,
421 std::vector<cv::Point> > >::iterator contour_it;
422 for(contour_it=contour_pairs.begin();
423 contour_it != contour_pairs.end();
424 contour_it++){
425 double check = 0;
426 std::vector<cv::Point> raw_contour = std::get<0>(*contour_it);
427 std::vector<cv::Point> contour = std::get<1>(*contour_it);
428 FullRect rect = calcFullRect(&contour);
429 if(contour.size() == 4 &&
430 cullObvious(rect, contourArea(contour)) &&
431 (check = checkHistogram(rect, *grey_mat)) <= HIST_MATCH){
432 // now we have a target, try to improve the square
433#if LOCAL_DEBUG
434 /* printf("________\n");
435 printf("\tcont= %d raw= %d\n",
436 (int)contour.size(), (int)raw_contour.size());
437 std::vector<cv::Point2i>::iterator point_it;
438 for(point_it=raw_contour.begin();
439 point_it != raw_contour.end(); point_it++){
440 printf("(%d,%d)", point_it->x, point_it->y);
441 }
442 printf("\n");*/
443#endif
444 target_list.push_back(Target(contour,
445 raw_contour, rect, check, is_90));
446 }
447 //if(contour.size() == 4 &&
448 // cullObvious(rect, contourArea(contour)) ) {
449 // printf("check= %.2f\n", check);
450 //}
451 }
452}
453