C++实现多项式曲线拟合--polyfit

基本原理:幂函数可逼近任意函数。

上式中,N表示多项式阶数,实际应用中一般取3或5;

假设N=5,则:

共有6个未知数,仅需6个点即可求解;

可表示为矩阵方程:

Y的维数为[R*1],U的维数[R * 6],K的维数[6 * 1]。

R> 6时,超定方程求解:


下面是使用C++实现的多项式拟合的程序,程序中使用opencv进行矩阵运算和图像显示。程序分别运行了N=3,5,7,9时的情况,结果如下:



#include <opencv2\opencv.hpp>
#include <iostream>
#include <vector>
using namespace cv;
using namespace std;

Mat polyfit(vector<Point>& in_point, int n);

int main()
{
	//数据输入
	Point in[19] = { Point(50,120),Point(74,110),Point(98,100),Point(122,100),Point(144,80)
		,Point(168,80),Point(192,70),Point(214,50),Point(236,40),Point(262,20)
		,Point(282,20),Point(306,30),Point(328,40),Point(356,50),Point(376,50)
		,Point(400,50),Point(424,50),Point(446,40),Point(468,30) };

	vector<Point> in_point(begin(in),end(in));
	
	//n:多项式阶次
	int n = 9;
	Mat mat_k = polyfit(in_point, n);


	//计算结果可视化
	Mat out(150, 500, CV_8UC3,Scalar::all(0));

	//画出拟合曲线
	for (int i = in[0].x; i < in[size(in)-1].x; ++i)
	{
		Point2d ipt;
		ipt.x = i;
		ipt.y = 0;
		for (int j = 0; j < n + 1; ++j)
		{
			ipt.y += mat_k.at<double>(j, 0)*pow(i,j);
		}
		circle(out, ipt, 1, Scalar(255, 255, 255), CV_FILLED, CV_AA);
	}

	//画出原始散点
	for (int i = 0; i < size(in); ++i)
	{
		Point ipt = in[i];
		circle(out, ipt, 3, Scalar(0, 0, 255), CV_FILLED, CV_AA);
	}

	imshow("9次拟合", out);
	waitKey(0);
	
	return 0;
}

Mat polyfit(vector<Point>& in_point, int n)
{
	int size = in_point.size();
	//所求未知数个数
	int x_num = n + 1;
	//构造矩阵U和Y
	Mat mat_u(size, x_num, CV_64F);
	Mat mat_y(size, 1, CV_64F);

	for (int i = 0; i < mat_u.rows; ++i)
		for (int j = 0; j < mat_u.cols; ++j)
		{
			mat_u.at<double>(i, j) = pow(in_point[i].x, j);
		}

	for (int i = 0; i < mat_y.rows; ++i)
	{
		mat_y.at<double>(i, 0) = in_point[i].y;
	}

	//矩阵运算,获得系数矩阵K
	Mat mat_k(x_num, 1, CV_64F);
	mat_k = (mat_u.t()*mat_u).inv()*mat_u.t()*mat_y;
	cout << mat_k << endl;
	return mat_k;
}

#ifndef FUNCTION_H_ #define FUNCTION_H_ #include #include #include "polyfit.h" #include using namespace std; dxs::dxs() { ifstream fin("多项式拟合.txt"); fin>>n; x=new float[n]; y=new float[n]; for(int i=0;i>x[i]; } for(i=0;i>y[i]; } cout<>nn; m=nn+1; u=new float*[m]; for(i=0;i<m;i++) { u[i]=new float[m+1]; }//创建m行,m+1列数组 } void dxs::dfine() { for(int i=0;i<m;i++) { for(int j=0;j<m+1;j++) { u[i][j]=0; } } for(i=0;i<m;i++) { for(int j=0;j<m;j++) { for(int k=0;k<n;k++) { u[i][j]=u[i][j]+pow(x[k],j+i); } } } for(i=0;i<m;i++) { for(int k=0;k<n;k++) { u[i][m]=u[i][m]+pow(x[k],i)*y[k]; } } } void dxs::show() { for(int i=0;i<m;i++) { for(int j=0;j<m+1;j++) { cout<<u[i][j]<<" ";//<<endl; } cout<<endl; } ////显示具有m行m+1列u数组的各元素值 } void dxs::select_main(int k,float **p,int m) { double d; d=*(*(p+k)+k); //cout<<d; int l=k; int i=k+1; for(;i fabs(d)) { d=*(*(p+i)+k); l=i; } else continue; } if(d==0) cout<<"错误"; else { if(k!=l) { for(int j=k;j<m+1;j++) { double t; t=*(*(p+l)+j); *(*(p+l)+j)=*(*(p+k)+j); *(*(p+k)+j)=t; } } } } void dxs::gaosi() { for(int k=0;k<m;k++) { select_main(k,u,m);//调用列主元函数 for(int i=1+k;i<m;i++) { // *(*(p+i)+k)=(float) *(*(p+i)+k) / *(*(p+k)+k); u[i][k]=(float) u[i][k] / u[k][k]; } for(i=k+1;i<m;i++) { for(int j=k+1;j=0;i--) { float a=0; for(int j=i+1;j<m;j++) { //a=a + (*(*(p+i)+j) * *(*(p+j)+m)); a=a+u[i][j] * u[j][m]; } //*(*(p+i)+n-1)= (*(*(p+i)+n-1) - a) / *(*(p+i)+i); u[i][m]= (u[i][m] -a) / u[i][i]; } cout<<"方程组的解为:"<<endl; for(i=0;i<m;i++) { cout<<"a"<<i+1<<"="; cout<<u[i][m]<<endl; // l[i]=*(*(p+i)+n-1); } cout<<"y="<<u[0][m]; for(i=1;i<m;i++) { cout<<showpos<<u[i][m]<<"x"; if(i!=1)cout<<"^"<<noshowpos<<i; } cout<<endl; } dxs::~dxs() { delete[]x,y; delete []*u; } #endif
评论 19
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值