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, ¢ers[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*)¶meters2) = 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