假设有n多个点,我们需要对这n多个点进行平面拟合,我们会考虑采用最小二乘法去拟合这个平面。
下面我们介绍以下最小二乘拟合平面的原理:
从推导过程分析,我们只需要计算出所有点的系数矩阵,然后等式同时左乘系数矩阵的逆,我们便能很容易的计算出a,b,c
实现思路:(此处随机以一个已知平面生成了一个平面点集)
1.首先初始化一个系数矩阵和结果矩阵。
2.判断系数矩阵是否为奇异阵
3.求逆,计算结果
//作者:dwy
//日期:2019/07/09
//用途:最小二乘拟合平面
#include
#include
#include
using namespace std;
using namespace cv;
void creatTestData(vector<Point3f> &output)
{
RNG rng(12345);
int a = 10;
int b = 2;
int c = 5;
cout << "测试系数为:a=" << a << ",b=" << b << ",c=" << c << endl;
for (int i = 0;i < 10;i++)
{
double x = rng.uniform(1, 100);
double y = rng.uniform(100,200);
double z = a*x + b*y + c;
Point3f point;
point.x = x;
point.y = y;
point.z = z;
output.push_back(point);
}
}
void plane_fitting(vector<double>& coeffient, vector<Point3f> &input)
{
Mat dst = Mat(3, 3, CV_32F, Scalar(0));//初始化系数矩阵A
Mat out = Mat(3, 1, CV_32F, Scalar(0));//初始化矩阵b
for (int i = 0;i < input.size();i++)
{
//计算3*3的系数矩阵
dst.at<float>(0, 0) = dst.at<float>(0, 0) + pow(input[i].x, 2);
dst.at<float>(0, 1) = dst.at<float>(0, 1) + input[i].x*input[i].y;
dst.at<float>(0, 2) = dst.at<float>(0, 2) + input[i].x;
dst.at<float>(1, 0) = dst.at<float>(1, 0) + input[i].x*input[i].y;
dst.at<float>(1, 1) = dst.at<float>(1, 1) + pow(input[i].y, 2);
dst.at<float>(1, 2) = dst.at<float>(1, 2) + input[i].y;
dst.at<float>(2, 0) = dst.at<float>(2, 0) + input[i].x;
dst.at<float>(2, 1) = dst.at<float>(2, 1) + input[i].y;
dst.at<float>(2, 2) = input.size();
//计算3*1的结果矩阵
out.at<float>(0, 0) = out.at<float>(0, 0) + input[i].x*input[i].z;
out.at<float>(1, 0) = out.at<float>(1, 0) + input[i].y*input[i].z;
out.at<float>(2, 0) = out.at<float>(2, 0) + input[i].z;
}
//判断矩阵是否奇异
double determ = determinant(dst);
if (abs(determ) < 0.001) {
cout << "矩阵奇异" << endl;
return;
}
Mat inv;
invert(dst, inv);//求矩阵的逆
Mat output = inv*out;//计算输出
coeffient.clear();//把结果输出
coeffient.push_back(output.at<float>(0, 0));
coeffient.push_back(output.at<float>(1, 0));
coeffient.push_back(output.at<float>(2, 0));
}
int main()
{
vector<double> coeffient;
vector<Point3f> input;
creatTestData(input);
plane_fitting(coeffient, input);
for (int i = 0;i < coeffient.size();i++)
{
cout << "第"<<i<<"个系数为:"<<coeffient[i] << endl;
}
cout << "hello,world" << endl;
return 0;
}
1.最小二乘是一种拟合平面,曲面较为简单的方式,需要掌握基本的算法原理。
2.本文采用opencv主要是opencv中的Mat类有很多内置的方法,包括转置,求逆,求特征值,特征向量等,具体的方法,大家可以参考这篇blog–Mat类操作。