本文参考了https://www.cnblogs.com/hehehaha/p/6332245.html 的内容与python对3次样条插值实现的代码。再原有代码的基础上增加了固定边界和非节点边界的算法,并将接口函数的参数类型改写为Mat,方便一次进行多次样条插值的计算。
CubicSpline.h
#ifndef CUBIC_SPLINE_H
#define CUBIC_SPLINE_H
#include <iostream>
#include <vector>
#include <math.h>
#include <opencv2/core.hpp>
#include <opencv2/imgproc.hpp>
#include <opencv2/imgcodecs.hpp>
using namespace std;
using namespace cv;
class CubicSplineCoeffs_Mat
{
public:
CubicSplineCoeffs_Mat(const int &count)
{
a = std::vector<Mat>(count);
b = std::vector<Mat>(count);
c = std::vector<Mat>(count);
d = std::vector<Mat>(count);
}
~CubicSplineCoeffs_Mat()
{
std::vector<Mat>().swap(a);
std::vector<Mat>().swap(b);
std::vector<Mat>().swap(c);
std::vector<Mat>().swap(d);
}
public:
std::vector<Mat> a, b, c, d;
};
enum CubicSplineMode
{
CUBIC_NATURAL,
CUBIC_CLAMPED,
CUBIC_NOT_A_KNOT
};
class CubicSpline
{
public:
~CubicSpline();
public:
void calCubicSplineCoeffs(std::vector<Mat> &input_x,
std::vector<Mat> &input_y, CubicSplineCoeffs_Mat *&cubicCoeffs,
CubicSplineMode splineMode = CUBIC_NATURAL);
void cubicSplineInterpolation2(CubicSplineCoeffs_Mat *&cubicCoeffs,
std::vector<Mat> &input_x, Mat &x, Mat &y, int targeindex);
bool caltridiagonalMatrices(cv::Mat &input_a,
cv::Mat &input_b, cv::Mat &input_c,
cv::Mat &input_d, cv::Mat &output_x);
};
#endif // CUBIC_SPLINE_H
CubicSpline.cpp
#include "CubicSpline.h"
void CubicSpline::calCubicSplineCoeffs(
std::vector<Mat> &input_x,
std::vector<Mat> &input_y,
CubicSplineCoeffs_Mat *&cubicCoeffs,
CubicSplineMode splineMode)
{
int sizeOfx = input_x.size();
int sizeOfy = input_y.size();
if (sizeOfx != sizeOfy)
{
std::cout << "Data input error!" << std::endl <<
"Location: CubicSplineInterpolation.cpp" <<
" -> calCubicSplineCoeffs()" << std::endl;
return;
}
Size matsize = input_x[0].size();
int matrow = input_x[0].rows;
int matcol = input_x[0].cols;
std::vector<Mat> copy_y = input_y;
const int count = sizeOfx;
const int count1 = sizeOfx - 1;
const int count2 = sizeOfx - 2;
cubicCoeffs = new CubicSplineCoeffs_Mat(count1);
std::vector<Mat> step_h(count1);
std::vector<Mat> slope(count1);
int dims2[] = { count ,matrow,matcol };
Mat m_a(3, dims2, CV_64F, Scalar(0));
Mat m_b(3, dims2, CV_64F, Scalar(0));
Mat m_c(3, dims2, CV_64F, Scalar(0));
Mat m_d(3, dims2, CV_64F, Scalar(0));
Mat m_part(3, dims2, CV_64F, Scalar(0));
// initial step hi
for (int idx = 0; idx < count1; idx++)
{
step_h[idx] = input_x[idx + 1] - input_x[idx];
slope[idx] = (copy_y[idx + 1] - copy_y[idx]) / step_h[idx];
}
// initial coefficients
for (int idx = 0; idx < count2; idx++)
{
step_h[idx + 1].copyTo(Mat(step_h[idx + 1].size(), CV_64F, m_a.ptr(idx)));
Mat temp = 2 * (step_h[idx] + step_h[idx + 1]);
temp.copyTo(Mat(step_h[idx].size(), CV_64F, m_b.ptr(idx + 1)));
step_h[idx].copyTo(Mat(step_h[idx].size(), CV_64F, m_c.ptr(idx + 2)));
}
// initial d
for (int idx = 0; idx < count2; idx++)
{
Mat temp, temp2;
multiply(step_h[idx + 1], slope[idx], temp);
multiply(step_h[idx], slope[idx + 1], temp2);
temp = (temp + temp2) * 3;
temp.copyTo(Mat(step_h[idx].size(), CV_64F, m_d.ptr(idx + 1)));
}
if (splineMode == CUBIC_NATURAL)
{
Mat temp_m_b_start = 2 * step_h[0];
temp_m_b_start.copyTo(Mat(step_h[0].size(), CV_64F, m_b.ptr(0)));
step_h[0].copyTo(Mat(step_h[0].size(), CV_64F, m_c.ptr(1)));
Mat temp_m_d_start = 3 * (copy_y[1] - copy_y[0]);
temp_m_d_start.copyTo(Mat(copy_y[0].size(), CV_64F, m_d.ptr(0)));
Mat temp_m_b_end = 2 * step_h[count1 - 1];
temp_m_b_end.copyTo(Mat(step_h[count1 - 1].size(), CV_64F, m_b.ptr(count - 1)));
step_h[count1 - 1].copyTo(Mat(step_h[0].size(), CV_64F, m_a.ptr(count - 2)));
Mat temp_m_d_end = 3 * (copy_y[count - 1] - copy_y[count - 2]);
temp_m_d_end.copyTo(Mat(copy_y[count - 1].size(), CV_64F, m_d.ptr(count - 1)));
}
else if (splineMode == CUBIC_NOT_A_KNOT) {
step_h[1].copyTo(Mat(step_h[1].size(), CV_64F, m_b.ptr(0)));
Mat temp_m_b_start = input_x[2] - input_x[0];
temp_m_b_start.copyTo(Mat(input_x[0].size(), CV_64F, m_c.ptr(1)));
Mat d = input_x[2] - input_x[0];
Mat temp_m_d_start;
multiply(step_h[0] + 2 * d, step_h[1], temp_m_d_start);
multiply(temp_m_d_start, slope[0], temp_m_d_start);
Mat temp;
cv::pow(step_h[0], 2, temp);
multiply(temp, slope[1], temp);
temp_m_d_start = (temp_m_d_start + temp) / d;
temp_m_d_start.copyTo(Mat(step_h[0].size(), CV_64F, m_d.ptr(0)));
step_h[count1 - 2].copyTo(Mat(step_h[count1 - 2].size(), CV_64F, m_b.ptr(count - 1)));
Mat temp_m_a_end = input_x[count - 1] - input_x[count - 3];
temp_m_a_end.copyTo(Mat(input_x[count - 1].size(), CV_64F, m_a.ptr(count - 2)));
d = input_x[count - 1] - input_x[count - 3];
Mat temp_m_d_end;
pow(step_h[count1 - 1], 2, temp_m_d_end);
multiply(temp_m_d_end, slope[count1 - 2], temp_m_d_end);
multiply(2 * d + step_h[count1 - 1], step_h[count1 - 2], temp);
multiply(temp, slope[count1 - 1], temp);
temp_m_d_end = (temp_m_d_end + temp) / d;
temp_m_d_end.copyTo(Mat(step_h[count1 - 1].size(), CV_64F, m_d.ptr(count - 1)));
}
else if (splineMode == CUBIC_CLAMPED) {
Mat(matrow, matcol, CV_64F, m_b.ptr(0)).setTo(1);
Mat(matrow, matcol, CV_64F, m_c.ptr(1)).setTo(0);
Mat(matrow, matcol, CV_64F, m_d.ptr(0)).setTo(0);
Mat(matrow, matcol, CV_64F, m_b.ptr(count - 1)).setTo(1);
Mat(matrow, matcol, CV_64F, m_a.ptr(count - 2)).setTo(0);
Mat(matrow, matcol, CV_64F, m_d.ptr(count - 1)).setTo(0);
}
bool isSucceed = caltridiagonalMatrices(m_a, m_b, m_c, m_d, m_part);
if (!isSucceed)
{
std::cout << "Calculate tridiagonal matrices failed!" << std::endl <<
"Location: CubicSplineInterpolation.cpp -> " <<
"caltridiagonalMatrices()" << std::endl;
return;
}
for (int i = 0; i < count1; i++)
{
Mat t = (Mat(matrow, matcol, m_part.type(), m_part.ptr(i)) + Mat(matrow, matcol, m_part.type(), m_part.ptr(i + 1)) - 2 * slope[i]) / step_h[i];
cubicCoeffs->a[i] = copy_y[i];
Mat(matrow, matcol, m_part.type(), m_part.ptr(i)).copyTo(cubicCoeffs->b[i]);
cubicCoeffs->c[i] = (slope[i] - Mat(matrow, matcol, m_part.type(), m_part.ptr(i))) / step_h[i] - t;
cubicCoeffs->d[i] = t / step_h[i];
}
}
CubicSpline::~CubicSpline() {
}
void CubicSpline::cubicSplineInterpolation2(CubicSplineCoeffs_Mat *&cubicCoeffs,
std::vector<Mat> &input_x, Mat &x, Mat &y, int targeindex) {
Mat cmpresult_self;
cv::compare(x, input_x[targeindex], cmpresult_self, CMP_GE);
Mat temp_cmp(cmpresult_self.size(), cmpresult_self.type());
cmpresult_self.copyTo(temp_cmp);
Mat temp_a(x.size(), CV_64F), temp_b(x.size(), CV_64F), temp_c(x.size(), CV_64F), temp_d(x.size(), CV_64F), dertx(x.size(), CV_64F);
int shift_index = 0;
do {
if (targeindex + shift_index >= cubicCoeffs->a.size()) {
break;
}
Mat tempx = x - input_x[targeindex + shift_index];
tempx.copyTo(dertx, temp_cmp);
cubicCoeffs->a[targeindex + shift_index].copyTo(temp_a, temp_cmp);
cubicCoeffs->b[targeindex + shift_index].copyTo(temp_b, temp_cmp);
cubicCoeffs->c[targeindex + shift_index].copyTo(temp_c, temp_cmp);
cubicCoeffs->d[targeindex + shift_index].copyTo(temp_d, temp_cmp);
shift_index++;
if (targeindex + shift_index >= input_x.size()) {
break;
}
cv::compare(x, input_x[targeindex + shift_index], temp_cmp, CMP_GE);
} while (countNonZero(temp_cmp) > 0);
shift_index = 1;
cv::bitwise_not(cmpresult_self, temp_cmp);
do {
if (targeindex - shift_index < 0) {
break;
}
Mat tempx = x - input_x[targeindex - shift_index];
tempx.copyTo(dertx, temp_cmp);
cubicCoeffs->a[targeindex - shift_index].copyTo(temp_a, temp_cmp);
cubicCoeffs->b[targeindex - shift_index].copyTo(temp_b, temp_cmp);
cubicCoeffs->c[targeindex - shift_index].copyTo(temp_c, temp_cmp);
cubicCoeffs->d[targeindex - shift_index].copyTo(temp_d, temp_cmp);
shift_index++;
if (targeindex - shift_index < 0) {
break;
}
cv::compare(x, input_x[targeindex - shift_index], temp_cmp, CMP_LT);
} while (countNonZero(temp_cmp) > 0);
y.setTo(-1);
Mat temp1;
cv::multiply(temp_b, dertx, temp1);
Mat temp2;
cv::pow(dertx, 2, temp2);
cv::multiply(temp_c, temp2, temp2);
Mat temp3;
cv::pow(dertx, 3, temp3);
cv::multiply(temp_d, temp3, temp3);
y = temp_a + temp1 + temp2 + temp3;
}
bool CubicSpline::caltridiagonalMatrices(cv::Mat &input_a,
cv::Mat &input_b, cv::Mat &input_c,
cv::Mat &input_d, cv::Mat &output_x) {
const int N = input_a.size[0];
const int matrow = input_a.size[1];
const int matcol = input_a.size[2];
input_d.copyTo(output_x);
Mat _input_b_0 = Mat(matrow, matcol, input_b.type(), input_b.ptr(0));
Mat _input_c_1 = Mat(matrow, matcol, input_c.type(), input_c.ptr(1));
Mat _output_x_0 = Mat(matrow, matcol, output_x.type(), output_x.ptr(0));
if (cv::countNonZero(_input_b_0) == matrow * matcol)
{
_input_c_1 /= _input_b_0;
_output_x_0 /= _input_b_0;
}
else
{
return false;
}
for (int n = 1; n < N; n++) {
Mat m;
cv::multiply(Mat(matrow, matcol, input_a.type(), input_a.ptr(n - 1)), Mat(matrow, matcol, input_c.type(), input_c.ptr(n)), m);
m = 1.0 / (Mat(matrow, matcol, input_b.type(), input_b.ptr(n)) - m);
if (n < (N - 1)) {
multiply(Mat(matrow, matcol, input_c.type(), input_c.ptr(n + 1)), m, Mat(matrow, matcol, input_c.type(), input_c.ptr(n + 1)));
}
Mat temp;
multiply(Mat(matrow, matcol, input_a.type(), input_a.ptr(n - 1)), Mat(matrow, matcol, output_x.type(), output_x.ptr(n - 1)), temp);
temp = Mat(matrow, matcol, output_x.type(), output_x.ptr(n)) - temp;
multiply(temp, m, Mat(matrow, matcol, output_x.type(), output_x.ptr(n)));
}
for (int n = N - 2; n >= 0; --n) {
Mat temp;
multiply(Mat(matrow, matcol, input_c.type(), input_c.ptr(n + 1)), Mat(matrow, matcol, output_x.type(), output_x.ptr(n + 1)), temp);
Mat(matrow, matcol, output_x.type(), output_x.ptr(n)) = Mat(matrow, matcol, output_x.type(), output_x.ptr(n)) - temp;
}
return true;
}
splinetest.cpp
#include "CubicSpline.h"
void main()
{
std::vector<Mat> input_x;
std::vector<Mat> input_y;
cv::Mat input_x_0(1, 1, CV_64F, 1);
cv::Mat input_x_1(1, 1, CV_64F, 2);
cv::Mat input_x_2(1, 1, CV_64F, 3);
cv::Mat input_x_3(1, 1, CV_64F, 5);
cv::Mat input_x_4(1, 1, CV_64F, 6);
input_x.push_back(input_x_0);
input_x.push_back(input_x_1);
input_x.push_back(input_x_2);
input_x.push_back(input_x_3);
input_x.push_back(input_x_4);
cv::Mat input_y_0(1, 1, CV_64F, 84.4049866);
cv::Mat input_y_1(1, 1, CV_64F, 96.15193733);
cv::Mat input_y_2(1, 1, CV_64F, 100.2343023);
cv::Mat input_y_3(1, 1, CV_64F, 101.0295086);
cv::Mat input_y_4(1, 1, CV_64F, 100.8174606);
input_y.push_back(input_y_0);
input_y.push_back(input_y_1);
input_y.push_back(input_y_2);
input_y.push_back(input_y_3);
input_y.push_back(input_y_4);
CubicSplineCoeffs_Mat *cubicCoeffs;
CubicSpline cubicSpline;
cubicSpline.calCubicSplineCoeffs(input_x, input_y, cubicCoeffs, CUBIC_NATURAL);
Mat result;
Mat mat_64(1, 1, CV_64F, Scalar(2.5));
cubicSpline.cubicSplineInterpolation2(cubicCoeffs, input_x, mat_64, result, 2);
cout<<"input 2.5 output = " << result.at<double>(0) << endl;
}
运行后输出: input 2.5 output = 98.9983