|
|
@ -76,6 +76,9 @@ |
|
|
|
#include <stdarg.h> |
|
|
|
#include <stdarg.h> |
|
|
|
#include <vector> |
|
|
|
#include <vector> |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
using namespace cv; |
|
|
|
|
|
|
|
using namespace std; |
|
|
|
|
|
|
|
|
|
|
|
//#define ENABLE_TRIM_COL_ROW
|
|
|
|
//#define ENABLE_TRIM_COL_ROW
|
|
|
|
|
|
|
|
|
|
|
|
//#define DEBUG_CHESSBOARD
|
|
|
|
//#define DEBUG_CHESSBOARD
|
|
|
@ -88,13 +91,9 @@ static int PRINTF( const char* fmt, ... ) |
|
|
|
return vprintf(fmt, args); |
|
|
|
return vprintf(fmt, args); |
|
|
|
} |
|
|
|
} |
|
|
|
#else |
|
|
|
#else |
|
|
|
static int PRINTF( const char*, ... ) |
|
|
|
#define PRINTF(...) |
|
|
|
{ |
|
|
|
|
|
|
|
return 0; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
#endif |
|
|
|
#endif |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//=====================================================================================
|
|
|
|
//=====================================================================================
|
|
|
|
// Implementation for the enhanced calibration object detection
|
|
|
|
// Implementation for the enhanced calibration object detection
|
|
|
|
//=====================================================================================
|
|
|
|
//=====================================================================================
|
|
|
@ -155,10 +154,42 @@ struct CvCBQuad |
|
|
|
|
|
|
|
|
|
|
|
//=====================================================================================
|
|
|
|
//=====================================================================================
|
|
|
|
|
|
|
|
|
|
|
|
//static CvMat* debug_img = 0;
|
|
|
|
#ifdef DEBUG_CHESSBOARD |
|
|
|
|
|
|
|
#include "opencv2/highgui.hpp" |
|
|
|
|
|
|
|
#include "opencv2/imgproc.hpp" |
|
|
|
|
|
|
|
static void SHOW(const std::string & name, Mat & img) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
imshow(name, img); |
|
|
|
|
|
|
|
while ((uchar)waitKey(0) != 'q') {} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
static void SHOW_QUADS(const std::string & name, const Mat & img_, CvCBQuad * quads, int quads_count) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
Mat img = img_.clone(); |
|
|
|
|
|
|
|
if (img.channels() == 1) |
|
|
|
|
|
|
|
cvtColor(img, img, COLOR_GRAY2BGR); |
|
|
|
|
|
|
|
for (int i = 0; i < quads_count; ++i) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
CvCBQuad & quad = quads[i]; |
|
|
|
|
|
|
|
for (int j = 0; j < 4; ++j) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
line(img, quad.corners[j]->pt, quad.corners[(j + 1) % 4]->pt, Scalar(0, 240, 0), 1, LINE_AA); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
imshow(name, img); |
|
|
|
|
|
|
|
while ((uchar)waitKey(0) != 'q') {} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
#else |
|
|
|
|
|
|
|
#define SHOW(...) |
|
|
|
|
|
|
|
#define SHOW_QUADS(...) |
|
|
|
|
|
|
|
#endif |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//=====================================================================================
|
|
|
|
|
|
|
|
|
|
|
|
static int icvGenerateQuads( CvCBQuad **quads, CvCBCorner **corners, |
|
|
|
static int icvGenerateQuads( CvCBQuad **quads, CvCBCorner **corners, |
|
|
|
CvMemStorage *storage, CvMat *image, int flags, int *max_quad_buf_size); |
|
|
|
CvMemStorage *storage, const Mat &image_, int flags, int *max_quad_buf_size); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static bool processQuads(CvCBQuad *quads, int quad_count, CvSize pattern_size, int max_quad_buf_size, |
|
|
|
|
|
|
|
CvMemStorage * storage, CvCBCorner *corners, CvPoint2D32f *out_corners, int *out_corner_count, int & prev_sqr_size); |
|
|
|
|
|
|
|
|
|
|
|
/*static int
|
|
|
|
/*static int
|
|
|
|
icvGenerateQuadsEx( CvCBQuad **out_quads, CvCBCorner **out_corners, |
|
|
|
icvGenerateQuadsEx( CvCBQuad **out_quads, CvCBCorner **out_corners, |
|
|
@ -195,35 +226,24 @@ static void icvRemoveQuadFromGroup(CvCBQuad **quads, int count, CvCBQuad *q0); |
|
|
|
|
|
|
|
|
|
|
|
static int icvCheckBoardMonotony( CvPoint2D32f* corners, CvSize pattern_size ); |
|
|
|
static int icvCheckBoardMonotony( CvPoint2D32f* corners, CvSize pattern_size ); |
|
|
|
|
|
|
|
|
|
|
|
int cvCheckChessboardBinary(IplImage* src, CvSize size); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/***************************************************************************************************/ |
|
|
|
/***************************************************************************************************/ |
|
|
|
//COMPUTE INTENSITY HISTOGRAM OF INPUT IMAGE
|
|
|
|
//COMPUTE INTENSITY HISTOGRAM OF INPUT IMAGE
|
|
|
|
static int icvGetIntensityHistogram( unsigned char* pucImage, int iSizeCols, int iSizeRows, std::vector<int>& piHist ); |
|
|
|
static int icvGetIntensityHistogram( const Mat & img, 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
|
|
|
|
// sum up all pixel in row direction and divide by number of columns
|
|
|
|
for ( int j=0; j<iSizeRows; j++ ) |
|
|
|
for ( int j=0; j<img.rows; j++ ) |
|
|
|
{ |
|
|
|
{ |
|
|
|
for ( int i=0; i<iSizeCols; i++ ) |
|
|
|
const uchar * row = img.ptr(j); |
|
|
|
|
|
|
|
for ( int i=0; i<img.cols; i++ ) |
|
|
|
{ |
|
|
|
{ |
|
|
|
iVal = (int)pucImage[j*iSizeCols+i]; |
|
|
|
piHist[row[i]]++; |
|
|
|
piHist[iVal]++; |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
return 0; |
|
|
|
return 0; |
|
|
|
} |
|
|
|
} |
|
|
|
/***************************************************************************************************/ |
|
|
|
/***************************************************************************************************/ |
|
|
|
int icvSmoothHistogram( const std::vector<int>& piHist, std::vector<int>& piHistSmooth, int iWidth ) |
|
|
|
//SMOOTH HISTOGRAM USING WINDOW OF SIZE 2*iWidth+1
|
|
|
|
|
|
|
|
static int icvSmoothHistogram( const std::vector<int>& piHist, std::vector<int>& piHistSmooth, int iWidth ) |
|
|
|
{ |
|
|
|
{ |
|
|
|
int iIdx; |
|
|
|
int iIdx; |
|
|
|
for ( int i=0; i<256; i++) |
|
|
|
for ( int i=0; i<256; i++) |
|
|
@ -242,7 +262,8 @@ int icvSmoothHistogram( const std::vector<int>& piHist, std::vector<int>& piHist |
|
|
|
return 0; |
|
|
|
return 0; |
|
|
|
} |
|
|
|
} |
|
|
|
/***************************************************************************************************/ |
|
|
|
/***************************************************************************************************/ |
|
|
|
int icvGradientOfHistogram( const std::vector<int>& piHist, std::vector<int>& piHistGrad ) |
|
|
|
//COMPUTE FAST HISTOGRAM GRADIENT
|
|
|
|
|
|
|
|
static int icvGradientOfHistogram( const std::vector<int>& piHist, std::vector<int>& piHistGrad ) |
|
|
|
{ |
|
|
|
{ |
|
|
|
piHistGrad[0] = 0; |
|
|
|
piHistGrad[0] = 0; |
|
|
|
for ( int i=1; i<255; i++) |
|
|
|
for ( int i=1; i<255; i++) |
|
|
@ -259,8 +280,12 @@ int icvGradientOfHistogram( const std::vector<int>& piHist, std::vector<int>& pi |
|
|
|
return 0; |
|
|
|
return 0; |
|
|
|
} |
|
|
|
} |
|
|
|
/***************************************************************************************************/ |
|
|
|
/***************************************************************************************************/ |
|
|
|
bool icvBinarizationHistogramBased( unsigned char* pucImg, int iCols, int iRows ) |
|
|
|
//PERFORM SMART IMAGE THRESHOLDING BASED ON ANALYSIS OF INTENSTY HISTOGRAM
|
|
|
|
|
|
|
|
static bool icvBinarizationHistogramBased( Mat & img ) |
|
|
|
{ |
|
|
|
{ |
|
|
|
|
|
|
|
CV_Assert(img.channels() == 1 && img.depth() == CV_8U); |
|
|
|
|
|
|
|
int iCols = img.cols; |
|
|
|
|
|
|
|
int iRows = img.rows; |
|
|
|
int iMaxPix = iCols*iRows; |
|
|
|
int iMaxPix = iCols*iRows; |
|
|
|
int iMaxPix1 = iMaxPix/100; |
|
|
|
int iMaxPix1 = iMaxPix/100; |
|
|
|
const int iNumBins = 256; |
|
|
|
const int iNumBins = 256; |
|
|
@ -273,7 +298,7 @@ bool icvBinarizationHistogramBased( unsigned char* pucImg, int iCols, int iRows |
|
|
|
int iIdx; |
|
|
|
int iIdx; |
|
|
|
int iWidth = 1; |
|
|
|
int iWidth = 1; |
|
|
|
|
|
|
|
|
|
|
|
icvGetIntensityHistogram( pucImg, iCols, iRows, piHistIntensity ); |
|
|
|
icvGetIntensityHistogram( img, piHistIntensity ); |
|
|
|
|
|
|
|
|
|
|
|
// get accumulated sum starting from bright
|
|
|
|
// get accumulated sum starting from bright
|
|
|
|
piAccumSum[iNumBins-1] = piHistIntensity[iNumBins-1]; |
|
|
|
piAccumSum[iNumBins-1] = piHistIntensity[iNumBins-1]; |
|
|
@ -381,12 +406,13 @@ bool icvBinarizationHistogramBased( unsigned char* pucImg, int iCols, int iRows |
|
|
|
{ |
|
|
|
{ |
|
|
|
for ( int jj=0; jj<iRows; jj++) |
|
|
|
for ( int jj=0; jj<iRows; jj++) |
|
|
|
{ |
|
|
|
{ |
|
|
|
|
|
|
|
uchar * row = img.ptr(jj); |
|
|
|
for ( int ii=0; ii<iCols; ii++) |
|
|
|
for ( int ii=0; ii<iCols; ii++) |
|
|
|
{ |
|
|
|
{ |
|
|
|
if ( pucImg[jj*iCols+ii]< iThresh ) |
|
|
|
if ( row[ii] < iThresh ) |
|
|
|
pucImg[jj*iCols+ii] = 0; |
|
|
|
row[ii] = 0; |
|
|
|
else |
|
|
|
else |
|
|
|
pucImg[jj*iCols+ii] = 255; |
|
|
|
row[ii] = 255; |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
@ -400,38 +426,23 @@ int cvFindChessboardCorners( const void* arr, CvSize pattern_size, |
|
|
|
int flags ) |
|
|
|
int flags ) |
|
|
|
{ |
|
|
|
{ |
|
|
|
int found = 0; |
|
|
|
int found = 0; |
|
|
|
CvCBQuad *quads = 0, **quad_group = 0; |
|
|
|
CvCBQuad *quads = 0; |
|
|
|
CvCBCorner *corners = 0, **corner_group = 0; |
|
|
|
CvCBCorner *corners = 0; |
|
|
|
IplImage* cImgSeg = 0; |
|
|
|
|
|
|
|
|
|
|
|
cv::Ptr<CvMemStorage> storage; |
|
|
|
|
|
|
|
|
|
|
|
try |
|
|
|
try |
|
|
|
{ |
|
|
|
{ |
|
|
|
int k = 0; |
|
|
|
int k = 0; |
|
|
|
const int min_dilations = 0; |
|
|
|
const int min_dilations = 0; |
|
|
|
const int max_dilations = 7; |
|
|
|
const int max_dilations = 7; |
|
|
|
cv::Ptr<CvMat> norm_img, thresh_img; |
|
|
|
|
|
|
|
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); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
int prev_sqr_size = 0; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if( out_corner_count ) |
|
|
|
if( out_corner_count ) |
|
|
|
*out_corner_count = 0; |
|
|
|
*out_corner_count = 0; |
|
|
|
|
|
|
|
|
|
|
|
int quad_count = 0, group_idx = 0, dilations = 0; |
|
|
|
Mat img = cvarrToMat((CvMat*)arr).clone(); |
|
|
|
|
|
|
|
|
|
|
|
img = cvGetMat( img, &stub ); |
|
|
|
|
|
|
|
//debug_img = img;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if( CV_MAT_DEPTH( img->type ) != CV_8U || CV_MAT_CN( img->type ) == 2 ) |
|
|
|
if( img.depth() != CV_8U || (img.channels() != 1 && img.channels() != 3) ) |
|
|
|
CV_Error( CV_StsUnsupportedFormat, "Only 8-bit grayscale or color images are supported" ); |
|
|
|
CV_Error( CV_StsUnsupportedFormat, "Only 8-bit grayscale or color images are supported" ); |
|
|
|
|
|
|
|
|
|
|
|
if( pattern_size.width <= 2 || pattern_size.height <= 2 ) |
|
|
|
if( pattern_size.width <= 2 || pattern_size.height <= 2 ) |
|
|
@ -440,273 +451,124 @@ int cvFindChessboardCorners( const void* arr, CvSize pattern_size, |
|
|
|
if( !out_corners ) |
|
|
|
if( !out_corners ) |
|
|
|
CV_Error( CV_StsNullPtr, "Null pointer to corners" ); |
|
|
|
CV_Error( CV_StsNullPtr, "Null pointer to corners" ); |
|
|
|
|
|
|
|
|
|
|
|
storage.reset(cvCreateMemStorage(0)); |
|
|
|
if (img.channels() != 1) |
|
|
|
thresh_img.reset(cvCreateMat( img->rows, img->cols, CV_8UC1 )); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if( CV_MAT_CN(img->type) != 1 || (flags & CV_CALIB_CB_NORMALIZE_IMAGE) ) |
|
|
|
|
|
|
|
{ |
|
|
|
{ |
|
|
|
// equalize the input image histogram -
|
|
|
|
cvtColor(img, img, COLOR_BGR2GRAY); |
|
|
|
// that should make the contrast between "black" and "white" areas big enough
|
|
|
|
|
|
|
|
norm_img.reset(cvCreateMat( img->rows, img->cols, CV_8UC1 )); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if( CV_MAT_CN(img->type) != 1 ) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
cvCvtColor( img, norm_img, CV_BGR2GRAY ); |
|
|
|
|
|
|
|
img = norm_img; |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
if( flags & CV_CALIB_CB_NORMALIZE_IMAGE ) |
|
|
|
|
|
|
|
{ |
|
|
|
Mat thresh_img_new = img.clone(); |
|
|
|
cvEqualizeHist( img, norm_img ); |
|
|
|
icvBinarizationHistogramBased( thresh_img_new ); // process image in-place
|
|
|
|
img = norm_img; |
|
|
|
SHOW("New binarization", thresh_img_new); |
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if( flags & CV_CALIB_CB_FAST_CHECK) |
|
|
|
if( flags & CV_CALIB_CB_FAST_CHECK) |
|
|
|
{ |
|
|
|
{ |
|
|
|
//perform new method for checking chessboard using a binary image.
|
|
|
|
//perform new method for checking chessboard using a binary image.
|
|
|
|
//image is binarised using a threshold dependent on the image histogram
|
|
|
|
//image is binarised using a threshold dependent on the image histogram
|
|
|
|
icvBinarizationHistogramBased( (unsigned char*) cImgSeg->imageData, cImgSeg->width, cImgSeg->height ); |
|
|
|
if (checkChessboardBinary(thresh_img_new, pattern_size) <= 0) //fall back to the old method
|
|
|
|
int check_chessboard_result = cvCheckChessboardBinary(cImgSeg, pattern_size); |
|
|
|
|
|
|
|
if(check_chessboard_result <= 0) //fall back to the old method
|
|
|
|
|
|
|
|
{ |
|
|
|
{ |
|
|
|
IplImage _img; |
|
|
|
if (checkChessboard(img, pattern_size) <= 0) |
|
|
|
cvGetImage(img, &_img); |
|
|
|
|
|
|
|
check_chessboard_result = cvCheckChessboard(&_img, pattern_size); |
|
|
|
|
|
|
|
if(check_chessboard_result <= 0) |
|
|
|
|
|
|
|
{ |
|
|
|
{ |
|
|
|
return 0; |
|
|
|
return found; |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
storage.reset(cvCreateMemStorage(0)); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
int prev_sqr_size = 0; |
|
|
|
|
|
|
|
|
|
|
|
// Try our standard "1" dilation, but if the pattern is not found, iterate the whole procedure with higher dilations.
|
|
|
|
// 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,
|
|
|
|
// 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,
|
|
|
|
// 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.
|
|
|
|
// making it difficult to detect smaller squares.
|
|
|
|
for( dilations = min_dilations; dilations <= max_dilations; dilations++ ) |
|
|
|
for( int dilations = min_dilations; dilations <= max_dilations; dilations++ ) |
|
|
|
{ |
|
|
|
{ |
|
|
|
if (found) |
|
|
|
if (found) |
|
|
|
break; // already found it
|
|
|
|
break; // already found it
|
|
|
|
|
|
|
|
|
|
|
|
cvFree(&quads); |
|
|
|
|
|
|
|
cvFree(&corners); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
int max_quad_buf_size = 0; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//USE BINARY IMAGE COMPUTED USING icvBinarizationHistogramBased METHOD
|
|
|
|
//USE BINARY IMAGE COMPUTED USING icvBinarizationHistogramBased METHOD
|
|
|
|
cvDilate( thresh_img_new, thresh_img_new, 0, 1 ); |
|
|
|
dilate( thresh_img_new, thresh_img_new, Mat(), Point(-1, -1), 1 ); |
|
|
|
|
|
|
|
|
|
|
|
// So we can find rectangles that go to the edge, we draw a white line around the image edge.
|
|
|
|
// 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.
|
|
|
|
// 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()...
|
|
|
|
// 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); |
|
|
|
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); |
|
|
|
quad_count = icvGenerateQuads( &quads, &corners, storage, thresh_img_new, flags, &max_quad_buf_size ); |
|
|
|
int max_quad_buf_size = 0; |
|
|
|
PRINTF("Quad count: %d/%d\n", quad_count, expected_corners_num); |
|
|
|
cvFree(&quads); |
|
|
|
|
|
|
|
cvFree(&corners); |
|
|
|
if( quad_count <= 0 ) |
|
|
|
int quad_count = icvGenerateQuads( &quads, &corners, storage, thresh_img_new, flags, &max_quad_buf_size ); |
|
|
|
{ |
|
|
|
PRINTF("Quad count: %d/%d\n", quad_count, (pattern_size.width/2+1)*(pattern_size.height/2+1)); |
|
|
|
continue; |
|
|
|
SHOW_QUADS("New quads", thresh_img_new, quads, quad_count); |
|
|
|
} |
|
|
|
if (processQuads(quads, quad_count, pattern_size, max_quad_buf_size, storage, corners, out_corners, out_corner_count, prev_sqr_size)) |
|
|
|
|
|
|
|
|
|
|
|
// 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 ); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 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); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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; |
|
|
|
found = 1; |
|
|
|
break; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
}//dilations
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
PRINTF("Chessboard detection result 0: %d\n", found); |
|
|
|
PRINTF("Chessboard detection result 0: %d\n", found); |
|
|
|
|
|
|
|
|
|
|
|
// revert to old, slower, method if detection failed
|
|
|
|
// revert to old, slower, method if detection failed
|
|
|
|
if (!found) |
|
|
|
if (!found) |
|
|
|
{ |
|
|
|
{ |
|
|
|
|
|
|
|
if( flags & CV_CALIB_CB_NORMALIZE_IMAGE ) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
equalizeHist( img, img ); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Mat thresh_img; |
|
|
|
|
|
|
|
prev_sqr_size = 0; |
|
|
|
|
|
|
|
|
|
|
|
PRINTF("Fallback to old algorithm\n"); |
|
|
|
PRINTF("Fallback to old algorithm\n"); |
|
|
|
|
|
|
|
const bool useAdaptive = flags & CV_CALIB_CB_ADAPTIVE_THRESH; |
|
|
|
|
|
|
|
if (!useAdaptive) |
|
|
|
|
|
|
|
{ |
|
|
|
// empiric threshold level
|
|
|
|
// empiric threshold level
|
|
|
|
// thresholding performed here and not inside the cycle to save processing time
|
|
|
|
// thresholding performed here and not inside the cycle to save processing time
|
|
|
|
int thresh_level; |
|
|
|
double mean = cv::mean(img).val[0]; |
|
|
|
if ( !(flags & CV_CALIB_CB_ADAPTIVE_THRESH) ) |
|
|
|
int thresh_level = MAX(cvRound( mean - 10 ), 10); |
|
|
|
{ |
|
|
|
threshold( img, thresh_img, thresh_level, 255, THRESH_BINARY ); |
|
|
|
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 ); |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
for( k = 0; k < 6; k++ ) |
|
|
|
//if flag CV_CALIB_CB_ADAPTIVE_THRESH is not set it doesn't make sense to iterate over k
|
|
|
|
|
|
|
|
int max_k = useAdaptive ? 6 : 1; |
|
|
|
|
|
|
|
for( k = 0; k < max_k; k++ ) |
|
|
|
{ |
|
|
|
{ |
|
|
|
int max_quad_buf_size = 0; |
|
|
|
for( int dilations = min_dilations; dilations <= max_dilations; dilations++ ) |
|
|
|
for( dilations = min_dilations; dilations <= max_dilations; dilations++ ) |
|
|
|
|
|
|
|
{ |
|
|
|
{ |
|
|
|
if (found) |
|
|
|
if (found) |
|
|
|
break; // already found it
|
|
|
|
break; // already found it
|
|
|
|
|
|
|
|
|
|
|
|
cvFree(&quads); |
|
|
|
|
|
|
|
cvFree(&corners); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// convert the input grayscale image to binary (black-n-white)
|
|
|
|
// convert the input grayscale image to binary (black-n-white)
|
|
|
|
if( flags & CV_CALIB_CB_ADAPTIVE_THRESH ) |
|
|
|
if (useAdaptive) |
|
|
|
{ |
|
|
|
{ |
|
|
|
int block_size = cvRound(prev_sqr_size == 0 ? |
|
|
|
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; |
|
|
|
? MIN(img.cols, img.rows) * (k % 2 == 0 ? 0.2 : 0.1) |
|
|
|
|
|
|
|
: prev_sqr_size * 2); |
|
|
|
|
|
|
|
block_size = block_size | 1; |
|
|
|
// convert to binary
|
|
|
|
// convert to binary
|
|
|
|
cvAdaptiveThreshold( img, thresh_img, 255, |
|
|
|
adaptiveThreshold( img, thresh_img, 255, ADAPTIVE_THRESH_MEAN_C, THRESH_BINARY, block_size, (k/2)*5 ); |
|
|
|
CV_ADAPTIVE_THRESH_MEAN_C, CV_THRESH_BINARY, block_size, (k/2)*5 ); |
|
|
|
|
|
|
|
if (dilations > 0) |
|
|
|
if (dilations > 0) |
|
|
|
cvDilate( thresh_img, thresh_img, 0, dilations-1 ); |
|
|
|
dilate( thresh_img, thresh_img, Mat(), Point(-1, -1), dilations-1 ); |
|
|
|
|
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
//if flag CV_CALIB_CB_ADAPTIVE_THRESH is not set it doesn't make sense
|
|
|
|
|
|
|
|
//to iterate over k
|
|
|
|
|
|
|
|
else |
|
|
|
else |
|
|
|
{ |
|
|
|
{ |
|
|
|
k = 6; |
|
|
|
dilate( thresh_img, thresh_img, Mat(), Point(-1, -1), 1 ); |
|
|
|
cvDilate( thresh_img, thresh_img, 0, 1 ); |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
SHOW("Old binarization", thresh_img); |
|
|
|
|
|
|
|
|
|
|
|
// So we can find rectangles that go to the edge, we draw a white line around the image edge.
|
|
|
|
// 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.
|
|
|
|
// 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()...
|
|
|
|
// 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, |
|
|
|
rectangle( thresh_img, Point(0,0), Point(thresh_img.cols-1, thresh_img.rows-1), Scalar(255,255,255), 3, LINE_8); |
|
|
|
thresh_img->rows-1), CV_RGB(255,255,255), 3, 8); |
|
|
|
int max_quad_buf_size = 0; |
|
|
|
|
|
|
|
cvFree(&quads); |
|
|
|
quad_count = icvGenerateQuads( &quads, &corners, storage, thresh_img, flags, &max_quad_buf_size); |
|
|
|
cvFree(&corners); |
|
|
|
PRINTF("Quad count: %d/%d\n", quad_count, expected_corners_num); |
|
|
|
int quad_count = icvGenerateQuads( &quads, &corners, storage, thresh_img, flags, &max_quad_buf_size); |
|
|
|
|
|
|
|
PRINTF("Quad count: %d/%d\n", quad_count, (pattern_size.width/2+1)*(pattern_size.height/2+1)); |
|
|
|
if( quad_count <= 0 ) |
|
|
|
SHOW_QUADS("Old quads", thresh_img, quads, quad_count); |
|
|
|
{ |
|
|
|
if (processQuads(quads, quad_count, pattern_size, max_quad_buf_size, storage, corners, out_corners, out_corner_count, prev_sqr_size)) |
|
|
|
continue; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 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 ); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 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); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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; |
|
|
|
found = 1; |
|
|
|
break; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
}//dilations
|
|
|
|
|
|
|
|
}// for k = 0 -> 6
|
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
PRINTF("Chessboard detection result 1: %d\n", found); |
|
|
|
PRINTF("Chessboard detection result 1: %d\n", found); |
|
|
@ -722,8 +584,8 @@ int cvFindChessboardCorners( const void* arr, CvSize pattern_size, |
|
|
|
const int BORDER = 8; |
|
|
|
const int BORDER = 8; |
|
|
|
for( k = 0; k < pattern_size.width*pattern_size.height; k++ ) |
|
|
|
for( k = 0; k < pattern_size.width*pattern_size.height; k++ ) |
|
|
|
{ |
|
|
|
{ |
|
|
|
if( out_corners[k].x <= BORDER || out_corners[k].x > img->cols - BORDER || |
|
|
|
if( out_corners[k].x <= BORDER || out_corners[k].x > img.cols - BORDER || |
|
|
|
out_corners[k].y <= BORDER || out_corners[k].y > img->rows - BORDER ) |
|
|
|
out_corners[k].y <= BORDER || out_corners[k].y > img.rows - BORDER ) |
|
|
|
break; |
|
|
|
break; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
@ -748,18 +610,9 @@ int cvFindChessboardCorners( const void* arr, CvSize pattern_size, |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
cv::Ptr<CvMat> gray; |
|
|
|
|
|
|
|
if( CV_MAT_CN(img->type) != 1 ) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
gray.reset(cvCreateMat(img->rows, img->cols, CV_8UC1)); |
|
|
|
|
|
|
|
cvCvtColor(img, gray, CV_BGR2GRAY); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
else |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
gray.reset(cvCloneMat(img)); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
int wsize = 2; |
|
|
|
int wsize = 2; |
|
|
|
cvFindCornerSubPix( gray, out_corners, pattern_size.width*pattern_size.height, |
|
|
|
CvMat old_img(img); |
|
|
|
|
|
|
|
cvFindCornerSubPix( &old_img, out_corners, pattern_size.width*pattern_size.height, |
|
|
|
cvSize(wsize, wsize), cvSize(-1,-1), |
|
|
|
cvSize(wsize, wsize), cvSize(-1,-1), |
|
|
|
cvTermCriteria(CV_TERMCRIT_EPS+CV_TERMCRIT_ITER, 15, 0.1)); |
|
|
|
cvTermCriteria(CV_TERMCRIT_EPS+CV_TERMCRIT_ITER, 15, 0.1)); |
|
|
|
} |
|
|
|
} |
|
|
@ -768,17 +621,10 @@ int cvFindChessboardCorners( const void* arr, CvSize pattern_size, |
|
|
|
{ |
|
|
|
{ |
|
|
|
cvFree(&quads); |
|
|
|
cvFree(&quads); |
|
|
|
cvFree(&corners); |
|
|
|
cvFree(&corners); |
|
|
|
cvFree(&quad_group); |
|
|
|
|
|
|
|
cvFree(&corner_group); |
|
|
|
|
|
|
|
cvFree(&cImgSeg); |
|
|
|
|
|
|
|
throw; |
|
|
|
throw; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
cvFree(&quads); |
|
|
|
cvFree(&quads); |
|
|
|
cvFree(&corners); |
|
|
|
cvFree(&corners); |
|
|
|
cvFree(&quad_group); |
|
|
|
|
|
|
|
cvFree(&corner_group); |
|
|
|
|
|
|
|
cvFree(&cImgSeg); |
|
|
|
|
|
|
|
return found; |
|
|
|
return found; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
@ -1866,8 +1712,9 @@ static void icvFindQuadNeighbors( CvCBQuad *quads, int quad_count ) |
|
|
|
|
|
|
|
|
|
|
|
static int |
|
|
|
static int |
|
|
|
icvGenerateQuads( CvCBQuad **out_quads, CvCBCorner **out_corners, |
|
|
|
icvGenerateQuads( CvCBQuad **out_quads, CvCBCorner **out_corners, |
|
|
|
CvMemStorage *storage, CvMat *image, int flags, int *max_quad_buf_size ) |
|
|
|
CvMemStorage *storage, const cv::Mat & image_, int flags, int *max_quad_buf_size ) |
|
|
|
{ |
|
|
|
{ |
|
|
|
|
|
|
|
CvMat image_old(image_), *image = &image_old; |
|
|
|
int quad_count = 0; |
|
|
|
int quad_count = 0; |
|
|
|
cv::Ptr<CvMemStorage> temp_storage; |
|
|
|
cv::Ptr<CvMemStorage> temp_storage; |
|
|
|
|
|
|
|
|
|
|
@ -2011,6 +1858,88 @@ icvGenerateQuads( CvCBQuad **out_quads, CvCBCorner **out_corners, |
|
|
|
return quad_count; |
|
|
|
return quad_count; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static bool processQuads(CvCBQuad *quads, int quad_count, CvSize pattern_size, int max_quad_buf_size, |
|
|
|
|
|
|
|
CvMemStorage * storage, CvCBCorner *corners, CvPoint2D32f *out_corners, int *out_corner_count, int & prev_sqr_size) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
if( quad_count <= 0 ) |
|
|
|
|
|
|
|
return false; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
bool found = false; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Find quad's neighbors
|
|
|
|
|
|
|
|
icvFindQuadNeighbors( quads, quad_count ); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// allocate extra for adding in icvOrderFoundQuads
|
|
|
|
|
|
|
|
CvCBQuad **quad_group = 0; |
|
|
|
|
|
|
|
CvCBCorner **corner_group = 0; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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( int group_idx = 0; ; group_idx++ ) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
int count = icvFindConnectedQuads( quads, quad_count, quad_group, group_idx, storage ); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if( count == 0 ) |
|
|
|
|
|
|
|
break; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// order the quad corners globally
|
|
|
|
|
|
|
|
// maybe delete or add some
|
|
|
|
|
|
|
|
PRINTF("Starting ordering of inner quads (%d)\n", count); |
|
|
|
|
|
|
|
count = icvOrderFoundConnectedQuads(count, quad_group, &quad_count, &quads, &corners, |
|
|
|
|
|
|
|
pattern_size, max_quad_buf_size, storage ); |
|
|
|
|
|
|
|
PRINTF("Finished ordering of inner quads (%d)\n", 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, count: %d\n", group_idx, count); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
count = icvCheckQuadGroup( quad_group, count, corner_group, pattern_size ); |
|
|
|
|
|
|
|
PRINTF("Connected group: %d, count: %d\n", group_idx, 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 = true; |
|
|
|
|
|
|
|
break; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
cvFree(&quad_group); |
|
|
|
|
|
|
|
cvFree(&corner_group); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return found; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//==================================================================================================
|
|
|
|
|
|
|
|
|
|
|
|
CV_IMPL void |
|
|
|
CV_IMPL void |
|
|
|
cvDrawChessboardCorners( CvArr* _image, CvSize pattern_size, |
|
|
|
cvDrawChessboardCorners( CvArr* _image, CvSize pattern_size, |
|
|
|