本文主要用于博主的备忘录。
1.原理
三次样条曲线公式:
2.代码实现
/* B样条曲线拟合 */
vector<Point2f> BSplineFit(const vector<Point2f>& controlPnts, double stride = 0.1) {
vector<Point2f> Outputs;
for (int i = 0; i < controlPnts.size() - 3; i++) {
Point2f cof[4];
cof[0] = (controlPnts[i] + 4 * controlPnts[(i + 1) % controlPnts.size()] + controlPnts[(i + 2) % controlPnts.size()]) / 6;
cof[1] = -(controlPnts[i] - controlPnts[(i + 2) % controlPnts.size()]) / 2;
cof[2] = (controlPnts[i] - 2 * controlPnts[(i + 1) % controlPnts.size()] + controlPnts[(i + 2) % controlPnts.size()]) / 2;
cof[3] = -(controlPnts[i] - 3 * controlPnts[(i + 1) % controlPnts.size()] + 3 * controlPnts[(i + 2) % controlPnts.size()] - controlPnts[(i + 3) % controlPnts.size()]) / 6;
for (double t = 0; t <= 1; t += stride) {
Point2f fittingPoints = Point2f(0, 0);
for (int j = 0; j < 4; j++) {
fittingPoints += cof[j] * pow(t, j);
}
Outputs.push_back(fittingPoints);
}
}
return Outputs;
}
3.测试
已知两段曲线上的点集,使用三次B样条曲线将其平滑连接。
#include <iostream>
#include <vector>
#include <opencv2\opencv.hpp>
#include <opencv2\opencv_modules.hpp>
using namespace cv;
using namespace std;
int main()
{
// 曲线点集1
vector<Point2f> pointpair1 = { Point2f(300, 450), Point2f(300, 440), Point2f(300, 430), Point2f(300, 420), Point2f(300, 410), Point2f(300, 400) };
// 曲线点集2
vector<Point2f> pointpair2 = { Point2f(600, 200), Point2f(610, 200), Point2f(620, 200), Point2f(630, 200), Point2f(640, 200), Point2f(650, 200) };
// 控制点点集
vector<Point2f> controlPoints = { Point2f(300, 500), Point2f(300, 400), Point2f(300, 300), Point2f(450, 200), Point2f(600, 200), Point2f(750, 200) };
vector<Point2f> output = BSplineFit(controlPoints);
// 为连接两曲线的端点,两端点也应为控制点,且位于输出曲线中
output.push_back(controlPoints[4]);
Mat img = Mat(800, 800, CV_32FC3, Scalar(0, 0, 0));
// 曲线点集1,白点
for (int i = 0; i < pointpair1.size(); i++) {
circle(img, pointpair1[i], 3, Scalar(255, 255, 255), -1);
}
// 曲线点集2,白点
for (int i = 0; i < pointpair2.size(); i++) {
circle(img, pointpair2[i], 3, Scalar(255, 255, 255), -1);
}
// 样条曲线拟合线,红线
for (int j = 0; j < output.size() - 1; j++) {
line(img, output[j], output[j + 1], Scalar(0, 0, 255), 1);
}
// 控制点,绿点
for (int i = 0; i < controlPoints.size(); i++) {
circle(img, controlPoints[i], 3, Scalar(0, 255, 0), -1);
}
imshow("PointsinImage", img);
waitKey(0);
return 0;
}