n阶贝塞尔曲线的理解以及c++编程实现

贝塞尔曲线的原理(BezierCruve)

查看原理戳我
这里就不介绍推导过程了,值得注意的是文章中的线段都是矢量,然后线段都可以被拆分成两个坐标的差来表示 A B = B − A AB=B-A AB=BA一开始看有点蒙蔽,代入这个等式后,之后所有的推导都可以理解。这里就直接介绍一下结论 P i k ( t ) = { P i ( t ) k=0 ( 1 − t ) P i k − 1 ( t ) + t P i + 1 k − 1 ( t ) , k=1,2,3 … n;i=0,1,2 … n-k P_i^k(t)= \begin{cases} P_i(t) & \text {k=0} \\ (1-t)P_i^{k-1}(t)+tP_{i+1}^{k-1}(t), & \text{k=1,2,3…n;i=0,1,2…n-k} \end{cases} Pik(t)={Pi(t)(1t)Pik1(t)+tPi+1k1(t),k=0k=1,2,3n;i=0,1,2n-k原文中的介绍没有带t,一开始以为 P i k P_i^k Pik是常量,觉得十分诡异,后来重新看了下推导过程,其实都是变量,这里补上自变量t,并且这里的 k k k不是指k次方的意思,是指第k阶,可以理解为一个区分开来的不同阶数的上标。

贝塞尔曲线的理解

首先从结果的公式上来看

  1. 一条贝塞尔至少要有两个控制点(此时的贝塞尔曲线为一条直线)

控制点: 当k等于0时,对应的 P i k ( t ) P_i^k(t) Pik(t),即输入点

  1. 这是一条迭代公式,每次迭代都会少掉一个“点”。

“点”: P i + 1 P_{i+1} Pi+1,可以看到每次的每次新迭代生成的 P i k ( t ) P_i^k(t) Pik(t)都由 ( 1 − t ) P i k − 1 ( t ) + t P i + 1 k − 1 ( t ) (1-t)P_i^{k-1}(t)+tP_{i+1}^{k-1}(t) (1t)Pik1(t)+tPi+1k1(t)生成,所以第k次迭代的时候,只剩下n-k个点,即 i < = n − k i<=n-k i<=nk

  1. 我们最终要得到的贝塞尔曲线是 P 0 n ( t ) P_0^n(t) P0n(t),所以迭代的终止条件便是i得最大值只能为1(因为从0开始计数),即 P 0 n ( t ) = ( 1 − t ) P 0 n − 1 ( t ) + t P 1 n − 1 ( t ) P_0^n(t)=(1-t)P_0^{n-1}(t)+tP_{1}^{n-1}(t) P0n(t)=(1t)P0n1(t)+tP1n1(t)
  2. 贝塞尔曲线必然经过第一个控制点和最后一个控制点。

贝塞尔曲线的编程实现

  1. 由于贝塞尔曲线本身的数学表达式便是一条递归式,所以决定采用递归的方式来实现。
  2. 由于都是对点进行四则运算,直接采用opencv的数据结构,都已经重载好了,顺便可以用opencv来显示结果。
  3. 当然如果不需要显示和不嫌麻烦的话,可以自己写Point2f这个结构,并且重载乘法运算符,也是可以实现的,那样就不用调用opencv了。

#include 
#include 
#include 
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;
}

实现效果

n阶贝塞尔曲线的理解以及c++编程实现_第1张图片

你可能感兴趣的:(opencv)