# 作业要求
完善以下函数:
bezier// 该函数实现绘制 Bézier 曲线的功能。它使用一个控制点序列和一个 OpenCV::Mat 对象作为输入,没有返回值。它会使 t 在 0 到 1 的范围内进行迭代,并在每次迭代中使 t 增加一个微小值。对于每个需要计算的 t,将调用另一个函数 recursive_bezier,然后该函数将返回在 Bézier 曲线上 t 处的点。最后,将返回的点绘制在 OpenCV ::Mat 对象上。 | |
recursive_bezier// 该函数使用一个控制点序列和一个浮点数 t 作为输入,实现 de Casteljau 算法来返回 Bézier 曲线上对应点的坐标。 |
# 作业实现
# 绘制 Bézier 曲线
过程如下:
cv::Point2f recursive_bezier(const std::vector<cv::Point2f> &control_points, | |
float t) { | |
// Implement de Casteljau's algorithm | |
if (control_points.size() == 1) { | |
return control_points[0]; | |
} | |
std::vector<cv::Point2f> new_points; | |
for (int i = 0; i < control_points.size() - 1; i++) { | |
auto point = (1 - t) * control_points[i] + t * control_points[i + 1]; | |
new_points.push_back(point); | |
} | |
return recursive_bezier(new_points, t); | |
} |
# de Casteljau 算法
De Casteljau 算法说明如下:
- 考虑一个 p0, p1, ... pn 为控制点序列的 Bézier 曲线。首先,将相邻的点连接
起来以形成线段。 - 用 t : (1− t) 的比例细分每个线段,并找到该分割点。
- 得到的分割点作为新的控制点序列,新序列的长度会减少一。
- 如果序列只包含一个点,则返回该点并终止。否则,使用新的控制点序列并转到步骤 1。
void bezier(const std::vector<cv::Point2f> &control_points, cv::Mat &window) { | |
// Iterate through all t = 0 to t = 1 with small steps, and call de | |
// Casteljau's recursive Bezier algorithm. | |
for (double t = 0.0; t <= 1.0; t += 0.001) { | |
auto point = recursive_bezier(control_points, t); | |
int x = point.x; | |
int y = point.y; | |
// 实现对 Bézier 曲线的反走样。 | |
// (对于一个曲线上的点,不只把它对应于一个像素, | |
// 你需要根据到像素中心的距离来考虑与它相邻的像素的颜色) | |
int x1 = std::max(0, x - 1); | |
int x2 = std::min(window.cols - 1, x + 1); | |
int y1 = std::max(0, y - 1); | |
int y2 = std::min(window.rows - 1, y + 1); | |
for (int i = x1; i <= x2; i++) { | |
for (int j = y1; j <= y2; j++) { | |
float distance = std::sqrt(std::pow(i - x, 2) + std::pow(j - y, 2)); | |
float weight = 1.0 / (1.0 + distance); | |
window.at<cv::Vec3b>(j, i)[1] = 255 * weight; | |
} | |
} | |
} | |
} |