Improved robustness of the findCirclesGrid() to affine distortions.
authorIlya Lysenkov <no@email>
Tue, 20 Sep 2011 12:47:24 +0000 (12:47 +0000)
committerIlya Lysenkov <no@email>
Tue, 20 Sep 2011 12:47:24 +0000 (12:47 +0000)
modules/calib3d/src/circlesgrid.cpp

index 952b8f5..405e409 100644 (file)
 using namespace cv;
 using namespace std;
 
+#ifdef DEBUG_CIRCLES
+void drawPoints(const vector<Point2f> &points, Mat &outImage, int radius = 2,  Scalar color = Scalar::all(255), int thickness = -1)
+{
+  for(size_t i=0; i<points.size(); i++)
+  {
+    circle(outImage, points[i], radius, color, thickness);
+  }
+}
+#endif
+
 void CirclesGridClusterFinder::hierarchicalClustering(const vector<Point2f> points, const Size &patternSize, vector<Point2f> &patternPoints)
 {
 #ifdef HAVE_TEGRA_OPTIMIZATION
@@ -106,6 +116,12 @@ void CirclesGridClusterFinder::hierarchicalClustering(const vector<Point2f> poin
         patternClusterIdx = minIdx;
     }
 
+    //the largest cluster can have more than pn points -- we need to filter out such situations
+    if(clusters[patternClusterIdx].size() != static_cast<size_t>(patternSize.area()))
+    {
+      return;
+    }
+
     patternPoints.reserve(clusters[patternClusterIdx].size());
     for(std::list<size_t>::iterator it = clusters[patternClusterIdx].begin(); it != clusters[patternClusterIdx].end(); it++)
     {
@@ -129,6 +145,12 @@ void CirclesGridClusterFinder::findGrid(const std::vector<cv::Point2f> points, c
     return;
   }
 
+#ifdef DEBUG_CIRCLES
+  Mat patternPointsImage(1024, 1248, CV_8UC1, Scalar(0));
+  drawPoints(patternPoints, patternPointsImage);
+  imshow("pattern points", patternPointsImage);
+#endif
+
   vector<Point2f> hull2f;
   convexHull(Mat(patternPoints), hull2f, false);
   const size_t cornersCount = isAsymmetricGrid ? 6 : 4;
@@ -178,60 +200,93 @@ void CirclesGridClusterFinder::findCorners(const std::vector<cv::Point2f> &hull2
   Mat sortedIndices;
   sortIdx(anglesMat, sortedIndices, CV_SORT_EVERY_COLUMN + CV_SORT_DESCENDING);
   CV_Assert(sortedIndices.type() == CV_32SC1);
+  CV_Assert(sortedIndices.cols == 1);
   const int cornersCount = isAsymmetricGrid ? 6 : 4;
+  Mat cornersIndices;
+  cv::sort(sortedIndices.rowRange(0, cornersCount), cornersIndices, CV_SORT_EVERY_COLUMN + CV_SORT_ASCENDING);
   corners.clear();
   for(int i=0; i<cornersCount; i++)
   {
-    corners.push_back(hull2f[sortedIndices.at<int>(i, 0)]);
+    corners.push_back(hull2f[cornersIndices.at<int>(i, 0)]);
   }
 }
 
 void CirclesGridClusterFinder::findOutsideCorners(const std::vector<cv::Point2f> &corners, std::vector<cv::Point2f> &outsideCorners)
 {
+  outsideCorners.clear();
   //find two pairs of the most nearest corners
   double min1 = std::numeric_limits<double>::max();
   double min2 = std::numeric_limits<double>::max();
   Point minLoc1, minLoc2;
   int i, j, n = (int)corners.size();
 
+#ifdef DEBUG_CIRCLES
+  Mat cornersImage(1024, 1248, CV_8UC1, Scalar(0));
+  drawPoints(corners, cornersImage);
+  imshow("corners", cornersImage);
+#endif
+
+  vector<Point2f> tangentVectors(corners.size());
+  for(size_t k=0; k<=corners.size(); k++)
+  {
+    Point2f diff = corners[(k + 1) % corners.size()] - corners[k];
+    tangentVectors[k] = diff * (1.0f / norm(diff));
+  }
+
+  //compute angles between all sides
+  Mat cosAngles(n, n, CV_32FC1, 0.0f);
   for(i = 0; i < n; i++)
   {
-    for(j = i+1; j < n; j++)
+    for(j = i + 1; j < n; j++)
     {
-      double dist = norm(corners[i] - corners[j]);
-      Point loc(j, i);
-      if(dist < min1)
-      {
-        min2 = min1;
-        minLoc2 = minLoc1;
-        min1 = dist;
-        minLoc1 = loc;
-      }
-      else
-      {
-        if(dist < min2)
-        {
-          min2 = dist;
-          minLoc2 = loc;
-        }
-      }
+      float val = fabs(tangentVectors[i].dot(tangentVectors[j]));
+      cosAngles.at<float>(i, j) = val;
+      cosAngles.at<float>(j, i) = val;
     }
   }
-  std::set<int> outsideCornersIndices;
-  for(i = 0; i < n; i++)
+
+  //find two parallel sides to which outside corners belong
+  Point maxLoc;
+  minMaxLoc(cosAngles, 0, 0, 0, &maxLoc);
+  const int diffBetweenFalseLines = 3;
+  if(abs(maxLoc.x - maxLoc.y) == diffBetweenFalseLines)
   {
-    outsideCornersIndices.insert(i);
+    cosAngles.row(maxLoc.x).setTo(0.0f);
+    cosAngles.col(maxLoc.x).setTo(0.0f);
+    cosAngles.row(maxLoc.y).setTo(0.0f);
+    cosAngles.col(maxLoc.y).setTo(0.0f);
+    minMaxLoc(cosAngles, 0, 0, 0, &maxLoc);
   }
-  outsideCornersIndices.erase(minLoc1.x);
-  outsideCornersIndices.erase(minLoc1.y);
-  outsideCornersIndices.erase(minLoc2.x);
-  outsideCornersIndices.erase(minLoc2.y);
 
-  outsideCorners.clear();
-  for(std::set<int>::iterator it = outsideCornersIndices.begin(); it != outsideCornersIndices.end(); it++)
+#ifdef DEBUG_CIRCLES
+  Mat linesImage(1024, 1248, CV_8UC1, Scalar(0));
+  line(linesImage, corners[maxLoc.y], corners[(maxLoc.y + 1) % n], Scalar(255));
+  line(linesImage, corners[maxLoc.x], corners[(maxLoc.x + 1) % n], Scalar(255));
+  imshow("lines", linesImage);
+#endif
+
+  int maxIdx = std::max(maxLoc.x, maxLoc.y);
+  int minIdx = std::min(maxLoc.x, maxLoc.y);
+  const int bigDiff = 4;
+  if(maxIdx - minIdx == bigDiff)
+  {
+    minIdx += n;
+    std::swap(maxIdx, minIdx);
+  }
+  if(maxIdx - minIdx != n - bigDiff)
   {
-    outsideCorners.push_back(corners[*it]);
+    return;
   }
+
+  int outsidersSegmentIdx = (minIdx + maxIdx) / 2;
+
+  outsideCorners.push_back(corners[outsidersSegmentIdx % n]);
+  outsideCorners.push_back(corners[(outsidersSegmentIdx + 1) % n]);
+
+#ifdef DEBUG_CIRCLES
+  drawPoints(outsideCorners, cornersImage, 2, Scalar(128));
+  imshow("corners", outsideCornersImage);
+#endif
 }
 
 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)
@@ -355,6 +410,9 @@ void CirclesGridClusterFinder::parsePatternPoints(const std::vector<cv::Point2f>
 
       if(dists[0] > maxRectifiedDistance)
       {
+#ifdef DEBUG_CIRCLES
+        cout << "Pattern not detected: too large rectified distance" << endl;
+#endif
         centers.clear();
         return;
       }