• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //M*//////////////////////////////////////////////////////////////////////////////////////
2 //
3 //  IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
4 //
5 //  By downloading, copying, installing or using the software you agree to this license.
6 //  If you do not agree to this license, do not download, install,
7 //  copy or use the software.
8 //
9 //
10 //                        Intel License Agreement
11 //                For Open Source Computer Vision Library
12 //
13 // Copyright (C) 2000, Intel Corporation, all rights reserved.
14 // Third party copyrights are property of their respective owners.
15 //
16 // Redistribution and use in source and binary forms, with or without modification,
17 // are permitted provided that the following conditions are met:
18 //
19 //   * Redistribution's of source code must retain the above copyright notice,
20 //     this list of conditions and the following disclaimer.
21 //
22 //   * Redistribution's in binary form must reproduce the above copyright notice,
23 //     this list of conditions and the following disclaimer in the documentation
24 //     and/or other materials provided with the distribution.
25 //
26 //   * The name of Intel Corporation may not be used to endorse or promote products
27 //     derived from this software without specific prior written permission.
28 //
29 // This software is provided by the copyright holders and contributors "as is" and
30 // any express or implied warranties, including, but not limited to, the implied
31 // warranties of merchantability and fitness for a particular purpose are disclaimed.
32 // In no event shall the Intel Corporation or contributors be liable for any direct,
33 // indirect, incidental, special, exemplary, or consequential damages
34 // (including, but not limited to, procurement of substitute goods or services;
35 // loss of use, data, or profits; or business interruption) however caused
36 // and on any theory of liability, whether in contract, strict liability,
37 // or tort (including negligence or otherwise) arising in any way out of
38 // the use of this software, even if advised of the possibility of such damage.
39 //
40 //M*/
41 
42 /************************************************************************************\
43     This is improved variant of chessboard corner detection algorithm that
44     uses a graph of connected quads. It is based on the code contributed
45     by Vladimir Vezhnevets and Philip Gruebele.
46     Here is the copyright notice from the original Vladimir's code:
47     ===============================================================
48 
49     The algorithms developed and implemented by Vezhnevets Vldimir
50     aka Dead Moroz (vvp@graphics.cs.msu.ru)
51     See http://graphics.cs.msu.su/en/research/calibration/opencv.html
52     for detailed information.
53 
54     Reliability additions and modifications made by Philip Gruebele.
55     <a href="mailto:pgruebele@cox.net">pgruebele@cox.net</a>
56 
57     Some further improvements for detection of partially ocluded boards at non-ideal
58     lighting conditions have been made by Alex Bovyrin and Kurt Kolonige
59 
60 \************************************************************************************/
61 
62 /************************************************************************************\
63   This version adds a new and improved variant of chessboard corner detection
64   that works better in poor lighting condition. It is based on work from
65   Oliver Schreer and Stefano Masneri. This method works faster than the previous
66   one and reverts back to the older method in case no chessboard detection is
67   possible. Overall performance improves also because now the method avoids
68   performing the same computation multiple times when not necessary.
69 
70 \************************************************************************************/
71 
72 #include "precomp.hpp"
73 #include "circlesgrid.hpp"
74 
75 #include <stack>
76 
77 //#define ENABLE_TRIM_COL_ROW
78 
79 // Requires CMake flag: DEBUG_opencv_calib3d=ON
80 //#define DEBUG_CHESSBOARD
81 #define DEBUG_CHESSBOARD_TIMEOUT 0  // 0 - wait for 'q'
82 
83 #include <opencv2/core/utils/logger.defines.hpp>
84 //#undef CV_LOG_STRIP_LEVEL
85 //#define CV_LOG_STRIP_LEVEL CV_LOG_LEVEL_VERBOSE + 1
86 #include <opencv2/core/utils/logger.hpp>
87 
88 #ifdef DEBUG_CHESSBOARD
89 #include "opencv2/highgui.hpp"
90 #include "opencv2/imgproc.hpp"
91 #define DPRINTF(...)  CV_LOG_INFO(NULL, cv::format("calib3d: " __VA_ARGS__))
92 #else
93 #define DPRINTF(...)
94 #endif
95 
96 namespace cv {
97 
98 //=====================================================================================
99 // Implementation for the enhanced calibration object detection
100 //=====================================================================================
101 
102 #define MAX_CONTOUR_APPROX  7
103 
104 #define USE_CV_FINDCONTOURS  // switch between cv::findContours() and legacy C API
105 #ifdef USE_CV_FINDCONTOURS
106 struct QuadCountour {
107     Point pt[4];
108     int parent_contour;
109 
QuadCountourcv::QuadCountour110     QuadCountour(const Point pt_[4], int parent_contour_) :
111         parent_contour(parent_contour_)
112     {
113         pt[0] = pt_[0]; pt[1] = pt_[1]; pt[2] = pt_[2]; pt[3] = pt_[3];
114     }
115 };
116 #else
117 
118 } // namespace
119 #include "opencv2/imgproc/imgproc_c.h"
120 namespace cv {
121 
122 struct CvContourEx
123 {
124     CV_CONTOUR_FIELDS()
125     int counter;
126 };
127 #endif
128 
129 
130 /** This structure stores information about the chessboard corner.*/
131 struct ChessBoardCorner
132 {
133     cv::Point2f pt;  // Coordinates of the corner
134     int row;         // Board row index
135     int count;       // Number of neighbor corners
136     struct ChessBoardCorner* neighbors[4]; // Neighbor corners
137 
ChessBoardCornercv::ChessBoardCorner138     ChessBoardCorner(const cv::Point2f& pt_ = cv::Point2f()) :
139         pt(pt_), row(0), count(0)
140     {
141         neighbors[0] = neighbors[1] = neighbors[2] = neighbors[3] = NULL;
142     }
143 
sumDistcv::ChessBoardCorner144     float sumDist(int& n_) const
145     {
146         float sum = 0;
147         int n = 0;
148         for (int i = 0; i < 4; ++i)
149         {
150             if (neighbors[i])
151             {
152                 sum += sqrt(normL2Sqr<float>(neighbors[i]->pt - pt));
153                 n++;
154             }
155         }
156         n_ = n;
157         return sum;
158     }
159 };
160 
161 
162 /** This structure stores information about the chessboard quadrangle.*/
163 struct ChessBoardQuad
164 {
165     int count;      // Number of quad neighbors
166     int group_idx;  // quad group ID
167     int row, col;   // row and column of this quad
168     bool ordered;   // true if corners/neighbors are ordered counter-clockwise
169     float edge_len; // quad edge len, in pix^2
170     // neighbors and corners are synced, i.e., neighbor 0 shares corner 0
171     ChessBoardCorner *corners[4]; // Coordinates of quad corners
172     struct ChessBoardQuad *neighbors[4]; // Pointers of quad neighbors
173 
ChessBoardQuadcv::ChessBoardQuad174     ChessBoardQuad(int group_idx_ = -1) :
175         count(0),
176         group_idx(group_idx_),
177         row(0), col(0),
178         ordered(0),
179         edge_len(0)
180     {
181         corners[0] = corners[1] = corners[2] = corners[3] = NULL;
182         neighbors[0] = neighbors[1] = neighbors[2] = neighbors[3] = NULL;
183     }
184 };
185 
186 
187 
188 #ifdef DEBUG_CHESSBOARD
SHOW(const std::string & name,Mat & img)189 static void SHOW(const std::string & name, Mat & img)
190 {
191     imshow(name, img);
192 #if DEBUG_CHESSBOARD_TIMEOUT
193     waitKey(DEBUG_CHESSBOARD_TIMEOUT);
194 #else
195     while ((uchar)waitKey(0) != 'q') {}
196 #endif
197 }
SHOW_QUADS(const std::string & name,const Mat & img_,ChessBoardQuad * quads,int quads_count)198 static void SHOW_QUADS(const std::string & name, const Mat & img_, ChessBoardQuad * quads, int quads_count)
199 {
200     Mat img = img_.clone();
201     if (img.channels() == 1)
202         cvtColor(img, img, COLOR_GRAY2BGR);
203     for (int i = 0; i < quads_count; ++i)
204     {
205         ChessBoardQuad & quad = quads[i];
206         for (int j = 0; j < 4; ++j)
207         {
208             line(img, quad.corners[j]->pt, quad.corners[(j + 1) & 3]->pt, Scalar(0, 240, 0), 1, LINE_AA);
209         }
210     }
211     imshow(name, img);
212 #if DEBUG_CHESSBOARD_TIMEOUT
213     waitKey(DEBUG_CHESSBOARD_TIMEOUT);
214 #else
215     while ((uchar)waitKey(0) != 'q') {}
216 #endif
217 }
218 #else
219 #define SHOW(...)
220 #define SHOW_QUADS(...)
221 #endif
222 
223 
224 
225 class ChessBoardDetector
226 {
227 public:
228     cv::Mat binarized_image;
229     Size pattern_size;
230 
231     cv::AutoBuffer<ChessBoardQuad> all_quads;
232     cv::AutoBuffer<ChessBoardCorner> all_corners;
233 
234     int all_quads_count;
235 
ChessBoardDetector(const Size & pattern_size_)236     ChessBoardDetector(const Size& pattern_size_) :
237         pattern_size(pattern_size_),
238         all_quads_count(0)
239     {
240     }
241 
reset()242     void reset()
243     {
244         all_quads.deallocate();
245         all_corners.deallocate();
246         all_quads_count = 0;
247     }
248 
249     void generateQuads(const cv::Mat& image_, int flags);
250 
251     bool processQuads(std::vector<cv::Point2f>& out_corners, int &prev_sqr_size);
252 
253     void findQuadNeighbors();
254 
255     void findConnectedQuads(std::vector<ChessBoardQuad*>& out_group, int group_idx);
256 
257     int checkQuadGroup(std::vector<ChessBoardQuad*>& quad_group, std::vector<ChessBoardCorner*>& out_corners);
258 
259     int cleanFoundConnectedQuads(std::vector<ChessBoardQuad*>& quad_group);
260 
261     int orderFoundConnectedQuads(std::vector<ChessBoardQuad*>& quads);
262 
263     void orderQuad(ChessBoardQuad& quad, ChessBoardCorner& corner, int common);
264 
265 #ifdef ENABLE_TRIM_COL_ROW
266     void trimCol(std::vector<ChessBoardQuad*>& quads, int col, int dir);
267     void trimRow(std::vector<ChessBoardQuad*>& quads, int row, int dir);
268 #endif
269 
270     int addOuterQuad(ChessBoardQuad& quad, std::vector<ChessBoardQuad*>& quads);
271 
272     void removeQuadFromGroup(std::vector<ChessBoardQuad*>& quads, ChessBoardQuad& q0);
273 
274     bool checkBoardMonotony(const std::vector<cv::Point2f>& corners);
275 };
276 
277 /***************************************************************************************************/
278 //COMPUTE INTENSITY HISTOGRAM OF INPUT IMAGE
279 template<typename ArrayContainer>
icvGetIntensityHistogram256(const Mat & img,ArrayContainer & piHist)280 static void icvGetIntensityHistogram256(const Mat& img, ArrayContainer& piHist)
281 {
282     for (int i = 0; i < 256; i++)
283         piHist[i] = 0;
284     // sum up all pixel in row direction and divide by number of columns
285     for (int j = 0; j < img.rows; ++j)
286     {
287         const uchar* row = img.ptr<uchar>(j);
288         for (int i = 0; i < img.cols; i++)
289         {
290             piHist[row[i]]++;
291         }
292     }
293 }
294 /***************************************************************************************************/
295 //SMOOTH HISTOGRAM USING WINDOW OF SIZE 2*iWidth+1
296 template<int iWidth_, typename ArrayContainer>
icvSmoothHistogram256(const ArrayContainer & piHist,ArrayContainer & piHistSmooth,int iWidth=0)297 static void icvSmoothHistogram256(const ArrayContainer& piHist, ArrayContainer& piHistSmooth, int iWidth = 0)
298 {
299     CV_DbgAssert(iWidth_ == 0 || (iWidth == iWidth_ || iWidth == 0));
300     iWidth = (iWidth_ != 0) ? iWidth_ : iWidth;
301     CV_Assert(iWidth > 0);
302     CV_DbgAssert(piHist.size() == 256);
303     CV_DbgAssert(piHistSmooth.size() == 256);
304     for (int i = 0; i < 256; ++i)
305     {
306         int iIdx_min = std::max(0, i - iWidth);
307         int iIdx_max = std::min(255, i + iWidth);
308         int iSmooth = 0;
309         for (int iIdx = iIdx_min; iIdx <= iIdx_max; ++iIdx)
310         {
311             CV_DbgAssert(iIdx >= 0 && iIdx < 256);
312             iSmooth += piHist[iIdx];
313         }
314         piHistSmooth[i] = iSmooth/(2*iWidth+1);
315     }
316 }
317 /***************************************************************************************************/
318 //COMPUTE FAST HISTOGRAM GRADIENT
319 template<typename ArrayContainer>
icvGradientOfHistogram256(const ArrayContainer & piHist,ArrayContainer & piHistGrad)320 static void icvGradientOfHistogram256(const ArrayContainer& piHist, ArrayContainer& piHistGrad)
321 {
322     CV_DbgAssert(piHist.size() == 256);
323     CV_DbgAssert(piHistGrad.size() == 256);
324     piHistGrad[0] = 0;
325     int prev_grad = 0;
326     for (int i = 1; i < 255; ++i)
327     {
328         int grad = piHist[i-1] - piHist[i+1];
329         if (std::abs(grad) < 100)
330         {
331             if (prev_grad == 0)
332                 grad = -100;
333             else
334                 grad = prev_grad;
335         }
336         piHistGrad[i] = grad;
337         prev_grad = grad;
338     }
339     piHistGrad[255] = 0;
340 }
341 /***************************************************************************************************/
342 //PERFORM SMART IMAGE THRESHOLDING BASED ON ANALYSIS OF INTENSTY HISTOGRAM
icvBinarizationHistogramBased(Mat & img)343 static void icvBinarizationHistogramBased(Mat & img)
344 {
345     CV_Assert(img.channels() == 1 && img.depth() == CV_8U);
346     int iCols = img.cols;
347     int iRows = img.rows;
348     int iMaxPix = iCols*iRows;
349     int iMaxPix1 = iMaxPix/100;
350     const int iNumBins = 256;
351     const int iMaxPos = 20;
352     cv::AutoBuffer<int, 256> piHistIntensity(iNumBins);
353     cv::AutoBuffer<int, 256> piHistSmooth(iNumBins);
354     cv::AutoBuffer<int, 256> piHistGrad(iNumBins);
355     cv::AutoBuffer<int> piMaxPos(iMaxPos);
356 
357     icvGetIntensityHistogram256(img, piHistIntensity);
358 
359 #if 0
360     // get accumulated sum starting from bright
361     cv::AutoBuffer<int, 256> piAccumSum(iNumBins);
362     piAccumSum[iNumBins-1] = piHistIntensity[iNumBins-1];
363     for (int i = iNumBins - 2; i >= 0; --i)
364     {
365         piAccumSum[i] = piHistIntensity[i] + piAccumSum[i+1];
366     }
367 #endif
368 
369     // first smooth the distribution
370     //const int iWidth = 1;
371     icvSmoothHistogram256<1>(piHistIntensity, piHistSmooth);
372 
373     // compute gradient
374     icvGradientOfHistogram256(piHistSmooth, piHistGrad);
375 
376     // check for zeros
377     unsigned iCntMaxima = 0;
378     for (int i = iNumBins-2; (i > 2) && (iCntMaxima < iMaxPos); --i)
379     {
380         if ((piHistGrad[i-1] < 0) && (piHistGrad[i] > 0))
381         {
382             int iSumAroundMax = piHistSmooth[i-1] + piHistSmooth[i] + piHistSmooth[i+1];
383             if (!(iSumAroundMax < iMaxPix1 && i < 64))
384             {
385                 piMaxPos[iCntMaxima++] = i;
386             }
387         }
388     }
389 
390     DPRINTF("HIST: MAXIMA COUNT: %d (%d, %d, %d, ...)", iCntMaxima,
391             iCntMaxima > 0 ? piMaxPos[0] : -1,
392             iCntMaxima > 1 ? piMaxPos[1] : -1,
393             iCntMaxima > 2 ? piMaxPos[2] : -1);
394 
395     int iThresh = 0;
396 
397     CV_Assert((size_t)iCntMaxima <= piMaxPos.size());
398 
399     DPRINTF("HIST: MAXIMA COUNT: %d (%d, %d, %d, ...)", iCntMaxima,
400                 iCntMaxima > 0 ? piMaxPos[0] : -1,
401                 iCntMaxima > 1 ? piMaxPos[1] : -1,
402                 iCntMaxima > 2 ? piMaxPos[2] : -1);
403 
404     if (iCntMaxima == 0)
405     {
406         // no any maxima inside (only 0 and 255 which are not counted above)
407         // Does image black-write already?
408         const int iMaxPix2 = iMaxPix / 2;
409         for (int sum = 0, i = 0; i < 256; ++i) // select mean intensity
410         {
411             sum += piHistIntensity[i];
412             if (sum > iMaxPix2)
413             {
414                 iThresh = i;
415                 break;
416             }
417         }
418     }
419     else if (iCntMaxima == 1)
420     {
421         iThresh = piMaxPos[0]/2;
422     }
423     else if (iCntMaxima == 2)
424     {
425         iThresh = (piMaxPos[0] + piMaxPos[1])/2;
426     }
427     else // iCntMaxima >= 3
428     {
429         // CHECKING THRESHOLD FOR WHITE
430         int iIdxAccSum = 0, iAccum = 0;
431         for (int i = iNumBins - 1; i > 0; --i)
432         {
433             iAccum += piHistIntensity[i];
434             // iMaxPix/18 is about 5,5%, minimum required number of pixels required for white part of chessboard
435             if ( iAccum > (iMaxPix/18) )
436             {
437                 iIdxAccSum = i;
438                 break;
439             }
440         }
441 
442         unsigned iIdxBGMax = 0;
443         int iBrightMax = piMaxPos[0];
444         // printf("iBrightMax = %d\n", iBrightMax);
445         for (unsigned n = 0; n < iCntMaxima - 1; ++n)
446         {
447             iIdxBGMax = n + 1;
448             if ( piMaxPos[n] < iIdxAccSum )
449             {
450                 break;
451             }
452             iBrightMax = piMaxPos[n];
453         }
454 
455         // CHECKING THRESHOLD FOR BLACK
456         int iMaxVal = piHistIntensity[piMaxPos[iIdxBGMax]];
457 
458         //IF TOO CLOSE TO 255, jump to next maximum
459         if (piMaxPos[iIdxBGMax] >= 250 && iIdxBGMax + 1 < iCntMaxima)
460         {
461             iIdxBGMax++;
462             iMaxVal = piHistIntensity[piMaxPos[iIdxBGMax]];
463         }
464 
465         for (unsigned n = iIdxBGMax + 1; n < iCntMaxima; n++)
466         {
467             if (piHistIntensity[piMaxPos[n]] >= iMaxVal)
468             {
469                 iMaxVal = piHistIntensity[piMaxPos[n]];
470                 iIdxBGMax = n;
471             }
472         }
473 
474         //SETTING THRESHOLD FOR BINARIZATION
475         int iDist2 = (iBrightMax - piMaxPos[iIdxBGMax])/2;
476         iThresh = iBrightMax - iDist2;
477         DPRINTF("THRESHOLD SELECTED = %d, BRIGHTMAX = %d, DARKMAX = %d", iThresh, iBrightMax, piMaxPos[iIdxBGMax]);
478     }
479 
480     if (iThresh > 0)
481     {
482         img = (img >= iThresh);
483     }
484 }
485 
findChessboardCorners(InputArray image_,Size pattern_size,OutputArray corners_,int flags)486 bool findChessboardCorners(InputArray image_, Size pattern_size,
487                            OutputArray corners_, int flags)
488 {
489     CV_INSTRUMENT_REGION();
490 
491     DPRINTF("==== findChessboardCorners(img=%dx%d, pattern=%dx%d, flags=%d)",
492             image_.cols(), image_.rows(), pattern_size.width, pattern_size.height, flags);
493 
494     bool found = false;
495 
496     const int min_dilations = 0;
497     const int max_dilations = 7;
498 
499     int type = image_.type(), depth = CV_MAT_DEPTH(type), cn = CV_MAT_CN(type);
500     Mat img = image_.getMat();
501 
502     CV_CheckType(type, depth == CV_8U && (cn == 1 || cn == 3 || cn == 4),
503             "Only 8-bit grayscale or color images are supported");
504 
505     if (pattern_size.width <= 2 || pattern_size.height <= 2)
506         CV_Error(Error::StsOutOfRange, "Both width and height of the pattern should have bigger than 2");
507 
508     if (!corners_.needed())
509         CV_Error(Error::StsNullPtr, "Null pointer to corners");
510 
511     std::vector<cv::Point2f> out_corners;
512 
513     if (img.channels() != 1)
514     {
515         cvtColor(img, img, COLOR_BGR2GRAY);
516     }
517 
518     int prev_sqr_size = 0;
519 
520     Mat thresh_img_new = img.clone();
521     icvBinarizationHistogramBased(thresh_img_new); // process image in-place
522     SHOW("New binarization", thresh_img_new);
523 
524     if (flags & CALIB_CB_FAST_CHECK)
525     {
526         //perform new method for checking chessboard using a binary image.
527         //image is binarised using a threshold dependent on the image histogram
528         if (checkChessboardBinary(thresh_img_new, pattern_size) <= 0) //fall back to the old method
529         {
530             if (checkChessboard(img, pattern_size) <= 0)
531             {
532                 corners_.release();
533                 return false;
534             }
535         }
536     }
537 
538     ChessBoardDetector detector(pattern_size);
539 
540     // Try our standard "1" dilation, but if the pattern is not found, iterate the whole procedure with higher dilations.
541     // This is necessary because some squares simply do not separate properly with a single dilation.  However,
542     // we want to use the minimum number of dilations possible since dilations cause the squares to become smaller,
543     // making it difficult to detect smaller squares.
544     for (int dilations = min_dilations; dilations <= max_dilations; dilations++)
545     {
546         //USE BINARY IMAGE COMPUTED USING icvBinarizationHistogramBased METHOD
547         dilate( thresh_img_new, thresh_img_new, Mat(), Point(-1, -1), 1 );
548 
549         // So we can find rectangles that go to the edge, we draw a white line around the image edge.
550         // Otherwise FindContours will miss those clipped rectangle contours.
551         // The border color will be the image mean, because otherwise we risk screwing up filters like cvSmooth()...
552         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);
553 
554         detector.reset();
555 
556 #ifdef USE_CV_FINDCONTOURS
557         Mat binarized_img = thresh_img_new;
558 #else
559         Mat binarized_img = thresh_img_new.clone(); // make clone because cvFindContours modifies the source image
560 #endif
561         detector.generateQuads(binarized_img, flags);
562         DPRINTF("Quad count: %d/%d", detector.all_quads_count, (pattern_size.width/2+1)*(pattern_size.height/2+1));
563         SHOW_QUADS("New quads", thresh_img_new, &detector.all_quads[0], detector.all_quads_count);
564         if (detector.processQuads(out_corners, prev_sqr_size))
565         {
566             found = true;
567             break;
568         }
569     }
570 
571     DPRINTF("Chessboard detection result 0: %d", (int)found);
572 
573     // revert to old, slower, method if detection failed
574     if (!found)
575     {
576         if (flags & CALIB_CB_NORMALIZE_IMAGE)
577         {
578             img = img.clone();
579             equalizeHist(img, img);
580         }
581 
582         Mat thresh_img;
583         prev_sqr_size = 0;
584 
585         DPRINTF("Fallback to old algorithm");
586         const bool useAdaptive = flags & CALIB_CB_ADAPTIVE_THRESH;
587         if (!useAdaptive)
588         {
589             // empiric threshold level
590             // thresholding performed here and not inside the cycle to save processing time
591             double mean = cv::mean(img).val[0];
592             int thresh_level = std::max(cvRound(mean - 10), 10);
593             threshold(img, thresh_img, thresh_level, 255, THRESH_BINARY);
594         }
595         //if flag CALIB_CB_ADAPTIVE_THRESH is not set it doesn't make sense to iterate over k
596         int max_k = useAdaptive ? 6 : 1;
597         for (int k = 0; k < max_k && !found; k++)
598         {
599             for (int dilations = min_dilations; dilations <= max_dilations; dilations++)
600             {
601                 // convert the input grayscale image to binary (black-n-white)
602                 if (useAdaptive)
603                 {
604                     int block_size = cvRound(prev_sqr_size == 0
605                                              ? std::min(img.cols, img.rows) * (k % 2 == 0 ? 0.2 : 0.1)
606                                              : prev_sqr_size * 2);
607                     block_size = block_size | 1;
608                     // convert to binary
609                     adaptiveThreshold( img, thresh_img, 255, ADAPTIVE_THRESH_MEAN_C, THRESH_BINARY, block_size, (k/2)*5 );
610                     if (dilations > 0)
611                         dilate( thresh_img, thresh_img, Mat(), Point(-1, -1), dilations-1 );
612 
613                 }
614                 else
615                 {
616                     dilate( thresh_img, thresh_img, Mat(), Point(-1, -1), 1 );
617                 }
618                 SHOW("Old binarization", thresh_img);
619 
620                 // So we can find rectangles that go to the edge, we draw a white line around the image edge.
621                 // Otherwise FindContours will miss those clipped rectangle contours.
622                 // The border color will be the image mean, because otherwise we risk screwing up filters like cvSmooth()...
623                 rectangle( thresh_img, Point(0,0), Point(thresh_img.cols-1, thresh_img.rows-1), Scalar(255,255,255), 3, LINE_8);
624 
625                 detector.reset();
626 
627 #ifdef USE_CV_FINDCONTOURS
628                 Mat binarized_img = thresh_img;
629 #else
630                 Mat binarized_img = (useAdaptive) ? thresh_img : thresh_img.clone(); // make clone because cvFindContours modifies the source image
631 #endif
632                 detector.generateQuads(binarized_img, flags);
633                 DPRINTF("Quad count: %d/%d", detector.all_quads_count, (pattern_size.width/2+1)*(pattern_size.height/2+1));
634                 SHOW_QUADS("Old quads", thresh_img, &detector.all_quads[0], detector.all_quads_count);
635                 if (detector.processQuads(out_corners, prev_sqr_size))
636                 {
637                     found = 1;
638                     break;
639                 }
640             }
641         }
642     }
643 
644     DPRINTF("Chessboard detection result 1: %d", (int)found);
645 
646     if (found)
647         found = detector.checkBoardMonotony(out_corners);
648 
649     DPRINTF("Chessboard detection result 2: %d", (int)found);
650 
651     // check that none of the found corners is too close to the image boundary
652     if (found)
653     {
654         const int BORDER = 8;
655         for (int k = 0; k < pattern_size.width*pattern_size.height; ++k)
656         {
657             if( out_corners[k].x <= BORDER || out_corners[k].x > img.cols - BORDER ||
658                 out_corners[k].y <= BORDER || out_corners[k].y > img.rows - BORDER )
659             {
660                 found = false;
661                 break;
662             }
663         }
664     }
665 
666     DPRINTF("Chessboard detection result 3: %d", (int)found);
667 
668     if (found)
669     {
670         if ((pattern_size.height & 1) == 0 && (pattern_size.width & 1) == 0 )
671         {
672             int last_row = (pattern_size.height-1)*pattern_size.width;
673             double dy0 = out_corners[last_row].y - out_corners[0].y;
674             if (dy0 < 0)
675             {
676                 int n = pattern_size.width*pattern_size.height;
677                 for(int i = 0; i < n/2; i++ )
678                 {
679                     std::swap(out_corners[i], out_corners[n-i-1]);
680                 }
681             }
682         }
683         cv::cornerSubPix(img, out_corners, Size(2, 2), Size(-1,-1),
684                          cv::TermCriteria(TermCriteria::EPS + TermCriteria::MAX_ITER, 15, 0.1));
685     }
686 
687     Mat(out_corners).copyTo(corners_);
688     return found;
689 }
690 
691 
692 //
693 // Checks that each board row and column is pretty much monotonous curve:
694 // It analyzes each row and each column of the chessboard as following:
695 //    for each corner c lying between end points in the same row/column it checks that
696 //    the point projection to the line segment (a,b) is lying between projections
697 //    of the neighbor corners in the same row/column.
698 //
699 // This function has been created as temporary workaround for the bug in current implementation
700 // of cvFindChessboardCornes that produces absolutely unordered sets of corners.
701 //
checkBoardMonotony(const std::vector<cv::Point2f> & corners)702 bool ChessBoardDetector::checkBoardMonotony(const std::vector<cv::Point2f>& corners)
703 {
704     for (int k = 0; k < 2; ++k)
705     {
706         int max_i = (k == 0 ? pattern_size.height : pattern_size.width);
707         int max_j = (k == 0 ? pattern_size.width: pattern_size.height) - 1;
708         for (int i = 0; i < max_i; ++i)
709         {
710             cv::Point2f a = k == 0 ? corners[i*pattern_size.width] : corners[i];
711             cv::Point2f b = k == 0 ? corners[(i+1)*pattern_size.width-1]
712                                    : corners[(pattern_size.height-1)*pattern_size.width + i];
713             float dx0 = b.x - a.x, dy0 = b.y - a.y;
714             if (fabs(dx0) + fabs(dy0) < FLT_EPSILON)
715                 return false;
716             float prevt = 0;
717             for (int j = 1; j < max_j; ++j)
718             {
719                 cv::Point2f c = k == 0 ? corners[i*pattern_size.width + j]
720                                        : corners[j*pattern_size.width + i];
721                 float t = ((c.x - a.x)*dx0 + (c.y - a.y)*dy0)/(dx0*dx0 + dy0*dy0);
722                 if (t < prevt || t > 1)
723                     return false;
724                 prevt = t;
725             }
726         }
727     }
728     return true;
729 }
730 
731 //
732 // order a group of connected quads
733 // order of corners:
734 //   0 is top left
735 //   clockwise from there
736 // note: "top left" is nominal, depends on initial ordering of starting quad
737 //   but all other quads are ordered consistently
738 //
739 // can change the number of quads in the group
740 // can add quads, so we need to have quad/corner arrays passed in
741 //
orderFoundConnectedQuads(std::vector<ChessBoardQuad * > & quads)742 int ChessBoardDetector::orderFoundConnectedQuads(std::vector<ChessBoardQuad*>& quads)
743 {
744     const int max_quad_buf_size = (int)all_quads.size();
745     int quad_count = (int)quads.size();
746 
747     std::stack<ChessBoardQuad*> stack;
748 
749     // first find an interior quad
750     ChessBoardQuad *start = NULL;
751     for (int i = 0; i < quad_count; i++)
752     {
753         if (quads[i]->count == 4)
754         {
755             start = quads[i];
756             break;
757         }
758     }
759 
760     if (start == NULL)
761         return 0;   // no 4-connected quad
762 
763     // start with first one, assign rows/cols
764     int row_min = 0, col_min = 0, row_max=0, col_max = 0;
765 
766     std::map<int, int> col_hist;
767     std::map<int, int> row_hist;
768 
769     stack.push(start);
770     start->row = 0;
771     start->col = 0;
772     start->ordered = true;
773 
774     // Recursively order the quads so that all position numbers (e.g.,
775     // 0,1,2,3) are in the at the same relative corner (e.g., lower right).
776 
777     while (!stack.empty())
778     {
779         ChessBoardQuad* q = stack.top(); stack.pop(); CV_Assert(q);
780 
781         int col = q->col;
782         int row = q->row;
783         col_hist[col]++;
784         row_hist[row]++;
785 
786         // check min/max
787         if (row > row_max) row_max = row;
788         if (row < row_min) row_min = row;
789         if (col > col_max) col_max = col;
790         if (col < col_min) col_min = col;
791 
792         for (int i = 0; i < 4; i++)
793         {
794             ChessBoardQuad *neighbor = q->neighbors[i];
795             switch(i)   // adjust col, row for this quad
796             {           // start at top left, go clockwise
797             case 0:
798                 row--; col--; break;
799             case 1:
800                 col += 2; break;
801             case 2:
802                 row += 2;   break;
803             case 3:
804                 col -= 2; break;
805             }
806 
807             // just do inside quads
808             if (neighbor && neighbor->ordered == false && neighbor->count == 4)
809             {
810                 DPRINTF("col: %d  row: %d", col, row);
811                 CV_Assert(q->corners[i]);
812                 orderQuad(*neighbor, *(q->corners[i]), (i+2)&3); // set in order
813                 neighbor->ordered = true;
814                 neighbor->row = row;
815                 neighbor->col = col;
816                 stack.push(neighbor);
817             }
818         }
819     }
820 
821 #ifdef DEBUG_CHESSBOARD
822     for (int i = col_min; i <= col_max; i++)
823         DPRINTF("HIST[%d] = %d", i, col_hist[i]);
824 #endif
825 
826     // analyze inner quad structure
827     int w = pattern_size.width - 1;
828     int h = pattern_size.height - 1;
829     int drow = row_max - row_min + 1;
830     int dcol = col_max - col_min + 1;
831 
832     // normalize pattern and found quad indices
833     if ((w > h && dcol < drow) ||
834         (w < h && drow < dcol))
835     {
836         h = pattern_size.width - 1;
837         w = pattern_size.height - 1;
838     }
839 
840     DPRINTF("Size: %dx%d  Pattern: %dx%d", dcol, drow, w, h);
841 
842     // check if there are enough inner quads
843     if (dcol < w || drow < h)   // found enough inner quads?
844     {
845         DPRINTF("Too few inner quad rows/cols");
846         return 0;   // no, return
847     }
848 #ifdef ENABLE_TRIM_COL_ROW
849     // too many columns, not very common
850     if (dcol == w+1)    // too many, trim
851     {
852         DPRINTF("Trimming cols");
853         if (col_hist[col_max] > col_hist[col_min])
854         {
855             DPRINTF("Trimming left col");
856             trimCol(quads, col_min, -1);
857         }
858         else
859         {
860             DPRINTF("Trimming right col");
861             trimCol(quads, col_max, +1);
862         }
863     }
864 
865     // too many rows, not very common
866     if (drow == h+1)    // too many, trim
867     {
868         DPRINTF("Trimming rows");
869         if (row_hist[row_max] > row_hist[row_min])
870         {
871             DPRINTF("Trimming top row");
872             trimRow(quads, row_min, -1);
873         }
874         else
875         {
876             DPRINTF("Trimming bottom row");
877             trimRow(quads, row_max, +1);
878         }
879     }
880 
881     quad_count = (int)quads.size(); // update after icvTrimCol/icvTrimRow
882 #endif
883 
884     // check edges of inner quads
885     // if there is an outer quad missing, fill it in
886     // first order all inner quads
887     int found = 0;
888     for (int i=0; i < quad_count; ++i)
889     {
890         ChessBoardQuad& q = *quads[i];
891         if (q.count != 4)
892             continue;
893 
894         {   // ok, look at neighbors
895             int col = q.col;
896             int row = q.row;
897             for (int j = 0; j < 4; j++)
898             {
899                 switch(j)   // adjust col, row for this quad
900                 {           // start at top left, go clockwise
901                 case 0:
902                     row--; col--; break;
903                 case 1:
904                     col += 2; break;
905                 case 2:
906                     row += 2;   break;
907                 case 3:
908                     col -= 2; break;
909                 }
910                 ChessBoardQuad *neighbor = q.neighbors[j];
911                 if (neighbor && !neighbor->ordered && // is it an inner quad?
912                     col <= col_max && col >= col_min &&
913                     row <= row_max && row >= row_min)
914                 {
915                     // if so, set in order
916                     DPRINTF("Adding inner: col: %d  row: %d", col, row);
917                     found++;
918                     CV_Assert(q.corners[j]);
919                     orderQuad(*neighbor, *q.corners[j], (j+2)&3);
920                     neighbor->ordered = true;
921                     neighbor->row = row;
922                     neighbor->col = col;
923                 }
924             }
925         }
926     }
927 
928     // if we have found inner quads, add corresponding outer quads,
929     //   which are missing
930     if (found > 0)
931     {
932         DPRINTF("Found %d inner quads not connected to outer quads, repairing", found);
933         for (int i = 0; i < quad_count && all_quads_count < max_quad_buf_size; i++)
934         {
935             ChessBoardQuad& q = *quads[i];
936             if (q.count < 4 && q.ordered)
937             {
938                 int added = addOuterQuad(q, quads);
939                 quad_count += added;
940             }
941         }
942 
943         if (all_quads_count >= max_quad_buf_size)
944             return 0;
945     }
946 
947 
948     // final trimming of outer quads
949     if (dcol == w && drow == h) // found correct inner quads
950     {
951         DPRINTF("Inner bounds ok, check outer quads");
952         for (int i = quad_count - 1; i >= 0; i--) // eliminate any quad not connected to an ordered quad
953         {
954             ChessBoardQuad& q = *quads[i];
955             if (q.ordered == false)
956             {
957                 bool outer = false;
958                 for (int j=0; j<4; j++) // any neighbors that are ordered?
959                 {
960                     if (q.neighbors[j] && q.neighbors[j]->ordered)
961                         outer = true;
962                 }
963                 if (!outer) // not an outer quad, eliminate
964                 {
965                     DPRINTF("Removing quad %d", i);
966                     removeQuadFromGroup(quads, q);
967                 }
968             }
969 
970         }
971         return (int)quads.size();
972     }
973 
974     return 0;
975 }
976 
977 
978 // add an outer quad
979 // looks for the neighbor of <quad> that isn't present,
980 //   tries to add it in.
981 // <quad> is ordered
addOuterQuad(ChessBoardQuad & quad,std::vector<ChessBoardQuad * > & quads)982 int ChessBoardDetector::addOuterQuad(ChessBoardQuad& quad, std::vector<ChessBoardQuad*>& quads)
983 {
984     int added = 0;
985     int max_quad_buf_size = (int)all_quads.size();
986 
987     for (int i = 0; i < 4 && all_quads_count < max_quad_buf_size; i++) // find no-neighbor corners
988     {
989         if (!quad.neighbors[i])    // ok, create and add neighbor
990         {
991             int j = (i+2)&3;
992             DPRINTF("Adding quad as neighbor 2");
993             int q_index = all_quads_count++;
994             ChessBoardQuad& q = all_quads[q_index];
995             q = ChessBoardQuad(0);
996             added++;
997             quads.push_back(&q);
998 
999             // set neighbor and group id
1000             quad.neighbors[i] = &q;
1001             quad.count += 1;
1002             q.neighbors[j] = &quad;
1003             q.group_idx = quad.group_idx;
1004             q.count = 1;   // number of neighbors
1005             q.ordered = false;
1006             q.edge_len = quad.edge_len;
1007 
1008             // make corners of new quad
1009             // same as neighbor quad, but offset
1010             const cv::Point2f pt_offset = quad.corners[i]->pt - quad.corners[j]->pt;
1011             for (int k = 0; k < 4; k++)
1012             {
1013                 ChessBoardCorner& corner = (ChessBoardCorner&)all_corners[q_index * 4 + k];
1014                 const cv::Point2f& pt = quad.corners[k]->pt;
1015                 corner = ChessBoardCorner(pt);
1016                 q.corners[k] = &corner;
1017                 corner.pt += pt_offset;
1018             }
1019             // have to set exact corner
1020             q.corners[j] = quad.corners[i];
1021 
1022             // now find other neighbor and add it, if possible
1023             int next_i = (i + 1) & 3;
1024             int prev_i = (i + 3) & 3; // equal to (j + 1) & 3
1025             ChessBoardQuad* quad_prev = quad.neighbors[prev_i];
1026             if (quad_prev &&
1027                 quad_prev->ordered &&
1028                 quad_prev->neighbors[i] &&
1029                 quad_prev->neighbors[i]->ordered )
1030             {
1031                 ChessBoardQuad* qn = quad_prev->neighbors[i];
1032                 q.count = 2;
1033                 q.neighbors[prev_i] = qn;
1034                 qn->neighbors[next_i] = &q;
1035                 qn->count += 1;
1036                 // have to set exact corner
1037                 q.corners[prev_i] = qn->corners[next_i];
1038             }
1039         }
1040     }
1041     return added;
1042 }
1043 
1044 
1045 // trimming routines
1046 #ifdef ENABLE_TRIM_COL_ROW
trimCol(std::vector<ChessBoardQuad * > & quads,int col,int dir)1047 void ChessBoardDetector::trimCol(std::vector<ChessBoardQuad*>& quads, int col, int dir)
1048 {
1049     std::vector<ChessBoardQuad*> quads_(quads);
1050     // find the right quad(s)
1051     for (size_t i = 0; i < quads_.size(); ++i)
1052     {
1053         ChessBoardQuad& q = *quads_[i];
1054 #ifdef DEBUG_CHESSBOARD
1055         if (q.ordered)
1056             DPRINTF("i: %d  index: %d  cur: %d", (int)i, col, q.col);
1057 #endif
1058         if (q.ordered && q.col == col)
1059         {
1060             if (dir == 1)
1061             {
1062                 if (q.neighbors[1])
1063                 {
1064                     removeQuadFromGroup(quads, *q.neighbors[1]);
1065                 }
1066                 if (q.neighbors[2])
1067                 {
1068                     removeQuadFromGroup(quads, *q.neighbors[2]);
1069                 }
1070             }
1071             else
1072             {
1073                 if (q.neighbors[0])
1074                 {
1075                     removeQuadFromGroup(quads, *q.neighbors[0]);
1076                 }
1077                 if (q.neighbors[3])
1078                 {
1079                     removeQuadFromGroup(quads, *q.neighbors[3]);
1080                 }
1081             }
1082         }
1083     }
1084 }
1085 
trimRow(std::vector<ChessBoardQuad * > & quads,int row,int dir)1086 void ChessBoardDetector::trimRow(std::vector<ChessBoardQuad*>& quads, int row, int dir)
1087 {
1088     std::vector<ChessBoardQuad*> quads_(quads);
1089     // find the right quad(s)
1090     for (size_t i = 0; i < quads_.size(); ++i)
1091     {
1092         ChessBoardQuad& q = *quads_[i];
1093 #ifdef DEBUG_CHESSBOARD
1094         if (q.ordered)
1095             DPRINTF("i: %d  index: %d  cur: %d", (int)i, row, q.row);
1096 #endif
1097         if (q.ordered && q.row == row)
1098         {
1099             if (dir == 1)   // remove from bottom
1100             {
1101                 if (q.neighbors[2])
1102                 {
1103                     removeQuadFromGroup(quads, *q.neighbors[2]);
1104                 }
1105                 if (q.neighbors[3])
1106                 {
1107                     removeQuadFromGroup(quads, *q.neighbors[3]);
1108                 }
1109             }
1110             else    // remove from top
1111             {
1112                 if (q.neighbors[0])
1113                 {
1114                     removeQuadFromGroup(quads, *q.neighbors[0]);
1115                 }
1116                 if (q.neighbors[1])
1117                 {
1118                     removeQuadFromGroup(quads, *q.neighbors[1]);
1119                 }
1120             }
1121 
1122         }
1123     }
1124 }
1125 #endif
1126 
1127 //
1128 // remove quad from quad group
1129 //
removeQuadFromGroup(std::vector<ChessBoardQuad * > & quads,ChessBoardQuad & q0)1130 void ChessBoardDetector::removeQuadFromGroup(std::vector<ChessBoardQuad*>& quads, ChessBoardQuad& q0)
1131 {
1132     const int count = (int)quads.size();
1133 
1134     int self_idx = -1;
1135 
1136     // remove any references to this quad as a neighbor
1137     for (int i = 0; i < count; ++i)
1138     {
1139         ChessBoardQuad* q = quads[i];
1140         if (q == &q0)
1141             self_idx = i;
1142         for (int j = 0; j < 4; j++)
1143         {
1144             if (q->neighbors[j] == &q0)
1145             {
1146                 q->neighbors[j] = NULL;
1147                 q->count--;
1148                 for (int k = 0; k < 4; ++k)
1149                 {
1150                     if (q0.neighbors[k] == q)
1151                     {
1152                         q0.neighbors[k] = 0;
1153                         q0.count--;
1154 #ifndef _DEBUG
1155                         break;
1156 #endif
1157                     }
1158                 }
1159                 break;
1160             }
1161         }
1162     }
1163     CV_Assert(self_idx >= 0); // item itself should be found
1164 
1165     // remove the quad
1166     if (self_idx != count-1)
1167         quads[self_idx] = quads[count-1];
1168     quads.resize(count - 1);
1169 }
1170 
1171 //
1172 // put quad into correct order, where <corner> has value <common>
1173 //
orderQuad(ChessBoardQuad & quad,ChessBoardCorner & corner,int common)1174 void ChessBoardDetector::orderQuad(ChessBoardQuad& quad, ChessBoardCorner& corner, int common)
1175 {
1176     CV_DbgAssert(common >= 0 && common <= 3);
1177 
1178     // find the corner
1179     int tc = 0;;
1180     for (; tc < 4; ++tc)
1181         if (quad.corners[tc]->pt == corner.pt)
1182             break;
1183 
1184     // set corner order
1185     // shift
1186     while (tc != common)
1187     {
1188         // shift by one
1189         ChessBoardCorner *tempc = quad.corners[3];
1190         ChessBoardQuad *tempq = quad.neighbors[3];
1191         for (int i = 3; i > 0; --i)
1192         {
1193             quad.corners[i] = quad.corners[i-1];
1194             quad.neighbors[i] = quad.neighbors[i-1];
1195         }
1196         quad.corners[0] = tempc;
1197         quad.neighbors[0] = tempq;
1198         tc = (tc + 1) & 3;
1199     }
1200 }
1201 
1202 
1203 // if we found too many connect quads, remove those which probably do not belong.
cleanFoundConnectedQuads(std::vector<ChessBoardQuad * > & quad_group)1204 int ChessBoardDetector::cleanFoundConnectedQuads(std::vector<ChessBoardQuad*>& quad_group)
1205 {
1206     // number of quads this pattern should contain
1207     int count = ((pattern_size.width + 1)*(pattern_size.height + 1) + 1)/2;
1208 
1209     // if we have more quadrangles than we should,
1210     // try to eliminate duplicates or ones which don't belong to the pattern rectangle...
1211     int quad_count = (int)quad_group.size();
1212     if (quad_count <= count)
1213         return quad_count;
1214     CV_DbgAssert(quad_count > 0);
1215 
1216     // create an array of quadrangle centers
1217     cv::AutoBuffer<cv::Point2f> centers(quad_count);
1218 
1219     cv::Point2f center;
1220     for (int i = 0; i < quad_count; ++i)
1221     {
1222         ChessBoardQuad* q = quad_group[i];
1223 
1224         const cv::Point2f ci = (
1225                 q->corners[0]->pt +
1226                 q->corners[1]->pt +
1227                 q->corners[2]->pt +
1228                 q->corners[3]->pt
1229             ) * 0.25f;
1230 
1231         centers[i] = ci;
1232         center += ci;
1233     }
1234     center.x *= (1.0f / quad_count);
1235 
1236     // If we still have more quadrangles than we should,
1237     // we try to eliminate bad ones based on minimizing the bounding box.
1238     // We iteratively remove the point which reduces the size of
1239     // the bounding box of the blobs the most
1240     // (since we want the rectangle to be as small as possible)
1241     // remove the quadrange that causes the biggest reduction
1242     // in pattern size until we have the correct number
1243     for (; quad_count > count; quad_count--)
1244     {
1245         double min_box_area = DBL_MAX;
1246         int min_box_area_index = -1;
1247 
1248         // For each point, calculate box area without that point
1249         for (int skip = 0; skip < quad_count; ++skip)
1250         {
1251             // get bounding rectangle
1252             cv::Point2f temp = centers[skip]; // temporarily make index 'skip' the same as
1253             centers[skip] = center;            // pattern center (so it is not counted for convex hull)
1254             std::vector<Point2f> hull;
1255             Mat points(1, quad_count, CV_32FC2, &centers[0]);
1256             cv::convexHull(points, hull, true);
1257             centers[skip] = temp;
1258             double hull_area = contourArea(hull, true);
1259 
1260             // remember smallest box area
1261             if (hull_area < min_box_area)
1262             {
1263                 min_box_area = hull_area;
1264                 min_box_area_index = skip;
1265             }
1266         }
1267 
1268         ChessBoardQuad *q0 = quad_group[min_box_area_index];
1269 
1270         // remove any references to this quad as a neighbor
1271         for (int i = 0; i < quad_count; ++i)
1272         {
1273             ChessBoardQuad *q = quad_group[i];
1274             for (int j = 0; j < 4; ++j)
1275             {
1276                 if (q->neighbors[j] == q0)
1277                 {
1278                     q->neighbors[j] = 0;
1279                     q->count--;
1280                     for (int k = 0; k < 4; ++k)
1281                     {
1282                         if (q0->neighbors[k] == q)
1283                         {
1284                             q0->neighbors[k] = 0;
1285                             q0->count--;
1286                             break;
1287                         }
1288                     }
1289                     break;
1290                 }
1291             }
1292         }
1293 
1294         // remove the quad
1295         quad_count--;
1296         quad_group[min_box_area_index] = quad_group[quad_count];
1297         centers[min_box_area_index] = centers[quad_count];
1298     }
1299 
1300     return quad_count;
1301 }
1302 
1303 
1304 
findConnectedQuads(std::vector<ChessBoardQuad * > & out_group,int group_idx)1305 void ChessBoardDetector::findConnectedQuads(std::vector<ChessBoardQuad*>& out_group, int group_idx)
1306 {
1307     out_group.clear();
1308 
1309     std::stack<ChessBoardQuad*> stack;
1310 
1311     int i = 0;
1312     for (; i < all_quads_count; i++)
1313     {
1314         ChessBoardQuad* q = (ChessBoardQuad*)&all_quads[i];
1315 
1316         // Scan the array for a first unlabeled quad
1317         if (q->count <= 0 || q->group_idx >= 0) continue;
1318 
1319         // Recursively find a group of connected quads starting from the seed all_quads[i]
1320         stack.push(q);
1321         out_group.push_back(q);
1322         q->group_idx = group_idx;
1323         q->ordered = false;
1324 
1325         while (!stack.empty())
1326         {
1327             q = stack.top(); CV_Assert(q);
1328             stack.pop();
1329             for (int k = 0; k < 4; k++ )
1330             {
1331                 ChessBoardQuad *neighbor = q->neighbors[k];
1332                 if (neighbor && neighbor->count > 0 && neighbor->group_idx < 0 )
1333                 {
1334                     stack.push(neighbor);
1335                     out_group.push_back(neighbor);
1336                     neighbor->group_idx = group_idx;
1337                     neighbor->ordered = false;
1338                 }
1339             }
1340         }
1341         break;
1342     }
1343 }
1344 
1345 
checkQuadGroup(std::vector<ChessBoardQuad * > & quad_group,std::vector<ChessBoardCorner * > & out_corners)1346 int ChessBoardDetector::checkQuadGroup(std::vector<ChessBoardQuad*>& quad_group, std::vector<ChessBoardCorner*>& out_corners)
1347 {
1348     const int ROW1 = 1000000;
1349     const int ROW2 = 2000000;
1350     const int ROW_ = 3000000;
1351 
1352     int quad_count = (int)quad_group.size();
1353 
1354     std::vector<ChessBoardCorner*> corners(quad_count*4);
1355     int corner_count = 0;
1356     int result = 0;
1357 
1358     int width = 0, height = 0;
1359     int hist[5] = {0,0,0,0,0};
1360     //ChessBoardCorner* first = 0, *first2 = 0, *right, *cur, *below, *c;
1361 
1362     // build dual graph, which vertices are internal quad corners
1363     // and two vertices are connected iff they lie on the same quad edge
1364     for (int i = 0; i < quad_count; ++i)
1365     {
1366         ChessBoardQuad* q = quad_group[i];
1367         /*CvScalar color = q->count == 0 ? cvScalar(0,255,255) :
1368                          q->count == 1 ? cvScalar(0,0,255) :
1369                          q->count == 2 ? cvScalar(0,255,0) :
1370                          q->count == 3 ? cvScalar(255,255,0) :
1371                                          cvScalar(255,0,0);*/
1372 
1373         for (int j = 0; j < 4; ++j)
1374         {
1375             //cvLine( debug_img, cvPointFrom32f(q->corners[j]->pt), cvPointFrom32f(q->corners[(j+1)&3]->pt), color, 1, CV_AA, 0 );
1376             if (q->neighbors[j])
1377             {
1378                 int next_j = (j + 1) & 3;
1379                 ChessBoardCorner *a = q->corners[j], *b = q->corners[next_j];
1380                 // mark internal corners that belong to:
1381                 //   - a quad with a single neighbor - with ROW1,
1382                 //   - a quad with two neighbors     - with ROW2
1383                 // make the rest of internal corners with ROW_
1384                 int row_flag = q->count == 1 ? ROW1 : q->count == 2 ? ROW2 : ROW_;
1385 
1386                 if (a->row == 0)
1387                 {
1388                     corners[corner_count++] = a;
1389                     a->row = row_flag;
1390                 }
1391                 else if (a->row > row_flag)
1392                 {
1393                     a->row = row_flag;
1394                 }
1395 
1396                 if (q->neighbors[next_j])
1397                 {
1398                     if (a->count >= 4 || b->count >= 4)
1399                         goto finalize;
1400                     for (int k = 0; k < 4; ++k)
1401                     {
1402                         if (a->neighbors[k] == b)
1403                             goto finalize;
1404                         if (b->neighbors[k] == a)
1405                             goto finalize;
1406                     }
1407                     a->neighbors[a->count++] = b;
1408                     b->neighbors[b->count++] = a;
1409                 }
1410             }
1411         }
1412     }
1413 
1414     if (corner_count != pattern_size.width*pattern_size.height)
1415         goto finalize;
1416 
1417 {
1418     ChessBoardCorner* first = NULL, *first2 = NULL;
1419     for (int i = 0; i < corner_count; ++i)
1420     {
1421         int n = corners[i]->count;
1422         CV_DbgAssert(0 <= n && n <= 4);
1423         hist[n]++;
1424         if (!first && n == 2)
1425         {
1426             if (corners[i]->row == ROW1)
1427                 first = corners[i];
1428             else if (!first2 && corners[i]->row == ROW2)
1429                 first2 = corners[i];
1430         }
1431     }
1432 
1433     // start with a corner that belongs to a quad with a single neighbor.
1434     // if we do not have such, start with a corner of a quad with two neighbors.
1435     if( !first )
1436         first = first2;
1437 
1438     if( !first || hist[0] != 0 || hist[1] != 0 || hist[2] != 4 ||
1439         hist[3] != (pattern_size.width + pattern_size.height)*2 - 8 )
1440         goto finalize;
1441 
1442     ChessBoardCorner* cur = first;
1443     ChessBoardCorner* right = NULL;
1444     ChessBoardCorner* below = NULL;
1445     out_corners.push_back(cur);
1446 
1447     for (int k = 0; k < 4; ++k)
1448     {
1449         ChessBoardCorner* c = cur->neighbors[k];
1450         if (c)
1451         {
1452             if (!right)
1453                 right = c;
1454             else if (!below)
1455                 below = c;
1456         }
1457     }
1458 
1459     if( !right || (right->count != 2 && right->count != 3) ||
1460         !below || (below->count != 2 && below->count != 3) )
1461         goto finalize;
1462 
1463     cur->row = 0;
1464     //cvCircle( debug_img, cvPointFrom32f(cur->pt), 3, cvScalar(0,255,0), -1, 8, 0 );
1465 
1466     first = below; // remember the first corner in the next row
1467 
1468     // find and store the first row (or column)
1469     for (int j = 1; ; ++j)
1470     {
1471         right->row = 0;
1472         out_corners.push_back(right);
1473         //cvCircle( debug_img, cvPointFrom32f(right->pt), 3, cvScalar(0,255-j*10,0), -1, 8, 0 );
1474         if( right->count == 2 )
1475             break;
1476         if( right->count != 3 || (int)out_corners.size() >= std::max(pattern_size.width,pattern_size.height) )
1477             goto finalize;
1478         cur = right;
1479         for (int k = 0; k < 4; ++k)
1480         {
1481             ChessBoardCorner* c = cur->neighbors[k];
1482             if (c && c->row > 0)
1483             {
1484                 int kk = 0;
1485                 for (; kk < 4; ++kk)
1486                 {
1487                     if (c->neighbors[kk] == below)
1488                         break;
1489                 }
1490                 if (kk < 4)
1491                     below = c;
1492                 else
1493                     right = c;
1494             }
1495         }
1496     }
1497 
1498     width = (int)out_corners.size();
1499     if (width == pattern_size.width)
1500         height = pattern_size.height;
1501     else if (width == pattern_size.height)
1502         height = pattern_size.width;
1503     else
1504         goto finalize;
1505 
1506     // find and store all the other rows
1507     for (int i = 1; ; ++i)
1508     {
1509         if( !first )
1510             break;
1511         cur = first;
1512         first = 0;
1513         int j = 0;
1514         for (; ; ++j)
1515         {
1516             cur->row = i;
1517             out_corners.push_back(cur);
1518             //cvCircle( debug_img, cvPointFrom32f(cur->pt), 3, cvScalar(0,0,255-j*10), -1, 8, 0 );
1519             if (cur->count == 2 + (i < height-1) && j > 0)
1520                 break;
1521 
1522             right = 0;
1523 
1524             // find a neighbor that has not been processed yet
1525             // and that has a neighbor from the previous row
1526             for (int k = 0; k < 4; ++k)
1527             {
1528                 ChessBoardCorner* c = cur->neighbors[k];
1529                 if (c && c->row > i)
1530                 {
1531                     int kk = 0;
1532                     for (; kk < 4; ++kk)
1533                     {
1534                         if (c->neighbors[kk] && c->neighbors[kk]->row == i-1)
1535                             break;
1536                     }
1537                     if(kk < 4)
1538                     {
1539                         right = c;
1540                         if (j > 0)
1541                             break;
1542                     }
1543                     else if (j == 0)
1544                         first = c;
1545                 }
1546             }
1547             if (!right)
1548                 goto finalize;
1549             cur = right;
1550         }
1551 
1552         if (j != width - 1)
1553             goto finalize;
1554     }
1555 
1556     if ((int)out_corners.size() != corner_count)
1557         goto finalize;
1558 
1559     // check if we need to transpose the board
1560     if (width != pattern_size.width)
1561     {
1562         std::swap(width, height);
1563 
1564         std::vector<ChessBoardCorner*> tmp(out_corners);
1565         for (int i = 0; i < height; ++i)
1566             for (int j = 0; j < width; ++j)
1567                 out_corners[i*width + j] = tmp[j*height + i];
1568     }
1569 
1570     // check if we need to revert the order in each row
1571     {
1572         cv::Point2f p0 = out_corners[0]->pt,
1573                     p1 = out_corners[pattern_size.width-1]->pt,
1574                     p2 = out_corners[pattern_size.width]->pt;
1575         if( (p1.x - p0.x)*(p2.y - p1.y) - (p1.y - p0.y)*(p2.x - p1.x) < 0 )
1576         {
1577             if (width % 2 == 0)
1578             {
1579                 for (int i = 0; i < height; ++i)
1580                     for (int j = 0; j < width/2; ++j)
1581                         std::swap(out_corners[i*width+j], out_corners[i*width+width-j-1]);
1582             }
1583             else
1584             {
1585                 for (int j = 0; j < width; ++j)
1586                     for (int i = 0; i < height/2; ++i)
1587                         std::swap(out_corners[i*width+j], out_corners[(height - i - 1)*width+j]);
1588             }
1589         }
1590     }
1591 
1592     result = corner_count;
1593 }
1594 
1595 finalize:
1596     if (result <= 0)
1597     {
1598         corner_count = std::min(corner_count, pattern_size.area());
1599         out_corners.resize(corner_count);
1600         for (int i = 0; i < corner_count; i++)
1601             out_corners[i] = corners[i];
1602 
1603         result = -corner_count;
1604 
1605         if (result == -pattern_size.area())
1606             result = -result;
1607     }
1608 
1609     return result;
1610 }
1611 
1612 
1613 
findQuadNeighbors()1614 void ChessBoardDetector::findQuadNeighbors()
1615 {
1616     const float thresh_scale = 1.f;
1617     // find quad neighbors
1618     for (int idx = 0; idx < all_quads_count; idx++)
1619     {
1620         ChessBoardQuad& cur_quad = (ChessBoardQuad&)all_quads[idx];
1621 
1622         // choose the points of the current quadrangle that are close to
1623         // some points of the other quadrangles
1624         // (it can happen for split corners (due to dilation) of the
1625         // checker board). Search only in other quadrangles!
1626 
1627         // for each corner of this quadrangle
1628         for (int i = 0; i < 4; i++)
1629         {
1630             if (cur_quad.neighbors[i])
1631                 continue;
1632 
1633             float min_dist = FLT_MAX;
1634             int closest_corner_idx = -1;
1635             ChessBoardQuad *closest_quad = 0;
1636 
1637             cv::Point2f pt = cur_quad.corners[i]->pt;
1638 
1639             // find the closest corner in all other quadrangles
1640             for (int k = 0; k < all_quads_count; k++)
1641             {
1642                 if (k == idx)
1643                     continue;
1644 
1645                 ChessBoardQuad& q_k = all_quads[k];
1646 
1647                 for (int j = 0; j < 4; j++)
1648                 {
1649                     if (q_k.neighbors[j])
1650                         continue;
1651 
1652                     float dist = normL2Sqr<float>(pt - q_k.corners[j]->pt);
1653                     if (dist < min_dist &&
1654                         dist <= cur_quad.edge_len*thresh_scale &&
1655                         dist <= q_k.edge_len*thresh_scale )
1656                     {
1657                         // check edge lengths, make sure they're compatible
1658                         // edges that are different by more than 1:4 are rejected
1659                         float ediff = cur_quad.edge_len - q_k.edge_len;
1660                         if (ediff > 32*cur_quad.edge_len ||
1661                             ediff > 32*q_k.edge_len)
1662                         {
1663                             DPRINTF("Incompatible edge lengths");
1664                             continue;
1665                         }
1666                         closest_corner_idx = j;
1667                         closest_quad = &q_k;
1668                         min_dist = dist;
1669                     }
1670                 }
1671             }
1672 
1673             // we found a matching corner point?
1674             if (closest_corner_idx >= 0 && min_dist < FLT_MAX)
1675             {
1676                 CV_Assert(closest_quad);
1677 
1678                 if (cur_quad.count >= 4 || closest_quad->count >= 4)
1679                     continue;
1680 
1681                 // If another point from our current quad is closer to the found corner
1682                 // than the current one, then we don't count this one after all.
1683                 // This is necessary to support small squares where otherwise the wrong
1684                 // corner will get matched to closest_quad;
1685                 ChessBoardCorner& closest_corner = *closest_quad->corners[closest_corner_idx];
1686 
1687                 int j = 0;
1688                 for (; j < 4; j++)
1689                 {
1690                     if (cur_quad.neighbors[j] == closest_quad)
1691                         break;
1692 
1693                     if (normL2Sqr<float>(closest_corner.pt - cur_quad.corners[j]->pt) < min_dist)
1694                         break;
1695                 }
1696                 if (j < 4)
1697                     continue;
1698 
1699                 // Check that each corner is a neighbor of different quads
1700                 for(j = 0; j < closest_quad->count; j++ )
1701                 {
1702                     if (closest_quad->neighbors[j] == &cur_quad)
1703                         break;
1704                 }
1705                 if (j < closest_quad->count)
1706                     continue;
1707 
1708                 // check whether the closest corner to closest_corner
1709                 // is different from cur_quad->corners[i]->pt
1710                 for (j = 0; j < all_quads_count; j++ )
1711                 {
1712                     ChessBoardQuad* q = &const_cast<ChessBoardQuad&>(all_quads[j]);
1713                     if (j == idx || q == closest_quad)
1714                         continue;
1715 
1716                     int k = 0;
1717                     for (; k < 4; k++ )
1718                     {
1719                         if (!q->neighbors[k])
1720                         {
1721                             if (normL2Sqr<float>(closest_corner.pt - q->corners[k]->pt) < min_dist)
1722                                 break;
1723                         }
1724                     }
1725                     if (k < 4)
1726                         break;
1727                 }
1728                 if (j < all_quads_count)
1729                     continue;
1730 
1731                 closest_corner.pt = (pt + closest_corner.pt) * 0.5f;
1732 
1733                 // We've found one more corner - remember it
1734                 cur_quad.count++;
1735                 cur_quad.neighbors[i] = closest_quad;
1736                 cur_quad.corners[i] = &closest_corner;
1737 
1738                 closest_quad->count++;
1739                 closest_quad->neighbors[closest_corner_idx] = &cur_quad;
1740             }
1741         }
1742     }
1743 }
1744 
1745 
1746 // returns corners in clockwise order
1747 // corners don't necessarily start at same position on quad (e.g.,
1748 //   top left corner)
generateQuads(const cv::Mat & image_,int flags)1749 void ChessBoardDetector::generateQuads(const cv::Mat& image_, int flags)
1750 {
1751     binarized_image = image_;  // save for debug purposes
1752 
1753     int quad_count = 0;
1754 
1755     all_quads.deallocate();
1756     all_corners.deallocate();
1757 
1758     // empiric bound for minimal allowed perimeter for squares
1759     int min_size = 25; //cvRound( image->cols * image->rows * .03 * 0.01 * 0.92 );
1760 
1761     bool filterQuads = (flags & CALIB_CB_FILTER_QUADS) != 0;
1762 #ifdef USE_CV_FINDCONTOURS // use cv::findContours
1763 
1764     std::vector<std::vector<Point> > contours;
1765     std::vector<Vec4i> hierarchy;
1766 
1767     cv::findContours(image_, contours, hierarchy, RETR_CCOMP, CHAIN_APPROX_SIMPLE);
1768 
1769     if (contours.empty())
1770     {
1771         CV_LOG_DEBUG(NULL, "calib3d(chessboard): cv::findContours() returns no contours");
1772         return;
1773     }
1774 
1775     std::vector<int> contour_child_counter(contours.size(), 0);
1776     int boardIdx = -1;
1777 
1778     std::vector<QuadCountour> contour_quads;
1779 
1780     for (int idx = (int)(contours.size() - 1); idx >= 0; --idx)
1781     {
1782         int parentIdx = hierarchy[idx][3];
1783         if (hierarchy[idx][2] != -1 || parentIdx == -1)  // holes only (no child contours and with parent)
1784             continue;
1785         const std::vector<Point>& contour = contours[idx];
1786 
1787         Rect contour_rect = boundingRect(contour);
1788         if (contour_rect.area() < min_size)
1789             continue;
1790 
1791         std::vector<Point> approx_contour;
1792 
1793         const int min_approx_level = 1, max_approx_level = MAX_CONTOUR_APPROX;
1794         for (int approx_level = min_approx_level; approx_level <= max_approx_level; approx_level++ )
1795         {
1796             approxPolyDP(contour, approx_contour, (float)approx_level, true);
1797             if (approx_contour.size() == 4)
1798                 break;
1799 
1800             // we call this again on its own output, because sometimes
1801             // approxPoly() does not simplify as much as it should.
1802             std::vector<Point> approx_contour_tmp;
1803             std::swap(approx_contour, approx_contour_tmp);
1804             approxPolyDP(approx_contour_tmp, approx_contour, (float)approx_level, true);
1805             if (approx_contour.size() == 4)
1806                 break;
1807         }
1808 
1809         // reject non-quadrangles
1810         if (approx_contour.size() != 4)
1811             continue;
1812         if (!cv::isContourConvex(approx_contour))
1813             continue;
1814 
1815         cv::Point pt[4];
1816         for (int i = 0; i < 4; ++i)
1817             pt[i] = approx_contour[i];
1818         CV_LOG_VERBOSE(NULL, 9, "... contours(" << contour_quads.size() << " added):" << pt[0] << " " << pt[1] << " " << pt[2] << " " << pt[3]);
1819 
1820         if (filterQuads)
1821         {
1822             double p = cv::arcLength(approx_contour, true);
1823             double area = cv::contourArea(approx_contour, false);
1824 
1825             double d1 = sqrt(normL2Sqr<double>(pt[0] - pt[2]));
1826             double d2 = sqrt(normL2Sqr<double>(pt[1] - pt[3]));
1827 
1828             // philipg.  Only accept those quadrangles which are more square
1829             // than rectangular and which are big enough
1830             double d3 = sqrt(normL2Sqr<double>(pt[0] - pt[1]));
1831             double d4 = sqrt(normL2Sqr<double>(pt[1] - pt[2]));
1832             if (!(d3*4 > d4 && d4*4 > d3 && d3*d4 < area*1.5 && area > min_size &&
1833                 d1 >= 0.15 * p && d2 >= 0.15 * p))
1834                 continue;
1835         }
1836 
1837         contour_child_counter[parentIdx]++;
1838         if (boardIdx != parentIdx && (boardIdx < 0 || contour_child_counter[boardIdx] < contour_child_counter[parentIdx]))
1839             boardIdx = parentIdx;
1840 
1841         contour_quads.push_back(QuadCountour(pt, parentIdx));
1842     }
1843 
1844     size_t total = contour_quads.size();
1845     size_t max_quad_buf_size = std::max((size_t)2, total * 3);
1846     all_quads.allocate(max_quad_buf_size);
1847     all_corners.allocate(max_quad_buf_size * 4);
1848 
1849     // Create array of quads structures
1850     for (size_t idx = 0; idx < total; ++idx)
1851     {
1852         QuadCountour& qc = contour_quads[idx];
1853         if (filterQuads && qc.parent_contour != boardIdx)
1854             continue;
1855 
1856         int quad_idx = quad_count++;
1857         ChessBoardQuad& q = all_quads[quad_idx];
1858 
1859         // reset group ID
1860         q = ChessBoardQuad();
1861         for (int i = 0; i < 4; ++i)
1862         {
1863             Point2f pt(qc.pt[i]);
1864             ChessBoardCorner& corner = all_corners[quad_idx * 4 + i];
1865 
1866             corner = ChessBoardCorner(pt);
1867             q.corners[i] = &corner;
1868         }
1869         q.edge_len = FLT_MAX;
1870         for (int i = 0; i < 4; ++i)
1871         {
1872             float d = normL2Sqr<float>(q.corners[i]->pt - q.corners[(i+1)&3]->pt);
1873             q.edge_len = std::min(q.edge_len, d);
1874         }
1875     }
1876 
1877 #else // use legacy API: cvStartFindContours / cvFindNextContour / cvEndFindContours
1878 
1879     CvMat image_old = cvMat(image_), *image = &image_old;
1880 
1881     CvContourEx* board = 0;
1882 
1883     // create temporary storage for contours and the sequence of pointers to found quadrangles
1884     cv::Ptr<CvMemStorage> temp_storage(cvCreateMemStorage(0));
1885     CvSeq *root = cvCreateSeq(0, sizeof(CvSeq), sizeof(CvSeq*), temp_storage);
1886 
1887     // initialize contour retrieving routine
1888     CvContourScanner scanner = cvStartFindContours(image, temp_storage, sizeof(CvContourEx),
1889                                    CV_RETR_CCOMP, CV_CHAIN_APPROX_SIMPLE);
1890 
1891     // get all the contours one by one
1892     CvSeq* src_contour = NULL;
1893     while ((src_contour = cvFindNextContour(scanner)) != NULL)
1894     {
1895         CvSeq *dst_contour = 0;
1896         CvRect rect = ((CvContour*)src_contour)->rect;
1897 
1898         // reject contours with too small perimeter
1899         if( CV_IS_SEQ_HOLE(src_contour) && rect.width*rect.height >= min_size )
1900         {
1901             const int min_approx_level = 1, max_approx_level = MAX_CONTOUR_APPROX;
1902             for (int approx_level = min_approx_level; approx_level <= max_approx_level; approx_level++ )
1903             {
1904                 dst_contour = cvApproxPoly( src_contour, sizeof(CvContour), temp_storage,
1905                                             CV_POLY_APPROX_DP, (float)approx_level );
1906                 if( dst_contour->total == 4 )
1907                     break;
1908 
1909                 // we call this again on its own output, because sometimes
1910                 // cvApproxPoly() does not simplify as much as it should.
1911                 dst_contour = cvApproxPoly( dst_contour, sizeof(CvContour), temp_storage,
1912                                             CV_POLY_APPROX_DP, (float)approx_level );
1913 
1914                 if( dst_contour->total == 4 )
1915                     break;
1916             }
1917 
1918             // reject non-quadrangles
1919             if( dst_contour->total == 4 && cvCheckContourConvexity(dst_contour) )
1920             {
1921                 cv::Point2i pt[4];
1922                 double p = cvContourPerimeter(dst_contour);
1923                 double area = fabs(cvContourArea(dst_contour, CV_WHOLE_SEQ));
1924 
1925                 for (int i = 0; i < 4; ++i)
1926                     pt[i] = *(CvPoint*)cvGetSeqElem(dst_contour, i);
1927                 CV_LOG_VERBOSE(NULL, 9, "... contours(" << root->total << " added):" << pt[0] << " " << pt[1] << " " << pt[2] << " " << pt[3]);
1928 
1929                 double d1 = sqrt(normL2Sqr<double>(pt[0] - pt[2]));
1930                 double d2 = sqrt(normL2Sqr<double>(pt[1] - pt[3]));
1931 
1932                 // philipg.  Only accept those quadrangles which are more square
1933                 // than rectangular and which are big enough
1934                 double d3 = sqrt(normL2Sqr<double>(pt[0] - pt[1]));
1935                 double d4 = sqrt(normL2Sqr<double>(pt[1] - pt[2]));
1936                 if (!filterQuads ||
1937                     (d3*4 > d4 && d4*4 > d3 && d3*d4 < area*1.5 && area > min_size &&
1938                     d1 >= 0.15 * p && d2 >= 0.15 * p))
1939                 {
1940                     CvContourEx* parent = (CvContourEx*)(src_contour->v_prev);
1941                     parent->counter++;
1942                     if( !board || board->counter < parent->counter )
1943                         board = parent;
1944                     dst_contour->v_prev = (CvSeq*)parent;
1945                     //for( i = 0; i < 4; i++ ) cvLine( debug_img, pt[i], pt[(i+1)&3], cvScalar(200,255,255), 1, CV_AA, 0 );
1946                     cvSeqPush( root, &dst_contour );
1947                 }
1948             }
1949         }
1950     }
1951 
1952     // finish contour retrieving
1953     cvEndFindContours( &scanner );
1954 
1955     // allocate quad & corner buffers
1956     int total = root->total;
1957     size_t max_quad_buf_size = std::max((size_t)2, (size_t)total * 3);
1958     all_quads.allocate(max_quad_buf_size);
1959     all_corners.allocate(max_quad_buf_size * 4);
1960 
1961     // Create array of quads structures
1962     for (int idx = 0; idx < total; ++idx)
1963     {
1964         /* CvSeq* */src_contour = *(CvSeq**)cvGetSeqElem(root, idx);
1965         if (filterQuads && src_contour->v_prev != (CvSeq*)board)
1966             continue;
1967 
1968         int quad_idx = quad_count++;
1969         ChessBoardQuad& q = all_quads[quad_idx];
1970 
1971         // reset group ID
1972         q = ChessBoardQuad();
1973         CV_Assert(src_contour->total == 4);
1974         for (int i = 0; i < 4; i++)
1975         {
1976             Point* onePoint = (Point*)cvGetSeqElem(src_contour, i);
1977             CV_Assert(onePoint != NULL);
1978             Point2f pt(*onePoint);
1979             ChessBoardCorner& corner = all_corners[quad_idx*4 + i];
1980 
1981             corner = ChessBoardCorner(pt);
1982             q.corners[i] = &corner;
1983         }
1984         q.edge_len = FLT_MAX;
1985         for (int i = 0; i < 4; ++i)
1986         {
1987             float d = normL2Sqr<float>(q.corners[i]->pt - q.corners[(i+1)&3]->pt);
1988             q.edge_len = std::min(q.edge_len, d);
1989         }
1990     }
1991 #endif
1992 
1993     all_quads_count = quad_count;
1994 
1995     CV_LOG_VERBOSE(NULL, 3, "Total quad contours: " << total);
1996     CV_LOG_VERBOSE(NULL, 3, "max_quad_buf_size=" << max_quad_buf_size);
1997     CV_LOG_VERBOSE(NULL, 3, "filtered quad_count=" << quad_count);
1998 }
1999 
processQuads(std::vector<cv::Point2f> & out_corners,int & prev_sqr_size)2000 bool ChessBoardDetector::processQuads(std::vector<cv::Point2f>& out_corners, int &prev_sqr_size)
2001 {
2002     out_corners.resize(0);
2003     if (all_quads_count <= 0)
2004         return false;
2005 
2006     size_t max_quad_buf_size = all_quads.size();
2007 
2008     // Find quad's neighbors
2009     findQuadNeighbors();
2010 
2011     // allocate extra for adding in orderFoundQuads
2012     std::vector<ChessBoardQuad*> quad_group;
2013     std::vector<ChessBoardCorner*> corner_group; corner_group.reserve(max_quad_buf_size * 4);
2014 
2015     for (int group_idx = 0; ; group_idx++)
2016     {
2017         findConnectedQuads(quad_group, group_idx);
2018         if (quad_group.empty())
2019             break;
2020 
2021         int count = (int)quad_group.size();
2022 
2023         // order the quad corners globally
2024         // maybe delete or add some
2025         DPRINTF("Starting ordering of inner quads (%d)", count);
2026         count = orderFoundConnectedQuads(quad_group);
2027         DPRINTF("Finished ordering of inner quads (%d)", count);
2028 
2029         if (count == 0)
2030             continue;       // haven't found inner quads
2031 
2032         // If count is more than it should be, this will remove those quads
2033         // which cause maximum deviation from a nice square pattern.
2034         count = cleanFoundConnectedQuads(quad_group);
2035         DPRINTF("Connected group: %d, count: %d", group_idx, count);
2036 
2037         count = checkQuadGroup(quad_group, corner_group);
2038         DPRINTF("Connected group: %d, count: %d", group_idx, count);
2039 
2040         int n = count > 0 ? pattern_size.width * pattern_size.height : -count;
2041         n = std::min(n, pattern_size.width * pattern_size.height);
2042         float sum_dist = 0;
2043         int total = 0;
2044 
2045         for(int i = 0; i < n; i++ )
2046         {
2047             int ni = 0;
2048             float sum = corner_group[i]->sumDist(ni);
2049             sum_dist += sum;
2050             total += ni;
2051         }
2052         prev_sqr_size = cvRound(sum_dist/std::max(total, 1));
2053 
2054         if (count > 0 || (-count > (int)out_corners.size()))
2055         {
2056             // copy corners to output array
2057             out_corners.reserve(n);
2058             for (int i = 0; i < n; ++i)
2059                 out_corners.push_back(corner_group[i]->pt);
2060 
2061             if (count == pattern_size.width*pattern_size.height
2062                     && checkBoardMonotony(out_corners))
2063             {
2064                 return true;
2065             }
2066         }
2067     }
2068 
2069     return false;
2070 }
2071 
2072 
2073 
drawChessboardCorners(InputOutputArray image,Size patternSize,InputArray _corners,bool patternWasFound)2074 void drawChessboardCorners( InputOutputArray image, Size patternSize,
2075                                 InputArray _corners,
2076                                 bool patternWasFound )
2077 {
2078     CV_INSTRUMENT_REGION();
2079 
2080     int type = image.type();
2081     int cn = CV_MAT_CN(type);
2082     CV_CheckType(type, cn == 1 || cn == 3 || cn == 4,
2083             "Number of channels must be 1, 3 or 4" );
2084 
2085     int depth = CV_MAT_DEPTH(type);
2086     CV_CheckType(type, depth == CV_8U || depth == CV_16U || depth == CV_32F,
2087             "Only 8-bit, 16-bit or floating-point 32-bit images are supported");
2088 
2089     if (_corners.empty())
2090         return;
2091     Mat corners = _corners.getMat();
2092     const Point2f* corners_data = corners.ptr<Point2f>(0);
2093     int nelems = corners.checkVector(2, CV_32F, true);
2094     CV_Assert(nelems >= 0);
2095 
2096     const int shift = 0;
2097     const int radius = 4;
2098     const int r = radius*(1 << shift);
2099 
2100     double scale = 1;
2101     switch (depth)
2102     {
2103     case CV_8U:
2104         scale = 1;
2105         break;
2106     case CV_16U:
2107         scale = 256;
2108         break;
2109     case CV_32F:
2110         scale = 1./255;
2111         break;
2112     }
2113 
2114     int line_type = (type == CV_8UC1 || type == CV_8UC3) ? LINE_AA : LINE_8;
2115 
2116     if (!patternWasFound)
2117     {
2118         Scalar color(0,0,255,0);
2119         if (cn == 1)
2120             color = Scalar::all(200);
2121         color *= scale;
2122 
2123         for (int i = 0; i < nelems; i++ )
2124         {
2125             cv::Point2i pt(
2126                     cvRound(corners_data[i].x*(1 << shift)),
2127                     cvRound(corners_data[i].y*(1 << shift))
2128             );
2129             line(image, Point(pt.x - r, pt.y - r), Point( pt.x + r, pt.y + r), color, 1, line_type, shift);
2130             line(image, Point(pt.x - r, pt.y + r), Point( pt.x + r, pt.y - r), color, 1, line_type, shift);
2131             circle(image, pt, r+(1<<shift), color, 1, line_type, shift);
2132         }
2133     }
2134     else
2135     {
2136         const int line_max = 7;
2137         static const int line_colors[line_max][4] =
2138         {
2139             {0,0,255,0},
2140             {0,128,255,0},
2141             {0,200,200,0},
2142             {0,255,0,0},
2143             {200,200,0,0},
2144             {255,0,0,0},
2145             {255,0,255,0}
2146         };
2147 
2148         cv::Point2i prev_pt;
2149         for (int y = 0, i = 0; y < patternSize.height; y++)
2150         {
2151             const int* line_color = &line_colors[y % line_max][0];
2152             Scalar color(line_color[0], line_color[1], line_color[2], line_color[3]);
2153             if (cn == 1)
2154                 color = Scalar::all(200);
2155             color *= scale;
2156 
2157             for (int x = 0; x < patternSize.width; x++, i++)
2158             {
2159                 cv::Point2i pt(
2160                         cvRound(corners_data[i].x*(1 << shift)),
2161                         cvRound(corners_data[i].y*(1 << shift))
2162                 );
2163 
2164                 if (i != 0)
2165                     line(image, prev_pt, pt, color, 1, line_type, shift);
2166 
2167                 line(image, Point(pt.x - r, pt.y - r), Point( pt.x + r, pt.y + r), color, 1, line_type, shift);
2168                 line(image, Point(pt.x - r, pt.y + r), Point( pt.x + r, pt.y - r), color, 1, line_type, shift);
2169                 circle(image, pt, r+(1<<shift), color, 1, line_type, shift);
2170                 prev_pt = pt;
2171             }
2172         }
2173     }
2174 }
2175 
quiet_error(int,const char *,const char *,const char *,int,void *)2176 static int quiet_error(int /*status*/, const char* /*func_name*/,
2177                        const char* /*err_msg*/, const char* /*file_name*/,
2178                        int /*line*/, void* /*userdata*/)
2179 {
2180     return 0;
2181 }
2182 
findCirclesGrid(InputArray image,Size patternSize,OutputArray centers,int flags,const Ptr<FeatureDetector> & blobDetector,CirclesGridFinderParameters parameters)2183 bool findCirclesGrid(InputArray image, Size patternSize,
2184                      OutputArray centers, int flags,
2185                      const Ptr<FeatureDetector> &blobDetector,
2186                      CirclesGridFinderParameters parameters)
2187 {
2188     CirclesGridFinderParameters2 parameters2;
2189     *((CirclesGridFinderParameters*)&parameters2) = parameters;
2190     return cv::findCirclesGrid2(image, patternSize, centers, flags, blobDetector, parameters2);
2191 }
2192 
findCirclesGrid2(InputArray _image,Size patternSize,OutputArray _centers,int flags,const Ptr<FeatureDetector> & blobDetector,CirclesGridFinderParameters2 parameters)2193 bool findCirclesGrid2(InputArray _image, Size patternSize,
2194                       OutputArray _centers, int flags, const Ptr<FeatureDetector> &blobDetector,
2195                       CirclesGridFinderParameters2 parameters)
2196 {
2197     CV_INSTRUMENT_REGION();
2198 
2199     bool isAsymmetricGrid = (flags & CALIB_CB_ASYMMETRIC_GRID) ? true : false;
2200     bool isSymmetricGrid  = (flags & CALIB_CB_SYMMETRIC_GRID ) ? true : false;
2201     CV_Assert(isAsymmetricGrid ^ isSymmetricGrid);
2202 
2203     Mat image = _image.getMat();
2204     std::vector<Point2f> centers;
2205 
2206     std::vector<KeyPoint> keypoints;
2207     blobDetector->detect(image, keypoints);
2208     std::vector<Point2f> points;
2209     for (size_t i = 0; i < keypoints.size(); i++)
2210     {
2211       points.push_back (keypoints[i].pt);
2212     }
2213 
2214     if(flags & CALIB_CB_ASYMMETRIC_GRID)
2215       parameters.gridType = CirclesGridFinderParameters::ASYMMETRIC_GRID;
2216     if(flags & CALIB_CB_SYMMETRIC_GRID)
2217       parameters.gridType = CirclesGridFinderParameters::SYMMETRIC_GRID;
2218 
2219     if(flags & CALIB_CB_CLUSTERING)
2220     {
2221       CirclesGridClusterFinder circlesGridClusterFinder(parameters);
2222       circlesGridClusterFinder.findGrid(points, patternSize, centers);
2223       Mat(centers).copyTo(_centers);
2224       return !centers.empty();
2225     }
2226 
2227     const int attempts = 2;
2228     const size_t minHomographyPoints = 4;
2229     Mat H;
2230     for (int i = 0; i < attempts; i++)
2231     {
2232       centers.clear();
2233       CirclesGridFinder boxFinder(patternSize, points, parameters);
2234       bool isFound = false;
2235 #define BE_QUIET 1
2236 #if BE_QUIET
2237       void* oldCbkData;
2238       ErrorCallback oldCbk = redirectError(quiet_error, 0, &oldCbkData); // FIXIT not thread safe
2239 #endif
2240     isFound = boxFinder.findHoles();
2241     //   try
2242     //     isFound = boxFinder.findHoles();
2243     //   }
2244     //   catch (const cv::Exception &)
2245     //   {
2246 
2247     //   }
2248 #if BE_QUIET
2249       redirectError(oldCbk, oldCbkData);
2250 #endif
2251       if (isFound)
2252       {
2253         switch(parameters.gridType)
2254         {
2255           case CirclesGridFinderParameters::SYMMETRIC_GRID:
2256             boxFinder.getHoles(centers);
2257             break;
2258           case CirclesGridFinderParameters::ASYMMETRIC_GRID:
2259         boxFinder.getAsymmetricHoles(centers);
2260         break;
2261           default:
2262             CV_Error(Error::StsBadArg, "Unknown pattern type");
2263         }
2264 
2265         if (i != 0)
2266         {
2267           Mat orgPointsMat;
2268           transform(centers, orgPointsMat, H.inv());
2269           convertPointsFromHomogeneous(orgPointsMat, centers);
2270         }
2271         Mat(centers).copyTo(_centers);
2272         return true;
2273       }
2274 
2275       boxFinder.getHoles(centers);
2276       if (i != attempts - 1)
2277       {
2278         if (centers.size() < minHomographyPoints)
2279           break;
2280         H = CirclesGridFinder::rectifyGrid(boxFinder.getDetectedGridSize(), centers, points, points);
2281       }
2282     }
2283     Mat(centers).copyTo(_centers);
2284     return false;
2285 }
2286 
findCirclesGrid(InputArray _image,Size patternSize,OutputArray _centers,int flags,const Ptr<FeatureDetector> & blobDetector)2287 bool findCirclesGrid(InputArray _image, Size patternSize,
2288                      OutputArray _centers, int flags, const Ptr<FeatureDetector> &blobDetector)
2289 {
2290     return cv::findCirclesGrid2(_image, patternSize, _centers, flags, blobDetector, CirclesGridFinderParameters2());
2291 }
2292 
2293 } // namespace
2294 /* End of file. */
2295