【GAMES101】作业4(提高)含Bazier曲线的反走样处理

本文档介绍了一种使用deCasteljau算法实现Bézier曲线的绘制方法,包括四点控制的Bézier曲线及其递归实现。在OpenCV环境中,通过迭代t值并应用反走样技术优化图像锯齿效果,使得曲线更加平滑。代码示例展示了如何在C++中实现这一过程,并给出了反走样前后对比,证明算法的有效性。
摘要由CSDN通过智能技术生成

作业描述

Bézier 曲线是一种用于计算机图形学的参数曲线。在本次作业中,你需要实现 de Casteljau 算法来绘制由 4 个控制点表示的 Bézier 曲线 (当你正确实现该算法时,你可以支持绘制由更多点来控制的 Bézier 曲线)。你需要修改的函数在提供的 main.cpp 文件中。

• bezier:该函数实现绘制 Bézier 曲线的功能。它使用一个控制点序列和一个OpenCV::Mat对象作为输入,没有返回值。它会使 t 在 0 到 1 的范围内进行迭代,并在每次迭代中使 t 增加一个微小值。对于每个需要计算的 t,将调用另一个函数 recursive_bezier,然后该函数将返回在 Bézier 曲线上 t处的点。最后,将返回的点绘制在 OpenCV ::Mat 对象上。
• recursive_bezier:该函数使用一个控制点序列和一个浮点数 t 作为输入,实现 de Casteljau 算法来返回 Bézier 曲线上对应点的坐标

De Casteljau 算法说明如下:

  1. 考虑一个 p0, p1, … pn 为控制点序列的 Bézier 曲线。首先,将相邻的点连接起来以形成线段。
  2. 用 t : (1 − t) 的比例细分每个线段,并找到该分割点。
  3. 得到的分割点作为新的控制点序列,新序列的长度会减少一。
  4. 如果序列只包含一个点,则返回该点并终止。否则,使用新的控制点序列并转到步骤 1。使用 [0,1] 中的多个不同的 t 来执行上述算法,你就能得到相应的 Bézier 曲线

bezier

由作业描述中可得,整个De Casteljau算法就是由n个点算出n-1个点,然后不断递归到只剩一个点时即为所求,所以为方便,这里我使用了两个容器points1, points2来记录每次求得的点,用flag来区分每次应该用哪个容器来算。

void bezier(const std::vector<cv::Point2f> &control_points, cv::Mat &window) 
{
    // TODO: Iterate through all t = 0 to t = 1 with small steps, and call de Casteljau's 
    // recursive Bezier algorithm.
    int size = control_points.size();
    std::vector<cv::Point2f> points1 = control_points, points2;
    bool flag = true, bflag;
    for (double t = 0.0; t <= 1.0; t += 0.001) 
    {
        bflag = false;
        while(!bflag){
            if(flag)
                bflag = recursive_bezier(control_points, points1, points2, t, window);
            else
                bflag = recursive_bezier(control_points, points2, points1, t, window);
            flag = !flag;
        }
    }
}

recursive_bezier

这里我对 recursive_bezier函数做了一点修改,增加了一些参数以方便将点画在屏幕上,同时也将返回类型设置成了bool,方便在bezier中进行结束的判断(若返回true,则证明只剩一个点了,bezier中就结束了一个t点的运算)

bool recursive_bezier(const std::vector<cv::Point2f> &control_points, std::vector<cv::Point2f> &points1, std::vector<cv::Point2f> &points2, float t, cv::Mat &window){
    int size;
    size = points1.size();

    if(size == 1){
        window.at<cv::Vec3b>(points1[0].y, points1[0].x)[1] = 255;//设置该点的绿通道为255
        points2 = control_points;
        points1.clear();//结束计算后记得初始化容器
        return true;
    }
    for(int i = 0; i < size - 1; i++)
        points2.push_back((1 - t) * points1[i] + t * points1[i + 1]);
    points1.clear();
    return false;
}

效果

只用bezier:
在这里插入图片描述
naive_bezier和bezier一起用:
在这里插入图片描述
发现画出的曲线颜色为黄色,说明计算结果一致,算法无误

同时还可以尝试八个控制点的效果:
在这里插入图片描述

提高

要求:实现对 Bézier 曲线的反走样。(对于一个曲线上的点,不只把它对应于一个像素,你需要根据到像素中心的距离来考虑与它相邻的像素的颜色。)

根据要求中的提示,每得到一个曲线上的点时,我们就根据它到自己周围的3×3个像素中心的距离d来为这些像素填色以达到平滑过渡的效果(每个像素的颜色是255*ratio,d的范围是[0,3/√2],ratio的范围是[0,1],那么ratio关于d的函数就是ratio=1-√2/3d),重复计算的点就按照该点的颜色最大值算,这样就不会在线段中间出现暗点了,按道理这样应该就能达成反走样的要求:

在这里插入图片描述

bool recursive_bezier(const std::vector<cv::Point2f> &control_points, std::vector<cv::Point2f> &points1, std::vector<cv::Point2f> &points2, float t, cv::Mat &window){
    int size;
    size = points1.size();
    double ratio = 1;
    if(size == 1){
        for(int i = -1; i <= 1; i++){
            for(int j = -1; j <= 1; j++){//遍历9个像素
                if(points1[0].y + j > 700 || points1[0].y + j < 0 || points1[0].x + i > 700 || points1[0].x + i < 0)//不处理越界像素
                    continue;
                ratio = 1 - sqrt(2)*sqrt(pow(points1[0].y - int(points1[0].y + j) - 0.5, 2) + pow(points1[0].x - int(points1[0].x + i) - 0.5, 2)) / 3;//计算ratio

                window.at<cv::Vec3b>(points1[0].y + j, points1[0].x + i)[1] = std::fmax(window.at<cv::Vec3b>(points1[0].y + j, points1[0].x + i)[1], 255 * ratio);//计算像素颜色
            }
        }
        points2 = control_points;
        points1.clear();
        return true;
    }
    for(int i = 0; i < size - 1; i++)
        points2.push_back((1 - t) * points1[i] + t * points1[i + 1]);
    points1.clear();
    return false;
}

反走样前:
在这里插入图片描述
在这里插入图片描述

反走样后:
在这里插入图片描述在这里插入图片描述

可以看到,曲线的锯齿效果的确被优化了许多

附件

附上源代码,有兴趣的朋友可以自己尝试一下效果:
CSDN:【GAMES101】作业4(提高)
GITHUB:【GAMES101】作业合集

  • 14
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 9
    评论
评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

ycr的帐号

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

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

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

打赏作者

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

抵扣说明:

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

余额充值