Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Tetragramm
GitHub Repository: Tetragramm/opencv
Path: blob/master/modules/calib3d/src/circlesgrid.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
// License Agreement
11
// For Open Source Computer Vision Library
12
//
13
// Copyright (C) 2000-2008, Intel Corporation, all rights reserved.
14
// Copyright (C) 2009, Willow Garage Inc., all rights reserved.
15
// Third party copyrights are property of their respective owners.
16
//
17
// Redistribution and use in source and binary forms, with or without modification,
18
// are permitted provided that the following conditions are met:
19
//
20
// * Redistribution's of source code must retain the above copyright notice,
21
// this list of conditions and the following disclaimer.
22
//
23
// * Redistribution's in binary form must reproduce the above copyright notice,
24
// this list of conditions and the following disclaimer in the documentation
25
// and/or other materials provided with the distribution.
26
//
27
// * The name of the copyright holders may not be used to endorse or promote products
28
// derived from this software without specific prior written permission.
29
//
30
// This software is provided by the copyright holders and contributors "as is" and
31
// any express or implied warranties, including, but not limited to, the implied
32
// warranties of merchantability and fitness for a particular purpose are disclaimed.
33
// In no event shall the Intel Corporation or contributors be liable for any direct,
34
// indirect, incidental, special, exemplary, or consequential damages
35
// (including, but not limited to, procurement of substitute goods or services;
36
// loss of use, data, or profits; or business interruption) however caused
37
// and on any theory of liability, whether in contract, strict liability,
38
// or tort (including negligence or otherwise) arising in any way out of
39
// the use of this software, even if advised of the possibility of such damage.
40
//
41
//M*/
42
43
#include "precomp.hpp"
44
#include "circlesgrid.hpp"
45
#include <limits>
46
//#define DEBUG_CIRCLES
47
48
#ifdef DEBUG_CIRCLES
49
# include <iostream>
50
# include "opencv2/opencv_modules.hpp"
51
# ifdef HAVE_OPENCV_HIGHGUI
52
# include "opencv2/highgui.hpp"
53
# else
54
# undef DEBUG_CIRCLES
55
# endif
56
#endif
57
58
using namespace cv;
59
60
#ifdef DEBUG_CIRCLES
61
void drawPoints(const std::vector<Point2f> &points, Mat &outImage, int radius = 2, Scalar color = Scalar::all(255), int thickness = -1)
62
{
63
for(size_t i=0; i<points.size(); i++)
64
{
65
circle(outImage, points[i], radius, color, thickness);
66
}
67
}
68
#endif
69
70
void CirclesGridClusterFinder::hierarchicalClustering(const std::vector<Point2f> &points, const Size &patternSz, std::vector<Point2f> &patternPoints)
71
{
72
int j, n = (int)points.size();
73
size_t pn = static_cast<size_t>(patternSz.area());
74
75
patternPoints.clear();
76
if (pn >= points.size())
77
{
78
if (pn == points.size())
79
patternPoints = points;
80
return;
81
}
82
83
Mat dists(n, n, CV_32FC1, Scalar(0));
84
Mat distsMask(dists.size(), CV_8UC1, Scalar(0));
85
for(int i = 0; i < n; i++)
86
{
87
for(j = i+1; j < n; j++)
88
{
89
dists.at<float>(i, j) = (float)norm(points[i] - points[j]);
90
distsMask.at<uchar>(i, j) = 255;
91
//TODO: use symmetry
92
distsMask.at<uchar>(j, i) = 255;//distsMask.at<uchar>(i, j);
93
dists.at<float>(j, i) = dists.at<float>(i, j);
94
}
95
}
96
97
std::vector<std::list<size_t> > clusters(points.size());
98
for(size_t i=0; i<points.size(); i++)
99
{
100
clusters[i].push_back(i);
101
}
102
103
int patternClusterIdx = 0;
104
while(clusters[patternClusterIdx].size() < pn)
105
{
106
Point minLoc;
107
minMaxLoc(dists, 0, 0, &minLoc, 0, distsMask);
108
int minIdx = std::min(minLoc.x, minLoc.y);
109
int maxIdx = std::max(minLoc.x, minLoc.y);
110
111
distsMask.row(maxIdx).setTo(0);
112
distsMask.col(maxIdx).setTo(0);
113
Mat tmpRow = dists.row(minIdx);
114
Mat tmpCol = dists.col(minIdx);
115
cv::min(dists.row(minLoc.x), dists.row(minLoc.y), tmpRow);
116
tmpRow = tmpRow.t();
117
tmpRow.copyTo(tmpCol);
118
119
clusters[minIdx].splice(clusters[minIdx].end(), clusters[maxIdx]);
120
patternClusterIdx = minIdx;
121
}
122
123
//the largest cluster can have more than pn points -- we need to filter out such situations
124
if(clusters[patternClusterIdx].size() != static_cast<size_t>(patternSz.area()))
125
{
126
return;
127
}
128
129
patternPoints.reserve(clusters[patternClusterIdx].size());
130
for(std::list<size_t>::iterator it = clusters[patternClusterIdx].begin(); it != clusters[patternClusterIdx].end();++it)
131
{
132
patternPoints.push_back(points[*it]);
133
}
134
}
135
136
void CirclesGridClusterFinder::findGrid(const std::vector<cv::Point2f> &points, cv::Size _patternSize, std::vector<Point2f>& centers)
137
{
138
patternSize = _patternSize;
139
centers.clear();
140
if(points.empty())
141
{
142
return;
143
}
144
145
std::vector<Point2f> patternPoints;
146
hierarchicalClustering(points, patternSize, patternPoints);
147
if(patternPoints.empty())
148
{
149
return;
150
}
151
152
#ifdef DEBUG_CIRCLES
153
Mat patternPointsImage(1024, 1248, CV_8UC1, Scalar(0));
154
drawPoints(patternPoints, patternPointsImage);
155
imshow("pattern points", patternPointsImage);
156
#endif
157
158
std::vector<Point2f> hull2f;
159
convexHull(Mat(patternPoints), hull2f, false);
160
const size_t cornersCount = isAsymmetricGrid ? 6 : 4;
161
if(hull2f.size() < cornersCount)
162
return;
163
164
std::vector<Point2f> corners;
165
findCorners(hull2f, corners);
166
if(corners.size() != cornersCount)
167
return;
168
169
std::vector<Point2f> outsideCorners, sortedCorners;
170
if(isAsymmetricGrid)
171
{
172
findOutsideCorners(corners, outsideCorners);
173
const size_t outsideCornersCount = 2;
174
if(outsideCorners.size() != outsideCornersCount)
175
return;
176
}
177
getSortedCorners(hull2f, corners, outsideCorners, sortedCorners);
178
if(sortedCorners.size() != cornersCount)
179
return;
180
181
std::vector<Point2f> rectifiedPatternPoints;
182
rectifyPatternPoints(patternPoints, sortedCorners, rectifiedPatternPoints);
183
if(patternPoints.size() != rectifiedPatternPoints.size())
184
return;
185
186
parsePatternPoints(patternPoints, rectifiedPatternPoints, centers);
187
}
188
189
void CirclesGridClusterFinder::findCorners(const std::vector<cv::Point2f> &hull2f, std::vector<cv::Point2f> &corners)
190
{
191
//find angles (cosines) of vertices in convex hull
192
std::vector<float> angles;
193
for(size_t i=0; i<hull2f.size(); i++)
194
{
195
Point2f vec1 = hull2f[(i+1) % hull2f.size()] - hull2f[i % hull2f.size()];
196
Point2f vec2 = hull2f[(i-1 + static_cast<int>(hull2f.size())) % hull2f.size()] - hull2f[i % hull2f.size()];
197
float angle = (float)(vec1.ddot(vec2) / (norm(vec1) * norm(vec2)));
198
angles.push_back(angle);
199
}
200
201
//sort angles by cosine
202
//corners are the most sharp angles (6)
203
Mat anglesMat = Mat(angles);
204
Mat sortedIndices;
205
sortIdx(anglesMat, sortedIndices, SORT_EVERY_COLUMN + SORT_DESCENDING);
206
CV_Assert(sortedIndices.type() == CV_32SC1);
207
CV_Assert(sortedIndices.cols == 1);
208
const int cornersCount = isAsymmetricGrid ? 6 : 4;
209
Mat cornersIndices;
210
cv::sort(sortedIndices.rowRange(0, cornersCount), cornersIndices, SORT_EVERY_COLUMN + SORT_ASCENDING);
211
corners.clear();
212
for(int i=0; i<cornersCount; i++)
213
{
214
corners.push_back(hull2f[cornersIndices.at<int>(i, 0)]);
215
}
216
}
217
218
void CirclesGridClusterFinder::findOutsideCorners(const std::vector<cv::Point2f> &corners, std::vector<cv::Point2f> &outsideCorners)
219
{
220
CV_Assert(!corners.empty());
221
outsideCorners.clear();
222
//find two pairs of the most nearest corners
223
const size_t n = corners.size();
224
225
#ifdef DEBUG_CIRCLES
226
Mat cornersImage(1024, 1248, CV_8UC1, Scalar(0));
227
drawPoints(corners, cornersImage);
228
imshow("corners", cornersImage);
229
#endif
230
231
std::vector<Point2f> tangentVectors(n);
232
for(size_t k=0; k < n; k++)
233
{
234
Point2f diff = corners[(k + 1) % n] - corners[k];
235
tangentVectors[k] = diff * (1.0f / norm(diff));
236
}
237
238
//compute angles between all sides
239
Mat cosAngles((int)n, (int)n, CV_32FC1, 0.0f);
240
for(size_t i = 0; i < n; i++)
241
{
242
for(size_t j = i + 1; j < n; j++)
243
{
244
float val = fabs(tangentVectors[i].dot(tangentVectors[j]));
245
cosAngles.at<float>((int)i, (int)j) = val;
246
cosAngles.at<float>((int)j, (int)i) = val;
247
}
248
}
249
250
//find two parallel sides to which outside corners belong
251
Point maxLoc;
252
minMaxLoc(cosAngles, 0, 0, 0, &maxLoc);
253
const int diffBetweenFalseLines = 3;
254
if(abs(maxLoc.x - maxLoc.y) == diffBetweenFalseLines)
255
{
256
cosAngles.row(maxLoc.x).setTo(0.0f);
257
cosAngles.col(maxLoc.x).setTo(0.0f);
258
cosAngles.row(maxLoc.y).setTo(0.0f);
259
cosAngles.col(maxLoc.y).setTo(0.0f);
260
minMaxLoc(cosAngles, 0, 0, 0, &maxLoc);
261
}
262
263
#ifdef DEBUG_CIRCLES
264
Mat linesImage(1024, 1248, CV_8UC1, Scalar(0));
265
line(linesImage, corners[maxLoc.y], corners[(maxLoc.y + 1) % n], Scalar(255));
266
line(linesImage, corners[maxLoc.x], corners[(maxLoc.x + 1) % n], Scalar(255));
267
imshow("lines", linesImage);
268
#endif
269
270
int maxIdx = std::max(maxLoc.x, maxLoc.y);
271
int minIdx = std::min(maxLoc.x, maxLoc.y);
272
const int bigDiff = 4;
273
if(maxIdx - minIdx == bigDiff)
274
{
275
minIdx += (int)n;
276
std::swap(maxIdx, minIdx);
277
}
278
if(maxIdx - minIdx != (int)n - bigDiff)
279
{
280
return;
281
}
282
283
int outsidersSegmentIdx = (minIdx + maxIdx) / 2;
284
285
outsideCorners.push_back(corners[outsidersSegmentIdx % n]);
286
outsideCorners.push_back(corners[(outsidersSegmentIdx + 1) % n]);
287
288
#ifdef DEBUG_CIRCLES
289
drawPoints(outsideCorners, cornersImage, 2, Scalar(128));
290
imshow("corners", cornersImage);
291
#endif
292
}
293
294
void CirclesGridClusterFinder::getSortedCorners(const std::vector<cv::Point2f> &hull2f, const std::vector<cv::Point2f> &corners, const std::vector<cv::Point2f> &outsideCorners, std::vector<cv::Point2f> &sortedCorners)
295
{
296
Point2f firstCorner;
297
if(isAsymmetricGrid)
298
{
299
Point2f center = std::accumulate(corners.begin(), corners.end(), Point2f(0.0f, 0.0f));
300
center *= 1.0 / corners.size();
301
302
std::vector<Point2f> centerToCorners;
303
for(size_t i=0; i<outsideCorners.size(); i++)
304
{
305
centerToCorners.push_back(outsideCorners[i] - center);
306
}
307
308
//TODO: use CirclesGridFinder::getDirection
309
float crossProduct = centerToCorners[0].x * centerToCorners[1].y - centerToCorners[0].y * centerToCorners[1].x;
310
//y axis is inverted in computer vision so we check > 0
311
bool isClockwise = crossProduct > 0;
312
firstCorner = isClockwise ? outsideCorners[1] : outsideCorners[0];
313
}
314
else
315
{
316
firstCorner = corners[0];
317
}
318
319
std::vector<Point2f>::const_iterator firstCornerIterator = std::find(hull2f.begin(), hull2f.end(), firstCorner);
320
sortedCorners.clear();
321
for(std::vector<Point2f>::const_iterator it = firstCornerIterator; it != hull2f.end();++it)
322
{
323
std::vector<Point2f>::const_iterator itCorners = std::find(corners.begin(), corners.end(), *it);
324
if(itCorners != corners.end())
325
{
326
sortedCorners.push_back(*it);
327
}
328
}
329
for(std::vector<Point2f>::const_iterator it = hull2f.begin(); it != firstCornerIterator;++it)
330
{
331
std::vector<Point2f>::const_iterator itCorners = std::find(corners.begin(), corners.end(), *it);
332
if(itCorners != corners.end())
333
{
334
sortedCorners.push_back(*it);
335
}
336
}
337
338
if(!isAsymmetricGrid)
339
{
340
double dist1 = norm(sortedCorners[0] - sortedCorners[1]);
341
double dist2 = norm(sortedCorners[1] - sortedCorners[2]);
342
343
if((dist1 > dist2 && patternSize.height > patternSize.width) || (dist1 < dist2 && patternSize.height < patternSize.width))
344
{
345
for(size_t i=0; i<sortedCorners.size()-1; i++)
346
{
347
sortedCorners[i] = sortedCorners[i+1];
348
}
349
sortedCorners[sortedCorners.size() - 1] = firstCorner;
350
}
351
}
352
}
353
354
void CirclesGridClusterFinder::rectifyPatternPoints(const std::vector<cv::Point2f> &patternPoints, const std::vector<cv::Point2f> &sortedCorners, std::vector<cv::Point2f> &rectifiedPatternPoints)
355
{
356
//indices of corner points in pattern
357
std::vector<Point> trueIndices;
358
trueIndices.push_back(Point(0, 0));
359
trueIndices.push_back(Point(patternSize.width - 1, 0));
360
if(isAsymmetricGrid)
361
{
362
trueIndices.push_back(Point(patternSize.width - 1, 1));
363
trueIndices.push_back(Point(patternSize.width - 1, patternSize.height - 2));
364
}
365
trueIndices.push_back(Point(patternSize.width - 1, patternSize.height - 1));
366
trueIndices.push_back(Point(0, patternSize.height - 1));
367
368
std::vector<Point2f> idealPoints;
369
for(size_t idx=0; idx<trueIndices.size(); idx++)
370
{
371
int i = trueIndices[idx].y;
372
int j = trueIndices[idx].x;
373
if(isAsymmetricGrid)
374
{
375
idealPoints.push_back(Point2f((2*j + i % 2)*squareSize, i*squareSize));
376
}
377
else
378
{
379
idealPoints.push_back(Point2f(j*squareSize, i*squareSize));
380
}
381
}
382
383
Mat homography = findHomography(Mat(sortedCorners), Mat(idealPoints), 0);
384
Mat rectifiedPointsMat;
385
transform(patternPoints, rectifiedPointsMat, homography);
386
rectifiedPatternPoints.clear();
387
convertPointsFromHomogeneous(rectifiedPointsMat, rectifiedPatternPoints);
388
}
389
390
void CirclesGridClusterFinder::parsePatternPoints(const std::vector<cv::Point2f> &patternPoints, const std::vector<cv::Point2f> &rectifiedPatternPoints, std::vector<cv::Point2f> &centers)
391
{
392
#ifndef HAVE_OPENCV_FLANN
393
CV_UNUSED(patternPoints);
394
CV_UNUSED(rectifiedPatternPoints);
395
CV_UNUSED(centers);
396
CV_Error(Error::StsNotImplemented, "The desired functionality requires flann module, which was disabled.");
397
#else
398
flann::LinearIndexParams flannIndexParams;
399
flann::Index flannIndex(Mat(rectifiedPatternPoints).reshape(1), flannIndexParams);
400
401
centers.clear();
402
for( int i = 0; i < patternSize.height; i++ )
403
{
404
for( int j = 0; j < patternSize.width; j++ )
405
{
406
Point2f idealPt;
407
if(isAsymmetricGrid)
408
idealPt = Point2f((2*j + i % 2)*squareSize, i*squareSize);
409
else
410
idealPt = Point2f(j*squareSize, i*squareSize);
411
412
Mat query(1, 2, CV_32F, &idealPt);
413
const int knn = 1;
414
int indicesbuf[knn] = {0};
415
float distsbuf[knn] = {0.f};
416
Mat indices(1, knn, CV_32S, &indicesbuf);
417
Mat dists(1, knn, CV_32F, &distsbuf);
418
flannIndex.knnSearch(query, indices, dists, knn, flann::SearchParams());
419
centers.push_back(patternPoints.at(indicesbuf[0]));
420
421
if(distsbuf[0] > maxRectifiedDistance)
422
{
423
#ifdef DEBUG_CIRCLES
424
std::cout << "Pattern not detected: too large rectified distance" << std::endl;
425
#endif
426
centers.clear();
427
return;
428
}
429
}
430
}
431
#endif
432
}
433
434
Graph::Graph(size_t n)
435
{
436
for (size_t i = 0; i < n; i++)
437
{
438
addVertex(i);
439
}
440
}
441
442
bool Graph::doesVertexExist(size_t id) const
443
{
444
return (vertices.find(id) != vertices.end());
445
}
446
447
void Graph::addVertex(size_t id)
448
{
449
CV_Assert( !doesVertexExist( id ) );
450
451
vertices.insert(std::pair<size_t, Vertex> (id, Vertex()));
452
}
453
454
void Graph::addEdge(size_t id1, size_t id2)
455
{
456
CV_Assert( doesVertexExist( id1 ) );
457
CV_Assert( doesVertexExist( id2 ) );
458
459
vertices[id1].neighbors.insert(id2);
460
vertices[id2].neighbors.insert(id1);
461
}
462
463
void Graph::removeEdge(size_t id1, size_t id2)
464
{
465
CV_Assert( doesVertexExist( id1 ) );
466
CV_Assert( doesVertexExist( id2 ) );
467
468
vertices[id1].neighbors.erase(id2);
469
vertices[id2].neighbors.erase(id1);
470
}
471
472
bool Graph::areVerticesAdjacent(size_t id1, size_t id2) const
473
{
474
Vertices::const_iterator it = vertices.find(id1);
475
CV_Assert(it != vertices.end());
476
const Neighbors & neighbors = it->second.neighbors;
477
return neighbors.find(id2) != neighbors.end();
478
}
479
480
size_t Graph::getVerticesCount() const
481
{
482
return vertices.size();
483
}
484
485
size_t Graph::getDegree(size_t id) const
486
{
487
Vertices::const_iterator it = vertices.find(id);
488
CV_Assert( it != vertices.end() );
489
return it->second.neighbors.size();
490
}
491
492
void Graph::floydWarshall(cv::Mat &distanceMatrix, int infinity) const
493
{
494
const int edgeWeight = 1;
495
496
const int n = (int)getVerticesCount();
497
distanceMatrix.create(n, n, CV_32SC1);
498
distanceMatrix.setTo(infinity);
499
for (Vertices::const_iterator it1 = vertices.begin(); it1 != vertices.end();++it1)
500
{
501
distanceMatrix.at<int> ((int)it1->first, (int)it1->first) = 0;
502
for (Neighbors::const_iterator it2 = it1->second.neighbors.begin(); it2 != it1->second.neighbors.end();++it2)
503
{
504
CV_Assert( it1->first != *it2 );
505
distanceMatrix.at<int> ((int)it1->first, (int)*it2) = edgeWeight;
506
}
507
}
508
509
for (Vertices::const_iterator it1 = vertices.begin(); it1 != vertices.end();++it1)
510
{
511
for (Vertices::const_iterator it2 = vertices.begin(); it2 != vertices.end();++it2)
512
{
513
for (Vertices::const_iterator it3 = vertices.begin(); it3 != vertices.end();++it3)
514
{
515
int i1 = (int)it1->first, i2 = (int)it2->first, i3 = (int)it3->first;
516
int val1 = distanceMatrix.at<int> (i2, i3);
517
int val2;
518
if (distanceMatrix.at<int> (i2, i1) == infinity ||
519
distanceMatrix.at<int> (i1, i3) == infinity)
520
val2 = val1;
521
else
522
{
523
val2 = distanceMatrix.at<int> (i2, i1) + distanceMatrix.at<int> (i1, i3);
524
}
525
distanceMatrix.at<int> (i2, i3) = (val1 == infinity) ? val2 : std::min(val1, val2);
526
}
527
}
528
}
529
}
530
531
const Graph::Neighbors& Graph::getNeighbors(size_t id) const
532
{
533
Vertices::const_iterator it = vertices.find(id);
534
CV_Assert( it != vertices.end() );
535
return it->second.neighbors;
536
}
537
538
CirclesGridFinder::Segment::Segment(cv::Point2f _s, cv::Point2f _e) :
539
s(_s), e(_e)
540
{
541
}
542
543
void computeShortestPath(Mat &predecessorMatrix, int v1, int v2, std::vector<int> &path);
544
void computePredecessorMatrix(const Mat &dm, int verticesCount, Mat &predecessorMatrix);
545
546
CirclesGridFinderParameters::CirclesGridFinderParameters()
547
{
548
minDensity = 10;
549
densityNeighborhoodSize = Size2f(16, 16);
550
minDistanceToAddKeypoint = 20;
551
kmeansAttempts = 100;
552
convexHullFactor = 1.1f;
553
keypointScale = 1;
554
555
minGraphConfidence = 9;
556
vertexGain = 1;
557
vertexPenalty = -0.6f;
558
edgeGain = 1;
559
edgePenalty = -0.6f;
560
existingVertexGain = 10000;
561
562
minRNGEdgeSwitchDist = 5.f;
563
gridType = SYMMETRIC_GRID;
564
565
squareSize = 1.0f;
566
maxRectifiedDistance = squareSize/2.0f;
567
}
568
569
CirclesGridFinder::CirclesGridFinder(Size _patternSize, const std::vector<Point2f> &testKeypoints,
570
const CirclesGridFinderParameters &_parameters) :
571
patternSize(static_cast<size_t> (_patternSize.width), static_cast<size_t> (_patternSize.height))
572
{
573
CV_Assert(_patternSize.height >= 0 && _patternSize.width >= 0);
574
575
keypoints = testKeypoints;
576
parameters = _parameters;
577
largeHoles = 0;
578
smallHoles = 0;
579
}
580
581
bool CirclesGridFinder::findHoles()
582
{
583
switch (parameters.gridType)
584
{
585
case CirclesGridFinderParameters::SYMMETRIC_GRID:
586
{
587
std::vector<Point2f> vectors, filteredVectors, basis;
588
Graph rng(0);
589
computeRNG(rng, vectors);
590
filterOutliersByDensity(vectors, filteredVectors);
591
std::vector<Graph> basisGraphs;
592
findBasis(filteredVectors, basis, basisGraphs);
593
findMCS(basis, basisGraphs);
594
break;
595
}
596
597
case CirclesGridFinderParameters::ASYMMETRIC_GRID:
598
{
599
std::vector<Point2f> vectors, tmpVectors, filteredVectors, basis;
600
Graph rng(0);
601
computeRNG(rng, tmpVectors);
602
rng2gridGraph(rng, vectors);
603
filterOutliersByDensity(vectors, filteredVectors);
604
std::vector<Graph> basisGraphs;
605
findBasis(filteredVectors, basis, basisGraphs);
606
findMCS(basis, basisGraphs);
607
eraseUsedGraph(basisGraphs);
608
holes2 = holes;
609
holes.clear();
610
findMCS(basis, basisGraphs);
611
break;
612
}
613
614
default:
615
CV_Error(Error::StsBadArg, "Unknown pattern type");
616
}
617
return (isDetectionCorrect());
618
//CV_Error( 0, "Detection is not correct" );
619
}
620
621
void CirclesGridFinder::rng2gridGraph(Graph &rng, std::vector<cv::Point2f> &vectors) const
622
{
623
for (size_t i = 0; i < rng.getVerticesCount(); i++)
624
{
625
Graph::Neighbors neighbors1 = rng.getNeighbors(i);
626
for (Graph::Neighbors::iterator it1 = neighbors1.begin(); it1 != neighbors1.end(); ++it1)
627
{
628
Graph::Neighbors neighbors2 = rng.getNeighbors(*it1);
629
for (Graph::Neighbors::iterator it2 = neighbors2.begin(); it2 != neighbors2.end(); ++it2)
630
{
631
if (i < *it2)
632
{
633
Point2f vec1 = keypoints[i] - keypoints[*it1];
634
Point2f vec2 = keypoints[*it1] - keypoints[*it2];
635
if (norm(vec1 - vec2) < parameters.minRNGEdgeSwitchDist || norm(vec1 + vec2)
636
< parameters.minRNGEdgeSwitchDist)
637
continue;
638
639
vectors.push_back(keypoints[i] - keypoints[*it2]);
640
vectors.push_back(keypoints[*it2] - keypoints[i]);
641
}
642
}
643
}
644
}
645
}
646
647
void CirclesGridFinder::eraseUsedGraph(std::vector<Graph> &basisGraphs) const
648
{
649
for (size_t i = 0; i < holes.size(); i++)
650
{
651
for (size_t j = 0; j < holes[i].size(); j++)
652
{
653
for (size_t k = 0; k < basisGraphs.size(); k++)
654
{
655
if (i != holes.size() - 1 && basisGraphs[k].areVerticesAdjacent(holes[i][j], holes[i + 1][j]))
656
{
657
basisGraphs[k].removeEdge(holes[i][j], holes[i + 1][j]);
658
}
659
660
if (j != holes[i].size() - 1 && basisGraphs[k].areVerticesAdjacent(holes[i][j], holes[i][j + 1]))
661
{
662
basisGraphs[k].removeEdge(holes[i][j], holes[i][j + 1]);
663
}
664
}
665
}
666
}
667
}
668
669
bool CirclesGridFinder::isDetectionCorrect()
670
{
671
switch (parameters.gridType)
672
{
673
case CirclesGridFinderParameters::SYMMETRIC_GRID:
674
{
675
if (holes.size() != patternSize.height)
676
return false;
677
678
std::set<size_t> vertices;
679
for (size_t i = 0; i < holes.size(); i++)
680
{
681
if (holes[i].size() != patternSize.width)
682
return false;
683
684
for (size_t j = 0; j < holes[i].size(); j++)
685
{
686
vertices.insert(holes[i][j]);
687
}
688
}
689
690
return vertices.size() == patternSize.area();
691
}
692
693
case CirclesGridFinderParameters::ASYMMETRIC_GRID:
694
{
695
if (holes.size() < holes2.size() || holes[0].size() < holes2[0].size())
696
{
697
largeHoles = &holes2;
698
smallHoles = &holes;
699
}
700
else
701
{
702
largeHoles = &holes;
703
smallHoles = &holes2;
704
}
705
706
size_t largeWidth = patternSize.width;
707
size_t largeHeight = (size_t)ceil(patternSize.height / 2.);
708
size_t smallWidth = patternSize.width;
709
size_t smallHeight = (size_t)floor(patternSize.height / 2.);
710
711
size_t sw = smallWidth, sh = smallHeight, lw = largeWidth, lh = largeHeight;
712
if (largeHoles->size() != largeHeight)
713
{
714
std::swap(lh, lw);
715
}
716
if (smallHoles->size() != smallHeight)
717
{
718
std::swap(sh, sw);
719
}
720
721
if (largeHoles->size() != lh || smallHoles->size() != sh)
722
{
723
return false;
724
}
725
726
std::set<size_t> vertices;
727
for (size_t i = 0; i < largeHoles->size(); i++)
728
{
729
if (largeHoles->at(i).size() != lw)
730
{
731
return false;
732
}
733
734
for (size_t j = 0; j < largeHoles->at(i).size(); j++)
735
{
736
vertices.insert(largeHoles->at(i)[j]);
737
}
738
739
if (i < smallHoles->size())
740
{
741
if (smallHoles->at(i).size() != sw)
742
{
743
return false;
744
}
745
746
for (size_t j = 0; j < smallHoles->at(i).size(); j++)
747
{
748
vertices.insert(smallHoles->at(i)[j]);
749
}
750
}
751
}
752
return (vertices.size() == largeHeight * largeWidth + smallHeight * smallWidth);
753
}
754
}
755
CV_Error(Error::StsBadArg, "Unknown pattern type");
756
}
757
758
void CirclesGridFinder::findMCS(const std::vector<Point2f> &basis, std::vector<Graph> &basisGraphs)
759
{
760
holes.clear();
761
Path longestPath;
762
size_t bestGraphIdx = findLongestPath(basisGraphs, longestPath);
763
std::vector<size_t> holesRow = longestPath.vertices;
764
765
while (holesRow.size() > std::max(patternSize.width, patternSize.height))
766
{
767
holesRow.pop_back();
768
holesRow.erase(holesRow.begin());
769
}
770
771
if (bestGraphIdx == 0)
772
{
773
holes.push_back(holesRow);
774
size_t w = holes[0].size();
775
size_t h = holes.size();
776
777
//parameters.minGraphConfidence = holes[0].size() * parameters.vertexGain + (holes[0].size() - 1) * parameters.edgeGain;
778
//parameters.minGraphConfidence = holes[0].size() * parameters.vertexGain + (holes[0].size() / 2) * parameters.edgeGain;
779
//parameters.minGraphConfidence = holes[0].size() * parameters.existingVertexGain + (holes[0].size() / 2) * parameters.edgeGain;
780
parameters.minGraphConfidence = holes[0].size() * parameters.existingVertexGain;
781
for (size_t i = h; i < patternSize.height; i++)
782
{
783
addHolesByGraph(basisGraphs, true, basis[1]);
784
}
785
786
//parameters.minGraphConfidence = holes.size() * parameters.existingVertexGain + (holes.size() / 2) * parameters.edgeGain;
787
parameters.minGraphConfidence = holes.size() * parameters.existingVertexGain;
788
789
for (size_t i = w; i < patternSize.width; i++)
790
{
791
addHolesByGraph(basisGraphs, false, basis[0]);
792
}
793
}
794
else
795
{
796
holes.resize(holesRow.size());
797
for (size_t i = 0; i < holesRow.size(); i++)
798
holes[i].push_back(holesRow[i]);
799
800
size_t w = holes[0].size();
801
size_t h = holes.size();
802
803
parameters.minGraphConfidence = holes.size() * parameters.existingVertexGain;
804
for (size_t i = w; i < patternSize.width; i++)
805
{
806
addHolesByGraph(basisGraphs, false, basis[0]);
807
}
808
809
parameters.minGraphConfidence = holes[0].size() * parameters.existingVertexGain;
810
for (size_t i = h; i < patternSize.height; i++)
811
{
812
addHolesByGraph(basisGraphs, true, basis[1]);
813
}
814
}
815
}
816
817
Mat CirclesGridFinder::rectifyGrid(Size detectedGridSize, const std::vector<Point2f>& centers,
818
const std::vector<Point2f> &keypoints, std::vector<Point2f> &warpedKeypoints)
819
{
820
CV_Assert( !centers.empty() );
821
const float edgeLength = 30;
822
const Point2f offset(150, 150);
823
824
std::vector<Point2f> dstPoints;
825
bool isClockwiseBefore =
826
getDirection(centers[0], centers[detectedGridSize.width - 1], centers[centers.size() - 1]) < 0;
827
828
int iStart = isClockwiseBefore ? 0 : detectedGridSize.height - 1;
829
int iEnd = isClockwiseBefore ? detectedGridSize.height : -1;
830
int iStep = isClockwiseBefore ? 1 : -1;
831
for (int i = iStart; i != iEnd; i += iStep)
832
{
833
for (int j = 0; j < detectedGridSize.width; j++)
834
{
835
dstPoints.push_back(offset + Point2f(edgeLength * j, edgeLength * i));
836
}
837
}
838
839
Mat H = findHomography(Mat(centers), Mat(dstPoints), RANSAC);
840
//Mat H = findHomography( Mat( corners ), Mat( dstPoints ) );
841
842
if (H.empty())
843
{
844
H = Mat::zeros(3, 3, CV_64FC1);
845
warpedKeypoints.clear();
846
return H;
847
}
848
849
std::vector<Point2f> srcKeypoints;
850
for (size_t i = 0; i < keypoints.size(); i++)
851
{
852
srcKeypoints.push_back(keypoints[i]);
853
}
854
855
Mat dstKeypointsMat;
856
transform(Mat(srcKeypoints), dstKeypointsMat, H);
857
std::vector<Point2f> dstKeypoints;
858
convertPointsFromHomogeneous(dstKeypointsMat, dstKeypoints);
859
860
warpedKeypoints.clear();
861
for (size_t i = 0; i < dstKeypoints.size(); i++)
862
{
863
Point2f pt = dstKeypoints[i];
864
warpedKeypoints.push_back(pt);
865
}
866
867
return H;
868
}
869
870
size_t CirclesGridFinder::findNearestKeypoint(Point2f pt) const
871
{
872
size_t bestIdx = 0;
873
double minDist = std::numeric_limits<double>::max();
874
for (size_t i = 0; i < keypoints.size(); i++)
875
{
876
double dist = norm(pt - keypoints[i]);
877
if (dist < minDist)
878
{
879
minDist = dist;
880
bestIdx = i;
881
}
882
}
883
return bestIdx;
884
}
885
886
void CirclesGridFinder::addPoint(Point2f pt, std::vector<size_t> &points)
887
{
888
size_t ptIdx = findNearestKeypoint(pt);
889
if (norm(keypoints[ptIdx] - pt) > parameters.minDistanceToAddKeypoint)
890
{
891
Point2f kpt = Point2f(pt);
892
keypoints.push_back(kpt);
893
points.push_back(keypoints.size() - 1);
894
}
895
else
896
{
897
points.push_back(ptIdx);
898
}
899
}
900
901
void CirclesGridFinder::findCandidateLine(std::vector<size_t> &line, size_t seedLineIdx, bool addRow, Point2f basisVec,
902
std::vector<size_t> &seeds)
903
{
904
line.clear();
905
seeds.clear();
906
907
if (addRow)
908
{
909
for (size_t i = 0; i < holes[seedLineIdx].size(); i++)
910
{
911
Point2f pt = keypoints[holes[seedLineIdx][i]] + basisVec;
912
addPoint(pt, line);
913
seeds.push_back(holes[seedLineIdx][i]);
914
}
915
}
916
else
917
{
918
for (size_t i = 0; i < holes.size(); i++)
919
{
920
Point2f pt = keypoints[holes[i][seedLineIdx]] + basisVec;
921
addPoint(pt, line);
922
seeds.push_back(holes[i][seedLineIdx]);
923
}
924
}
925
926
CV_Assert( line.size() == seeds.size() );
927
}
928
929
void CirclesGridFinder::findCandidateHoles(std::vector<size_t> &above, std::vector<size_t> &below, bool addRow, Point2f basisVec,
930
std::vector<size_t> &aboveSeeds, std::vector<size_t> &belowSeeds)
931
{
932
above.clear();
933
below.clear();
934
aboveSeeds.clear();
935
belowSeeds.clear();
936
937
findCandidateLine(above, 0, addRow, -basisVec, aboveSeeds);
938
size_t lastIdx = addRow ? holes.size() - 1 : holes[0].size() - 1;
939
findCandidateLine(below, lastIdx, addRow, basisVec, belowSeeds);
940
941
CV_Assert( below.size() == above.size() );
942
CV_Assert( belowSeeds.size() == aboveSeeds.size() );
943
CV_Assert( below.size() == belowSeeds.size() );
944
}
945
946
bool CirclesGridFinder::areCentersNew(const std::vector<size_t> &newCenters, const std::vector<std::vector<size_t> > &holes)
947
{
948
for (size_t i = 0; i < newCenters.size(); i++)
949
{
950
for (size_t j = 0; j < holes.size(); j++)
951
{
952
if (holes[j].end() != std::find(holes[j].begin(), holes[j].end(), newCenters[i]))
953
{
954
return false;
955
}
956
}
957
}
958
959
return true;
960
}
961
962
void CirclesGridFinder::insertWinner(float aboveConfidence, float belowConfidence, float minConfidence, bool addRow,
963
const std::vector<size_t> &above, const std::vector<size_t> &below,
964
std::vector<std::vector<size_t> > &holes)
965
{
966
if (aboveConfidence < minConfidence && belowConfidence < minConfidence)
967
return;
968
969
if (addRow)
970
{
971
if (aboveConfidence >= belowConfidence)
972
{
973
if (!areCentersNew(above, holes))
974
CV_Error( 0, "Centers are not new" );
975
976
holes.insert(holes.begin(), above);
977
}
978
else
979
{
980
if (!areCentersNew(below, holes))
981
CV_Error( 0, "Centers are not new" );
982
983
holes.insert(holes.end(), below);
984
}
985
}
986
else
987
{
988
if (aboveConfidence >= belowConfidence)
989
{
990
if (!areCentersNew(above, holes))
991
CV_Error( 0, "Centers are not new" );
992
993
for (size_t i = 0; i < holes.size(); i++)
994
{
995
holes[i].insert(holes[i].begin(), above[i]);
996
}
997
}
998
else
999
{
1000
if (!areCentersNew(below, holes))
1001
CV_Error( 0, "Centers are not new" );
1002
1003
for (size_t i = 0; i < holes.size(); i++)
1004
{
1005
holes[i].insert(holes[i].end(), below[i]);
1006
}
1007
}
1008
}
1009
}
1010
1011
float CirclesGridFinder::computeGraphConfidence(const std::vector<Graph> &basisGraphs, bool addRow,
1012
const std::vector<size_t> &points, const std::vector<size_t> &seeds)
1013
{
1014
CV_Assert( points.size() == seeds.size() );
1015
float confidence = 0;
1016
const size_t vCount = basisGraphs[0].getVerticesCount();
1017
CV_Assert( basisGraphs[0].getVerticesCount() == basisGraphs[1].getVerticesCount() );
1018
1019
for (size_t i = 0; i < seeds.size(); i++)
1020
{
1021
if (seeds[i] < vCount && points[i] < vCount)
1022
{
1023
if (!basisGraphs[addRow].areVerticesAdjacent(seeds[i], points[i]))
1024
{
1025
confidence += parameters.vertexPenalty;
1026
}
1027
else
1028
{
1029
confidence += parameters.vertexGain;
1030
}
1031
}
1032
1033
if (points[i] < vCount)
1034
{
1035
confidence += parameters.existingVertexGain;
1036
}
1037
}
1038
1039
for (size_t i = 1; i < points.size(); i++)
1040
{
1041
if (points[i - 1] < vCount && points[i] < vCount)
1042
{
1043
if (!basisGraphs[!addRow].areVerticesAdjacent(points[i - 1], points[i]))
1044
{
1045
confidence += parameters.edgePenalty;
1046
}
1047
else
1048
{
1049
confidence += parameters.edgeGain;
1050
}
1051
}
1052
}
1053
return confidence;
1054
1055
}
1056
1057
void CirclesGridFinder::addHolesByGraph(const std::vector<Graph> &basisGraphs, bool addRow, Point2f basisVec)
1058
{
1059
std::vector<size_t> above, below, aboveSeeds, belowSeeds;
1060
findCandidateHoles(above, below, addRow, basisVec, aboveSeeds, belowSeeds);
1061
float aboveConfidence = computeGraphConfidence(basisGraphs, addRow, above, aboveSeeds);
1062
float belowConfidence = computeGraphConfidence(basisGraphs, addRow, below, belowSeeds);
1063
1064
insertWinner(aboveConfidence, belowConfidence, parameters.minGraphConfidence, addRow, above, below, holes);
1065
}
1066
1067
void CirclesGridFinder::filterOutliersByDensity(const std::vector<Point2f> &samples, std::vector<Point2f> &filteredSamples)
1068
{
1069
if (samples.empty())
1070
CV_Error( 0, "samples is empty" );
1071
1072
filteredSamples.clear();
1073
1074
for (size_t i = 0; i < samples.size(); i++)
1075
{
1076
Rect_<float> rect(samples[i] - Point2f(parameters.densityNeighborhoodSize) * 0.5,
1077
parameters.densityNeighborhoodSize);
1078
int neighborsCount = 0;
1079
for (size_t j = 0; j < samples.size(); j++)
1080
{
1081
if (rect.contains(samples[j]))
1082
neighborsCount++;
1083
}
1084
if (neighborsCount >= parameters.minDensity)
1085
filteredSamples.push_back(samples[i]);
1086
}
1087
1088
if (filteredSamples.empty())
1089
CV_Error( 0, "filteredSamples is empty" );
1090
}
1091
1092
void CirclesGridFinder::findBasis(const std::vector<Point2f> &samples, std::vector<Point2f> &basis, std::vector<Graph> &basisGraphs)
1093
{
1094
basis.clear();
1095
Mat bestLabels;
1096
TermCriteria termCriteria;
1097
Mat centers;
1098
const int clustersCount = 4;
1099
kmeans(Mat(samples).reshape(1, 0), clustersCount, bestLabels, termCriteria, parameters.kmeansAttempts,
1100
KMEANS_RANDOM_CENTERS, centers);
1101
CV_Assert( centers.type() == CV_32FC1 );
1102
1103
std::vector<int> basisIndices;
1104
//TODO: only remove duplicate
1105
for (int i = 0; i < clustersCount; i++)
1106
{
1107
int maxIdx = (fabs(centers.at<float> (i, 0)) < fabs(centers.at<float> (i, 1)));
1108
if (centers.at<float> (i, maxIdx) > 0)
1109
{
1110
Point2f vec(centers.at<float> (i, 0), centers.at<float> (i, 1));
1111
basis.push_back(vec);
1112
basisIndices.push_back(i);
1113
}
1114
}
1115
if (basis.size() != 2)
1116
CV_Error(0, "Basis size is not 2");
1117
1118
if (basis[1].x > basis[0].x)
1119
{
1120
std::swap(basis[0], basis[1]);
1121
std::swap(basisIndices[0], basisIndices[1]);
1122
}
1123
1124
const float minBasisDif = 2;
1125
if (norm(basis[0] - basis[1]) < minBasisDif)
1126
CV_Error(0, "degenerate basis" );
1127
1128
std::vector<std::vector<Point2f> > clusters(2), hulls(2);
1129
for (int k = 0; k < (int)samples.size(); k++)
1130
{
1131
int label = bestLabels.at<int> (k, 0);
1132
int idx = -1;
1133
if (label == basisIndices[0])
1134
idx = 0;
1135
if (label == basisIndices[1])
1136
idx = 1;
1137
if (idx >= 0)
1138
{
1139
clusters[idx].push_back(basis[idx] + parameters.convexHullFactor * (samples[k] - basis[idx]));
1140
}
1141
}
1142
for (size_t i = 0; i < basis.size(); i++)
1143
{
1144
convexHull(Mat(clusters[i]), hulls[i]);
1145
}
1146
1147
basisGraphs.resize(basis.size(), Graph(keypoints.size()));
1148
for (size_t i = 0; i < keypoints.size(); i++)
1149
{
1150
for (size_t j = 0; j < keypoints.size(); j++)
1151
{
1152
if (i == j)
1153
continue;
1154
1155
Point2f vec = keypoints[i] - keypoints[j];
1156
1157
for (size_t k = 0; k < hulls.size(); k++)
1158
{
1159
if (pointPolygonTest(Mat(hulls[k]), vec, false) >= 0)
1160
{
1161
basisGraphs[k].addEdge(i, j);
1162
}
1163
}
1164
}
1165
}
1166
if (basisGraphs.size() != 2)
1167
CV_Error(0, "Number of basis graphs is not 2");
1168
}
1169
1170
void CirclesGridFinder::computeRNG(Graph &rng, std::vector<cv::Point2f> &vectors, Mat *drawImage) const
1171
{
1172
rng = Graph(keypoints.size());
1173
vectors.clear();
1174
1175
//TODO: use more fast algorithm instead of naive N^3
1176
for (size_t i = 0; i < keypoints.size(); i++)
1177
{
1178
for (size_t j = 0; j < keypoints.size(); j++)
1179
{
1180
if (i == j)
1181
continue;
1182
1183
Point2f vec = keypoints[i] - keypoints[j];
1184
double dist = norm(vec);
1185
1186
bool isNeighbors = true;
1187
for (size_t k = 0; k < keypoints.size(); k++)
1188
{
1189
if (k == i || k == j)
1190
continue;
1191
1192
double dist1 = norm(keypoints[i] - keypoints[k]);
1193
double dist2 = norm(keypoints[j] - keypoints[k]);
1194
if (dist1 < dist && dist2 < dist)
1195
{
1196
isNeighbors = false;
1197
break;
1198
}
1199
}
1200
1201
if (isNeighbors)
1202
{
1203
rng.addEdge(i, j);
1204
vectors.push_back(keypoints[i] - keypoints[j]);
1205
if (drawImage != 0)
1206
{
1207
line(*drawImage, keypoints[i], keypoints[j], Scalar(255, 0, 0), 2);
1208
circle(*drawImage, keypoints[i], 3, Scalar(0, 0, 255), -1);
1209
circle(*drawImage, keypoints[j], 3, Scalar(0, 0, 255), -1);
1210
}
1211
}
1212
}
1213
}
1214
}
1215
1216
void computePredecessorMatrix(const Mat &dm, int verticesCount, Mat &predecessorMatrix)
1217
{
1218
CV_Assert( dm.type() == CV_32SC1 );
1219
predecessorMatrix.create(verticesCount, verticesCount, CV_32SC1);
1220
predecessorMatrix = -1;
1221
for (int i = 0; i < predecessorMatrix.rows; i++)
1222
{
1223
for (int j = 0; j < predecessorMatrix.cols; j++)
1224
{
1225
int dist = dm.at<int> (i, j);
1226
for (int k = 0; k < verticesCount; k++)
1227
{
1228
if (dm.at<int> (i, k) == dist - 1 && dm.at<int> (k, j) == 1)
1229
{
1230
predecessorMatrix.at<int> (i, j) = k;
1231
break;
1232
}
1233
}
1234
}
1235
}
1236
}
1237
1238
static void computeShortestPath(Mat &predecessorMatrix, size_t v1, size_t v2, std::vector<size_t> &path)
1239
{
1240
if (predecessorMatrix.at<int> ((int)v1, (int)v2) < 0)
1241
{
1242
path.push_back(v1);
1243
return;
1244
}
1245
1246
computeShortestPath(predecessorMatrix, v1, predecessorMatrix.at<int> ((int)v1, (int)v2), path);
1247
path.push_back(v2);
1248
}
1249
1250
size_t CirclesGridFinder::findLongestPath(std::vector<Graph> &basisGraphs, Path &bestPath)
1251
{
1252
std::vector<Path> longestPaths(1);
1253
std::vector<int> confidences;
1254
1255
size_t bestGraphIdx = 0;
1256
const int infinity = -1;
1257
for (size_t graphIdx = 0; graphIdx < basisGraphs.size(); graphIdx++)
1258
{
1259
const Graph &g = basisGraphs[graphIdx];
1260
Mat distanceMatrix;
1261
g.floydWarshall(distanceMatrix, infinity);
1262
Mat predecessorMatrix;
1263
computePredecessorMatrix(distanceMatrix, (int)g.getVerticesCount(), predecessorMatrix);
1264
1265
double maxVal;
1266
Point maxLoc;
1267
minMaxLoc(distanceMatrix, 0, &maxVal, 0, &maxLoc);
1268
1269
if (maxVal > longestPaths[0].length)
1270
{
1271
longestPaths.clear();
1272
confidences.clear();
1273
bestGraphIdx = graphIdx;
1274
}
1275
if (longestPaths.empty() || (maxVal == longestPaths[0].length && graphIdx == bestGraphIdx))
1276
{
1277
Path path = Path(maxLoc.x, maxLoc.y, cvRound(maxVal));
1278
CV_Assert(maxLoc.x >= 0 && maxLoc.y >= 0)
1279
;
1280
size_t id1 = static_cast<size_t> (maxLoc.x);
1281
size_t id2 = static_cast<size_t> (maxLoc.y);
1282
computeShortestPath(predecessorMatrix, id1, id2, path.vertices);
1283
longestPaths.push_back(path);
1284
1285
int conf = 0;
1286
for (int v2 = 0; v2 < (int)path.vertices.size(); v2++)
1287
{
1288
conf += (int)basisGraphs[1 - (int)graphIdx].getDegree(v2);
1289
}
1290
confidences.push_back(conf);
1291
}
1292
}
1293
//if( bestGraphIdx != 0 )
1294
//CV_Error( 0, "" );
1295
1296
int maxConf = -1;
1297
int bestPathIdx = -1;
1298
for (int i = 0; i < (int)confidences.size(); i++)
1299
{
1300
if (confidences[i] > maxConf)
1301
{
1302
maxConf = confidences[i];
1303
bestPathIdx = i;
1304
}
1305
}
1306
1307
//int bestPathIdx = rand() % longestPaths.size();
1308
bestPath = longestPaths.at(bestPathIdx);
1309
bool needReverse = (bestGraphIdx == 0 && keypoints[bestPath.lastVertex].x < keypoints[bestPath.firstVertex].x)
1310
|| (bestGraphIdx == 1 && keypoints[bestPath.lastVertex].y < keypoints[bestPath.firstVertex].y);
1311
if (needReverse)
1312
{
1313
std::swap(bestPath.lastVertex, bestPath.firstVertex);
1314
std::reverse(bestPath.vertices.begin(), bestPath.vertices.end());
1315
}
1316
return bestGraphIdx;
1317
}
1318
1319
void CirclesGridFinder::drawBasis(const std::vector<Point2f> &basis, Point2f origin, Mat &drawImg) const
1320
{
1321
for (size_t i = 0; i < basis.size(); i++)
1322
{
1323
Point2f pt(basis[i]);
1324
line(drawImg, origin, origin + pt, Scalar(0, (double)(i * 255), 0), 2);
1325
}
1326
}
1327
1328
void CirclesGridFinder::drawBasisGraphs(const std::vector<Graph> &basisGraphs, Mat &drawImage, bool drawEdges,
1329
bool drawVertices) const
1330
{
1331
//const int vertexRadius = 1;
1332
const int vertexRadius = 3;
1333
const Scalar vertexColor = Scalar(0, 0, 255);
1334
const int vertexThickness = -1;
1335
1336
const Scalar edgeColor = Scalar(255, 0, 0);
1337
//const int edgeThickness = 1;
1338
const int edgeThickness = 2;
1339
1340
if (drawEdges)
1341
{
1342
for (size_t i = 0; i < basisGraphs.size(); i++)
1343
{
1344
for (size_t v1 = 0; v1 < basisGraphs[i].getVerticesCount(); v1++)
1345
{
1346
for (size_t v2 = 0; v2 < basisGraphs[i].getVerticesCount(); v2++)
1347
{
1348
if (basisGraphs[i].areVerticesAdjacent(v1, v2))
1349
{
1350
line(drawImage, keypoints[v1], keypoints[v2], edgeColor, edgeThickness);
1351
}
1352
}
1353
}
1354
}
1355
}
1356
if (drawVertices)
1357
{
1358
for (size_t v = 0; v < basisGraphs[0].getVerticesCount(); v++)
1359
{
1360
circle(drawImage, keypoints[v], vertexRadius, vertexColor, vertexThickness);
1361
}
1362
}
1363
}
1364
1365
void CirclesGridFinder::drawHoles(const Mat &srcImage, Mat &drawImage) const
1366
{
1367
//const int holeRadius = 4;
1368
//const int holeRadius = 2;
1369
//const int holeThickness = 1;
1370
const int holeRadius = 3;
1371
const int holeThickness = -1;
1372
1373
//const Scalar holeColor = Scalar(0, 0, 255);
1374
const Scalar holeColor = Scalar(0, 255, 0);
1375
1376
if (srcImage.channels() == 1)
1377
cvtColor(srcImage, drawImage, COLOR_GRAY2RGB);
1378
else
1379
srcImage.copyTo(drawImage);
1380
1381
for (size_t i = 0; i < holes.size(); i++)
1382
{
1383
for (size_t j = 0; j < holes[i].size(); j++)
1384
{
1385
if (j != holes[i].size() - 1)
1386
line(drawImage, keypoints[holes[i][j]], keypoints[holes[i][j + 1]], Scalar(255, 0, 0), 2);
1387
if (i != holes.size() - 1)
1388
line(drawImage, keypoints[holes[i][j]], keypoints[holes[i + 1][j]], Scalar(255, 0, 0), 2);
1389
1390
//circle(drawImage, keypoints[holes[i][j]], holeRadius, holeColor, holeThickness);
1391
circle(drawImage, keypoints[holes[i][j]], holeRadius, holeColor, holeThickness);
1392
}
1393
}
1394
}
1395
1396
Size CirclesGridFinder::getDetectedGridSize() const
1397
{
1398
if (holes.size() == 0)
1399
return Size(0, 0);
1400
1401
return Size((int)holes[0].size(), (int)holes.size());
1402
}
1403
1404
void CirclesGridFinder::getHoles(std::vector<Point2f> &outHoles) const
1405
{
1406
outHoles.clear();
1407
1408
for (size_t i = 0; i < holes.size(); i++)
1409
{
1410
for (size_t j = 0; j < holes[i].size(); j++)
1411
{
1412
outHoles.push_back(keypoints[holes[i][j]]);
1413
}
1414
}
1415
}
1416
1417
static bool areIndicesCorrect(Point pos, std::vector<std::vector<size_t> > *points)
1418
{
1419
if (pos.y < 0 || pos.x < 0)
1420
return false;
1421
return (static_cast<size_t> (pos.y) < points->size() && static_cast<size_t> (pos.x) < points->at(pos.y).size());
1422
}
1423
1424
void CirclesGridFinder::getAsymmetricHoles(std::vector<cv::Point2f> &outHoles) const
1425
{
1426
outHoles.clear();
1427
1428
std::vector<Point> largeCornerIndices, smallCornerIndices;
1429
std::vector<Point> firstSteps, secondSteps;
1430
size_t cornerIdx = getFirstCorner(largeCornerIndices, smallCornerIndices, firstSteps, secondSteps);
1431
CV_Assert(largeHoles != 0 && smallHoles != 0)
1432
;
1433
1434
Point srcLargePos = largeCornerIndices[cornerIdx];
1435
Point srcSmallPos = smallCornerIndices[cornerIdx];
1436
1437
while (areIndicesCorrect(srcLargePos, largeHoles) || areIndicesCorrect(srcSmallPos, smallHoles))
1438
{
1439
Point largePos = srcLargePos;
1440
while (areIndicesCorrect(largePos, largeHoles))
1441
{
1442
outHoles.push_back(keypoints[largeHoles->at(largePos.y)[largePos.x]]);
1443
largePos += firstSteps[cornerIdx];
1444
}
1445
srcLargePos += secondSteps[cornerIdx];
1446
1447
Point smallPos = srcSmallPos;
1448
while (areIndicesCorrect(smallPos, smallHoles))
1449
{
1450
outHoles.push_back(keypoints[smallHoles->at(smallPos.y)[smallPos.x]]);
1451
smallPos += firstSteps[cornerIdx];
1452
}
1453
srcSmallPos += secondSteps[cornerIdx];
1454
}
1455
}
1456
1457
double CirclesGridFinder::getDirection(Point2f p1, Point2f p2, Point2f p3)
1458
{
1459
Point2f a = p3 - p1;
1460
Point2f b = p2 - p1;
1461
return a.x * b.y - a.y * b.x;
1462
}
1463
1464
bool CirclesGridFinder::areSegmentsIntersecting(Segment seg1, Segment seg2)
1465
{
1466
bool doesStraddle1 = (getDirection(seg2.s, seg2.e, seg1.s) * getDirection(seg2.s, seg2.e, seg1.e)) < 0;
1467
bool doesStraddle2 = (getDirection(seg1.s, seg1.e, seg2.s) * getDirection(seg1.s, seg1.e, seg2.e)) < 0;
1468
return doesStraddle1 && doesStraddle2;
1469
1470
/*
1471
Point2f t1 = e1-s1;
1472
Point2f n1(t1.y, -t1.x);
1473
double c1 = -n1.ddot(s1);
1474
1475
Point2f t2 = e2-s2;
1476
Point2f n2(t2.y, -t2.x);
1477
double c2 = -n2.ddot(s2);
1478
1479
bool seg1 = ((n1.ddot(s2) + c1) * (n1.ddot(e2) + c1)) <= 0;
1480
bool seg1 = ((n2.ddot(s1) + c2) * (n2.ddot(e1) + c2)) <= 0;
1481
1482
return seg1 && seg2;
1483
*/
1484
}
1485
1486
void CirclesGridFinder::getCornerSegments(const std::vector<std::vector<size_t> > &points, std::vector<std::vector<Segment> > &segments,
1487
std::vector<Point> &cornerIndices, std::vector<Point> &firstSteps,
1488
std::vector<Point> &secondSteps) const
1489
{
1490
segments.clear();
1491
cornerIndices.clear();
1492
firstSteps.clear();
1493
secondSteps.clear();
1494
int h = (int)points.size();
1495
int w = (int)points[0].size();
1496
CV_Assert(h >= 2 && w >= 2)
1497
;
1498
1499
//all 8 segments with one end in a corner
1500
std::vector<Segment> corner;
1501
corner.push_back(Segment(keypoints[points[1][0]], keypoints[points[0][0]]));
1502
corner.push_back(Segment(keypoints[points[0][0]], keypoints[points[0][1]]));
1503
segments.push_back(corner);
1504
cornerIndices.push_back(Point(0, 0));
1505
firstSteps.push_back(Point(1, 0));
1506
secondSteps.push_back(Point(0, 1));
1507
corner.clear();
1508
1509
corner.push_back(Segment(keypoints[points[0][w - 2]], keypoints[points[0][w - 1]]));
1510
corner.push_back(Segment(keypoints[points[0][w - 1]], keypoints[points[1][w - 1]]));
1511
segments.push_back(corner);
1512
cornerIndices.push_back(Point(w - 1, 0));
1513
firstSteps.push_back(Point(0, 1));
1514
secondSteps.push_back(Point(-1, 0));
1515
corner.clear();
1516
1517
corner.push_back(Segment(keypoints[points[h - 2][w - 1]], keypoints[points[h - 1][w - 1]]));
1518
corner.push_back(Segment(keypoints[points[h - 1][w - 1]], keypoints[points[h - 1][w - 2]]));
1519
segments.push_back(corner);
1520
cornerIndices.push_back(Point(w - 1, h - 1));
1521
firstSteps.push_back(Point(-1, 0));
1522
secondSteps.push_back(Point(0, -1));
1523
corner.clear();
1524
1525
corner.push_back(Segment(keypoints[points[h - 1][1]], keypoints[points[h - 1][0]]));
1526
corner.push_back(Segment(keypoints[points[h - 1][0]], keypoints[points[h - 2][0]]));
1527
cornerIndices.push_back(Point(0, h - 1));
1528
firstSteps.push_back(Point(0, -1));
1529
secondSteps.push_back(Point(1, 0));
1530
segments.push_back(corner);
1531
corner.clear();
1532
1533
//y axis is inverted in computer vision so we check < 0
1534
bool isClockwise =
1535
getDirection(keypoints[points[0][0]], keypoints[points[0][w - 1]], keypoints[points[h - 1][w - 1]]) < 0;
1536
if (!isClockwise)
1537
{
1538
#ifdef DEBUG_CIRCLES
1539
std::cout << "Corners are counterclockwise" << std::endl;
1540
#endif
1541
std::reverse(segments.begin(), segments.end());
1542
std::reverse(cornerIndices.begin(), cornerIndices.end());
1543
std::reverse(firstSteps.begin(), firstSteps.end());
1544
std::reverse(secondSteps.begin(), secondSteps.end());
1545
std::swap(firstSteps, secondSteps);
1546
}
1547
}
1548
1549
bool CirclesGridFinder::doesIntersectionExist(const std::vector<Segment> &corner, const std::vector<std::vector<Segment> > &segments)
1550
{
1551
for (size_t i = 0; i < corner.size(); i++)
1552
{
1553
for (size_t j = 0; j < segments.size(); j++)
1554
{
1555
for (size_t k = 0; k < segments[j].size(); k++)
1556
{
1557
if (areSegmentsIntersecting(corner[i], segments[j][k]))
1558
return true;
1559
}
1560
}
1561
}
1562
1563
return false;
1564
}
1565
1566
size_t CirclesGridFinder::getFirstCorner(std::vector<Point> &largeCornerIndices, std::vector<Point> &smallCornerIndices, std::vector<
1567
Point> &firstSteps, std::vector<Point> &secondSteps) const
1568
{
1569
std::vector<std::vector<Segment> > largeSegments;
1570
std::vector<std::vector<Segment> > smallSegments;
1571
1572
getCornerSegments(*largeHoles, largeSegments, largeCornerIndices, firstSteps, secondSteps);
1573
getCornerSegments(*smallHoles, smallSegments, smallCornerIndices, firstSteps, secondSteps);
1574
1575
const size_t cornersCount = 4;
1576
CV_Assert(largeSegments.size() == cornersCount)
1577
;
1578
1579
bool isInsider[cornersCount];
1580
1581
for (size_t i = 0; i < cornersCount; i++)
1582
{
1583
isInsider[i] = doesIntersectionExist(largeSegments[i], smallSegments);
1584
}
1585
1586
int cornerIdx = 0;
1587
bool waitOutsider = true;
1588
1589
for(;;)
1590
{
1591
if (waitOutsider)
1592
{
1593
if (!isInsider[(cornerIdx + 1) % cornersCount])
1594
waitOutsider = false;
1595
}
1596
else
1597
{
1598
if (isInsider[(cornerIdx + 1) % cornersCount])
1599
break;
1600
}
1601
1602
cornerIdx = (cornerIdx + 1) % cornersCount;
1603
}
1604
1605
return cornerIdx;
1606
}
1607
1608