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