一种简单的贝塞尔拟合算法

一种简单的贝塞尔拟合算法

前两天实现了一项功能,在一端进行书写,在另一端还原笔迹。由于两端的开发平台和绘图引擎不一致,书写端的笔迹很平滑,而另一端还原出来的笔迹为折线。为了使两端保持一致的效果,需要在还原端对笔迹进行贝塞尔拟合。本文将首先介绍贝塞尔曲线的基本原理及公式推导,然后提出一种简单的二次贝塞尔近似拟合算法,并用 C# 编程实现之。

贝塞尔曲线

相信大家都或多或少了解过贝塞尔曲线,此处就不再赘述,仅仅介绍其原理及推导。

如上图所示,对于平面上的两个点 P0 和 P1,假设另一点 B 匀速地从 P0 点运动到 P1 点,则有 B 点在 t 时刻的坐标公式:

将 B 点在各个时刻的坐标依次连接起来所形成的线,就是所谓的贝塞尔曲线。此公式表示的是一次贝塞尔曲线,也称为线性贝塞尔曲线。

二次贝塞尔曲线

同样地,对于平面上的三个点 P0、P1 和 P2 ,假设 P0P1 之间有个点 B1 匀速地从 P0 运动到 P1 ,P1P2 之间有个点 B2 匀速地从 P1 运动到 P2,则有:


假设另一点 B 匀速地从 B1 运动到 B2,则有 B 点的坐标公式:

将 B1 和 B2 的坐标公式代入上面的表达式,整理后得到 B 点的坐标公式:

B 点在各个时刻的坐标所连成的曲线即为二次贝塞尔曲线,其中 P0 和 P2 称为 数据点,P1 称为 控制点

简单的二次拟合算法

有了上面的基本原理的理解,我们回到问题的解决。对于较为稀疏的三个点 P1、P2 和 P3,如果直接顺次相连,会呈现出折线,所以需要进行贝塞尔插值,使其平滑。如果按照标准的贝塞尔曲线算法,P1 和 P2 应该为数据点,并且需要为这两个数据点寻找控制点。但是此处采用了近似的拟合算法,首先计算出 P2 和 P3 的中点,并以此点和 P1 点为数据点,然后以 P2 点为控制点,由此来确定一条二次贝塞尔曲线。虽然最终生成的贝塞尔曲线未能通过所有的真实数据点,但是整条曲线的形态能和另一端保持一致,并且位置也不会存在较大偏差。

/// <summary>
/// 计算两点之间的二次贝塞尔曲线点集。
/// </summary>
private IList<Point> CalculateBezierPoints(Point begin, Point handle, Point end)
{
    // 根据两点之间的距离来确定曲线上的点数,进而确定 t 的步长(步长越小,精度越高)
    var bx = begin.X;
    var by = begin.Y;
    var ex = end.X;
    var ey = end.Y;
    var hx = handle.X;
    var hy = handle.Y;

    var bhDis = Math.Sqrt((bx - hx) * (bx - hx) + (by - hy) * (by - hy));
    var ehDis = Math.Sqrt((ex - hx) * (ex - hx) + (ey - hy) * (ey - hy));

    var distance = bhDis + ehDis;
    var count = distance / 40;

    if (count < 2)
    {
        count = 2;
    }

    // 确定步长,并计算曲线上的点集
    var step = 1.0 / count;
    var points = new List<Point>();

    var t = 0.0; // t∈[0,1]
    for (var i = 0; i < count; i++)
    {
        points.Add(GetBezierPointByT(begin, handle, end, t));
        t += step;
    }

    return points;
}

/// <summary>
/// 根据二次贝塞尔曲线的公式,计算各个时刻的坐标值。
/// </summary>
private Point GetBezierPointByT(Point begin, Point handle, Point end, double t)
{
    var pow = Math.Pow(1 - t, 2);

    // B = (1-t)^2*P0 + 2t(1-t)P1 + t^2*P2 , t∈[0,1]
    var x = pow * begin.X + 2 * t * (1 - t) * handle.X + t * t * end.X;
    var y = pow * begin.Y + 2 * t * (1 - t) * handle.Y + t * t * end.Y;

    return new Point(x, y);
}

private void Usage()
{
    var start = point1;
    var end = new Point((point2.X + point3.X) / 2, (point2.Y + point3.Y) / 2);
    
    var points = CalculateBezierPoints(start, point2, end);
}

效果图

附:高次贝塞尔曲线

下面展示一些高阶贝塞尔曲线的原理图和公式,此处不做推导,感兴趣的可以参照上面的二次曲线的推导方法自行演算。

  • 三次贝塞尔曲线


  • 四次贝塞尔曲线

  • 五次贝塞尔曲线

参考资料

  • 18
    点赞
  • 47
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 7
    评论
RRT算法贝塞尔曲线拟合是两个不同的问题,可能需要更明确的描述你的问题和需求。以下提供一些关于RRT算法贝塞尔曲线拟合的基本概念和相关matlab代码,供参考。 RRT算法 RRT(Rapidly-exploring Random Tree)算法一种用于路径规划的算法,常用于机器人、自动驾驶车辆等领域。其核心思想是通过随机采样和树结构的建立,快速找到一条可行路径。 以下是一个简单的matlab示例代码: ```matlab % 初始化 start = [0,0]; % 起点 goal = [10,10]; % 终点 maxIter = 1000; % 最大迭代次数 delta = 0.5; % 采样步长 obstacle = [5,5,1]; % 障碍物,格式为[x,y,r],表示圆形障碍物 tree = start; % 初始化树,第一个节点为起点 % 迭代 for i = 1:maxIter % 随机采样 if rand < 0.5 q = [rand*10, rand*10]; % 在地图内随机采样 else q = goal; % 有一定概率采样终点 end % 找到最近的节点 [idx, dist] = knnsearch(tree, q); qNear = tree(idx,:); % 按照步长delta向qNear移动 qNew = qNear + delta*(q-qNear)/dist; % 如果没有碰撞,就加入树中 if ~collisionCheck(qNear, qNew, obstacle) tree = [tree; qNew]; plot([qNear(1), qNew(1)], [qNear(2), qNew(2)], 'b'); drawnow; % 如果qNew接近终点,就停止迭代 if norm(qNew-goal) < delta break; end end end % 路径回溯 path = goal; while norm(path(1,:)-start) > delta [idx, ~] = knnsearch(tree, path(1,:)); path = [tree(idx,:); path]; end path = [start; path]; % 碰撞检测函数 function flag = collisionCheck(q1, q2, obstacle) flag = 0; for r = linspace(0, 1, 10) q = (1-r)*q1 + r*q2; if norm(q-obstacle(1:2)) < obstacle(3) flag = 1; break; end end end ``` 贝塞尔曲线拟合 贝塞尔曲线一种常用的曲线拟合方法,常用于图形处理和计算机辅助设计等领域。贝塞尔曲线由若干个控制点和其它参数定义,可以用来拟合任意形状的曲线。 以下是一个简单的matlab示例代码: ```matlab % 控制点 P = [0, 0; 1, 2; 3, 3; 4, 1; 5, 2]; % 参数 n = size(P, 1) - 1; % 阶数 t = linspace(0, 1, 100); % 参数向量 % 计算基函数值 B = zeros(n+1, length(t)); for i = 0:n B(i+1,:) = nchoosek(n, i) .* t.^i .* (1-t).^(n-i); end % 计算曲线点 Pc = B * P; % 绘制结果 plot(P(:,1), P(:,2), 'o', Pc(:,1), Pc(:,2), '-'); ``` 这段代码将给出五个控制点,计算出阶数为4的贝塞尔曲线在参数向量t上的点,并将两者绘制在一张图中。你可以自己修改控制点和阶数,尝试不同的曲线拟合效果。 希望这些代码能对你有所帮助!
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Ironyho

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值