繁体   English   中英

如何使用最小二乘法将球体拟合到一组 3d 点?

[英]How can I fit a sphere to a set of 3d points with least squares method?

我正在尝试使用我在此处找到的方法将球体拟合到一组 3d 点: https://jekel.me/2015/Least-Squares-Sphere-Fit

我确实理解为什么以及如何形成输入矩阵和向量,但我还没有完全理解最小二乘法本身。 (我希望跳过它,因为我只需要结果)。 我已经做到了这一点:

        // simplified example: Expected result: R=1000.0, C=(0, 1000, 0)
        points = new Vector<double>[]
        {
            DenseVector.OfArray(new double[] {0.0, 0.0, 0.0, 1.0}),
            DenseVector.OfArray(new double[] {0.0, 1000.0, 1000.0, 1.0}),
            DenseVector.OfArray(new double[] {1000.0, 1000.0, 0.0, 1.0}),
            DenseVector.OfArray(new double[] {0.0, 1000.0, -1000.0, 1.0}),
        };

        Matrix<double> A = DenseMatrix.OfRowVectors(points);
        Vector<double> f = DenseVector.Create(A.RowCount, 0.0);
        foreach (var tuple in A.EnumerateRowsIndexed())
        {
            var index = tuple.Item1;
            var row = tuple.Item2;

            // Assemble the A matrix
            row[0] *= 2.0;
            row[1] *= 2.0;
            row[2] *= 2.0;
            row[3] = 1;
            A.SetRow(index, row);

            // Assemble the f matrix
            f[index] = row[0] * row[0] + row[1] * row[1] + row[2] * row[2];
        }

        var C = MultipleRegression.NormalEquations(A, f);

        // solve for the radius
        double t = (C[0] * C[0]) + (C[1] * C[1]) + (C[2] * C[2]) + C[3];
        double radius = System.Math.Sqrt(t);

        // Actual result: R=4000, C=(0, 4000, 0)

不幸的是,结果不正确。 我注意到 math.net 文档中的回归示例只处理曲线。 我犯了错误还是图书馆不适合这种问题?

我从 C# 内部使用 numpy 库(参见问题中的链接)运行 python 脚本。 它将正确的结果返回给我可以处理它的应用程序。

我不确定你的代码哪里出了问题,但我重写了矩阵的填充方式和计算的方式。

下面给出了计算最佳拟合球体的通用方法。

public static (double radius, double x, double y, double z) BestFitSphere(double[] xValues, double[] yValues, double[] zValues)
{
    if ((xValues.Length != yValues.Length) || (yValues.Length != zValues.Length))
        throw new ArgumentException("Array inputs for x y z all need to be the same length");
    
    var numPoints = xValues.Length;
    
    var a = new double[numPoints, 4];
    var f = new double[numPoints];
    
    for(var i = 0; i < numPoints; i++)
    {
        a[i,0] = xValues[i] * 2;
        a[i,1] = yValues[i] * 2;
        a[i,2] = zValues[i] * 2;
        a[i,3] = 1.0;
        f[i] = (xValues[i] * xValues[i]) + (yValues[i] * yValues[i]) + (zValues[i] * zValues[i]);
    }
    
    var aMatrix = Matrix<double>.Build.DenseOfArray(a);
    var fVector = Vector<double>.Build.DenseOfArray(f);
    
    var cVector = MultipleRegression.NormalEquations(aMatrix, fVector);
    // solve for the radius
    double t = (cVector[0] * cVector[0]) + (cVector[1] * cVector[1]) + (cVector[2] * cVector[2]) + cVector[3];
    double radius = System.Math.Sqrt(t);
    return (radius, cVector[0], cVector[1], cVector[2]);
}

然后使用该方法与您的示例数据看起来像这样

// simplified example: Expected result: R=1000.0, C=(0, 1000, 0)
var xValues = new double[]{0.0, 0.0, 1000.0, 0.0};
var yValues = new double[]{0.0, 1000.0, 1000.0, 1000.0};
var zValues = new double[]{0.0, 1000.0, 0.0, -1000.0};

var (radius, x, y, z) = BestFitSphere(xValues, yValues, zValues);
Console.WriteLine($"radius = {radius} center = ({x},{y},{z})");

返回的实际结果是

半径 = 1000 中心 = (1.0339757656912846E-28,999.9999999999998,0)

我在 dotnetfiddle 中对此进行了测试,可以在此处查看https://dotnetfiddle.net/U8dWot

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM