1 //M*//////////////////////////////////////////////////////////////////////////////////////
3 // IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
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.
10 // Intel License Agreement
11 // For Open Source Computer Vision Library
13 // Copyright (C) 2000, Intel Corporation, all rights reserved.
14 // Third party copyrights are property of their respective owners.
16 // Redistribution and use in source and binary forms, with or without modification,
17 // are permitted provided that the following conditions are met:
19 // * Redistribution's of source code must retain the above copyright notice,
20 // this list of conditions and the following disclaimer.
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.
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.
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.
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 ===============================================================
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.
54 Reliability additions and modifications made by Philip Gruebele.
55 <a href="mailto:pgruebele@cox.net">pgruebele@cox.net</a>
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
60 \************************************************************************************/
62 /************************************************************************************\
63 This version adds a new and improved variant of chessboard corner detection
64 that works better in poor lighting condition. It is based on work from
65 Oliver Schreer and Stefano Masneri. This method works faster than the previous
66 one and reverts back to the older method in case no chessboard detection is
67 possible. Overall performance improves also because now the method avoids
68 performing the same computation multiple times when not necessary.
70 \************************************************************************************/
72 #include "precomp.hpp"
73 #include "opencv2/imgproc/imgproc_c.h"
74 #include "opencv2/calib3d/calib3d_c.h"
75 #include "circlesgrid.hpp"
82 //#define ENABLE_TRIM_COL_ROW
84 //#define DEBUG_CHESSBOARD
86 #ifdef DEBUG_CHESSBOARD
87 static int PRINTF( const char* fmt, ... )
91 return vprintf(fmt, args);
97 //=====================================================================================
98 // Implementation for the enhanced calibration object detection
99 //=====================================================================================
101 #define MAX_CONTOUR_APPROX 7
109 //=====================================================================================
111 /// Corner info structure
112 /** This structure stores information about the chessboard corner.*/
115 CvPoint2D32f pt; // Coordinates of the corner
116 int row; // Board row index
117 int count; // Number of neighbor corners
118 struct CvCBCorner* neighbors[4]; // Neighbor corners
120 float meanDist(int *_n) const
124 for( int i = 0; i < 4; i++ )
128 float dx = neighbors[i]->pt.x - pt.x;
129 float dy = neighbors[i]->pt.y - pt.y;
130 sum += sqrt(dx*dx + dy*dy);
140 //=====================================================================================
141 /// Quadrangle contour info structure
142 /** This structure stores information about the chessboard quadrange.*/
145 int count; // Number of quad neighbors
146 int group_idx; // quad group ID
147 int row, col; // row and column of this quad
148 bool ordered; // true if corners/neighbors are ordered counter-clockwise
149 float edge_len; // quad edge len, in pix^2
150 // neighbors and corners are synced, i.e., neighbor 0 shares corner 0
151 CvCBCorner *corners[4]; // Coordinates of quad corners
152 struct CvCBQuad *neighbors[4]; // Pointers of quad neighbors
155 //=====================================================================================
157 #ifdef DEBUG_CHESSBOARD
158 #include "opencv2/highgui.hpp"
159 #include "opencv2/imgproc.hpp"
160 static void SHOW(const std::string & name, Mat & img)
163 while ((uchar)waitKey(0) != 'q') {}
165 static void SHOW_QUADS(const std::string & name, const Mat & img_, CvCBQuad * quads, int quads_count)
167 Mat img = img_.clone();
168 if (img.channels() == 1)
169 cvtColor(img, img, COLOR_GRAY2BGR);
170 for (int i = 0; i < quads_count; ++i)
172 CvCBQuad & quad = quads[i];
173 for (int j = 0; j < 4; ++j)
175 line(img, quad.corners[j]->pt, quad.corners[(j + 1) % 4]->pt, Scalar(0, 240, 0), 1, LINE_AA);
179 while ((uchar)waitKey(0) != 'q') {}
183 #define SHOW_QUADS(...)
186 //=====================================================================================
188 static int icvGenerateQuads( CvCBQuad **quads, CvCBCorner **corners,
189 CvMemStorage *storage, const Mat &image_, int flags, int *max_quad_buf_size);
191 static bool processQuads(CvCBQuad *quads, int quad_count, CvSize pattern_size, int max_quad_buf_size,
192 CvMemStorage * storage, CvCBCorner *corners, CvPoint2D32f *out_corners, int *out_corner_count, int & prev_sqr_size);
195 icvGenerateQuadsEx( CvCBQuad **out_quads, CvCBCorner **out_corners,
196 CvMemStorage *storage, CvMat *image, CvMat *thresh_img, int dilation, int flags );*/
198 static void icvFindQuadNeighbors( CvCBQuad *quads, int quad_count );
200 static int icvFindConnectedQuads( CvCBQuad *quads, int quad_count,
201 CvCBQuad **quad_group, int group_idx,
202 CvMemStorage* storage );
204 static int icvCheckQuadGroup( CvCBQuad **quad_group, int count,
205 CvCBCorner **out_corners, CvSize pattern_size );
207 static int icvCleanFoundConnectedQuads( int quad_count,
208 CvCBQuad **quads, CvSize pattern_size );
210 static int icvOrderFoundConnectedQuads( int quad_count, CvCBQuad **quads,
211 int *all_count, CvCBQuad **all_quads, CvCBCorner **corners,
212 CvSize pattern_size, int max_quad_buf_size, CvMemStorage* storage );
214 static void icvOrderQuad(CvCBQuad *quad, CvCBCorner *corner, int common);
216 #ifdef ENABLE_TRIM_COL_ROW
217 static int icvTrimCol(CvCBQuad **quads, int count, int col, int dir);
219 static int icvTrimRow(CvCBQuad **quads, int count, int row, int dir);
222 static int icvAddOuterQuad(CvCBQuad *quad, CvCBQuad **quads, int quad_count,
223 CvCBQuad **all_quads, int all_count, CvCBCorner **corners, int max_quad_buf_size);
225 static void icvRemoveQuadFromGroup(CvCBQuad **quads, int count, CvCBQuad *q0);
227 static int icvCheckBoardMonotony( CvPoint2D32f* corners, CvSize pattern_size );
229 /***************************************************************************************************/
230 //COMPUTE INTENSITY HISTOGRAM OF INPUT IMAGE
231 static int icvGetIntensityHistogram( const Mat & img, std::vector<int>& piHist )
233 // sum up all pixel in row direction and divide by number of columns
234 for ( int j=0; j<img.rows; j++ )
236 const uchar * row = img.ptr(j);
237 for ( int i=0; i<img.cols; i++ )
244 /***************************************************************************************************/
245 //SMOOTH HISTOGRAM USING WINDOW OF SIZE 2*iWidth+1
246 static int icvSmoothHistogram( const std::vector<int>& piHist, std::vector<int>& piHistSmooth, int iWidth )
249 for ( int i=0; i<256; i++)
252 for ( int ii=-iWidth; ii<=iWidth; ii++)
255 if (iIdx > 0 && iIdx < 256)
257 iSmooth += piHist[iIdx];
260 piHistSmooth[i] = iSmooth/(2*iWidth+1);
264 /***************************************************************************************************/
265 //COMPUTE FAST HISTOGRAM GRADIENT
266 static int icvGradientOfHistogram( const std::vector<int>& piHist, std::vector<int>& piHistGrad )
269 for ( int i=1; i<255; i++)
271 piHistGrad[i] = piHist[i-1] - piHist[i+1];
272 if ( abs(piHistGrad[i]) < 100 )
274 if ( piHistGrad[i-1] == 0)
275 piHistGrad[i] = -100;
277 piHistGrad[i] = piHistGrad[i-1];
282 /***************************************************************************************************/
283 //PERFORM SMART IMAGE THRESHOLDING BASED ON ANALYSIS OF INTENSTY HISTOGRAM
284 static bool icvBinarizationHistogramBased( Mat & img )
286 CV_Assert(img.channels() == 1 && img.depth() == CV_8U);
287 int iCols = img.cols;
288 int iRows = img.rows;
289 int iMaxPix = iCols*iRows;
290 int iMaxPix1 = iMaxPix/100;
291 const int iNumBins = 256;
292 std::vector<int> piHistIntensity(iNumBins, 0);
293 std::vector<int> piHistSmooth(iNumBins, 0);
294 std::vector<int> piHistGrad(iNumBins, 0);
295 std::vector<int> piAccumSum(iNumBins, 0);
296 std::vector<int> piMaxPos; piMaxPos.reserve(20);
301 icvGetIntensityHistogram( img, piHistIntensity );
303 // get accumulated sum starting from bright
304 piAccumSum[iNumBins-1] = piHistIntensity[iNumBins-1];
305 for ( int i=iNumBins-2; i>=0; i-- )
307 piAccumSum[i] = piHistIntensity[i] + piAccumSum[i+1];
310 // first smooth the distribution
311 icvSmoothHistogram( piHistIntensity, piHistSmooth, iWidth );
314 icvGradientOfHistogram( piHistSmooth, piHistGrad );
318 for ( int i=iNumBins-2; (i>2) && (iCntMaxima<20); i--)
320 if ( (piHistGrad[i-1] < 0) && (piHistGrad[i] > 0) )
322 piMaxPos.push_back(i);
328 int iSumAroundMax = 0;
329 for ( int i=0; i<iCntMaxima; i++ )
332 iSumAroundMax = piHistSmooth[iIdx-1] + piHistSmooth[iIdx] + piHistSmooth[iIdx+1];
333 if ( iSumAroundMax < iMaxPix1 && iIdx < 64 )
335 piMaxPos.erase(piMaxPos.begin() + i);
341 CV_Assert((size_t)iCntMaxima == piMaxPos.size());
343 PRINTF("HIST: MAXIMA COUNT: %d (%d, %d, %d, ...)\n", iCntMaxima,
344 iCntMaxima > 0 ? piMaxPos[0] : -1,
345 iCntMaxima > 1 ? piMaxPos[1] : -1,
346 iCntMaxima > 2 ? piMaxPos[2] : -1);
350 // no any maxima inside (except 0 and 255 which are not handled above)
351 // Does image black-write already?
352 const int iMaxPix2 = iMaxPix / 2;
353 for (int sum = 0, i = 0; i < 256; ++i) // select mean intensity
355 sum += piHistIntensity[i];
363 else if (iCntMaxima == 1)
365 iThresh = piMaxPos[0]/2;
367 else if ( iCntMaxima == 2)
369 iThresh = (piMaxPos[0] + piMaxPos[1])/2;
371 else // iCntMaxima >= 3
373 // CHECKING THRESHOLD FOR WHITE
374 int iIdxAccSum = 0, iAccum = 0;
375 for (int i=iNumBins-1; i>0; i--)
377 iAccum += piHistIntensity[i];
378 // iMaxPix/18 is about 5,5%, minimum required number of pixels required for white part of chessboard
379 if ( iAccum > (iMaxPix/18) )
387 int iBrightMax = piMaxPos[0];
388 // printf("iBrightMax = %d\n", iBrightMax);
389 for ( int n=0; n<iCntMaxima-1; n++)
392 if ( piMaxPos[n] < iIdxAccSum )
396 iBrightMax = piMaxPos[n];
399 // CHECKING THRESHOLD FOR BLACK
400 int iMaxVal = piHistIntensity[piMaxPos[iIdxBGMax]];
402 //IF TOO CLOSE TO 255, jump to next maximum
403 if ( piMaxPos[iIdxBGMax] >= 250 && iIdxBGMax + 1 < iCntMaxima )
406 iMaxVal = piHistIntensity[piMaxPos[iIdxBGMax]];
409 for ( int n=iIdxBGMax + 1; n<iCntMaxima; n++)
411 if ( piHistIntensity[piMaxPos[n]] >= iMaxVal )
413 iMaxVal = piHistIntensity[piMaxPos[n]];
418 //SETTING THRESHOLD FOR BINARIZATION
419 int iDist2 = (iBrightMax - piMaxPos[iIdxBGMax])/2;
420 iThresh = iBrightMax - iDist2;
421 PRINTF("THRESHOLD SELECTED = %d, BRIGHTMAX = %d, DARKMAX = %d\n", iThresh, iBrightMax, piMaxPos[iIdxBGMax]);
427 for ( int jj=0; jj<iRows; jj++)
429 uchar * row = img.ptr(jj);
430 for ( int ii=0; ii<iCols; ii++)
432 if ( row[ii] < iThresh )
444 int cvFindChessboardCorners( const void* arr, CvSize pattern_size,
445 CvPoint2D32f* out_corners, int* out_corner_count,
450 CvCBCorner *corners = 0;
452 cv::Ptr<CvMemStorage> storage;
457 const int min_dilations = 0;
458 const int max_dilations = 7;
460 if( out_corner_count )
461 *out_corner_count = 0;
463 Mat img = cvarrToMat((CvMat*)arr).clone();
465 if( img.depth() != CV_8U || (img.channels() != 1 && img.channels() != 3 && img.channels() != 4) )
466 CV_Error( CV_StsUnsupportedFormat, "Only 8-bit grayscale or color images are supported" );
468 if( pattern_size.width <= 2 || pattern_size.height <= 2 )
469 CV_Error( CV_StsOutOfRange, "Both width and height of the pattern should have bigger than 2" );
472 CV_Error( CV_StsNullPtr, "Null pointer to corners" );
474 if (img.channels() != 1)
476 cvtColor(img, img, COLOR_BGR2GRAY);
480 Mat thresh_img_new = img.clone();
481 icvBinarizationHistogramBased( thresh_img_new ); // process image in-place
482 SHOW("New binarization", thresh_img_new);
484 if( flags & CV_CALIB_CB_FAST_CHECK)
486 //perform new method for checking chessboard using a binary image.
487 //image is binarised using a threshold dependent on the image histogram
488 if (checkChessboardBinary(thresh_img_new, pattern_size) <= 0) //fall back to the old method
490 if (checkChessboard(img, pattern_size) <= 0)
497 storage.reset(cvCreateMemStorage(0));
499 int prev_sqr_size = 0;
501 // Try our standard "1" dilation, but if the pattern is not found, iterate the whole procedure with higher dilations.
502 // This is necessary because some squares simply do not separate properly with a single dilation. However,
503 // we want to use the minimum number of dilations possible since dilations cause the squares to become smaller,
504 // making it difficult to detect smaller squares.
505 for( int dilations = min_dilations; dilations <= max_dilations; dilations++ )
508 break; // already found it
510 //USE BINARY IMAGE COMPUTED USING icvBinarizationHistogramBased METHOD
511 dilate( thresh_img_new, thresh_img_new, Mat(), Point(-1, -1), 1 );
513 // So we can find rectangles that go to the edge, we draw a white line around the image edge.
514 // Otherwise FindContours will miss those clipped rectangle contours.
515 // The border color will be the image mean, because otherwise we risk screwing up filters like cvSmooth()...
516 rectangle( thresh_img_new, Point(0,0), Point(thresh_img_new.cols-1, thresh_img_new.rows-1), Scalar(255,255,255), 3, LINE_8);
517 int max_quad_buf_size = 0;
520 int quad_count = icvGenerateQuads( &quads, &corners, storage, thresh_img_new, flags, &max_quad_buf_size );
521 PRINTF("Quad count: %d/%d\n", quad_count, (pattern_size.width/2+1)*(pattern_size.height/2+1));
522 SHOW_QUADS("New quads", thresh_img_new, quads, quad_count);
523 if (processQuads(quads, quad_count, pattern_size, max_quad_buf_size, storage, corners, out_corners, out_corner_count, prev_sqr_size))
527 PRINTF("Chessboard detection result 0: %d\n", found);
529 // revert to old, slower, method if detection failed
532 if( flags & CV_CALIB_CB_NORMALIZE_IMAGE )
534 equalizeHist( img, img );
540 PRINTF("Fallback to old algorithm\n");
541 const bool useAdaptive = flags & CV_CALIB_CB_ADAPTIVE_THRESH;
544 // empiric threshold level
545 // thresholding performed here and not inside the cycle to save processing time
546 double mean = cv::mean(img).val[0];
547 int thresh_level = MAX(cvRound( mean - 10 ), 10);
548 threshold( img, thresh_img, thresh_level, 255, THRESH_BINARY );
550 //if flag CV_CALIB_CB_ADAPTIVE_THRESH is not set it doesn't make sense to iterate over k
551 int max_k = useAdaptive ? 6 : 1;
552 for( k = 0; k < max_k; k++ )
554 for( int dilations = min_dilations; dilations <= max_dilations; dilations++ )
557 break; // already found it
559 // convert the input grayscale image to binary (black-n-white)
562 int block_size = cvRound(prev_sqr_size == 0
563 ? MIN(img.cols, img.rows) * (k % 2 == 0 ? 0.2 : 0.1)
564 : prev_sqr_size * 2);
565 block_size = block_size | 1;
567 adaptiveThreshold( img, thresh_img, 255, ADAPTIVE_THRESH_MEAN_C, THRESH_BINARY, block_size, (k/2)*5 );
569 dilate( thresh_img, thresh_img, Mat(), Point(-1, -1), dilations-1 );
574 dilate( thresh_img, thresh_img, Mat(), Point(-1, -1), 1 );
576 SHOW("Old binarization", thresh_img);
578 // So we can find rectangles that go to the edge, we draw a white line around the image edge.
579 // Otherwise FindContours will miss those clipped rectangle contours.
580 // The border color will be the image mean, because otherwise we risk screwing up filters like cvSmooth()...
581 rectangle( thresh_img, Point(0,0), Point(thresh_img.cols-1, thresh_img.rows-1), Scalar(255,255,255), 3, LINE_8);
582 int max_quad_buf_size = 0;
585 int quad_count = icvGenerateQuads( &quads, &corners, storage, thresh_img, flags, &max_quad_buf_size);
586 PRINTF("Quad count: %d/%d\n", quad_count, (pattern_size.width/2+1)*(pattern_size.height/2+1));
587 SHOW_QUADS("Old quads", thresh_img, quads, quad_count);
588 if (processQuads(quads, quad_count, pattern_size, max_quad_buf_size, storage, corners, out_corners, out_corner_count, prev_sqr_size))
594 PRINTF("Chessboard detection result 1: %d\n", found);
597 found = icvCheckBoardMonotony( out_corners, pattern_size );
599 PRINTF("Chessboard detection result 2: %d\n", found);
601 // check that none of the found corners is too close to the image boundary
604 const int BORDER = 8;
605 for( k = 0; k < pattern_size.width*pattern_size.height; k++ )
607 if( out_corners[k].x <= BORDER || out_corners[k].x > img.cols - BORDER ||
608 out_corners[k].y <= BORDER || out_corners[k].y > img.rows - BORDER )
612 found = k == pattern_size.width*pattern_size.height;
615 PRINTF("Chessboard detection result 3: %d\n", found);
619 if ( pattern_size.height % 2 == 0 && pattern_size.width % 2 == 0 )
621 int last_row = (pattern_size.height-1)*pattern_size.width;
622 double dy0 = out_corners[last_row].y - out_corners[0].y;
625 int n = pattern_size.width*pattern_size.height;
626 for(int i = 0; i < n/2; i++ )
629 CV_SWAP(out_corners[i], out_corners[n-i-1], temp);
635 cvFindCornerSubPix( &old_img, out_corners, pattern_size.width*pattern_size.height,
636 cvSize(wsize, wsize), cvSize(-1,-1),
637 cvTermCriteria(CV_TERMCRIT_EPS+CV_TERMCRIT_ITER, 15, 0.1));
652 // Checks that each board row and column is pretty much monotonous curve:
653 // It analyzes each row and each column of the chessboard as following:
654 // for each corner c lying between end points in the same row/column it checks that
655 // the point projection to the line segment (a,b) is lying between projections
656 // of the neighbor corners in the same row/column.
658 // This function has been created as temporary workaround for the bug in current implementation
659 // of cvFindChessboardCornes that produces absolutely unordered sets of corners.
663 icvCheckBoardMonotony( CvPoint2D32f* corners, CvSize pattern_size )
667 for( k = 0; k < 2; k++ )
669 for( i = 0; i < (k == 0 ? pattern_size.height : pattern_size.width); i++ )
671 CvPoint2D32f a = k == 0 ? corners[i*pattern_size.width] : corners[i];
672 CvPoint2D32f b = k == 0 ? corners[(i+1)*pattern_size.width-1] :
673 corners[(pattern_size.height-1)*pattern_size.width + i];
674 float prevt = 0, dx0 = b.x - a.x, dy0 = b.y - a.y;
675 if( fabs(dx0) + fabs(dy0) < FLT_EPSILON )
677 for( j = 1; j < (k == 0 ? pattern_size.width : pattern_size.height) - 1; j++ )
679 CvPoint2D32f c = k == 0 ? corners[i*pattern_size.width + j] :
680 corners[j*pattern_size.width + i];
681 float t = ((c.x - a.x)*dx0 + (c.y - a.y)*dy0)/(dx0*dx0 + dy0*dy0);
682 if( t < prevt || t > 1 )
693 // order a group of connected quads
696 // clockwise from there
697 // note: "top left" is nominal, depends on initial ordering of starting quad
698 // but all other quads are ordered consistently
700 // can change the number of quads in the group
701 // can add quads, so we need to have quad/corner arrays passed in
705 icvOrderFoundConnectedQuads( int quad_count, CvCBQuad **quads,
706 int *all_count, CvCBQuad **all_quads, CvCBCorner **corners,
707 CvSize pattern_size, int max_quad_buf_size, CvMemStorage* storage )
709 cv::Ptr<CvMemStorage> temp_storage(cvCreateChildMemStorage( storage ));
710 CvSeq* stack = cvCreateSeq( 0, sizeof(*stack), sizeof(void*), temp_storage );
712 // first find an interior quad
713 CvCBQuad *start = NULL;
714 for (int i=0; i<quad_count; i++)
716 if (quads[i]->count == 4)
724 return 0; // no 4-connected quad
726 // start with first one, assign rows/cols
727 int row_min = 0, col_min = 0, row_max=0, col_max = 0;
729 std::map<int, int> col_hist;
730 std::map<int, int> row_hist;
732 cvSeqPush(stack, &start);
735 start->ordered = true;
737 // Recursively order the quads so that all position numbers (e.g.,
738 // 0,1,2,3) are in the at the same relative corner (e.g., lower right).
740 while( stack->total )
743 cvSeqPop( stack, &q );
750 if (row > row_max) row_max = row;
751 if (row < row_min) row_min = row;
752 if (col > col_max) col_max = col;
753 if (col < col_min) col_min = col;
755 for(int i = 0; i < 4; i++ )
757 CvCBQuad *neighbor = q->neighbors[i];
758 switch(i) // adjust col, row for this quad
759 { // start at top left, go clockwise
770 // just do inside quads
771 if (neighbor && neighbor->ordered == false && neighbor->count == 4)
773 PRINTF("col: %d row: %d\n", col, row);
774 icvOrderQuad(neighbor, q->corners[i], (i+2)%4); // set in order
775 neighbor->ordered = true;
778 cvSeqPush( stack, &neighbor );
783 for (int i=col_min; i<=col_max; i++)
784 PRINTF("HIST[%d] = %d\n", i, col_hist[i]);
786 // analyze inner quad structure
787 int w = pattern_size.width - 1;
788 int h = pattern_size.height - 1;
789 int drow = row_max - row_min + 1;
790 int dcol = col_max - col_min + 1;
792 // normalize pattern and found quad indices
793 if ((w > h && dcol < drow) ||
794 (w < h && drow < dcol))
796 h = pattern_size.width - 1;
797 w = pattern_size.height - 1;
800 PRINTF("Size: %dx%d Pattern: %dx%d\n", dcol, drow, w, h);
802 // check if there are enough inner quads
803 if (dcol < w || drow < h) // found enough inner quads?
805 PRINTF("Too few inner quad rows/cols\n");
806 return 0; // no, return
808 #ifdef ENABLE_TRIM_COL_ROW
809 // too many columns, not very common
810 if (dcol == w+1) // too many, trim
812 PRINTF("Trimming cols\n");
813 if (col_hist[col_max] > col_hist[col_min])
815 PRINTF("Trimming left col\n");
816 quad_count = icvTrimCol(quads,quad_count,col_min,-1);
820 PRINTF("Trimming right col\n");
821 quad_count = icvTrimCol(quads,quad_count,col_max,+1);
825 // too many rows, not very common
826 if (drow == h+1) // too many, trim
828 PRINTF("Trimming rows\n");
829 if (row_hist[row_max] > row_hist[row_min])
831 PRINTF("Trimming top row\n");
832 quad_count = icvTrimRow(quads,quad_count,row_min,-1);
836 PRINTF("Trimming bottom row\n");
837 quad_count = icvTrimRow(quads,quad_count,row_max,+1);
842 // check edges of inner quads
843 // if there is an outer quad missing, fill it in
844 // first order all inner quads
846 for (int i=0; i<quad_count; i++)
848 if (quads[i]->count == 4)
849 { // ok, look at neighbors
850 int col = quads[i]->col;
851 int row = quads[i]->row;
852 for (int j=0; j<4; j++)
854 switch(j) // adjust col, row for this quad
855 { // start at top left, go clockwise
865 CvCBQuad *neighbor = quads[i]->neighbors[j];
866 if (neighbor && !neighbor->ordered && // is it an inner quad?
867 col <= col_max && col >= col_min &&
868 row <= row_max && row >= row_min)
870 // if so, set in order
871 PRINTF("Adding inner: col: %d row: %d\n", col, row);
873 icvOrderQuad(neighbor, quads[i]->corners[j], (j+2)%4);
874 neighbor->ordered = true;
882 // if we have found inner quads, add corresponding outer quads,
886 PRINTF("Found %d inner quads not connected to outer quads, repairing\n", found);
887 for (int i=0; i<quad_count && *all_count < max_quad_buf_size; i++)
889 if (quads[i]->count < 4 && quads[i]->ordered)
891 int added = icvAddOuterQuad(quads[i],quads,quad_count,all_quads,*all_count,corners, max_quad_buf_size);
897 if (*all_count >= max_quad_buf_size)
902 // final trimming of outer quads
903 if (dcol == w && drow == h) // found correct inner quads
905 PRINTF("Inner bounds ok, check outer quads\n");
906 int rcount = quad_count;
907 for (int i=quad_count-1; i>=0; i--) // eliminate any quad not connected to
910 if (quads[i]->ordered == false)
913 for (int j=0; j<4; j++) // any neighbors that are ordered?
915 if (quads[i]->neighbors[j] && quads[i]->neighbors[j]->ordered)
918 if (!outer) // not an outer quad, eliminate
920 PRINTF("Removing quad %d\n", i);
921 icvRemoveQuadFromGroup(quads,rcount,quads[i]);
935 // looks for the neighbor of <quad> that isn't present,
936 // tries to add it in.
940 icvAddOuterQuad( CvCBQuad *quad, CvCBQuad **quads, int quad_count,
941 CvCBQuad **all_quads, int all_count, CvCBCorner **corners, int max_quad_buf_size )
945 for (int i=0; i<4 && all_count < max_quad_buf_size; i++) // find no-neighbor corners
947 if (!quad->neighbors[i]) // ok, create and add neighbor
950 PRINTF("Adding quad as neighbor 2\n");
951 CvCBQuad *q = &(*all_quads)[all_count];
952 memset( q, 0, sizeof(*q) );
954 quads[quad_count] = q;
957 // set neighbor and group id
958 quad->neighbors[i] = q;
960 q->neighbors[j] = quad;
961 q->group_idx = quad->group_idx;
962 q->count = 1; // number of neighbors
964 q->edge_len = quad->edge_len;
966 // make corners of new quad
967 // same as neighbor quad, but offset
968 CvPoint2D32f pt = quad->corners[i]->pt;
970 float dx = pt.x - quad->corners[j]->pt.x;
971 float dy = pt.y - quad->corners[j]->pt.y;
972 for (int k=0; k<4; k++)
974 corner = &(*corners)[all_count*4+k];
975 pt = quad->corners[k]->pt;
976 memset( corner, 0, sizeof(*corner) );
978 q->corners[k] = corner;
982 // have to set exact corner
983 q->corners[j] = quad->corners[i];
985 // now find other neighbor and add it, if possible
986 if (quad->neighbors[(i+3)%4] &&
987 quad->neighbors[(i+3)%4]->ordered &&
988 quad->neighbors[(i+3)%4]->neighbors[i] &&
989 quad->neighbors[(i+3)%4]->neighbors[i]->ordered )
991 CvCBQuad *qn = quad->neighbors[(i+3)%4]->neighbors[i];
993 q->neighbors[(j+1)%4] = qn;
994 qn->neighbors[(i+1)%4] = q;
996 // have to set exact corner
997 q->corners[(j+1)%4] = qn->corners[(i+1)%4];
1007 // trimming routines
1008 #ifdef ENABLE_TRIM_COL_ROW
1010 icvTrimCol(CvCBQuad **quads, int count, int col, int dir)
1013 // find the right quad(s)
1014 for (int i=0; i<count; i++)
1016 #ifdef DEBUG_CHESSBOARD
1017 if (quads[i]->ordered)
1018 PRINTF("index: %d cur: %d\n", col, quads[i]->col);
1020 if (quads[i]->ordered && quads[i]->col == col)
1024 if (quads[i]->neighbors[1])
1026 icvRemoveQuadFromGroup(quads,rcount,quads[i]->neighbors[1]);
1029 if (quads[i]->neighbors[2])
1031 icvRemoveQuadFromGroup(quads,rcount,quads[i]->neighbors[2]);
1037 if (quads[i]->neighbors[0])
1039 icvRemoveQuadFromGroup(quads,rcount,quads[i]->neighbors[0]);
1042 if (quads[i]->neighbors[3])
1044 icvRemoveQuadFromGroup(quads,rcount,quads[i]->neighbors[3]);
1055 icvTrimRow(CvCBQuad **quads, int count, int row, int dir)
1057 int i, rcount = count;
1058 // find the right quad(s)
1059 for (i=0; i<count; i++)
1061 #ifdef DEBUG_CHESSBOARD
1062 if (quads[i]->ordered)
1063 PRINTF("index: %d cur: %d\n", row, quads[i]->row);
1065 if (quads[i]->ordered && quads[i]->row == row)
1067 if (dir == 1) // remove from bottom
1069 if (quads[i]->neighbors[2])
1071 icvRemoveQuadFromGroup(quads,rcount,quads[i]->neighbors[2]);
1074 if (quads[i]->neighbors[3])
1076 icvRemoveQuadFromGroup(quads,rcount,quads[i]->neighbors[3]);
1080 else // remove from top
1082 if (quads[i]->neighbors[0])
1084 icvRemoveQuadFromGroup(quads,rcount,quads[i]->neighbors[0]);
1087 if (quads[i]->neighbors[1])
1089 icvRemoveQuadFromGroup(quads,rcount,quads[i]->neighbors[1]);
1101 // remove quad from quad group
1105 icvRemoveQuadFromGroup(CvCBQuad **quads, int count, CvCBQuad *q0)
1108 // remove any references to this quad as a neighbor
1109 for(i = 0; i < count; i++ )
1111 CvCBQuad *q = quads[i];
1112 for(j = 0; j < 4; j++ )
1114 if( q->neighbors[j] == q0 )
1116 q->neighbors[j] = 0;
1118 for(int k = 0; k < 4; k++ )
1119 if( q0->neighbors[k] == q )
1121 q0->neighbors[k] = 0;
1131 for(i = 0; i < count; i++ )
1133 CvCBQuad *q = quads[i];
1136 quads[i] = quads[count-1];
1143 // put quad into correct order, where <corner> has value <common>
1147 icvOrderQuad(CvCBQuad *quad, CvCBCorner *corner, int common)
1151 for (tc=0; tc<4; tc++)
1152 if (quad->corners[tc]->pt.x == corner->pt.x &&
1153 quad->corners[tc]->pt.y == corner->pt.y)
1158 while (tc != common)
1163 tempc = quad->corners[3];
1164 tempq = quad->neighbors[3];
1165 for (int i=3; i>0; i--)
1167 quad->corners[i] = quad->corners[i-1];
1168 quad->neighbors[i] = quad->neighbors[i-1];
1170 quad->corners[0] = tempc;
1171 quad->neighbors[0] = tempq;
1178 // if we found too many connect quads, remove those which probably do not belong.
1180 icvCleanFoundConnectedQuads( int quad_count, CvCBQuad **quad_group, CvSize pattern_size )
1182 CvPoint2D32f center;
1184 // number of quads this pattern should contain
1185 int count = ((pattern_size.width + 1)*(pattern_size.height + 1) + 1)/2;
1187 // if we have more quadrangles than we should,
1188 // try to eliminate duplicates or ones which don't belong to the pattern rectangle...
1189 if( quad_count <= count )
1192 // create an array of quadrangle centers
1193 cv::AutoBuffer<CvPoint2D32f> centers( quad_count );
1194 cv::Ptr<CvMemStorage> temp_storage(cvCreateMemStorage(0));
1196 for( i = 0; i < quad_count; i++ )
1199 CvCBQuad* q = quad_group[i];
1201 for( j = 0; j < 4; j++ )
1203 CvPoint2D32f pt = q->corners[j]->pt;
1215 center.x /= quad_count;
1216 center.y /= quad_count;
1218 // If we still have more quadrangles than we should,
1219 // we try to eliminate bad ones based on minimizing the bounding box.
1220 // We iteratively remove the point which reduces the size of
1221 // the bounding box of the blobs the most
1222 // (since we want the rectangle to be as small as possible)
1223 // remove the quadrange that causes the biggest reduction
1224 // in pattern size until we have the correct number
1225 for( ; quad_count > count; quad_count-- )
1227 double min_box_area = DBL_MAX;
1228 int skip, min_box_area_index = -1;
1230 // For each point, calculate box area without that point
1231 for( skip = 0; skip < quad_count; skip++ )
1233 // get bounding rectangle
1234 CvPoint2D32f temp = centers[skip]; // temporarily make index 'skip' the same as
1235 centers[skip] = center; // pattern center (so it is not counted for convex hull)
1236 CvMat pointMat = cvMat(1, quad_count, CV_32FC2, centers);
1237 CvSeq *hull = cvConvexHull2( &pointMat, temp_storage, CV_CLOCKWISE, 1 );
1238 centers[skip] = temp;
1239 double hull_area = fabs(cvContourArea(hull, CV_WHOLE_SEQ));
1241 // remember smallest box area
1242 if( hull_area < min_box_area )
1244 min_box_area = hull_area;
1245 min_box_area_index = skip;
1247 cvClearMemStorage( temp_storage );
1250 CvCBQuad *q0 = quad_group[min_box_area_index];
1252 // remove any references to this quad as a neighbor
1253 for( i = 0; i < quad_count; i++ )
1255 CvCBQuad *q = quad_group[i];
1256 for( j = 0; j < 4; j++ )
1258 if( q->neighbors[j] == q0 )
1260 q->neighbors[j] = 0;
1262 for( k = 0; k < 4; k++ )
1263 if( q0->neighbors[k] == q )
1265 q0->neighbors[k] = 0;
1276 quad_group[min_box_area_index] = quad_group[quad_count];
1277 centers[min_box_area_index] = centers[quad_count];
1283 //=====================================================================================
1286 icvFindConnectedQuads( CvCBQuad *quad, int quad_count, CvCBQuad **out_group,
1287 int group_idx, CvMemStorage* storage )
1289 cv::Ptr<CvMemStorage> temp_storage(cvCreateChildMemStorage( storage ));
1290 CvSeq* stack = cvCreateSeq( 0, sizeof(*stack), sizeof(void*), temp_storage );
1293 // Scan the array for a first unlabeled quad
1294 for( i = 0; i < quad_count; i++ )
1296 if( quad[i].count > 0 && quad[i].group_idx < 0)
1300 // Recursively find a group of connected quads starting from the seed quad[i]
1301 if( i < quad_count )
1303 CvCBQuad* q = &quad[i];
1304 cvSeqPush( stack, &q );
1305 out_group[count++] = q;
1306 q->group_idx = group_idx;
1309 while( stack->total )
1311 cvSeqPop( stack, &q );
1312 for( i = 0; i < 4; i++ )
1314 CvCBQuad *neighbor = q->neighbors[i];
1315 if( neighbor && neighbor->count > 0 && neighbor->group_idx < 0 )
1317 cvSeqPush( stack, &neighbor );
1318 out_group[count++] = neighbor;
1319 neighbor->group_idx = group_idx;
1320 neighbor->ordered = false;
1330 //=====================================================================================
1333 icvCheckQuadGroup( CvCBQuad **quad_group, int quad_count,
1334 CvCBCorner **out_corners, CvSize pattern_size )
1336 const int ROW1 = 1000000;
1337 const int ROW2 = 2000000;
1338 const int ROW_ = 3000000;
1340 int i, out_corner_count = 0, corner_count = 0;
1341 std::vector<CvCBCorner*> corners(quad_count*4);
1344 int width = 0, height = 0;
1345 int hist[5] = {0,0,0,0,0};
1346 CvCBCorner* first = 0, *first2 = 0, *right, *cur, *below, *c;
1348 // build dual graph, which vertices are internal quad corners
1349 // and two vertices are connected iff they lie on the same quad edge
1350 for( i = 0; i < quad_count; i++ )
1352 CvCBQuad* q = quad_group[i];
1353 /*CvScalar color = q->count == 0 ? cvScalar(0,255,255) :
1354 q->count == 1 ? cvScalar(0,0,255) :
1355 q->count == 2 ? cvScalar(0,255,0) :
1356 q->count == 3 ? cvScalar(255,255,0) :
1357 cvScalar(255,0,0);*/
1359 for( j = 0; j < 4; j++ )
1361 //cvLine( debug_img, cvPointFrom32f(q->corners[j]->pt), cvPointFrom32f(q->corners[(j+1)&3]->pt), color, 1, CV_AA, 0 );
1362 if( q->neighbors[j] )
1364 CvCBCorner *a = q->corners[j], *b = q->corners[(j+1)&3];
1365 // mark internal corners that belong to:
1366 // - a quad with a single neighbor - with ROW1,
1367 // - a quad with two neighbors - with ROW2
1368 // make the rest of internal corners with ROW_
1369 int row_flag = q->count == 1 ? ROW1 : q->count == 2 ? ROW2 : ROW_;
1373 corners[corner_count++] = a;
1376 else if( a->row > row_flag )
1379 if( q->neighbors[(j+1)&3] )
1381 if( a->count >= 4 || b->count >= 4 )
1383 for( k = 0; k < 4; k++ )
1385 if( a->neighbors[k] == b )
1387 if( b->neighbors[k] == a )
1390 a->neighbors[a->count++] = b;
1391 b->neighbors[b->count++] = a;
1397 if( corner_count != pattern_size.width*pattern_size.height )
1400 for( i = 0; i < corner_count; i++ )
1402 int n = corners[i]->count;
1403 assert( 0 <= n && n <= 4 );
1405 if( !first && n == 2 )
1407 if( corners[i]->row == ROW1 )
1409 else if( !first2 && corners[i]->row == ROW2 )
1410 first2 = corners[i];
1414 // start with a corner that belongs to a quad with a single neighbor.
1415 // if we do not have such, start with a corner of a quad with two neighbors.
1419 if( !first || hist[0] != 0 || hist[1] != 0 || hist[2] != 4 ||
1420 hist[3] != (pattern_size.width + pattern_size.height)*2 - 8 )
1425 out_corners[out_corner_count++] = cur;
1427 for( k = 0; k < 4; k++ )
1429 c = cur->neighbors[k];
1439 if( !right || (right->count != 2 && right->count != 3) ||
1440 !below || (below->count != 2 && below->count != 3) )
1444 //cvCircle( debug_img, cvPointFrom32f(cur->pt), 3, cvScalar(0,255,0), -1, 8, 0 );
1446 first = below; // remember the first corner in the next row
1447 // find and store the first row (or column)
1451 out_corners[out_corner_count++] = right;
1452 //cvCircle( debug_img, cvPointFrom32f(right->pt), 3, cvScalar(0,255-j*10,0), -1, 8, 0 );
1453 if( right->count == 2 )
1455 if( right->count != 3 || out_corner_count >= MAX(pattern_size.width,pattern_size.height) )
1458 for( k = 0; k < 4; k++ )
1460 c = cur->neighbors[k];
1461 if( c && c->row > 0 )
1463 for( kk = 0; kk < 4; kk++ )
1465 if( c->neighbors[kk] == below )
1476 width = out_corner_count;
1477 if( width == pattern_size.width )
1478 height = pattern_size.height;
1479 else if( width == pattern_size.height )
1480 height = pattern_size.width;
1484 // find and store all the other rows
1494 out_corners[out_corner_count++] = cur;
1495 //cvCircle( debug_img, cvPointFrom32f(cur->pt), 3, cvScalar(0,0,255-j*10), -1, 8, 0 );
1496 if( cur->count == 2 + (i < height-1) && j > 0 )
1501 // find a neighbor that has not been processed yet
1502 // and that has a neighbor from the previous row
1503 for( k = 0; k < 4; k++ )
1505 c = cur->neighbors[k];
1506 if( c && c->row > i )
1508 for( kk = 0; kk < 4; kk++ )
1510 if( c->neighbors[kk] && c->neighbors[kk]->row == i-1 )
1528 if( j != width - 1 )
1532 if( out_corner_count != corner_count )
1535 // check if we need to transpose the board
1536 if( width != pattern_size.width )
1538 CV_SWAP( width, height, k );
1540 memcpy( &corners[0], out_corners, corner_count*sizeof(corners[0]) );
1541 for( i = 0; i < height; i++ )
1542 for( j = 0; j < width; j++ )
1543 out_corners[i*width + j] = corners[j*height + i];
1546 // check if we need to revert the order in each row
1548 CvPoint2D32f p0 = out_corners[0]->pt, p1 = out_corners[pattern_size.width-1]->pt,
1549 p2 = out_corners[pattern_size.width]->pt;
1550 if( (p1.x - p0.x)*(p2.y - p1.y) - (p1.y - p0.y)*(p2.x - p1.x) < 0 )
1552 if( width % 2 == 0 )
1554 for( i = 0; i < height; i++ )
1555 for( j = 0; j < width/2; j++ )
1556 CV_SWAP( out_corners[i*width+j], out_corners[i*width+width-j-1], c );
1560 for( j = 0; j < width; j++ )
1561 for( i = 0; i < height/2; i++ )
1562 CV_SWAP( out_corners[i*width+j], out_corners[(height - i - 1)*width+j], c );
1567 result = corner_count;
1573 corner_count = MIN( corner_count, pattern_size.width*pattern_size.height );
1574 for( i = 0; i < corner_count; i++ )
1575 out_corners[i] = corners[i];
1576 result = -corner_count;
1578 if (result == -pattern_size.width*pattern_size.height)
1588 //=====================================================================================
1590 static void icvFindQuadNeighbors( CvCBQuad *quads, int quad_count )
1592 const float thresh_scale = 1.f;
1596 // find quad neighbors
1597 for( idx = 0; idx < quad_count; idx++ )
1599 CvCBQuad* cur_quad = &quads[idx];
1601 // choose the points of the current quadrangle that are close to
1602 // some points of the other quadrangles
1603 // (it can happen for split corners (due to dilation) of the
1604 // checker board). Search only in other quadrangles!
1606 // for each corner of this quadrangle
1607 for( i = 0; i < 4; i++ )
1610 float min_dist = FLT_MAX;
1611 int closest_corner_idx = -1;
1612 CvCBQuad *closest_quad = 0;
1613 CvCBCorner *closest_corner = 0;
1615 if( cur_quad->neighbors[i] )
1618 pt = cur_quad->corners[i]->pt;
1620 // find the closest corner in all other quadrangles
1621 for( k = 0; k < quad_count; k++ )
1626 for( j = 0; j < 4; j++ )
1628 if( quads[k].neighbors[j] )
1631 dx = pt.x - quads[k].corners[j]->pt.x;
1632 dy = pt.y - quads[k].corners[j]->pt.y;
1633 dist = dx * dx + dy * dy;
1635 if( dist < min_dist &&
1636 dist <= cur_quad->edge_len*thresh_scale &&
1637 dist <= quads[k].edge_len*thresh_scale )
1639 // check edge lengths, make sure they're compatible
1640 // edges that are different by more than 1:4 are rejected
1641 float ediff = cur_quad->edge_len - quads[k].edge_len;
1642 if (ediff > 32*cur_quad->edge_len ||
1643 ediff > 32*quads[k].edge_len)
1645 PRINTF("Incompatible edge lengths\n");
1648 closest_corner_idx = j;
1649 closest_quad = &quads[k];
1655 // we found a matching corner point?
1656 if( closest_corner_idx >= 0 && min_dist < FLT_MAX )
1658 // If another point from our current quad is closer to the found corner
1659 // than the current one, then we don't count this one after all.
1660 // This is necessary to support small squares where otherwise the wrong
1661 // corner will get matched to closest_quad;
1662 closest_corner = closest_quad->corners[closest_corner_idx];
1664 for( j = 0; j < 4; j++ )
1666 if( cur_quad->neighbors[j] == closest_quad )
1669 dx = closest_corner->pt.x - cur_quad->corners[j]->pt.x;
1670 dy = closest_corner->pt.y - cur_quad->corners[j]->pt.y;
1672 if( dx * dx + dy * dy < min_dist )
1676 if( j < 4 || cur_quad->count >= 4 || closest_quad->count >= 4 )
1679 // Check that each corner is a neighbor of different quads
1680 for( j = 0; j < closest_quad->count; j++ )
1682 if( closest_quad->neighbors[j] == cur_quad )
1685 if( j < closest_quad->count )
1688 // check whether the closest corner to closest_corner
1689 // is different from cur_quad->corners[i]->pt
1690 for( k = 0; k < quad_count; k++ )
1692 CvCBQuad* q = &quads[k];
1693 if( k == idx || q == closest_quad )
1696 for( j = 0; j < 4; j++ )
1697 if( !q->neighbors[j] )
1699 dx = closest_corner->pt.x - q->corners[j]->pt.x;
1700 dy = closest_corner->pt.y - q->corners[j]->pt.y;
1701 dist = dx*dx + dy*dy;
1702 if( dist < min_dist )
1709 if( k < quad_count )
1712 closest_corner->pt.x = (pt.x + closest_corner->pt.x) * 0.5f;
1713 closest_corner->pt.y = (pt.y + closest_corner->pt.y) * 0.5f;
1715 // We've found one more corner - remember it
1717 cur_quad->neighbors[i] = closest_quad;
1718 cur_quad->corners[i] = closest_corner;
1720 closest_quad->count++;
1721 closest_quad->neighbors[closest_corner_idx] = cur_quad;
1727 //=====================================================================================
1729 // returns corners in clockwise order
1730 // corners don't necessarily start at same position on quad (e.g.,
1734 icvGenerateQuads( CvCBQuad **out_quads, CvCBCorner **out_corners,
1735 CvMemStorage *storage, const cv::Mat & image_, int flags, int *max_quad_buf_size )
1737 CvMat image_old(image_), *image = &image_old;
1739 cv::Ptr<CvMemStorage> temp_storage;
1747 CvSeq *src_contour = 0;
1749 CvContourEx* board = 0;
1750 CvContourScanner scanner;
1751 int i, idx, min_size;
1753 CV_Assert( out_corners && out_quads );
1755 // empiric bound for minimal allowed perimeter for squares
1756 min_size = 25; //cvRound( image->cols * image->rows * .03 * 0.01 * 0.92 );
1758 // create temporary storage for contours and the sequence of pointers to found quadrangles
1759 temp_storage.reset(cvCreateChildMemStorage( storage ));
1760 root = cvCreateSeq( 0, sizeof(CvSeq), sizeof(CvSeq*), temp_storage );
1762 // initialize contour retrieving routine
1763 scanner = cvStartFindContours( image, temp_storage, sizeof(CvContourEx),
1764 CV_RETR_CCOMP, CV_CHAIN_APPROX_SIMPLE );
1766 // get all the contours one by one
1767 while( (src_contour = cvFindNextContour( scanner )) != 0 )
1769 CvSeq *dst_contour = 0;
1770 CvRect rect = ((CvContour*)src_contour)->rect;
1772 // reject contours with too small perimeter
1773 if( CV_IS_SEQ_HOLE(src_contour) && rect.width*rect.height >= min_size )
1775 const int min_approx_level = 1, max_approx_level = MAX_CONTOUR_APPROX;
1777 for( approx_level = min_approx_level; approx_level <= max_approx_level; approx_level++ )
1779 dst_contour = cvApproxPoly( src_contour, sizeof(CvContour), temp_storage,
1780 CV_POLY_APPROX_DP, (float)approx_level );
1781 if( dst_contour->total == 4 )
1784 // we call this again on its own output, because sometimes
1785 // cvApproxPoly() does not simplify as much as it should.
1786 dst_contour = cvApproxPoly( dst_contour, sizeof(CvContour), temp_storage,
1787 CV_POLY_APPROX_DP, (float)approx_level );
1789 if( dst_contour->total == 4 )
1793 // reject non-quadrangles
1794 if( dst_contour->total == 4 && cvCheckContourConvexity(dst_contour) )
1797 double d1, d2, p = cvContourPerimeter(dst_contour);
1798 double area = fabs(cvContourArea(dst_contour, CV_WHOLE_SEQ));
1801 for( i = 0; i < 4; i++ )
1802 pt[i] = *(CvPoint*)cvGetSeqElem(dst_contour, i);
1804 dx = pt[0].x - pt[2].x;
1805 dy = pt[0].y - pt[2].y;
1806 d1 = sqrt(dx*dx + dy*dy);
1808 dx = pt[1].x - pt[3].x;
1809 dy = pt[1].y - pt[3].y;
1810 d2 = sqrt(dx*dx + dy*dy);
1812 // philipg. Only accept those quadrangles which are more square
1813 // than rectangular and which are big enough
1815 dx = pt[0].x - pt[1].x;
1816 dy = pt[0].y - pt[1].y;
1817 d3 = sqrt(dx*dx + dy*dy);
1818 dx = pt[1].x - pt[2].x;
1819 dy = pt[1].y - pt[2].y;
1820 d4 = sqrt(dx*dx + dy*dy);
1821 if( !(flags & CV_CALIB_CB_FILTER_QUADS) ||
1822 (d3*4 > d4 && d4*4 > d3 && d3*d4 < area*1.5 && area > min_size &&
1823 d1 >= 0.15 * p && d2 >= 0.15 * p) )
1825 CvContourEx* parent = (CvContourEx*)(src_contour->v_prev);
1827 if( !board || board->counter < parent->counter )
1829 dst_contour->v_prev = (CvSeq*)parent;
1830 //for( i = 0; i < 4; i++ ) cvLine( debug_img, pt[i], pt[(i+1)&3], cvScalar(200,255,255), 1, CV_AA, 0 );
1831 cvSeqPush( root, &dst_contour );
1837 // finish contour retrieving
1838 cvEndFindContours( &scanner );
1840 // allocate quad & corner buffers
1841 *max_quad_buf_size = MAX(1, (root->total+root->total / 2)) * 2;
1842 *out_quads = (CvCBQuad*)cvAlloc(*max_quad_buf_size * sizeof((*out_quads)[0]));
1843 *out_corners = (CvCBCorner*)cvAlloc(*max_quad_buf_size * 4 * sizeof((*out_corners)[0]));
1845 // Create array of quads structures
1846 for( idx = 0; idx < root->total; idx++ )
1848 CvCBQuad* q = &(*out_quads)[quad_count];
1849 src_contour = *(CvSeq**)cvGetSeqElem( root, idx );
1850 if( (flags & CV_CALIB_CB_FILTER_QUADS) && src_contour->v_prev != (CvSeq*)board )
1854 memset( q, 0, sizeof(*q) );
1856 assert( src_contour->total == 4 );
1857 for( i = 0; i < 4; i++ )
1859 CvPoint * onePoint = (CvPoint*)cvGetSeqElem(src_contour, i);
1860 CV_Assert(onePoint != NULL);
1861 CvPoint2D32f pt = cvPointTo32f(*onePoint);
1862 CvCBCorner* corner = &(*out_corners)[quad_count*4 + i];
1864 memset( corner, 0, sizeof(*corner) );
1866 q->corners[i] = corner;
1868 q->edge_len = FLT_MAX;
1869 for( i = 0; i < 4; i++ )
1871 float dx = q->corners[i]->pt.x - q->corners[(i+1)&3]->pt.x;
1872 float dy = q->corners[i]->pt.y - q->corners[(i+1)&3]->pt.y;
1873 float d = dx*dx + dy*dy;
1874 if( q->edge_len > d )
1883 static bool processQuads(CvCBQuad *quads, int quad_count, CvSize pattern_size, int max_quad_buf_size,
1884 CvMemStorage * storage, CvCBCorner *corners, CvPoint2D32f *out_corners, int *out_corner_count, int & prev_sqr_size)
1886 if( quad_count <= 0 )
1891 // Find quad's neighbors
1892 icvFindQuadNeighbors( quads, quad_count );
1894 // allocate extra for adding in icvOrderFoundQuads
1895 CvCBQuad **quad_group = 0;
1896 CvCBCorner **corner_group = 0;
1898 quad_group = (CvCBQuad**)cvAlloc( sizeof(quad_group[0]) * max_quad_buf_size);
1899 corner_group = (CvCBCorner**)cvAlloc( sizeof(corner_group[0]) * max_quad_buf_size * 4 );
1901 for( int group_idx = 0; ; group_idx++ )
1903 int count = icvFindConnectedQuads( quads, quad_count, quad_group, group_idx, storage );
1908 // order the quad corners globally
1909 // maybe delete or add some
1910 PRINTF("Starting ordering of inner quads (%d)\n", count);
1911 count = icvOrderFoundConnectedQuads(count, quad_group, &quad_count, &quads, &corners,
1912 pattern_size, max_quad_buf_size, storage );
1913 PRINTF("Finished ordering of inner quads (%d)\n", count);
1916 continue; // haven't found inner quads
1918 // If count is more than it should be, this will remove those quads
1919 // which cause maximum deviation from a nice square pattern.
1920 count = icvCleanFoundConnectedQuads( count, quad_group, pattern_size );
1921 PRINTF("Connected group: %d, count: %d\n", group_idx, count);
1923 count = icvCheckQuadGroup( quad_group, count, corner_group, pattern_size );
1924 PRINTF("Connected group: %d, count: %d\n", group_idx, count);
1926 int n = count > 0 ? pattern_size.width * pattern_size.height : -count;
1927 n = MIN( n, pattern_size.width * pattern_size.height );
1931 for(int i = 0; i < n; i++ )
1934 float avgi = corner_group[i]->meanDist(&ni);
1935 sum_dist += avgi*ni;
1938 prev_sqr_size = cvRound(sum_dist/MAX(total, 1));
1940 if( count > 0 || (out_corner_count && -count > *out_corner_count) )
1942 // copy corners to output array
1943 for(int i = 0; i < n; i++ )
1944 out_corners[i] = corner_group[i]->pt;
1946 if( out_corner_count )
1947 *out_corner_count = n;
1949 if( count == pattern_size.width*pattern_size.height
1950 && icvCheckBoardMonotony( out_corners, pattern_size ))
1958 cvFree(&quad_group);
1959 cvFree(&corner_group);
1964 //==================================================================================================
1967 cvDrawChessboardCorners( CvArr* _image, CvSize pattern_size,
1968 CvPoint2D32f* corners, int count, int found )
1970 const int shift = 0;
1971 const int radius = 4;
1972 const int r = radius*(1 << shift);
1976 int type, cn, line_type;
1978 image = cvGetMat( _image, &stub );
1980 type = CV_MAT_TYPE(image->type);
1981 cn = CV_MAT_CN(type);
1982 if( cn != 1 && cn != 3 && cn != 4 )
1983 CV_Error( CV_StsUnsupportedFormat, "Number of channels must be 1, 3 or 4" );
1985 switch( CV_MAT_DEPTH(image->type) )
1997 CV_Error( CV_StsUnsupportedFormat,
1998 "Only 8-bit, 16-bit or floating-point 32-bit images are supported" );
2001 line_type = type == CV_8UC1 || type == CV_8UC3 ? CV_AA : 8;
2005 CvScalar color(0,0,255,0);
2007 color = cvScalarAll(200);
2008 color.val[0] *= scale;
2009 color.val[1] *= scale;
2010 color.val[2] *= scale;
2011 color.val[3] *= scale;
2013 for( i = 0; i < count; i++ )
2016 pt.x = cvRound(corners[i].x*(1 << shift));
2017 pt.y = cvRound(corners[i].y*(1 << shift));
2018 cvLine( image, cvPoint( pt.x - r, pt.y - r ),
2019 cvPoint( pt.x + r, pt.y + r ), color, 1, line_type, shift );
2020 cvLine( image, cvPoint( pt.x - r, pt.y + r),
2021 cvPoint( pt.x + r, pt.y - r), color, 1, line_type, shift );
2022 cvCircle( image, pt, r+(1<<shift), color, 1, line_type, shift );
2029 const int line_max = 7;
2030 static const CvScalar line_colors[line_max] =
2033 CvScalar(0,128,255),
2034 CvScalar(0,200,200),
2036 CvScalar(200,200,0),
2041 for( y = 0, i = 0; y < pattern_size.height; y++ )
2043 CvScalar color = line_colors[y % line_max];
2045 color = cvScalarAll(200);
2046 color.val[0] *= scale;
2047 color.val[1] *= scale;
2048 color.val[2] *= scale;
2049 color.val[3] *= scale;
2051 for( x = 0; x < pattern_size.width; x++, i++ )
2054 pt.x = cvRound(corners[i].x*(1 << shift));
2055 pt.y = cvRound(corners[i].y*(1 << shift));
2058 cvLine( image, prev_pt, pt, color, 1, line_type, shift );
2060 cvLine( image, cvPoint(pt.x - r, pt.y - r),
2061 cvPoint(pt.x + r, pt.y + r), color, 1, line_type, shift );
2062 cvLine( image, cvPoint(pt.x - r, pt.y + r),
2063 cvPoint(pt.x + r, pt.y - r), color, 1, line_type, shift );
2064 cvCircle( image, pt, r+(1<<shift), color, 1, line_type, shift );
2071 bool cv::findChessboardCorners( InputArray _image, Size patternSize,
2072 OutputArray corners, int flags )
2074 CV_INSTRUMENT_REGION()
2076 int count = patternSize.area()*2;
2077 std::vector<Point2f> tmpcorners(count+1);
2078 Mat image = _image.getMat(); CvMat c_image = image;
2079 bool ok = cvFindChessboardCorners(&c_image, patternSize,
2080 (CvPoint2D32f*)&tmpcorners[0], &count, flags ) > 0;
2083 tmpcorners.resize(count);
2084 Mat(tmpcorners).copyTo(corners);
2093 int quiet_error(int /*status*/, const char* /*func_name*/,
2094 const char* /*err_msg*/, const char* /*file_name*/,
2095 int /*line*/, void* /*userdata*/ )
2101 void cv::drawChessboardCorners( InputOutputArray _image, Size patternSize,
2102 InputArray _corners,
2103 bool patternWasFound )
2105 CV_INSTRUMENT_REGION()
2107 Mat corners = _corners.getMat();
2108 if( corners.empty() )
2110 Mat image = _image.getMat(); CvMat c_image = image;
2111 int nelems = corners.checkVector(2, CV_32F, true);
2112 CV_Assert(nelems >= 0);
2113 cvDrawChessboardCorners( &c_image, patternSize, corners.ptr<CvPoint2D32f>(),
2114 nelems, patternWasFound );
2117 bool cv::findCirclesGrid( InputArray image, Size patternSize,
2118 OutputArray centers, int flags,
2119 const Ptr<FeatureDetector> &blobDetector,
2120 CirclesGridFinderParameters parameters)
2122 CirclesGridFinderParameters2 parameters2;
2123 *((CirclesGridFinderParameters*)¶meters2) = parameters;
2124 return cv::findCirclesGrid2(image, patternSize, centers, flags, blobDetector, parameters2);
2127 bool cv::findCirclesGrid2( InputArray _image, Size patternSize,
2128 OutputArray _centers, int flags, const Ptr<FeatureDetector> &blobDetector,
2129 CirclesGridFinderParameters2 parameters)
2131 CV_INSTRUMENT_REGION()
2133 bool isAsymmetricGrid = (flags & CALIB_CB_ASYMMETRIC_GRID) ? true : false;
2134 bool isSymmetricGrid = (flags & CALIB_CB_SYMMETRIC_GRID ) ? true : false;
2135 CV_Assert(isAsymmetricGrid ^ isSymmetricGrid);
2137 Mat image = _image.getMat();
2138 std::vector<Point2f> centers;
2140 std::vector<KeyPoint> keypoints;
2141 blobDetector->detect(image, keypoints);
2142 std::vector<Point2f> points;
2143 for (size_t i = 0; i < keypoints.size(); i++)
2145 points.push_back (keypoints[i].pt);
2148 if(flags & CALIB_CB_ASYMMETRIC_GRID)
2149 parameters.gridType = CirclesGridFinderParameters::ASYMMETRIC_GRID;
2150 if(flags & CALIB_CB_SYMMETRIC_GRID)
2151 parameters.gridType = CirclesGridFinderParameters::SYMMETRIC_GRID;
2153 if(flags & CALIB_CB_CLUSTERING)
2155 CirclesGridClusterFinder circlesGridClusterFinder(parameters);
2156 circlesGridClusterFinder.findGrid(points, patternSize, centers);
2157 Mat(centers).copyTo(_centers);
2158 return !centers.empty();
2161 const int attempts = 2;
2162 const size_t minHomographyPoints = 4;
2164 for (int i = 0; i < attempts; i++)
2167 CirclesGridFinder boxFinder(patternSize, points, parameters);
2168 bool isFound = false;
2172 ErrorCallback oldCbk = redirectError(quiet_error, 0, &oldCbkData);
2176 isFound = boxFinder.findHoles();
2178 CV_CATCH(Exception, e)
2183 redirectError(oldCbk, oldCbkData);
2187 switch(parameters.gridType)
2189 case CirclesGridFinderParameters::SYMMETRIC_GRID:
2190 boxFinder.getHoles(centers);
2192 case CirclesGridFinderParameters::ASYMMETRIC_GRID:
2193 boxFinder.getAsymmetricHoles(centers);
2196 CV_Error(CV_StsBadArg, "Unknown pattern type");
2202 transform(centers, orgPointsMat, H.inv());
2203 convertPointsFromHomogeneous(orgPointsMat, centers);
2205 Mat(centers).copyTo(_centers);
2209 boxFinder.getHoles(centers);
2210 if (i != attempts - 1)
2212 if (centers.size() < minHomographyPoints)
2214 H = CirclesGridFinder::rectifyGrid(boxFinder.getDetectedGridSize(), centers, points, points);
2217 Mat(centers).copyTo(_centers);
2221 bool cv::findCirclesGrid( InputArray _image, Size patternSize,
2222 OutputArray _centers, int flags, const Ptr<FeatureDetector> &blobDetector)
2224 return cv::findCirclesGrid2(_image, patternSize, _centers, flags, blobDetector, CirclesGridFinderParameters2());