• Modified Least Square Method and Ransan Method to Fit Circle from Data


    In OpenCv, it only provide the function fitEllipse to fit Ellipse, but doesn't provide function to fit circle, so i read some paper, and write a function to do it. The paper it refer to is "A Few Methods for Fitting Circles to Data".

    Also when there is a lot of noise in the data, need to find a way to exclude the noise data and get a more accuracy result.

    There are two methods, one is iterate method, first use all the data to fit a model, then find the points exceed the error tolerance to the model, excude them and fit again. Until reach iteration times limit or all the data is in error tolerance.

    Another method is ransac method, it has detailed introduction in paper "Random Sample Consensus: A Paradigm for Model Fitting with Apphcatlons to Image Analysis and Automated Cartography".

    // This function is based on Modified Least Square Methods from Paper
    // "A Few Methods for Fitting Circles to Data".
    cv::RotatedRect FitCircle(const std::vector<cv::Point2f> &vecPoints)
    {
        cv::RotatedRect rotatedRect;
        if ( vecPoints.size() < 3 )
            return rotatedRect;
    
        double Sx = 0., Sy = 0., Sx2 = 0., Sy2 = 0., Sxy = 0., Sx3 = 0., Sy3 = 0., Sxy2 = 0., Syx2 = 0.;
        for ( const auto &point : vecPoints )   {
            Sx   += point.x;
            Sy   += point.y;
            Sx2  += point.x * point.x;
            Sy2  += point.y * point.y;
            Sxy  += point.x * point.y;
            Sx3  += point.x * point.x * point.x;
            Sy3  += point.y * point.y * point.y;
            Sxy2 += point.x * point.y * point.y;
            Syx2 += point.y * point.x * point.x;
        }
    
        double A, B, C, D, E;
        int n = vecPoints.size();
        A = n * Sx2 - Sx * Sx;
        B = n * Sxy - Sx * Sy;
        C = n * Sy2 - Sy * Sy;
        D = 0.5 * ( n * Sxy2 - Sx * Sy2 + n * Sx3 - Sx * Sx2 );
        E = 0.5 * ( n * Syx2 - Sy * Sx2 + n * Sy3 - Sy * Sy2 );
    
        auto AC_B2 = ( A * C - B * B);  // The variable name is from AC - B^2
        auto am = ( D * C - B * E ) / AC_B2;
        auto bm = ( A * E - B * D ) / AC_B2;
    
        double rSqureSum = 0.f;
        for ( const auto &point : vecPoints )
        {
            rSqureSum += sqrt ( ( point.x - am ) * ( point.x - am ) + ( point.y - bm) * ( point.y - bm) );
        }
        float r = static_cast<float>( rSqureSum / n );
        rotatedRect.center.x = static_cast<float>( am );
        rotatedRect.center.y = static_cast<float>( bm );
        rotatedRect.size = cv::Size2f( 2.f * r,  2.f * r  );
    
        return rotatedRect;
    }
    
    std::vector<size_t> findPointOverTol( const std::vector<cv::Point2f> &vecPoints, const cv::RotatedRect &rotatedRect, int method, float tolerance )
    {
        std::vector<size_t> vecResult;
        for ( size_t i = 0;i < vecPoints.size(); ++ i )  {
            cv::Point2f point = vecPoints[i];
            auto disToCtr = sqrt( ( point.x - rotatedRect.center.x) * (point.x - rotatedRect.center.x)  + ( point.y - rotatedRect.center.y) * ( point.y - rotatedRect.center.y ) );
            auto err = disToCtr - rotatedRect.size.width / 2;
            if ( method == 1 && fabs ( err ) > tolerance )  {
                vecResult.push_back(i);
            }else if ( method == 2 && err > tolerance ) {
                vecResult.push_back(i);
            }else if ( method == 3 && err < -tolerance ) {
                vecResult.push_back(i);
            }
        }
        return vecResult;
    }
    
    //method 1 : Exclude all the points out of positive error tolerance and inside the negative error tolerance.
    //method 2 : Exclude all the points out of positive error tolerance.
    //method 3 : Exclude all the points inside the negative error tolerance.
    cv::RotatedRect FitCircleIterate(const std::vector<cv::Point2f> &vecPoints, int method, float tolerance)
    {
        cv::RotatedRect rotatedRect;
        if (vecPoints.size() < 3)
            return rotatedRect;
    
        std::vector<cv::Point2f> vecLocalPoints;
        for ( const auto &point : vecPoints )   {
            vecLocalPoints.push_back ( point );
        }
        rotatedRect = FitCircle ( vecLocalPoints );
        
        std::vector<size_t> overTolPoints = findPointOverTol ( vecLocalPoints, rotatedRect, method, tolerance );
        int nIteratorNum = 1;
        while ( ! overTolPoints.empty() && nIteratorNum < 20 )   {
            for (auto it = overTolPoints.rbegin(); it != overTolPoints.rend(); ++ it)
                vecLocalPoints.erase(vecLocalPoints.begin() + *it);
            rotatedRect = FitCircle ( vecLocalPoints );
            overTolPoints = findPointOverTol ( vecLocalPoints, rotatedRect, method, tolerance );
    ++ nIteratorNum; }
    return rotatedRect; } std::vector<cv::Point2f> randomSelectPoints(const std::vector<cv::Point2f> &vecPoints, int needToSelect) { std::set<int> setResult; std::vector<cv::Point2f> vecResultPoints; if ( (int)vecPoints.size() < needToSelect ) return vecResultPoints; while ((int)setResult.size() < needToSelect) { int nValue = std::rand() % vecPoints.size(); setResult.insert(nValue); } for ( auto index : setResult ) vecResultPoints.push_back ( vecPoints[index] ); return vecResultPoints; } std::vector<cv::Point2f> findPointsInTol( const std::vector<cv::Point2f> &vecPoints, const cv::RotatedRect &rotatedRect, float tolerance ) { std::vector<cv::Point2f> vecResult; for ( const auto &point : vecPoints ) { auto disToCtr = sqrt( ( point.x - rotatedRect.center.x) * (point.x - rotatedRect.center.x) + ( point.y - rotatedRect.center.y) * ( point.y - rotatedRect.center.y ) ); auto err = disToCtr - rotatedRect.size.width / 2; if ( fabs ( err ) < tolerance ) { vecResult.push_back(point); } } return vecResult; } /* The ransac algorithm is from paper "Random Sample Consensus: A Paradigm for Model Fitting with Apphcatlons to Image Analysis and Automated Cartography". * Given a model that requires a minimum of n data points to instantiate * its free parameters, and a set of data points P such that the number of * points in P is greater than n, randomly select a subset S1 of * n data points from P and instantiate the model. Use the instantiated * model M1 to determine the subset S1* of points in P that are within * some error tolerance of M1. The set S1* is called the consensus set of * S1. * If g (S1*) is greater than some threshold t, which is a function of the * estimate of the number of gross errors in P, use S1* to compute * (possibly using least squares) a new model M1* and return. * If g (S1*) is less than t, randomly select a new subset S2 and repeat the * above process. If, after some predetermined number of trials, no * consensus set with t or more members has been found, either solve the * model with the largest consensus set found, or terminate in failure. */ cv::RotatedRect FitCircleRansac(const std::vector<cv::Point2f> &vecPoints, float tolerance, int maxRansacTime, int nFinishThreshold) { cv::RotatedRect fitResult; if (vecPoints.size() < 3) return fitResult; int nRansacTime = 0; const int RANSAC_CIRCLE_POINT = 3; size_t nMaxConsentNum = 0; while ( nRansacTime < maxRansacTime ) { std::vector<cv::Point2f> vecSelectedPoints = randomSelectPoints ( vecPoints, RANSAC_CIRCLE_POINT ); cv::RotatedRect rectReult = FitCircle ( vecSelectedPoints ); vecSelectedPoints = findPointsInTol ( vecPoints, rectReult, tolerance ); if ( vecSelectedPoints.size() >= (size_t)nFinishThreshold ) { return FitCircle ( vecSelectedPoints ); } else if ( vecSelectedPoints.size() > nMaxConsentNum ) { fitResult = FitCircle ( vecSelectedPoints ); nMaxConsentNum = vecSelectedPoints.size(); } ++ nRansacTime; } return fitResult; } void TestFitCircle() { std::vector<cv::Point2f> vecPoints; vecPoints.push_back(cv::Point2f(0, 10)); vecPoints.push_back(cv::Point2f(2.9f, 2.9f)); vecPoints.push_back(cv::Point2f(17.07f, 17.07f)); vecPoints.push_back(cv::Point2f(3.f, 8.f)); vecPoints.push_back(cv::Point2f(10, 0)); vecPoints.push_back(cv::Point2f(10, 20)); vecPoints.push_back(cv::Point2f(11, 18)); vecPoints.push_back(cv::Point2f(20, 10)); vecPoints.push_back(cv::Point2f(28, 18)); vecPoints.push_back(cv::Point2f(17, 9)); cv::RotatedRect rectResult; rectResult = FitCircle(vecPoints); cout << " X, Y " << rectResult.center.x << ", " << rectResult.center.y << " r " << rectResult.size.width / 2. << endl; rectResult = FitCircleIterate ( vecPoints, 2, 2 ); cout << "Iterator Result X, Y " << rectResult.center.x << ", " << rectResult.center.y << " r " << rectResult.size.width / 2. << endl; rectResult = FitCircleRansac ( vecPoints, 1, 20, 7 ); cout << "Ransac Result X, Y " << rectResult.center.x << ", " << rectResult.center.y << " r " << rectResult.size.width / 2. << endl; }
  • 相关阅读:
    Windows Phone 7(WP7)开发 自订磁贴(深度链接)
    Windows Phone 7(WP7)开发 在ViewModel中使用NavigationService
    Windows Phone 7(WP7)开发 显示长文本(高度大于2000px)
    类属性生成器(小程序)
    Windows Phone 7(WP7)开发 ListBox的分页加载
    Windows Phone 7(WP7)开发工具 查看独立存储空间中数据库内容
    Windows Phone 7(WP7)开发 获取网络状态
    发布一个XNA写的小雷电源码
    用python来个百度关键词刷排名脚本
    win7下 VirtualBox虚拟机开机后台自启动
  • 原文地址:https://www.cnblogs.com/shengguang/p/5889172.html
Copyright © 2020-2023  润新知