Theory:
Bezier curve:
i represents the order, and j represents the index of the point.
So it can be implemented recursively!
Antialiasing:
According to the instructions, for each point obtained on the curve, we color the surrounding 3×3 pixels based on the distance, denoted as d, from each pixel's center to the point. This is to achieve a smooth transition effect, where each pixel's color is calculated as 255 times the ratio. The range of d is [0, 3/√2], and the range of ratio is [0, 1]. The function of ratio with respect to d is defined as ratio = 1 - √2/3d. Points that are recalculated take the maximum value of their colors. This approach ensures that there are no dark spots in the middle of the line segments, theoretically achieving the requirements for anti-aliasing:
【GAMES101】作业4(提高)含Bazier曲线的反走样处理_games101作业4反走样-CSDN博客
Why is dmax=3/sqrt(2)? This is a question I encountered after reading this article, and the author's explanation was rather vague.
explanation :
Ratio is used to map the range from 0 to 3/sqrt(2) to the range from 0 to 1.
Code:
Bezier curve:
cv::Point2f recursive_bezier(const std::vector<cv::Point2f> &control_points, float t)
{
// auto p0= control_points[0];
// auto p1= control_points[1];
// auto p2= control_points[2];
// auto p3= control_points[3];
// auto p10 = (1-t)*p0+t*p1;
// auto p12 = (1-t)*p1+t*p2;
// auto p13 = (1-t)*p2+t*p3;
// auto p20 = (1-t)*p10+t*p12;
// auto p21 = (1-t)*p12+t*p13;
// auto p30 = (1-t)*p20+t*p21;
// return p30;
// TODO: Implement de Casteljau's algorithm
if(control_points.size()==1){return control_points[0];}
std::vector<cv::Point2f> v;
for(int i=0;i<control_points.size()-1;i++)
{
cv::Point2f p=t*control_points[i]+(1-t)*control_points[i+1];
v.push_back(p);
}
return recursive_bezier(v,t);
}
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.
for(float t=0;t<1;t+=0.00001f){
auto point= recursive_bezier(control_points, t);
// 窗口与像素访问:这里的window是一个OpenCV定义的Mat类型对象,它通常用来存储图像数据。
//at< cv::Vec3b >是Mat类的一个成员函数,用于按模板类型访问矩阵(这里代表图像)中的元素。
//在这个情况下,cv::Vec3b表示一个BGR(蓝色、绿色、红色)三通道的像素值。
// 坐标定位:point是一个Point结构体或类似的类型,它包含了二维平面内的(x, y)坐标。
//在这里,(point.y, point.x)是对图像进行索引的坐标,因为OpenCV中图像的行优先存储顺序是先列 后行,所以先用y轴坐标再用x轴坐标。
// 像素值修改:window.at<cv::Vec3b>(point.y, point.x)返回了对应于给定点 (point.x, point.y) 的像素值,
//这个像素值被表示为一个包含三个8位无符号整数(分别对应BGR三个通道)的向量。
// 设置绿色通道为最大值:最后的[1]=255操作,是对上述得到的像素向量的第二个元素(即绿色通道)赋值为255。
//颜色通道的索引是从0开始的,因此[1]指的是绿色通道。将绿色通道值设置为255意味着将该像素的绿色部分设为最大亮度,不影响蓝色和红色通道的原有值。
window.at<cv::Vec3b>(point.y,point.x)[1]=255;
}
Antialiasing:
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.
for(float t=0;t<1;t+=0.00001f){
auto point= recursive_bezier(control_points, t);
for(int i=-1;i<=1;i++){
for(int j=-1;j<=1;j++){
auto pixel_x = floor(point.x) + i+0.5f;
auto pixel_y = floor(point.y) + j+0.5f;
auto dis_x = abs(point.x - pixel_x );
auto dis_y = abs(point.y - pixel_y );
auto dis = sqrt(dis_x * dis_x + dis_y * dis_y);
// max distance is 3/sqrt(2)
window.at<cv::Vec3b>(pixel_y, pixel_x)[1] = std::max(255 * (1 - dis * (1.414 / 3.0)), (double)window.at<cv::Vec3b>(pixel_y, pixel_x)[1]);
//https://blog.csdn.net/weixin_42489848/article/details/124918043
}
}
}
}
Why use max: Some points already have filled colors and should not be overwritten by smaller values.
Result:
You need to change the call in the main function:
cool!