如何连接曲线的两个部分并获取连接曲线的点位置?

3
我有两段曲线。曲线上的点位置(x,y)已知。如何连接它们并获得连接曲线的位置?这里有一个类似的问题。similar question 他说要使用贝塞尔曲线。但是在我看来,通常拟合曲线不会经过控制点。因此,如果我从两段曲线中选择一些点作为控制点,则拟合结果可能如下图所示。那不是我的目标。有人能给我一些建议吗?

enter image description here

enter image description here


1
然后使用提出的替代方案——样条插值 :),因为曲线经过这些点。 - Humam Helfawi
或者说Catmull-Rom曲线,它们也穿过点,并且可以轻松地重写为三次贝塞尔形式。这个问题几乎就是它们被发明的原因 =) - Mike 'Pomax' Kamermans
3个回答

4
使用Catmull-Rom曲线,它与Bezier曲线相关且易于转换为Bezier形式,其好处是可以“穿过”点,而不仅仅是由它们控制。有关详细信息,请参见http://pomax.github.io/bezierinfo/#catmullconv,但我们基本上只需要这两个端点和曲线外的两个点,以确保我们在两个曲线上的点上具有正确的切线。

enter image description here

p2和p3是"你的"点,而p1和p4则是相对任意的:我们只需要确保线段p1-p3与p2处的切线平行(切线及其平行线用蓝紫色表示),同样地,线段p2-p4也要与p3处的切线平行(切线及其平行线用粉红色表示)。一个通常较为简单的方法就是将点p2和p3投影到这些平行线上project points p2 and p3 onto the parallel lines

只要我们确保了这一点,我们就可以将连接线段形成为Catmull-Rom曲线,其曲线坐标为(p1,p2,p3,p4)。如果没有Catmull-Rom绘图原语,我们也可以将其作为贝塞尔曲线来轻松绘制,方法是使用以下三次贝塞尔曲线坐标:

  1. 起始点:p2
  2. 控制点1:p2 + (p3-p1)/(6*t)
  3. 控制点2:p3 - (p4-p2)/(6*t)
  4. 结束点:p3
这里的t值是Catmull-Rom曲线的张力;您将其设置得越高,连接看起来就越“紧密”(在大多数支持Catmull-Rom的图形上下文中,默认张力为1)。
一些示例值:

enter image description here

请注意,每个示例中在点p2和p3处的切线方向保持不变,但是切线向量的长度不同,导致拟合结果紧密、好、或者过于松散。

我可以问两个问题吗?1.为什么起始/结束点p1,p4在平行线上?我能否使用曲线上的点作为起始/结束和控制点,因为我知道曲线上所有点的位置。2.我在这里找到了方程式2 http://www.mvps.org/directx/articles/catmull/,但我想使用更多的控制点使其更准确。你能给我展示一下通用方程式吗?例如,我想使用10个控制点。 - kookoo121
1:在答案和给出的链接中已经解释了(您需要保留由线p1-p3控制的点p2处的切线,以及由线p2-p4控制的点p3处的切线)。 2:不需要。通过添加“更多控制点”并不能使Catmull-Rom曲线“更精确”。 - Mike 'Pomax' Kamermans
非常感谢。1.但我找不到关于为什么需要保留由线p1-p3控制的点p2处的切线以及p3的解释。2.您的意思是Catmull-Rom曲线只能使用一个或两个控制点吗?对于提出太多问题,我很抱歉。 - kookoo121
如果您想要一个平滑的连接段,需要保留通过P2和P3的曲线方向。否则会出现折痕。在Catmull-Rom曲线中,曲线上点的切线始终与其前面的点到其后面的点的连线平行,因此需要将P1和P3的位置匹配。由于您已经有了P3,因此我们需要放置P1,使得p1--p3线段与p2处的切线平行(对于P3也是如此,使用p2和p4点)。请参见http://pomax.github.io/bezierinfo/#catmullconv。 - Mike 'Pomax' Kamermans

0

正如我在评论中提到的,样条曲线可能比贝塞尔曲线在这种情况下更好。然而,您也可以使用更简单的方法。您有4个点(红色)。尝试使用以下公式将它们拟合为多项式(3次方程,因为您只有4个点):

A x^3 + B x^2 + C x + D = y

您有4个点数 (P0,P1,P2,P3):

A x0^3 + B x0^2 + C x0 + D = y0
A x1^3 + B x1^2 + C x1 + D = y1
A x2^3 + B x2^2 + C x2 + D = y2
A x3^3 + B x3^2 + C x3 + D = y3

解决这个线性方程组将给出A,B,C,D的值。
要获取缺失的曲线部分:
for(auto x=P1.x; x<P2.x; ++x){
    auto y=A*x*x*x + B*x*x + C*x + D;
    cv::circle(image,cv::Point(x,y),.......);
}

感谢您的评论。但我认为将表达曲线表示为y=A x^3 + B x^2 + C x + D并不是很好,因为形状会旋转。 http://www.mathopenref.com/cubicexplorer.html - kookoo121
你展示了你只有4个点,所以没有其他选项可用... 你还有更多吗? - Humam Helfawi
@曲线上所有点的位置都已知。我们可以使用每个点。我只是从中选择了四个。 - kookoo121
然后,您可以将多项式的次数增加到4或5或任何其他次数,因为您有另外的点可以添加到方程中。 - Humam Helfawi

0
不要排除贝塞尔曲线。你是对的,这条曲线通常不会与控制点相交,但是端点会相交,你可以利用这一点来约束曲线的形状。
更具体地说,你可以通过构建一个新的二次贝塞尔曲线来连接这两条线 - 使用你两条线的端点作为曲线的端点,并以两条当前线段的端点延长线相交的中点作为曲线的中点。

Bezier

在上面的例子中,红色圆圈是曲线的固定端点,绿色线是延长线,蓝色圆圈是它们的交点(用作控制点),而蓝色线是你最终得到的近似曲线。
编辑:经过进一步思考,你可能想要使用立方曲线,并在每条绿线上有两个蓝色控制点。将每个控制点定位在离端点距离L/2的位置,其中L是红色端点之间的直线距离,可能会产生良好的结果。二次曲线的问题在于,当绿线接近平行时,二次曲线在控制点附近会出现一个尖角。在平行情况下,它们实际上并不相交。使用立方曲线将产生一条更圆滑的线,并且没有与平行切线相关的任何问题。例如,在下面的图像中,顶部曲线使用二次曲线(一个控制点),底部曲线使用立方曲线(两个控制点)。

Bezier2


谢谢您的评论。您能给我展示一下如何获取扩展行的更多细节吗? - kookoo121
@kookoo121 这取决于您的原始线条如何定义,但假设它们是参数化定义的,您应该能够推导出端点处的切向量,然后使用正常的线-线交点方程式。例如,如果您的两条原始线是它们自己的贝塞尔曲线,则从最后一个控制点到端点的向量就是切向量。 - MooseBoys
非常感谢。我会尝试的。 - kookoo121

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接