c# - 从一段曲面点中找到圆心

标签 c# algorithm math geometry

我需要根据圆柱形物体的多个表面测量值找到圆心。

我目前使用基于三个点的简化算法(用 C# 编写)来找到中心(取自 How to determine the radius and center of a circle when only three noncollinear points are known? ):

private Point CircleCenter(List<Point> points, double p1Skip, double p2Skip, double p3Skip)
{
    var p1 = points.Skip((int)(points.Count * p1Skip)).First();
    var p2 = points.Skip((int)(points.Count * p2Skip)).First();
    var p3 = points.Skip((int)(points.Count * p3Skip)).First();

    double mr = (p2.Y - p1.Y) / (p2.X - p1.X);
    double mt = (p3.Y - p2.Y) / (p3.X - p2.X);

    double centerX = (mr * mt * (p3.Y - p1.Y) + mr * (p2.X + p3.X) - mt * (p1.X + p2.X)) / (2 * (mr - mt));

    double centerY = (-1 / mr) * (centerX - ((p1.X + p2.X) / 2)) + ((p1.Y + p2.Y) / 2);

    return new Point(centerX, centerY, p1.Z);
}

问题是这种方法对噪声非常敏感。如果其中一个点关闭,它当然会影响中心点。 对于每个横截面,我有 640 个表面点可用,我认为应该可以使用 3 个以上的点。

我猜测现有算法应该可以扩展更多点,但我不知道如何扩展。

最佳答案

在我的数学大师同事的帮助下,我找到了使用最小二乘矩阵计算的解决方案。

所用算法描述here .

public static void FitCircle(IEnumerable<Point> points, out double x0, out double y0, out double r)
{
    setMklProvider();
    DenseMatrix A = DenseMatrix.Create(3, 3, (i, j) => 0);
    DenseMatrix b = DenseMatrix.Create(3, 1, (i, j) => 0);
    A[0, 0] = points.Sum(point => point.X * point.X);
    A[0, 1] = points.Sum(point => point.X * point.Y);
    A[0, 2] = points.Sum(point => point.X);
    A[1, 0] = A[0, 1];
    A[1, 1] = points.Sum(point => point.Y * point.Y);
    A[1, 2] = points.Sum(point => point.Y);
    A[2, 0] = A[0, 2];
    A[2, 1] = A[1, 2];
    A[2, 2] = points.Count();
    b[0, 0] = points.Sum(point => point.X * (point.X * point.X + point.Y * point.Y));
    b[1, 0] = points.Sum(point => point.Y * (point.X * point.X + point.Y * point.Y));
    b[2, 0] = points.Sum(point => point.X * point.X + point.Y * point.Y);
    var x = A.QR().Solve(b);
    x0 = x[0, 0] / 2;
    y0 = x[1, 0] / 2;
    r = Math.Sqrt(x[2, 0] + x0 * x0 + y0 * y0);
}

private static void setMklProvider()
{
    if (!_mklProviderSet) MathNet.Numerics.Control.LinearAlgebraProvider = new MathNet.Numerics.Algorithms.LinearAlgebra.Mkl.MklLinearAlgebraProvider();
}

此解决方案产生了非常好的可重复结果,至少对于我的数据而言。 DenseMatrixMathNet 的一部分图书馆。

编辑

为了进一步减少噪音,正如用户 samgak 所建议的,我添加了一种提高准确性的迭代减少方法:

double x0, y0, r;
FitCircle(surfacePoints, out x0, out y0, out r);
var center = new Point(x0, y0, surfacePoints.First().Z);

int reductionIterations = 10;
var reducedSet = surfacePoints;

for (int i = 1; i < reductionIterations; i++)
{
    var orderedByDistanceToCenter = reducedSet.OrderBy(p => (p-center).GetRho()).ToList();

    reducedSet = orderedByDistanceToCenter
        .Skip((int)(orderedByDistanceToCenter.Count * (i / 10f)))
        .Take((int)(orderedByDistanceToCenter.Count - orderedByDistanceToCenter.Count * (i / 10f)*2))
        .ToList();

    // Reduced to zero, abort
    if (reducedSet.Count < 3)
        break;

    FitCircle(reducedSet, out x0, out y0, out r);
    center = new Point(x0, y0, reducedSet.First().Z);
}

public static double GetRho(this Point p) => Math.Sqrt(p.X * p.X + p.Y * p.Y);

关于c# - 从一段曲面点中找到圆心,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37962552/

相关文章:

c# - Xamarin 外部图像不显示问题

c# - Outlook AddOn 如何获取选中的邮件?

java - 用于压缩(例如 LZW)字符串的 Java 库

c# - C# 中的简单数学问题

algorithm - 如何从购物 list 的多个 bundle 中找到最便宜的产品组合

c# - 在没有管理员权限的情况下运行 taskmgr.exe?

c++ - 从备用键值输入创建映射

c# - 从一组中挑选最亮/最多样化图像的算法?

javascript - 为什么 2.23 + 0.17 = 2.4899999999999998 在 JavaScript 中?

c# - 使用 MVVM Light 的 Messenger 在 View 模型之间传递值