Path: blob/master/modules/calib3d/src/circlesgrid.cpp
16354 views
/*M///////////////////////////////////////////////////////////////////////////////////////1//2// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.3//4// By downloading, copying, installing or using the software you agree to this license.5// If you do not agree to this license, do not download, install,6// copy or use the software.7//8//9// License Agreement10// For Open Source Computer Vision Library11//12// Copyright (C) 2000-2008, Intel Corporation, all rights reserved.13// Copyright (C) 2009, Willow Garage Inc., all rights reserved.14// Third party copyrights are property of their respective owners.15//16// Redistribution and use in source and binary forms, with or without modification,17// are permitted provided that the following conditions are met:18//19// * Redistribution's of source code must retain the above copyright notice,20// this list of conditions and the following disclaimer.21//22// * Redistribution's in binary form must reproduce the above copyright notice,23// this list of conditions and the following disclaimer in the documentation24// and/or other materials provided with the distribution.25//26// * The name of the copyright holders may not be used to endorse or promote products27// derived from this software without specific prior written permission.28//29// This software is provided by the copyright holders and contributors "as is" and30// any express or implied warranties, including, but not limited to, the implied31// warranties of merchantability and fitness for a particular purpose are disclaimed.32// In no event shall the Intel Corporation or contributors be liable for any direct,33// indirect, incidental, special, exemplary, or consequential damages34// (including, but not limited to, procurement of substitute goods or services;35// loss of use, data, or profits; or business interruption) however caused36// and on any theory of liability, whether in contract, strict liability,37// or tort (including negligence or otherwise) arising in any way out of38// the use of this software, even if advised of the possibility of such damage.39//40//M*/4142#include "precomp.hpp"43#include "circlesgrid.hpp"44#include <limits>45//#define DEBUG_CIRCLES4647#ifdef DEBUG_CIRCLES48# include <iostream>49# include "opencv2/opencv_modules.hpp"50# ifdef HAVE_OPENCV_HIGHGUI51# include "opencv2/highgui.hpp"52# else53# undef DEBUG_CIRCLES54# endif55#endif5657using namespace cv;5859#ifdef DEBUG_CIRCLES60void drawPoints(const std::vector<Point2f> &points, Mat &outImage, int radius = 2, Scalar color = Scalar::all(255), int thickness = -1)61{62for(size_t i=0; i<points.size(); i++)63{64circle(outImage, points[i], radius, color, thickness);65}66}67#endif6869void CirclesGridClusterFinder::hierarchicalClustering(const std::vector<Point2f> &points, const Size &patternSz, std::vector<Point2f> &patternPoints)70{71int j, n = (int)points.size();72size_t pn = static_cast<size_t>(patternSz.area());7374patternPoints.clear();75if (pn >= points.size())76{77if (pn == points.size())78patternPoints = points;79return;80}8182Mat dists(n, n, CV_32FC1, Scalar(0));83Mat distsMask(dists.size(), CV_8UC1, Scalar(0));84for(int i = 0; i < n; i++)85{86for(j = i+1; j < n; j++)87{88dists.at<float>(i, j) = (float)norm(points[i] - points[j]);89distsMask.at<uchar>(i, j) = 255;90//TODO: use symmetry91distsMask.at<uchar>(j, i) = 255;//distsMask.at<uchar>(i, j);92dists.at<float>(j, i) = dists.at<float>(i, j);93}94}9596std::vector<std::list<size_t> > clusters(points.size());97for(size_t i=0; i<points.size(); i++)98{99clusters[i].push_back(i);100}101102int patternClusterIdx = 0;103while(clusters[patternClusterIdx].size() < pn)104{105Point minLoc;106minMaxLoc(dists, 0, 0, &minLoc, 0, distsMask);107int minIdx = std::min(minLoc.x, minLoc.y);108int maxIdx = std::max(minLoc.x, minLoc.y);109110distsMask.row(maxIdx).setTo(0);111distsMask.col(maxIdx).setTo(0);112Mat tmpRow = dists.row(minIdx);113Mat tmpCol = dists.col(minIdx);114cv::min(dists.row(minLoc.x), dists.row(minLoc.y), tmpRow);115tmpRow = tmpRow.t();116tmpRow.copyTo(tmpCol);117118clusters[minIdx].splice(clusters[minIdx].end(), clusters[maxIdx]);119patternClusterIdx = minIdx;120}121122//the largest cluster can have more than pn points -- we need to filter out such situations123if(clusters[patternClusterIdx].size() != static_cast<size_t>(patternSz.area()))124{125return;126}127128patternPoints.reserve(clusters[patternClusterIdx].size());129for(std::list<size_t>::iterator it = clusters[patternClusterIdx].begin(); it != clusters[patternClusterIdx].end();++it)130{131patternPoints.push_back(points[*it]);132}133}134135void CirclesGridClusterFinder::findGrid(const std::vector<cv::Point2f> &points, cv::Size _patternSize, std::vector<Point2f>& centers)136{137patternSize = _patternSize;138centers.clear();139if(points.empty())140{141return;142}143144std::vector<Point2f> patternPoints;145hierarchicalClustering(points, patternSize, patternPoints);146if(patternPoints.empty())147{148return;149}150151#ifdef DEBUG_CIRCLES152Mat patternPointsImage(1024, 1248, CV_8UC1, Scalar(0));153drawPoints(patternPoints, patternPointsImage);154imshow("pattern points", patternPointsImage);155#endif156157std::vector<Point2f> hull2f;158convexHull(Mat(patternPoints), hull2f, false);159const size_t cornersCount = isAsymmetricGrid ? 6 : 4;160if(hull2f.size() < cornersCount)161return;162163std::vector<Point2f> corners;164findCorners(hull2f, corners);165if(corners.size() != cornersCount)166return;167168std::vector<Point2f> outsideCorners, sortedCorners;169if(isAsymmetricGrid)170{171findOutsideCorners(corners, outsideCorners);172const size_t outsideCornersCount = 2;173if(outsideCorners.size() != outsideCornersCount)174return;175}176getSortedCorners(hull2f, corners, outsideCorners, sortedCorners);177if(sortedCorners.size() != cornersCount)178return;179180std::vector<Point2f> rectifiedPatternPoints;181rectifyPatternPoints(patternPoints, sortedCorners, rectifiedPatternPoints);182if(patternPoints.size() != rectifiedPatternPoints.size())183return;184185parsePatternPoints(patternPoints, rectifiedPatternPoints, centers);186}187188void CirclesGridClusterFinder::findCorners(const std::vector<cv::Point2f> &hull2f, std::vector<cv::Point2f> &corners)189{190//find angles (cosines) of vertices in convex hull191std::vector<float> angles;192for(size_t i=0; i<hull2f.size(); i++)193{194Point2f vec1 = hull2f[(i+1) % hull2f.size()] - hull2f[i % hull2f.size()];195Point2f vec2 = hull2f[(i-1 + static_cast<int>(hull2f.size())) % hull2f.size()] - hull2f[i % hull2f.size()];196float angle = (float)(vec1.ddot(vec2) / (norm(vec1) * norm(vec2)));197angles.push_back(angle);198}199200//sort angles by cosine201//corners are the most sharp angles (6)202Mat anglesMat = Mat(angles);203Mat sortedIndices;204sortIdx(anglesMat, sortedIndices, SORT_EVERY_COLUMN + SORT_DESCENDING);205CV_Assert(sortedIndices.type() == CV_32SC1);206CV_Assert(sortedIndices.cols == 1);207const int cornersCount = isAsymmetricGrid ? 6 : 4;208Mat cornersIndices;209cv::sort(sortedIndices.rowRange(0, cornersCount), cornersIndices, SORT_EVERY_COLUMN + SORT_ASCENDING);210corners.clear();211for(int i=0; i<cornersCount; i++)212{213corners.push_back(hull2f[cornersIndices.at<int>(i, 0)]);214}215}216217void CirclesGridClusterFinder::findOutsideCorners(const std::vector<cv::Point2f> &corners, std::vector<cv::Point2f> &outsideCorners)218{219CV_Assert(!corners.empty());220outsideCorners.clear();221//find two pairs of the most nearest corners222const size_t n = corners.size();223224#ifdef DEBUG_CIRCLES225Mat cornersImage(1024, 1248, CV_8UC1, Scalar(0));226drawPoints(corners, cornersImage);227imshow("corners", cornersImage);228#endif229230std::vector<Point2f> tangentVectors(n);231for(size_t k=0; k < n; k++)232{233Point2f diff = corners[(k + 1) % n] - corners[k];234tangentVectors[k] = diff * (1.0f / norm(diff));235}236237//compute angles between all sides238Mat cosAngles((int)n, (int)n, CV_32FC1, 0.0f);239for(size_t i = 0; i < n; i++)240{241for(size_t j = i + 1; j < n; j++)242{243float val = fabs(tangentVectors[i].dot(tangentVectors[j]));244cosAngles.at<float>((int)i, (int)j) = val;245cosAngles.at<float>((int)j, (int)i) = val;246}247}248249//find two parallel sides to which outside corners belong250Point maxLoc;251minMaxLoc(cosAngles, 0, 0, 0, &maxLoc);252const int diffBetweenFalseLines = 3;253if(abs(maxLoc.x - maxLoc.y) == diffBetweenFalseLines)254{255cosAngles.row(maxLoc.x).setTo(0.0f);256cosAngles.col(maxLoc.x).setTo(0.0f);257cosAngles.row(maxLoc.y).setTo(0.0f);258cosAngles.col(maxLoc.y).setTo(0.0f);259minMaxLoc(cosAngles, 0, 0, 0, &maxLoc);260}261262#ifdef DEBUG_CIRCLES263Mat linesImage(1024, 1248, CV_8UC1, Scalar(0));264line(linesImage, corners[maxLoc.y], corners[(maxLoc.y + 1) % n], Scalar(255));265line(linesImage, corners[maxLoc.x], corners[(maxLoc.x + 1) % n], Scalar(255));266imshow("lines", linesImage);267#endif268269int maxIdx = std::max(maxLoc.x, maxLoc.y);270int minIdx = std::min(maxLoc.x, maxLoc.y);271const int bigDiff = 4;272if(maxIdx - minIdx == bigDiff)273{274minIdx += (int)n;275std::swap(maxIdx, minIdx);276}277if(maxIdx - minIdx != (int)n - bigDiff)278{279return;280}281282int outsidersSegmentIdx = (minIdx + maxIdx) / 2;283284outsideCorners.push_back(corners[outsidersSegmentIdx % n]);285outsideCorners.push_back(corners[(outsidersSegmentIdx + 1) % n]);286287#ifdef DEBUG_CIRCLES288drawPoints(outsideCorners, cornersImage, 2, Scalar(128));289imshow("corners", cornersImage);290#endif291}292293void 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)294{295Point2f firstCorner;296if(isAsymmetricGrid)297{298Point2f center = std::accumulate(corners.begin(), corners.end(), Point2f(0.0f, 0.0f));299center *= 1.0 / corners.size();300301std::vector<Point2f> centerToCorners;302for(size_t i=0; i<outsideCorners.size(); i++)303{304centerToCorners.push_back(outsideCorners[i] - center);305}306307//TODO: use CirclesGridFinder::getDirection308float crossProduct = centerToCorners[0].x * centerToCorners[1].y - centerToCorners[0].y * centerToCorners[1].x;309//y axis is inverted in computer vision so we check > 0310bool isClockwise = crossProduct > 0;311firstCorner = isClockwise ? outsideCorners[1] : outsideCorners[0];312}313else314{315firstCorner = corners[0];316}317318std::vector<Point2f>::const_iterator firstCornerIterator = std::find(hull2f.begin(), hull2f.end(), firstCorner);319sortedCorners.clear();320for(std::vector<Point2f>::const_iterator it = firstCornerIterator; it != hull2f.end();++it)321{322std::vector<Point2f>::const_iterator itCorners = std::find(corners.begin(), corners.end(), *it);323if(itCorners != corners.end())324{325sortedCorners.push_back(*it);326}327}328for(std::vector<Point2f>::const_iterator it = hull2f.begin(); it != firstCornerIterator;++it)329{330std::vector<Point2f>::const_iterator itCorners = std::find(corners.begin(), corners.end(), *it);331if(itCorners != corners.end())332{333sortedCorners.push_back(*it);334}335}336337if(!isAsymmetricGrid)338{339double dist1 = norm(sortedCorners[0] - sortedCorners[1]);340double dist2 = norm(sortedCorners[1] - sortedCorners[2]);341342if((dist1 > dist2 && patternSize.height > patternSize.width) || (dist1 < dist2 && patternSize.height < patternSize.width))343{344for(size_t i=0; i<sortedCorners.size()-1; i++)345{346sortedCorners[i] = sortedCorners[i+1];347}348sortedCorners[sortedCorners.size() - 1] = firstCorner;349}350}351}352353void CirclesGridClusterFinder::rectifyPatternPoints(const std::vector<cv::Point2f> &patternPoints, const std::vector<cv::Point2f> &sortedCorners, std::vector<cv::Point2f> &rectifiedPatternPoints)354{355//indices of corner points in pattern356std::vector<Point> trueIndices;357trueIndices.push_back(Point(0, 0));358trueIndices.push_back(Point(patternSize.width - 1, 0));359if(isAsymmetricGrid)360{361trueIndices.push_back(Point(patternSize.width - 1, 1));362trueIndices.push_back(Point(patternSize.width - 1, patternSize.height - 2));363}364trueIndices.push_back(Point(patternSize.width - 1, patternSize.height - 1));365trueIndices.push_back(Point(0, patternSize.height - 1));366367std::vector<Point2f> idealPoints;368for(size_t idx=0; idx<trueIndices.size(); idx++)369{370int i = trueIndices[idx].y;371int j = trueIndices[idx].x;372if(isAsymmetricGrid)373{374idealPoints.push_back(Point2f((2*j + i % 2)*squareSize, i*squareSize));375}376else377{378idealPoints.push_back(Point2f(j*squareSize, i*squareSize));379}380}381382Mat homography = findHomography(Mat(sortedCorners), Mat(idealPoints), 0);383Mat rectifiedPointsMat;384transform(patternPoints, rectifiedPointsMat, homography);385rectifiedPatternPoints.clear();386convertPointsFromHomogeneous(rectifiedPointsMat, rectifiedPatternPoints);387}388389void CirclesGridClusterFinder::parsePatternPoints(const std::vector<cv::Point2f> &patternPoints, const std::vector<cv::Point2f> &rectifiedPatternPoints, std::vector<cv::Point2f> ¢ers)390{391#ifndef HAVE_OPENCV_FLANN392CV_UNUSED(patternPoints);393CV_UNUSED(rectifiedPatternPoints);394CV_UNUSED(centers);395CV_Error(Error::StsNotImplemented, "The desired functionality requires flann module, which was disabled.");396#else397flann::LinearIndexParams flannIndexParams;398flann::Index flannIndex(Mat(rectifiedPatternPoints).reshape(1), flannIndexParams);399400centers.clear();401for( int i = 0; i < patternSize.height; i++ )402{403for( int j = 0; j < patternSize.width; j++ )404{405Point2f idealPt;406if(isAsymmetricGrid)407idealPt = Point2f((2*j + i % 2)*squareSize, i*squareSize);408else409idealPt = Point2f(j*squareSize, i*squareSize);410411Mat query(1, 2, CV_32F, &idealPt);412const int knn = 1;413int indicesbuf[knn] = {0};414float distsbuf[knn] = {0.f};415Mat indices(1, knn, CV_32S, &indicesbuf);416Mat dists(1, knn, CV_32F, &distsbuf);417flannIndex.knnSearch(query, indices, dists, knn, flann::SearchParams());418centers.push_back(patternPoints.at(indicesbuf[0]));419420if(distsbuf[0] > maxRectifiedDistance)421{422#ifdef DEBUG_CIRCLES423std::cout << "Pattern not detected: too large rectified distance" << std::endl;424#endif425centers.clear();426return;427}428}429}430#endif431}432433Graph::Graph(size_t n)434{435for (size_t i = 0; i < n; i++)436{437addVertex(i);438}439}440441bool Graph::doesVertexExist(size_t id) const442{443return (vertices.find(id) != vertices.end());444}445446void Graph::addVertex(size_t id)447{448CV_Assert( !doesVertexExist( id ) );449450vertices.insert(std::pair<size_t, Vertex> (id, Vertex()));451}452453void Graph::addEdge(size_t id1, size_t id2)454{455CV_Assert( doesVertexExist( id1 ) );456CV_Assert( doesVertexExist( id2 ) );457458vertices[id1].neighbors.insert(id2);459vertices[id2].neighbors.insert(id1);460}461462void Graph::removeEdge(size_t id1, size_t id2)463{464CV_Assert( doesVertexExist( id1 ) );465CV_Assert( doesVertexExist( id2 ) );466467vertices[id1].neighbors.erase(id2);468vertices[id2].neighbors.erase(id1);469}470471bool Graph::areVerticesAdjacent(size_t id1, size_t id2) const472{473Vertices::const_iterator it = vertices.find(id1);474CV_Assert(it != vertices.end());475const Neighbors & neighbors = it->second.neighbors;476return neighbors.find(id2) != neighbors.end();477}478479size_t Graph::getVerticesCount() const480{481return vertices.size();482}483484size_t Graph::getDegree(size_t id) const485{486Vertices::const_iterator it = vertices.find(id);487CV_Assert( it != vertices.end() );488return it->second.neighbors.size();489}490491void Graph::floydWarshall(cv::Mat &distanceMatrix, int infinity) const492{493const int edgeWeight = 1;494495const int n = (int)getVerticesCount();496distanceMatrix.create(n, n, CV_32SC1);497distanceMatrix.setTo(infinity);498for (Vertices::const_iterator it1 = vertices.begin(); it1 != vertices.end();++it1)499{500distanceMatrix.at<int> ((int)it1->first, (int)it1->first) = 0;501for (Neighbors::const_iterator it2 = it1->second.neighbors.begin(); it2 != it1->second.neighbors.end();++it2)502{503CV_Assert( it1->first != *it2 );504distanceMatrix.at<int> ((int)it1->first, (int)*it2) = edgeWeight;505}506}507508for (Vertices::const_iterator it1 = vertices.begin(); it1 != vertices.end();++it1)509{510for (Vertices::const_iterator it2 = vertices.begin(); it2 != vertices.end();++it2)511{512for (Vertices::const_iterator it3 = vertices.begin(); it3 != vertices.end();++it3)513{514int i1 = (int)it1->first, i2 = (int)it2->first, i3 = (int)it3->first;515int val1 = distanceMatrix.at<int> (i2, i3);516int val2;517if (distanceMatrix.at<int> (i2, i1) == infinity ||518distanceMatrix.at<int> (i1, i3) == infinity)519val2 = val1;520else521{522val2 = distanceMatrix.at<int> (i2, i1) + distanceMatrix.at<int> (i1, i3);523}524distanceMatrix.at<int> (i2, i3) = (val1 == infinity) ? val2 : std::min(val1, val2);525}526}527}528}529530const Graph::Neighbors& Graph::getNeighbors(size_t id) const531{532Vertices::const_iterator it = vertices.find(id);533CV_Assert( it != vertices.end() );534return it->second.neighbors;535}536537CirclesGridFinder::Segment::Segment(cv::Point2f _s, cv::Point2f _e) :538s(_s), e(_e)539{540}541542void computeShortestPath(Mat &predecessorMatrix, int v1, int v2, std::vector<int> &path);543void computePredecessorMatrix(const Mat &dm, int verticesCount, Mat &predecessorMatrix);544545CirclesGridFinderParameters::CirclesGridFinderParameters()546{547minDensity = 10;548densityNeighborhoodSize = Size2f(16, 16);549minDistanceToAddKeypoint = 20;550kmeansAttempts = 100;551convexHullFactor = 1.1f;552keypointScale = 1;553554minGraphConfidence = 9;555vertexGain = 1;556vertexPenalty = -0.6f;557edgeGain = 1;558edgePenalty = -0.6f;559existingVertexGain = 10000;560561minRNGEdgeSwitchDist = 5.f;562gridType = SYMMETRIC_GRID;563564squareSize = 1.0f;565maxRectifiedDistance = squareSize/2.0f;566}567568CirclesGridFinder::CirclesGridFinder(Size _patternSize, const std::vector<Point2f> &testKeypoints,569const CirclesGridFinderParameters &_parameters) :570patternSize(static_cast<size_t> (_patternSize.width), static_cast<size_t> (_patternSize.height))571{572CV_Assert(_patternSize.height >= 0 && _patternSize.width >= 0);573574keypoints = testKeypoints;575parameters = _parameters;576largeHoles = 0;577smallHoles = 0;578}579580bool CirclesGridFinder::findHoles()581{582switch (parameters.gridType)583{584case CirclesGridFinderParameters::SYMMETRIC_GRID:585{586std::vector<Point2f> vectors, filteredVectors, basis;587Graph rng(0);588computeRNG(rng, vectors);589filterOutliersByDensity(vectors, filteredVectors);590std::vector<Graph> basisGraphs;591findBasis(filteredVectors, basis, basisGraphs);592findMCS(basis, basisGraphs);593break;594}595596case CirclesGridFinderParameters::ASYMMETRIC_GRID:597{598std::vector<Point2f> vectors, tmpVectors, filteredVectors, basis;599Graph rng(0);600computeRNG(rng, tmpVectors);601rng2gridGraph(rng, vectors);602filterOutliersByDensity(vectors, filteredVectors);603std::vector<Graph> basisGraphs;604findBasis(filteredVectors, basis, basisGraphs);605findMCS(basis, basisGraphs);606eraseUsedGraph(basisGraphs);607holes2 = holes;608holes.clear();609findMCS(basis, basisGraphs);610break;611}612613default:614CV_Error(Error::StsBadArg, "Unknown pattern type");615}616return (isDetectionCorrect());617//CV_Error( 0, "Detection is not correct" );618}619620void CirclesGridFinder::rng2gridGraph(Graph &rng, std::vector<cv::Point2f> &vectors) const621{622for (size_t i = 0; i < rng.getVerticesCount(); i++)623{624Graph::Neighbors neighbors1 = rng.getNeighbors(i);625for (Graph::Neighbors::iterator it1 = neighbors1.begin(); it1 != neighbors1.end(); ++it1)626{627Graph::Neighbors neighbors2 = rng.getNeighbors(*it1);628for (Graph::Neighbors::iterator it2 = neighbors2.begin(); it2 != neighbors2.end(); ++it2)629{630if (i < *it2)631{632Point2f vec1 = keypoints[i] - keypoints[*it1];633Point2f vec2 = keypoints[*it1] - keypoints[*it2];634if (norm(vec1 - vec2) < parameters.minRNGEdgeSwitchDist || norm(vec1 + vec2)635< parameters.minRNGEdgeSwitchDist)636continue;637638vectors.push_back(keypoints[i] - keypoints[*it2]);639vectors.push_back(keypoints[*it2] - keypoints[i]);640}641}642}643}644}645646void CirclesGridFinder::eraseUsedGraph(std::vector<Graph> &basisGraphs) const647{648for (size_t i = 0; i < holes.size(); i++)649{650for (size_t j = 0; j < holes[i].size(); j++)651{652for (size_t k = 0; k < basisGraphs.size(); k++)653{654if (i != holes.size() - 1 && basisGraphs[k].areVerticesAdjacent(holes[i][j], holes[i + 1][j]))655{656basisGraphs[k].removeEdge(holes[i][j], holes[i + 1][j]);657}658659if (j != holes[i].size() - 1 && basisGraphs[k].areVerticesAdjacent(holes[i][j], holes[i][j + 1]))660{661basisGraphs[k].removeEdge(holes[i][j], holes[i][j + 1]);662}663}664}665}666}667668bool CirclesGridFinder::isDetectionCorrect()669{670switch (parameters.gridType)671{672case CirclesGridFinderParameters::SYMMETRIC_GRID:673{674if (holes.size() != patternSize.height)675return false;676677std::set<size_t> vertices;678for (size_t i = 0; i < holes.size(); i++)679{680if (holes[i].size() != patternSize.width)681return false;682683for (size_t j = 0; j < holes[i].size(); j++)684{685vertices.insert(holes[i][j]);686}687}688689return vertices.size() == patternSize.area();690}691692case CirclesGridFinderParameters::ASYMMETRIC_GRID:693{694if (holes.size() < holes2.size() || holes[0].size() < holes2[0].size())695{696largeHoles = &holes2;697smallHoles = &holes;698}699else700{701largeHoles = &holes;702smallHoles = &holes2;703}704705size_t largeWidth = patternSize.width;706size_t largeHeight = (size_t)ceil(patternSize.height / 2.);707size_t smallWidth = patternSize.width;708size_t smallHeight = (size_t)floor(patternSize.height / 2.);709710size_t sw = smallWidth, sh = smallHeight, lw = largeWidth, lh = largeHeight;711if (largeHoles->size() != largeHeight)712{713std::swap(lh, lw);714}715if (smallHoles->size() != smallHeight)716{717std::swap(sh, sw);718}719720if (largeHoles->size() != lh || smallHoles->size() != sh)721{722return false;723}724725std::set<size_t> vertices;726for (size_t i = 0; i < largeHoles->size(); i++)727{728if (largeHoles->at(i).size() != lw)729{730return false;731}732733for (size_t j = 0; j < largeHoles->at(i).size(); j++)734{735vertices.insert(largeHoles->at(i)[j]);736}737738if (i < smallHoles->size())739{740if (smallHoles->at(i).size() != sw)741{742return false;743}744745for (size_t j = 0; j < smallHoles->at(i).size(); j++)746{747vertices.insert(smallHoles->at(i)[j]);748}749}750}751return (vertices.size() == largeHeight * largeWidth + smallHeight * smallWidth);752}753}754CV_Error(Error::StsBadArg, "Unknown pattern type");755}756757void CirclesGridFinder::findMCS(const std::vector<Point2f> &basis, std::vector<Graph> &basisGraphs)758{759holes.clear();760Path longestPath;761size_t bestGraphIdx = findLongestPath(basisGraphs, longestPath);762std::vector<size_t> holesRow = longestPath.vertices;763764while (holesRow.size() > std::max(patternSize.width, patternSize.height))765{766holesRow.pop_back();767holesRow.erase(holesRow.begin());768}769770if (bestGraphIdx == 0)771{772holes.push_back(holesRow);773size_t w = holes[0].size();774size_t h = holes.size();775776//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;779parameters.minGraphConfidence = holes[0].size() * parameters.existingVertexGain;780for (size_t i = h; i < patternSize.height; i++)781{782addHolesByGraph(basisGraphs, true, basis[1]);783}784785//parameters.minGraphConfidence = holes.size() * parameters.existingVertexGain + (holes.size() / 2) * parameters.edgeGain;786parameters.minGraphConfidence = holes.size() * parameters.existingVertexGain;787788for (size_t i = w; i < patternSize.width; i++)789{790addHolesByGraph(basisGraphs, false, basis[0]);791}792}793else794{795holes.resize(holesRow.size());796for (size_t i = 0; i < holesRow.size(); i++)797holes[i].push_back(holesRow[i]);798799size_t w = holes[0].size();800size_t h = holes.size();801802parameters.minGraphConfidence = holes.size() * parameters.existingVertexGain;803for (size_t i = w; i < patternSize.width; i++)804{805addHolesByGraph(basisGraphs, false, basis[0]);806}807808parameters.minGraphConfidence = holes[0].size() * parameters.existingVertexGain;809for (size_t i = h; i < patternSize.height; i++)810{811addHolesByGraph(basisGraphs, true, basis[1]);812}813}814}815816Mat CirclesGridFinder::rectifyGrid(Size detectedGridSize, const std::vector<Point2f>& centers,817const std::vector<Point2f> &keypoints, std::vector<Point2f> &warpedKeypoints)818{819CV_Assert( !centers.empty() );820const float edgeLength = 30;821const Point2f offset(150, 150);822823std::vector<Point2f> dstPoints;824bool isClockwiseBefore =825getDirection(centers[0], centers[detectedGridSize.width - 1], centers[centers.size() - 1]) < 0;826827int iStart = isClockwiseBefore ? 0 : detectedGridSize.height - 1;828int iEnd = isClockwiseBefore ? detectedGridSize.height : -1;829int iStep = isClockwiseBefore ? 1 : -1;830for (int i = iStart; i != iEnd; i += iStep)831{832for (int j = 0; j < detectedGridSize.width; j++)833{834dstPoints.push_back(offset + Point2f(edgeLength * j, edgeLength * i));835}836}837838Mat H = findHomography(Mat(centers), Mat(dstPoints), RANSAC);839//Mat H = findHomography( Mat( corners ), Mat( dstPoints ) );840841if (H.empty())842{843H = Mat::zeros(3, 3, CV_64FC1);844warpedKeypoints.clear();845return H;846}847848std::vector<Point2f> srcKeypoints;849for (size_t i = 0; i < keypoints.size(); i++)850{851srcKeypoints.push_back(keypoints[i]);852}853854Mat dstKeypointsMat;855transform(Mat(srcKeypoints), dstKeypointsMat, H);856std::vector<Point2f> dstKeypoints;857convertPointsFromHomogeneous(dstKeypointsMat, dstKeypoints);858859warpedKeypoints.clear();860for (size_t i = 0; i < dstKeypoints.size(); i++)861{862Point2f pt = dstKeypoints[i];863warpedKeypoints.push_back(pt);864}865866return H;867}868869size_t CirclesGridFinder::findNearestKeypoint(Point2f pt) const870{871size_t bestIdx = 0;872double minDist = std::numeric_limits<double>::max();873for (size_t i = 0; i < keypoints.size(); i++)874{875double dist = norm(pt - keypoints[i]);876if (dist < minDist)877{878minDist = dist;879bestIdx = i;880}881}882return bestIdx;883}884885void CirclesGridFinder::addPoint(Point2f pt, std::vector<size_t> &points)886{887size_t ptIdx = findNearestKeypoint(pt);888if (norm(keypoints[ptIdx] - pt) > parameters.minDistanceToAddKeypoint)889{890Point2f kpt = Point2f(pt);891keypoints.push_back(kpt);892points.push_back(keypoints.size() - 1);893}894else895{896points.push_back(ptIdx);897}898}899900void CirclesGridFinder::findCandidateLine(std::vector<size_t> &line, size_t seedLineIdx, bool addRow, Point2f basisVec,901std::vector<size_t> &seeds)902{903line.clear();904seeds.clear();905906if (addRow)907{908for (size_t i = 0; i < holes[seedLineIdx].size(); i++)909{910Point2f pt = keypoints[holes[seedLineIdx][i]] + basisVec;911addPoint(pt, line);912seeds.push_back(holes[seedLineIdx][i]);913}914}915else916{917for (size_t i = 0; i < holes.size(); i++)918{919Point2f pt = keypoints[holes[i][seedLineIdx]] + basisVec;920addPoint(pt, line);921seeds.push_back(holes[i][seedLineIdx]);922}923}924925CV_Assert( line.size() == seeds.size() );926}927928void CirclesGridFinder::findCandidateHoles(std::vector<size_t> &above, std::vector<size_t> &below, bool addRow, Point2f basisVec,929std::vector<size_t> &aboveSeeds, std::vector<size_t> &belowSeeds)930{931above.clear();932below.clear();933aboveSeeds.clear();934belowSeeds.clear();935936findCandidateLine(above, 0, addRow, -basisVec, aboveSeeds);937size_t lastIdx = addRow ? holes.size() - 1 : holes[0].size() - 1;938findCandidateLine(below, lastIdx, addRow, basisVec, belowSeeds);939940CV_Assert( below.size() == above.size() );941CV_Assert( belowSeeds.size() == aboveSeeds.size() );942CV_Assert( below.size() == belowSeeds.size() );943}944945bool CirclesGridFinder::areCentersNew(const std::vector<size_t> &newCenters, const std::vector<std::vector<size_t> > &holes)946{947for (size_t i = 0; i < newCenters.size(); i++)948{949for (size_t j = 0; j < holes.size(); j++)950{951if (holes[j].end() != std::find(holes[j].begin(), holes[j].end(), newCenters[i]))952{953return false;954}955}956}957958return true;959}960961void CirclesGridFinder::insertWinner(float aboveConfidence, float belowConfidence, float minConfidence, bool addRow,962const std::vector<size_t> &above, const std::vector<size_t> &below,963std::vector<std::vector<size_t> > &holes)964{965if (aboveConfidence < minConfidence && belowConfidence < minConfidence)966return;967968if (addRow)969{970if (aboveConfidence >= belowConfidence)971{972if (!areCentersNew(above, holes))973CV_Error( 0, "Centers are not new" );974975holes.insert(holes.begin(), above);976}977else978{979if (!areCentersNew(below, holes))980CV_Error( 0, "Centers are not new" );981982holes.insert(holes.end(), below);983}984}985else986{987if (aboveConfidence >= belowConfidence)988{989if (!areCentersNew(above, holes))990CV_Error( 0, "Centers are not new" );991992for (size_t i = 0; i < holes.size(); i++)993{994holes[i].insert(holes[i].begin(), above[i]);995}996}997else998{999if (!areCentersNew(below, holes))1000CV_Error( 0, "Centers are not new" );10011002for (size_t i = 0; i < holes.size(); i++)1003{1004holes[i].insert(holes[i].end(), below[i]);1005}1006}1007}1008}10091010float CirclesGridFinder::computeGraphConfidence(const std::vector<Graph> &basisGraphs, bool addRow,1011const std::vector<size_t> &points, const std::vector<size_t> &seeds)1012{1013CV_Assert( points.size() == seeds.size() );1014float confidence = 0;1015const size_t vCount = basisGraphs[0].getVerticesCount();1016CV_Assert( basisGraphs[0].getVerticesCount() == basisGraphs[1].getVerticesCount() );10171018for (size_t i = 0; i < seeds.size(); i++)1019{1020if (seeds[i] < vCount && points[i] < vCount)1021{1022if (!basisGraphs[addRow].areVerticesAdjacent(seeds[i], points[i]))1023{1024confidence += parameters.vertexPenalty;1025}1026else1027{1028confidence += parameters.vertexGain;1029}1030}10311032if (points[i] < vCount)1033{1034confidence += parameters.existingVertexGain;1035}1036}10371038for (size_t i = 1; i < points.size(); i++)1039{1040if (points[i - 1] < vCount && points[i] < vCount)1041{1042if (!basisGraphs[!addRow].areVerticesAdjacent(points[i - 1], points[i]))1043{1044confidence += parameters.edgePenalty;1045}1046else1047{1048confidence += parameters.edgeGain;1049}1050}1051}1052return confidence;10531054}10551056void CirclesGridFinder::addHolesByGraph(const std::vector<Graph> &basisGraphs, bool addRow, Point2f basisVec)1057{1058std::vector<size_t> above, below, aboveSeeds, belowSeeds;1059findCandidateHoles(above, below, addRow, basisVec, aboveSeeds, belowSeeds);1060float aboveConfidence = computeGraphConfidence(basisGraphs, addRow, above, aboveSeeds);1061float belowConfidence = computeGraphConfidence(basisGraphs, addRow, below, belowSeeds);10621063insertWinner(aboveConfidence, belowConfidence, parameters.minGraphConfidence, addRow, above, below, holes);1064}10651066void CirclesGridFinder::filterOutliersByDensity(const std::vector<Point2f> &samples, std::vector<Point2f> &filteredSamples)1067{1068if (samples.empty())1069CV_Error( 0, "samples is empty" );10701071filteredSamples.clear();10721073for (size_t i = 0; i < samples.size(); i++)1074{1075Rect_<float> rect(samples[i] - Point2f(parameters.densityNeighborhoodSize) * 0.5,1076parameters.densityNeighborhoodSize);1077int neighborsCount = 0;1078for (size_t j = 0; j < samples.size(); j++)1079{1080if (rect.contains(samples[j]))1081neighborsCount++;1082}1083if (neighborsCount >= parameters.minDensity)1084filteredSamples.push_back(samples[i]);1085}10861087if (filteredSamples.empty())1088CV_Error( 0, "filteredSamples is empty" );1089}10901091void CirclesGridFinder::findBasis(const std::vector<Point2f> &samples, std::vector<Point2f> &basis, std::vector<Graph> &basisGraphs)1092{1093basis.clear();1094Mat bestLabels;1095TermCriteria termCriteria;1096Mat centers;1097const int clustersCount = 4;1098kmeans(Mat(samples).reshape(1, 0), clustersCount, bestLabels, termCriteria, parameters.kmeansAttempts,1099KMEANS_RANDOM_CENTERS, centers);1100CV_Assert( centers.type() == CV_32FC1 );11011102std::vector<int> basisIndices;1103//TODO: only remove duplicate1104for (int i = 0; i < clustersCount; i++)1105{1106int maxIdx = (fabs(centers.at<float> (i, 0)) < fabs(centers.at<float> (i, 1)));1107if (centers.at<float> (i, maxIdx) > 0)1108{1109Point2f vec(centers.at<float> (i, 0), centers.at<float> (i, 1));1110basis.push_back(vec);1111basisIndices.push_back(i);1112}1113}1114if (basis.size() != 2)1115CV_Error(0, "Basis size is not 2");11161117if (basis[1].x > basis[0].x)1118{1119std::swap(basis[0], basis[1]);1120std::swap(basisIndices[0], basisIndices[1]);1121}11221123const float minBasisDif = 2;1124if (norm(basis[0] - basis[1]) < minBasisDif)1125CV_Error(0, "degenerate basis" );11261127std::vector<std::vector<Point2f> > clusters(2), hulls(2);1128for (int k = 0; k < (int)samples.size(); k++)1129{1130int label = bestLabels.at<int> (k, 0);1131int idx = -1;1132if (label == basisIndices[0])1133idx = 0;1134if (label == basisIndices[1])1135idx = 1;1136if (idx >= 0)1137{1138clusters[idx].push_back(basis[idx] + parameters.convexHullFactor * (samples[k] - basis[idx]));1139}1140}1141for (size_t i = 0; i < basis.size(); i++)1142{1143convexHull(Mat(clusters[i]), hulls[i]);1144}11451146basisGraphs.resize(basis.size(), Graph(keypoints.size()));1147for (size_t i = 0; i < keypoints.size(); i++)1148{1149for (size_t j = 0; j < keypoints.size(); j++)1150{1151if (i == j)1152continue;11531154Point2f vec = keypoints[i] - keypoints[j];11551156for (size_t k = 0; k < hulls.size(); k++)1157{1158if (pointPolygonTest(Mat(hulls[k]), vec, false) >= 0)1159{1160basisGraphs[k].addEdge(i, j);1161}1162}1163}1164}1165if (basisGraphs.size() != 2)1166CV_Error(0, "Number of basis graphs is not 2");1167}11681169void CirclesGridFinder::computeRNG(Graph &rng, std::vector<cv::Point2f> &vectors, Mat *drawImage) const1170{1171rng = Graph(keypoints.size());1172vectors.clear();11731174//TODO: use more fast algorithm instead of naive N^31175for (size_t i = 0; i < keypoints.size(); i++)1176{1177for (size_t j = 0; j < keypoints.size(); j++)1178{1179if (i == j)1180continue;11811182Point2f vec = keypoints[i] - keypoints[j];1183double dist = norm(vec);11841185bool isNeighbors = true;1186for (size_t k = 0; k < keypoints.size(); k++)1187{1188if (k == i || k == j)1189continue;11901191double dist1 = norm(keypoints[i] - keypoints[k]);1192double dist2 = norm(keypoints[j] - keypoints[k]);1193if (dist1 < dist && dist2 < dist)1194{1195isNeighbors = false;1196break;1197}1198}11991200if (isNeighbors)1201{1202rng.addEdge(i, j);1203vectors.push_back(keypoints[i] - keypoints[j]);1204if (drawImage != 0)1205{1206line(*drawImage, keypoints[i], keypoints[j], Scalar(255, 0, 0), 2);1207circle(*drawImage, keypoints[i], 3, Scalar(0, 0, 255), -1);1208circle(*drawImage, keypoints[j], 3, Scalar(0, 0, 255), -1);1209}1210}1211}1212}1213}12141215void computePredecessorMatrix(const Mat &dm, int verticesCount, Mat &predecessorMatrix)1216{1217CV_Assert( dm.type() == CV_32SC1 );1218predecessorMatrix.create(verticesCount, verticesCount, CV_32SC1);1219predecessorMatrix = -1;1220for (int i = 0; i < predecessorMatrix.rows; i++)1221{1222for (int j = 0; j < predecessorMatrix.cols; j++)1223{1224int dist = dm.at<int> (i, j);1225for (int k = 0; k < verticesCount; k++)1226{1227if (dm.at<int> (i, k) == dist - 1 && dm.at<int> (k, j) == 1)1228{1229predecessorMatrix.at<int> (i, j) = k;1230break;1231}1232}1233}1234}1235}12361237static void computeShortestPath(Mat &predecessorMatrix, size_t v1, size_t v2, std::vector<size_t> &path)1238{1239if (predecessorMatrix.at<int> ((int)v1, (int)v2) < 0)1240{1241path.push_back(v1);1242return;1243}12441245computeShortestPath(predecessorMatrix, v1, predecessorMatrix.at<int> ((int)v1, (int)v2), path);1246path.push_back(v2);1247}12481249size_t CirclesGridFinder::findLongestPath(std::vector<Graph> &basisGraphs, Path &bestPath)1250{1251std::vector<Path> longestPaths(1);1252std::vector<int> confidences;12531254size_t bestGraphIdx = 0;1255const int infinity = -1;1256for (size_t graphIdx = 0; graphIdx < basisGraphs.size(); graphIdx++)1257{1258const Graph &g = basisGraphs[graphIdx];1259Mat distanceMatrix;1260g.floydWarshall(distanceMatrix, infinity);1261Mat predecessorMatrix;1262computePredecessorMatrix(distanceMatrix, (int)g.getVerticesCount(), predecessorMatrix);12631264double maxVal;1265Point maxLoc;1266minMaxLoc(distanceMatrix, 0, &maxVal, 0, &maxLoc);12671268if (maxVal > longestPaths[0].length)1269{1270longestPaths.clear();1271confidences.clear();1272bestGraphIdx = graphIdx;1273}1274if (longestPaths.empty() || (maxVal == longestPaths[0].length && graphIdx == bestGraphIdx))1275{1276Path path = Path(maxLoc.x, maxLoc.y, cvRound(maxVal));1277CV_Assert(maxLoc.x >= 0 && maxLoc.y >= 0)1278;1279size_t id1 = static_cast<size_t> (maxLoc.x);1280size_t id2 = static_cast<size_t> (maxLoc.y);1281computeShortestPath(predecessorMatrix, id1, id2, path.vertices);1282longestPaths.push_back(path);12831284int conf = 0;1285for (int v2 = 0; v2 < (int)path.vertices.size(); v2++)1286{1287conf += (int)basisGraphs[1 - (int)graphIdx].getDegree(v2);1288}1289confidences.push_back(conf);1290}1291}1292//if( bestGraphIdx != 0 )1293//CV_Error( 0, "" );12941295int maxConf = -1;1296int bestPathIdx = -1;1297for (int i = 0; i < (int)confidences.size(); i++)1298{1299if (confidences[i] > maxConf)1300{1301maxConf = confidences[i];1302bestPathIdx = i;1303}1304}13051306//int bestPathIdx = rand() % longestPaths.size();1307bestPath = longestPaths.at(bestPathIdx);1308bool needReverse = (bestGraphIdx == 0 && keypoints[bestPath.lastVertex].x < keypoints[bestPath.firstVertex].x)1309|| (bestGraphIdx == 1 && keypoints[bestPath.lastVertex].y < keypoints[bestPath.firstVertex].y);1310if (needReverse)1311{1312std::swap(bestPath.lastVertex, bestPath.firstVertex);1313std::reverse(bestPath.vertices.begin(), bestPath.vertices.end());1314}1315return bestGraphIdx;1316}13171318void CirclesGridFinder::drawBasis(const std::vector<Point2f> &basis, Point2f origin, Mat &drawImg) const1319{1320for (size_t i = 0; i < basis.size(); i++)1321{1322Point2f pt(basis[i]);1323line(drawImg, origin, origin + pt, Scalar(0, (double)(i * 255), 0), 2);1324}1325}13261327void CirclesGridFinder::drawBasisGraphs(const std::vector<Graph> &basisGraphs, Mat &drawImage, bool drawEdges,1328bool drawVertices) const1329{1330//const int vertexRadius = 1;1331const int vertexRadius = 3;1332const Scalar vertexColor = Scalar(0, 0, 255);1333const int vertexThickness = -1;13341335const Scalar edgeColor = Scalar(255, 0, 0);1336//const int edgeThickness = 1;1337const int edgeThickness = 2;13381339if (drawEdges)1340{1341for (size_t i = 0; i < basisGraphs.size(); i++)1342{1343for (size_t v1 = 0; v1 < basisGraphs[i].getVerticesCount(); v1++)1344{1345for (size_t v2 = 0; v2 < basisGraphs[i].getVerticesCount(); v2++)1346{1347if (basisGraphs[i].areVerticesAdjacent(v1, v2))1348{1349line(drawImage, keypoints[v1], keypoints[v2], edgeColor, edgeThickness);1350}1351}1352}1353}1354}1355if (drawVertices)1356{1357for (size_t v = 0; v < basisGraphs[0].getVerticesCount(); v++)1358{1359circle(drawImage, keypoints[v], vertexRadius, vertexColor, vertexThickness);1360}1361}1362}13631364void CirclesGridFinder::drawHoles(const Mat &srcImage, Mat &drawImage) const1365{1366//const int holeRadius = 4;1367//const int holeRadius = 2;1368//const int holeThickness = 1;1369const int holeRadius = 3;1370const int holeThickness = -1;13711372//const Scalar holeColor = Scalar(0, 0, 255);1373const Scalar holeColor = Scalar(0, 255, 0);13741375if (srcImage.channels() == 1)1376cvtColor(srcImage, drawImage, COLOR_GRAY2RGB);1377else1378srcImage.copyTo(drawImage);13791380for (size_t i = 0; i < holes.size(); i++)1381{1382for (size_t j = 0; j < holes[i].size(); j++)1383{1384if (j != holes[i].size() - 1)1385line(drawImage, keypoints[holes[i][j]], keypoints[holes[i][j + 1]], Scalar(255, 0, 0), 2);1386if (i != holes.size() - 1)1387line(drawImage, keypoints[holes[i][j]], keypoints[holes[i + 1][j]], Scalar(255, 0, 0), 2);13881389//circle(drawImage, keypoints[holes[i][j]], holeRadius, holeColor, holeThickness);1390circle(drawImage, keypoints[holes[i][j]], holeRadius, holeColor, holeThickness);1391}1392}1393}13941395Size CirclesGridFinder::getDetectedGridSize() const1396{1397if (holes.size() == 0)1398return Size(0, 0);13991400return Size((int)holes[0].size(), (int)holes.size());1401}14021403void CirclesGridFinder::getHoles(std::vector<Point2f> &outHoles) const1404{1405outHoles.clear();14061407for (size_t i = 0; i < holes.size(); i++)1408{1409for (size_t j = 0; j < holes[i].size(); j++)1410{1411outHoles.push_back(keypoints[holes[i][j]]);1412}1413}1414}14151416static bool areIndicesCorrect(Point pos, std::vector<std::vector<size_t> > *points)1417{1418if (pos.y < 0 || pos.x < 0)1419return false;1420return (static_cast<size_t> (pos.y) < points->size() && static_cast<size_t> (pos.x) < points->at(pos.y).size());1421}14221423void CirclesGridFinder::getAsymmetricHoles(std::vector<cv::Point2f> &outHoles) const1424{1425outHoles.clear();14261427std::vector<Point> largeCornerIndices, smallCornerIndices;1428std::vector<Point> firstSteps, secondSteps;1429size_t cornerIdx = getFirstCorner(largeCornerIndices, smallCornerIndices, firstSteps, secondSteps);1430CV_Assert(largeHoles != 0 && smallHoles != 0)1431;14321433Point srcLargePos = largeCornerIndices[cornerIdx];1434Point srcSmallPos = smallCornerIndices[cornerIdx];14351436while (areIndicesCorrect(srcLargePos, largeHoles) || areIndicesCorrect(srcSmallPos, smallHoles))1437{1438Point largePos = srcLargePos;1439while (areIndicesCorrect(largePos, largeHoles))1440{1441outHoles.push_back(keypoints[largeHoles->at(largePos.y)[largePos.x]]);1442largePos += firstSteps[cornerIdx];1443}1444srcLargePos += secondSteps[cornerIdx];14451446Point smallPos = srcSmallPos;1447while (areIndicesCorrect(smallPos, smallHoles))1448{1449outHoles.push_back(keypoints[smallHoles->at(smallPos.y)[smallPos.x]]);1450smallPos += firstSteps[cornerIdx];1451}1452srcSmallPos += secondSteps[cornerIdx];1453}1454}14551456double CirclesGridFinder::getDirection(Point2f p1, Point2f p2, Point2f p3)1457{1458Point2f a = p3 - p1;1459Point2f b = p2 - p1;1460return a.x * b.y - a.y * b.x;1461}14621463bool CirclesGridFinder::areSegmentsIntersecting(Segment seg1, Segment seg2)1464{1465bool doesStraddle1 = (getDirection(seg2.s, seg2.e, seg1.s) * getDirection(seg2.s, seg2.e, seg1.e)) < 0;1466bool doesStraddle2 = (getDirection(seg1.s, seg1.e, seg2.s) * getDirection(seg1.s, seg1.e, seg2.e)) < 0;1467return doesStraddle1 && doesStraddle2;14681469/*1470Point2f t1 = e1-s1;1471Point2f n1(t1.y, -t1.x);1472double c1 = -n1.ddot(s1);14731474Point2f t2 = e2-s2;1475Point2f n2(t2.y, -t2.x);1476double c2 = -n2.ddot(s2);14771478bool seg1 = ((n1.ddot(s2) + c1) * (n1.ddot(e2) + c1)) <= 0;1479bool seg1 = ((n2.ddot(s1) + c2) * (n2.ddot(e1) + c2)) <= 0;14801481return seg1 && seg2;1482*/1483}14841485void CirclesGridFinder::getCornerSegments(const std::vector<std::vector<size_t> > &points, std::vector<std::vector<Segment> > &segments,1486std::vector<Point> &cornerIndices, std::vector<Point> &firstSteps,1487std::vector<Point> &secondSteps) const1488{1489segments.clear();1490cornerIndices.clear();1491firstSteps.clear();1492secondSteps.clear();1493int h = (int)points.size();1494int w = (int)points[0].size();1495CV_Assert(h >= 2 && w >= 2)1496;14971498//all 8 segments with one end in a corner1499std::vector<Segment> corner;1500corner.push_back(Segment(keypoints[points[1][0]], keypoints[points[0][0]]));1501corner.push_back(Segment(keypoints[points[0][0]], keypoints[points[0][1]]));1502segments.push_back(corner);1503cornerIndices.push_back(Point(0, 0));1504firstSteps.push_back(Point(1, 0));1505secondSteps.push_back(Point(0, 1));1506corner.clear();15071508corner.push_back(Segment(keypoints[points[0][w - 2]], keypoints[points[0][w - 1]]));1509corner.push_back(Segment(keypoints[points[0][w - 1]], keypoints[points[1][w - 1]]));1510segments.push_back(corner);1511cornerIndices.push_back(Point(w - 1, 0));1512firstSteps.push_back(Point(0, 1));1513secondSteps.push_back(Point(-1, 0));1514corner.clear();15151516corner.push_back(Segment(keypoints[points[h - 2][w - 1]], keypoints[points[h - 1][w - 1]]));1517corner.push_back(Segment(keypoints[points[h - 1][w - 1]], keypoints[points[h - 1][w - 2]]));1518segments.push_back(corner);1519cornerIndices.push_back(Point(w - 1, h - 1));1520firstSteps.push_back(Point(-1, 0));1521secondSteps.push_back(Point(0, -1));1522corner.clear();15231524corner.push_back(Segment(keypoints[points[h - 1][1]], keypoints[points[h - 1][0]]));1525corner.push_back(Segment(keypoints[points[h - 1][0]], keypoints[points[h - 2][0]]));1526cornerIndices.push_back(Point(0, h - 1));1527firstSteps.push_back(Point(0, -1));1528secondSteps.push_back(Point(1, 0));1529segments.push_back(corner);1530corner.clear();15311532//y axis is inverted in computer vision so we check < 01533bool isClockwise =1534getDirection(keypoints[points[0][0]], keypoints[points[0][w - 1]], keypoints[points[h - 1][w - 1]]) < 0;1535if (!isClockwise)1536{1537#ifdef DEBUG_CIRCLES1538std::cout << "Corners are counterclockwise" << std::endl;1539#endif1540std::reverse(segments.begin(), segments.end());1541std::reverse(cornerIndices.begin(), cornerIndices.end());1542std::reverse(firstSteps.begin(), firstSteps.end());1543std::reverse(secondSteps.begin(), secondSteps.end());1544std::swap(firstSteps, secondSteps);1545}1546}15471548bool CirclesGridFinder::doesIntersectionExist(const std::vector<Segment> &corner, const std::vector<std::vector<Segment> > &segments)1549{1550for (size_t i = 0; i < corner.size(); i++)1551{1552for (size_t j = 0; j < segments.size(); j++)1553{1554for (size_t k = 0; k < segments[j].size(); k++)1555{1556if (areSegmentsIntersecting(corner[i], segments[j][k]))1557return true;1558}1559}1560}15611562return false;1563}15641565size_t CirclesGridFinder::getFirstCorner(std::vector<Point> &largeCornerIndices, std::vector<Point> &smallCornerIndices, std::vector<1566Point> &firstSteps, std::vector<Point> &secondSteps) const1567{1568std::vector<std::vector<Segment> > largeSegments;1569std::vector<std::vector<Segment> > smallSegments;15701571getCornerSegments(*largeHoles, largeSegments, largeCornerIndices, firstSteps, secondSteps);1572getCornerSegments(*smallHoles, smallSegments, smallCornerIndices, firstSteps, secondSteps);15731574const size_t cornersCount = 4;1575CV_Assert(largeSegments.size() == cornersCount)1576;15771578bool isInsider[cornersCount];15791580for (size_t i = 0; i < cornersCount; i++)1581{1582isInsider[i] = doesIntersectionExist(largeSegments[i], smallSegments);1583}15841585int cornerIdx = 0;1586bool waitOutsider = true;15871588for(;;)1589{1590if (waitOutsider)1591{1592if (!isInsider[(cornerIdx + 1) % cornersCount])1593waitOutsider = false;1594}1595else1596{1597if (isInsider[(cornerIdx + 1) % cornersCount])1598break;1599}16001601cornerIdx = (cornerIdx + 1) % cornersCount;1602}16031604return cornerIdx;1605}160616071608