Games101--Assignment4


Assignment4的要求

實現de Casteljau 算法來繪制由4 個控制點表示的Bézier 曲線(當你正確實現該算法時,你可以支持繪制由更多點來控制的Bézier 曲線)。
• 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 曲
    線。

具體步驟

注意1的是OpenCV中的坐標系基於rows和columns,因為圖像被編碼為矩陣形式,矩陣索引先行后列,所以定義pixel的使用的是先y后x,y即為第幾列,x即為第幾行;
注意2的是OpenCV使用的是BGR顏色存儲順序;

recursive_bezier函數如下:

cv::Point2f recursive_bezier(const std::vector<cv::Point2f> &control_points, float t) 
{
    // TODO: Implement de Casteljau's algorithm
    cv::Point2f ret;
    if(control_points.size()==1) ret=*control_points.begin();
    std::vector<cv::Point2f> old_points(control_points),new_points;
    while(old_points.size()!=1){
        for(int i=0;i<old_points.size()-1;i++){
            new_points.push_back(old_points[i]*(1-t)+old_points[i+1]*t);
        }
        old_points.clear();
        old_points=new_points;
        new_points.clear();
    }
    ret=*old_points.begin();
    return ret;
}

bezier函數如下:

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.0; t <= 1.0; t += 0.001){
        auto p=recursive_bezier(control_points,t);
        //window.at<cv::Vec3b>(p.y, p.x)[0] = 255;  //B
        window.at<cv::Vec3b>(p.y, p.x)[1] = 255;  //G
        //window.at<cv::Vec3b>(p.y, p.x)[2] = 102;  //R
    }
}

使用6個控制點:

#include <chrono>
#include <iostream>
#include <opencv2/opencv.hpp>
std::vector<cv::Point2f> control_points;
void mouse_handler(int event, int x, int y, int flags, void *userdata) 
{
    if (event == cv::EVENT_LBUTTONDOWN && control_points.size() < 6) 
    {
        std::cout << "Left button of the mouse is clicked - position (" << x << ", "<< y << ")" << '\n';
        control_points.emplace_back(x, y);
    }     
}
void naive_bezier(const std::vector<cv::Point2f> &points, cv::Mat &window) 
{
    auto &p_0 = points[0];
    auto &p_1 = points[1];
    auto &p_2 = points[2];
    auto &p_3 = points[3];
    for (double t = 0.0; t <= 1.0; t += 0.001) 
    {
        auto point = std::pow(1 - t, 3) * p_0 + 3 * t * std::pow(1 - t, 2) * p_1 +
                 3 * std::pow(t, 2) * (1 - t) * p_2 + std::pow(t, 3) * p_3;
        //window.at<cv::Vec3b>(point.y, point.x)[0] = 255; //B
        window.at<cv::Vec3b>(point.y, point.x)[1] = 255; //G
        //window.at<cv::Vec3b>(point.y, point.x)[2] = 255; //R
    }
}
cv::Point2f recursive_bezier(const std::vector<cv::Point2f> &control_points, float t) 
{
    // TODO: Implement de Casteljau's algorithm
    cv::Point2f ret;
    if(control_points.size()==1) ret=*control_points.begin();
    std::vector<cv::Point2f> old_points(control_points),new_points;
    while(old_points.size()!=1){
        for(int i=0;i<old_points.size()-1;i++){
            new_points.push_back(old_points[i]*(1-t)+old_points[i+1]*t);
        }
        old_points.clear();
        old_points=new_points;
        new_points.clear();
    }
    ret=*old_points.begin();
    return ret;
}
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.0; t <= 1.0; t += 0.001){
        auto p=recursive_bezier(control_points,t);
        window.at<cv::Vec3b>(p.y, p.x)[0] = 255;  //B
        window.at<cv::Vec3b>(p.y, p.x)[1] = 204;  //G
        window.at<cv::Vec3b>(p.y, p.x)[2] = 102;  //R
    }
}
int main() 
{
    cv::Mat window = cv::Mat(700, 700, CV_8UC3, cv::Scalar(0));
    cv::cvtColor(window, window, cv::COLOR_BGR2RGB);
    cv::namedWindow("Bezier Curve", cv::WINDOW_AUTOSIZE);
    cv::setMouseCallback("Bezier Curve", mouse_handler, nullptr);
    int key = -1;
    while (key != 27) 
    {
        for (auto &point : control_points) 
        {
            cv::circle(window, point, 3, {255, 255, 255}, 3);
        }
        if (control_points.size() == 6) 
        {
            //naive_bezier(control_points, window);
            bezier(control_points, window);

            cv::imshow("Bezier Curve", window);
            cv::imwrite("my_bezier_curve.png", window);
            key = cv::waitKey(0);

            return 0;
        }
        cv::imshow("Bezier Curve", window);
        key = cv::waitKey(20);
    }
return 0;
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM