#include "precomp.hpp"
#include "circlesgrid.hpp"
#include <stack>
#define DEBUG_CHESSBOARD_TIMEOUT 0
#include <opencv2/core/utils/logger.defines.hpp>
#include <opencv2/core/utils/logger.hpp>
#ifdef DEBUG_CHESSBOARD
#include "opencv2/highgui.hpp"
#include "opencv2/imgproc.hpp"
#define DPRINTF(...) CV_LOG_INFO(NULL, cv::format("calib3d: " __VA_ARGS__))
#else
#define DPRINTF(...)
#endif
namespace cv {
#define MAX_CONTOUR_APPROX 7
#define USE_CV_FINDCONTOURS
#ifdef USE_CV_FINDCONTOURS
struct QuadCountour {
Point pt[4];
int parent_contour;
QuadCountour(const Point pt_[4], int parent_contour_) :
parent_contour(parent_contour_)
{
pt[0] = pt_[0]; pt[1] = pt_[1]; pt[2] = pt_[2]; pt[3] = pt_[3];
}
};
#else
}
#include "opencv2/imgproc/imgproc_c.h"
namespace cv {
struct CvContourEx
{
CV_CONTOUR_FIELDS()
int counter;
};
#endif
struct ChessBoardCorner
{
cv::Point2f pt;
int row;
int count;
struct ChessBoardCorner* neighbors[4];
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)
{
if (neighbors[i])
{
sum += sqrt(normL2Sqr<float>(neighbors[i]->pt - pt));
n++;
}
}
n_ = n;
return sum;
}
};
struct ChessBoardQuad
{
int count;
int group_idx;
int row, col;
bool ordered;
float edge_len;
ChessBoardCorner *corners[4];
struct ChessBoardQuad *neighbors[4];
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
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_, 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)
{
ChessBoardQuad & quad = quads[i];
for (int j = 0; j < 4; ++j)
{
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
class ChessBoardDetector
{
public:
cv::Mat binarized_image;
Size pattern_size;
cv::AutoBuffer<ChessBoardQuad> all_quads;
cv::AutoBuffer<ChessBoardCorner> all_corners;
int all_quads_count;
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;
}
void generateQuads(const cv::Mat& image_, int flags);
bool processQuads(std::vector<cv::Point2f>& out_corners, int &prev_sqr_size);
void findQuadNeighbors();
void findConnectedQuads(std::vector<ChessBoardQuad*>& out_group, int group_idx);
int checkQuadGroup(std::vector<ChessBoardQuad*>& quad_group, std::vector<ChessBoardCorner*>& out_corners);
int cleanFoundConnectedQuads(std::vector<ChessBoardQuad*>& quad_group);
int orderFoundConnectedQuads(std::vector<ChessBoardQuad*>& quads);
void orderQuad(ChessBoardQuad& quad, ChessBoardCorner& corner, int common);
#ifdef ENABLE_TRIM_COL_ROW
void trimCol(std::vector<ChessBoardQuad*>& quads, int col, int dir);
void trimRow(std::vector<ChessBoardQuad*>& quads, int row, int dir);
#endif
int addOuterQuad(ChessBoardQuad& quad, std::vector<ChessBoardQuad*>& quads);
void removeQuadFromGroup(std::vector<ChessBoardQuad*>& quads, ChessBoardQuad& q0);
bool checkBoardMonotony(const std::vector<cv::Point2f>& corners);
};
template<typename ArrayContainer>
static void icvGetIntensityHistogram256(const Mat& img, ArrayContainer& piHist)
{
for (int i = 0; i < 256; i++)
piHist[i] = 0;
for (int j = 0; j < img.rows; ++j)
{
const uchar* row = img.ptr<uchar>(j);
for (int i = 0; i < img.cols; i++)
{
piHist[row[i]]++;
}
}
}
template<int iWidth_, typename ArrayContainer>
static void icvSmoothHistogram256(const ArrayContainer& piHist, ArrayContainer& piHistSmooth, int iWidth = 0)
{
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 iIdx = iIdx_min; iIdx <= iIdx_max; ++iIdx)
{
CV_DbgAssert(iIdx >= 0 && iIdx < 256);
iSmooth += piHist[iIdx];
}
piHistSmooth[i] = iSmooth/(2*iWidth+1);
}
}
template<typename ArrayContainer>
static void icvGradientOfHistogram256(const ArrayContainer& piHist, ArrayContainer& piHistGrad)
{
CV_DbgAssert(piHist.size() == 256);
CV_DbgAssert(piHistGrad.size() == 256);
piHistGrad[0] = 0;
int prev_grad = 0;
for (int i = 1; i < 255; ++i)
{
int grad = piHist[i-1] - piHist[i+1];
if (std::abs(grad) < 100)
{
if (prev_grad == 0)
grad = -100;
else
grad = prev_grad;
}
piHistGrad[i] = grad;
prev_grad = grad;
}
piHistGrad[255] = 0;
}
static void 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;
const int iMaxPos = 20;
cv::AutoBuffer<int, 256> piHistIntensity(iNumBins);
cv::AutoBuffer<int, 256> piHistSmooth(iNumBins);
cv::AutoBuffer<int, 256> piHistGrad(iNumBins);
cv::AutoBuffer<int> piMaxPos(iMaxPos);
icvGetIntensityHistogram256(img, piHistIntensity);
#if 0
cv::AutoBuffer<int, 256> piAccumSum(iNumBins);
piAccumSum[iNumBins-1] = piHistIntensity[iNumBins-1];
for (int i = iNumBins - 2; i >= 0; --i)
{
piAccumSum[i] = piHistIntensity[i] + piAccumSum[i+1];
}
#endif
icvSmoothHistogram256<1>(piHistIntensity, piHistSmooth);
icvGradientOfHistogram256(piHistSmooth, piHistGrad);
unsigned iCntMaxima = 0;
for (int i = iNumBins-2; (i > 2) && (iCntMaxima < iMaxPos); --i)
{
if ((piHistGrad[i-1] < 0) && (piHistGrad[i] > 0))
{
int iSumAroundMax = piHistSmooth[i-1] + piHistSmooth[i] + piHistSmooth[i+1];
if (!(iSumAroundMax < iMaxPix1 && i < 64))
{
piMaxPos[iCntMaxima++] = i;
}
}
}
DPRINTF("HIST: MAXIMA COUNT: %d (%d, %d, %d, ...)", iCntMaxima,
iCntMaxima > 0 ? piMaxPos[0] : -1,
iCntMaxima > 1 ? piMaxPos[1] : -1,
iCntMaxima > 2 ? piMaxPos[2] : -1);
int iThresh = 0;
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)
{
const int iMaxPix2 = iMaxPix / 2;
for (int sum = 0, i = 0; i < 256; ++i)
{
sum += piHistIntensity[i];
if (sum > iMaxPix2)
{
iThresh = i;
break;
}
}
}
else if (iCntMaxima == 1)
{
iThresh = piMaxPos[0]/2;
}
else if (iCntMaxima == 2)
{
iThresh = (piMaxPos[0] + piMaxPos[1])/2;
}
else
{
int iIdxAccSum = 0, iAccum = 0;
for (int i = iNumBins - 1; i > 0; --i)
{
iAccum += piHistIntensity[i];
if ( iAccum > (iMaxPix/18) )
{
iIdxAccSum = i;
break;
}
}
unsigned iIdxBGMax = 0;
int iBrightMax = piMaxPos[0];
for (unsigned n = 0; n < iCntMaxima - 1; ++n)
{
iIdxBGMax = n + 1;
if ( piMaxPos[n] < iIdxAccSum )
{
break;
}
iBrightMax = piMaxPos[n];
}
int iMaxVal = piHistIntensity[piMaxPos[iIdxBGMax]];
if (piMaxPos[iIdxBGMax] >= 250 && iIdxBGMax + 1 < iCntMaxima)
{
iIdxBGMax++;
iMaxVal = piHistIntensity[piMaxPos[iIdxBGMax]];
}
for (unsigned n = iIdxBGMax + 1; n < iCntMaxima; n++)
{
if (piHistIntensity[piMaxPos[n]] >= iMaxVal)
{
iMaxVal = piHistIntensity[piMaxPos[n]];
iIdxBGMax = n;
}
}
int iDist2 = (iBrightMax - piMaxPos[iIdxBGMax])/2;
iThresh = iBrightMax - iDist2;
DPRINTF("THRESHOLD SELECTED = %d, BRIGHTMAX = %d, DARKMAX = %d", iThresh, iBrightMax, piMaxPos[iIdxBGMax]);
}
if (iThresh > 0)
{
img = (img >= iThresh);
}
}
bool findChessboardCorners(InputArray image_, Size pattern_size,
OutputArray corners_, int flags)
{
CV_INSTRUMENT_REGION();
DPRINTF("==== findChessboardCorners(img=%dx%d, pattern=%dx%d, flags=%d)",
image_.cols(), image_.rows(), pattern_size.width, pattern_size.height, flags);
bool found = false;
const int min_dilations = 0;
const int max_dilations = 7;
int type = image_.type(), depth = CV_MAT_DEPTH(type), cn = CV_MAT_CN(type);
Mat img = image_.getMat();
CV_CheckType(type, depth == CV_8U && (cn == 1 || cn == 3 || cn == 4),
"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 (!corners_.needed())
CV_Error(Error::StsNullPtr, "Null pointer to corners");
std::vector<cv::Point2f> out_corners;
if (img.channels() != 1)
{
cvtColor(img, img, COLOR_BGR2GRAY);
}
int prev_sqr_size = 0;
Mat thresh_img_new = img.clone();
icvBinarizationHistogramBased(thresh_img_new);
SHOW("New binarization", thresh_img_new);
if (flags & CALIB_CB_FAST_CHECK)
{
if (checkChessboardBinary(thresh_img_new, pattern_size) <= 0)
{
if (checkChessboard(img, pattern_size) <= 0)
{
corners_.release();
return false;
}
}
}
ChessBoardDetector detector(pattern_size);
for (int dilations = min_dilations; dilations <= max_dilations; dilations++)
{
dilate( thresh_img_new, thresh_img_new, Mat(), Point(-1, -1), 1 );
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);
detector.reset();
#ifdef USE_CV_FINDCONTOURS
Mat binarized_img = thresh_img_new;
#else
Mat binarized_img = thresh_img_new.clone();
#endif
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;
}
}
DPRINTF("Chessboard detection result 0: %d", (int)found);
if (!found)
{
if (flags & CALIB_CB_NORMALIZE_IMAGE)
{
img = img.clone();
equalizeHist(img, img);
}
Mat thresh_img;
prev_sqr_size = 0;
DPRINTF("Fallback to old algorithm");
const bool useAdaptive = flags & CALIB_CB_ADAPTIVE_THRESH;
if (!useAdaptive)
{
double mean = cv::mean(img).val[0];
int thresh_level = std::max(cvRound(mean - 10), 10);
threshold(img, thresh_img, thresh_level, 255, THRESH_BINARY);
}
int max_k = useAdaptive ? 6 : 1;
for (int k = 0; k < max_k && !found; k++)
{
for (int dilations = min_dilations; dilations <= max_dilations; dilations++)
{
if (useAdaptive)
{
int block_size = cvRound(prev_sqr_size == 0
? std::min(img.cols, img.rows) * (k % 2 == 0 ? 0.2 : 0.1)
: prev_sqr_size * 2);
block_size = block_size | 1;
adaptiveThreshold( img, thresh_img, 255, ADAPTIVE_THRESH_MEAN_C, THRESH_BINARY, block_size, (k/2)*5 );
if (dilations > 0)
dilate( thresh_img, thresh_img, Mat(), Point(-1, -1), dilations-1 );
}
else
{
dilate( thresh_img, thresh_img, Mat(), Point(-1, -1), 1 );
}
SHOW("Old binarization", thresh_img);
rectangle( thresh_img, Point(0,0), Point(thresh_img.cols-1, thresh_img.rows-1), Scalar(255,255,255), 3, LINE_8);
detector.reset();
#ifdef USE_CV_FINDCONTOURS
Mat binarized_img = thresh_img;
#else
Mat binarized_img = (useAdaptive) ? thresh_img : thresh_img.clone();
#endif
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;
}
}
}
}
DPRINTF("Chessboard detection result 1: %d", (int)found);
if (found)
found = detector.checkBoardMonotony(out_corners);
DPRINTF("Chessboard detection result 2: %d", (int)found);
if (found)
{
const int BORDER = 8;
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;
}
}
}
DPRINTF("Chessboard detection result 3: %d", (int)found);
if (found)
{
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)
{
int n = pattern_size.width*pattern_size.height;
for(int i = 0; i < n/2; i++ )
{
std::swap(out_corners[i], out_corners[n-i-1]);
}
}
}
cv::cornerSubPix(img, out_corners, Size(2, 2), Size(-1,-1),
cv::TermCriteria(TermCriteria::EPS + TermCriteria::MAX_ITER, 15, 0.1));
}
Mat(out_corners).copyTo(corners_);
return found;
}
bool ChessBoardDetector::checkBoardMonotony(const std::vector<cv::Point2f>& corners)
{
for (int k = 0; k < 2; ++k)
{
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)
{
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)
{
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 false;
prevt = t;
}
}
}
return true;
}
int ChessBoardDetector::orderFoundConnectedQuads(std::vector<ChessBoardQuad*>& quads)
{
const int max_quad_buf_size = (int)all_quads.size();
int quad_count = (int)quads.size();
std::stack<ChessBoardQuad*> stack;
ChessBoardQuad *start = NULL;
for (int i = 0; i < quad_count; i++)
{
if (quads[i]->count == 4)
{
start = quads[i];
break;
}
}
if (start == NULL)
return 0;
int row_min = 0, col_min = 0, row_max=0, col_max = 0;
std::map<int, int> col_hist;
std::map<int, int> row_hist;
stack.push(start);
start->row = 0;
start->col = 0;
start->ordered = true;
while (!stack.empty())
{
ChessBoardQuad* q = stack.top(); stack.pop(); CV_Assert(q);
int col = q->col;
int row = q->row;
col_hist[col]++;
row_hist[row]++;
if (row > row_max) row_max = row;
if (row < row_min) row_min = row;
if (col > col_max) col_max = col;
if (col < col_min) col_min = col;
for (int i = 0; i < 4; i++)
{
ChessBoardQuad *neighbor = q->neighbors[i];
switch(i)
{
case 0:
row--; col--; break;
case 1:
col += 2; break;
case 2:
row += 2; break;
case 3:
col -= 2; break;
}
if (neighbor && neighbor->ordered == false && neighbor->count == 4)
{
DPRINTF("col: %d row: %d", col, row);
CV_Assert(q->corners[i]);
orderQuad(*neighbor, *(q->corners[i]), (i+2)&3);
neighbor->ordered = true;
neighbor->row = row;
neighbor->col = col;
stack.push(neighbor);
}
}
}
#ifdef DEBUG_CHESSBOARD
for (int i = col_min; i <= col_max; i++)
DPRINTF("HIST[%d] = %d", i, col_hist[i]);
#endif
int w = pattern_size.width - 1;
int h = pattern_size.height - 1;
int drow = row_max - row_min + 1;
int dcol = col_max - col_min + 1;
if ((w > h && dcol < drow) ||
(w < h && drow < dcol))
{
h = pattern_size.width - 1;
w = pattern_size.height - 1;
}
DPRINTF("Size: %dx%d Pattern: %dx%d", dcol, drow, w, h);
if (dcol < w || drow < h)
{
DPRINTF("Too few inner quad rows/cols");
return 0;
}
#ifdef ENABLE_TRIM_COL_ROW
if (dcol == w+1)
{
DPRINTF("Trimming cols");
if (col_hist[col_max] > col_hist[col_min])
{
DPRINTF("Trimming left col");
trimCol(quads, col_min, -1);
}
else
{
DPRINTF("Trimming right col");
trimCol(quads, col_max, +1);
}
}
if (drow == h+1)
{
DPRINTF("Trimming rows");
if (row_hist[row_max] > row_hist[row_min])
{
DPRINTF("Trimming top row");
trimRow(quads, row_min, -1);
}
else
{
DPRINTF("Trimming bottom row");
trimRow(quads, row_max, +1);
}
}
quad_count = (int)quads.size();
#endif
int found = 0;
for (int i=0; i < quad_count; ++i)
{
ChessBoardQuad& q = *quads[i];
if (q.count != 4)
continue;
{
int col = q.col;
int row = q.row;
for (int j = 0; j < 4; j++)
{
switch(j)
{
case 0:
row--; col--; break;
case 1:
col += 2; break;
case 2:
row += 2; break;
case 3:
col -= 2; break;
}
ChessBoardQuad *neighbor = q.neighbors[j];
if (neighbor && !neighbor->ordered &&
col <= col_max && col >= col_min &&
row <= row_max && row >= row_min)
{
DPRINTF("Adding inner: col: %d row: %d", col, row);
found++;
CV_Assert(q.corners[j]);
orderQuad(*neighbor, *q.corners[j], (j+2)&3);
neighbor->ordered = true;
neighbor->row = row;
neighbor->col = col;
}
}
}
}
if (found > 0)
{
DPRINTF("Found %d inner quads not connected to outer quads, repairing", found);
for (int i = 0; i < quad_count && all_quads_count < max_quad_buf_size; i++)
{
ChessBoardQuad& q = *quads[i];
if (q.count < 4 && q.ordered)
{
int added = addOuterQuad(q, quads);
quad_count += added;
}
}
if (all_quads_count >= max_quad_buf_size)
return 0;
}
if (dcol == w && drow == h)
{
DPRINTF("Inner bounds ok, check outer quads");
for (int i = quad_count - 1; i >= 0; i--)
{
ChessBoardQuad& q = *quads[i];
if (q.ordered == false)
{
bool outer = false;
for (int j=0; j<4; j++)
{
if (q.neighbors[j] && q.neighbors[j]->ordered)
outer = true;
}
if (!outer)
{
DPRINTF("Removing quad %d", i);
removeQuadFromGroup(quads, q);
}
}
}
return (int)quads.size();
}
return 0;
}
int ChessBoardDetector::addOuterQuad(ChessBoardQuad& quad, std::vector<ChessBoardQuad*>& quads)
{
int added = 0;
int max_quad_buf_size = (int)all_quads.size();
for (int i = 0; i < 4 && all_quads_count < max_quad_buf_size; i++)
{
if (!quad.neighbors[i])
{
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.push_back(&q);
quad.neighbors[i] = &q;
quad.count += 1;
q.neighbors[j] = &quad;
q.group_idx = quad.group_idx;
q.count = 1;
q.ordered = false;
q.edge_len = quad.edge_len;
const cv::Point2f pt_offset = quad.corners[i]->pt - quad.corners[j]->pt;
for (int k = 0; k < 4; k++)
{
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;
}
q.corners[j] = quad.corners[i];
int next_i = (i + 1) & 3;
int prev_i = (i + 3) & 3;
ChessBoardQuad* quad_prev = quad.neighbors[prev_i];
if (quad_prev &&
quad_prev->ordered &&
quad_prev->neighbors[i] &&
quad_prev->neighbors[i]->ordered )
{
ChessBoardQuad* qn = quad_prev->neighbors[i];
q.count = 2;
q.neighbors[prev_i] = qn;
qn->neighbors[next_i] = &q;
qn->count += 1;
q.corners[prev_i] = qn->corners[next_i];
}
}
}
return added;
}
#ifdef ENABLE_TRIM_COL_ROW
void ChessBoardDetector::trimCol(std::vector<ChessBoardQuad*>& quads, int col, int dir)
{
std::vector<ChessBoardQuad*> quads_(quads);
for (size_t i = 0; i < quads_.size(); ++i)
{
ChessBoardQuad& q = *quads_[i];
#ifdef DEBUG_CHESSBOARD
if (q.ordered)
DPRINTF("i: %d index: %d cur: %d", (int)i, col, q.col);
#endif
if (q.ordered && q.col == col)
{
if (dir == 1)
{
if (q.neighbors[1])
{
removeQuadFromGroup(quads, *q.neighbors[1]);
}
if (q.neighbors[2])
{
removeQuadFromGroup(quads, *q.neighbors[2]);
}
}
else
{
if (q.neighbors[0])
{
removeQuadFromGroup(quads, *q.neighbors[0]);
}
if (q.neighbors[3])
{
removeQuadFromGroup(quads, *q.neighbors[3]);
}
}
}
}
}
void ChessBoardDetector::trimRow(std::vector<ChessBoardQuad*>& quads, int row, int dir)
{
std::vector<ChessBoardQuad*> quads_(quads);
for (size_t i = 0; i < quads_.size(); ++i)
{
ChessBoardQuad& q = *quads_[i];
#ifdef DEBUG_CHESSBOARD
if (q.ordered)
DPRINTF("i: %d index: %d cur: %d", (int)i, row, q.row);
#endif
if (q.ordered && q.row == row)
{
if (dir == 1)
{
if (q.neighbors[2])
{
removeQuadFromGroup(quads, *q.neighbors[2]);
}
if (q.neighbors[3])
{
removeQuadFromGroup(quads, *q.neighbors[3]);
}
}
else
{
if (q.neighbors[0])
{
removeQuadFromGroup(quads, *q.neighbors[0]);
}
if (q.neighbors[1])
{
removeQuadFromGroup(quads, *q.neighbors[1]);
}
}
}
}
}
#endif
void ChessBoardDetector::removeQuadFromGroup(std::vector<ChessBoardQuad*>& quads, ChessBoardQuad& q0)
{
const int count = (int)quads.size();
int self_idx = -1;
for (int i = 0; i < count; ++i)
{
ChessBoardQuad* q = quads[i];
if (q == &q0)
self_idx = i;
for (int j = 0; j < 4; j++)
{
if (q->neighbors[j] == &q0)
{
q->neighbors[j] = NULL;
q->count--;
for (int k = 0; k < 4; ++k)
{
if (q0.neighbors[k] == q)
{
q0.neighbors[k] = 0;
q0.count--;
#ifndef _DEBUG
break;
#endif
}
}
break;
}
}
}
CV_Assert(self_idx >= 0);
if (self_idx != count-1)
quads[self_idx] = quads[count-1];
quads.resize(count - 1);
}
void ChessBoardDetector::orderQuad(ChessBoardQuad& quad, ChessBoardCorner& corner, int common)
{
CV_DbgAssert(common >= 0 && common <= 3);
int tc = 0;;
for (; tc < 4; ++tc)
if (quad.corners[tc]->pt == corner.pt)
break;
while (tc != common)
{
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[0] = tempc;
quad.neighbors[0] = tempq;
tc = (tc + 1) & 3;
}
}
int ChessBoardDetector::cleanFoundConnectedQuads(std::vector<ChessBoardQuad*>& quad_group)
{
int count = ((pattern_size.width + 1)*(pattern_size.height + 1) + 1)/2;
int quad_count = (int)quad_group.size();
if (quad_count <= count)
return quad_count;
CV_DbgAssert(quad_count > 0);
cv::AutoBuffer<cv::Point2f> centers(quad_count);
cv::Point2f center;
for (int i = 0; i < quad_count; ++i)
{
ChessBoardQuad* q = quad_group[i];
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 += ci;
}
center.x *= (1.0f / quad_count);
for (; quad_count > count; quad_count--)
{
double min_box_area = DBL_MAX;
int min_box_area_index = -1;
for (int skip = 0; skip < quad_count; ++skip)
{
cv::Point2f temp = centers[skip];
centers[skip] = center;
std::vector<Point2f> hull;
Mat points(1, quad_count, CV_32FC2, ¢ers[0]);
cv::convexHull(points, hull, true);
centers[skip] = temp;
double hull_area = contourArea(hull, true);
if (hull_area < min_box_area)
{
min_box_area = hull_area;
min_box_area_index = skip;
}
}
ChessBoardQuad *q0 = quad_group[min_box_area_index];
for (int i = 0; i < quad_count; ++i)
{
ChessBoardQuad *q = quad_group[i];
for (int j = 0; j < 4; ++j)
{
if (q->neighbors[j] == q0)
{
q->neighbors[j] = 0;
q->count--;
for (int k = 0; k < 4; ++k)
{
if (q0->neighbors[k] == q)
{
q0->neighbors[k] = 0;
q0->count--;
break;
}
}
break;
}
}
}
quad_count--;
quad_group[min_box_area_index] = quad_group[quad_count];
centers[min_box_area_index] = centers[quad_count];
}
return quad_count;
}
void ChessBoardDetector::findConnectedQuads(std::vector<ChessBoardQuad*>& out_group, int group_idx)
{
out_group.clear();
std::stack<ChessBoardQuad*> stack;
int i = 0;
for (; i < all_quads_count; i++)
{
ChessBoardQuad* q = (ChessBoardQuad*)&all_quads[i];
if (q->count <= 0 || q->group_idx >= 0) continue;
stack.push(q);
out_group.push_back(q);
q->group_idx = group_idx;
q->ordered = false;
while (!stack.empty())
{
q = stack.top(); CV_Assert(q);
stack.pop();
for (int k = 0; k < 4; k++ )
{
ChessBoardQuad *neighbor = q->neighbors[k];
if (neighbor && neighbor->count > 0 && neighbor->group_idx < 0 )
{
stack.push(neighbor);
out_group.push_back(neighbor);
neighbor->group_idx = group_idx;
neighbor->ordered = false;
}
}
}
break;
}
}
int ChessBoardDetector::checkQuadGroup(std::vector<ChessBoardQuad*>& quad_group, std::vector<ChessBoardCorner*>& out_corners)
{
const int ROW1 = 1000000;
const int ROW2 = 2000000;
const int ROW_ = 3000000;
int quad_count = (int)quad_group.size();
std::vector<ChessBoardCorner*> corners(quad_count*4);
int corner_count = 0;
int result = 0;
int width = 0, height = 0;
int hist[5] = {0,0,0,0,0};
for (int i = 0; i < quad_count; ++i)
{
ChessBoardQuad* q = quad_group[i];
for (int j = 0; j < 4; ++j)
{
if (q->neighbors[j])
{
int next_j = (j + 1) & 3;
ChessBoardCorner *a = q->corners[j], *b = q->corners[next_j];
int row_flag = q->count == 1 ? ROW1 : q->count == 2 ? ROW2 : ROW_;
if (a->row == 0)
{
corners[corner_count++] = a;
a->row = row_flag;
}
else if (a->row > row_flag)
{
a->row = row_flag;
}
if (q->neighbors[next_j])
{
if (a->count >= 4 || b->count >= 4)
goto finalize;
for (int k = 0; k < 4; ++k)
{
if (a->neighbors[k] == b)
goto finalize;
if (b->neighbors[k] == a)
goto finalize;
}
a->neighbors[a->count++] = b;
b->neighbors[b->count++] = a;
}
}
}
}
if (corner_count != pattern_size.width*pattern_size.height)
goto finalize;
{
ChessBoardCorner* first = NULL, *first2 = NULL;
for (int i = 0; i < corner_count; ++i)
{
int n = corners[i]->count;
CV_DbgAssert(0 <= n && n <= 4);
hist[n]++;
if (!first && n == 2)
{
if (corners[i]->row == ROW1)
first = corners[i];
else if (!first2 && corners[i]->row == ROW2)
first2 = corners[i];
}
}
if( !first )
first = first2;
if( !first || hist[0] != 0 || hist[1] != 0 || hist[2] != 4 ||
hist[3] != (pattern_size.width + pattern_size.height)*2 - 8 )
goto finalize;
ChessBoardCorner* cur = first;
ChessBoardCorner* right = NULL;
ChessBoardCorner* below = NULL;
out_corners.push_back(cur);
for (int k = 0; k < 4; ++k)
{
ChessBoardCorner* c = cur->neighbors[k];
if (c)
{
if (!right)
right = c;
else if (!below)
below = c;
}
}
if( !right || (right->count != 2 && right->count != 3) ||
!below || (below->count != 2 && below->count != 3) )
goto finalize;
cur->row = 0;
first = below;
for (int j = 1; ; ++j)
{
right->row = 0;
out_corners.push_back(right);
if( right->count == 2 )
break;
if( right->count != 3 || (int)out_corners.size() >= std::max(pattern_size.width,pattern_size.height) )
goto finalize;
cur = right;
for (int k = 0; k < 4; ++k)
{
ChessBoardCorner* c = cur->neighbors[k];
if (c && c->row > 0)
{
int kk = 0;
for (; kk < 4; ++kk)
{
if (c->neighbors[kk] == below)
break;
}
if (kk < 4)
below = c;
else
right = c;
}
}
}
width = (int)out_corners.size();
if (width == pattern_size.width)
height = pattern_size.height;
else if (width == pattern_size.height)
height = pattern_size.width;
else
goto finalize;
for (int i = 1; ; ++i)
{
if( !first )
break;
cur = first;
first = 0;
int j = 0;
for (; ; ++j)
{
cur->row = i;
out_corners.push_back(cur);
if (cur->count == 2 + (i < height-1) && j > 0)
break;
right = 0;
for (int k = 0; k < 4; ++k)
{
ChessBoardCorner* c = cur->neighbors[k];
if (c && c->row > i)
{
int kk = 0;
for (; kk < 4; ++kk)
{
if (c->neighbors[kk] && c->neighbors[kk]->row == i-1)
break;
}
if(kk < 4)
{
right = c;
if (j > 0)
break;
}
else if (j == 0)
first = c;
}
}
if (!right)
goto finalize;
cur = right;
}
if (j != width - 1)
goto finalize;
}
if ((int)out_corners.size() != corner_count)
goto finalize;
if (width != pattern_size.width)
{
std::swap(width, height);
std::vector<ChessBoardCorner*> 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];
}
{
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)
{
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 (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)
{
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.area())
result = -result;
}
return result;
}
void ChessBoardDetector::findQuadNeighbors()
{
const float thresh_scale = 1.f;
for (int idx = 0; idx < all_quads_count; idx++)
{
ChessBoardQuad& cur_quad = (ChessBoardQuad&)all_quads[idx];
for (int i = 0; i < 4; i++)
{
if (cur_quad.neighbors[i])
continue;
float min_dist = FLT_MAX;
int closest_corner_idx = -1;
ChessBoardQuad *closest_quad = 0;
cv::Point2f pt = cur_quad.corners[i]->pt;
for (int k = 0; k < all_quads_count; k++)
{
if (k == idx)
continue;
ChessBoardQuad& q_k = all_quads[k];
for (int j = 0; j < 4; j++)
{
if (q_k.neighbors[j])
continue;
float dist = normL2Sqr<float>(pt - q_k.corners[j]->pt);
if (dist < min_dist &&
dist <= cur_quad.edge_len*thresh_scale &&
dist <= q_k.edge_len*thresh_scale )
{
float ediff = cur_quad.edge_len - q_k.edge_len;
if (ediff > 32*cur_quad.edge_len ||
ediff > 32*q_k.edge_len)
{
DPRINTF("Incompatible edge lengths");
continue;
}
closest_corner_idx = j;
closest_quad = &q_k;
min_dist = dist;
}
}
}
if (closest_corner_idx >= 0 && min_dist < FLT_MAX)
{
CV_Assert(closest_quad);
if (cur_quad.count >= 4 || closest_quad->count >= 4)
continue;
ChessBoardCorner& closest_corner = *closest_quad->corners[closest_corner_idx];
int j = 0;
for (; j < 4; j++)
{
if (cur_quad.neighbors[j] == closest_quad)
break;
if (normL2Sqr<float>(closest_corner.pt - cur_quad.corners[j]->pt) < min_dist)
break;
}
if (j < 4)
continue;
for(j = 0; j < closest_quad->count; j++ )
{
if (closest_quad->neighbors[j] == &cur_quad)
break;
}
if (j < closest_quad->count)
continue;
for (j = 0; j < all_quads_count; j++ )
{
ChessBoardQuad* q = &const_cast<ChessBoardQuad&>(all_quads[j]);
if (j == idx || q == closest_quad)
continue;
int k = 0;
for (; k < 4; k++ )
{
if (!q->neighbors[k])
{
if (normL2Sqr<float>(closest_corner.pt - q->corners[k]->pt) < min_dist)
break;
}
}
if (k < 4)
break;
}
if (j < all_quads_count)
continue;
closest_corner.pt = (pt + closest_corner.pt) * 0.5f;
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;
}
}
}
}
void ChessBoardDetector::generateQuads(const cv::Mat& image_, int flags)
{
binarized_image = image_;
int quad_count = 0;
all_quads.deallocate();
all_corners.deallocate();
int min_size = 25;
bool filterQuads = (flags & CALIB_CB_FILTER_QUADS) != 0;
#ifdef USE_CV_FINDCONTOURS
std::vector<std::vector<Point> > contours;
std::vector<Vec4i> hierarchy;
cv::findContours(image_, contours, hierarchy, RETR_CCOMP, CHAIN_APPROX_SIMPLE);
if (contours.empty())
{
CV_LOG_DEBUG(NULL, "calib3d(chessboard): cv::findContours() returns no contours");
return;
}
std::vector<int> contour_child_counter(contours.size(), 0);
int boardIdx = -1;
std::vector<QuadCountour> contour_quads;
for (int idx = (int)(contours.size() - 1); idx >= 0; --idx)
{
int parentIdx = hierarchy[idx][3];
if (hierarchy[idx][2] != -1 || parentIdx == -1)
continue;
const std::vector<Point>& contour = contours[idx];
Rect contour_rect = boundingRect(contour);
if (contour_rect.area() < min_size)
continue;
std::vector<Point> approx_contour;
const int min_approx_level = 1, max_approx_level = MAX_CONTOUR_APPROX;
for (int approx_level = min_approx_level; approx_level <= max_approx_level; approx_level++ )
{
approxPolyDP(contour, approx_contour, (float)approx_level, true);
if (approx_contour.size() == 4)
break;
std::vector<Point> approx_contour_tmp;
std::swap(approx_contour, approx_contour_tmp);
approxPolyDP(approx_contour_tmp, approx_contour, (float)approx_level, true);
if (approx_contour.size() == 4)
break;
}
if (approx_contour.size() != 4)
continue;
if (!cv::isContourConvex(approx_contour))
continue;
cv::Point pt[4];
for (int i = 0; i < 4; ++i)
pt[i] = approx_contour[i];
CV_LOG_VERBOSE(NULL, 9, "... contours(" << contour_quads.size() << " added):" << pt[0] << " " << pt[1] << " " << pt[2] << " " << pt[3]);
if (filterQuads)
{
double p = cv::arcLength(approx_contour, true);
double area = cv::contourArea(approx_contour, false);
double d1 = sqrt(normL2Sqr<double>(pt[0] - pt[2]));
double d2 = sqrt(normL2Sqr<double>(pt[1] - pt[3]));
double d3 = sqrt(normL2Sqr<double>(pt[0] - pt[1]));
double d4 = sqrt(normL2Sqr<double>(pt[1] - pt[2]));
if (!(d3*4 > d4 && d4*4 > d3 && d3*d4 < area*1.5 && area > min_size &&
d1 >= 0.15 * p && d2 >= 0.15 * p))
continue;
}
contour_child_counter[parentIdx]++;
if (boardIdx != parentIdx && (boardIdx < 0 || contour_child_counter[boardIdx] < contour_child_counter[parentIdx]))
boardIdx = parentIdx;
contour_quads.push_back(QuadCountour(pt, parentIdx));
}
size_t total = contour_quads.size();
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);
for (size_t idx = 0; idx < total; ++idx)
{
QuadCountour& qc = contour_quads[idx];
if (filterQuads && qc.parent_contour != boardIdx)
continue;
int quad_idx = quad_count++;
ChessBoardQuad& q = all_quads[quad_idx];
q = ChessBoardQuad();
for (int i = 0; i < 4; ++i)
{
Point2f pt(qc.pt[i]);
ChessBoardCorner& corner = all_corners[quad_idx * 4 + i];
corner = ChessBoardCorner(pt);
q.corners[i] = &corner;
}
q.edge_len = FLT_MAX;
for (int i = 0; i < 4; ++i)
{
float d = normL2Sqr<float>(q.corners[i]->pt - q.corners[(i+1)&3]->pt);
q.edge_len = std::min(q.edge_len, d);
}
}
#else
CvMat image_old = cvMat(image_), *image = &image_old;
CvContourEx* board = 0;
cv::Ptr<CvMemStorage> temp_storage(cvCreateMemStorage(0));
CvSeq *root = cvCreateSeq(0, sizeof(CvSeq), sizeof(CvSeq*), temp_storage);
CvContourScanner scanner = cvStartFindContours(image, temp_storage, sizeof(CvContourEx),
CV_RETR_CCOMP, CV_CHAIN_APPROX_SIMPLE);
CvSeq* src_contour = NULL;
while ((src_contour = cvFindNextContour(scanner)) != NULL)
{
CvSeq *dst_contour = 0;
CvRect rect = ((CvContour*)src_contour)->rect;
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;
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 );
if( dst_contour->total == 4 )
break;
dst_contour = cvApproxPoly( dst_contour, sizeof(CvContour), temp_storage,
CV_POLY_APPROX_DP, (float)approx_level );
if( dst_contour->total == 4 )
break;
}
if( dst_contour->total == 4 && cvCheckContourConvexity(dst_contour) )
{
cv::Point2i pt[4];
double p = cvContourPerimeter(dst_contour);
double area = fabs(cvContourArea(dst_contour, CV_WHOLE_SEQ));
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]);
double d1 = sqrt(normL2Sqr<double>(pt[0] - pt[2]));
double d2 = sqrt(normL2Sqr<double>(pt[1] - pt[3]));
double d3 = sqrt(normL2Sqr<double>(pt[0] - pt[1]));
double d4 = sqrt(normL2Sqr<double>(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))
{
CvContourEx* parent = (CvContourEx*)(src_contour->v_prev);
parent->counter++;
if( !board || board->counter < parent->counter )
board = parent;
dst_contour->v_prev = (CvSeq*)parent;
cvSeqPush( root, &dst_contour );
}
}
}
}
cvEndFindContours( &scanner );
int total = root->total;
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);
for (int idx = 0; idx < total; ++idx)
{
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];
q = ChessBoardQuad();
CV_Assert(src_contour->total == 4);
for (int i = 0; i < 4; i++)
{
Point* onePoint = (Point*)cvGetSeqElem(src_contour, i);
CV_Assert(onePoint != NULL);
Point2f pt(*onePoint);
ChessBoardCorner& corner = all_corners[quad_idx*4 + i];
corner = ChessBoardCorner(pt);
q.corners[i] = &corner;
}
q.edge_len = FLT_MAX;
for (int i = 0; i < 4; ++i)
{
float d = normL2Sqr<float>(q.corners[i]->pt - q.corners[(i+1)&3]->pt);
q.edge_len = std::min(q.edge_len, d);
}
}
#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, "filtered quad_count=" << quad_count);
}
bool ChessBoardDetector::processQuads(std::vector<cv::Point2f>& out_corners, int &prev_sqr_size)
{
out_corners.resize(0);
if (all_quads_count <= 0)
return false;
size_t max_quad_buf_size = all_quads.size();
findQuadNeighbors();
std::vector<ChessBoardQuad*> quad_group;
std::vector<ChessBoardCorner*> corner_group; corner_group.reserve(max_quad_buf_size * 4);
for (int group_idx = 0; ; group_idx++)
{
findConnectedQuads(quad_group, group_idx);
if (quad_group.empty())
break;
int count = (int)quad_group.size();
DPRINTF("Starting ordering of inner quads (%d)", count);
count = orderFoundConnectedQuads(quad_group);
DPRINTF("Finished ordering of inner quads (%d)", count);
if (count == 0)
continue;
count = cleanFoundConnectedQuads(quad_group);
DPRINTF("Connected group: %d, count: %d", 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 = 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 sum = corner_group[i]->sumDist(ni);
sum_dist += sum;
total += ni;
}
prev_sqr_size = cvRound(sum_dist/std::max(total, 1));
if (count > 0 || (-count > (int)out_corners.size()))
{
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
&& checkBoardMonotony(out_corners))
{
return true;
}
}
}
return false;
}
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<Point2f>(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);
double scale = 1;
switch (depth)
{
case CV_8U:
scale = 1;
break;
case CV_16U:
scale = 256;
break;
case CV_32F:
scale = 1./255;
break;
}
int line_type = (type == CV_8UC1 || type == CV_8UC3) ? LINE_AA : LINE_8;
if (!patternWasFound)
{
Scalar color(0,0,255,0);
if (cn == 1)
color = Scalar::all(200);
color *= scale;
for (int i = 0; i < nelems; i++ )
{
cv::Point2i pt(
cvRound(corners_data[i].x*(1 << shift)),
cvRound(corners_data[i].y*(1 << shift))
);
line(image, Point(pt.x - r, pt.y - r), Point( pt.x + r, pt.y + r), color, 1, line_type, shift);
line(image, Point(pt.x - r, pt.y + r), Point( pt.x + r, pt.y - r), color, 1, line_type, shift);
circle(image, pt, r+(1<<shift), color, 1, line_type, shift);
}
}
else
{
const int line_max = 7;
static const int line_colors[line_max][4] =
{
{0,0,255,0},
{0,128,255,0},
{0,200,200,0},
{0,255,0,0},
{200,200,0,0},
{255,0,0,0},
{255,0,255,0}
};
cv::Point2i prev_pt;
for (int y = 0, i = 0; y < patternSize.height; y++)
{
const int* line_color = &line_colors[y % line_max][0];
Scalar color(line_color[0], line_color[1], line_color[2], line_color[3]);
if (cn == 1)
color = Scalar::all(200);
color *= scale;
for (int x = 0; x < patternSize.width; x++, i++)
{
cv::Point2i pt(
cvRound(corners_data[i].x*(1 << shift)),
cvRound(corners_data[i].y*(1 << shift))
);
if (i != 0)
line(image, prev_pt, pt, color, 1, line_type, shift);
line(image, Point(pt.x - r, pt.y - r), Point( pt.x + r, pt.y + r), color, 1, line_type, shift);
line(image, Point(pt.x - r, pt.y + r), Point( pt.x + r, pt.y - r), color, 1, line_type, shift);
circle(image, pt, r+(1<<shift), color, 1, line_type, shift);
prev_pt = pt;
}
}
}
}
static int quiet_error(int , const char* ,
const char* , const char* ,
int , void* )
{
return 0;
}
bool findCirclesGrid( InputArray _image, Size patternSize,
OutputArray _centers, int flags, const Ptr<FeatureDetector> &blobDetector,
const CirclesGridFinderParameters& parameters_)
{
CV_INSTRUMENT_REGION();
CirclesGridFinderParameters parameters = parameters_;
bool isAsymmetricGrid = (flags & CALIB_CB_ASYMMETRIC_GRID) ? true : false;
bool isSymmetricGrid = (flags & CALIB_CB_SYMMETRIC_GRID ) ? true : false;
CV_Assert(isAsymmetricGrid ^ isSymmetricGrid);
Mat image = _image.getMat();
std::vector<Point2f> centers;
std::vector<KeyPoint> keypoints;
blobDetector->detect(image, keypoints);
std::vector<Point2f> points;
for (size_t i = 0; i < keypoints.size(); i++)
{
points.push_back (keypoints[i].pt);
}
if(flags & CALIB_CB_ASYMMETRIC_GRID)
parameters.gridType = CirclesGridFinderParameters::ASYMMETRIC_GRID;
if(flags & CALIB_CB_SYMMETRIC_GRID)
parameters.gridType = CirclesGridFinderParameters::SYMMETRIC_GRID;
if(flags & CALIB_CB_CLUSTERING)
{
CirclesGridClusterFinder circlesGridClusterFinder(parameters);
circlesGridClusterFinder.findGrid(points, patternSize, centers);
Mat(centers).copyTo(_centers);
return !centers.empty();
}
const int attempts = 2;
const size_t minHomographyPoints = 4;
Mat H;
for (int i = 0; i < attempts; i++)
{
centers.clear();
CirclesGridFinder boxFinder(patternSize, points, parameters);
bool isFound = false;
#define BE_QUIET 1
#if BE_QUIET
void* oldCbkData;
ErrorCallback oldCbk = redirectError(quiet_error, 0, &oldCbkData);
#endif
CV_TRY
{
isFound = boxFinder.findHoles();
}
CV_CATCH(Exception, e)
{
CV_UNUSED(e);
}
#if BE_QUIET
redirectError(oldCbk, oldCbkData);
#endif
if (isFound)
{
switch(parameters.gridType)
{
case CirclesGridFinderParameters::SYMMETRIC_GRID:
boxFinder.getHoles(centers);
break;
case CirclesGridFinderParameters::ASYMMETRIC_GRID:
boxFinder.getAsymmetricHoles(centers);
break;
default:
CV_Error(Error::StsBadArg, "Unknown pattern type");
}
if (i != 0)
{
Mat orgPointsMat;
transform(centers, orgPointsMat, H.inv());
convertPointsFromHomogeneous(orgPointsMat, centers);
}
Mat(centers).copyTo(_centers);
return true;
}
boxFinder.getHoles(centers);
if (i != attempts - 1)
{
if (centers.size() < minHomographyPoints)
break;
H = CirclesGridFinder::rectifyGrid(boxFinder.getDetectedGridSize(), centers, points, points);
}
}
Mat(centers).copyTo(_centers);
return false;
}
bool findCirclesGrid(InputArray _image, Size patternSize,
OutputArray _centers, int flags, const Ptr<FeatureDetector> &blobDetector)
{
return cv::findCirclesGrid(_image, patternSize, _centers, flags, blobDetector, CirclesGridFinderParameters());
}
}