1//M*////////////////////////////////////////////////////////////////////////////////////// 2// 3// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. 4// 5// By downloading, copying, installing or using the software you agree to this license. 6// If you do not agree to this license, do not download, install, 7// copy or use the software. 8// 9// 10// Intel License Agreement 11// For Open Source Computer Vision Library 12// 13// Copyright (C) 2000, Intel Corporation, all rights reserved. 14// Third party copyrights are property of their respective owners. 15// 16// Redistribution and use in source and binary forms, with or without modification, 17// are permitted provided that the following conditions are met: 18// 19// * Redistribution's of source code must retain the above copyright notice, 20// this list of conditions and the following disclaimer. 21// 22// * Redistribution's in binary form must reproduce the above copyright notice, 23// this list of conditions and the following disclaimer in the documentation 24// and/or other materials provided with the distribution. 25// 26// * The name of Intel Corporation may not be used to endorse or promote products 27// derived from this software without specific prior written permission. 28// 29// This software is provided by the copyright holders and contributors "as is" and 30// any express or implied warranties, including, but not limited to, the implied 31// warranties of merchantability and fitness for a particular purpose are disclaimed. 32// In no event shall the Intel Corporation or contributors be liable for any direct, 33// indirect, incidental, special, exemplary, or consequential damages 34// (including, but not limited to, procurement of substitute goods or services; 35// loss of use, data, or profits; or business interruption) however caused 36// and on any theory of liability, whether in contract, strict liability, 37// or tort (including negligence or otherwise) arising in any way out of 38// the use of this software, even if advised of the possibility of such damage. 39// 40//M*/ 41 42/************************************************************************************\ 43 This is improved variant of chessboard corner detection algorithm that 44 uses a graph of connected quads. It is based on the code contributed 45 by Vladimir Vezhnevets and Philip Gruebele. 46 Here is the copyright notice from the original Vladimir's code: 47 =============================================================== 48 49 The algorithms developed and implemented by Vezhnevets Vldimir 50 aka Dead Moroz (vvp@graphics.cs.msu.ru) 51 See http://graphics.cs.msu.su/en/research/calibration/opencv.html 52 for detailed information. 53 54 Reliability additions and modifications made by Philip Gruebele. 55 <a href="mailto:pgruebele@cox.net">pgruebele@cox.net</a> 56 57 Some further improvements for detection of partially ocluded boards at non-ideal 58 lighting conditions have been made by Alex Bovyrin and Kurt Kolonige 59 60\************************************************************************************/ 61 62#include "_cv.h" 63#include <stdarg.h> 64 65//#define DEBUG_CHESSBOARD 66#ifdef DEBUG_CHESSBOARD 67static int PRINTF( const char* fmt, ... ) 68{ 69 va_list args; 70 va_start(args, fmt); 71 return vprintf(fmt, args); 72} 73#include "..//..//otherlibs/highgui/highgui.h" 74#else 75static int PRINTF( const char*, ... ) 76{ 77 return 0; 78} 79#endif 80 81 82//===================================================================================== 83// Implementation for the enhanced calibration object detection 84//===================================================================================== 85 86#define MAX_CONTOUR_APPROX 7 87 88typedef struct CvContourEx 89{ 90 CV_CONTOUR_FIELDS() 91 int counter; 92} 93CvContourEx; 94 95//===================================================================================== 96 97/// Corner info structure 98/** This structure stores information about the chessboard corner.*/ 99typedef struct CvCBCorner 100{ 101 CvPoint2D32f pt; // Coordinates of the corner 102 int row; // Board row index 103 int count; // Number of neighbor corners 104 struct CvCBCorner* neighbors[4]; // Neighbor corners 105} 106CvCBCorner; 107 108//===================================================================================== 109/// Quadrangle contour info structure 110/** This structure stores information about the chessboard quadrange.*/ 111typedef struct CvCBQuad 112{ 113 int count; // Number of quad neighbors 114 int group_idx; // quad group ID 115 int row, col; // row and column of this quad 116 bool ordered; // true if corners/neighbors are ordered counter-clockwise 117 float edge_len; // quad edge len, in pix^2 118 // neighbors and corners are synced, i.e., neighbor 0 shares corner 0 119 CvCBCorner *corners[4]; // Coordinates of quad corners 120 struct CvCBQuad *neighbors[4]; // Pointers of quad neighbors 121} 122CvCBQuad; 123 124//===================================================================================== 125 126//static CvMat* debug_img = 0; 127 128static int icvGenerateQuads( CvCBQuad **quads, CvCBCorner **corners, 129 CvMemStorage *storage, CvMat *image, int flags ); 130 131static int 132icvGenerateQuadsEx( CvCBQuad **out_quads, CvCBCorner **out_corners, 133 CvMemStorage *storage, CvMat *image, CvMat *thresh_img, int dilation, int flags ); 134 135static void icvFindQuadNeighbors( CvCBQuad *quads, int quad_count ); 136 137static int icvFindConnectedQuads( CvCBQuad *quads, int quad_count, 138 CvCBQuad **quad_group, int group_idx, 139 CvMemStorage* storage ); 140 141static int icvCheckQuadGroup( CvCBQuad **quad_group, int count, 142 CvCBCorner **out_corners, CvSize pattern_size ); 143 144static int icvCleanFoundConnectedQuads( int quad_count, 145 CvCBQuad **quads, CvSize pattern_size ); 146 147static int icvOrderFoundConnectedQuads( int quad_count, CvCBQuad **quads, 148 int *all_count, CvCBQuad **all_quads, CvCBCorner **corners, 149 CvSize pattern_size, CvMemStorage* storage ); 150 151static void icvOrderQuad(CvCBQuad *quad, CvCBCorner *corner, int common); 152 153static int icvTrimCol(CvCBQuad **quads, int count, int col, int dir); 154 155static int icvTrimRow(CvCBQuad **quads, int count, int row, int dir); 156 157static int icvAddOuterQuad(CvCBQuad *quad, CvCBQuad **quads, int quad_count, 158 CvCBQuad **all_quads, int all_count, CvCBCorner **corners); 159 160static void icvRemoveQuadFromGroup(CvCBQuad **quads, int count, CvCBQuad *q0); 161 162static int icvCheckBoardMonotony( CvPoint2D32f* corners, CvSize pattern_size ); 163 164#if 0 165static void 166icvCalcAffineTranf2D32f(CvPoint2D32f* pts1, CvPoint2D32f* pts2, int count, CvMat* affine_trans) 167{ 168 int i, j; 169 int real_count = 0; 170 for( j = 0; j < count; j++ ) 171 { 172 if( pts1[j].x >= 0 ) real_count++; 173 } 174 if(real_count < 3) return; 175 CvMat* xy = cvCreateMat( 2*real_count, 6, CV_32FC1 ); 176 CvMat* uv = cvCreateMat( 2*real_count, 1, CV_32FC1 ); 177 //estimate affine transfromation 178 for( i = 0, j = 0; j < count; j++ ) 179 { 180 if( pts1[j].x >= 0 ) 181 { 182 CV_MAT_ELEM( *xy, float, i*2+1, 2 ) = CV_MAT_ELEM( *xy, float, i*2, 0 ) = pts2[j].x; 183 CV_MAT_ELEM( *xy, float, i*2+1, 3 ) = CV_MAT_ELEM( *xy, float, i*2, 1 ) = pts2[j].y; 184 CV_MAT_ELEM( *xy, float, i*2, 2 ) = CV_MAT_ELEM( *xy, float, i*2, 3 ) = CV_MAT_ELEM( *xy, float, i*2, 5 ) = \ 185 CV_MAT_ELEM( *xy, float, i*2+1, 0 ) = CV_MAT_ELEM( *xy, float, i*2+1, 1 ) = CV_MAT_ELEM( *xy, float, i*2+1, 4 ) = 0; 186 CV_MAT_ELEM( *xy, float, i*2, 4 ) = CV_MAT_ELEM( *xy, float, i*2+1, 5 ) = 1; 187 CV_MAT_ELEM( *uv, float, i*2, 0 ) = pts1[j].x; 188 CV_MAT_ELEM( *uv, float, i*2+1, 0 ) = pts1[j].y; 189 i++; 190 } 191 } 192 193 cvSolve( xy, uv, affine_trans, CV_SVD ); 194 cvReleaseMat(&xy); 195 cvReleaseMat(&uv); 196} 197#endif 198 199CV_IMPL 200int cvFindChessboardCorners( const void* arr, CvSize pattern_size, 201 CvPoint2D32f* out_corners, int* out_corner_count, 202 int flags ) 203{ 204 int k = 0; 205 const int min_dilations = 0; 206 const int max_dilations = 3; 207 int found = 0; 208 CvMat* norm_img = 0; 209 CvMat* thresh_img = 0; 210#ifdef DEBUG_CHESSBOARD 211 IplImage *dbg_img = 0; 212 IplImage *dbg1_img = 0; 213 IplImage *dbg2_img = 0; 214#endif 215 CvMemStorage* storage = 0; 216 217 CvCBQuad *quads = 0, **quad_group = 0; 218 CvCBCorner *corners = 0, **corner_group = 0; 219 220 int expected_corners_num = (pattern_size.width/2+1)*(pattern_size.height/2+1); 221 222 if( out_corner_count ) 223 *out_corner_count = 0; 224 225 CV_FUNCNAME( "cvFindChessBoardCornerGuesses2" ); 226 227 __BEGIN__; 228 229 int quad_count = 0, group_idx = 0, i = 0, dilations = 0; 230 CvMat stub, *img = (CvMat*)arr; 231 232 CV_CALL( img = cvGetMat( img, &stub )); 233 //debug_img = img; 234 235 if( CV_MAT_DEPTH( img->type ) != CV_8U || CV_MAT_CN( img->type ) == 2 ) 236 CV_ERROR( CV_StsUnsupportedFormat, "Only 8-bit grayscale or color images are supported" ); 237 238 if( pattern_size.width <= 2 || pattern_size.height <= 2 ) 239 CV_ERROR( CV_StsOutOfRange, "Both width and height of the pattern should have bigger than 2" ); 240 241 if( !out_corners ) 242 CV_ERROR( CV_StsNullPtr, "Null pointer to corners" ); 243 244 CV_CALL( storage = cvCreateMemStorage(0) ); 245 CV_CALL( thresh_img = cvCreateMat( img->rows, img->cols, CV_8UC1 )); 246 247#ifdef DEBUG_CHESSBOARD 248 CV_CALL( dbg_img = cvCreateImage(cvGetSize(img), IPL_DEPTH_8U, 3 )); 249 CV_CALL( dbg1_img = cvCreateImage(cvGetSize(img), IPL_DEPTH_8U, 3 )); 250 CV_CALL( dbg2_img = cvCreateImage(cvGetSize(img), IPL_DEPTH_8U, 3 )); 251#endif 252 253 if( CV_MAT_CN(img->type) != 1 || (flags & CV_CALIB_CB_NORMALIZE_IMAGE) ) 254 { 255 // equalize the input image histogram - 256 // that should make the contrast between "black" and "white" areas big enough 257 CV_CALL( norm_img = cvCreateMat( img->rows, img->cols, CV_8UC1 )); 258 259 if( CV_MAT_CN(img->type) != 1 ) 260 { 261 CV_CALL( cvCvtColor( img, norm_img, CV_BGR2GRAY )); 262 img = norm_img; 263 } 264 265 if( flags & CV_CALIB_CB_NORMALIZE_IMAGE ) 266 { 267 cvEqualizeHist( img, norm_img ); 268 img = norm_img; 269 } 270 } 271 272 // Try our standard "1" dilation, but if the pattern is not found, iterate the whole procedure with higher dilations. 273 // This is necessary because some squares simply do not separate properly with a single dilation. However, 274 // we want to use the minimum number of dilations possible since dilations cause the squares to become smaller, 275 // making it difficult to detect smaller squares. 276 for( k = 0; k < 2; k++ ) 277 { 278 for( dilations = min_dilations; dilations <= max_dilations; dilations++ ) 279 { 280 if (found) break; // already found it 281 282 if( k == 1 ) 283 { 284 //Pattern was not found using binarization 285 // Run multi-level quads extraction 286 // In case one-level binarization did not give enough number of quads 287 CV_CALL( quad_count = icvGenerateQuadsEx( &quads, &corners, storage, img, thresh_img, dilations, flags )); 288 PRINTF("EX quad count: %d/%d\n", quad_count, expected_corners_num); 289 } 290 else 291 { 292 // convert the input grayscale image to binary (black-n-white) 293 if( flags & CV_CALIB_CB_ADAPTIVE_THRESH ) 294 { 295 int block_size = cvRound(MIN(img->cols,img->rows)*0.2)|1; 296 297 // convert to binary 298 cvAdaptiveThreshold( img, thresh_img, 255, 299 CV_ADAPTIVE_THRESH_MEAN_C, CV_THRESH_BINARY, block_size, 0 ); 300 if (dilations > 0) 301 cvDilate( thresh_img, thresh_img, 0, dilations-1 ); 302 } 303 else 304 { 305 // Make dilation before the thresholding. 306 // It splits chessboard corners 307 //cvDilate( img, thresh_img, 0, 1 ); 308 309 // empiric threshold level 310 double mean = cvMean( img ); 311 int thresh_level = cvRound( mean - 10 ); 312 thresh_level = MAX( thresh_level, 10 ); 313 314 cvThreshold( img, thresh_img, thresh_level, 255, CV_THRESH_BINARY ); 315 cvDilate( thresh_img, thresh_img, 0, dilations ); 316 } 317 318 319 320#ifdef DEBUG_CHESSBOARD 321 cvCvtColor(thresh_img,dbg_img,CV_GRAY2BGR); 322#endif 323 324 // So we can find rectangles that go to the edge, we draw a white line around the image edge. 325 // Otherwise FindContours will miss those clipped rectangle contours. 326 // The border color will be the image mean, because otherwise we risk screwing up filters like cvSmooth()... 327 cvRectangle( thresh_img, cvPoint(0,0), cvPoint(thresh_img->cols-1, 328 thresh_img->rows-1), CV_RGB(255,255,255), 3, 8); 329 330 CV_CALL( quad_count = icvGenerateQuads( &quads, &corners, storage, thresh_img, flags )); 331 332 333 PRINTF("Quad count: %d/%d\n", quad_count, expected_corners_num); 334 } 335 336 337#ifdef DEBUG_CHESSBOARD 338 cvCopy(dbg_img, dbg1_img); 339 cvNamedWindow("all_quads", 1); 340 // copy corners to temp array 341 for( i = 0; i < quad_count; i++ ) 342 { 343 for (int k=0; k<4; k++) 344 { 345 CvPoint2D32f pt1, pt2; 346 CvScalar color = CV_RGB(30,255,30); 347 pt1 = quads[i].corners[k]->pt; 348 pt2 = quads[i].corners[(k+1)%4]->pt; 349 pt2.x = (pt1.x + pt2.x)/2; 350 pt2.y = (pt1.y + pt2.y)/2; 351 if (k>0) 352 color = CV_RGB(200,200,0); 353 cvLine( dbg1_img, cvPointFrom32f(pt1), cvPointFrom32f(pt2), color, 3, 8); 354 } 355 } 356 357 358// cvShowImage("all_quads", (IplImage*)dbg1_img); 359 cvWaitKey(); 360#endif 361 362 363 if( quad_count <= 0 ) 364 continue; 365 366 // Find quad's neighbors 367 CV_CALL( icvFindQuadNeighbors( quads, quad_count )); 368 369 // allocate extra for adding in icvOrderFoundQuads 370 CV_CALL( quad_group = (CvCBQuad**)cvAlloc( sizeof(quad_group[0]) * (quad_count+quad_count / 2))); 371 CV_CALL( corner_group = (CvCBCorner**)cvAlloc( sizeof(corner_group[0]) * (quad_count+quad_count / 2)*4 )); 372 373 for( group_idx = 0; ; group_idx++ ) 374 { 375 int count = 0; 376 CV_CALL( count = icvFindConnectedQuads( quads, quad_count, quad_group, group_idx, storage )); 377 378 int icount = count; 379 if( count == 0 ) 380 break; 381 382 // order the quad corners globally 383 // maybe delete or add some 384 PRINTF("Starting ordering of inner quads\n"); 385 count = icvOrderFoundConnectedQuads(count, quad_group, &quad_count, &quads, &corners, 386 pattern_size, storage ); 387 PRINTF("Orig count: %d After ordering: %d\n", icount, count); 388 389 390#ifdef DEBUG_CHESSBOARD 391 cvCopy(dbg_img,dbg2_img); 392 cvNamedWindow("connected_group", 1); 393 // copy corners to temp array 394 for( i = 0; i < quad_count; i++ ) 395 { 396 if (quads[i].group_idx == group_idx) 397 for (int k=0; k<4; k++) 398 { 399 CvPoint2D32f pt1, pt2; 400 CvScalar color = CV_RGB(30,255,30); 401 if (quads[i].ordered) 402 color = CV_RGB(255,30,30); 403 pt1 = quads[i].corners[k]->pt; 404 pt2 = quads[i].corners[(k+1)%4]->pt; 405 pt2.x = (pt1.x + pt2.x)/2; 406 pt2.y = (pt1.y + pt2.y)/2; 407 if (k>0) 408 color = CV_RGB(200,200,0); 409 cvLine( dbg2_img, cvPointFrom32f(pt1), cvPointFrom32f(pt2), color, 3, 8); 410 } 411 } 412// cvShowImage("connected_group", (IplImage*)dbg2_img); 413 cvWaitKey(); 414#endif 415 416 if (count == 0) 417 continue; // haven't found inner quads 418 419 420 // If count is more than it should be, this will remove those quads 421 // which cause maximum deviation from a nice square pattern. 422 CV_CALL( count = icvCleanFoundConnectedQuads( count, quad_group, pattern_size )); 423 PRINTF("Connected group: %d orig count: %d cleaned: %d\n", group_idx, icount, count); 424 425 CV_CALL( count = icvCheckQuadGroup( quad_group, count, corner_group, pattern_size )); 426 PRINTF("Connected group: %d count: %d cleaned: %d\n", group_idx, icount, count); 427 428 if( count > 0 || (out_corner_count && -count > *out_corner_count) ) 429 { 430 int n = count > 0 ? pattern_size.width * pattern_size.height : -count; 431 n = MIN( n, pattern_size.width * pattern_size.height ); 432 433 // copy corners to output array 434 for( i = 0; i < n; i++ ) 435 out_corners[i] = corner_group[i]->pt; 436 437 if( out_corner_count ) 438 *out_corner_count = n; 439 440 if( count > 0 ) 441 { 442 found = 1; 443 break; 444 } 445 } 446 } 447 448 cvFree( &quads ); 449 cvFree( &corners ); 450 cvFree( &quad_group ); 451 cvFree( &corner_group ); 452 }//dilations 453 }// 454 455 456 __END__; 457 458 cvReleaseMemStorage( &storage ); 459 cvReleaseMat( &norm_img ); 460 cvReleaseMat( &thresh_img ); 461 cvFree( &quads ); 462 cvFree( &corners ); 463 464 if( found ) 465 found = icvCheckBoardMonotony( out_corners, pattern_size ); 466 467 if( found && pattern_size.height % 2 == 0 && pattern_size.width % 2 == 0 ) 468 { 469 int last_row = (pattern_size.height-1)*pattern_size.width; 470 double dy0 = out_corners[last_row].y - out_corners[0].y; 471 if( dy0 < 0 ) 472 { 473 int i, n = pattern_size.width*pattern_size.height; 474 for( i = 0; i < n/2; i++ ) 475 { 476 CvPoint2D32f temp; 477 CV_SWAP(out_corners[i], out_corners[n-i-1], temp); 478 } 479 } 480 } 481 482 return found; 483} 484 485// 486// Checks that each board row and column is pretty much monotonous curve: 487// It analyzes each row and each column of the chessboard as following: 488// for each corner c lying between end points in the same row/column it checks that 489// the point projection to the line segment (a,b) is lying between projections 490// of the neighbor corners in the same row/column. 491// 492// This function has been created as temporary workaround for the bug in current implementation 493// of cvFindChessboardCornes that produces absolutely unordered sets of corners. 494// 495 496static int 497icvCheckBoardMonotony( CvPoint2D32f* corners, CvSize pattern_size ) 498{ 499 int i, j, k; 500 501 for( k = 0; k < 2; k++ ) 502 { 503 for( i = 0; i < (k == 0 ? pattern_size.height : pattern_size.width); i++ ) 504 { 505 CvPoint2D32f a = k == 0 ? corners[i*pattern_size.width] : corners[i]; 506 CvPoint2D32f b = k == 0 ? corners[(i+1)*pattern_size.width-1] : 507 corners[(pattern_size.height-1)*pattern_size.width + i]; 508 float prevt = 0, dx0 = b.x - a.x, dy0 = b.y - a.y; 509 if( fabs(dx0) + fabs(dy0) < FLT_EPSILON ) 510 return 0; 511 for( j = 1; j < (k == 0 ? pattern_size.width : pattern_size.height) - 1; j++ ) 512 { 513 CvPoint2D32f c = k == 0 ? corners[i*pattern_size.width + j] : 514 corners[j*pattern_size.width + i]; 515 float t = ((c.x - a.x)*dx0 + (c.y - a.y)*dy0)/(dx0*dx0 + dy0*dy0); 516 if( t < prevt || t > 1 ) 517 return 0; 518 prevt = t; 519 } 520 } 521 } 522 523 return 1; 524} 525 526// 527// order a group of connected quads 528// order of corners: 529// 0 is top left 530// clockwise from there 531// note: "top left" is nominal, depends on initial ordering of starting quad 532// but all other quads are ordered consistently 533// 534// can change the number of quads in the group 535// can add quads, so we need to have quad/corner arrays passed in 536// 537 538static int 539icvOrderFoundConnectedQuads( int quad_count, CvCBQuad **quads, 540 int *all_count, CvCBQuad **all_quads, CvCBCorner **corners, 541 CvSize pattern_size, CvMemStorage* storage ) 542{ 543 CvMemStorage* temp_storage = cvCreateChildMemStorage( storage ); 544 CvSeq* stack = cvCreateSeq( 0, sizeof(*stack), sizeof(void*), temp_storage ); 545 int i; 546 547 // first find an interior quad 548 CvCBQuad *start = NULL; 549 for (i=0; i<quad_count; i++) 550 { 551 if (quads[i]->count == 4) 552 { 553 start = quads[i]; 554 break; 555 } 556 } 557 558 if (start == NULL) 559 { 560 cvReleaseMemStorage( &temp_storage ); 561 return 0; // no 4-connected quad 562 } 563 564 // start with first one, assign rows/cols 565 int row_min = 0, col_min = 0, row_max=0, col_max = 0; 566#define HSIZE 20 567 int col_hist[HSIZE*2]; 568 int row_hist[HSIZE*2]; // bad programming, should allow variable size 569 570 for (i=0; i<HSIZE*2; i++) // init to zero 571 col_hist[i] = row_hist[i] = 0; 572 cvSeqPush(stack, &start); 573 start->row = 0; 574 start->col = 0; 575 start->ordered = true; 576 577 // Recursively order the quads so that all position numbers (e.g., 578 // 0,1,2,3) are in the at the same relative corner (e.g., lower right). 579 580 while( stack->total ) 581 { 582 CvCBQuad* q; 583 cvSeqPop( stack, &q ); 584 int col = q->col; 585 int row = q->row; 586 col_hist[col+HSIZE]++; 587 row_hist[row+HSIZE]++; 588 589 // check min/max 590 if (row > row_max) row_max = row; 591 if (row < row_min) row_min = row; 592 if (col > col_max) col_max = col; 593 if (col < col_min) col_min = col; 594 595 for(int i = 0; i < 4; i++ ) 596 { 597 CvCBQuad *neighbor = q->neighbors[i]; 598 switch(i) // adjust col, row for this quad 599 { // start at top left, go clockwise 600 case 0: 601 row--; col--; break; 602 case 1: 603 col += 2; break; 604 case 2: 605 row += 2; break; 606 case 3: 607 col -= 2; break; 608 } 609 610 // just do inside quads 611 if (neighbor && neighbor->ordered == false && neighbor->count == 4) 612 { 613 PRINTF("col: %d row: %d\n", col, row); 614 icvOrderQuad(neighbor, q->corners[i], (i+2)%4); // set in order 615 neighbor->ordered = true; 616 neighbor->row = row; 617 neighbor->col = col; 618 cvSeqPush( stack, &neighbor ); 619 } 620 } 621 } 622 623 cvReleaseMemStorage( &temp_storage ); 624 625 for (i=col_min; i<=col_max; i++) 626 PRINTF("HIST[%d] = %d\n", i, col_hist[i+HSIZE]); 627 628 // analyze inner quad structure 629 int w = pattern_size.width - 1; 630 int h = pattern_size.height - 1; 631 int drow = row_max - row_min + 1; 632 int dcol = col_max - col_min + 1; 633 634 // normalize pattern and found quad indices 635 if ((w > h && dcol < drow) || 636 (w < h && drow < dcol)) 637 { 638 h = pattern_size.width - 1; 639 w = pattern_size.height - 1; 640 } 641 642 PRINTF("Size: %dx%d Pattern: %dx%d\n", dcol, drow, w, h); 643 644 // check if there are enough inner quads 645 if (dcol < w || drow < h) // found enough inner quads? 646 { 647 PRINTF("Too few inner quad rows/cols\n"); 648 return 0; // no, return 649 } 650 651 // too many columns, not very common 652 if (dcol == w+1) // too many, trim 653 { 654 PRINTF("Trimming cols\n"); 655 if (col_hist[col_max+HSIZE] > col_hist[col_min+HSIZE]) 656 { 657 PRINTF("Trimming left col\n"); 658 quad_count = icvTrimCol(quads,quad_count,col_min,-1); 659 } 660 else 661 { 662 PRINTF("Trimming right col\n"); 663 quad_count = icvTrimCol(quads,quad_count,col_max,+1); 664 } 665 } 666 667 // too many rows, not very common 668 if (drow == h+1) // too many, trim 669 { 670 PRINTF("Trimming rows\n"); 671 if (row_hist[row_max+HSIZE] > row_hist[row_min+HSIZE]) 672 { 673 PRINTF("Trimming top row\n"); 674 quad_count = icvTrimRow(quads,quad_count,row_min,-1); 675 } 676 else 677 { 678 PRINTF("Trimming bottom row\n"); 679 quad_count = icvTrimRow(quads,quad_count,row_max,+1); 680 } 681 } 682 683 684 // check edges of inner quads 685 // if there is an outer quad missing, fill it in 686 // first order all inner quads 687 int found = 0; 688 for (i=0; i<quad_count; i++) 689 { 690 if (quads[i]->count == 4) 691 { // ok, look at neighbors 692 int col = quads[i]->col; 693 int row = quads[i]->row; 694 for (int j=0; j<4; j++) 695 { 696 switch(j) // adjust col, row for this quad 697 { // start at top left, go clockwise 698 case 0: 699 row--; col--; break; 700 case 1: 701 col += 2; break; 702 case 2: 703 row += 2; break; 704 case 3: 705 col -= 2; break; 706 } 707 CvCBQuad *neighbor = quads[i]->neighbors[j]; 708 if (neighbor && !neighbor->ordered && // is it an inner quad? 709 col <= col_max && col >= col_min && 710 row <= row_max && row >= row_min) 711 { 712 // if so, set in order 713 PRINTF("Adding inner: col: %d row: %d\n", col, row); 714 found++; 715 icvOrderQuad(neighbor, quads[i]->corners[j], (j+2)%4); 716 neighbor->ordered = true; 717 neighbor->row = row; 718 neighbor->col = col; 719 } 720 } 721 } 722 } 723 724 // if we have found inner quads, add corresponding outer quads, 725 // which are missing 726 if (found > 0) 727 { 728 PRINTF("Found %d inner quads not connected to outer quads, repairing\n", found); 729 for (int i=0; i<quad_count; i++) 730 { 731 if (quads[i]->count < 4 && quads[i]->ordered) 732 { 733 int added = icvAddOuterQuad(quads[i],quads,quad_count,all_quads,*all_count,corners); 734 *all_count += added; 735 quad_count += added; 736 } 737 } 738 } 739 740 741 // final trimming of outer quads 742 if (dcol == w && drow == h) // found correct inner quads 743 { 744 PRINTF("Inner bounds ok, check outer quads\n"); 745 int rcount = quad_count; 746 for (int i=quad_count-1; i>=0; i--) // eliminate any quad not connected to 747 // an ordered quad 748 { 749 if (quads[i]->ordered == false) 750 { 751 bool outer = false; 752 for (int j=0; j<4; j++) // any neighbors that are ordered? 753 { 754 if (quads[i]->neighbors[j] && quads[i]->neighbors[j]->ordered) 755 outer = true; 756 } 757 if (!outer) // not an outer quad, eliminate 758 { 759 PRINTF("Removing quad %d\n", i); 760 icvRemoveQuadFromGroup(quads,rcount,quads[i]); 761 rcount--; 762 } 763 } 764 765 } 766 return rcount; 767 } 768 769 return 0; 770} 771 772 773// add an outer quad 774// looks for the neighbor of <quad> that isn't present, 775// tries to add it in. 776// <quad> is ordered 777 778static int 779icvAddOuterQuad( CvCBQuad *quad, CvCBQuad **quads, int quad_count, 780 CvCBQuad **all_quads, int all_count, CvCBCorner **corners ) 781 782{ 783 int added = 0; 784 for (int i=0; i<4; i++) // find no-neighbor corners 785 { 786 if (!quad->neighbors[i]) // ok, create and add neighbor 787 { 788 int j = (i+2)%4; 789 PRINTF("Adding quad as neighbor 2\n"); 790 CvCBQuad *q = &(*all_quads)[all_count]; 791 memset( q, 0, sizeof(*q) ); 792 added++; 793 quads[quad_count] = q; 794 quad_count++; 795 796 // set neighbor and group id 797 quad->neighbors[i] = q; 798 quad->count += 1; 799 q->neighbors[j] = quad; 800 q->group_idx = quad->group_idx; 801 q->count = 1; // number of neighbors 802 q->ordered = false; 803 q->edge_len = quad->edge_len; 804 805 // make corners of new quad 806 // same as neighbor quad, but offset 807 CvPoint2D32f pt = quad->corners[i]->pt; 808 CvCBCorner* corner; 809 float dx = pt.x - quad->corners[j]->pt.x; 810 float dy = pt.y - quad->corners[j]->pt.y; 811 for (int k=0; k<4; k++) 812 { 813 corner = &(*corners)[all_count*4+k]; 814 pt = quad->corners[k]->pt; 815 memset( corner, 0, sizeof(*corner) ); 816 corner->pt = pt; 817 q->corners[k] = corner; 818 corner->pt.x += dx; 819 corner->pt.y += dy; 820 } 821 // have to set exact corner 822 q->corners[j] = quad->corners[i]; 823 824 // now find other neighbor and add it, if possible 825 if (quad->neighbors[(i+3)%4] && 826 quad->neighbors[(i+3)%4]->ordered && 827 quad->neighbors[(i+3)%4]->neighbors[i] && 828 quad->neighbors[(i+3)%4]->neighbors[i]->ordered ) 829 { 830 CvCBQuad *qn = quad->neighbors[(i+3)%4]->neighbors[i]; 831 q->count = 2; 832 q->neighbors[(j+1)%4] = qn; 833 qn->neighbors[(i+1)%4] = q; 834 qn->count += 1; 835 // have to set exact corner 836 q->corners[(j+1)%4] = qn->corners[(i+1)%4]; 837 } 838 839 all_count++; 840 } 841 } 842 return added; 843} 844 845 846// trimming routines 847 848static int 849icvTrimCol(CvCBQuad **quads, int count, int col, int dir) 850{ 851 int rcount = count; 852 // find the right quad(s) 853 for (int i=0; i<count; i++) 854 { 855#ifdef DEBUG_CHESSBOARD 856 if (quads[i]->ordered) 857 PRINTF("index: %d cur: %d\n", col, quads[i]->col); 858#endif 859 if (quads[i]->ordered && quads[i]->col == col) 860 { 861 if (dir == 1) 862 { 863 if (quads[i]->neighbors[1]) 864 { 865 icvRemoveQuadFromGroup(quads,rcount,quads[i]->neighbors[1]); 866 rcount--; 867 } 868 if (quads[i]->neighbors[2]) 869 { 870 icvRemoveQuadFromGroup(quads,rcount,quads[i]->neighbors[2]); 871 rcount--; 872 } 873 } 874 else 875 { 876 if (quads[i]->neighbors[0]) 877 { 878 icvRemoveQuadFromGroup(quads,rcount,quads[i]->neighbors[0]); 879 rcount--; 880 } 881 if (quads[i]->neighbors[3]) 882 { 883 icvRemoveQuadFromGroup(quads,rcount,quads[i]->neighbors[3]); 884 rcount--; 885 } 886 } 887 888 } 889 } 890 return rcount; 891} 892 893static int 894icvTrimRow(CvCBQuad **quads, int count, int row, int dir) 895{ 896 int i, rcount = count; 897 // find the right quad(s) 898 for (i=0; i<count; i++) 899 { 900#ifdef DEBUG_CHESSBOARD 901 if (quads[i]->ordered) 902 PRINTF("index: %d cur: %d\n", row, quads[i]->row); 903#endif 904 if (quads[i]->ordered && quads[i]->row == row) 905 { 906 if (dir == 1) // remove from bottom 907 { 908 if (quads[i]->neighbors[2]) 909 { 910 icvRemoveQuadFromGroup(quads,rcount,quads[i]->neighbors[2]); 911 rcount--; 912 } 913 if (quads[i]->neighbors[3]) 914 { 915 icvRemoveQuadFromGroup(quads,rcount,quads[i]->neighbors[3]); 916 rcount--; 917 } 918 } 919 else // remove from top 920 { 921 if (quads[i]->neighbors[0]) 922 { 923 icvRemoveQuadFromGroup(quads,rcount,quads[i]->neighbors[0]); 924 rcount--; 925 } 926 if (quads[i]->neighbors[1]) 927 { 928 icvRemoveQuadFromGroup(quads,rcount,quads[i]->neighbors[1]); 929 rcount--; 930 } 931 } 932 933 } 934 } 935 return rcount; 936} 937 938 939// 940// remove quad from quad group 941// 942 943static void 944icvRemoveQuadFromGroup(CvCBQuad **quads, int count, CvCBQuad *q0) 945{ 946 int i, j; 947 // remove any references to this quad as a neighbor 948 for(i = 0; i < count; i++ ) 949 { 950 CvCBQuad *q = quads[i]; 951 for(j = 0; j < 4; j++ ) 952 { 953 if( q->neighbors[j] == q0 ) 954 { 955 q->neighbors[j] = 0; 956 q->count--; 957 for(int k = 0; k < 4; k++ ) 958 if( q0->neighbors[k] == q ) 959 { 960 q0->neighbors[k] = 0; 961 q0->count--; 962 break; 963 } 964 break; 965 } 966 } 967 } 968 969 // remove the quad 970 for(i = 0; i < count; i++ ) 971 { 972 CvCBQuad *q = quads[i]; 973 if (q == q0) 974 { 975 quads[i] = quads[count-1]; 976 break; 977 } 978 } 979} 980 981// 982// put quad into correct order, where <corner> has value <common> 983// 984 985static void 986icvOrderQuad(CvCBQuad *quad, CvCBCorner *corner, int common) 987{ 988 // find the corner 989 int tc; 990 for (tc=0; tc<4; tc++) 991 if (quad->corners[tc]->pt.x == corner->pt.x && 992 quad->corners[tc]->pt.y == corner->pt.y) 993 break; 994 995 // set corner order 996 // shift 997 while (tc != common) 998 { 999 // shift by one 1000 CvCBCorner *tempc; 1001 CvCBQuad *tempq; 1002 tempc = quad->corners[3]; 1003 tempq = quad->neighbors[3]; 1004 for (int i=3; i>0; i--) 1005 { 1006 quad->corners[i] = quad->corners[i-1]; 1007 quad->neighbors[i] = quad->neighbors[i-1]; 1008 } 1009 quad->corners[0] = tempc; 1010 quad->neighbors[0] = tempq; 1011 tc++; 1012 tc = tc%4; 1013 } 1014} 1015 1016 1017// if we found too many connect quads, remove those which probably do not belong. 1018static int 1019icvCleanFoundConnectedQuads( int quad_count, CvCBQuad **quad_group, CvSize pattern_size ) 1020{ 1021 CvMemStorage *temp_storage = 0; 1022 CvPoint2D32f *centers = 0; 1023 1024 CV_FUNCNAME( "icvCleanFoundConnectedQuads" ); 1025 1026 __BEGIN__; 1027 1028 CvPoint2D32f center = {0,0}; 1029 int i, j, k; 1030 // number of quads this pattern should contain 1031 int count = ((pattern_size.width + 1)*(pattern_size.height + 1) + 1)/2; 1032 1033 // if we have more quadrangles than we should, 1034 // try to eliminate duplicates or ones which don't belong to the pattern rectangle... 1035 if( quad_count <= count ) 1036 EXIT; 1037 1038 // create an array of quadrangle centers 1039 CV_CALL( centers = (CvPoint2D32f *)cvAlloc( sizeof(centers[0])*quad_count )); 1040 CV_CALL( temp_storage = cvCreateMemStorage(0)); 1041 1042 for( i = 0; i < quad_count; i++ ) 1043 { 1044 CvPoint2D32f ci = {0,0}; 1045 CvCBQuad* q = quad_group[i]; 1046 1047 for( j = 0; j < 4; j++ ) 1048 { 1049 CvPoint2D32f pt = q->corners[j]->pt; 1050 ci.x += pt.x; 1051 ci.y += pt.y; 1052 } 1053 1054 ci.x *= 0.25f; 1055 ci.y *= 0.25f; 1056 1057 centers[i] = ci; 1058 center.x += ci.x; 1059 center.y += ci.y; 1060 } 1061 center.x /= quad_count; 1062 center.y /= quad_count; 1063 1064 // If we still have more quadrangles than we should, 1065 // we try to eliminate bad ones based on minimizing the bounding box. 1066 // We iteratively remove the point which reduces the size of 1067 // the bounding box of the blobs the most 1068 // (since we want the rectangle to be as small as possible) 1069 // remove the quadrange that causes the biggest reduction 1070 // in pattern size until we have the correct number 1071 for( ; quad_count > count; quad_count-- ) 1072 { 1073 double min_box_area = DBL_MAX; 1074 int skip, min_box_area_index = -1; 1075 CvCBQuad *q0, *q; 1076 1077 // For each point, calculate box area without that point 1078 for( skip = 0; skip < quad_count; skip++ ) 1079 { 1080 // get bounding rectangle 1081 CvPoint2D32f temp = centers[skip]; // temporarily make index 'skip' the same as 1082 centers[skip] = center; // pattern center (so it is not counted for convex hull) 1083 CvMat pointMat = cvMat(1, quad_count, CV_32FC2, centers); 1084 CvSeq *hull = cvConvexHull2( &pointMat, temp_storage, CV_CLOCKWISE, 1 ); 1085 centers[skip] = temp; 1086 double hull_area = fabs(cvContourArea(hull, CV_WHOLE_SEQ)); 1087 1088 // remember smallest box area 1089 if( hull_area < min_box_area ) 1090 { 1091 min_box_area = hull_area; 1092 min_box_area_index = skip; 1093 } 1094 cvClearMemStorage( temp_storage ); 1095 } 1096 1097 q0 = quad_group[min_box_area_index]; 1098 1099 // remove any references to this quad as a neighbor 1100 for( i = 0; i < quad_count; i++ ) 1101 { 1102 q = quad_group[i]; 1103 for( j = 0; j < 4; j++ ) 1104 { 1105 if( q->neighbors[j] == q0 ) 1106 { 1107 q->neighbors[j] = 0; 1108 q->count--; 1109 for( k = 0; k < 4; k++ ) 1110 if( q0->neighbors[k] == q ) 1111 { 1112 q0->neighbors[k] = 0; 1113 q0->count--; 1114 break; 1115 } 1116 break; 1117 } 1118 } 1119 } 1120 1121 // remove the quad 1122 quad_count--; 1123 quad_group[min_box_area_index] = quad_group[quad_count]; 1124 centers[min_box_area_index] = centers[quad_count]; 1125 } 1126 1127 __END__; 1128 1129 cvReleaseMemStorage( &temp_storage ); 1130 cvFree( ¢ers ); 1131 1132 return quad_count; 1133} 1134 1135//===================================================================================== 1136 1137static int 1138icvFindConnectedQuads( CvCBQuad *quad, int quad_count, CvCBQuad **out_group, 1139 int group_idx, CvMemStorage* storage ) 1140{ 1141 CvMemStorage* temp_storage = cvCreateChildMemStorage( storage ); 1142 CvSeq* stack = cvCreateSeq( 0, sizeof(*stack), sizeof(void*), temp_storage ); 1143 int i, count = 0; 1144 1145 // Scan the array for a first unlabeled quad 1146 for( i = 0; i < quad_count; i++ ) 1147 { 1148 if( quad[i].count > 0 && quad[i].group_idx < 0) 1149 break; 1150 } 1151 1152 // Recursively find a group of connected quads starting from the seed quad[i] 1153 if( i < quad_count ) 1154 { 1155 CvCBQuad* q = &quad[i]; 1156 cvSeqPush( stack, &q ); 1157 out_group[count++] = q; 1158 q->group_idx = group_idx; 1159 q->ordered = false; 1160 1161 while( stack->total ) 1162 { 1163 cvSeqPop( stack, &q ); 1164 for( i = 0; i < 4; i++ ) 1165 { 1166 CvCBQuad *neighbor = q->neighbors[i]; 1167 if( neighbor && neighbor->count > 0 && neighbor->group_idx < 0 ) 1168 { 1169 cvSeqPush( stack, &neighbor ); 1170 out_group[count++] = neighbor; 1171 neighbor->group_idx = group_idx; 1172 neighbor->ordered = false; 1173 } 1174 } 1175 } 1176 } 1177 1178 cvReleaseMemStorage( &temp_storage ); 1179 return count; 1180} 1181 1182 1183//===================================================================================== 1184 1185static int 1186icvCheckQuadGroup( CvCBQuad **quad_group, int quad_count, 1187 CvCBCorner **out_corners, CvSize pattern_size ) 1188{ 1189 const int ROW1 = 1000000; 1190 const int ROW2 = 2000000; 1191 const int ROW_ = 3000000; 1192 int result = 0; 1193 int i, out_corner_count = 0, corner_count = 0; 1194 CvCBCorner** corners = 0; 1195 1196 CV_FUNCNAME( "icvCheckQuadGroup" ); 1197 1198 __BEGIN__; 1199 1200 int j, k, kk; 1201 int width = 0, height = 0; 1202 int hist[5] = {0,0,0,0,0}; 1203 CvCBCorner* first = 0, *first2 = 0, *right, *cur, *below, *c; 1204 CV_CALL( corners = (CvCBCorner**)cvAlloc( quad_count*4*sizeof(corners[0]) )); 1205 1206 // build dual graph, which vertices are internal quad corners 1207 // and two vertices are connected iff they lie on the same quad edge 1208 for( i = 0; i < quad_count; i++ ) 1209 { 1210 CvCBQuad* q = quad_group[i]; 1211 /*CvScalar color = q->count == 0 ? cvScalar(0,255,255) : 1212 q->count == 1 ? cvScalar(0,0,255) : 1213 q->count == 2 ? cvScalar(0,255,0) : 1214 q->count == 3 ? cvScalar(255,255,0) : 1215 cvScalar(255,0,0);*/ 1216 1217 for( j = 0; j < 4; j++ ) 1218 { 1219 //cvLine( debug_img, cvPointFrom32f(q->corners[j]->pt), cvPointFrom32f(q->corners[(j+1)&3]->pt), color, 1, CV_AA, 0 ); 1220 if( q->neighbors[j] ) 1221 { 1222 CvCBCorner *a = q->corners[j], *b = q->corners[(j+1)&3]; 1223 // mark internal corners that belong to: 1224 // - a quad with a single neighbor - with ROW1, 1225 // - a quad with two neighbors - with ROW2 1226 // make the rest of internal corners with ROW_ 1227 int row_flag = q->count == 1 ? ROW1 : q->count == 2 ? ROW2 : ROW_; 1228 1229 if( a->row == 0 ) 1230 { 1231 corners[corner_count++] = a; 1232 a->row = row_flag; 1233 } 1234 else if( a->row > row_flag ) 1235 a->row = row_flag; 1236 1237 if( q->neighbors[(j+1)&3] ) 1238 { 1239 if( a->count >= 4 || b->count >= 4 ) 1240 EXIT; 1241 for( k = 0; k < 4; k++ ) 1242 { 1243 if( a->neighbors[k] == b ) 1244 EXIT; 1245 if( b->neighbors[k] == a ) 1246 EXIT; 1247 } 1248 a->neighbors[a->count++] = b; 1249 b->neighbors[b->count++] = a; 1250 } 1251 } 1252 } 1253 } 1254 1255 if( corner_count != pattern_size.width*pattern_size.height ) 1256 EXIT; 1257 1258 for( i = 0; i < corner_count; i++ ) 1259 { 1260 int n = corners[i]->count; 1261 assert( 0 <= n && n <= 4 ); 1262 hist[n]++; 1263 if( !first && n == 2 ) 1264 { 1265 if( corners[i]->row == ROW1 ) 1266 first = corners[i]; 1267 else if( !first2 && corners[i]->row == ROW2 ) 1268 first2 = corners[i]; 1269 } 1270 } 1271 1272 // start with a corner that belongs to a quad with a signle neighbor. 1273 // if we do not have such, start with a corner of a quad with two neighbors. 1274 if( !first ) 1275 first = first2; 1276 1277 if( !first || hist[0] != 0 || hist[1] != 0 || hist[2] != 4 || 1278 hist[3] != (pattern_size.width + pattern_size.height)*2 - 8 ) 1279 EXIT; 1280 1281 cur = first; 1282 right = below = 0; 1283 out_corners[out_corner_count++] = cur; 1284 1285 for( k = 0; k < 4; k++ ) 1286 { 1287 c = cur->neighbors[k]; 1288 if( c ) 1289 { 1290 if( !right ) 1291 right = c; 1292 else if( !below ) 1293 below = c; 1294 } 1295 } 1296 1297 if( !right || (right->count != 2 && right->count != 3) || 1298 !below || (below->count != 2 && below->count != 3) ) 1299 EXIT; 1300 1301 cur->row = 0; 1302 //cvCircle( debug_img, cvPointFrom32f(cur->pt), 3, cvScalar(0,255,0), -1, 8, 0 ); 1303 1304 first = below; // remember the first corner in the next row 1305 // find and store the first row (or column) 1306 for(j=1;;j++) 1307 { 1308 right->row = 0; 1309 out_corners[out_corner_count++] = right; 1310 //cvCircle( debug_img, cvPointFrom32f(right->pt), 3, cvScalar(0,255-j*10,0), -1, 8, 0 ); 1311 if( right->count == 2 ) 1312 break; 1313 if( right->count != 3 || out_corner_count >= MAX(pattern_size.width,pattern_size.height) ) 1314 EXIT; 1315 cur = right; 1316 for( k = 0; k < 4; k++ ) 1317 { 1318 c = cur->neighbors[k]; 1319 if( c && c->row > 0 ) 1320 { 1321 for( kk = 0; kk < 4; kk++ ) 1322 { 1323 if( c->neighbors[kk] == below ) 1324 break; 1325 } 1326 if( kk < 4 ) 1327 below = c; 1328 else 1329 right = c; 1330 } 1331 } 1332 } 1333 1334 width = out_corner_count; 1335 if( width == pattern_size.width ) 1336 height = pattern_size.height; 1337 else if( width == pattern_size.height ) 1338 height = pattern_size.width; 1339 else 1340 EXIT; 1341 1342 // find and store all the other rows 1343 for( i = 1; ; i++ ) 1344 { 1345 if( !first ) 1346 break; 1347 cur = first; 1348 first = 0; 1349 for( j = 0;; j++ ) 1350 { 1351 cur->row = i; 1352 out_corners[out_corner_count++] = cur; 1353 //cvCircle( debug_img, cvPointFrom32f(cur->pt), 3, cvScalar(0,0,255-j*10), -1, 8, 0 ); 1354 if( cur->count == 2 + (i < height-1) && j > 0 ) 1355 break; 1356 1357 right = 0; 1358 1359 // find a neighbor that has not been processed yet 1360 // and that has a neighbor from the previous row 1361 for( k = 0; k < 4; k++ ) 1362 { 1363 c = cur->neighbors[k]; 1364 if( c && c->row > i ) 1365 { 1366 for( kk = 0; kk < 4; kk++ ) 1367 { 1368 if( c->neighbors[kk] && c->neighbors[kk]->row == i-1 ) 1369 break; 1370 } 1371 if( kk < 4 ) 1372 { 1373 right = c; 1374 if( j > 0 ) 1375 break; 1376 } 1377 else if( j == 0 ) 1378 first = c; 1379 } 1380 } 1381 if( !right ) 1382 EXIT; 1383 cur = right; 1384 } 1385 1386 if( j != width - 1 ) 1387 EXIT; 1388 } 1389 1390 if( out_corner_count != corner_count ) 1391 EXIT; 1392 1393 // check if we need to transpose the board 1394 if( width != pattern_size.width ) 1395 { 1396 CV_SWAP( width, height, k ); 1397 1398 memcpy( corners, out_corners, corner_count*sizeof(corners[0]) ); 1399 for( i = 0; i < height; i++ ) 1400 for( j = 0; j < width; j++ ) 1401 out_corners[i*width + j] = corners[j*height + i]; 1402 } 1403 1404 // check if we need to revert the order in each row 1405 { 1406 CvPoint2D32f p0 = out_corners[0]->pt, p1 = out_corners[pattern_size.width-1]->pt, 1407 p2 = out_corners[pattern_size.width]->pt; 1408 if( (p1.x - p0.x)*(p2.y - p1.y) - (p1.y - p0.y)*(p2.x - p1.x) < 0 ) 1409 { 1410 if( width % 2 == 0 ) 1411 { 1412 for( i = 0; i < height; i++ ) 1413 for( j = 0; j < width/2; j++ ) 1414 CV_SWAP( out_corners[i*width+j], out_corners[i*width+width-j-1], c ); 1415 } 1416 else 1417 { 1418 for( j = 0; j < width; j++ ) 1419 for( i = 0; i < height/2; i++ ) 1420 CV_SWAP( out_corners[i*width+j], out_corners[(height - i - 1)*width+j], c ); 1421 } 1422 } 1423 } 1424 1425 result = corner_count; 1426 1427 __END__; 1428 1429 if( result <= 0 && corners ) 1430 { 1431 corner_count = MIN( corner_count, pattern_size.width*pattern_size.height ); 1432 for( i = 0; i < corner_count; i++ ) 1433 out_corners[i] = corners[i]; 1434 result = -corner_count; 1435 1436 if (result == -pattern_size.width*pattern_size.height) 1437 result = -result; 1438 } 1439 1440 cvFree( &corners ); 1441 1442 return result; 1443} 1444 1445 1446 1447 1448//===================================================================================== 1449 1450static void icvFindQuadNeighbors( CvCBQuad *quads, int quad_count ) 1451{ 1452 const float thresh_scale = 1.f; 1453 int idx, i, k, j; 1454 float dx, dy, dist; 1455 1456 // find quad neighbors 1457 for( idx = 0; idx < quad_count; idx++ ) 1458 { 1459 CvCBQuad* cur_quad = &quads[idx]; 1460 1461 // choose the points of the current quadrangle that are close to 1462 // some points of the other quadrangles 1463 // (it can happen for split corners (due to dilation) of the 1464 // checker board). Search only in other quadrangles! 1465 1466 // for each corner of this quadrangle 1467 for( i = 0; i < 4; i++ ) 1468 { 1469 CvPoint2D32f pt; 1470 float min_dist = FLT_MAX; 1471 int closest_corner_idx = -1; 1472 CvCBQuad *closest_quad = 0; 1473 CvCBCorner *closest_corner = 0; 1474 1475 if( cur_quad->neighbors[i] ) 1476 continue; 1477 1478 pt = cur_quad->corners[i]->pt; 1479 1480 // find the closest corner in all other quadrangles 1481 for( k = 0; k < quad_count; k++ ) 1482 { 1483 if( k == idx ) 1484 continue; 1485 1486 for( j = 0; j < 4; j++ ) 1487 { 1488 if( quads[k].neighbors[j] ) 1489 continue; 1490 1491 dx = pt.x - quads[k].corners[j]->pt.x; 1492 dy = pt.y - quads[k].corners[j]->pt.y; 1493 dist = dx * dx + dy * dy; 1494 1495 if( dist < min_dist && 1496 dist <= cur_quad->edge_len*thresh_scale && 1497 dist <= quads[k].edge_len*thresh_scale ) 1498 { 1499 // check edge lengths, make sure they're compatible 1500 // edges that are different by more than 1:4 are rejected 1501 float ediff = cur_quad->edge_len - quads[k].edge_len; 1502 if (ediff > 32*cur_quad->edge_len || 1503 ediff > 32*quads[k].edge_len) 1504 { 1505 PRINTF("Incompatible edge lengths\n"); 1506 continue; 1507 } 1508 closest_corner_idx = j; 1509 closest_quad = &quads[k]; 1510 min_dist = dist; 1511 } 1512 } 1513 } 1514 1515 // we found a matching corner point? 1516 if( closest_corner_idx >= 0 && min_dist < FLT_MAX ) 1517 { 1518 // If another point from our current quad is closer to the found corner 1519 // than the current one, then we don't count this one after all. 1520 // This is necessary to support small squares where otherwise the wrong 1521 // corner will get matched to closest_quad; 1522 closest_corner = closest_quad->corners[closest_corner_idx]; 1523 1524 for( j = 0; j < 4; j++ ) 1525 { 1526 if( cur_quad->neighbors[j] == closest_quad ) 1527 break; 1528 1529 dx = closest_corner->pt.x - cur_quad->corners[j]->pt.x; 1530 dy = closest_corner->pt.y - cur_quad->corners[j]->pt.y; 1531 1532 if( dx * dx + dy * dy < min_dist ) 1533 break; 1534 } 1535 1536 if( j < 4 || cur_quad->count >= 4 || closest_quad->count >= 4 ) 1537 continue; 1538 1539 // Check that each corner is a neighbor of different quads 1540 for( j = 0; j < closest_quad->count; j++ ) 1541 { 1542 if( closest_quad->neighbors[j] == cur_quad ) 1543 break; 1544 } 1545 if( j < closest_quad->count ) 1546 continue; 1547 1548 // check whether the closest corner to closest_corner 1549 // is different from cur_quad->corners[i]->pt 1550 for( k = 0; k < quad_count; k++ ) 1551 { 1552 CvCBQuad* q = &quads[k]; 1553 if( k == idx || q == closest_quad ) 1554 continue; 1555 1556 for( j = 0; j < 4; j++ ) 1557 if( !q->neighbors[j] ) 1558 { 1559 dx = closest_corner->pt.x - q->corners[j]->pt.x; 1560 dy = closest_corner->pt.y - q->corners[j]->pt.y; 1561 dist = dx*dx + dy*dy; 1562 if( dist < min_dist ) 1563 break; 1564 } 1565 if( j < 4 ) 1566 break; 1567 } 1568 1569 if( k < quad_count ) 1570 continue; 1571 1572 closest_corner->pt.x = (pt.x + closest_corner->pt.x) * 0.5f; 1573 closest_corner->pt.y = (pt.y + closest_corner->pt.y) * 0.5f; 1574 1575 // We've found one more corner - remember it 1576 cur_quad->count++; 1577 cur_quad->neighbors[i] = closest_quad; 1578 cur_quad->corners[i] = closest_corner; 1579 1580 closest_quad->count++; 1581 closest_quad->neighbors[closest_corner_idx] = cur_quad; 1582 } 1583 } 1584 } 1585} 1586 1587//===================================================================================== 1588 1589// returns corners in clockwise order 1590// corners don't necessarily start at same position on quad (e.g., 1591// top left corner) 1592 1593static int 1594icvGenerateQuads( CvCBQuad **out_quads, CvCBCorner **out_corners, 1595 CvMemStorage *storage, CvMat *image, int flags ) 1596{ 1597 int quad_count = 0; 1598 CvMemStorage *temp_storage = 0; 1599 1600 if( out_quads ) 1601 *out_quads = 0; 1602 1603 if( out_corners ) 1604 *out_corners = 0; 1605 1606 CV_FUNCNAME( "icvGenerateQuads" ); 1607 1608 __BEGIN__; 1609 1610 CvSeq *src_contour = 0; 1611 CvSeq *root; 1612 CvContourEx* board = 0; 1613 CvContourScanner scanner; 1614 int i, idx, min_size; 1615 1616 CV_ASSERT( out_corners && out_quads ); 1617 1618 // empiric bound for minimal allowed perimeter for squares 1619 min_size = cvRound( image->cols * image->rows * .03 * 0.01 * 0.92 ); 1620 1621 // create temporary storage for contours and the sequence of pointers to found quadrangles 1622 CV_CALL( temp_storage = cvCreateChildMemStorage( storage )); 1623 CV_CALL( root = cvCreateSeq( 0, sizeof(CvSeq), sizeof(CvSeq*), temp_storage )); 1624 1625 // initialize contour retrieving routine 1626 CV_CALL( scanner = cvStartFindContours( image, temp_storage, sizeof(CvContourEx), 1627 CV_RETR_CCOMP, CV_CHAIN_APPROX_SIMPLE )); 1628 1629 // get all the contours one by one 1630 while( (src_contour = cvFindNextContour( scanner )) != 0 ) 1631 { 1632 CvSeq *dst_contour = 0; 1633 CvRect rect = ((CvContour*)src_contour)->rect; 1634 1635 // reject contours with too small perimeter 1636 if( CV_IS_SEQ_HOLE(src_contour) && rect.width*rect.height >= min_size ) 1637 { 1638 const int min_approx_level = 2, max_approx_level = MAX_CONTOUR_APPROX; 1639 int approx_level; 1640 for( approx_level = min_approx_level; approx_level <= max_approx_level; approx_level++ ) 1641 { 1642 dst_contour = cvApproxPoly( src_contour, sizeof(CvContour), temp_storage, 1643 CV_POLY_APPROX_DP, (float)approx_level ); 1644 // we call this again on its own output, because sometimes 1645 // cvApproxPoly() does not simplify as much as it should. 1646 dst_contour = cvApproxPoly( dst_contour, sizeof(CvContour), temp_storage, 1647 CV_POLY_APPROX_DP, (float)approx_level ); 1648 1649 if( dst_contour->total == 4 ) 1650 break; 1651 } 1652 1653 // reject non-quadrangles 1654 if( dst_contour->total == 4 && cvCheckContourConvexity(dst_contour) ) 1655 { 1656 CvPoint pt[4]; 1657 double d1, d2, p = cvContourPerimeter(dst_contour); 1658 double area = fabs(cvContourArea(dst_contour, CV_WHOLE_SEQ)); 1659 double dx, dy; 1660 1661 for( i = 0; i < 4; i++ ) 1662 pt[i] = *(CvPoint*)cvGetSeqElem(dst_contour, i); 1663 1664 dx = pt[0].x - pt[2].x; 1665 dy = pt[0].y - pt[2].y; 1666 d1 = sqrt(dx*dx + dy*dy); 1667 1668 dx = pt[1].x - pt[3].x; 1669 dy = pt[1].y - pt[3].y; 1670 d2 = sqrt(dx*dx + dy*dy); 1671 1672 // philipg. Only accept those quadrangles which are more square 1673 // than rectangular and which are big enough 1674 double d3, d4; 1675 dx = pt[0].x - pt[1].x; 1676 dy = pt[0].y - pt[1].y; 1677 d3 = sqrt(dx*dx + dy*dy); 1678 dx = pt[1].x - pt[2].x; 1679 dy = pt[1].y - pt[2].y; 1680 d4 = sqrt(dx*dx + dy*dy); 1681 if( !(flags & CV_CALIB_CB_FILTER_QUADS) || 1682 (d3*4 > d4 && d4*4 > d3 && d3*d4 < area*1.5 && area > min_size && 1683 d1 >= 0.15 * p && d2 >= 0.15 * p) ) 1684 { 1685 CvContourEx* parent = (CvContourEx*)(src_contour->v_prev); 1686 parent->counter++; 1687 if( !board || board->counter < parent->counter ) 1688 board = parent; 1689 dst_contour->v_prev = (CvSeq*)parent; 1690 //for( i = 0; i < 4; i++ ) cvLine( debug_img, pt[i], pt[(i+1)&3], cvScalar(200,255,255), 1, CV_AA, 0 ); 1691 cvSeqPush( root, &dst_contour ); 1692 } 1693 } 1694 } 1695 } 1696 1697 // finish contour retrieving 1698 cvEndFindContours( &scanner ); 1699 1700 // allocate quad & corner buffers 1701 CV_CALL( *out_quads = (CvCBQuad*)cvAlloc((root->total+root->total / 2) * sizeof((*out_quads)[0]))); 1702 CV_CALL( *out_corners = (CvCBCorner*)cvAlloc((root->total+root->total / 2) * 4 * sizeof((*out_corners)[0]))); 1703 1704 // Create array of quads structures 1705 for( idx = 0; idx < root->total; idx++ ) 1706 { 1707 CvCBQuad* q = &(*out_quads)[quad_count]; 1708 src_contour = *(CvSeq**)cvGetSeqElem( root, idx ); 1709 if( (flags & CV_CALIB_CB_FILTER_QUADS) && src_contour->v_prev != (CvSeq*)board ) 1710 continue; 1711 1712 // reset group ID 1713 memset( q, 0, sizeof(*q) ); 1714 q->group_idx = -1; 1715 assert( src_contour->total == 4 ); 1716 for( i = 0; i < 4; i++ ) 1717 { 1718 CvPoint2D32f pt = cvPointTo32f(*(CvPoint*)cvGetSeqElem(src_contour, i)); 1719 CvCBCorner* corner = &(*out_corners)[quad_count*4 + i]; 1720 1721 memset( corner, 0, sizeof(*corner) ); 1722 corner->pt = pt; 1723 q->corners[i] = corner; 1724 } 1725 q->edge_len = FLT_MAX; 1726 for( i = 0; i < 4; i++ ) 1727 { 1728 float dx = q->corners[i]->pt.x - q->corners[(i+1)&3]->pt.x; 1729 float dy = q->corners[i]->pt.y - q->corners[(i+1)&3]->pt.y; 1730 float d = dx*dx + dy*dy; 1731 if( q->edge_len > d ) 1732 q->edge_len = d; 1733 } 1734 quad_count++; 1735 } 1736 1737 __END__; 1738 1739 if( cvGetErrStatus() < 0 ) 1740 { 1741 if( out_quads ) 1742 cvFree( out_quads ); 1743 if( out_corners ) 1744 cvFree( out_corners ); 1745 quad_count = 0; 1746 } 1747 1748 cvReleaseMemStorage( &temp_storage ); 1749 return quad_count; 1750} 1751 1752 1753//===================================================================================== 1754 1755static int is_equal_quad( const void* _a, const void* _b, void* ) 1756{ 1757 CvRect a = (*((CvContour**)_a))->rect; 1758 CvRect b = (*((CvContour**)_b))->rect; 1759 1760 int dx = MIN( b.x + b.width - 1, a.x + a.width - 1) - MAX( b.x, a.x); 1761 int dy = MIN( b.y + b.height - 1, a.y + a.height - 1) - MAX( b.y, a.y); 1762 int w = (a.width + b.width)>>1; 1763 int h = (a.height + b.height)>>1; 1764 1765 if( dx > w*0.75 && dy > h*0.75 && dx < w*1.25 && dy < h*1.25 ) return 1; 1766 1767 return 0; 1768} 1769 1770static int 1771icvGenerateQuadsEx( CvCBQuad **out_quads, CvCBCorner **out_corners, 1772 CvMemStorage *storage, CvMat *image, CvMat *thresh_img, int dilations, int flags ) 1773{ 1774 int l; 1775 int quad_count = 0; 1776 CvMemStorage *temp_storage = 0; 1777 1778 if( out_quads ) 1779 *out_quads = 0; 1780 1781 if( out_corners ) 1782 *out_corners = 0; 1783 1784 CV_FUNCNAME( "icvGenerateQuads" ); 1785 1786 __BEGIN__; 1787 1788 CvSeq *src_contour = 0; 1789 CvSeq *root, *root_tmp; 1790 CvContourEx* board = 0; 1791 CvContourScanner scanner; 1792 int i, idx, min_size; 1793 int step_level = 25; 1794 1795 CV_ASSERT( out_corners && out_quads ); 1796 1797 // empiric bound for minimal allowed perimeter for squares 1798 min_size = cvRound( image->cols * image->rows * .03 * 0.01 * 0.92 ); 1799 1800 // create temporary storage for contours and the sequence of pointers to found quadrangles 1801 CV_CALL( temp_storage = cvCreateChildMemStorage( storage )); 1802 CV_CALL( root_tmp = cvCreateSeq( 0, sizeof(CvSeq), sizeof(CvSeq*), temp_storage )); 1803 CV_CALL( root = cvCreateSeq( 0, sizeof(CvSeq), sizeof(CvSeq*), temp_storage )); 1804 1805 //perform contours slicing 1806 cvEqualizeHist(image,image); 1807 for(l = step_level; l < 256-step_level; l+= step_level) 1808 { 1809 cvThreshold( image, thresh_img, l, 255, CV_THRESH_BINARY ); 1810 cvDilate( thresh_img, thresh_img, 0, dilations ); 1811 1812 //draw frame to extract edge quads 1813 cvRectangle( thresh_img, cvPoint(0,0), cvPoint(thresh_img->cols-1, 1814 thresh_img->rows-1), CV_RGB(255,255,255), 3, 8); 1815 1816 // initialize contour retrieving routine 1817 CV_CALL( scanner = cvStartFindContours( thresh_img, temp_storage, sizeof(CvContourEx), 1818 CV_RETR_CCOMP, CV_CHAIN_APPROX_SIMPLE )); 1819 1820 // get all the contours one by one 1821 while( (src_contour = cvFindNextContour( scanner )) != 0 ) 1822 { 1823 CvSeq *dst_contour = 0; 1824 CvRect rect = ((CvContour*)src_contour)->rect; 1825 1826 // reject contours with too small perimeter 1827 if( CV_IS_SEQ_HOLE(src_contour) && rect.width*rect.height >= min_size ) 1828 { 1829 const int min_approx_level = 2, max_approx_level = MAX_CONTOUR_APPROX; 1830 int approx_level; 1831 for( approx_level = min_approx_level; approx_level <= max_approx_level; approx_level++ ) 1832 { 1833 dst_contour = cvApproxPoly( src_contour, sizeof(CvContour), temp_storage, 1834 CV_POLY_APPROX_DP, (float)approx_level ); 1835 // we call this again on its own output, because sometimes 1836 // cvApproxPoly() does not simplify as much as it should. 1837 dst_contour = cvApproxPoly( dst_contour, sizeof(CvContour), temp_storage, 1838 CV_POLY_APPROX_DP, (float)approx_level ); 1839 1840 if( dst_contour->total == 4 ) 1841 break; 1842 } 1843 1844 // reject non-quadrangles 1845 if( dst_contour->total == 4 && cvCheckContourConvexity(dst_contour) ) 1846 { 1847 CvPoint pt[4]; 1848 double d1, d2, p = cvContourPerimeter(dst_contour); 1849 double area = fabs(cvContourArea(dst_contour, CV_WHOLE_SEQ)); 1850 double dx, dy; 1851 1852 for( i = 0; i < 4; i++ ) 1853 pt[i] = *(CvPoint*)cvGetSeqElem(dst_contour, i); 1854 1855 //check border condition. if this is edge square we will add this as is 1856 int edge_flag = 0, eps = 2; 1857 for( i = 0; i < 4; i++ ) 1858 if( pt[i].x <= eps || pt[i].y <= eps || 1859 pt[i].x >= image->width - eps || 1860 pt[i].y >= image->height - eps ) edge_flag = 1; 1861 1862 dx = pt[0].x - pt[2].x; 1863 dy = pt[0].y - pt[2].y; 1864 d1 = sqrt(dx*dx + dy*dy); 1865 1866 dx = pt[1].x - pt[3].x; 1867 dy = pt[1].y - pt[3].y; 1868 d2 = sqrt(dx*dx + dy*dy); 1869 1870 // philipg. Only accept those quadrangles which are more square 1871 // than rectangular and which are big enough 1872 double d3, d4; 1873 dx = pt[0].x - pt[1].x; 1874 dy = pt[0].y - pt[1].y; 1875 d3 = sqrt(dx*dx + dy*dy); 1876 dx = pt[1].x - pt[2].x; 1877 dy = pt[1].y - pt[2].y; 1878 d4 = sqrt(dx*dx + dy*dy); 1879 if( edge_flag || 1880 (!(flags & CV_CALIB_CB_FILTER_QUADS) || 1881 (d3*4 > d4 && d4*4 > d3 && d3*d4 < area*1.5 && area > min_size && 1882 d1 >= 0.15 * p && d2 >= 0.15 * p)) ) 1883 { 1884 CvContourEx* parent = (CvContourEx*)(src_contour->v_prev); 1885 parent->counter++; 1886 if( !board || board->counter < parent->counter ) 1887 board = parent; 1888 dst_contour->v_prev = (CvSeq*)parent; 1889 //for( i = 0; i < 4; i++ ) cvLine( debug_img, pt[i], pt[(i+1)&3], cvScalar(200,255,255), 1, CV_AA, 0 ); 1890 cvSeqPush( root_tmp, &dst_contour ); 1891 } 1892 } 1893 } 1894 } 1895 // finish contour retrieving 1896 cvEndFindContours( &scanner ); 1897 } 1898 1899 1900 // Perform clustering of extracted quads 1901 // Same quad can be extracted from different binarization levels 1902 if( root_tmp->total ) 1903 { 1904 CvSeq* idx_seq = 0; 1905 int n_quads = cvSeqPartition( root_tmp, temp_storage, &idx_seq, is_equal_quad, 0 ); 1906 for( i = 0; i < n_quads; i++ ) 1907 { 1908 //extract biggest quad in group 1909 int max_size = 0; 1910 CvSeq* max_seq = 0; 1911 for( int j = 0; j < root_tmp->total; j++ ) 1912 { 1913 int index = *(int*)cvGetSeqElem(idx_seq, j); 1914 if(index!=i) continue; 1915 CvContour* cnt = *(CvContour**)cvGetSeqElem(root_tmp, j); 1916 if(cnt->rect.width > max_size) 1917 { 1918 max_size = cnt->rect.width; 1919 max_seq = (CvSeq*)cnt; 1920 } 1921 } 1922 cvSeqPush( root, &max_seq); 1923 } 1924 } 1925 1926 // allocate quad & corner buffers 1927 CV_CALL( *out_quads = (CvCBQuad*)cvAlloc((root->total+root->total / 2) * sizeof((*out_quads)[0]))); 1928 CV_CALL( *out_corners = (CvCBCorner*)cvAlloc((root->total+root->total / 2) * 4 * sizeof((*out_corners)[0]))); 1929 1930 // Create array of quads structures 1931 for( idx = 0; idx < root->total; idx++ ) 1932 { 1933 CvCBQuad* q = &(*out_quads)[quad_count]; 1934 src_contour = *(CvSeq**)cvGetSeqElem( root, idx ); 1935 if( (flags & CV_CALIB_CB_FILTER_QUADS) && src_contour->v_prev != (CvSeq*)board ) 1936 continue; 1937 1938 // reset group ID 1939 memset( q, 0, sizeof(*q) ); 1940 q->group_idx = -1; 1941 assert( src_contour->total == 4 ); 1942 for( i = 0; i < 4; i++ ) 1943 { 1944 CvPoint2D32f pt = cvPointTo32f(*(CvPoint*)cvGetSeqElem(src_contour, i)); 1945 CvCBCorner* corner = &(*out_corners)[quad_count*4 + i]; 1946 1947 memset( corner, 0, sizeof(*corner) ); 1948 corner->pt = pt; 1949 q->corners[i] = corner; 1950 } 1951 q->edge_len = FLT_MAX; 1952 for( i = 0; i < 4; i++ ) 1953 { 1954 float dx = q->corners[i]->pt.x - q->corners[(i+1)&3]->pt.x; 1955 float dy = q->corners[i]->pt.y - q->corners[(i+1)&3]->pt.y; 1956 float d = dx*dx + dy*dy; 1957 if( q->edge_len > d ) 1958 q->edge_len = d; 1959 } 1960 quad_count++; 1961 } 1962 1963 __END__; 1964 1965 if( cvGetErrStatus() < 0 ) 1966 { 1967 if( out_quads ) 1968 cvFree( out_quads ); 1969 if( out_corners ) 1970 cvFree( out_corners ); 1971 quad_count = 0; 1972 } 1973 1974 cvReleaseMemStorage( &temp_storage ); 1975 return quad_count; 1976} 1977 1978 1979CV_IMPL void 1980cvDrawChessboardCorners( CvArr* _image, CvSize pattern_size, 1981 CvPoint2D32f* corners, int count, int found ) 1982{ 1983 CV_FUNCNAME( "cvDrawChessboardCorners" ); 1984 1985 __BEGIN__; 1986 1987 const int shift = 0; 1988 const int radius = 4; 1989 const int r = radius*(1 << shift); 1990 int i; 1991 CvMat stub, *image; 1992 double scale = 1; 1993 int type, cn, line_type; 1994 1995 CV_CALL( image = cvGetMat( _image, &stub )); 1996 1997 type = CV_MAT_TYPE(image->type); 1998 cn = CV_MAT_CN(type); 1999 if( cn != 1 && cn != 3 && cn != 4 ) 2000 CV_ERROR( CV_StsUnsupportedFormat, "Number of channels must be 1, 3 or 4" ); 2001 2002 switch( CV_MAT_DEPTH(image->type) ) 2003 { 2004 case CV_8U: 2005 scale = 1; 2006 break; 2007 case CV_16U: 2008 scale = 256; 2009 break; 2010 case CV_32F: 2011 scale = 1./255; 2012 break; 2013 default: 2014 CV_ERROR( CV_StsUnsupportedFormat, 2015 "Only 8-bit, 16-bit or floating-point 32-bit images are supported" ); 2016 } 2017 2018 line_type = type == CV_8UC1 || type == CV_8UC3 ? CV_AA : 8; 2019 2020 if( !found ) 2021 { 2022 CvScalar color = {{0,0,255}}; 2023 if( cn == 1 ) 2024 color = cvScalarAll(200); 2025 color.val[0] *= scale; 2026 color.val[1] *= scale; 2027 color.val[2] *= scale; 2028 color.val[3] *= scale; 2029 2030 for( i = 0; i < count; i++ ) 2031 { 2032 CvPoint pt; 2033 pt.x = cvRound(corners[i].x*(1 << shift)); 2034 pt.y = cvRound(corners[i].y*(1 << shift)); 2035 cvLine( image, cvPoint( pt.x - r, pt.y - r ), 2036 cvPoint( pt.x + r, pt.y + r ), color, 1, line_type, shift ); 2037 cvLine( image, cvPoint( pt.x - r, pt.y + r), 2038 cvPoint( pt.x + r, pt.y - r), color, 1, line_type, shift ); 2039 cvCircle( image, pt, r+(1<<shift), color, 1, line_type, shift ); 2040 } 2041 } 2042 else 2043 { 2044 int x, y; 2045 CvPoint prev_pt = {0, 0}; 2046 const int line_max = 7; 2047 static const CvScalar line_colors[line_max] = 2048 { 2049 {{0,0,255}}, 2050 {{0,128,255}}, 2051 {{0,200,200}}, 2052 {{0,255,0}}, 2053 {{200,200,0}}, 2054 {{255,0,0}}, 2055 {{255,0,255}} 2056 }; 2057 2058 for( y = 0, i = 0; y < pattern_size.height; y++ ) 2059 { 2060 CvScalar color = line_colors[y % line_max]; 2061 if( cn == 1 ) 2062 color = cvScalarAll(200); 2063 color.val[0] *= scale; 2064 color.val[1] *= scale; 2065 color.val[2] *= scale; 2066 color.val[3] *= scale; 2067 2068 for( x = 0; x < pattern_size.width; x++, i++ ) 2069 { 2070 CvPoint pt; 2071 pt.x = cvRound(corners[i].x*(1 << shift)); 2072 pt.y = cvRound(corners[i].y*(1 << shift)); 2073 2074 if( i != 0 ) 2075 cvLine( image, prev_pt, pt, color, 1, line_type, shift ); 2076 2077 cvLine( image, cvPoint(pt.x - r, pt.y - r), 2078 cvPoint(pt.x + r, pt.y + r), color, 1, line_type, shift ); 2079 cvLine( image, cvPoint(pt.x - r, pt.y + r), 2080 cvPoint(pt.x + r, pt.y - r), color, 1, line_type, shift ); 2081 cvCircle( image, pt, r+(1<<shift), color, 1, line_type, shift ); 2082 prev_pt = pt; 2083 } 2084 } 2085 } 2086 2087 __END__; 2088} 2089 2090 2091/* End of file. */ 2092