mirror of
https://github.com/opencv/opencv.git
synced 2025-07-31 18:07:08 +08:00
Merge pull request #6956 from mshabunin:fix-chessboard-bug
This commit is contained in:
commit
73e1d64ae0
@ -76,6 +76,9 @@
|
||||
#include <stdarg.h>
|
||||
#include <vector>
|
||||
|
||||
using namespace cv;
|
||||
using namespace std;
|
||||
|
||||
//#define ENABLE_TRIM_COL_ROW
|
||||
|
||||
//#define DEBUG_CHESSBOARD
|
||||
@ -88,13 +91,9 @@ static int PRINTF( const char* fmt, ... )
|
||||
return vprintf(fmt, args);
|
||||
}
|
||||
#else
|
||||
static int PRINTF( const char*, ... )
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
#define PRINTF(...)
|
||||
#endif
|
||||
|
||||
|
||||
//=====================================================================================
|
||||
// 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,
|
||||
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
|
||||
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 );
|
||||
|
||||
int cvCheckChessboardBinary(IplImage* src, CvSize 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 )
|
||||
static int icvGetIntensityHistogram( const Mat & img, 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 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[iVal]++;
|
||||
piHist[row[i]]++;
|
||||
}
|
||||
}
|
||||
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;
|
||||
for ( int i=0; i<256; i++)
|
||||
@ -242,7 +262,8 @@ int icvSmoothHistogram( const std::vector<int>& piHist, std::vector<int>& piHist
|
||||
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;
|
||||
for ( int i=1; i<255; i++)
|
||||
@ -259,8 +280,12 @@ int icvGradientOfHistogram( const std::vector<int>& piHist, std::vector<int>& pi
|
||||
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 iMaxPix1 = iMaxPix/100;
|
||||
const int iNumBins = 256;
|
||||
@ -273,7 +298,7 @@ bool icvBinarizationHistogramBased( unsigned char* pucImg, int iCols, int iRows
|
||||
int iIdx;
|
||||
int iWidth = 1;
|
||||
|
||||
icvGetIntensityHistogram( pucImg, iCols, iRows, piHistIntensity );
|
||||
icvGetIntensityHistogram( img, piHistIntensity );
|
||||
|
||||
// get accumulated sum starting from bright
|
||||
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++)
|
||||
{
|
||||
uchar * row = img.ptr(jj);
|
||||
for ( int ii=0; ii<iCols; ii++)
|
||||
{
|
||||
if ( pucImg[jj*iCols+ii]< iThresh )
|
||||
pucImg[jj*iCols+ii] = 0;
|
||||
if ( row[ii] < iThresh )
|
||||
row[ii] = 0;
|
||||
else
|
||||
pucImg[jj*iCols+ii] = 255;
|
||||
row[ii] = 255;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -400,38 +426,23 @@ int cvFindChessboardCorners( const void* arr, CvSize pattern_size,
|
||||
int flags )
|
||||
{
|
||||
int found = 0;
|
||||
CvCBQuad *quads = 0, **quad_group = 0;
|
||||
CvCBCorner *corners = 0, **corner_group = 0;
|
||||
IplImage* cImgSeg = 0;
|
||||
CvCBQuad *quads = 0;
|
||||
CvCBCorner *corners = 0;
|
||||
|
||||
cv::Ptr<CvMemStorage> storage;
|
||||
|
||||
try
|
||||
{
|
||||
int k = 0;
|
||||
const int min_dilations = 0;
|
||||
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 )
|
||||
*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" );
|
||||
|
||||
if( pattern_size.width <= 2 || pattern_size.height <= 2 )
|
||||
@ -440,274 +451,125 @@ int cvFindChessboardCorners( const void* arr, CvSize pattern_size,
|
||||
if( !out_corners )
|
||||
CV_Error( CV_StsNullPtr, "Null pointer to corners" );
|
||||
|
||||
storage.reset(cvCreateMemStorage(0));
|
||||
thresh_img.reset(cvCreateMat( img->rows, img->cols, CV_8UC1 ));
|
||||
|
||||
if( CV_MAT_CN(img->type) != 1 || (flags & CV_CALIB_CB_NORMALIZE_IMAGE) )
|
||||
if (img.channels() != 1)
|
||||
{
|
||||
// equalize the input image histogram -
|
||||
// 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;
|
||||
cvtColor(img, img, COLOR_BGR2GRAY);
|
||||
}
|
||||
|
||||
if( flags & CV_CALIB_CB_NORMALIZE_IMAGE )
|
||||
{
|
||||
cvEqualizeHist( img, norm_img );
|
||||
img = norm_img;
|
||||
}
|
||||
}
|
||||
|
||||
Mat thresh_img_new = img.clone();
|
||||
icvBinarizationHistogramBased( thresh_img_new ); // process image in-place
|
||||
SHOW("New binarization", thresh_img_new);
|
||||
|
||||
if( flags & CV_CALIB_CB_FAST_CHECK)
|
||||
{
|
||||
//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 );
|
||||
int check_chessboard_result = cvCheckChessboardBinary(cImgSeg, pattern_size);
|
||||
if(check_chessboard_result <= 0) //fall back to the old method
|
||||
if (checkChessboardBinary(thresh_img_new, pattern_size) <= 0) //fall back to the old method
|
||||
{
|
||||
IplImage _img;
|
||||
cvGetImage(img, &_img);
|
||||
check_chessboard_result = cvCheckChessboard(&_img, pattern_size);
|
||||
if(check_chessboard_result <= 0)
|
||||
if (checkChessboard(img, pattern_size) <= 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.
|
||||
// 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( dilations = min_dilations; dilations <= max_dilations; dilations++ )
|
||||
for( int dilations = min_dilations; dilations <= max_dilations; dilations++ )
|
||||
{
|
||||
if (found)
|
||||
break; // already found it
|
||||
|
||||
cvFree(&quads);
|
||||
cvFree(&corners);
|
||||
|
||||
int max_quad_buf_size = 0;
|
||||
|
||||
//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.
|
||||
// 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);
|
||||
|
||||
if( quad_count <= 0 )
|
||||
{
|
||||
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 ))
|
||||
{
|
||||
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);
|
||||
int max_quad_buf_size = 0;
|
||||
cvFree(&quads);
|
||||
cvFree(&corners);
|
||||
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));
|
||||
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))
|
||||
found = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}//dilations
|
||||
|
||||
PRINTF("Chessboard detection result 0: %d\n", found);
|
||||
|
||||
// revert to old, slower, method if detection failed
|
||||
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");
|
||||
const bool useAdaptive = flags & CV_CALIB_CB_ADAPTIVE_THRESH;
|
||||
if (!useAdaptive)
|
||||
{
|
||||
// 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 );
|
||||
double mean = cv::mean(img).val[0];
|
||||
int thresh_level = MAX(cvRound( mean - 10 ), 10);
|
||||
threshold( img, thresh_img, thresh_level, 255, 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( dilations = min_dilations; dilations <= max_dilations; dilations++ )
|
||||
for( int 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 )
|
||||
if (useAdaptive)
|
||||
{
|
||||
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;
|
||||
|
||||
int block_size = cvRound(prev_sqr_size == 0
|
||||
? MIN(img.cols, img.rows) * (k % 2 == 0 ? 0.2 : 0.1)
|
||||
: prev_sqr_size * 2);
|
||||
block_size = block_size | 1;
|
||||
// convert to binary
|
||||
cvAdaptiveThreshold( img, thresh_img, 255,
|
||||
CV_ADAPTIVE_THRESH_MEAN_C, CV_THRESH_BINARY, block_size, (k/2)*5 );
|
||||
adaptiveThreshold( img, thresh_img, 255, ADAPTIVE_THRESH_MEAN_C, THRESH_BINARY, block_size, (k/2)*5 );
|
||||
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
|
||||
{
|
||||
k = 6;
|
||||
cvDilate( thresh_img, thresh_img, 0, 1 );
|
||||
dilate( thresh_img, thresh_img, Mat(), Point(-1, -1), 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.
|
||||
// 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);
|
||||
|
||||
quad_count = icvGenerateQuads( &quads, &corners, storage, thresh_img, flags, &max_quad_buf_size);
|
||||
PRINTF("Quad count: %d/%d\n", quad_count, expected_corners_num);
|
||||
|
||||
if( quad_count <= 0 )
|
||||
{
|
||||
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 ))
|
||||
{
|
||||
rectangle( thresh_img, Point(0,0), Point(thresh_img.cols-1, thresh_img.rows-1), Scalar(255,255,255), 3, LINE_8);
|
||||
int max_quad_buf_size = 0;
|
||||
cvFree(&quads);
|
||||
cvFree(&corners);
|
||||
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));
|
||||
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))
|
||||
found = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}//dilations
|
||||
}// for k = 0 -> 6
|
||||
}
|
||||
|
||||
PRINTF("Chessboard detection result 1: %d\n", found);
|
||||
|
||||
@ -722,8 +584,8 @@ int cvFindChessboardCorners( const void* arr, CvSize pattern_size,
|
||||
const int BORDER = 8;
|
||||
for( k = 0; k < pattern_size.width*pattern_size.height; k++ )
|
||||
{
|
||||
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 )
|
||||
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 )
|
||||
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;
|
||||
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),
|
||||
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(&corners);
|
||||
cvFree(&quad_group);
|
||||
cvFree(&corner_group);
|
||||
cvFree(&cImgSeg);
|
||||
throw;
|
||||
}
|
||||
|
||||
cvFree(&quads);
|
||||
cvFree(&corners);
|
||||
cvFree(&quad_group);
|
||||
cvFree(&corner_group);
|
||||
cvFree(&cImgSeg);
|
||||
return found;
|
||||
}
|
||||
|
||||
@ -1866,8 +1712,9 @@ static void icvFindQuadNeighbors( CvCBQuad *quads, int quad_count )
|
||||
|
||||
static int
|
||||
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;
|
||||
cv::Ptr<CvMemStorage> temp_storage;
|
||||
|
||||
@ -2011,6 +1858,88 @@ icvGenerateQuads( CvCBQuad **out_quads, CvCBCorner **out_corners,
|
||||
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
|
||||
cvDrawChessboardCorners( CvArr* _image, CvSize pattern_size,
|
||||
|
@ -46,28 +46,26 @@
|
||||
#include <vector>
|
||||
#include <algorithm>
|
||||
|
||||
//#define DEBUG_WINDOWS
|
||||
using namespace cv;
|
||||
using namespace std;
|
||||
|
||||
#if defined(DEBUG_WINDOWS)
|
||||
# include "opencv2/opencv_modules.hpp"
|
||||
# ifdef HAVE_OPENCV_HIGHGUI
|
||||
# include "opencv2/highgui.hpp"
|
||||
# else
|
||||
# undef DEBUG_WINDOWS
|
||||
# endif
|
||||
#endif
|
||||
|
||||
int cvCheckChessboardBinary(IplImage* src, CvSize size);
|
||||
|
||||
static void icvGetQuadrangleHypotheses(CvSeq* contours, std::vector<std::pair<float, int> >& quads, int class_id)
|
||||
static void icvGetQuadrangleHypotheses(const std::vector<std::vector< cv::Point > > & contours, const std::vector< cv::Vec4i > & hierarchy, std::vector<std::pair<float, int> >& quads, int class_id)
|
||||
{
|
||||
const float min_aspect_ratio = 0.3f;
|
||||
const float max_aspect_ratio = 3.0f;
|
||||
const float min_box_size = 10.0f;
|
||||
|
||||
for(CvSeq* seq = contours; seq != NULL; seq = seq->h_next)
|
||||
typedef std::vector< std::vector< cv::Point > >::const_iterator iter_t;
|
||||
iter_t i;
|
||||
for (i = contours.begin(); i != contours.end(); ++i)
|
||||
{
|
||||
CvBox2D box = cvMinAreaRect2(seq);
|
||||
const iter_t::difference_type idx = i - contours.begin();
|
||||
if (hierarchy.at(idx)[3] != -1)
|
||||
continue; // skip holes
|
||||
|
||||
const std::vector< cv::Point > & c = *i;
|
||||
cv::RotatedRect box = cv::minAreaRect(c);
|
||||
|
||||
float box_size = MAX(box.size.width, box.size.height);
|
||||
if(box_size < min_box_size)
|
||||
{
|
||||
@ -98,71 +96,28 @@ inline bool less_pred(const std::pair<float, int>& p1, const std::pair<float, in
|
||||
return p1.first < p2.first;
|
||||
}
|
||||
|
||||
// does a fast check if a chessboard is in the input image. This is a workaround to
|
||||
// a problem of cvFindChessboardCorners being slow on images with no chessboard
|
||||
// - src: input image
|
||||
// - size: chessboard size
|
||||
// Returns 1 if a chessboard can be in this image and findChessboardCorners should be called,
|
||||
// 0 if there is no chessboard, -1 in case of error
|
||||
int cvCheckChessboard(IplImage* src, CvSize size)
|
||||
static void fillQuads(Mat & white, Mat & black, double white_thresh, double black_thresh, vector<pair<float, int> > & quads)
|
||||
{
|
||||
if(src->nChannels > 1)
|
||||
Mat thresh;
|
||||
{
|
||||
cvError(CV_BadNumChannels, "cvCheckChessboard", "supports single-channel images only",
|
||||
__FILE__, __LINE__);
|
||||
vector< vector<Point> > contours;
|
||||
vector< Vec4i > hierarchy;
|
||||
threshold(white, thresh, white_thresh, 255, THRESH_BINARY);
|
||||
findContours(thresh, contours, hierarchy, RETR_CCOMP, CHAIN_APPROX_SIMPLE);
|
||||
icvGetQuadrangleHypotheses(contours, hierarchy, quads, 1);
|
||||
}
|
||||
|
||||
if(src->depth != 8)
|
||||
{
|
||||
cvError(CV_BadDepth, "cvCheckChessboard", "supports depth=8 images only",
|
||||
__FILE__, __LINE__);
|
||||
vector< vector<Point> > contours;
|
||||
vector< Vec4i > hierarchy;
|
||||
threshold(black, thresh, black_thresh, 255, THRESH_BINARY_INV);
|
||||
findContours(thresh, contours, hierarchy, RETR_CCOMP, CHAIN_APPROX_SIMPLE);
|
||||
icvGetQuadrangleHypotheses(contours, hierarchy, quads, 0);
|
||||
}
|
||||
}
|
||||
|
||||
const int erosion_count = 1;
|
||||
const float black_level = 20.f;
|
||||
const float white_level = 130.f;
|
||||
const float black_white_gap = 70.f;
|
||||
|
||||
#if defined(DEBUG_WINDOWS)
|
||||
cvNamedWindow("1", 1);
|
||||
cvShowImage("1", src);
|
||||
cvWaitKey(0);
|
||||
#endif //DEBUG_WINDOWS
|
||||
|
||||
CvMemStorage* storage = cvCreateMemStorage();
|
||||
|
||||
IplImage* white = cvCloneImage(src);
|
||||
IplImage* black = cvCloneImage(src);
|
||||
|
||||
cvErode(white, white, NULL, erosion_count);
|
||||
cvDilate(black, black, NULL, erosion_count);
|
||||
IplImage* thresh = cvCreateImage(cvGetSize(src), IPL_DEPTH_8U, 1);
|
||||
|
||||
int result = 0;
|
||||
for(float thresh_level = black_level; thresh_level < white_level && !result; thresh_level += 20.0f)
|
||||
static bool checkQuads(vector<pair<float, int> > & quads, const cv::Size & size)
|
||||
{
|
||||
cvThreshold(white, thresh, thresh_level + black_white_gap, 255, CV_THRESH_BINARY);
|
||||
|
||||
#if defined(DEBUG_WINDOWS)
|
||||
cvShowImage("1", thresh);
|
||||
cvWaitKey(0);
|
||||
#endif //DEBUG_WINDOWS
|
||||
|
||||
CvSeq* first = 0;
|
||||
std::vector<std::pair<float, int> > quads;
|
||||
cvFindContours(thresh, storage, &first, sizeof(CvContour), CV_RETR_CCOMP);
|
||||
icvGetQuadrangleHypotheses(first, quads, 1);
|
||||
|
||||
cvThreshold(black, thresh, thresh_level, 255, CV_THRESH_BINARY_INV);
|
||||
|
||||
#if defined(DEBUG_WINDOWS)
|
||||
cvShowImage("1", thresh);
|
||||
cvWaitKey(0);
|
||||
#endif //DEBUG_WINDOWS
|
||||
|
||||
cvFindContours(thresh, storage, &first, sizeof(CvContour), CV_RETR_CCOMP);
|
||||
icvGetQuadrangleHypotheses(first, quads, 0);
|
||||
|
||||
const size_t min_quads_count = size.width*size.height/2;
|
||||
std::sort(quads.begin(), quads.end(), less_pred);
|
||||
|
||||
@ -193,18 +148,46 @@ int cvCheckChessboard(IplImage* src, CvSize size)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// does a fast check if a chessboard is in the input image. This is a workaround to
|
||||
// a problem of cvFindChessboardCorners being slow on images with no chessboard
|
||||
// - src: input image
|
||||
// - size: chessboard size
|
||||
// Returns 1 if a chessboard can be in this image and findChessboardCorners should be called,
|
||||
// 0 if there is no chessboard, -1 in case of error
|
||||
int cvCheckChessboard(IplImage* src, CvSize size)
|
||||
{
|
||||
cv::Mat img = cv::cvarrToMat(src);
|
||||
return checkChessboard(img, size);
|
||||
}
|
||||
|
||||
int checkChessboard(const cv::Mat & img, const cv::Size & size)
|
||||
{
|
||||
CV_Assert(img.channels() == 1 && img.depth() == CV_8U);
|
||||
|
||||
const int erosion_count = 1;
|
||||
const float black_level = 20.f;
|
||||
const float white_level = 130.f;
|
||||
const float black_white_gap = 70.f;
|
||||
|
||||
Mat white;
|
||||
Mat black;
|
||||
erode(img, white, Mat(), Point(-1, -1), erosion_count);
|
||||
dilate(img, black, Mat(), Point(-1, -1), erosion_count);
|
||||
|
||||
int result = 0;
|
||||
for(float thresh_level = black_level; thresh_level < white_level && !result; thresh_level += 20.0f)
|
||||
{
|
||||
vector<pair<float, int> > quads;
|
||||
fillQuads(white, black, thresh_level + black_white_gap, thresh_level, quads);
|
||||
if (checkQuads(quads, size))
|
||||
result = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
cvReleaseImage(&thresh);
|
||||
cvReleaseImage(&white);
|
||||
cvReleaseImage(&black);
|
||||
cvReleaseMemStorage(&storage);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@ -214,28 +197,14 @@ int cvCheckChessboard(IplImage* src, CvSize size)
|
||||
// - size: chessboard size
|
||||
// Returns 1 if a chessboard can be in this image and findChessboardCorners should be called,
|
||||
// 0 if there is no chessboard, -1 in case of error
|
||||
int cvCheckChessboardBinary(IplImage* src, CvSize size)
|
||||
int checkChessboardBinary(const cv::Mat & img, const cv::Size & size)
|
||||
{
|
||||
if(src->nChannels > 1)
|
||||
{
|
||||
cvError(CV_BadNumChannels, "cvCheckChessboard", "supports single-channel images only",
|
||||
__FILE__, __LINE__);
|
||||
}
|
||||
CV_Assert(img.channels() == 1 && img.depth() == CV_8U);
|
||||
|
||||
if(src->depth != 8)
|
||||
{
|
||||
cvError(CV_BadDepth, "cvCheckChessboard", "supports depth=8 images only",
|
||||
__FILE__, __LINE__);
|
||||
}
|
||||
|
||||
CvMemStorage* storage = cvCreateMemStorage();
|
||||
|
||||
IplImage* white = cvCloneImage(src);
|
||||
IplImage* black = cvCloneImage(src);
|
||||
IplImage* thresh = cvCreateImage(cvGetSize(src), IPL_DEPTH_8U, 1);
|
||||
Mat white = img.clone();
|
||||
Mat black = img.clone();
|
||||
|
||||
int result = 0;
|
||||
|
||||
for ( int erosion_count = 0; erosion_count <= 3; erosion_count++ )
|
||||
{
|
||||
if ( 1 == result )
|
||||
@ -243,61 +212,14 @@ int cvCheckChessboardBinary(IplImage* src, CvSize size)
|
||||
|
||||
if ( 0 != erosion_count ) // first iteration keeps original images
|
||||
{
|
||||
cvErode(white, white, NULL, 1);
|
||||
cvDilate(black, black, NULL, 1);
|
||||
erode(white, white, Mat(), Point(-1, -1), 1);
|
||||
dilate(black, black, Mat(), Point(-1, -1), 1);
|
||||
}
|
||||
|
||||
cvThreshold(white, thresh, 128, 255, CV_THRESH_BINARY);
|
||||
|
||||
CvSeq* first = 0;
|
||||
std::vector<std::pair<float, int> > quads;
|
||||
cvFindContours(thresh, storage, &first, sizeof(CvContour), CV_RETR_CCOMP);
|
||||
icvGetQuadrangleHypotheses(first, quads, 1);
|
||||
|
||||
cvThreshold(black, thresh, 128, 255, CV_THRESH_BINARY_INV);
|
||||
cvFindContours(thresh, storage, &first, sizeof(CvContour), CV_RETR_CCOMP);
|
||||
icvGetQuadrangleHypotheses(first, quads, 0);
|
||||
|
||||
const size_t min_quads_count = size.width*size.height/2;
|
||||
std::sort(quads.begin(), quads.end(), less_pred);
|
||||
|
||||
// now check if there are many hypotheses with similar sizes
|
||||
// do this by floodfill-style algorithm
|
||||
const float size_rel_dev = 0.4f;
|
||||
|
||||
for(size_t i = 0; i < quads.size(); i++)
|
||||
{
|
||||
size_t j = i + 1;
|
||||
for(; j < quads.size(); j++)
|
||||
{
|
||||
if(quads[j].first/quads[i].first > 1.0f + size_rel_dev)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(j + 1 > min_quads_count + i)
|
||||
{
|
||||
// check the number of black and white squares
|
||||
std::vector<int> counts;
|
||||
countClasses(quads, i, j, counts);
|
||||
const int black_count = cvRound(ceil(size.width/2.0)*ceil(size.height/2.0));
|
||||
const int white_count = cvRound(floor(size.width/2.0)*floor(size.height/2.0));
|
||||
if(counts[0] < black_count*0.75 ||
|
||||
counts[1] < white_count*0.75)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
vector<pair<float, int> > quads;
|
||||
fillQuads(white, black, 128, 128, quads);
|
||||
if (checkQuads(quads, size))
|
||||
result = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cvReleaseImage(&thresh);
|
||||
cvReleaseImage(&white);
|
||||
cvReleaseImage(&black);
|
||||
cvReleaseMemStorage(&storage);
|
||||
|
||||
return result;
|
||||
}
|
@ -117,4 +117,7 @@ template<typename T> inline int compressElems( T* ptr, const uchar* mask, int ms
|
||||
|
||||
}
|
||||
|
||||
int checkChessboard(const cv::Mat & img, const cv::Size & size);
|
||||
int checkChessboardBinary(const cv::Mat & img, const cv::Size & size);
|
||||
|
||||
#endif
|
||||
|
@ -51,29 +51,31 @@ using namespace cv;
|
||||
|
||||
#define _L2_ERR
|
||||
|
||||
void show_points( const Mat& gray, const Mat& u, const vector<Point2f>& v, Size pattern_size, bool was_found )
|
||||
//#define DEBUG_CHESSBOARD
|
||||
|
||||
#ifdef DEBUG_CHESSBOARD
|
||||
#include "opencv2/highgui.hpp"
|
||||
void show_points( const Mat& gray, const Mat& expected, const vector<Point2f>& actual, bool was_found )
|
||||
{
|
||||
Mat rgb( gray.size(), CV_8U);
|
||||
merge(vector<Mat>(3, gray), rgb);
|
||||
|
||||
for(size_t i = 0; i < v.size(); i++ )
|
||||
circle( rgb, v[i], 3, Scalar(255, 0, 0), FILLED);
|
||||
for(size_t i = 0; i < actual.size(); i++ )
|
||||
circle( rgb, actual[i], 5, Scalar(0, 0, 200), 1, LINE_AA);
|
||||
|
||||
if( !u.empty() )
|
||||
if( !expected.empty() )
|
||||
{
|
||||
const Point2f* u_data = u.ptr<Point2f>();
|
||||
size_t count = u.cols * u.rows;
|
||||
const Point2f* u_data = expected.ptr<Point2f>();
|
||||
size_t count = expected.cols * expected.rows;
|
||||
for(size_t i = 0; i < count; i++ )
|
||||
circle( rgb, u_data[i], 3, Scalar(0, 255, 0), FILLED);
|
||||
circle(rgb, u_data[i], 4, Scalar(0, 240, 0), 1, LINE_AA);
|
||||
}
|
||||
if (!v.empty())
|
||||
{
|
||||
Mat corners((int)v.size(), 1, CV_32FC2, (void*)&v[0]);
|
||||
drawChessboardCorners( rgb, pattern_size, corners, was_found );
|
||||
putText(rgb, was_found ? "FOUND !!!" : "NOT FOUND", Point(5, 20), FONT_HERSHEY_PLAIN, 1, Scalar(0, 240, 0));
|
||||
imshow( "test", rgb ); while ((uchar)waitKey(0) != 'q') {};
|
||||
}
|
||||
//namedWindow( "test", 0 ); imshow( "test", rgb ); waitKey(0);
|
||||
}
|
||||
|
||||
#else
|
||||
#define show_points(...)
|
||||
#endif
|
||||
|
||||
enum Pattern { CHESSBOARD, CIRCLES_GRID, ASYMMETRIC_CIRCLES_GRID };
|
||||
|
||||
@ -253,7 +255,6 @@ void CV_ChessboardDetectorTest::run_batch( const string& filename )
|
||||
result = findCirclesGrid(gray, pattern_size, v, CALIB_CB_ASYMMETRIC_GRID | algorithmFlags);
|
||||
break;
|
||||
}
|
||||
show_points( gray, Mat(), v, pattern_size, result );
|
||||
|
||||
if( result ^ doesContatinChessboard || v.size() != count_exp )
|
||||
{
|
||||
@ -280,7 +281,7 @@ void CV_ChessboardDetectorTest::run_batch( const string& filename )
|
||||
if( pattern == CHESSBOARD )
|
||||
cornerSubPix( gray, v, Size(5, 5), Size(-1,-1), TermCriteria(TermCriteria::EPS|TermCriteria::MAX_ITER, 30, 0.1));
|
||||
//find4QuadCornerSubpix(gray, v, Size(5, 5));
|
||||
show_points( gray, expected, v, pattern_size, result );
|
||||
show_points( gray, expected, v, result );
|
||||
#ifndef WRITE_POINTS
|
||||
// printf("called find4QuadCornerSubpix\n");
|
||||
err = calcError(v, expected);
|
||||
@ -298,6 +299,10 @@ void CV_ChessboardDetectorTest::run_batch( const string& filename )
|
||||
max_precise_error = MAX( max_precise_error, err );
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
show_points( gray, Mat(), v, result );
|
||||
}
|
||||
|
||||
#ifdef WRITE_POINTS
|
||||
Mat mat_v(pattern_size, CV_32FC2, (void*)&v[0]);
|
||||
|
@ -57,7 +57,7 @@ class calibration_test(NewOpenCVTests):
|
||||
|
||||
eps = 0.01
|
||||
normCamEps = 10.0
|
||||
normDistEps = 0.001
|
||||
normDistEps = 0.05
|
||||
|
||||
cameraMatrixTest = [[ 532.80992189, 0., 342.4952186 ],
|
||||
[ 0., 532.93346422, 233.8879292 ],
|
||||
|
Loading…
Reference in New Issue
Block a user