任务
bezier:该函数实现绘制 Bézier
曲线的功能。它使用一个控制点序列和一个OpenCV::
Mat
对象作为输入,没有返回值。它会使
t
在
0
到
1
的范围内进行迭代,并在每次迭代中使 t
增加一个微小值。对于每个需要计算的
t
,将调用另一个函数 recursive_bezier
,然后该函数将返回在
Bézier
曲线上
t处的点。最后,将返回的点绘制在 OpenCV
::
Mat
对象上。
recursive_bezier:该函数使用一个控制点序列和一个浮点数
t
作为输入,实现 de Casteljau
算法来返回
Bézier
曲线上对应点的坐标。
实现
本次的任务相比上一次简单。代码量很少且原理简单,这里直接给出代码。
recursive_bezier
cv::Point2f recursive_bezier(const std::vector<cv::Point2f> &control_points, float t)
{
auto points = control_points; //获取控制点序列
//当控制点序列点不为1时,每次都遍历一次序列,获取新的点序列
while(points.size() >1){
std::vector<cv::Point2f> temp = {};
for(int i =0;i<points.size()-1;i++){
cv::Point2f point = points[i] + (points[i+1] - points[i]) * t;
temp.push_back(point);
}
points = temp;
}
return points[0];
}
bezier
void bezier(const std::vector<cv::Point2f> &control_points, cv::Mat &window)
{
for (double t = 0.0; t <= 1.0; t += 0.001)
{
cv::Point2f point = recursive_bezier(control_points,t);
window.at<cv::Vec3b>(point.y, point.x)[2] = 255;
}
}
提高
反走样
这里考虑周围的3*3空间,每个像素点的中心点到该点的距离。可以很容易得出,最大距离为3√3/2,最短距离为0,因此可以按照这个比例,求出每个像素点到该点的距离后,再根据距离计算颜色的权重。(因为有一些像素会被重复计算,因此为了实现反走样,新的像素颜色要和老的像素颜色比较,取最大值。)
void bezier(const std::vector<cv::Point2f> &control_points, cv::Mat &window)
{
const float maxd = 3 * std::sqrt(2) / 2.0;
for (double t = 0.0; t <= 1.0; t += 0.001)
{
cv::Point2f point = recursive_bezier(control_points, t);
int pointx = (int)point.x;
int pointy = (int)point.y;
for (int i = -1; i <= 1; i++)
{
for (int j = -1; j <= 1; j++)
{
float x2 = std::pow(pointx + j - (point.x), 2);
float y2 = std::pow(pointy + i - (point.y), 2);
float d = std::sqrt(x2 + y2);
window.at<cv::Vec3b>(point.y + i, point.x + j)[1] = std::fmax(255 * (1 - d / maxd), window.at<cv::Vec3b>(point.y + i, point.x + j)[1]);
}
}
}
}
结果
正常的曲线
放大可以看到明显的锯齿
采用反走样的曲线
放大后可以看到平滑了很多