Brian Silverman | 5b3e51e | 2013-03-29 22:53:44 -0700 | [diff] [blame] | 1 | #include "vision/CameraProcessor.h" |
Austin Schuh | 86bec78 | 2013-04-04 05:50:52 +0000 | [diff] [blame^] | 2 | #include "aos/common/logging/logging.h" |
| 3 | |
| 4 | //#define LOCAL_DEBUG 1 |
Brian Silverman | 6ae77dd | 2013-03-29 22:28:08 -0700 | [diff] [blame] | 5 | |
| 6 | // create a new target |
| 7 | Target::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 |
| 18 | double 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 | |
| 35 | void Target::refineTarget() { |
| 36 | printf("okay refined\n"); |
| 37 | } |
| 38 | |
| 39 | FullRect::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 |
| 51 | FullRect 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 |
| 112 | bool 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. |
| 144 | double 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 |
| 188 | double 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 |
| 197 | double 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 |
| 207 | double 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 |
| 239 | template<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 |
| 245 | template<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 |
| 251 | void 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 |
| 258 | ProcessorData::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 Schuh | 86bec78 | 2013-04-04 05:50:52 +0000 | [diff] [blame^] | 270 | global_display = cvCreateImage(cvSize(width, height), |
| 271 | IPL_DEPTH_8U, 3); |
Brian Silverman | 6ae77dd | 2013-03-29 22:28:08 -0700 | [diff] [blame] | 272 | #endif |
| 273 | grey_image = cvCreateImage(cvSize(width, height), |
Austin Schuh | 86bec78 | 2013-04-04 05:50:52 +0000 | [diff] [blame^] | 274 | IPL_DEPTH_8U, 1); |
Brian Silverman | 6ae77dd | 2013-03-29 22:28:08 -0700 | [diff] [blame] | 275 | 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 Silverman | af53594 | 2013-03-31 19:07:35 -0700 | [diff] [blame] | 293 | |
Austin Schuh | 86bec78 | 2013-04-04 05:50:52 +0000 | [diff] [blame^] | 294 | src_header_image = cvCreateImage(cvSize(width, height), |
| 295 | IPL_DEPTH_8U, 3); |
Brian Silverman | 6ae77dd | 2013-03-29 22:28:08 -0700 | [diff] [blame] | 296 | } |
| 297 | |
| 298 | // throw stuff away |
| 299 | ProcessorData::~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. |
| 306 | void 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) |
| 316 | void 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 |
| 340 | void ProcessorData::threshold(uchar* buffer) { |
| 341 | #if LOCAL_DEBUG |
Austin Schuh | 86bec78 | 2013-04-04 05:50:52 +0000 | [diff] [blame^] | 342 | uchar * draw_buffer = (uchar *) global_display->imageData; |
Brian Silverman | 6ae77dd | 2013-03-29 22:28:08 -0700 | [diff] [blame] | 343 | #endif |
Austin Schuh | 86bec78 | 2013-04-04 05:50:52 +0000 | [diff] [blame^] | 344 | 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 Silverman | 6ae77dd | 2013-03-29 22:28:08 -0700 | [diff] [blame] | 349 | |
Austin Schuh | 86bec78 | 2013-04-04 05:50:52 +0000 | [diff] [blame^] | 350 | RGBtoHSV(r, g, b, &h, &s, &v); |
Brian Silverman | 6ae77dd | 2013-03-29 22:28:08 -0700 | [diff] [blame] | 351 | |
Austin Schuh | 86bec78 | 2013-04-04 05:50:52 +0000 | [diff] [blame^] | 352 | if (g > 128) { |
Brian Silverman | 6ae77dd | 2013-03-29 22:28:08 -0700 | [diff] [blame] | 353 | #if LOCAL_DEBUG |
Austin Schuh | 86bec78 | 2013-04-04 05:50:52 +0000 | [diff] [blame^] | 354 | draw_buffer[i + 0] = 120; |
| 355 | draw_buffer[i + 1] = 80; |
| 356 | draw_buffer[i + 2] = 70; |
Brian Silverman | 6ae77dd | 2013-03-29 22:28:08 -0700 | [diff] [blame] | 357 | #endif |
Austin Schuh | 86bec78 | 2013-04-04 05:50:52 +0000 | [diff] [blame^] | 358 | grey_image->imageData[j] = 255; |
| 359 | } else if (h == 0 && s == 0 && v >= v1 && v <= v2) { |
| 360 | // Value thresholds invalid pixels. |
Brian Silverman | 6ae77dd | 2013-03-29 22:28:08 -0700 | [diff] [blame] | 361 | #if LOCAL_DEBUG |
Austin Schuh | 86bec78 | 2013-04-04 05:50:52 +0000 | [diff] [blame^] | 362 | draw_buffer[i + 0] = 200; |
| 363 | draw_buffer[i + 1] = 50; |
| 364 | draw_buffer[i + 2] = 100; |
Brian Silverman | 6ae77dd | 2013-03-29 22:28:08 -0700 | [diff] [blame] | 365 | #endif |
Austin Schuh | 86bec78 | 2013-04-04 05:50:52 +0000 | [diff] [blame^] | 366 | 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 Silverman | 6ae77dd | 2013-03-29 22:28:08 -0700 | [diff] [blame] | 370 | #if LOCAL_DEBUG |
Austin Schuh | 86bec78 | 2013-04-04 05:50:52 +0000 | [diff] [blame^] | 371 | draw_buffer[i + 0] = 255; |
| 372 | draw_buffer[i + 1] = 0; |
| 373 | draw_buffer[i + 2] = 0; |
Brian Silverman | 6ae77dd | 2013-03-29 22:28:08 -0700 | [diff] [blame] | 374 | #endif |
Austin Schuh | 86bec78 | 2013-04-04 05:50:52 +0000 | [diff] [blame^] | 375 | 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 Silverman | 6ae77dd | 2013-03-29 22:28:08 -0700 | [diff] [blame] | 388 | } |
| 389 | |
| 390 | // run find contours and try to make them squares |
| 391 | void 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 |
| 432 | void ProcessorData::filterToTargets() { |
Austin Schuh | 86bec78 | 2013-04-04 05:50:52 +0000 | [diff] [blame^] | 433 | 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 Silverman | 6ae77dd | 2013-03-29 22:28:08 -0700 | [diff] [blame] | 447 | #if LOCAL_DEBUG |
Austin Schuh | 86bec78 | 2013-04-04 05:50:52 +0000 | [diff] [blame^] | 448 | /* 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 Silverman | 6ae77dd | 2013-03-29 22:28:08 -0700 | [diff] [blame] | 457 | #endif |
Austin Schuh | 86bec78 | 2013-04-04 05:50:52 +0000 | [diff] [blame^] | 458 | target_list.push_back(Target(contour, |
| 459 | raw_contour, rect, check, is_90)); |
| 460 | } |
| 461 | if (contour.size() == 4 && cullObvious(rect, contourArea(contour))) { |
| 462 | LOG(INFO, "check= %.2f\n", check); |
| 463 | } |
| 464 | } |
Brian Silverman | 6ae77dd | 2013-03-29 22:28:08 -0700 | [diff] [blame] | 465 | } |
| 466 | |