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