贝塞尔曲线 插值拟合

参考: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 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 originPoint, int originCount, std::vector &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;
}

效果图贝塞尔曲线 插值拟合_第1张图片

你可能感兴趣的:(图像算法)