作业4是使用 De Casteljau 算法实现 Bézier 曲线
De Casteljau 算法的核心是递归,使用递归,通过输入的多个顶点计算出连续插值后的最后一个顶点
1cv::Point2f recursive_bezier(const std::vector<cv::Point2f> &control_points, float t)
2{
3 // TODO: Implement de Casteljau's algorithm
4 if (control_points.size() == 1) return control_points.front();
5
6 std::vector<cv::Point2f> next_points;
7 for (int i = 0; i < control_points.size() - 1; i++)
8 {
9 next_points.push_back(((1 - t) * control_points[i] + t * control_points[i + 1]));
10 }
11
12 return recursive_bezier(next_points, t);
13}
在循环中计算当前顶点与下一顶点的插值点,并将其push_back进要在下一层递归中使用的顶点组;每次递归都会使总顶点数减一,直到只剩最后一个顶点,将其作为返回值输出
1void bezier(const std::vector<cv::Point2f> &control_points, cv::Mat &window)
2{
3 // TODO: Iterate through all t = 0 to t = 1 with small steps, and call de Casteljau's
4 // recursive Bezier algorithm.
5 for (double t = 0; t <= 1; t += 0.0001)
6 {
7 auto point = recursive_bezier(control_points, t);
8 window.at<cv::Vec3b>(point.y, point.x)[1] = 255;
9 }
10}
在这个函数中,通过对t值进行细微的调整,可以得到平滑的曲线效果(本质就是在两个点之间计算出大量的插值点)
我们在循环中所计算的坐标是有零1有整的,比如(10.2,15.8),但是程序会把它转换为距离最近的整数(10,16),这也是锯齿产生的原因
1void bezier(const std::vector<cv::Point2f> &control_points, cv::Mat &window)
2{
3 // TODO: Iterate through all t = 0 to t = 1 with small steps, and call de Casteljau's
4 // recursive Bezier algorithm.
5
6 for (double t = 0; t <= 1; t += 0.0001)
7 {
8 int color = 0;
9 auto point = recursive_bezier(control_points, t);
10 int x = std::floor(point.x);
11 int y = std::floor(point.y);
12
13 std::vector<cv::Point2f> near_pixels
14 {
15 cv::Point2f(x + 1, y + 1), cv::Point2f(x + 1, y),
16 cv::Point2f(x, y + 1), cv::Point2f(x, y)
17 };
18 for (const auto& near : near_pixels)
19 {
20 int nx = static_cast<int>(near.x);
21 int ny = static_cast<int>(near.y);
22
23 if (nx < 0 || nx >= window.cols || ny < 0 || ny >= window.rows) continue;
24
25 float distance = std::sqrt((point.x - near.x) * (point.x - near.x) +
26 (point.y - near.y) * (point.y - near.y));
27
28 float weight = 1.0f - (distance / 1.414f);
29 int new_green = std::max(0, static_cast<int>(weight * 255.0f));
30
31 auto& pixel = window.at<cv::Vec3b>(ny, nx);
32 pixel[1] = std::max(static_cast<int>(pixel[1]), new_green);
33 }
34 }
35}
有两种方法,一种是通过周围的点计算当前的颜色值,但是在这个循环中,这个方法有些麻烦
所以,另一种方法是使用当前点的颜色值,通过欧氏距离插值,赋予周围的点颜色
1.先对当前坐标取整,并计算包围着当前的的四个点(2x2采样)
2.通过遍历,分别计算distance并赋予颜色值(取最大值)
原文:4