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