⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 circlefit.cpp

📁 barcode readers [ from Image]
💻 CPP
字号:
//
// Fit a circle to a collection of points using the "Iterated
// Weighted Inversion Method" described in
// "Classical geometrical approach to circle fitting --
//  review and new developments", C. Rusu, M. Tico,
// P. Kuosmanen, E. Delp, Journal of Electronic Imaging,
// 12 (1) 179-193 (1/2003). Also on web at 
// http://www.nokia.com/downloads/aboutnokia/research/library/audio_visual/AVC10.pdf
//
// Copyright (C) 2003, 2006 by Jon A. Webb (Contact via GMail; username is jonawebb)
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
// 
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Lesser General Public License for more details.
// 
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
//
//

#include "CircleFit.h"

namespace Algorithm
{

    EXPORT_C CCircleFit* CCircleFit::NewL(TReal rEps)
    {
        return new (ELeave) CCircleFit(rEps);
    }

    CCircleFit::CCircleFit(TReal rEps) :
        _rEpsSq(rEps*rEps)
    {
    }

    CCircleFit::~CCircleFit(void)
    {
    }

    void FreeMemory(TReal* pU, TReal* pV, TReal* pW) {
		CleanupStack::Check(pW);
        CleanupStack::PopAndDestroy(); // pW
 		CleanupStack::Check(pV);
        CleanupStack::PopAndDestroy(); // pV
		CleanupStack::Check(pU);
        CleanupStack::PopAndDestroy(); // pU  
    }

    bool CCircleFit::FitCircleL(TPoint* pPoints, int nPoints, TReal& rCX, TReal& rCY, TReal& rR)
    {
        if (nPoints<3) {
            return false;
        }
        // first allocate the inversion coordinates
        TReal* pU = new (ELeave) TReal [nPoints];
        CleanupStack::PushL(pU);
        TReal* pV = new (ELeave) TReal [nPoints];
        CleanupStack::PushL(pV);
        // allocate weight vector 
        TReal* pW = new (ELeave) TReal [nPoints];
        CleanupStack::PushL(pW);

        TReal rErrSq = 0.0;
        // compute initial guess at a point on the circle for inversion (i.e., the inversion pole).
        // we want this to be as far as possible away from the other points
        // we choose a point directly opposite the middle point in the sequence
        // on the circle, assuming the estimate of the center is correct
        TReal rRhoSq = 2500.0; // this should be on the order of the square of the expected circle radius
        TReal rX = 2.0*rCX - TReal(pPoints[nPoints/2].iX);
        TReal rY = 2.0*rCY - TReal(pPoints[nPoints/2].iY);
        TReal rXK1 = 0.0, rYK1 = 0.0; // initialized by first 2 passes through the loop below
        int k = 0;
        do {
            if (k>25) {
                // the circle fit is not converging; give up
                FreeMemory(pU, pV, pW);
                return false;
            }
            int i = 0;
            TReal rUAvg = 0.0, rVAvg = 0.0, rWSum = 0.0;
            for (; i<nPoints; i++) {
                // compute the inversion coordinates
                TReal rXDiff = TReal(pPoints[i].iX) - rX;
                TReal rYDiff = TReal(pPoints[i].iY) - rY;
                TReal rDistSq = rXDiff * rXDiff + rYDiff * rYDiff;
                if (rDistSq == 0.0) {
                    // probably indicates an error in the circle fit. give up
                    FreeMemory(pU, pV, pW);
                    return false;
                }
                pU[i] = rX + (rXDiff * rRhoSq) / rDistSq;
                pV[i] = rY + (rYDiff * rRhoSq) / rDistSq;
                // compute the weights
                pW[i] = rDistSq * rDistSq;
                // sum the inversion coordinates to form average
                rUAvg += pU[i] * pW[i];
                rVAvg += pV[i] * pW[i];
                // sum of weights
                rWSum += pW[i];
            }
            // compute weighted average u and v
            if (rWSum == 0.0) {
                // indicates an error in the circle fit. give up.
                FreeMemory(pU, pV, pW);
                return false;
            }
            rUAvg /= rWSum;
            rVAvg /= rWSum;
            // compute the sums for fitting the line in the inversion space
            TReal rSuu = 0.0, rSuv = 0.0, rSvv = 0.0;
            for (i = 0; i<nPoints; i++) {
                TReal rUDiff = pU[i] - rUAvg;
                TReal rVDiff = pV[i] - rVAvg;
                rSuu += pW[i] * rUDiff * rUDiff;
                rSuv += pW[i] * rUDiff * rVDiff;
                rSvv += pW[i] * rVDiff * rVDiff;
            }
            TReal rSDiff = rSuu - rSvv;
            TReal rSqrt;
            TReal rSurd = rSDiff * rSDiff + 4 * rSvv * rSvv;
            if (rSurd < 0.0) {
                // an error in the circle fit. give up
                FreeMemory(pU, pV, pW);
                return false;
            }
            Math::Sqrt(rSqrt, rSurd);
            // compute slope (A) and v intercept (B) of line v = a + bu
            if (rSvv == 0.0) {
                // an error in the circle fit. give up
                FreeMemory(pU, pV, pW);
                return false;
            }
            TReal rB = (-rSDiff + rSqrt) / (2.0 * rSvv);
            TReal rA = rVAvg - rB * rUAvg;
            // compute nearest point on this line to the current inversion pole (rX, rY)
            TReal rXNear = (rB * rY + rX - rA * rB) / (1 + rB*rB);
            TReal rYNear = rA + rB * (rB * rY + rX - rA * rB)/ (1 + rB * rB);
            TReal rXNearDiff = rXNear - rX;
            TReal rYNearDiff = rYNear - rY;
            TReal rNearDist = rXNearDiff * rXNearDiff + rYNearDiff * rYNearDiff;
            // compute image of this nearest point on the circle. this is also the point on the
            // circle that is diametrically opposite the current inversion pole
            if (rNearDist == 0.0) {
                // an error in the circle fit. give up
                FreeMemory(pU, pV, pW);
                return false;
            }
            TReal rXImage = rX + rXNearDiff*rRhoSq / rNearDist;
            TReal rYImage = rY + rYNearDiff*rRhoSq / rNearDist;
            // compute center by averaging these two points
            rCX = (rX + rXImage) / 2;
            rCY = (rY + rYImage) / 2;
            TReal rXCXDiff = rX - rCX;
            TReal rYCYDiff = rY - rCY;
            // compute radius
            Math::Sqrt(rR, rXCXDiff * rXCXDiff + rYCYDiff * rYCYDiff);
            // stopping criterion--difference between new inversion pole and the one two
            // steps ago
            TReal rXK1Diff = rXImage - rXK1;
            TReal rYK1Diff = rYImage - rYK1;
            rErrSq = rXK1Diff * rXK1Diff + rYK1Diff * rYK1Diff;
            // update k-1 and k records of inversion pole
            rXK1 = rX;
            rYK1 = rY;
            rX = rXImage;
            rY = rYImage;
        } while (k++<=2 || // always do loop at least 3 times to initialize rErrSq properly
                 rErrSq > _rEpsSq); 
        FreeMemory(pU, pV, pW);
        return true;
    }

};

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -