]> rtime.felk.cvut.cz Git - opencv.git/commitdiff
Added an alternative algorithm for chessboard corner detection with subpixel accuracy
authorrelrotciv <relrotciv@73c94f0f-984f-4a5f-82bc-2d8db8d8ee08>
Fri, 15 Jan 2010 17:21:52 +0000 (17:21 +0000)
committerrelrotciv <relrotciv@73c94f0f-984f-4a5f-82bc-2d8db8d8ee08>
Fri, 15 Jan 2010 17:21:52 +0000 (17:21 +0000)
git-svn-id: https://code.ros.org/svn/opencv/trunk@2569 73c94f0f-984f-4a5f-82bc-2d8db8d8ee08

opencv/include/opencv/cvaux.hpp
opencv/src/cvaux/cvquadsubpix.cpp

index 327af20f21588954d731d08e91494cef2f62392a..7dfe4d63e260a8a58236b766f787b78ba9fcfbd3 100644 (file)
@@ -1821,6 +1821,9 @@ struct DefaultRngAuto
                int original_num_classes_;  
                bool keep_floats_;
        };
+    
+CV_EXPORTS bool find4QuadCornerSubpix(const Mat& img, std::vector<Point2f>& corners, Size region_size);
+
 
 }
 
index d82c3124f7e8029255fa3dfae1bf0a7b3385a854..51dd9a30c99c141aecc5457a6ee7a1f702bbb4ee 100644 (file)
 #include <utility>
 #include <algorithm>
 
+#include <math.h>
+
 #include <cvaux.h>
 #include <highgui.h>
 
-#define _SUBPIX_VERBOSE
+//#define _SUBPIX_VERBOSE
 
 #undef max
 
@@ -70,7 +72,7 @@ int histQuantile(const MatND& hist, float quantile)
     float quantile_sum = total_sum*quantile;
     for(int j = 0; j < hist.size[0]; j++)
     {
-        cur_sum += hist.at<float>(j);
+        cur_sum += hist.at<double>(j);
         if(cur_sum > quantile_sum)
         {
             return j;
@@ -144,52 +146,84 @@ void findCorner(const vector<Point>& contour, Point2f point, Point2f& corner)
     // temporary solution, have to make something more precise
     corner = contour[min_idx];
     return;
-    
-#if 0
-    // find the points corresponding to neighboring sides of the quadrangle
-    int idx;
+}
 
-    for(idx = min_idx; idx != min_idx; idx = (idx + 1) % contour.size())
+void findCorner(const vector<Point2f>& contour, Point2f point, Point2f& corner)
+{
+    // find the nearest point
+    double min_dist = std::numeric_limits<double>::max();
+    int min_idx = -1;
+    
+    Rect brect = boundingRect(contour);
+    
+    // find corner idx
+    for(size_t i = 0; i < contour.size(); i++)
     {
-        if(fabs(contour[idx].x - point.x) > brect.width*0.5f || 
-           fabs(contour[idx].y - point.y) > brect.height*0.5f)
+        double dist = norm(contour[i] - point);
+        if(dist < min_dist)
         {
-            break;
+            min_dist = dist;
+            min_idx = i;
         }
     }
-    assert(idx == min_idx);
-    size_t idx1 = idx;
-    vector<float> curve1;
-    vector<Point2f> border_points1;
-    for(size_t i = min_idx; i != idx1; i = (i + 1) % contour.size())
-    {
-        border_points1.push_back(contour[i]);
-    }
-    fitCurve2Order(border_points1, curve1);
+    assert(min_idx >= 0);
+    
+    // temporary solution, have to make something more precise
+    corner = contour[min_idx];
+    return;
+}
+    
+int segment_hist_max(const MatND& hist, int& low_thresh, int& high_thresh)
+{
+    Mat bw;
+    const double max_bell_width = 20; // we expect two bells with width bounded above
+    const double min_bell_width = 5; // and below
+    
+    double total_sum = sum(hist).val[0];
+    double thresh = total_sum/(2*max_bell_width)*0.25f; // quarter of a bar inside a bell
+    
+//    threshold(hist, bw, thresh, 255.0, CV_THRESH_BINARY);
     
-    for(idx = min_idx; idx != min_idx; idx = (idx - 1) % contour.size())
+    double quantile_sum = 0.0;
+    double min_quantile = 0.2;
+    double low_sum = 0;
+    double max_segment_length = 0;
+    int max_start_x = -1;
+    int max_end_x = -1;
+    int start_x = 0;
+    const double out_of_bells_fraction = 0.1;
+    for(int x = 0; x < hist.size[0]; x++)
     {
-        if(fabs(contour[idx].x - point.x) > brect.width*0.5f || 
-           fabs(contour[idx].y - point.y) > brect.height*0.5f)
+        quantile_sum += hist.at<double>(x);
+        if(quantile_sum < 0.2*total_sum) continue;
+        
+        if(quantile_sum - low_sum > out_of_bells_fraction*total_sum)
         {
-            break;
+            if(max_segment_length < x - start_x)
+            {
+                max_segment_length = x - start_x;
+                max_start_x = start_x;
+                max_end_x = x;
+            }
+
+            low_sum = quantile_sum;
+            start_x = x;
         }
     }
-    assert(idx == min_idx);
-    size_t idx2 = idx;
-    vector<float> curve2;
-    vector<Point2f> border_points2;
-    for(size_t i = min_idx; i != idx2; i = (i - 1) % contour.size())
+    
+    if(start_x == -1)
     {
-        border_points2.push_back(contour[i]);
+        return 0;
+    }
+    else
+    {
+        low_thresh = max_start_x + 0.25*(max_end_x - max_start_x);
+        high_thresh = max_start_x + 0.75*(max_end_x - max_start_x);
+        return 1;
     }
-    fitCurve2Order(border_points2, curve2);
-    
-    findCurvesCross(curve1, curve2, corner);
-#endif
 }
-
-bool find4QuadCornerSubpix(const Mat& img, std::vector<Point2f>& corners, Size region_size, std::vector<Point2f>& ground_truth_corners)
+    
+bool find4QuadCornerSubpix(const Mat& img, std::vector<Point2f>& corners, Size region_size)
 {
     const int nbins = 256;
     float ranges[] = {0, 256};
@@ -204,35 +238,31 @@ bool find4QuadCornerSubpix(const Mat& img, std::vector<Point2f>& corners, Size r
     
     Mat black_comp, white_comp;
     for(size_t i = 0; i < corners.size(); i++)
-    {
-        size_t ground_truth_idx = 0;
-        double min_ground_truth_dist = std::numeric_limits<double>::max();
-        for(size_t idx = 0; idx < ground_truth_corners.size(); idx++)
-        {
-            float dist = norm(ground_truth_corners[idx] - corners[i]);
-            if(dist < min_ground_truth_dist)
-            {
-                min_ground_truth_dist = dist;
-                ground_truth_idx = idx;
-            }
-        }
-        
+    {        
         int channels = 0;
-        Rect roi(corners[i].x - region_size.width, corners[i].y - region_size.height, region_size.width*2, region_size.height*2);
+        Rect roi(corners[i].x - region_size.width, corners[i].y - region_size.height, region_size.width*2 + 1, region_size.height*2 + 1);
         Mat img_roi = img(roi);
         calcHist(&img_roi, 1, &channels, Mat(), hist, 1, &nbins, &_ranges);
         
+#if 0
         int black_thresh = histQuantile(hist, 0.45f);
         int white_thresh = histQuantile(hist, 0.55f);
+#else
+        int black_thresh, white_thresh;
+        segment_hist_max(hist, black_thresh, white_thresh);
+#endif
         
         threshold(img, black_comp, black_thresh, 255.0, CV_THRESH_BINARY_INV);
         threshold(img, white_comp, white_thresh, 255.0, CV_THRESH_BINARY);
         
-        const int erode_count = 2;
+        const int erode_count = 1;
         erode(black_comp, black_comp, Mat(), Point(-1, -1), erode_count);
         erode(white_comp, white_comp, Mat(), Point(-1, -1), erode_count);
 
-#if 0
+#if defined(_SUBPIX_VERBOSE)
+        namedWindow("roi", 1);
+        imshow("roi", img_roi);
+        imwrite("test.jpg", img);
         namedWindow("black", 1);
         imshow("black", black_comp);
         namedWindow("white", 1);
@@ -248,7 +278,7 @@ bool find4QuadCornerSubpix(const Mat& img, std::vector<Point2f>& corners, Size r
         findContours(black_comp, black_contours, black_hierarchy, CV_RETR_LIST, CV_CHAIN_APPROX_SIMPLE);
         findContours(white_comp, white_contours, white_hierarchy, CV_RETR_LIST, CV_CHAIN_APPROX_SIMPLE);
         
-        if(black_contours.size() < 10 || white_contours.size() < 10) continue;
+        if(black_contours.size() < 5 || white_contours.size() < 5) continue;
         
         // find two white and black blobs that are close to the input point
         vector<std::pair<int, float> > white_order, black_order;
@@ -264,47 +294,53 @@ bool find4QuadCornerSubpix(const Mat& img, std::vector<Point2f>& corners, Size r
         
         const vector<Point>* quads[4] = {&black_contours[black_order[0].first], &black_contours[black_order[1].first], 
                                          &white_contours[white_order[0].first], &white_contours[white_order[1].first]};
+        vector<Point2f> quads_approx[4];
         Point2f quad_corners[4];
         for(int k = 0; k < 4; k++)
         {
+#if 1
+            vector<Point2f> temp;
+            for(size_t j = 0; j < quads[k]->size(); j++) temp.push_back((*quads[k])[j]);
+            approxPolyDP(temp, quads_approx[k], 0.5, true);
+            
+            findCorner(quads_approx[k], corners[i], quad_corners[k]);
+#else
             findCorner(*quads[k], corners[i], quad_corners[k]);
+#endif
             quad_corners[k] += Point2f(0.5f, 0.5f);
         }
         
-        double old_dist = norm(corners[i] - ground_truth_corners[ground_truth_idx]);
         // cross two lines
         Point2f origin1 = quad_corners[0];
         Point2f dir1 = quad_corners[1] - quad_corners[0];
         Point2f origin2 = quad_corners[2];
         Point2f dir2 = quad_corners[3] - quad_corners[2];
+        double angle = acos(dir1.dot(dir2)/(norm(dir1)*norm(dir2)));
+        if(cvIsNaN(angle) || cvIsInf(angle) || angle < 0.5 || angle > CV_PI - 0.5) continue;
+           
         findLinesCrossPoint(origin1, dir1, origin2, dir2, corners[i]);
-
-        double new_dist = norm(corners[i] - ground_truth_corners[ground_truth_idx]);
       
 #if defined(_SUBPIX_VERBOSE)
-#if 1
-//        printf("Corner position: %f,%f, true position: %f,%f\n", corners[i].x, corners[i].y, 
-//               ground_truth_corners[ground_truth_idx].x, ground_truth_corners[ground_truth_idx].y);
-        printf("Improved error for corner %d from %f to %f\n", (int)i, old_dist, new_dist);
-#endif
         radius[i] = norm(corners[i] - ground_truth_corners[ground_truth_idx])*6;
-     
+        
 #if 1
-        Mat img1 = img(Rect(corners[i].x - 30, corners[i].y - 30, 60, 60));
-        Mat test(img1.size(), CV_32FC3);
-        cvtColor(img1, test, CV_GRAY2RGB);
-        line(test, quad_corners[0] - corners[i] + Point2f(30, 30), quad_corners[1] - corners[i] + Point2f(30, 30), cvScalar(0, 255, 0));
-        line(test, quad_corners[2] - corners[i] + Point2f(30, 30), quad_corners[3] - corners[i] + Point2f(30, 30), cvScalar(0, 255, 0));
+        Mat test(img.size(), CV_32FC3);
+        cvtColor(img, test, CV_GRAY2RGB);
+//        line(test, quad_corners[0] - corners[i] + Point2f(30, 30), quad_corners[1] - corners[i] + Point2f(30, 30), cvScalar(0, 255, 0));
+//        line(test, quad_corners[2] - corners[i] + Point2f(30, 30), quad_corners[3] - corners[i] + Point2f(30, 30), cvScalar(0, 255, 0));
         vector<vector<Point> > contrs;
         contrs.resize(1);
         for(int k = 0; k < 4; k++)
         {
-            contrs[0] = *quads[k];
-            drawContours(test, contrs, 0, CV_RGB(0, 0, 255), 1, 1, vector<Vec4i>(), 2, Point2f(30, 30) - corners[i]);
-            circle(test, quad_corners[k] - corners[i] + Point2f(30, 30), 1, CV_RGB(255, 0, 0));
+            //contrs[0] = quads_approx[k];
+            contrs[0].clear();
+            for(size_t j = 0; j < quads_approx[k].size(); j++) contrs[0].push_back(quads_approx[k][j]);
+            drawContours(test, contrs, 0, CV_RGB(0, 0, 255), 1, 1, vector<Vec4i>(), 2);
+            circle(test, quad_corners[k], 0.5, CV_RGB(255, 0, 0));
         }
+        Mat test1 = test(Rect(corners[i].x - 30, corners[i].y - 30, 60, 60));
         namedWindow("1", 1);
-        imshow("1", test);
+        imshow("1", test1);
         imwrite("test.jpg", test);
         waitKey(0);
 #endif