From c7fc563dc0ac2d21ad2bd618e741227c0a4b4f76 Mon Sep 17 00:00:00 2001 From: Alexander Alekhin Date: Wed, 23 May 2018 17:44:12 +0300 Subject: [PATCH] calib3d: chessboard detector - replace OpenCV C API --- modules/calib3d/src/calib3d_c_api.cpp | 47 + modules/calib3d/src/calibinit.cpp | 1728 ++++++++--------- modules/calib3d/src/precomp.hpp | 6 +- .../calib3d/test/test_cameracalibration.cpp | 3 +- modules/core/include/opencv2/core/types_c.h | 24 + modules/ts/src/ts.cpp | 12 +- 6 files changed, 902 insertions(+), 918 deletions(-) create mode 100644 modules/calib3d/src/calib3d_c_api.cpp diff --git a/modules/calib3d/src/calib3d_c_api.cpp b/modules/calib3d/src/calib3d_c_api.cpp new file mode 100644 index 0000000000..284c8a074b --- /dev/null +++ b/modules/calib3d/src/calib3d_c_api.cpp @@ -0,0 +1,47 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html. + +// This file contains wrappers for legacy OpenCV C API + +#include "precomp.hpp" +#include "opencv2/calib3d/calib3d_c.h" + +using namespace cv; + +CV_IMPL void +cvDrawChessboardCorners(CvArr* _image, CvSize pattern_size, + CvPoint2D32f* corners, int count, int found) +{ + CV_Assert(corners != NULL); //CV_CheckNULL(corners, "NULL is not allowed for 'corners' parameter"); + Mat image = cvarrToMat(_image); + CV_StaticAssert(sizeof(CvPoint2D32f) == sizeof(Point2f), ""); + drawChessboardCorners(image, pattern_size, Mat(1, count, traits::Type::value, corners), found != 0); +} + +CV_IMPL int +cvFindChessboardCorners(const void* arr, CvSize pattern_size, + CvPoint2D32f* out_corners_, int* out_corner_count, + int flags) +{ + if (!out_corners_) + CV_Error( CV_StsNullPtr, "Null pointer to corners" ); + + Mat image = cvarrToMat(arr); + std::vector out_corners; + + if (out_corner_count) + *out_corner_count = 0; + + bool res = cv::findChessboardCorners(image, pattern_size, out_corners, flags); + + int corner_count = (int)out_corners.size(); + if (out_corner_count) + *out_corner_count = corner_count; + CV_CheckLE(corner_count, Size(pattern_size).area(), "Unexpected number of corners"); + for (int i = 0; i < corner_count; ++i) + { + out_corners_[i] = cvPoint2D32f(out_corners[i]); + } + return res ? 1 : 0; +} diff --git a/modules/calib3d/src/calibinit.cpp b/modules/calib3d/src/calibinit.cpp index feaaf4a73a..3e31550240 100644 --- a/modules/calib3d/src/calibinit.cpp +++ b/modules/calib3d/src/calibinit.cpp @@ -70,36 +70,29 @@ \************************************************************************************/ #include "precomp.hpp" -#include "opencv2/imgproc/imgproc_c.h" -#include "opencv2/calib3d/calib3d_c.h" #include "circlesgrid.hpp" -#include -#include -#include "opencv2/core/utility.hpp" -#include +#include //#define ENABLE_TRIM_COL_ROW //#define DEBUG_CHESSBOARD +#define DEBUG_CHESSBOARD_TIMEOUT 0 // 0 - wait for 'q' +#include //#undef CV_LOG_STRIP_LEVEL //#define CV_LOG_STRIP_LEVEL CV_LOG_LEVEL_VERBOSE + 1 #include #ifdef DEBUG_CHESSBOARD -static int PRINTF( const char* fmt, ... ) -{ - va_list args; - va_start(args, fmt); - return vprintf(fmt, args); -} +#include "opencv2/highgui.hpp" +#include "opencv2/imgproc.hpp" +#define DPRINTF(...) CV_LOG_INFO(NULL, cv::format("calib3d: " __VA_ARGS__)) #else -#define PRINTF(...) +#define DPRINTF(...) #endif -using namespace cv; -using namespace std; +namespace cv { //===================================================================================== // Implementation for the enhanced calibration object detection @@ -120,6 +113,11 @@ struct QuadCountour { } }; #else + +} // namespace +#include "opencv2/imgproc/imgproc_c.h" +namespace cv { + struct CvContourEx { CV_CONTOUR_FIELDS() @@ -127,41 +125,41 @@ struct CvContourEx }; #endif -//===================================================================================== -/// Corner info structure /** This structure stores information about the chessboard corner.*/ -struct CvCBCorner +struct ChessBoardCorner { - CvPoint2D32f pt; // Coordinates of the corner + cv::Point2f pt; // Coordinates of the corner int row; // Board row index int count; // Number of neighbor corners - struct CvCBCorner* neighbors[4]; // Neighbor corners + struct ChessBoardCorner* neighbors[4]; // Neighbor corners - float meanDist(int *_n) const + ChessBoardCorner(const cv::Point2f& pt_ = cv::Point2f()) : + pt(pt_), row(0), count(0) + { + neighbors[0] = neighbors[1] = neighbors[2] = neighbors[3] = NULL; + } + + float sumDist(int& n_) const { float sum = 0; int n = 0; - for( int i = 0; i < 4; i++ ) + for (int i = 0; i < 4; ++i) { - if( neighbors[i] ) + if (neighbors[i]) { - float dx = neighbors[i]->pt.x - pt.x; - float dy = neighbors[i]->pt.y - pt.y; - sum += sqrt(dx*dx + dy*dy); + sum += sqrt(normL2Sqr(neighbors[i]->pt - pt)); n++; } } - if(_n) - *_n = n; - return sum/MAX(n,1); + n_ = n; + return sum; } }; -//===================================================================================== -/// Quadrangle contour info structure -/** This structure stores information about the chessboard quadrange.*/ -struct CvCBQuad + +/** This structure stores information about the chessboard quadrangle.*/ +struct ChessBoardQuad { int count; // Number of quad neighbors int group_idx; // quad group ID @@ -169,140 +167,179 @@ struct CvCBQuad bool ordered; // true if corners/neighbors are ordered counter-clockwise float edge_len; // quad edge len, in pix^2 // neighbors and corners are synced, i.e., neighbor 0 shares corner 0 - CvCBCorner *corners[4]; // Coordinates of quad corners - struct CvCBQuad *neighbors[4]; // Pointers of quad neighbors + ChessBoardCorner *corners[4]; // Coordinates of quad corners + struct ChessBoardQuad *neighbors[4]; // Pointers of quad neighbors + + ChessBoardQuad(int group_idx_ = -1) : + count(0), + group_idx(group_idx_), + row(0), col(0), + ordered(0), + edge_len(0) + { + corners[0] = corners[1] = corners[2] = corners[3] = NULL; + neighbors[0] = neighbors[1] = neighbors[2] = neighbors[3] = NULL; + } }; -//===================================================================================== + #ifdef DEBUG_CHESSBOARD -#include "opencv2/highgui.hpp" -#include "opencv2/imgproc.hpp" static void SHOW(const std::string & name, Mat & img) { imshow(name, img); +#if DEBUG_CHESSBOARD_TIMEOUT + waitKey(DEBUG_CHESSBOARD_TIMEOUT); +#else while ((uchar)waitKey(0) != 'q') {} +#endif } -static void SHOW_QUADS(const std::string & name, const Mat & img_, CvCBQuad * quads, int quads_count) +static void SHOW_QUADS(const std::string & name, const Mat & img_, ChessBoardQuad * 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]; + ChessBoardQuad & 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); + line(img, quad.corners[j]->pt, quad.corners[(j + 1) & 3]->pt, Scalar(0, 240, 0), 1, LINE_AA); } } imshow(name, img); +#if DEBUG_CHESSBOARD_TIMEOUT + waitKey(DEBUG_CHESSBOARD_TIMEOUT); +#else while ((uchar)waitKey(0) != 'q') {} +#endif } #else #define SHOW(...) #define SHOW_QUADS(...) #endif -//===================================================================================== -static int icvGenerateQuads( CvCBQuad **quads, CvCBCorner **corners, - 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); +class ChessBoardDetector +{ +public: + cv::Mat binarized_image; + Size pattern_size; -/*static int -icvGenerateQuadsEx( CvCBQuad **out_quads, CvCBCorner **out_corners, - CvMemStorage *storage, CvMat *image, CvMat *thresh_img, int dilation, int flags );*/ + cv::AutoBuffer all_quads; + cv::AutoBuffer all_corners; -static void icvFindQuadNeighbors( CvCBQuad *quads, int quad_count ); + int all_quads_count; -static int icvFindConnectedQuads( CvCBQuad *quads, int quad_count, - CvCBQuad **quad_group, int group_idx, - CvMemStorage* storage ); + ChessBoardDetector(const Size& pattern_size_) : + pattern_size(pattern_size_), + all_quads_count(0) + { + } + + void reset() + { + all_quads.deallocate(); + all_corners.deallocate(); + all_quads_count = 0; + } -static int icvCheckQuadGroup( CvCBQuad **quad_group, int count, - CvCBCorner **out_corners, CvSize pattern_size ); + void generateQuads(const cv::Mat& image_, int flags); -static int icvCleanFoundConnectedQuads( int quad_count, - CvCBQuad **quads, CvSize pattern_size ); + bool processQuads(std::vector& out_corners, int &prev_sqr_size); -static int icvOrderFoundConnectedQuads( int quad_count, CvCBQuad **quads, - int *all_count, CvCBQuad **all_quads, CvCBCorner **corners, - CvSize pattern_size, int max_quad_buf_size, CvMemStorage* storage ); + void findQuadNeighbors(); -static void icvOrderQuad(CvCBQuad *quad, CvCBCorner *corner, int common); + void findConnectedQuads(std::vector& out_group, int group_idx); -#ifdef ENABLE_TRIM_COL_ROW -static int icvTrimCol(CvCBQuad **quads, int count, int col, int dir); + int checkQuadGroup(std::vector& quad_group, std::vector& out_corners); + + int cleanFoundConnectedQuads(std::vector& quad_group); + + int orderFoundConnectedQuads(std::vector& quads); -static int icvTrimRow(CvCBQuad **quads, int count, int row, int dir); + void orderQuad(ChessBoardQuad& quad, ChessBoardCorner& corner, int common); + +#ifdef ENABLE_TRIM_COL_ROW + void trimCol(std::vector& quads, int col, int dir); + void trimRow(std::vector& quads, int row, int dir); #endif -static int icvAddOuterQuad(CvCBQuad *quad, CvCBQuad **quads, int quad_count, - CvCBQuad **all_quads, int all_count, CvCBCorner **corners, int max_quad_buf_size); + int addOuterQuad(ChessBoardQuad& quad, std::vector& quads); -static void icvRemoveQuadFromGroup(CvCBQuad **quads, int count, CvCBQuad *q0); + void removeQuadFromGroup(std::vector& quads, ChessBoardQuad& q0); -static int icvCheckBoardMonotony( CvPoint2D32f* corners, CvSize pattern_size ); + bool checkBoardMonotony(const std::vector& corners); +}; /***************************************************************************************************/ //COMPUTE INTENSITY HISTOGRAM OF INPUT IMAGE -static int icvGetIntensityHistogram( const Mat & img, std::vector& piHist ) +template +static void icvGetIntensityHistogram256(const Mat& img, ArrayContainer& piHist) { + for (int i = 0; i < 256; i++) + piHist[i] = 0; // sum up all pixel in row direction and divide by number of columns - for ( int j=0; j(j); + for (int i = 0; i < img.cols; i++) { piHist[row[i]]++; } } - return 0; } /***************************************************************************************************/ //SMOOTH HISTOGRAM USING WINDOW OF SIZE 2*iWidth+1 -static int icvSmoothHistogram( const std::vector& piHist, std::vector& piHistSmooth, int iWidth ) +template +static void icvSmoothHistogram256(const ArrayContainer& piHist, ArrayContainer& piHistSmooth, int iWidth = 0) { - int iIdx; - for ( int i=0; i<256; i++) - { + CV_DbgAssert(iWidth_ == 0 || (iWidth == iWidth_ || iWidth == 0)); + iWidth = (iWidth_ != 0) ? iWidth_ : iWidth; + CV_Assert(iWidth > 0); + CV_DbgAssert(piHist.size() == 256); + CV_DbgAssert(piHistSmooth.size() == 256); + for (int i = 0; i < 256; ++i) + { + int iIdx_min = std::max(0, i - iWidth); + int iIdx_max = std::min(255, i + iWidth); int iSmooth = 0; - for ( int ii=-iWidth; ii<=iWidth; ii++) + for (int iIdx = iIdx_min; iIdx <= iIdx_max; ++iIdx) { - iIdx = i+ii; - if (iIdx >= 0 && iIdx < 256) - { - iSmooth += piHist[iIdx]; - } + CV_DbgAssert(iIdx >= 0 && iIdx < 256); + iSmooth += piHist[iIdx]; } piHistSmooth[i] = iSmooth/(2*iWidth+1); } - return 0; } /***************************************************************************************************/ //COMPUTE FAST HISTOGRAM GRADIENT -static int icvGradientOfHistogram( const std::vector& piHist, std::vector& piHistGrad ) +template +static void icvGradientOfHistogram256(const ArrayContainer& piHist, ArrayContainer& piHistGrad) { + CV_DbgAssert(piHist.size() == 256); + CV_DbgAssert(piHistGrad.size() == 256); piHistGrad[0] = 0; - for ( int i=1; i<255; i++) + int prev_grad = 0; + for (int i = 1; i < 255; ++i) { - piHistGrad[i] = piHist[i-1] - piHist[i+1]; - if ( abs(piHistGrad[i]) < 100 ) + int grad = piHist[i-1] - piHist[i+1]; + if (std::abs(grad) < 100) { - if ( piHistGrad[i-1] == 0) - piHistGrad[i] = -100; + if (prev_grad == 0) + grad = -100; else - piHistGrad[i] = piHistGrad[i-1]; + grad = prev_grad; } + piHistGrad[i] = grad; + prev_grad = grad; } - return 0; + piHistGrad[255] = 0; } /***************************************************************************************************/ //PERFORM SMART IMAGE THRESHOLDING BASED ON ANALYSIS OF INTENSTY HISTOGRAM -static bool icvBinarizationHistogramBased( Mat & img ) +static void icvBinarizationHistogramBased(Mat & img) { CV_Assert(img.channels() == 1 && img.depth() == CV_8U); int iCols = img.cols; @@ -310,65 +347,62 @@ static bool icvBinarizationHistogramBased( Mat & img ) int iMaxPix = iCols*iRows; int iMaxPix1 = iMaxPix/100; const int iNumBins = 256; - std::vector piHistIntensity(iNumBins, 0); - std::vector piHistSmooth(iNumBins, 0); - std::vector piHistGrad(iNumBins, 0); - std::vector piAccumSum(iNumBins, 0); - std::vector piMaxPos; piMaxPos.reserve(20); - int iThresh = 0; - int iIdx; - int iWidth = 1; + const int iMaxPos = 20; + cv::AutoBuffer piHistIntensity(iNumBins); + cv::AutoBuffer piHistSmooth(iNumBins); + cv::AutoBuffer piHistGrad(iNumBins); + cv::AutoBuffer piMaxPos(iMaxPos); - icvGetIntensityHistogram( img, piHistIntensity ); + icvGetIntensityHistogram256(img, piHistIntensity); +#if 0 // get accumulated sum starting from bright + cv::AutoBuffer piAccumSum(iNumBins); piAccumSum[iNumBins-1] = piHistIntensity[iNumBins-1]; - for ( int i=iNumBins-2; i>=0; i-- ) + for (int i = iNumBins - 2; i >= 0; --i) { piAccumSum[i] = piHistIntensity[i] + piAccumSum[i+1]; } +#endif // first smooth the distribution - icvSmoothHistogram( piHistIntensity, piHistSmooth, iWidth ); + //const int iWidth = 1; + icvSmoothHistogram256<1>(piHistIntensity, piHistSmooth); // compute gradient - icvGradientOfHistogram( piHistSmooth, piHistGrad ); + icvGradientOfHistogram256(piHistSmooth, piHistGrad); // check for zeros - int iCntMaxima = 0; - for ( int i=iNumBins-2; (i>2) && (iCntMaxima<20); i--) + unsigned iCntMaxima = 0; + for (int i = iNumBins-2; (i > 2) && (iCntMaxima < iMaxPos); --i) { - if ( (piHistGrad[i-1] < 0) && (piHistGrad[i] > 0) ) + if ((piHistGrad[i-1] < 0) && (piHistGrad[i] > 0)) { - piMaxPos.push_back(i); - iCntMaxima++; + int iSumAroundMax = piHistSmooth[i-1] + piHistSmooth[i] + piHistSmooth[i+1]; + if (!(iSumAroundMax < iMaxPix1 && i < 64)) + { + piMaxPos[iCntMaxima++] = i; + } } } - iIdx = 0; - int iSumAroundMax = 0; - for ( int i=0; i 0 ? piMaxPos[0] : -1, + iCntMaxima > 1 ? piMaxPos[1] : -1, + iCntMaxima > 2 ? piMaxPos[2] : -1); - CV_Assert((size_t)iCntMaxima == piMaxPos.size()); + int iThresh = 0; - PRINTF("HIST: MAXIMA COUNT: %d (%d, %d, %d, ...)\n", iCntMaxima, + CV_Assert((size_t)iCntMaxima <= piMaxPos.size()); + + DPRINTF("HIST: MAXIMA COUNT: %d (%d, %d, %d, ...)", iCntMaxima, iCntMaxima > 0 ? piMaxPos[0] : -1, iCntMaxima > 1 ? piMaxPos[1] : -1, iCntMaxima > 2 ? piMaxPos[2] : -1); if (iCntMaxima == 0) { - // no any maxima inside (except 0 and 255 which are not handled above) + // no any maxima inside (only 0 and 255 which are not counted above) // Does image black-write already? const int iMaxPix2 = iMaxPix / 2; for (int sum = 0, i = 0; i < 256; ++i) // select mean intensity @@ -376,8 +410,8 @@ static bool icvBinarizationHistogramBased( Mat & img ) sum += piHistIntensity[i]; if (sum > iMaxPix2) { - iThresh = i; - break; + iThresh = i; + break; } } } @@ -385,7 +419,7 @@ static bool icvBinarizationHistogramBased( Mat & img ) { iThresh = piMaxPos[0]/2; } - else if ( iCntMaxima == 2) + else if (iCntMaxima == 2) { iThresh = (piMaxPos[0] + piMaxPos[1])/2; } @@ -393,7 +427,7 @@ static bool icvBinarizationHistogramBased( Mat & img ) { // CHECKING THRESHOLD FOR WHITE int iIdxAccSum = 0, iAccum = 0; - for (int i=iNumBins-1; i>0; i--) + 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 @@ -404,12 +438,12 @@ static bool icvBinarizationHistogramBased( Mat & img ) } } - int iIdxBGMax = 0; + unsigned iIdxBGMax = 0; int iBrightMax = piMaxPos[0]; // printf("iBrightMax = %d\n", iBrightMax); - for ( int n=0; n= 250 && iIdxBGMax + 1 < iCntMaxima ) + if (piMaxPos[iIdxBGMax] >= 250 && iIdxBGMax + 1 < iCntMaxima) { iIdxBGMax++; iMaxVal = piHistIntensity[piMaxPos[iIdxBGMax]]; } - for ( int n=iIdxBGMax + 1; n= iMaxVal ) + if (piHistIntensity[piMaxPos[n]] >= iMaxVal) { iMaxVal = piHistIntensity[piMaxPos[n]]; iIdxBGMax = n; @@ -439,70 +473,58 @@ static bool icvBinarizationHistogramBased( Mat & img ) //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]); + DPRINTF("THRESHOLD SELECTED = %d, BRIGHTMAX = %d, DARKMAX = %d", iThresh, iBrightMax, piMaxPos[iIdxBGMax]); } - - if ( iThresh > 0 ) + if (iThresh > 0) { - for ( int jj=0; jj= iThresh); } - - return true; } -CV_IMPL -int cvFindChessboardCorners( const void* arr, CvSize pattern_size, - CvPoint2D32f* out_corners, int* out_corner_count, - int flags ) +bool findChessboardCorners(InputArray image_, Size pattern_size, + OutputArray corners_, int flags) { - int found = 0; - CvCBQuad *quads = 0; - CvCBCorner *corners = 0; + CV_INSTRUMENT_REGION() - cv::Ptr storage; + DPRINTF("==== findChessboardCorners(img=%dx%d, pattern=%dx%d, flags=%d)", + image_.cols(), image_.rows(), pattern_size.width, pattern_size.height, flags); + + bool found = false; - CV_TRY - { - int k = 0; const int min_dilations = 0; const int max_dilations = 7; - if( out_corner_count ) - *out_corner_count = 0; + int type = image_.type(), depth = CV_MAT_DEPTH(type), cn = CV_MAT_CN(type); + Mat img = image_.getMat(); - Mat img = cvarrToMat((CvMat*)arr).clone(); + CV_CheckType(type, depth == CV_8U && (cn == 1 || cn == 3 || cn == 4), + "Only 8-bit grayscale or color images are supported"); - if( img.depth() != CV_8U || (img.channels() != 1 && img.channels() != 3 && img.channels() != 4) ) - CV_Error( CV_StsUnsupportedFormat, "Only 8-bit grayscale or color images are supported" ); + if (pattern_size.width <= 2 || pattern_size.height <= 2) + CV_Error(Error::StsOutOfRange, "Both width and height of the pattern should have bigger than 2"); - if( pattern_size.width <= 2 || pattern_size.height <= 2 ) - CV_Error( CV_StsOutOfRange, "Both width and height of the pattern should have bigger than 2" ); + if (!corners_.needed()) + CV_Error(Error::StsNullPtr, "Null pointer to corners"); - if( !out_corners ) - CV_Error( CV_StsNullPtr, "Null pointer to corners" ); + std::vector out_corners; if (img.channels() != 1) { cvtColor(img, img, COLOR_BGR2GRAY); } + else + { + img.clone(); + } + int prev_sqr_size = 0; Mat thresh_img_new = img.clone(); - icvBinarizationHistogramBased( thresh_img_new ); // process image in-place + icvBinarizationHistogramBased(thresh_img_new); // process image in-place SHOW("New binarization", thresh_img_new); - if( flags & CV_CALIB_CB_FAST_CHECK) + if (flags & 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 @@ -510,24 +532,20 @@ int cvFindChessboardCorners( const void* arr, CvSize pattern_size, { if (checkChessboard(img, pattern_size) <= 0) { - return found; + corners_.release(); + return false; } } } - storage.reset(cvCreateMemStorage(0)); - - int prev_sqr_size = 0; + ChessBoardDetector detector(pattern_size); // 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( int dilations = min_dilations; dilations <= max_dilations; dilations++ ) + for (int dilations = min_dilations; dilations <= max_dilations; dilations++) { - if (found) - break; // already found it - //USE BINARY IMAGE COMPUTED USING icvBinarizationHistogramBased METHOD dilate( thresh_img_new, thresh_img_new, Mat(), Point(-1, -1), 1 ); @@ -535,58 +553,58 @@ int cvFindChessboardCorners( const void* arr, CvSize pattern_size, // 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()... 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); + + detector.reset(); + #ifdef USE_CV_FINDCONTOURS Mat binarized_img = thresh_img_new; #else Mat binarized_img = thresh_img_new.clone(); // make clone because cvFindContours modifies the source image #endif - int quad_count = icvGenerateQuads( &quads, &corners, storage, binarized_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("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; + detector.generateQuads(binarized_img, flags); + DPRINTF("Quad count: %d/%d", detector.all_quads_count, (pattern_size.width/2+1)*(pattern_size.height/2+1)); + SHOW_QUADS("New quads", thresh_img_new, &detector.all_quads[0], detector.all_quads_count); + if (detector.processQuads(out_corners, prev_sqr_size)) + { + found = true; + break; + } } - PRINTF("Chessboard detection result 0: %d\n", found); + DPRINTF("Chessboard detection result 0: %d", (int)found); // revert to old, slower, method if detection failed if (!found) { - if( flags & CV_CALIB_CB_NORMALIZE_IMAGE ) + if (flags & CALIB_CB_NORMALIZE_IMAGE) { - equalizeHist( img, img ); + 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; + DPRINTF("Fallback to old algorithm"); + const bool useAdaptive = flags & CALIB_CB_ADAPTIVE_THRESH; if (!useAdaptive) { // empiric threshold level // thresholding performed here and not inside the cycle to save processing time double mean = cv::mean(img).val[0]; - int thresh_level = MAX(cvRound( mean - 10 ), 10); - threshold( img, thresh_img, thresh_level, 255, THRESH_BINARY ); + int thresh_level = std::max(cvRound(mean - 10), 10); + threshold(img, thresh_img, thresh_level, 255, THRESH_BINARY); } - //if flag CV_CALIB_CB_ADAPTIVE_THRESH is not set it doesn't make sense to iterate over k + //if flag 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++ ) + for (int k = 0; k < max_k && !found; k++) { - for( int dilations = min_dilations; dilations <= max_dilations; dilations++ ) + for (int dilations = min_dilations; dilations <= max_dilations; dilations++) { - if (found) - break; // already found it - // convert the input grayscale image to binary (black-n-white) if (useAdaptive) { int block_size = cvRound(prev_sqr_size == 0 - ? MIN(img.cols, img.rows) * (k % 2 == 0 ? 0.2 : 0.1) + ? std::min(img.cols, img.rows) * (k % 2 == 0 ? 0.2 : 0.1) : prev_sqr_size * 2); block_size = block_size | 1; // convert to binary @@ -605,80 +623,74 @@ int cvFindChessboardCorners( const void* arr, CvSize pattern_size, // 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()... 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); + + detector.reset(); + #ifdef USE_CV_FINDCONTOURS Mat binarized_img = thresh_img; #else Mat binarized_img = (useAdaptive) ? thresh_img : thresh_img.clone(); // make clone because cvFindContours modifies the source image #endif - int quad_count = icvGenerateQuads( &quads, &corners, storage, binarized_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)) + detector.generateQuads(binarized_img, flags); + DPRINTF("Quad count: %d/%d", detector.all_quads_count, (pattern_size.width/2+1)*(pattern_size.height/2+1)); + SHOW_QUADS("Old quads", thresh_img, &detector.all_quads[0], detector.all_quads_count); + if (detector.processQuads(out_corners, prev_sqr_size)) + { found = 1; + break; + } } } } - PRINTF("Chessboard detection result 1: %d\n", found); + DPRINTF("Chessboard detection result 1: %d", (int)found); - if( found ) - found = icvCheckBoardMonotony( out_corners, pattern_size ); + if (found) + found = detector.checkBoardMonotony(out_corners); - PRINTF("Chessboard detection result 2: %d\n", found); + DPRINTF("Chessboard detection result 2: %d", (int)found); // check that none of the found corners is too close to the image boundary - if( found ) + if (found) { const int BORDER = 8; - for( k = 0; k < pattern_size.width*pattern_size.height; k++ ) + for (int 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 ) + { + found = false; break; + } } - - found = k == pattern_size.width*pattern_size.height; } - PRINTF("Chessboard detection result 3: %d\n", found); + DPRINTF("Chessboard detection result 3: %d", (int)found); - if( found ) + if (found) { - if ( pattern_size.height % 2 == 0 && pattern_size.width % 2 == 0 ) + if ((pattern_size.height & 1) == 0 && (pattern_size.width & 1) == 0 ) { int last_row = (pattern_size.height-1)*pattern_size.width; double dy0 = out_corners[last_row].y - out_corners[0].y; - if( dy0 < 0 ) + if (dy0 < 0) { int n = pattern_size.width*pattern_size.height; for(int i = 0; i < n/2; i++ ) { - CvPoint2D32f temp; - CV_SWAP(out_corners[i], out_corners[n-i-1], temp); + std::swap(out_corners[i], out_corners[n-i-1]); } } } - int wsize = 2; - 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)); + cv::cornerSubPix(img, out_corners, Size(2, 2), Size(-1,-1), + cv::TermCriteria(TermCriteria::EPS + TermCriteria::MAX_ITER, 15, 0.1)); } - } - CV_CATCH_ALL - { - cvFree(&quads); - cvFree(&corners); - CV_RETHROW(); - } - cvFree(&quads); - cvFree(&corners); + + Mat(out_corners).copyTo(corners_); return found; } + // // Checks that each board row and column is pretty much monotonous curve: // It analyzes each row and each column of the chessboard as following: @@ -689,35 +701,33 @@ int cvFindChessboardCorners( const void* arr, CvSize pattern_size, // This function has been created as temporary workaround for the bug in current implementation // of cvFindChessboardCornes that produces absolutely unordered sets of corners. // - -static int -icvCheckBoardMonotony( CvPoint2D32f* corners, CvSize pattern_size ) +bool ChessBoardDetector::checkBoardMonotony(const std::vector& corners) { - int i, j, k; - - for( k = 0; k < 2; k++ ) + for (int k = 0; k < 2; ++k) { - for( i = 0; i < (k == 0 ? pattern_size.height : pattern_size.width); i++ ) + int max_i = (k == 0 ? pattern_size.height : pattern_size.width); + int max_j = (k == 0 ? pattern_size.width: pattern_size.height) - 1; + for (int i = 0; i < max_i; ++i) { - CvPoint2D32f a = k == 0 ? corners[i*pattern_size.width] : corners[i]; - CvPoint2D32f b = k == 0 ? corners[(i+1)*pattern_size.width-1] : - corners[(pattern_size.height-1)*pattern_size.width + i]; - float prevt = 0, dx0 = b.x - a.x, dy0 = b.y - a.y; - if( fabs(dx0) + fabs(dy0) < FLT_EPSILON ) - return 0; - for( j = 1; j < (k == 0 ? pattern_size.width : pattern_size.height) - 1; j++ ) + cv::Point2f a = k == 0 ? corners[i*pattern_size.width] : corners[i]; + cv::Point2f b = k == 0 ? corners[(i+1)*pattern_size.width-1] + : corners[(pattern_size.height-1)*pattern_size.width + i]; + float dx0 = b.x - a.x, dy0 = b.y - a.y; + if (fabs(dx0) + fabs(dy0) < FLT_EPSILON) + return false; + float prevt = 0; + for (int j = 1; j < max_j; ++j) { - CvPoint2D32f c = k == 0 ? corners[i*pattern_size.width + j] : - corners[j*pattern_size.width + i]; + cv::Point2f c = k == 0 ? corners[i*pattern_size.width + j] + : corners[j*pattern_size.width + i]; float t = ((c.x - a.x)*dx0 + (c.y - a.y)*dy0)/(dx0*dx0 + dy0*dy0); - if( t < prevt || t > 1 ) - return 0; + if (t < prevt || t > 1) + return false; prevt = t; } } } - - return 1; + return true; } // @@ -731,18 +741,16 @@ icvCheckBoardMonotony( CvPoint2D32f* corners, CvSize pattern_size ) // can change the number of quads in the group // can add quads, so we need to have quad/corner arrays passed in // - -static int -icvOrderFoundConnectedQuads( int quad_count, CvCBQuad **quads, - int *all_count, CvCBQuad **all_quads, CvCBCorner **corners, - CvSize pattern_size, int max_quad_buf_size, CvMemStorage* storage ) +int ChessBoardDetector::orderFoundConnectedQuads(std::vector& quads) { - cv::Ptr temp_storage(cvCreateChildMemStorage( storage )); - CvSeq* stack = cvCreateSeq( 0, sizeof(*stack), sizeof(void*), temp_storage ); + const int max_quad_buf_size = (int)all_quads.size(); + int quad_count = (int)quads.size(); + + std::stack stack; // first find an interior quad - CvCBQuad *start = NULL; - for (int i=0; icount == 4) { @@ -760,7 +768,7 @@ icvOrderFoundConnectedQuads( int quad_count, CvCBQuad **quads, std::map col_hist; std::map row_hist; - cvSeqPush(stack, &start); + stack.push(start); start->row = 0; start->col = 0; start->ordered = true; @@ -768,10 +776,10 @@ icvOrderFoundConnectedQuads( int quad_count, CvCBQuad **quads, // Recursively order the quads so that all position numbers (e.g., // 0,1,2,3) are in the at the same relative corner (e.g., lower right). - while( stack->total ) + while (!stack.empty()) { - CvCBQuad* q; - cvSeqPop( stack, &q ); + ChessBoardQuad* q = stack.top(); stack.pop(); CV_Assert(q); + int col = q->col; int row = q->row; col_hist[col]++; @@ -783,9 +791,9 @@ icvOrderFoundConnectedQuads( int quad_count, CvCBQuad **quads, if (col > col_max) col_max = col; if (col < col_min) col_min = col; - for(int i = 0; i < 4; i++ ) + for (int i = 0; i < 4; i++) { - CvCBQuad *neighbor = q->neighbors[i]; + ChessBoardQuad *neighbor = q->neighbors[i]; switch(i) // adjust col, row for this quad { // start at top left, go clockwise case 0: @@ -801,18 +809,21 @@ icvOrderFoundConnectedQuads( int quad_count, CvCBQuad **quads, // just do inside quads if (neighbor && neighbor->ordered == false && neighbor->count == 4) { - PRINTF("col: %d row: %d\n", col, row); - icvOrderQuad(neighbor, q->corners[i], (i+2)%4); // set in order + DPRINTF("col: %d row: %d", col, row); + CV_Assert(q->corners[i]); + orderQuad(*neighbor, *(q->corners[i]), (i+2)&3); // set in order neighbor->ordered = true; neighbor->row = row; neighbor->col = col; - cvSeqPush( stack, &neighbor ); + stack.push(neighbor); } } } - for (int i=col_min; i<=col_max; i++) - PRINTF("HIST[%d] = %d\n", i, col_hist[i]); +#ifdef DEBUG_CHESSBOARD + for (int i = col_min; i <= col_max; i++) + DPRINTF("HIST[%d] = %d", i, col_hist[i]); +#endif // analyze inner quad structure int w = pattern_size.width - 1; @@ -828,62 +839,67 @@ icvOrderFoundConnectedQuads( int quad_count, CvCBQuad **quads, w = pattern_size.height - 1; } - PRINTF("Size: %dx%d Pattern: %dx%d\n", dcol, drow, w, h); + DPRINTF("Size: %dx%d Pattern: %dx%d", dcol, drow, w, h); // check if there are enough inner quads if (dcol < w || drow < h) // found enough inner quads? { - PRINTF("Too few inner quad rows/cols\n"); + DPRINTF("Too few inner quad rows/cols"); return 0; // no, return } #ifdef ENABLE_TRIM_COL_ROW // too many columns, not very common if (dcol == w+1) // too many, trim { - PRINTF("Trimming cols\n"); + DPRINTF("Trimming cols"); if (col_hist[col_max] > col_hist[col_min]) { - PRINTF("Trimming left col\n"); - quad_count = icvTrimCol(quads,quad_count,col_min,-1); + DPRINTF("Trimming left col"); + trimCol(quads, col_min, -1); } else { - PRINTF("Trimming right col\n"); - quad_count = icvTrimCol(quads,quad_count,col_max,+1); + DPRINTF("Trimming right col"); + trimCol(quads, col_max, +1); } } // too many rows, not very common if (drow == h+1) // too many, trim { - PRINTF("Trimming rows\n"); + DPRINTF("Trimming rows"); if (row_hist[row_max] > row_hist[row_min]) { - PRINTF("Trimming top row\n"); - quad_count = icvTrimRow(quads,quad_count,row_min,-1); + DPRINTF("Trimming top row"); + trimRow(quads, row_min, -1); } else { - PRINTF("Trimming bottom row\n"); - quad_count = icvTrimRow(quads,quad_count,row_max,+1); + DPRINTF("Trimming bottom row"); + trimRow(quads, row_max, +1); } } + + quad_count = (int)quads.size(); // update after icvTrimCol/icvTrimRow #endif // check edges of inner quads // if there is an outer quad missing, fill it in // first order all inner quads int found = 0; - for (int i=0; icount == 4) + ChessBoardQuad& q = *quads[i]; + if (q.count != 4) + continue; + { // ok, look at neighbors - int col = quads[i]->col; - int row = quads[i]->row; - for (int j=0; j<4; j++) + int col = q.col; + int row = q.row; + for (int j = 0; j < 4; j++) { switch(j) // adjust col, row for this quad - { // start at top left, go clockwise + { // start at top left, go clockwise case 0: row--; col--; break; case 1: @@ -893,15 +909,16 @@ icvOrderFoundConnectedQuads( int quad_count, CvCBQuad **quads, case 3: col -= 2; break; } - CvCBQuad *neighbor = quads[i]->neighbors[j]; + ChessBoardQuad *neighbor = q.neighbors[j]; if (neighbor && !neighbor->ordered && // is it an inner quad? col <= col_max && col >= col_min && row <= row_max && row >= row_min) { // if so, set in order - PRINTF("Adding inner: col: %d row: %d\n", col, row); + DPRINTF("Adding inner: col: %d row: %d", col, row); found++; - icvOrderQuad(neighbor, quads[i]->corners[j], (j+2)%4); + CV_Assert(q.corners[j]); + orderQuad(*neighbor, *q.corners[j], (j+2)&3); neighbor->ordered = true; neighbor->row = row; neighbor->col = col; @@ -914,18 +931,18 @@ icvOrderFoundConnectedQuads( int quad_count, CvCBQuad **quads, // which are missing if (found > 0) { - PRINTF("Found %d inner quads not connected to outer quads, repairing\n", found); - for (int i=0; icount < 4 && quads[i]->ordered) + ChessBoardQuad& q = *quads[i]; + if (q.count < 4 && q.ordered) { - int added = icvAddOuterQuad(quads[i],quads,quad_count,all_quads,*all_count,corners, max_quad_buf_size); - *all_count += added; + int added = addOuterQuad(q, quads); quad_count += added; } } - if (*all_count >= max_quad_buf_size) + if (all_quads_count >= max_quad_buf_size) return 0; } @@ -933,29 +950,27 @@ icvOrderFoundConnectedQuads( int quad_count, CvCBQuad **quads, // final trimming of outer quads if (dcol == w && drow == h) // found correct inner quads { - PRINTF("Inner bounds ok, check outer quads\n"); - int rcount = quad_count; - for (int i=quad_count-1; i>=0; i--) // eliminate any quad not connected to - // an ordered quad + DPRINTF("Inner bounds ok, check outer quads"); + for (int i = quad_count - 1; i >= 0; i--) // eliminate any quad not connected to an ordered quad { - if (quads[i]->ordered == false) + ChessBoardQuad& q = *quads[i]; + if (q.ordered == false) { bool outer = false; for (int j=0; j<4; j++) // any neighbors that are ordered? { - if (quads[i]->neighbors[j] && quads[i]->neighbors[j]->ordered) + if (q.neighbors[j] && q.neighbors[j]->ordered) outer = true; } if (!outer) // not an outer quad, eliminate { - PRINTF("Removing quad %d\n", i); - icvRemoveQuadFromGroup(quads,rcount,quads[i]); - rcount--; + DPRINTF("Removing quad %d", i); + removeQuadFromGroup(quads, q); } } } - return rcount; + return (int)quads.size(); } return 0; @@ -966,69 +981,63 @@ icvOrderFoundConnectedQuads( int quad_count, CvCBQuad **quads, // looks for the neighbor of that isn't present, // tries to add it in. // is ordered - -static int -icvAddOuterQuad( CvCBQuad *quad, CvCBQuad **quads, int quad_count, - CvCBQuad **all_quads, int all_count, CvCBCorner **corners, int max_quad_buf_size ) - +int ChessBoardDetector::addOuterQuad(ChessBoardQuad& quad, std::vector& quads) { int added = 0; - for (int i=0; i<4 && all_count < max_quad_buf_size; i++) // find no-neighbor corners + int max_quad_buf_size = (int)all_quads.size(); + + for (int i = 0; i < 4 && all_quads_count < max_quad_buf_size; i++) // find no-neighbor corners { - if (!quad->neighbors[i]) // ok, create and add neighbor + if (!quad.neighbors[i]) // ok, create and add neighbor { - int j = (i+2)%4; - PRINTF("Adding quad as neighbor 2\n"); - CvCBQuad *q = &(*all_quads)[all_count]; - memset( q, 0, sizeof(*q) ); + int j = (i+2)&3; + DPRINTF("Adding quad as neighbor 2"); + int q_index = all_quads_count++; + ChessBoardQuad& q = all_quads[q_index]; + q = ChessBoardQuad(0); added++; - quads[quad_count] = q; - quad_count++; + quads.push_back(&q); // set neighbor and group id - quad->neighbors[i] = q; - quad->count += 1; - q->neighbors[j] = quad; - q->group_idx = quad->group_idx; - q->count = 1; // number of neighbors - q->ordered = false; - q->edge_len = quad->edge_len; + quad.neighbors[i] = &q; + quad.count += 1; + q.neighbors[j] = &quad; + q.group_idx = quad.group_idx; + q.count = 1; // number of neighbors + q.ordered = false; + q.edge_len = quad.edge_len; // make corners of new quad // same as neighbor quad, but offset - CvPoint2D32f pt = quad->corners[i]->pt; - CvCBCorner* corner; - float dx = pt.x - quad->corners[j]->pt.x; - float dy = pt.y - quad->corners[j]->pt.y; - for (int k=0; k<4; k++) + const cv::Point2f pt_offset = quad.corners[i]->pt - quad.corners[j]->pt; + for (int k = 0; k < 4; k++) { - corner = &(*corners)[all_count*4+k]; - pt = quad->corners[k]->pt; - memset( corner, 0, sizeof(*corner) ); - corner->pt = pt; - q->corners[k] = corner; - corner->pt.x += dx; - corner->pt.y += dy; + ChessBoardCorner& corner = (ChessBoardCorner&)all_corners[q_index * 4 + k]; + const cv::Point2f& pt = quad.corners[k]->pt; + corner = ChessBoardCorner(pt); + q.corners[k] = &corner; + corner.pt += pt_offset; } // have to set exact corner - q->corners[j] = quad->corners[i]; + q.corners[j] = quad.corners[i]; // now find other neighbor and add it, if possible - if (quad->neighbors[(i+3)%4] && - quad->neighbors[(i+3)%4]->ordered && - quad->neighbors[(i+3)%4]->neighbors[i] && - quad->neighbors[(i+3)%4]->neighbors[i]->ordered ) + int next_i = (i + 1) & 3; + int prev_i = (i + 3) & 3; // equal to (j + 1) & 3 + ChessBoardQuad* quad_prev = quad.neighbors[prev_i]; + if (quad_prev && + quad_prev->ordered && + quad_prev->neighbors[i] && + quad_prev->neighbors[i]->ordered ) { - CvCBQuad *qn = quad->neighbors[(i+3)%4]->neighbors[i]; - q->count = 2; - q->neighbors[(j+1)%4] = qn; - qn->neighbors[(i+1)%4] = q; + ChessBoardQuad* qn = quad_prev->neighbors[i]; + q.count = 2; + q.neighbors[prev_i] = qn; + qn->neighbors[next_i] = &q; qn->count += 1; // have to set exact corner - q->corners[(j+1)%4] = qn->corners[(i+1)%4]; + q.corners[prev_i] = qn->corners[next_i]; } - - all_count++; } } return added; @@ -1037,151 +1046,141 @@ icvAddOuterQuad( CvCBQuad *quad, CvCBQuad **quads, int quad_count, // trimming routines #ifdef ENABLE_TRIM_COL_ROW -static int -icvTrimCol(CvCBQuad **quads, int count, int col, int dir) +void ChessBoardDetector::trimCol(std::vector& quads, int col, int dir) { - int rcount = count; + std::vector quads_(quads); // find the right quad(s) - for (int i=0; iordered) - PRINTF("index: %d cur: %d\n", col, quads[i]->col); + if (q.ordered) + DPRINTF("i: %d index: %d cur: %d", (int)i, col, q.col); #endif - if (quads[i]->ordered && quads[i]->col == col) + if (q.ordered && q.col == col) { if (dir == 1) { - if (quads[i]->neighbors[1]) + if (q.neighbors[1]) { - icvRemoveQuadFromGroup(quads,rcount,quads[i]->neighbors[1]); - rcount--; + removeQuadFromGroup(quads, *q.neighbors[1]); } - if (quads[i]->neighbors[2]) + if (q.neighbors[2]) { - icvRemoveQuadFromGroup(quads,rcount,quads[i]->neighbors[2]); - rcount--; + removeQuadFromGroup(quads, *q.neighbors[2]); } } else { - if (quads[i]->neighbors[0]) + if (q.neighbors[0]) { - icvRemoveQuadFromGroup(quads,rcount,quads[i]->neighbors[0]); - rcount--; + removeQuadFromGroup(quads, *q.neighbors[0]); } - if (quads[i]->neighbors[3]) + if (q.neighbors[3]) { - icvRemoveQuadFromGroup(quads,rcount,quads[i]->neighbors[3]); - rcount--; + removeQuadFromGroup(quads, *q.neighbors[3]); } } - } } - return rcount; } -static int -icvTrimRow(CvCBQuad **quads, int count, int row, int dir) +void ChessBoardDetector::trimRow(std::vector& quads, int row, int dir) { - int i, rcount = count; + std::vector quads_(quads); // find the right quad(s) - for (i=0; iordered) - PRINTF("index: %d cur: %d\n", row, quads[i]->row); + if (q.ordered) + DPRINTF("i: %d index: %d cur: %d", (int)i, row, q.row); #endif - if (quads[i]->ordered && quads[i]->row == row) + if (q.ordered && q.row == row) { if (dir == 1) // remove from bottom { - if (quads[i]->neighbors[2]) + if (q.neighbors[2]) { - icvRemoveQuadFromGroup(quads,rcount,quads[i]->neighbors[2]); - rcount--; + removeQuadFromGroup(quads, *q.neighbors[2]); } - if (quads[i]->neighbors[3]) + if (q.neighbors[3]) { - icvRemoveQuadFromGroup(quads,rcount,quads[i]->neighbors[3]); - rcount--; + removeQuadFromGroup(quads, *q.neighbors[3]); } } else // remove from top { - if (quads[i]->neighbors[0]) + if (q.neighbors[0]) { - icvRemoveQuadFromGroup(quads,rcount,quads[i]->neighbors[0]); - rcount--; + removeQuadFromGroup(quads, *q.neighbors[0]); } - if (quads[i]->neighbors[1]) + if (q.neighbors[1]) { - icvRemoveQuadFromGroup(quads,rcount,quads[i]->neighbors[1]); - rcount--; + removeQuadFromGroup(quads, *q.neighbors[1]); } } } } - return rcount; } #endif // // remove quad from quad group // - -static void -icvRemoveQuadFromGroup(CvCBQuad **quads, int count, CvCBQuad *q0) +void ChessBoardDetector::removeQuadFromGroup(std::vector& quads, ChessBoardQuad& q0) { - int i, j; + const int count = (int)quads.size(); + + int self_idx = -1; + // remove any references to this quad as a neighbor - for(i = 0; i < count; i++ ) + for (int i = 0; i < count; ++i) { - CvCBQuad *q = quads[i]; - for(j = 0; j < 4; j++ ) + ChessBoardQuad* q = quads[i]; + if (q == &q0) + self_idx = i; + for (int j = 0; j < 4; j++) { - if( q->neighbors[j] == q0 ) + if (q->neighbors[j] == &q0) { - q->neighbors[j] = 0; + q->neighbors[j] = NULL; q->count--; - for(int k = 0; k < 4; k++ ) - if( q0->neighbors[k] == q ) + for (int k = 0; k < 4; ++k) + { + if (q0.neighbors[k] == q) { - q0->neighbors[k] = 0; - q0->count--; + q0.neighbors[k] = 0; + q0.count--; +#ifndef _DEBUG break; +#endif } + } break; } } } + CV_Assert(self_idx >= 0); // item itself should be found // remove the quad - for(i = 0; i < count; i++ ) - { - CvCBQuad *q = quads[i]; - if (q == q0) - { - quads[i] = quads[count-1]; - break; - } - } + if (self_idx != count-1) + quads[self_idx] = quads[count-1]; + quads.resize(count - 1); } // // put quad into correct order, where has value // - -static void -icvOrderQuad(CvCBQuad *quad, CvCBCorner *corner, int common) +void ChessBoardDetector::orderQuad(ChessBoardQuad& quad, ChessBoardCorner& corner, int common) { + CV_DbgAssert(common >= 0 && common <= 3); + // find the corner - int tc; - for (tc=0; tc<4; tc++) - if (quad->corners[tc]->pt.x == corner->pt.x && - quad->corners[tc]->pt.y == corner->pt.y) + int tc = 0;; + for (; tc < 4; ++tc) + if (quad.corners[tc]->pt == corner.pt) break; // set corner order @@ -1189,62 +1188,52 @@ icvOrderQuad(CvCBQuad *quad, CvCBCorner *corner, int common) while (tc != common) { // shift by one - CvCBCorner *tempc; - CvCBQuad *tempq; - tempc = quad->corners[3]; - tempq = quad->neighbors[3]; - for (int i=3; i>0; i--) + ChessBoardCorner *tempc = quad.corners[3]; + ChessBoardQuad *tempq = quad.neighbors[3]; + for (int i = 3; i > 0; --i) { - quad->corners[i] = quad->corners[i-1]; - quad->neighbors[i] = quad->neighbors[i-1]; + quad.corners[i] = quad.corners[i-1]; + quad.neighbors[i] = quad.neighbors[i-1]; } - quad->corners[0] = tempc; - quad->neighbors[0] = tempq; - tc++; - tc = tc%4; + quad.corners[0] = tempc; + quad.neighbors[0] = tempq; + tc = (tc + 1) & 3; } } // if we found too many connect quads, remove those which probably do not belong. -static int -icvCleanFoundConnectedQuads( int quad_count, CvCBQuad **quad_group, CvSize pattern_size ) +int ChessBoardDetector::cleanFoundConnectedQuads(std::vector& quad_group) { - CvPoint2D32f center; - int i, j, k; // number of quads this pattern should contain int count = ((pattern_size.width + 1)*(pattern_size.height + 1) + 1)/2; // if we have more quadrangles than we should, // try to eliminate duplicates or ones which don't belong to the pattern rectangle... - if( quad_count <= count ) + int quad_count = (int)quad_group.size(); + if (quad_count <= count) return quad_count; + CV_DbgAssert(quad_count > 0); // create an array of quadrangle centers - cv::AutoBuffer centers( quad_count ); - cv::Ptr temp_storage(cvCreateMemStorage(0)); + cv::AutoBuffer centers(quad_count); - for( i = 0; i < quad_count; i++ ) + cv::Point2f center; + for (int i = 0; i < quad_count; ++i) { - CvPoint2D32f ci; - CvCBQuad* q = quad_group[i]; - - for( j = 0; j < 4; j++ ) - { - CvPoint2D32f pt = q->corners[j]->pt; - ci.x += pt.x; - ci.y += pt.y; - } + ChessBoardQuad* q = quad_group[i]; - ci.x *= 0.25f; - ci.y *= 0.25f; + const cv::Point2f ci = ( + q->corners[0]->pt + + q->corners[1]->pt + + q->corners[2]->pt + + q->corners[3]->pt + ) * 0.25f; centers[i] = ci; - center.x += ci.x; - center.y += ci.y; + center += ci; } - center.x /= quad_count; - center.y /= quad_count; + center.x *= (1.0f / quad_count); // If we still have more quadrangles than we should, // we try to eliminate bad ones based on minimizing the bounding box. @@ -1253,50 +1242,52 @@ icvCleanFoundConnectedQuads( int quad_count, CvCBQuad **quad_group, CvSize patte // (since we want the rectangle to be as small as possible) // remove the quadrange that causes the biggest reduction // in pattern size until we have the correct number - for( ; quad_count > count; quad_count-- ) + for (; quad_count > count; quad_count--) { double min_box_area = DBL_MAX; - int skip, min_box_area_index = -1; + int min_box_area_index = -1; // For each point, calculate box area without that point - for( skip = 0; skip < quad_count; skip++ ) + for (int skip = 0; skip < quad_count; ++skip) { // get bounding rectangle - CvPoint2D32f temp = centers[skip]; // temporarily make index 'skip' the same as + cv::Point2f temp = centers[skip]; // temporarily make index 'skip' the same as centers[skip] = center; // pattern center (so it is not counted for convex hull) - CvMat pointMat = cvMat(1, quad_count, CV_32FC2, centers.data()); - CvSeq *hull = cvConvexHull2( &pointMat, temp_storage, CV_CLOCKWISE, 1 ); + std::vector hull; + Mat points(1, quad_count, CV_32FC2, ¢ers[0]); + cv::convexHull(points, hull, true); centers[skip] = temp; - double hull_area = fabs(cvContourArea(hull, CV_WHOLE_SEQ)); + double hull_area = contourArea(hull, true); // remember smallest box area - if( hull_area < min_box_area ) + if (hull_area < min_box_area) { min_box_area = hull_area; min_box_area_index = skip; } - cvClearMemStorage( temp_storage ); } - CvCBQuad *q0 = quad_group[min_box_area_index]; + ChessBoardQuad *q0 = quad_group[min_box_area_index]; // remove any references to this quad as a neighbor - for( i = 0; i < quad_count; i++ ) + for (int i = 0; i < quad_count; ++i) { - CvCBQuad *q = quad_group[i]; - for( j = 0; j < 4; j++ ) + ChessBoardQuad *q = quad_group[i]; + for (int j = 0; j < 4; ++j) { - if( q->neighbors[j] == q0 ) + if (q->neighbors[j] == q0) { q->neighbors[j] = 0; q->count--; - for( k = 0; k < 4; k++ ) - if( q0->neighbors[k] == q ) + for (int k = 0; k < 4; ++k) + { + if (q0->neighbors[k] == q) { q0->neighbors[k] = 0; q0->count--; break; } + } break; } } @@ -1311,111 +1302,108 @@ icvCleanFoundConnectedQuads( int quad_count, CvCBQuad **quad_group, CvSize patte return quad_count; } -//===================================================================================== -static int -icvFindConnectedQuads( CvCBQuad *quad, int quad_count, CvCBQuad **out_group, - int group_idx, CvMemStorage* storage ) + +void ChessBoardDetector::findConnectedQuads(std::vector& out_group, int group_idx) { - cv::Ptr temp_storage(cvCreateChildMemStorage( storage )); - CvSeq* stack = cvCreateSeq( 0, sizeof(*stack), sizeof(void*), temp_storage ); - int i, count = 0; + out_group.clear(); - // Scan the array for a first unlabeled quad - for( i = 0; i < quad_count; i++ ) - { - if( quad[i].count > 0 && quad[i].group_idx < 0) - break; - } + std::stack stack; - // Recursively find a group of connected quads starting from the seed quad[i] - if( i < quad_count ) + int i = 0; + for (; i < all_quads_count; i++) { - CvCBQuad* q = &quad[i]; - cvSeqPush( stack, &q ); - out_group[count++] = q; + ChessBoardQuad* q = (ChessBoardQuad*)&all_quads[i]; + + // Scan the array for a first unlabeled quad + if (q->count <= 0 || q->group_idx >= 0) continue; + + // Recursively find a group of connected quads starting from the seed all_quads[i] + stack.push(q); + out_group.push_back(q); q->group_idx = group_idx; q->ordered = false; - while( stack->total ) + while (!stack.empty()) { - cvSeqPop( stack, &q ); - for( i = 0; i < 4; i++ ) + q = stack.top(); CV_Assert(q); + stack.pop(); + for (int k = 0; k < 4; k++ ) { - CvCBQuad *neighbor = q->neighbors[i]; - if( neighbor && neighbor->count > 0 && neighbor->group_idx < 0 ) + ChessBoardQuad *neighbor = q->neighbors[k]; + if (neighbor && neighbor->count > 0 && neighbor->group_idx < 0 ) { - cvSeqPush( stack, &neighbor ); - out_group[count++] = neighbor; + stack.push(neighbor); + out_group.push_back(neighbor); neighbor->group_idx = group_idx; neighbor->ordered = false; } } } + break; } - - return count; } -//===================================================================================== - -static int -icvCheckQuadGroup( CvCBQuad **quad_group, int quad_count, - CvCBCorner **out_corners, CvSize pattern_size ) +int ChessBoardDetector::checkQuadGroup(std::vector& quad_group, std::vector& out_corners) { const int ROW1 = 1000000; const int ROW2 = 2000000; const int ROW_ = 3000000; + + int quad_count = (int)quad_group.size(); + + std::vector corners(quad_count*4); + int corner_count = 0; int result = 0; - int i, out_corner_count = 0, corner_count = 0; - std::vector corners(quad_count*4); - int j, k, kk; int width = 0, height = 0; int hist[5] = {0,0,0,0,0}; - CvCBCorner* first = 0, *first2 = 0, *right, *cur, *below, *c; + //ChessBoardCorner* first = 0, *first2 = 0, *right, *cur, *below, *c; // build dual graph, which vertices are internal quad corners // and two vertices are connected iff they lie on the same quad edge - for( i = 0; i < quad_count; i++ ) + for (int i = 0; i < quad_count; ++i) { - CvCBQuad* q = quad_group[i]; + ChessBoardQuad* q = quad_group[i]; /*CvScalar color = q->count == 0 ? cvScalar(0,255,255) : q->count == 1 ? cvScalar(0,0,255) : q->count == 2 ? cvScalar(0,255,0) : q->count == 3 ? cvScalar(255,255,0) : cvScalar(255,0,0);*/ - for( j = 0; j < 4; j++ ) + for (int j = 0; j < 4; ++j) { //cvLine( debug_img, cvPointFrom32f(q->corners[j]->pt), cvPointFrom32f(q->corners[(j+1)&3]->pt), color, 1, CV_AA, 0 ); - if( q->neighbors[j] ) + if (q->neighbors[j]) { - CvCBCorner *a = q->corners[j], *b = q->corners[(j+1)&3]; + int next_j = (j + 1) & 3; + ChessBoardCorner *a = q->corners[j], *b = q->corners[next_j]; // mark internal corners that belong to: // - a quad with a single neighbor - with ROW1, // - a quad with two neighbors - with ROW2 // make the rest of internal corners with ROW_ int row_flag = q->count == 1 ? ROW1 : q->count == 2 ? ROW2 : ROW_; - if( a->row == 0 ) + if (a->row == 0) { corners[corner_count++] = a; a->row = row_flag; } - else if( a->row > row_flag ) + else if (a->row > row_flag) + { a->row = row_flag; + } - if( q->neighbors[(j+1)&3] ) + if (q->neighbors[next_j]) { - if( a->count >= 4 || b->count >= 4 ) + if (a->count >= 4 || b->count >= 4) goto finalize; - for( k = 0; k < 4; k++ ) + for (int k = 0; k < 4; ++k) { - if( a->neighbors[k] == b ) + if (a->neighbors[k] == b) goto finalize; - if( b->neighbors[k] == a ) + if (b->neighbors[k] == a) goto finalize; } a->neighbors[a->count++] = b; @@ -1425,19 +1413,21 @@ icvCheckQuadGroup( CvCBQuad **quad_group, int quad_count, } } - if( corner_count != pattern_size.width*pattern_size.height ) + if (corner_count != pattern_size.width*pattern_size.height) goto finalize; - for( i = 0; i < corner_count; i++ ) +{ + ChessBoardCorner* first = NULL, *first2 = NULL; + for (int i = 0; i < corner_count; ++i) { int n = corners[i]->count; - assert( 0 <= n && n <= 4 ); + CV_DbgAssert(0 <= n && n <= 4); hist[n]++; - if( !first && n == 2 ) + if (!first && n == 2) { - if( corners[i]->row == ROW1 ) + if (corners[i]->row == ROW1) first = corners[i]; - else if( !first2 && corners[i]->row == ROW2 ) + else if (!first2 && corners[i]->row == ROW2) first2 = corners[i]; } } @@ -1451,18 +1441,19 @@ icvCheckQuadGroup( CvCBQuad **quad_group, int quad_count, hist[3] != (pattern_size.width + pattern_size.height)*2 - 8 ) goto finalize; - cur = first; - right = below = 0; - out_corners[out_corner_count++] = cur; + ChessBoardCorner* cur = first; + ChessBoardCorner* right = NULL; + ChessBoardCorner* below = NULL; + out_corners.push_back(cur); - for( k = 0; k < 4; k++ ) + for (int k = 0; k < 4; ++k) { - c = cur->neighbors[k]; - if( c ) + ChessBoardCorner* c = cur->neighbors[k]; + if (c) { - if( !right ) + if (!right) right = c; - else if( !below ) + else if (!below) below = c; } } @@ -1475,28 +1466,30 @@ icvCheckQuadGroup( CvCBQuad **quad_group, int quad_count, //cvCircle( debug_img, cvPointFrom32f(cur->pt), 3, cvScalar(0,255,0), -1, 8, 0 ); first = below; // remember the first corner in the next row + // find and store the first row (or column) - for(j=1;;j++) + for (int j = 1; ; ++j) { right->row = 0; - out_corners[out_corner_count++] = right; + out_corners.push_back(right); //cvCircle( debug_img, cvPointFrom32f(right->pt), 3, cvScalar(0,255-j*10,0), -1, 8, 0 ); if( right->count == 2 ) break; - if( right->count != 3 || out_corner_count >= MAX(pattern_size.width,pattern_size.height) ) + if( right->count != 3 || (int)out_corners.size() >= std::max(pattern_size.width,pattern_size.height) ) goto finalize; cur = right; - for( k = 0; k < 4; k++ ) + for (int k = 0; k < 4; ++k) { - c = cur->neighbors[k]; - if( c && c->row > 0 ) + ChessBoardCorner* c = cur->neighbors[k]; + if (c && c->row > 0) { - for( kk = 0; kk < 4; kk++ ) + int kk = 0; + for (; kk < 4; ++kk) { - if( c->neighbors[kk] == below ) + if (c->neighbors[kk] == below) break; } - if( kk < 4 ) + if (kk < 4) below = c; else right = c; @@ -1504,109 +1497,114 @@ icvCheckQuadGroup( CvCBQuad **quad_group, int quad_count, } } - width = out_corner_count; - if( width == pattern_size.width ) + width = (int)out_corners.size(); + if (width == pattern_size.width) height = pattern_size.height; - else if( width == pattern_size.height ) + else if (width == pattern_size.height) height = pattern_size.width; else goto finalize; // find and store all the other rows - for( i = 1; ; i++ ) + for (int i = 1; ; ++i) { if( !first ) break; cur = first; first = 0; - for( j = 0;; j++ ) + int j = 0; + for (; ; ++j) { cur->row = i; - out_corners[out_corner_count++] = cur; + out_corners.push_back(cur); //cvCircle( debug_img, cvPointFrom32f(cur->pt), 3, cvScalar(0,0,255-j*10), -1, 8, 0 ); - if( cur->count == 2 + (i < height-1) && j > 0 ) + if (cur->count == 2 + (i < height-1) && j > 0) break; right = 0; // find a neighbor that has not been processed yet // and that has a neighbor from the previous row - for( k = 0; k < 4; k++ ) + for (int k = 0; k < 4; ++k) { - c = cur->neighbors[k]; - if( c && c->row > i ) + ChessBoardCorner* c = cur->neighbors[k]; + if (c && c->row > i) { - for( kk = 0; kk < 4; kk++ ) + int kk = 0; + for (; kk < 4; ++kk) { - if( c->neighbors[kk] && c->neighbors[kk]->row == i-1 ) + if (c->neighbors[kk] && c->neighbors[kk]->row == i-1) break; } - if( kk < 4 ) + if(kk < 4) { right = c; - if( j > 0 ) + if (j > 0) break; } - else if( j == 0 ) + else if (j == 0) first = c; } } - if( !right ) + if (!right) goto finalize; cur = right; } - if( j != width - 1 ) + if (j != width - 1) goto finalize; } - if( out_corner_count != corner_count ) + if ((int)out_corners.size() != corner_count) goto finalize; // check if we need to transpose the board - if( width != pattern_size.width ) + if (width != pattern_size.width) { - CV_SWAP( width, height, k ); + std::swap(width, height); - memcpy( &corners[0], out_corners, corner_count*sizeof(corners[0]) ); - for( i = 0; i < height; i++ ) - for( j = 0; j < width; j++ ) - out_corners[i*width + j] = corners[j*height + i]; + std::vector tmp(out_corners); + for (int i = 0; i < height; ++i) + for (int j = 0; j < width; ++j) + out_corners[i*width + j] = tmp[j*height + i]; } // check if we need to revert the order in each row { - CvPoint2D32f p0 = out_corners[0]->pt, p1 = out_corners[pattern_size.width-1]->pt, - p2 = out_corners[pattern_size.width]->pt; + cv::Point2f p0 = out_corners[0]->pt, + p1 = out_corners[pattern_size.width-1]->pt, + p2 = out_corners[pattern_size.width]->pt; if( (p1.x - p0.x)*(p2.y - p1.y) - (p1.y - p0.y)*(p2.x - p1.x) < 0 ) { - if( width % 2 == 0 ) + if (width % 2 == 0) { - for( i = 0; i < height; i++ ) - for( j = 0; j < width/2; j++ ) - CV_SWAP( out_corners[i*width+j], out_corners[i*width+width-j-1], c ); + for (int i = 0; i < height; ++i) + for (int j = 0; j < width/2; ++j) + std::swap(out_corners[i*width+j], out_corners[i*width+width-j-1]); } else { - for( j = 0; j < width; j++ ) - for( i = 0; i < height/2; i++ ) - CV_SWAP( out_corners[i*width+j], out_corners[(height - i - 1)*width+j], c ); + for (int j = 0; j < width; ++j) + for (int i = 0; i < height/2; ++i) + std::swap(out_corners[i*width+j], out_corners[(height - i - 1)*width+j]); } } } result = corner_count; +} finalize: - - if( result <= 0 ) + if (result <= 0) { - corner_count = MIN( corner_count, pattern_size.width*pattern_size.height ); - for( i = 0; i < corner_count; i++ ) + corner_count = std::min(corner_count, pattern_size.area()); + out_corners.resize(corner_count); + for (int i = 0; i < corner_count; i++) out_corners[i] = corners[i]; + result = -corner_count; - if (result == -pattern_size.width*pattern_size.height) + if (result == -pattern_size.area()) result = -result; } @@ -1615,19 +1613,13 @@ finalize: - -//===================================================================================== - -static void icvFindQuadNeighbors( CvCBQuad *quads, int quad_count ) +void ChessBoardDetector::findQuadNeighbors() { const float thresh_scale = 1.f; - int idx, i, k, j; - float dx, dy, dist; - // find quad neighbors - for( idx = 0; idx < quad_count; idx++ ) + for (int idx = 0; idx < all_quads_count; idx++) { - CvCBQuad* cur_quad = &quads[idx]; + ChessBoardQuad& cur_quad = (ChessBoardQuad&)all_quads[idx]; // choose the points of the current quadrangle that are close to // some points of the other quadrangles @@ -1635,151 +1627,141 @@ static void icvFindQuadNeighbors( CvCBQuad *quads, int quad_count ) // checker board). Search only in other quadrangles! // for each corner of this quadrangle - for( i = 0; i < 4; i++ ) + for (int i = 0; i < 4; i++) { - CvPoint2D32f pt; + if (cur_quad.neighbors[i]) + continue; + float min_dist = FLT_MAX; int closest_corner_idx = -1; - CvCBQuad *closest_quad = 0; - CvCBCorner *closest_corner = 0; + ChessBoardQuad *closest_quad = 0; - if( cur_quad->neighbors[i] ) - continue; - - pt = cur_quad->corners[i]->pt; + cv::Point2f pt = cur_quad.corners[i]->pt; // find the closest corner in all other quadrangles - for( k = 0; k < quad_count; k++ ) + for (int k = 0; k < all_quads_count; k++) { - if( k == idx ) + if (k == idx) continue; - for( j = 0; j < 4; j++ ) + ChessBoardQuad& q_k = all_quads[k]; + + for (int j = 0; j < 4; j++) { - if( quads[k].neighbors[j] ) + if (q_k.neighbors[j]) continue; - dx = pt.x - quads[k].corners[j]->pt.x; - dy = pt.y - quads[k].corners[j]->pt.y; - dist = dx * dx + dy * dy; - - if( dist < min_dist && - dist <= cur_quad->edge_len*thresh_scale && - dist <= quads[k].edge_len*thresh_scale ) + float dist = normL2Sqr(pt - q_k.corners[j]->pt); + if (dist < min_dist && + dist <= cur_quad.edge_len*thresh_scale && + dist <= q_k.edge_len*thresh_scale ) { // check edge lengths, make sure they're compatible // edges that are different by more than 1:4 are rejected - float ediff = cur_quad->edge_len - quads[k].edge_len; - if (ediff > 32*cur_quad->edge_len || - ediff > 32*quads[k].edge_len) + float ediff = cur_quad.edge_len - q_k.edge_len; + if (ediff > 32*cur_quad.edge_len || + ediff > 32*q_k.edge_len) { - PRINTF("Incompatible edge lengths\n"); + DPRINTF("Incompatible edge lengths"); continue; } closest_corner_idx = j; - closest_quad = &quads[k]; + closest_quad = &q_k; min_dist = dist; } } } // we found a matching corner point? - if( closest_corner_idx >= 0 && min_dist < FLT_MAX ) + if (closest_corner_idx >= 0 && min_dist < FLT_MAX) { + CV_Assert(closest_quad); + + if (cur_quad.count >= 4 || closest_quad->count >= 4) + continue; + // If another point from our current quad is closer to the found corner // than the current one, then we don't count this one after all. // This is necessary to support small squares where otherwise the wrong // corner will get matched to closest_quad; - closest_corner = closest_quad->corners[closest_corner_idx]; + ChessBoardCorner& closest_corner = *closest_quad->corners[closest_corner_idx]; - for( j = 0; j < 4; j++ ) + int j = 0; + for (; j < 4; j++) { - if( cur_quad->neighbors[j] == closest_quad ) + if (cur_quad.neighbors[j] == closest_quad) break; - dx = closest_corner->pt.x - cur_quad->corners[j]->pt.x; - dy = closest_corner->pt.y - cur_quad->corners[j]->pt.y; - - if( dx * dx + dy * dy < min_dist ) + if (normL2Sqr(closest_corner.pt - cur_quad.corners[j]->pt) < min_dist) break; } - - if( j < 4 || cur_quad->count >= 4 || closest_quad->count >= 4 ) + if (j < 4) continue; // Check that each corner is a neighbor of different quads - for( j = 0; j < closest_quad->count; j++ ) + for(j = 0; j < closest_quad->count; j++ ) { - if( closest_quad->neighbors[j] == cur_quad ) + if (closest_quad->neighbors[j] == &cur_quad) break; } - if( j < closest_quad->count ) + if (j < closest_quad->count) continue; // check whether the closest corner to closest_corner // is different from cur_quad->corners[i]->pt - for( k = 0; k < quad_count; k++ ) + for (j = 0; j < all_quads_count; j++ ) { - CvCBQuad* q = &quads[k]; - if( k == idx || q == closest_quad ) + ChessBoardQuad* q = &const_cast(all_quads[j]); + if (j == idx || q == closest_quad) continue; - for( j = 0; j < 4; j++ ) - if( !q->neighbors[j] ) + int k = 0; + for (; k < 4; k++ ) + { + if (!q->neighbors[k]) { - dx = closest_corner->pt.x - q->corners[j]->pt.x; - dy = closest_corner->pt.y - q->corners[j]->pt.y; - dist = dx*dx + dy*dy; - if( dist < min_dist ) + if (normL2Sqr(closest_corner.pt - q->corners[k]->pt) < min_dist) break; } - if( j < 4 ) + } + if (k < 4) break; } - - if( k < quad_count ) + if (j < all_quads_count) continue; - closest_corner->pt.x = (pt.x + closest_corner->pt.x) * 0.5f; - closest_corner->pt.y = (pt.y + closest_corner->pt.y) * 0.5f; + closest_corner.pt = (pt + closest_corner.pt) * 0.5f; // We've found one more corner - remember it - cur_quad->count++; - cur_quad->neighbors[i] = closest_quad; - cur_quad->corners[i] = closest_corner; + cur_quad.count++; + cur_quad.neighbors[i] = closest_quad; + cur_quad.corners[i] = &closest_corner; closest_quad->count++; - closest_quad->neighbors[closest_corner_idx] = cur_quad; + closest_quad->neighbors[closest_corner_idx] = &cur_quad; } } } } -//===================================================================================== // returns corners in clockwise order // corners don't necessarily start at same position on quad (e.g., // top left corner) - -static int -icvGenerateQuads( CvCBQuad **out_quads, CvCBCorner **out_corners, - CvMemStorage *storage, const cv::Mat & image_, int flags, int *max_quad_buf_size ) +void ChessBoardDetector::generateQuads(const cv::Mat& image_, int flags) { - int quad_count = 0; - cv::Ptr temp_storage; + binarized_image = image_; // save for debug purposes - if( out_quads ) - *out_quads = 0; + int quad_count = 0; - if( out_corners ) - *out_corners = 0; + all_quads.deallocate(); + all_corners.deallocate(); // empiric bound for minimal allowed perimeter for squares int min_size = 25; //cvRound( image->cols * image->rows * .03 * 0.01 * 0.92 ); bool filterQuads = (flags & CALIB_CB_FILTER_QUADS) != 0; #ifdef USE_CV_FINDCONTOURS // use cv::findContours - CV_UNUSED(storage); std::vector > contours; std::vector hierarchy; @@ -1789,8 +1771,7 @@ icvGenerateQuads( CvCBQuad **out_quads, CvCBCorner **out_corners, if (contours.empty()) { CV_LOG_DEBUG(NULL, "calib3d(chessboard): cv::findContours() returns no contours"); - *max_quad_buf_size = 0; - return 0; + return; } std::vector contour_child_counter(contours.size(), 0); @@ -1863,65 +1844,55 @@ icvGenerateQuads( CvCBQuad **out_quads, CvCBCorner **out_corners, } size_t total = contour_quads.size(); - *max_quad_buf_size = (int)std::max((size_t)2, total * 3); - *out_quads = (CvCBQuad*)cvAlloc(*max_quad_buf_size * sizeof((*out_quads)[0])); - *out_corners = (CvCBCorner*)cvAlloc(*max_quad_buf_size * 4 * sizeof((*out_corners)[0])); + size_t max_quad_buf_size = std::max((size_t)2, total * 3); + all_quads.allocate(max_quad_buf_size); + all_corners.allocate(max_quad_buf_size * 4); // Create array of quads structures - for(int idx = 0; idx < (int)contour_quads.size(); idx++ ) + for (size_t idx = 0; idx < total; ++idx) { - CvCBQuad* q = &(*out_quads)[quad_count]; - QuadCountour& qc = contour_quads[idx]; if (filterQuads && qc.parent_contour != boardIdx) continue; + int quad_idx = quad_count++; + ChessBoardQuad& q = all_quads[quad_idx]; + // reset group ID - memset(q, 0, sizeof(*q)); - q->group_idx = -1; + q = ChessBoardQuad(); for (int i = 0; i < 4; ++i) { - CvCBCorner* corner = &(*out_corners)[quad_count*4 + i]; + Point2f pt(qc.pt[i]); + ChessBoardCorner& corner = all_corners[quad_idx * 4 + i]; - memset(corner, 0, sizeof(*corner)); - corner->pt = qc.pt[i]; - q->corners[i] = corner; + corner = ChessBoardCorner(pt); + q.corners[i] = &corner; } - q->edge_len = FLT_MAX; + q.edge_len = FLT_MAX; for (int i = 0; i < 4; ++i) { - // TODO simplify with normL2Sqr() - float dx = q->corners[i]->pt.x - q->corners[(i+1)&3]->pt.x; - float dy = q->corners[i]->pt.y - q->corners[(i+1)&3]->pt.y; - float d = dx*dx + dy*dy; - if (q->edge_len > d) - q->edge_len = d; + float d = normL2Sqr(q.corners[i]->pt - q.corners[(i+1)&3]->pt); + q.edge_len = std::min(q.edge_len, d); } - quad_count++; } #else // use legacy API: cvStartFindContours / cvFindNextContour / cvEndFindContours - CvMat image_old(image_), *image = &image_old; + CvMat image_old = cvMat(image_), *image = &image_old; - CvSeq *src_contour = 0; - CvSeq *root; CvContourEx* board = 0; - CvContourScanner scanner; - int i, idx; - - CV_Assert( out_corners && out_quads ); // create temporary storage for contours and the sequence of pointers to found quadrangles - temp_storage.reset(cvCreateChildMemStorage( storage )); - root = cvCreateSeq( 0, sizeof(CvSeq), sizeof(CvSeq*), temp_storage ); + cv::Ptr temp_storage(cvCreateMemStorage(0)); + CvSeq *root = cvCreateSeq(0, sizeof(CvSeq), sizeof(CvSeq*), temp_storage); // initialize contour retrieving routine - scanner = cvStartFindContours( image, temp_storage, sizeof(CvContourEx), - CV_RETR_CCOMP, CV_CHAIN_APPROX_SIMPLE ); + CvContourScanner scanner = cvStartFindContours(image, temp_storage, sizeof(CvContourEx), + CV_RETR_CCOMP, CV_CHAIN_APPROX_SIMPLE); // get all the contours one by one - while( (src_contour = cvFindNextContour( scanner )) != 0 ) + CvSeq* src_contour = NULL; + while ((src_contour = cvFindNextContour(scanner)) != NULL) { CvSeq *dst_contour = 0; CvRect rect = ((CvContour*)src_contour)->rect; @@ -1930,8 +1901,7 @@ icvGenerateQuads( CvCBQuad **out_quads, CvCBCorner **out_corners, if( CV_IS_SEQ_HOLE(src_contour) && rect.width*rect.height >= min_size ) { const int min_approx_level = 1, max_approx_level = MAX_CONTOUR_APPROX; - int approx_level; - for( approx_level = min_approx_level; approx_level <= max_approx_level; approx_level++ ) + for (int approx_level = min_approx_level; approx_level <= max_approx_level; approx_level++ ) { dst_contour = cvApproxPoly( src_contour, sizeof(CvContour), temp_storage, CV_POLY_APPROX_DP, (float)approx_level ); @@ -1950,31 +1920,21 @@ icvGenerateQuads( CvCBQuad **out_quads, CvCBCorner **out_corners, // reject non-quadrangles if( dst_contour->total == 4 && cvCheckContourConvexity(dst_contour) ) { - CvPoint pt[4]; - double d1, d2, p = cvContourPerimeter(dst_contour); + cv::Point2i pt[4]; + double p = cvContourPerimeter(dst_contour); double area = fabs(cvContourArea(dst_contour, CV_WHOLE_SEQ)); - double dx, dy; - for( i = 0; i < 4; i++ ) + for (int i = 0; i < 4; ++i) pt[i] = *(CvPoint*)cvGetSeqElem(dst_contour, i); + CV_LOG_VERBOSE(NULL, 9, "... contours(" << root->total << " added):" << pt[0] << " " << pt[1] << " " << pt[2] << " " << pt[3]); - dx = pt[0].x - pt[2].x; - dy = pt[0].y - pt[2].y; - d1 = sqrt(dx*dx + dy*dy); - - dx = pt[1].x - pt[3].x; - dy = pt[1].y - pt[3].y; - d2 = sqrt(dx*dx + dy*dy); + double d1 = sqrt(normL2Sqr(pt[0] - pt[2])); + double d2 = sqrt(normL2Sqr(pt[1] - pt[3])); // philipg. Only accept those quadrangles which are more square // than rectangular and which are big enough - double d3, d4; - dx = pt[0].x - pt[1].x; - dy = pt[0].y - pt[1].y; - d3 = sqrt(dx*dx + dy*dy); - dx = pt[1].x - pt[2].x; - dy = pt[1].y - pt[2].y; - d4 = sqrt(dx*dx + dy*dy); + double d3 = sqrt(normL2Sqr(pt[0] - pt[1])); + double d4 = sqrt(normL2Sqr(pt[1] - pt[2])); if (!filterQuads || (d3*4 > d4 && d4*4 > d3 && d3*d4 < area*1.5 && area > min_size && d1 >= 0.15 * p && d2 >= 0.15 * p)) @@ -1996,156 +1956,151 @@ icvGenerateQuads( CvCBQuad **out_quads, CvCBCorner **out_corners, // allocate quad & corner buffers int total = root->total; - *max_quad_buf_size = MAX(1, (total + total / 2)) * 2; - *out_quads = (CvCBQuad*)cvAlloc(*max_quad_buf_size * sizeof((*out_quads)[0])); - *out_corners = (CvCBCorner*)cvAlloc(*max_quad_buf_size * 4 * sizeof((*out_corners)[0])); + size_t max_quad_buf_size = std::max((size_t)2, (size_t)total * 3); + all_quads.allocate(max_quad_buf_size); + all_corners.allocate(max_quad_buf_size * 4); // Create array of quads structures - for( idx = 0; idx < root->total; idx++ ) + for (int idx = 0; idx < total; ++idx) { - CvCBQuad* q = &(*out_quads)[quad_count]; - src_contour = *(CvSeq**)cvGetSeqElem( root, idx ); + /* CvSeq* */src_contour = *(CvSeq**)cvGetSeqElem(root, idx); if (filterQuads && src_contour->v_prev != (CvSeq*)board) continue; + int quad_idx = quad_count++; + ChessBoardQuad& q = all_quads[quad_idx]; + // reset group ID - memset( q, 0, sizeof(*q) ); - q->group_idx = -1; - assert( src_contour->total == 4 ); - for( i = 0; i < 4; i++ ) + q = ChessBoardQuad(); + CV_Assert(src_contour->total == 4); + for (int i = 0; i < 4; i++) { - CvPoint * onePoint = (CvPoint*)cvGetSeqElem(src_contour, i); + Point* onePoint = (Point*)cvGetSeqElem(src_contour, i); CV_Assert(onePoint != NULL); - CvPoint2D32f pt = cvPointTo32f(*onePoint); - CvCBCorner* corner = &(*out_corners)[quad_count*4 + i]; + Point2f pt(*onePoint); + ChessBoardCorner& corner = all_corners[quad_idx*4 + i]; - memset( corner, 0, sizeof(*corner) ); - corner->pt = pt; - q->corners[i] = corner; + corner = ChessBoardCorner(pt); + q.corners[i] = &corner; } - q->edge_len = FLT_MAX; - for( i = 0; i < 4; i++ ) + q.edge_len = FLT_MAX; + for (int i = 0; i < 4; ++i) { - float dx = q->corners[i]->pt.x - q->corners[(i+1)&3]->pt.x; - float dy = q->corners[i]->pt.y - q->corners[(i+1)&3]->pt.y; - float d = dx*dx + dy*dy; - if( q->edge_len > d ) - q->edge_len = d; + float d = normL2Sqr(q.corners[i]->pt - q.corners[(i+1)&3]->pt); + q.edge_len = std::min(q.edge_len, d); } - quad_count++; } #endif + all_quads_count = quad_count; + CV_LOG_VERBOSE(NULL, 3, "Total quad contours: " << total); - CV_LOG_VERBOSE(NULL, 3, "max_quad_buf_size=" << *max_quad_buf_size); + CV_LOG_VERBOSE(NULL, 3, "max_quad_buf_size=" << max_quad_buf_size); CV_LOG_VERBOSE(NULL, 3, "filtered quad_count=" << 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) +bool ChessBoardDetector::processQuads(std::vector& out_corners, int &prev_sqr_size) { - if( quad_count <= 0 ) + out_corners.resize(0); + if (all_quads_count <= 0) return false; - bool found = false; + size_t max_quad_buf_size = all_quads.size(); // Find quad's neighbors - icvFindQuadNeighbors( quads, quad_count ); - - // allocate extra for adding in icvOrderFoundQuads - CvCBQuad **quad_group = 0; - CvCBCorner **corner_group = 0; + findQuadNeighbors(); - 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 orderFoundQuads + std::vector quad_group; + std::vector corner_group; corner_group.reserve(max_quad_buf_size * 4); - for( int group_idx = 0; ; group_idx++ ) + for (int group_idx = 0; ; group_idx++) { - int count = icvFindConnectedQuads( quads, quad_count, quad_group, group_idx, storage ); - - if( count == 0 ) + findConnectedQuads(quad_group, group_idx); + if (quad_group.empty()) break; + int count = (int)quad_group.size(); + // 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); + DPRINTF("Starting ordering of inner quads (%d)", count); + count = orderFoundConnectedQuads(quad_group); + DPRINTF("Finished ordering of inner quads (%d)", 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 = cleanFoundConnectedQuads(quad_group); + DPRINTF("Connected group: %d, count: %d", group_idx, count); - count = icvCheckQuadGroup( quad_group, count, corner_group, pattern_size ); - PRINTF("Connected group: %d, count: %d\n", group_idx, count); + count = checkQuadGroup(quad_group, corner_group); + DPRINTF("Connected group: %d, count: %d", group_idx, count); int n = count > 0 ? pattern_size.width * pattern_size.height : -count; - n = MIN( n, pattern_size.width * pattern_size.height ); + n = std::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; + float sum = corner_group[i]->sumDist(ni); + sum_dist += sum; total += ni; } - prev_sqr_size = cvRound(sum_dist/MAX(total, 1)); + prev_sqr_size = cvRound(sum_dist/std::max(total, 1)); - if( count > 0 || (out_corner_count && -count > *out_corner_count) ) + if (count > 0 || (-count > (int)out_corners.size())) { // 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; + out_corners.reserve(n); + for (int i = 0; i < n; ++i) + out_corners.push_back(corner_group[i]->pt); - if( count == pattern_size.width*pattern_size.height - && icvCheckBoardMonotony( out_corners, pattern_size )) + if (count == pattern_size.width*pattern_size.height + && checkBoardMonotony(out_corners)) { - found = true; - break; + return true; } } } - cvFree(&quad_group); - cvFree(&corner_group); - - return found; + return false; } -//================================================================================================== -CV_IMPL void -cvDrawChessboardCorners( CvArr* _image, CvSize pattern_size, - CvPoint2D32f* corners, int count, int found ) + +void drawChessboardCorners( InputOutputArray image, Size patternSize, + InputArray _corners, + bool patternWasFound ) { + CV_INSTRUMENT_REGION(); + + int type = image.type(); + int cn = CV_MAT_CN(type); + CV_CheckType(type, cn == 1 || cn == 3 || cn == 4, + "Number of channels must be 1, 3 or 4" ); + + int depth = CV_MAT_DEPTH(type); + CV_CheckType(type, depth == CV_8U || depth == CV_16U || depth == CV_32F, + "Only 8-bit, 16-bit or floating-point 32-bit images are supported"); + + if (_corners.empty()) + return; + Mat corners = _corners.getMat(); + const Point2f* corners_data = corners.ptr(0); + int nelems = corners.checkVector(2, CV_32F, true); + CV_Assert(nelems >= 0); + const int shift = 0; const int radius = 4; const int r = radius*(1 << shift); - int i; - CvMat stub, *image; - double scale = 1; - int type, cn, line_type; - - image = cvGetMat( _image, &stub ); - - type = CV_MAT_TYPE(image->type); - cn = CV_MAT_CN(type); - if( cn != 1 && cn != 3 && cn != 4 ) - CV_Error( CV_StsUnsupportedFormat, "Number of channels must be 1, 3 or 4" ); - switch( CV_MAT_DEPTH(image->type) ) + double scale = 1; + switch (depth) { case CV_8U: scale = 1; @@ -2156,140 +2111,90 @@ cvDrawChessboardCorners( CvArr* _image, CvSize pattern_size, case CV_32F: scale = 1./255; break; - default: - CV_Error( CV_StsUnsupportedFormat, - "Only 8-bit, 16-bit or floating-point 32-bit images are supported" ); } - line_type = type == CV_8UC1 || type == CV_8UC3 ? CV_AA : 8; + int line_type = (type == CV_8UC1 || type == CV_8UC3) ? LINE_AA : LINE_8; - if( !found ) + if (!patternWasFound) { - CvScalar color(0,0,255,0); - if( cn == 1 ) - color = cvScalarAll(200); - color.val[0] *= scale; - color.val[1] *= scale; - color.val[2] *= scale; - color.val[3] *= scale; - - for( i = 0; i < count; i++ ) + Scalar color(0,0,255,0); + if (cn == 1) + color = Scalar::all(200); + color *= scale; + + for (int i = 0; i < nelems; i++ ) { - CvPoint pt; - pt.x = cvRound(corners[i].x*(1 << shift)); - pt.y = cvRound(corners[i].y*(1 << shift)); - cvLine( image, cvPoint( pt.x - r, pt.y - r ), - cvPoint( pt.x + r, pt.y + r ), color, 1, line_type, shift ); - cvLine( image, cvPoint( pt.x - r, pt.y + r), - cvPoint( pt.x + r, pt.y - r), color, 1, line_type, shift ); - cvCircle( image, pt, r+(1< tmpcorners(count+1); - Mat image = _image.getMat(); CvMat c_image = image; - bool ok = cvFindChessboardCorners(&c_image, patternSize, - (CvPoint2D32f*)&tmpcorners[0], &count, flags ) > 0; - if( count > 0 ) - { - tmpcorners.resize(count); - Mat(tmpcorners).copyTo(corners); - } - else - corners.release(); - return ok; -} - -namespace -{ -int quiet_error(int /*status*/, const char* /*func_name*/, - const char* /*err_msg*/, const char* /*file_name*/, - int /*line*/, void* /*userdata*/ ) -{ - return 0; -} -} - -void cv::drawChessboardCorners( InputOutputArray _image, Size patternSize, - InputArray _corners, - bool patternWasFound ) -{ - CV_INSTRUMENT_REGION() - - Mat corners = _corners.getMat(); - if( corners.empty() ) - return; - Mat image = _image.getMat(); CvMat c_image = image; - int nelems = corners.checkVector(2, CV_32F, true); - CV_Assert(nelems >= 0); - cvDrawChessboardCorners( &c_image, patternSize, corners.ptr(), - nelems, patternWasFound ); + return 0; } -bool cv::findCirclesGrid( InputArray image, Size patternSize, - OutputArray centers, int flags, - const Ptr &blobDetector, - CirclesGridFinderParameters parameters) +bool findCirclesGrid(InputArray image, Size patternSize, + OutputArray centers, int flags, + const Ptr &blobDetector, + CirclesGridFinderParameters parameters) { CirclesGridFinderParameters2 parameters2; *((CirclesGridFinderParameters*)¶meters2) = parameters; return cv::findCirclesGrid2(image, patternSize, centers, flags, blobDetector, parameters2); } -bool cv::findCirclesGrid2( InputArray _image, Size patternSize, - OutputArray _centers, int flags, const Ptr &blobDetector, - CirclesGridFinderParameters2 parameters) +bool findCirclesGrid2(InputArray _image, Size patternSize, + OutputArray _centers, int flags, const Ptr &blobDetector, + CirclesGridFinderParameters2 parameters) { CV_INSTRUMENT_REGION() @@ -2332,7 +2237,7 @@ bool cv::findCirclesGrid2( InputArray _image, Size patternSize, #define BE_QUIET 1 #if BE_QUIET void* oldCbkData; - ErrorCallback oldCbk = redirectError(quiet_error, 0, &oldCbkData); + ErrorCallback oldCbk = redirectError(quiet_error, 0, &oldCbkData); // FIXIT not thread safe #endif CV_TRY { @@ -2356,7 +2261,7 @@ bool cv::findCirclesGrid2( InputArray _image, Size patternSize, boxFinder.getAsymmetricHoles(centers); break; default: - CV_Error(CV_StsBadArg, "Unknown pattern type"); + CV_Error(Error::StsBadArg, "Unknown pattern type"); } if (i != 0) @@ -2381,10 +2286,11 @@ bool cv::findCirclesGrid2( InputArray _image, Size patternSize, return false; } -bool cv::findCirclesGrid( InputArray _image, Size patternSize, - OutputArray _centers, int flags, const Ptr &blobDetector) +bool findCirclesGrid(InputArray _image, Size patternSize, + OutputArray _centers, int flags, const Ptr &blobDetector) { return cv::findCirclesGrid2(_image, patternSize, _centers, flags, blobDetector, CirclesGridFinderParameters2()); } +} // namespace /* End of file. */ diff --git a/modules/calib3d/src/precomp.hpp b/modules/calib3d/src/precomp.hpp index 753c46ae54..329692eb01 100644 --- a/modules/calib3d/src/precomp.hpp +++ b/modules/calib3d/src/precomp.hpp @@ -42,12 +42,14 @@ #ifndef __OPENCV_PRECOMP_H__ #define __OPENCV_PRECOMP_H__ +#include "opencv2/core/utility.hpp" + +#include "opencv2/core/private.hpp" + #include "opencv2/calib3d.hpp" #include "opencv2/imgproc.hpp" #include "opencv2/features2d.hpp" -#include "opencv2/core/utility.hpp" -#include "opencv2/core/private.hpp" #include "opencv2/core/ocl.hpp" diff --git a/modules/calib3d/test/test_cameracalibration.cpp b/modules/calib3d/test/test_cameracalibration.cpp index 3534c324fe..d8f6cd2e03 100644 --- a/modules/calib3d/test/test_cameracalibration.cpp +++ b/modules/calib3d/test/test_cameracalibration.cpp @@ -1618,7 +1618,8 @@ void CV_StereoCalibrationTest::run( int ) bool found2 = findChessboardCorners(right, patternSize, imgpt2[i]); if(!found1 || !found2) { - ts->printf( cvtest::TS::LOG, "The function could not detect boards on the images %s and %s, testcase %d\n", + ts->printf( cvtest::TS::LOG, "The function could not detect boards (%d x %d) on the images %s and %s, testcase %d\n", + patternSize.width, patternSize.height, imglist[i*2].c_str(), imglist[i*2+1].c_str(), testcase ); ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_OUTPUT ); return; diff --git a/modules/core/include/opencv2/core/types_c.h b/modules/core/include/opencv2/core/types_c.h index 65f354e070..81b24f0cc7 100644 --- a/modules/core/include/opencv2/core/types_c.h +++ b/modules/core/include/opencv2/core/types_c.h @@ -409,6 +409,11 @@ IplConvKernelFP; #define CV_MAT_MAGIC_VAL 0x42420000 #define CV_TYPE_NAME_MAT "opencv-matrix" +#ifdef __cplusplus +typedef struct CvMat CvMat; +CV_INLINE CvMat cvMat(const cv::Mat& m); +#endif + /** Matrix elements are stored row by row. Element (i, j) (i - 0-based row index, j - 0-based column index) of a matrix can be retrieved or modified using CV_MAT_ELEM macro: @@ -531,6 +536,16 @@ inline CvMat::CvMat(const cv::Mat& m) step = (int)m.step[0]; type = (type & ~cv::Mat::CONTINUOUS_FLAG) | (m.flags & cv::Mat::CONTINUOUS_FLAG); } + +inline CvMat cvMat(const cv::Mat& m) +{ + CvMat self; + CV_DbgAssert(m.dims <= 2); + self = cvMat(m.rows, m.dims == 1 ? 1 : m.cols, m.type(), m.data); + self.step = (int)m.step[0]; + self.type = (self.type & ~cv::Mat::CONTINUOUS_FLAG) | (m.flags & cv::Mat::CONTINUOUS_FLAG); + return self; +} #endif @@ -916,6 +931,15 @@ CV_INLINE CvPoint2D32f cvPoint2D32f( double x, double y ) return p; } +#ifdef __cplusplus +template +CvPoint2D32f cvPoint2D32f(const cv::Point_<_Tp>& pt) +{ + CvPoint2D32f p((float)pt.x, (float)pt.y); + return p; +} +#endif + /** converts CvPoint to CvPoint2D32f. */ CV_INLINE CvPoint2D32f cvPointTo32f( CvPoint point ) { diff --git a/modules/ts/src/ts.cpp b/modules/ts/src/ts.cpp index ee823a54a9..06f9118a28 100644 --- a/modules/ts/src/ts.cpp +++ b/modules/ts/src/ts.cpp @@ -255,8 +255,9 @@ void BaseTest::safe_run( int start_from ) const char* errorStr = cvErrorStr(exc.code); char buf[1 << 16]; - sprintf( buf, "OpenCV Error:\n\t%s (%s) in %s, file %s, line %d", - errorStr, exc.err.c_str(), exc.func.size() > 0 ? + const char* delim = exc.err.find('\n') == cv::String::npos ? "" : "\n"; + sprintf( buf, "OpenCV Error:\n\t%s (%s%s) in %s, file %s, line %d", + errorStr, delim, exc.err.c_str(), exc.func.size() > 0 ? exc.func.c_str() : "unknown function", exc.file.c_str(), exc.line ); ts->printf(TS::LOG, "%s\n", buf); @@ -384,7 +385,9 @@ int BadArgTest::run_test_case( int expected_code, const string& _descr ) catch(const cv::Exception& e) { thrown = true; - if( e.code != expected_code ) + if (e.code != expected_code && + e.code != cv::Error::StsError && e.code != cv::Error::StsAssert // Exact error codes support will be dropped. Checks should provide proper text messages intead. + ) { ts->printf(TS::LOG, "%s (test case #%d): the error code %d is different from the expected %d\n", descr, test_case_idx, e.code, expected_code); @@ -471,7 +474,8 @@ string TS::str_from_code( const TS::FailureCode code ) static int tsErrorCallback( int status, const char* func_name, const char* err_msg, const char* file_name, int line, TS* ts ) { - ts->printf(TS::LOG, "OpenCV Error:\n\t%s (%s) in %s, file %s, line %d\n", cvErrorStr(status), err_msg, func_name[0] != 0 ? func_name : "unknown function", file_name, line); + const char* delim = std::string(err_msg).find('\n') == std::string::npos ? "" : "\n"; + ts->printf(TS::LOG, "OpenCV Error:\n\t%s (%s%s) in %s, file %s, line %d\n", cvErrorStr(status), delim, err_msg, func_name[0] != 0 ? func_name : "unknown function", file_name, line); return 0; }