B样条曲线的方程:P=∑i=0nPiFi,k(t)\sum_{i=0}^nP_iF_{i,k}(t)∑i=0nPiFi,k(t)
其中Fi,k(t)F_{i,k}(t)Fi,k(t)为基函数,三次B样条的基函数为:
F0,3(t)=16(1−t)3F_{0,3}(t)=\displaystyle{\frac{1}{6}{(1-t)}^3}F0,3(t)=61(1−t)3
F1,3(t)=16(3t3−6t2+4)F_{1,3}(t)=\displaystyle{\frac{1}{6}(3t^3-6t^2+4)}F1,3(t)=61(3t3−6t2+4)
F2,3(t)=16(−3t3+3t2+3t+1)F_{2,3}(t)=\displaystyle{\frac{1}{6}(-3t^3+3t^2+3t+1)}F2,3(t)=61(−3t3+3t2+3t+1)
F3,3(t)=16t3F_{3,3}(t)=\displaystyle{\frac{1}{6}t^3}F3,3(t)=61t3
所以,三次B样条的方程式为:
P=P0F0,3(t)+P1F1,3(t)+P2F2,3(t)+P3F3,3(t)P=P_0F_{0,3}(t)+P_1F_{1,3}(t)+P_2F_{2,3}(t)+P_3F_{3,3}(t)P=P0F0,3(t)+P1F1,3(t)+P2F2,3(t)+P3F3,3(t)
把基函数代入可以简化为:
P=w0+w1t+w2t2+w3t3P=w_0+w_1t+w_2t^2+w_3t^3P=w0+w1t+w2t2+w3t3 (0≤t<≤1)
其中,w0=16(P0+4P1+P2)w_0=\displaystyle{\frac{1}{6}(P_0+4P_1+P_2)}w0=61(P0+4P1+P2)
w1=−12(P0−P2)w_1=\displaystyle{-\frac{1}{2}(P_0-P_2)}w1=−21(P0−P2)
w2=12(P0−2P1+P2)w_2=\displaystyle{\frac{1}{2}(P_0-2P_1+P_2)}w2=21(P0−2P1+P2)
w3=−16(P0−3P1+3P2−P3)w_3=\displaystyle{-\frac{1}{6}(P_0-3P_1+3P_2-P_3)}w3=−61(P0−3P1+3P2−P3)
因此,每四个离散点就可拟合一段曲线,比如P0,P1,P2,P3P_0,P_1,P_2,P_3P0,P1,P2,P3可以拟合一段光滑曲线,P1,P2,P3,P4P_1,P_2,P_3,P_4P1,P2,P3,P4可以拟合下一段,相邻两段曲线是平滑过渡的,以此类推N个点可以拟合出N-3段平滑相接的曲线。
以上是非闭合曲线的拟合,闭合曲线只需离散点集首尾相连,也就是说,还需用PN−1,P0,P1,P2P_{N-1},P_0,P_1,P_2PN−1,P0,P1,P2拟合一段曲线。
c++代码如下:
/*B样条曲线拟合
@return 返回拟合得到的曲线
@discretePoints 输入的离散点,至少4个点
@closed 是否拟合闭合曲线,true表示闭合,false不闭合
@stride 拟合精度
*/
vector<Point2f> BSplineFit(vector<Point2f> discretePoints, bool closed, double stride = 0.01) {
vector<Point2f> fittingPoints;
for (int i = 0; i < (closed ? discretePoints.size() : discretePoints.size() - 1); i++) {
Point2f xy[4];
xy[0] = (discretePoints[i] + 4 * discretePoints[(i + 1) % discretePoints.size()] + discretePoints[(i + 2) % discretePoints.size()]) / 6;
xy[1] = -(discretePoints[i] - discretePoints[(i + 2) % discretePoints.size()]) / 2;
xy[2] = (discretePoints[i] - 2 * discretePoints[(i + 1) % discretePoints.size()] + discretePoints[(i + 2) % discretePoints.size()]) / 2;
xy[3] = -(discretePoints[i] - 3 * discretePoints[(i + 1) % discretePoints.size()] + 3 * discretePoints[(i + 2) % discretePoints.size()] - discretePoints[(i + 3) % discretePoints.size()]) / 6;
for (double t = 0; t <= 1; t += stride) {
Point2f totalPoints = Point2f(0, 0);
for (int j = 0; j < 4; j++) {
totalPoints += xy[j] * pow(t, j);
}
fittingPoints.push_back(totalPoints);
}
}
return fittingPoints;
}
非闭合拟合效果:

闭合曲线拟合效果:


本文介绍了如何使用C++实现三次B样条曲线拟合,通过基函数公式推导出权重系数,并展示了非闭合和闭合曲线的拟合效果。
395





