n階貝塞爾曲線的理解以及c++程式設計實現
阿新 • • 發佈:2018-12-21
貝塞爾曲線的原理(BezierCruve)
檢視原理戳我
這裡就不介紹推導過程了,值得注意的是文章中的線段都是向量,然後線段都可以被拆分成兩個座標的差來表示
一開始看有點矇蔽,代入這個等式後,之後所有的推導都可以理解。這裡就直接介紹一下結論
原文中的介紹沒有帶t,一開始以為
是常量,覺得十分詭異,後來重新看了下推導過程,其實都是變數,這裡補上自變數t,並且這裡的
不是指k次方的意思,是指第k階,可以理解為一個區分開來的不同階數的上標。
貝塞爾曲線的理解
首先從結果的公式上來看
- 一條貝塞爾至少要有兩個控制點(此時的貝塞爾曲線為一條直線)
控制點: 當k等於0時,對應的 ,即輸入點
- 這是一條迭代公式,每次迭代都會少掉一個“點”。
“點”: 即 ,可以看到每次的每次新迭代生成的 都由 生成,所以第k次迭代的時候,只剩下n-k個點,即
- 我們最終要得到的貝塞爾曲線是 ,所以迭代的終止條件便是i得最大值只能為1(因為從0開始計數),即
- 貝塞爾曲線必然經過第一個控制點和最後一個控制點。
貝塞爾曲線的程式設計實現
- 由於貝塞爾曲線本身的數學表示式便是一條遞迴式,所以決定採用遞迴的方式來實現。
- 由於都是對點進行四則運算,直接採用opencv的資料結構,都已經過載好了,順便可以用opencv來顯示結果。
- 當然如果不需要顯示和不嫌麻煩的話,可以自己寫Point2f這個結構,並且過載乘法運算子,也是可以實現的,那樣就不用呼叫opencv了。
#include <iostream>
#include <opencv2/opencv.hpp>
#include <vector>
using namespace cv;
using std::cout;
using std::endl;
using std::vector;
vector<Point2f> bezierCurve(vector<Point2f> src);
int main(int argc, char const* argv[])
{
while (1) {
vector<Point2f> path;
CvRNG rng;
rng = cvRNG(cvGetTickCount());
for (int i = 1; i < 6; i++)
path.push_back(Point2f(i * 800 / 6.0, cvRandInt(&rng) % 800));
Mat img(800, 800, CV_8UC3);
img = 0;
for (int i = 0; i < path.size(); i++)
circle(img, path[i], 3, Scalar(0, 0, 255), 3); //BGR
vector<Point2f> bezierPath = bezierCurve(path);
for (int i = 0; i < bezierPath.size(); i++) {
//circle(img, bezierPath[i], 3, Scalar(0, 255, 255), 3); //BGR
img.at<cv::Vec3b>(cvRound(bezierPath[i].y), cvRound(bezierPath[i].x)) = { 0, 255, 255 };
}
imshow("black", img);
if (waitKey(0) == 'q')
break;
}
return 0;
}
vector<Point2f> bezierCurve(vector<Point2f> src)
{
if (src.size() < 1)//這種情況是不允許出現的,出現只能證明程式出錯了
return src;
const float step = 0.01;//採集100個點,即1.0/step
vector<Point2f> res;
if (src.size() == 1) {//遞迴結束條件,k=0
for (float t = 0; t < 1; t += step)
res.push_back(src[0]);//為了和其他情況保持一致,生成了1.0/step個一樣的點
return res;
}
vector<Point2f> src1;
vector<Point2f> src2;
src1.assign(src.begin(), src.end() - 1);//分成兩部分,即Pi和Pi+1
src2.assign(src.begin() + 1, src.end());
for (int i = 0; i < src1.size(); i++)
cout << src1[i] << endl;
cout << endl;
for (int i = 0; i < src2.size(); i++)
cout << src2[i] << endl;
cout << endl;
vector<Point2f> pln1 = bezierCurve(src1);
vector<Point2f> pln2 = bezierCurve(src2);
for (float t = 0; t < 1; t += step) {
Point2f temp;
temp = (1.0 - t) * pln1[cvRound(1.0 / step * t)] + t * pln2[cvRound(1.0 / step * t)];
res.push_back(temp);
}
return res;
}