[英]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.