在计算机图形学和图像处理领域,贝塞尔曲线是一种非常重要的数学工具。它们主要用于插值、近似、曲线拟合和对象表示。本文将以一种简单直观的方式,展示如何构造这些曲线并加以利用。
贝塞尔曲线是一种参数曲线,具有高度的可定制性和平滑性,非常适合多种应用场景。这种曲线以法国数学家和工程师皮埃尔·贝塞尔(Pierre Bézier)的名字命名,他在20世纪60年代末为汽车制造商雷诺工作时开发了这种计算机绘图方法。据说,在同一时期,福特公司的研究中也出现了类似的发展。目前,关于谁首先发现这种方法还存在一些争议。
由于背景是图像处理,本文将主要关注插值和曲线拟合。在插值中,目标是使用已知值来找到未知点。这样,离散的情况就可以用更连续的结构来表示,可以为缺失的点定义一条良好的曲线。曲线初始化时会使用某些数据点,并尝试生成新的点来近似(或插值)旧的值。
考虑n+1个点P0,…,Pn,并将这些点连接成一条多边形线,称之为控制多边形。给定点Pi, i = 0,...,n,目标是确定一条曲线g(t),对于所有t ∈ [0,1]的值。这个想法如下所示:
这里的目标是在两个相邻点之间找到中间点,并重复这个过程,直到没有更多的迭代。新的点值将给曲线。著名的贝塞尔方程正是这个想法的精确公式。以下是算法步骤:
步骤1:选择一个值t ∈ [0,1]。这个值在剩余步骤中保持不变。
步骤2:设置Pi[0](t) = Pi,对于i = 0,...,n。
步骤3:对于j= 0,...,n,设置Pi[j+1](t) = (1-t) * Pi[j](t) + t * Pi+1[j](t),对于i = j,...,n。
步骤4:g(t) = Pn[n](t)。
现在,将给出一些常见特殊情况的公式,这些公式在某些应用中可能会有所帮助。文章中的代码没有展示它们中的任何一个,但它使用了一般公式。所以,让从一般公式开始:
为了简化和遵循本文和代码中使用的惯例,最好将这个公式表示为:
这个方程告诉无非是上述算法(中间点迭代)的公式化。这在意义上非常重要,因为整个算法可以总结为一个公式,一个简单的实现将产生正确的结果。在这里,n表示点的数量,P表示点本身。点的阶乘系数简单地被称为伯恩斯坦基函数,因为创始人的名字。
以下是特殊情况:
线性贝塞尔:
二次贝塞尔:
三次贝塞尔:
这是执行所有工作的函数。认为它非常简短且易于理解。因为只处理2D曲线,所以有X和Y坐标的点。该函数简单地计算贝塞尔点。
public void Bezier2D(double[] b, int cpts, double[] p) {
int npts = (b.Length) / 2;
int icount, jcount;
double step, t;
// Calculate points on curve
icount = 0;
t = 0;
step = (double)1.0 / (cpts - 1);
for (int i1 = 0; i1 != cpts; i1++) {
if ((1.0 - t) < 5e-6) t = 1.0;
jcount = 0;
p[icount] = 0.0;
p[icount + 1] = 0.0;
for (int i = 0; i != npts; i++) {
double basis = Bernstein(npts - 1, i, t);
p[icount] += basis * b[jcount];
p[icount + 1] += basis * b[jcount + 1];
jcount = jcount + 2;
}
icount += 2;
t += step;
}
}
其余的函数只是参与阶乘计算和基函数计算的辅助函数,相信它们是相当明显的。要正确使用这个函数,请给它一组格式为:XYXYXYXYXYXYXYXYXYXYXY...的点坐标,以及希望在曲线上计算的点的数量。函数将用路径点填充p数组。