opencv拟合高维曲线

代码来自 https://blog.csdn.net/guduruyu/article/details/72866144,略有修改

#include <opencv2/opencv.hpp>

#define ORDER 4

bool polynomial_curve_fit(std::vector<cv::Point>& key_point, int n, cv::Mat& A)
{
	//Number of key points
	int N = key_point.size();
 
	//构造矩阵X
	cv::Mat X = cv::Mat::zeros(n + 1, n + 1, CV_64FC1);
	for (int i = 0; i < n + 1; i++)
	{
		for (int j = 0; j < n + 1; j++)
		{
			for (int k = 0; k < N; k++)
			{
				X.at<double>(i, j) = X.at<double>(i, j) +
					std::pow(key_point[k].x, i + j);
			}
		}
	}
 
	//构造矩阵Y
	cv::Mat Y = cv::Mat::zeros(n + 1, 1, CV_64FC1);
	for (int i = 0; i < n + 1; i++)
	{
		for (int k = 0; k < N; k++)
		{
			Y.at<double>(i, 0) = Y.at<double>(i, 0) +
				std::pow(key_point[k].x, i) * key_point[k].y;
		}
	}
 
	A = cv::Mat::zeros(n + 1, 1, CV_64FC1);
	//求解矩阵A
	cv::solve(X, Y, A, cv::DECOMP_LU);
	return true;
}

int main()
{
	//创建用于绘制的深蓝色背景图像
	cv::Mat image = cv::Mat::zeros(480, 640, CV_8UC3);
	image.setTo(cv::Scalar(100, 0, 0));
 
	//输入拟合点  
	std::vector<cv::Point> points;
	points.push_back(cv::Point(100., 58.));
	points.push_back(cv::Point(150., 70.));
	points.push_back(cv::Point(200., 90.));
	points.push_back(cv::Point(252., 140.));
	points.push_back(cv::Point(300., 220.));
	points.push_back(cv::Point(350., 400.));
 
	//将拟合点绘制到空白图上  
	for (int i = 0; i < points.size(); i++)
	{
		cv::circle(image, points[i], 5, cv::Scalar(0, 0, 255), 2, 8, 0);
	}
 
	//绘制折线
	cv::polylines(image, points, false, cv::Scalar(0, 255, 0), 1, 8, 0);
 
	cv::Mat A;
 
	polynomial_curve_fit(points, ORDER, A);
	std::cout << "A = " << A << std::endl;
 
	std::vector<cv::Point> points_fitted;
 
	for (int x = 0; x < 400; x++)
	{
		double y = A.at<double>(0, 0);
		for(int i = 0; i < ORDER; i++){
			y += A.at<double>(i + 1, 0) *std::pow(x, i + 1);
		}
 
		points_fitted.push_back(cv::Point(x, y));
	}
	cv::polylines(image, points_fitted, false, cv::Scalar(0, 255, 255), 1, 8, 0);
	cv::imwrite("result.jpg", image);
 
	cv::imshow("image", image);
 
	cv::waitKey(0);
	return 0;
}

上面代码中,如果ORDER为1,就是线性变换,

拟合结果为(黄色为拟合曲线)

如果ORDER为2,就是二维曲线,拟合结果为:

如果ORDER为3,就是三维曲线,拟合结果为:

如果ORDER为4,就是三维曲线,拟合结果为:

OpenCV中没有直接提供拟合贝塞尔曲线的函数,但是可以通过拟合多阶Bezier曲线的方法来逼近贝塞尔曲线。以下是两种拟合多阶Bezier曲线的方法: 1. 最小二乘法拟合 这种方法通过最小化给定点集与拟合Bezier曲线之间的距离来拟合Bezier曲线。 可以使用OpenCV中的`cv::approxPolyDP()`函数对给定点集进行曲线近似,然后使用`cv::fitLine()`函数拟合Bezier曲线的控制点。具体步骤如下: - 对给定点集进行曲线近似,得到一条多阶Bezier曲线。 - 计算该多阶Bezier曲线的控制点。 - 通过控制点构造Bezier曲线,得到最终的Bezier曲线。 下面是一个使用最小二乘法拟合Bezier曲线的示例代码: ```c++ #include <opencv2/opencv.hpp> #include <iostream> using namespace cv; using namespace std; int main() { vector<Point2f> points = { Point2f(100, 100), Point2f(200, 300), Point2f(400, 200), Point2f(500, 350) }; vector<Point2f> approxCurve; double epsilon = 1.0; approxPolyDP(points, approxCurve, epsilon, true); int n = approxCurve.size(); Mat X(n, 3, CV_32FC1); Mat Y(n, 1, CV_32FC1); for (int i = 0; i < n; i++) { float x = approxCurve[i].x; float y = approxCurve[i].y; X.at<float>(i, 0) = x * x; X.at<float>(i, 1) = x; X.at<float>(i, 2) = 1; Y.at<float>(i, 0) = y; } Mat coefficients; fitLine(X, Y, coefficients, CV_32FC1, 0); Point2f p1 = Point2f(0, coefficients.at<float>(2, 0)); Point2f p2 = Point2f(640, coefficients.at<float>(0, 0) * 640 * 640 + coefficients.at<float>(1, 0) * 640 + coefficients.at<float>(2, 0)); vector<Point2f> controlPoints = { p1, approxCurve[1], approxCurve[2], p2 }; Mat bezierPoints; for (double t = 0; t <= 1; t += 0.01) { double x = (1 - t) * (1 - t) * (1 - t) * controlPoints[0].x + 3 * t * (1 - t) * (1 - t) * controlPoints[1].x + 3 * t * t * (1 - t) * controlPoints[2].x + t * t * t * controlPoints[3].x; double y = (1 - t) * (1 - t) * (1 - t) * controlPoints[0].y + 3 * t * (1 - t) * (1 - t) * controlPoints[1].y + 3 * t * t * (1 - t) * controlPoints[2].y + t * t * t * controlPoints[3].y; bezierPoints.push_back(Point2f(x, y)); } for (int i = 0; i < bezierPoints.rows; i++) { circle(bezierPoints, bezierPoints.at<Point2f>(i), 1, Scalar(0, 0, 255), -1); } imshow("Bezier Curve", bezierPoints); waitKey(0); return 0; } ``` 在上述代码中,我们首先定义了一个包含四个点的点集,然后使用`cv::approxPolyDP()`函数对该点集进行曲线近似,得到一条多阶Bezier曲线。之后,我们根据该Bezier曲线的控制点用最小二乘法拟合Bezier曲线,并根据Bezier曲线方程计算Bezier曲线上的点集。最后,我们在图像上绘制Bezier曲线上的点。 2. Bezier曲线逼近 这种方法通过迭代逼近给定点集与Bezier曲线之间的距离来拟合Bezier曲线。 可以使用OpenCV中的`cv::approxPolyDP()`函数对给定点集进行曲线近似,然后使用一个递归函数对Bezier曲线进行逼近。具体步骤如下: - 对给定点集进行曲线近似,得到一条多阶Bezier曲线。 - 计算该多阶Bezier曲线的控制点。 - 如果控制点个数大于4,将Bezier曲线分成两段,并对每一段进行递归调用,直到控制点个数小于等于4。 - 通过控制点构造Bezier曲线,得到最终的Bezier曲线。 下面是一个使用Bezier曲线逼近方法拟合Bezier曲线的示例代码: ```c++ #include <opencv2/opencv.hpp> #include <iostream> using namespace cv; using namespace std; void fitBezierCurve(vector<Point2f>& points, vector<Point2f>& curve) { int n = points.size(); if (n <= 4) { for (int i = 0; i < n; i++) { curve.push_back(points[i]); } return; } vector<Point2f> leftPoints, rightPoints; for (int i = 0; i < n / 2; i++) { leftPoints.push_back(points[i]); } for (int i = n / 2; i < n; i++) { rightPoints.push_back(points[i]); } vector<Point2f> leftCurve, rightCurve; fitBezierCurve(leftPoints, leftCurve); fitBezierCurve(rightPoints, rightCurve); for (int i = 0; i < leftCurve.size() - 1; i++) { curve.push_back(leftCurve[i]); } curve.push_back(leftCurve[leftCurve.size() - 1]); curve.push_back(rightCurve[0]); for (int i = 1; i < rightCurve.size(); i++) { curve.push_back(rightCurve[i]); } } int main() { vector<Point2f> points = { Point2f(100, 100), Point2f(200, 300), Point2f(400, 200), Point2f(500, 350) }; vector<Point2f> approxCurve; double epsilon = 1.0; approxPolyDP(points, approxCurve, epsilon, true); vector<Point2f> curve; fitBezierCurve(approxCurve, curve); Mat bezierPoints; for (double t = 0; t <= 1; t += 0.01) { double x = (1 - t) * (1 - t) * (1 - t) * curve[0].x + 3 * t * (1 - t) * (1 - t) * curve[1].x + 3 * t * t * (1 - t) * curve[2].x + t * t * t * curve[3].x; double y = (1 - t) * (1 - t) * (1 - t) * curve[0].y + 3 * t * (1 - t) * (1 - t) * curve[1].y + 3 * t * t * (1 - t) * curve[2].y + t * t * t * curve[3].y; bezierPoints.push_back(Point2f(x, y)); } for (int i = 0; i < bezierPoints.rows; i++) { circle(bezierPoints, bezierPoints.at<Point2f>(i), 1, Scalar(0, 0, 255), -1); } imshow("Bezier Curve", bezierPoints); waitKey(0); return 0; } ``` 在上述代码中,我们首先定义了一个包含四个点的点集,然后使用`cv::approxPolyDP()`函数对该点集进行曲线近似,得到一条多阶Bezier曲线。之后,我们使用一个递归函数对该Bezier曲线进行递归逼近,直到控制点个数小于等于4。最后,我们根据Bezier曲线方程计算Bezier曲线上的点集,并在图像上绘制Bezier曲线上的点。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值