@ -59,11 +59,22 @@
\ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/************************************************************************************\
This version adds a new and improved variant of chessboard corner detection
that works better in poor lighting condition . It is based on work from
Oliver Schreer and Stefano Masneri . This method works faster than the previous
one and reverts back to the older method in case no chessboard detection is
possible . Overall performance improves also because now the method avoids
performing the same computation multiple times when not necessary .
\ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
# include "precomp.hpp"
# include "opencv2/imgproc/imgproc_c.h"
# include "opencv2/calib3d/calib3d_c.h"
# include "circlesgrid.hpp"
# include <stdarg.h>
# include <vector>
//#define ENABLE_TRIM_COL_ROW
@ -191,6 +202,202 @@ static void icvRemoveQuadFromGroup(CvCBQuad **quads, int count, CvCBQuad *q0);
static int icvCheckBoardMonotony ( CvPoint2D32f * corners , CvSize pattern_size ) ;
/***************************************************************************************************/
//COMPUTE INTENSITY HISTOGRAM OF INPUT IMAGE
static int icvGetIntensityHistogram ( unsigned char * pucImage , int iSizeCols , int iSizeRows , std : : vector < int > & piHist ) ;
//SMOOTH HISTOGRAM USING WINDOW OF SIZE 2*iWidth+1
static int icvSmoothHistogram ( const std : : vector < int > & piHist , std : : vector < int > & piHistSmooth , int iWidth ) ;
//COMPUTE FAST HISTOGRAM GRADIENT
static int icvGradientOfHistogram ( const std : : vector < int > & piHist , std : : vector < int > & piHistGrad ) ;
//PERFORM SMART IMAGE THRESHOLDING BASED ON ANALYSIS OF INTENSTY HISTOGRAM
static bool icvBinarizationHistogramBased ( unsigned char * pucImg , int iCols , int iRows ) ;
/***************************************************************************************************/
int icvGetIntensityHistogram ( unsigned char * pucImage , int iSizeCols , int iSizeRows , std : : vector < int > & piHist )
{
int iVal ;
// sum up all pixel in row direction and divide by number of columns
for ( int j = 0 ; j < iSizeRows ; j + + )
{
for ( int i = 0 ; i < iSizeCols ; i + + )
{
iVal = ( int ) pucImage [ j * iSizeCols + i ] ;
piHist [ iVal ] + + ;
}
}
return 0 ;
}
/***************************************************************************************************/
int icvSmoothHistogram ( const std : : vector < int > & piHist , std : : vector < int > & piHistSmooth , int iWidth )
{
int iIdx ;
for ( int i = 0 ; i < 256 ; i + + )
{
int iSmooth = 0 ;
for ( int ii = - iWidth ; ii < = iWidth ; ii + + )
{
iIdx = i + ii ;
if ( iIdx > 0 & & iIdx < 256 )
{
iSmooth + = piHist [ iIdx ] ;
}
}
piHistSmooth [ i ] = iSmooth / ( 2 * iWidth + 1 ) ;
}
return 0 ;
}
/***************************************************************************************************/
int icvGradientOfHistogram ( const std : : vector < int > & piHist , std : : vector < int > & piHistGrad )
{
piHistGrad [ 0 ] = 0 ;
for ( int i = 1 ; i < 255 ; i + + )
{
piHistGrad [ i ] = piHist [ i - 1 ] - piHist [ i + 1 ] ;
if ( abs ( piHistGrad [ i ] ) < 100 )
{
if ( piHistGrad [ i - 1 ] = = 0 )
piHistGrad [ i ] = - 100 ;
else
piHistGrad [ i ] = piHistGrad [ i - 1 ] ;
}
}
return 0 ;
}
/***************************************************************************************************/
bool icvBinarizationHistogramBased ( unsigned char * pucImg , int iCols , int iRows )
{
int iMaxPix = iCols * iRows ;
int iMaxPix1 = iMaxPix / 100 ;
const int iNumBins = 256 ;
std : : vector < int > piHistIntensity ( iNumBins , 0 ) ;
std : : vector < int > piHistSmooth ( iNumBins , 0 ) ;
std : : vector < int > piHistGrad ( iNumBins , 0 ) ;
std : : vector < int > piAccumSum ( iNumBins , 0 ) ;
std : : vector < int > piMaxPos ( 20 , 0 ) ;
int iThresh = 0 ;
int iIdx ;
int iWidth = 1 ;
icvGetIntensityHistogram ( pucImg , iCols , iRows , piHistIntensity ) ;
// get accumulated sum starting from bright
piAccumSum [ iNumBins - 1 ] = piHistIntensity [ iNumBins - 1 ] ;
for ( int i = iNumBins - 2 ; i > = 0 ; i - - )
{
piAccumSum [ i ] = piHistIntensity [ i ] + piAccumSum [ i + 1 ] ;
}
// first smooth the distribution
icvSmoothHistogram ( piHistIntensity , piHistSmooth , iWidth ) ;
// compute gradient
icvGradientOfHistogram ( piHistSmooth , piHistGrad ) ;
// check for zeros
int iCntMaxima = 0 ;
for ( int i = iNumBins - 2 ; ( i > 2 ) & & ( iCntMaxima < 20 ) ; i - - )
{
if ( ( piHistGrad [ i - 1 ] < 0 ) & & ( piHistGrad [ i ] > 0 ) )
{
piMaxPos [ iCntMaxima ] = i ;
iCntMaxima + + ;
}
}
iIdx = 0 ;
int iSumAroundMax = 0 ;
for ( int i = 0 ; i < iCntMaxima ; i + + )
{
iIdx = piMaxPos [ i ] ;
iSumAroundMax = piHistSmooth [ iIdx - 1 ] + piHistSmooth [ iIdx ] + piHistSmooth [ iIdx + 1 ] ;
if ( iSumAroundMax < iMaxPix1 & & iIdx < 64 )
{
for ( int j = i ; j < iCntMaxima - 1 ; j + + )
{
piMaxPos [ j ] = piMaxPos [ j + 1 ] ;
}
iCntMaxima - - ;
i - - ;
}
}
if ( iCntMaxima = = 1 )
{
iThresh = piMaxPos [ 0 ] / 2 ;
}
else if ( iCntMaxima = = 2 )
{
iThresh = ( piMaxPos [ 0 ] + piMaxPos [ 1 ] ) / 2 ;
}
else // iCntMaxima >= 3
{
// CHECKING THRESHOLD FOR WHITE
int iIdxAccSum = 0 , iAccum = 0 ;
for ( int i = iNumBins - 1 ; i > 0 ; i - - )
{
iAccum + = piHistIntensity [ i ] ;
// iMaxPix/18 is about 5,5%, minimum required number of pixels required for white part of chessboard
if ( iAccum > ( iMaxPix / 18 ) )
{
iIdxAccSum = i ;
break ;
}
}
int iIdxBGMax = 0 ;
int iBrightMax = piMaxPos [ 0 ] ;
// printf("iBrightMax = %d\n", iBrightMax);
for ( int n = 0 ; n < iCntMaxima - 1 ; n + + )
{
iIdxBGMax = n + 1 ;
if ( piMaxPos [ n ] < iIdxAccSum )
{
break ;
}
iBrightMax = piMaxPos [ n ] ;
}
// CHECKING THRESHOLD FOR BLACK
int iMaxVal = piHistIntensity [ piMaxPos [ iIdxBGMax ] ] ;
//IF TOO CLOSE TO 255, jump to next maximum
if ( piMaxPos [ iIdxBGMax ] > = 250 & & iIdxBGMax < iCntMaxima )
{
iIdxBGMax + + ;
iMaxVal = piHistIntensity [ piMaxPos [ iIdxBGMax ] ] ;
}
for ( int n = iIdxBGMax + 1 ; n < iCntMaxima ; n + + )
{
if ( piHistIntensity [ piMaxPos [ n ] ] > = iMaxVal )
{
iMaxVal = piHistIntensity [ piMaxPos [ n ] ] ;
iIdxBGMax = n ;
}
}
//SETTING THRESHOLD FOR BINARIZATION
int iDist2 = ( iBrightMax - piMaxPos [ iIdxBGMax ] ) / 2 ;
iThresh = iBrightMax - iDist2 ;
PRINTF ( " THRESHOLD SELECTED = %d, BRIGHTMAX = %d, DARKMAX = %d \n " , iThresh , iBrightMax , piMaxPos [ iIdxBGMax ] ) ;
}
if ( iThresh > 0 )
{
for ( int jj = 0 ; jj < iRows ; jj + + )
{
for ( int ii = 0 ; ii < iCols ; ii + + )
{
if ( pucImg [ jj * iCols + ii ] < iThresh )
pucImg [ jj * iCols + ii ] = 0 ;
else
pucImg [ jj * iCols + ii ] = 255 ;
}
}
}
return true ;
}
#if 0
static void
icvCalcAffineTranf2D32f ( CvPoint2D32f * pts1 , CvPoint2D32f * pts2 , int count , CvMat * affine_trans )
@ -232,6 +439,7 @@ int cvFindChessboardCorners( const void* arr, CvSize pattern_size,
int found = 0 ;
CvCBQuad * quads = 0 , * * quad_group = 0 ;
CvCBCorner * corners = 0 , * * corner_group = 0 ;
IplImage * cImgSeg = 0 ;
try
{
@ -247,6 +455,11 @@ int cvFindChessboardCorners( const void* arr, CvSize pattern_size,
cv : : Ptr < CvMemStorage > storage ;
CvMat stub , * img = ( CvMat * ) arr ;
cImgSeg = cvCreateImage ( cvGetSize ( img ) , IPL_DEPTH_8U , 1 ) ;
memcpy ( cImgSeg - > imageData , cvPtr1D ( img , 0 ) , img - > rows * img - > cols ) ;
CvMat stub2 , * thresh_img_new ;
thresh_img_new = cvGetMat ( cImgSeg , & stub2 , 0 , 0 ) ;
int expected_corners_num = ( pattern_size . width / 2 + 1 ) * ( pattern_size . height / 2 + 1 ) ;
@ -255,7 +468,6 @@ int cvFindChessboardCorners( const void* arr, CvSize pattern_size,
if ( out_corner_count )
* out_corner_count = 0 ;
IplImage _img ;
int quad_count = 0 , group_idx = 0 , dilations = 0 ;
img = cvGetMat ( img , & stub ) ;
@ -300,209 +512,303 @@ int cvFindChessboardCorners( const void* arr, CvSize pattern_size,
if ( flags & CV_CALIB_CB_FAST_CHECK )
{
cvGetImage ( img , & _img ) ;
int check_chessboard_result = cvCheckChessboard ( & _img , pattern_size ) ;
if ( check_chessboard_result < = 0 )
//perform new method for checking chessboard using a binary image.
//image is binarised using a threshold dependent on the image histogram
icvBinarizationHistogramBased ( ( unsigned char * ) cImgSeg - > imageData , cImgSeg - > width , cImgSeg - > height ) ;
check_chessboard_result = cvCheckChessboardBinary ( cImgSeg , pattern_size ) ;
if ( check_chessboard_result < = 0 ) //fall back to the old method
{
return 0 ;
IplImage _img ;
cvGetImage ( img , & _img ) ;
check_chessboard_result = cvCheckChessboard ( & _img , pattern_size ) ;
if ( check_chessboard_result < = 0 )
{
return 0 ;
}
}
}
// empiric threshold level
// thresholding performed here and not inside the cycle to save processing time
int thresh_level ;
if ( ! ( flags & CV_CALIB_CB_ADAPTIVE_THRESH ) )
{
double mean = cvAvg ( img ) . val [ 0 ] ;
thresh_level = cvRound ( mean - 10 ) ;
thresh_level = MAX ( thresh_level , 10 ) ;
cvThreshold ( img , thresh_img , thresh_level , 255 , CV_THRESH_BINARY ) ;
}
// Try our standard "1" dilation, but if the pattern is not found, iterate the whole procedure with higher dilations.
// This is necessary because some squares simply do not separate properly with a single dilation. However,
// we want to use the minimum number of dilations possible since dilations cause the squares to become smaller,
// making it difficult to detect smaller squares.
for ( k = 0 ; k < 6 ; k + + )
for ( dilations = min_dilations ; dilations < = max_dilations ; dilations + + )
{
int max_quad_buf_size = 0 ;
for ( dilations = min_dilations ; dilations < = max_dilations ; dilations + + )
{
if ( found )
break ; // already found it
if ( found )
break ; // already found it
cvFree ( & quads ) ;
cvFree ( & corners ) ;
cvFree ( & quads ) ;
cvFree ( & corners ) ;
/*if( k == 1 )
{
//Pattern was not found using binarization
// Run multi-level quads extraction
// In case one-level binarization did not give enough number of quads
CV_CALL ( quad_count = icvGenerateQuadsEx ( & quads , & corners , storage , img , thresh_img , dilations , flags ) ) ;
PRINTF ( " EX quad count: %d/%d \n " , quad_count , expected_corners_num ) ;
}
else */
{
// convert the input grayscale image to binary (black-n-white)
if ( flags & CV_CALIB_CB_ADAPTIVE_THRESH )
{
int block_size = cvRound ( prev_sqr_size = = 0 ?
MIN ( img - > cols , img - > rows ) * ( k % 2 = = 0 ? 0.2 : 0.1 ) : prev_sqr_size * 2 ) | 1 ;
// convert to binary
cvAdaptiveThreshold ( img , thresh_img , 255 ,
CV_ADAPTIVE_THRESH_MEAN_C , CV_THRESH_BINARY , block_size , ( k / 2 ) * 5 ) ;
if ( dilations > 0 )
cvDilate ( thresh_img , thresh_img , 0 , dilations - 1 ) ;
}
else
{
// Make dilation before the thresholding.
// It splits chessboard corners
//cvDilate( img, thresh_img, 0, 1 );
//USE BINARY IMAGE COMPUTED USING icvBinarizationHistogramBased METHOD
cvDilate ( thresh_img_new , thresh_img_new , 0 , 1 ) ;
// empiric threshold level
double mean = cvAvg ( img ) . val [ 0 ] ;
int thresh_level = cvRound ( mean - 10 ) ;
thresh_level = MAX ( thresh_level , 10 ) ;
// So we can find rectangles that go to the edge, we draw a white line around the image edge.
// Otherwise FindContours will miss those clipped rectangle contours.
// The border color will be the image mean, because otherwise we risk screwing up filters like cvSmooth()...
cvRectangle ( thresh_img_new , cvPoint ( 0 , 0 ) , cvPoint ( thresh_img_new - > cols - 1 , thresh_img_new - > rows - 1 ) , CV_RGB ( 255 , 255 , 255 ) , 3 , 8 ) ;
quad_count = icvGenerateQuads ( & quads , & corners , storage , thresh_img_new , flags , & max_quad_buf_size ) ;
PRINTF ( " Quad count: %d/%d \n " , quad_count , expected_corners_num ) ;
cvThreshold ( img , thresh_img , thresh_level , 255 , CV_THRESH_BINARY ) ;
cvDilate ( thresh_img , thresh_img , 0 , dilations ) ;
}
if ( quad_count < = 0 )
{
continue ;
}
# ifdef DEBUG_CHESSBOARD
cvCvtColor ( thresh_img , dbg_img , CV_GRAY2BGR ) ;
# endif
// Find quad's neighbors
icvFindQuadNeighbors ( quads , quad_count ) ;
// So we can find rectangles that go to the edge, we draw a white line around the image edge.
// Otherwise FindContours will miss those clipped rectangle contours.
// The border color will be the image mean, because otherwise we risk screwing up filters like cvSmooth()...
cvRectangle ( thresh_img , cvPoint ( 0 , 0 ) , cvPoint ( thresh_img - > cols - 1 ,
thresh_img - > rows - 1 ) , CV_RGB ( 255 , 255 , 255 ) , 3 , 8 ) ;
// allocate extra for adding in icvOrderFoundQuads
cvFree ( & quad_group ) ;
cvFree ( & corner_group ) ;
quad_group = ( CvCBQuad * * ) cvAlloc ( sizeof ( quad_group [ 0 ] ) * max_quad_buf_size ) ;
corner_group = ( CvCBCorner * * ) cvAlloc ( sizeof ( corner_group [ 0 ] ) * max_quad_buf_size * 4 ) ;
quad_count = icvGenerateQuads ( & quads , & corners , storage , thresh_img , flags , & max_quad_buf_size ) ;
for ( group_idx = 0 ; ; group_idx + + )
{
int count = 0 ;
count = icvFindConnectedQuads ( quads , quad_count , quad_group , group_idx , storage ) ;
PRINTF ( " Quad count: %d/%d \n " , quad_count , expected_corners_num ) ;
}
int icount = count ;
if ( count = = 0 )
break ;
// order the quad corners globally
// maybe delete or add some
PRINTF ( " Starting ordering of inner quads \n " ) ;
count = icvOrderFoundConnectedQuads ( count , quad_group , & quad_count , & quads , & corners ,
pattern_size , storage ) ;
PRINTF ( " Orig count: %d After ordering: %d \n " , icount , count ) ;
if ( count = = 0 )
continue ; // haven't found inner quads
// If count is more than it should be, this will remove those quads
// which cause maximum deviation from a nice square pattern.
count = icvCleanFoundConnectedQuads ( count , quad_group , pattern_size ) ;
PRINTF ( " Connected group: %d orig count: %d cleaned: %d \n " , group_idx , icount , count ) ;
count = icvCheckQuadGroup ( quad_group , count , corner_group , pattern_size ) ;
PRINTF ( " Connected group: %d count: %d cleaned: %d \n " , group_idx , icount , count ) ;
int n = count > 0 ? pattern_size . width * pattern_size . height : - count ;
n = MIN ( n , pattern_size . width * pattern_size . height ) ;
float sum_dist = 0 ;
int total = 0 ;
for ( int i = 0 ; i < n ; i + + )
{
int ni = 0 ;
float avgi = corner_group [ i ] - > meanDist ( & ni ) ;
sum_dist + = avgi * ni ;
total + = ni ;
}
prev_sqr_size = cvRound ( sum_dist / MAX ( total , 1 ) ) ;
if ( count > 0 | | ( out_corner_count & & - count > * out_corner_count ) )
{
// copy corners to output array
for ( int i = 0 ; i < n ; i + + )
out_corners [ i ] = corner_group [ i ] - > pt ;
if ( out_corner_count )
* out_corner_count = n ;
if ( count = = pattern_size . width * pattern_size . height & &
icvCheckBoardMonotony ( out_corners , pattern_size ) )
{
found = 1 ;
break ;
}
}
}
} //dilations
// revert to old, slower, method if detection failed
if ( ! found )
{
for ( k = 0 ; k < 6 ; k + + )
{
int max_quad_buf_size = 0 ;
for ( dilations = min_dilations ; dilations < = max_dilations ; dilations + + )
{
if ( found )
break ; // already found it
cvFree ( & quads ) ;
cvFree ( & corners ) ;
// convert the input grayscale image to binary (black-n-white)
if ( flags & CV_CALIB_CB_ADAPTIVE_THRESH )
{
int block_size = cvRound ( prev_sqr_size = = 0 ?
MIN ( img - > cols , img - > rows ) * ( k % 2 = = 0 ? 0.2 : 0.1 ) : prev_sqr_size * 2 ) | 1 ;
// convert to binary
cvAdaptiveThreshold ( img , thresh_img , 255 ,
CV_ADAPTIVE_THRESH_MEAN_C , CV_THRESH_BINARY , block_size , ( k / 2 ) * 5 ) ;
if ( dilations > 0 )
cvDilate ( thresh_img , thresh_img , 0 , dilations - 1 ) ;
}
//if flag CV_CALIB_CB_ADAPTIVE_THRESH is not set it doesn't make sense
//to iterate over k
else
{
k = 6 ;
cvDilate ( thresh_img , thresh_img , 0 , 1 ) ;
}
# ifdef DEBUG_CHESSBOARD
cvCopy ( dbg_img , dbg1_img ) ;
cvNamedWindow ( " all_quads " , 1 ) ;
// copy corners to temp array
for ( int i = 0 ; i < quad_count ; i + + )
{
for ( int k = 0 ; k < 4 ; k + + )
{
CvPoint2D32f pt1 , pt2 ;
CvScalar color = CV_RGB ( 30 , 255 , 30 ) ;
pt1 = quads [ i ] . corners [ k ] - > pt ;
pt2 = quads [ i ] . corners [ ( k + 1 ) % 4 ] - > pt ;
pt2 . x = ( pt1 . x + pt2 . x ) / 2 ;
pt2 . y = ( pt1 . y + pt2 . y ) / 2 ;
if ( k > 0 )
color = CV_RGB ( 200 , 200 , 0 ) ;
cvLine ( dbg1_img , cvPointFrom32f ( pt1 ) , cvPointFrom32f ( pt2 ) , color , 3 , 8 ) ;
}
}
cvCvtColor ( thresh_img , dbg_img , CV_GRAY2BGR ) ;
# endif
// So we can find rectangles that go to the edge, we draw a white line around the image edge.
// Otherwise FindContours will miss those clipped rectangle contours.
// The border color will be the image mean, because otherwise we risk screwing up filters like cvSmooth()...
cvRectangle ( thresh_img , cvPoint ( 0 , 0 ) , cvPoint ( thresh_img - > cols - 1 ,
thresh_img - > rows - 1 ) , CV_RGB ( 255 , 255 , 255 ) , 3 , 8 ) ;
cvShowImage ( " all_quads " , ( IplImage * ) dbg1_img ) ;
cvWaitKey ( ) ;
quad_count = icvGenerateQuads ( & quads , & corners , storage , thresh_img , flags , & max_quad_buf_size ) ;
PRINTF ( " Quad count: %d/%d \n " , quad_count , expected_corners_num ) ;
# ifdef DEBUG_CHESSBOARD
cvCopy ( dbg_img , dbg1_img ) ;
cvNamedWindow ( " all_quads " , 1 ) ;
// copy corners to temp array
for ( int i = 0 ; i < quad_count ; i + + )
{
for ( int z = 0 ; z < 4 ; z + + )
{
CvPoint2D32f pt1 , pt2 ;
CvScalar color = CV_RGB ( 30 , 255 , 30 ) ;
pt1 = quads [ i ] . corners [ z ] - > pt ;
pt2 = quads [ i ] . corners [ ( z + 1 ) % 4 ] - > pt ;
pt2 . x = ( pt1 . x + pt2 . x ) / 2 ;
pt2 . y = ( pt1 . y + pt2 . y ) / 2 ;
if ( z > 0 )
color = CV_RGB ( 200 , 200 , 0 ) ;
cvLine ( dbg1_img , cvPointFrom32f ( pt1 ) , cvPointFrom32f ( pt2 ) , color , 3 , 8 ) ;
}
}
cvShowImage ( " all_quads " , ( IplImage * ) dbg1_img ) ;
cvWaitKey ( ) ;
# endif
if ( quad_count < = 0 )
continue ;
if ( quad_count < = 0 )
{
continue ;
}
// Find quad's neighbors
icvFindQuadNeighbors ( quads , quad_count ) ;
// Find quad's neighbors
icvFindQuadNeighbors ( quads , quad_count ) ;
// allocate extra for adding in icvOrderFoundQuads
cvFree ( & quad_group ) ;
cvFree ( & corner_group ) ;
quad_group = ( CvCBQuad * * ) cvAlloc ( sizeof ( quad_group [ 0 ] ) * max_quad_buf_size ) ;
corner_group = ( CvCBCorner * * ) cvAlloc ( sizeof ( corner_group [ 0 ] ) * max_quad_buf_size * 4 ) ;
// allocate extra for adding in icvOrderFoundQuads
cvFree ( & quad_group ) ;
cvFree ( & corner_group ) ;
quad_group = ( CvCBQuad * * ) cvAlloc ( sizeof ( quad_group [ 0 ] ) * max_quad_buf_size ) ;
corner_group = ( CvCBCorner * * ) cvAlloc ( sizeof ( corner_group [ 0 ] ) * max_quad_buf_size * 4 ) ;
for ( group_idx = 0 ; ; group_idx + + )
{
int count = 0 ;
count = icvFindConnectedQuads ( quads , quad_count , quad_group , group_idx , storage ) ;
for ( group_idx = 0 ; ; group_idx + + )
{
int count = 0 ;
count = icvFindConnectedQuads ( quads , quad_count , quad_group , group_idx , storage ) ;
int icount = count ;
if ( count = = 0 )
break ;
int icount = count ;
if ( count = = 0 )
break ;
// order the quad corners globally
// maybe delete or add some
PRINTF ( " Starting ordering of inner quads \n " ) ;
count = icvOrderFoundConnectedQuads ( count , quad_group , & quad_count , & quads , & corners ,
pattern_size , max_quad_buf_size , storage ) ;
PRINTF ( " Orig count: %d After ordering: %d \n " , icount , count ) ;
// order the quad corners globally
// maybe delete or add some
PRINTF ( " Starting ordering of inner quads \n " ) ;
count = icvOrderFoundConnectedQuads ( count , quad_group , & quad_count , & quads , & corners , pattern_size , max_quad_buf_size , storage ) ;
PRINTF ( " Orig count: %d After ordering: %d \n " , icount , count ) ;
# ifdef DEBUG_CHESSBOARD
cvCopy ( dbg_img , dbg2_img ) ;
cvNamedWindow ( " connected_group " , 1 ) ;
// copy corners to temp array
for ( int i = 0 ; i < quad_count ; i + + )
{
if ( quads [ i ] . group_idx = = group_idx )
for ( int k = 0 ; k < 4 ; k + + )
{
CvPoint2D32f pt1 , pt2 ;
CvScalar color = CV_RGB ( 30 , 255 , 30 ) ;
if ( quads [ i ] . ordered )
color = CV_RGB ( 255 , 30 , 30 ) ;
pt1 = quads [ i ] . corners [ k ] - > pt ;
pt2 = quads [ i ] . corners [ ( k + 1 ) % 4 ] - > pt ;
pt2 . x = ( pt1 . x + pt2 . x ) / 2 ;
pt2 . y = ( pt1 . y + pt2 . y ) / 2 ;
if ( k > 0 )
color = CV_RGB ( 200 , 200 , 0 ) ;
cvLine ( dbg2_img , cvPointFrom32f ( pt1 ) , cvPointFrom32f ( pt2 ) , color , 3 , 8 ) ;
}
}
cvShowImage ( " connected_group " , ( IplImage * ) dbg2_img ) ;
cvWaitKey ( ) ;
cvCopy ( dbg_img , dbg2_img ) ;
cvNamedWindow ( " connected_group " , 1 ) ;
// copy corners to temp array
for ( int i = 0 ; i < quad_count ; i + + )
{
if ( quads [ i ] . group_idx = = group_idx )
for ( int z = 0 ; z < 4 ; z + + )
{
CvPoint2D32f pt1 , pt2 ;
CvScalar color = CV_RGB ( 30 , 255 , 30 ) ;
if ( quads [ i ] . ordered )
color = CV_RGB ( 255 , 30 , 30 ) ;
pt1 = quads [ i ] . corners [ z ] - > pt ;
pt2 = quads [ i ] . corners [ ( z + 1 ) % 4 ] - > pt ;
pt2 . x = ( pt1 . x + pt2 . x ) / 2 ;
pt2 . y = ( pt1 . y + pt2 . y ) / 2 ;
if ( z > 0 )
color = CV_RGB ( 200 , 200 , 0 ) ;
cvLine ( dbg2_img , cvPointFrom32f ( pt1 ) , cvPointFrom32f ( pt2 ) , color , 3 , 8 ) ;
}
}
cvShowImage ( " connected_group " , ( IplImage * ) dbg2_img ) ;
cvWaitKey ( ) ;
# endif
if ( count = = 0 )
continue ; // haven't found inner quads
if ( count = = 0 )
continue ; // haven't found inner quads
// If count is more than it should be, this will remove those quads
// which cause maximum deviation from a nice square pattern.
count = icvCleanFoundConnectedQuads ( count , quad_group , pattern_size ) ;
PRINTF ( " Connected group: %d orig count: %d cleaned: %d \n " , group_idx , icount , count ) ;
// If count is more than it should be, this will remove those quads
// which cause maximum deviation from a nice square pattern.
count = icvCleanFoundConnectedQuads ( count , quad_group , pattern_size ) ;
PRINTF ( " Connected group: %d orig count: %d cleaned: %d \n " , group_idx , icount , count ) ;
count = icvCheckQuadGroup ( quad_group , count , corner_group , pattern_size ) ;
PRINTF ( " Connected group: %d count: %d cleaned: %d \n " , group_idx , icount , count ) ;
count = icvCheckQuadGroup ( quad_group , count , corner_group , pattern_size ) ;
PRINTF ( " Connected group: %d count: %d cleaned: %d \n " , group_idx , icount , count ) ;
{
int n = count > 0 ? pattern_size . width * pattern_size . height : - count ;
n = MIN ( n , pattern_size . width * pattern_size . height ) ;
float sum_dist = 0 ;
int total = 0 ;
int n = count > 0 ? pattern_size . width * pattern_size . height : - count ;
n = MIN ( n , pattern_size . width * pattern_size . height ) ;
float sum_dist = 0 ;
int total = 0 ;
for ( int i = 0 ; i < n ; i + + )
{
int ni = 0 ;
float avgi = corner_group [ i ] - > meanDist ( & ni ) ;
sum_dist + = avgi * ni ;
total + = ni ;
}
prev_sqr_size = cvRound ( sum_dist / MAX ( total , 1 ) ) ;
for ( int i = 0 ; i < n ; i + + )
{
int ni = 0 ;
float avgi = corner_group [ i ] - > meanDist ( & ni ) ;
sum_dist + = avgi * ni ;
total + = ni ;
}
prev_sqr_size = cvRound ( sum_dist / MAX ( total , 1 ) ) ;
if ( count > 0 | | ( out_corner_count & & - count > * out_corner_count ) )
{
// copy corners to output array
for ( int i = 0 ; i < n ; i + + )
out_corners [ i ] = corner_group [ i ] - > pt ;
if ( count > 0 | | ( out_corner_count & & - count > * out_corner_count ) )
{
// copy corners to output array
for ( int i = 0 ; i < n ; i + + )
out_corners [ i ] = corner_group [ i ] - > pt ;
if ( out_corner_count )
* out_corner_count = n ;
if ( out_corner_count )
* out_corner_count = n ;
if ( count = = pattern_size . width * pattern_size . height & &
icvCheckBoardMonotony ( out_corners , pattern_size ) )
{
found = 1 ;
break ;
}
}
}
if ( count = = pattern_size . width * pattern_size . height & & icvCheckBoardMonotony ( out_corners , pattern_size ) )
{
found = 1 ;
break ;
}
}
}
} //dilations
} //
} // for k = 0 -> 6
}
if ( found )
found = icvCheckBoardMonotony ( out_corners , pattern_size ) ;
@ -559,6 +865,7 @@ int cvFindChessboardCorners( const void* arr, CvSize pattern_size,
cvFree ( & corners ) ;
cvFree ( & quad_group ) ;
cvFree ( & corner_group ) ;
cvFree ( & cImgSeg ) ;
throw ;
}
@ -566,6 +873,7 @@ int cvFindChessboardCorners( const void* arr, CvSize pattern_size,
cvFree ( & corners ) ;
cvFree ( & quad_group ) ;
cvFree ( & corner_group ) ;
cvFree ( & cImgSeg ) ;
return found ;
}