欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

贝塞尔曲线 插值拟合

程序员文章站 2023-12-27 10:33:15
...

参考:https://blog.csdn.net/ch_soft/article/details/7401582博客

          原文链接:http://www.antigrain.com/research/bezier_interpolation/index.html#PAGE_BEZIER_INTERPOLATION

先上QT中的贝塞尔曲线调用代码,由于控制点计算过于简单,所以插值曲线经过数据点并不是非常光滑:

/*******************************************
*              贝塞尔曲线拟合1              *
               Bézier curve  
*QT自带 由于控制点设置过于简单 曲线过渡不平滑
*******************************************/
void MyClass::paintEvent(QPaintEvent *event)
{

	static QList<QPointF> points;
	for (int i = 0; i < key_points.size(); i++)
	{
		QPointF Qp(key_points[i].x, key_points[i].y);
		points.push_back(Qp);
	}
	QPainterPath path(points[0]);

	for (int i = 0; i < points.size() - 1; ++i) {
		// 控制点的 x 坐标为 sp 与 ep 的 x 坐标和的一半
		// 第一个控制点 c1 的 y 坐标为起始点 sp 的 y 坐标
		// 第二个控制点 c2 的 y 坐标为结束点 ep 的 y 坐标
		QPointF sp = points[i];
		QPointF ep = points[i + 1];
		QPointF c1 = QPointF((sp.x() + ep.x()) / 2, sp.y());
		QPointF c2 = QPointF((sp.x() + ep.x()) / 2, ep.y());
		path.cubicTo(c1, c2, ep);
	}
	QPainter painter(this);//**painter
	//设置渲染提示为消除锯齿
	painter.setRenderHint(QPainter::Antialiasing, true);
	//设置画笔颜色和宽度
	painter.setPen(QPen(Qt::black, 2));
	//将坐标系转换为矢量
	painter.translate(40, 130);
	//绘制path
	painter.drawPath(path);
	// 绘制曲线上的点
	painter.setBrush(Qt::red);
    painter.end();//释放
	//绘制曲线上的点
	for (int i = 0; i < points.size(); ++i) {
		painter.drawEllipse(points[i], 4, 4);
	}
}

以下代码是链接中代码的改进,原代码中曲线是闭合的,如果改为不闭合,需要对头尾两个点的控制点进行修改:



/*******************************************
*              贝塞尔曲线拟合2              *
               Bézier curve  
*                 参考网上                 *
大致思路就是先算出相邻原始点的中点,
在把相邻中点连成的线段平移到对应的原始点,
以平移后的中点作为控制点,
相邻原始点为起始点画贝塞尔曲线,
这样就保证了连接处的光滑
*******************************************/
/*****************画图*************************/
for (int i = 0; i < curvePoint.size() - 1; i++)
{// 注意:这里遍历到倒数第二个点即可,如果需要封闭曲线,则遍历到最后一个点

	cv::Point p1;
	p1.x = curvePoint[i].x;
	p1.y = curvePoint[i].y;

	cv::Point p2;
	p2.x = curvePoint[i].x;
	p2.y = curvePoint[i].y;

	line(src, p1, p2, cv::Scalar(0, 122,233), 3, 8, 0);
	
}
for (int i = 0; i < m_save_Point.size(); i++)
{
	circle(src, m_save_Point[i].m_point, 5, Scalar(0, 0,255), -1);
}


/*****************核心代码*************************/
//三次贝塞尔曲线  
float bezier3funcX(float uu, cv::Point* controlP)
{
	float part0 = controlP[0].x * uu * uu * uu;
	float part1 = 3 * controlP[1].x * uu * uu * (1 - uu);
	float part2 = 3 * controlP[2].x * uu * (1 - uu) * (1 - uu);
	float part3 = controlP[3].x * (1 - uu) * (1 - uu) * (1 - uu);
	return part0 + part1 + part2 + part3;
}
float bezier3funcY(float uu, cv::Point* controlP)
{
	float part0 = controlP[0].y * uu * uu * uu;
	float part1 = 3 * controlP[1].y * uu * uu * (1 - uu);
	float part2 = 3 * controlP[2].y * uu * (1 - uu) * (1 - uu);
	float part3 = controlP[3].y * (1 - uu) * (1 - uu) * (1 - uu);
	return part0 + part1 + part2 + part3;
}
void createCurve(std::vector<cv::Point> originPoint, int originCount, std::vector<cv::Point> &curvePoint)
{
	//控制点收缩系数 ,经调试0.6较好,CvPoint是opencv的,可自行定义结构体(x,y)  
	float scale = 0.6;
	cv::Point *midpoints = new cv::Point[originCount];
	//生成中点       
	for (int i = 0; i < originCount; i++){
		int nexti = (i + 1) % originCount;
		midpoints[i].x = (originPoint[i].x + originPoint[nexti].x) / 2.0;
		midpoints[i].y = (originPoint[i].y + originPoint[nexti].y) / 2.0;
	}

	//平移中点  
	cv::Point *extrapoints = new cv::Point[2 * originCount];
	for (int i = 0; i < originCount; i++){
		if (i == 0 || i == originCount - 1)
			scale = 0;//作用 头尾两点各两个控制点位置调整到头尾位置点
		else
			scale = 0.6;
		int nexti = (i + 1) % originCount;
		int backi = (i + originCount - 1) % originCount;
		cv::Point midinmid;
		//相邻中点连成的线段的中点
		midinmid.x = (midpoints[i].x + midpoints[backi].x) / 2.0;
		midinmid.y = (midpoints[i].y + midpoints[backi].y) / 2.0;
		int offsetx = originPoint[i].x - midinmid.x;
		int offsety = originPoint[i].y - midinmid.y;
		int extraindex = 2 * i;
		extrapoints[extraindex].x = midpoints[backi].x + offsetx;
		extrapoints[extraindex].y = midpoints[backi].y + offsety;
		//朝 originPoint[i]方向收缩   
		int addx = (extrapoints[extraindex].x - originPoint[i].x) * scale;
		int addy = (extrapoints[extraindex].y - originPoint[i].y) * scale;
		extrapoints[extraindex].x = originPoint[i].x + addx;
		extrapoints[extraindex].y = originPoint[i].y + addy;

		int extranexti = (extraindex + 1) % (2 * originCount);
		extrapoints[extranexti].x = midpoints[i].x + offsetx;
		extrapoints[extranexti].y = midpoints[i].y + offsety;
		//朝 originPoint[i]方向收缩   
		addx = (extrapoints[extranexti].x - originPoint[i].x) * scale;
		addy = (extrapoints[extranexti].y - originPoint[i].y) * scale;
		extrapoints[extranexti].x = originPoint[i].x + addx;
		extrapoints[extranexti].y = originPoint[i].y + addy;

	}

	cv::Point controlPoint[4];
	//生成4控制点,产生贝塞尔曲线  originCount-1时得到的点不包括首尾点之间的点,曲线不闭合, originCount时则得到首尾点之间的点,曲线会闭合
	for (int i = 0; i < originCount; i++){
		controlPoint[0] = originPoint[i];
		int extraindex = 2 * i;
		controlPoint[1] = extrapoints[extraindex + 1];
		int extranexti = (extraindex + 2) % (2 * originCount);
		controlPoint[2] = extrapoints[extranexti];
		int nexti = (i + 1) % originCount;
		controlPoint[3] = originPoint[nexti];
		float u = 1;
		while (u >= 0){
			int px = bezier3funcX(u, controlPoint);
			int py = bezier3funcY(u, controlPoint);
			//u的步长决定曲线的疏密  
			u -= 0.005;
			cv::Point tempP = cvPoint(px, py);
			//存入曲线点   
			curvePoint.push_back(tempP);
		}
	}
	delete []midpoints;
	delete []extrapoints;
}

效果图贝塞尔曲线 插值拟合

上一篇:

下一篇: