OpenCV 拟合抛物线精简版

直接上代码,原理自行寻找,网上多得很

#include <opencv2/opencv.hpp> // opencv
#include <stdio.h> // 内存操作
#include <stdlib.h>
#include <iostream>

using namespace cv;

// 二维离散点拟合抛物线的三种写法,三种表达式均为 y = a·X^2 + b·X + c
// 方法一:利用 CvMat
void fitParabola_1(const std::vector<cv::Point2f> &vecPoints, float &a, float &b, float &c) {

    // 初始化
    CvMat *matA = cvCreateMat(vecPoints.size(), 3, CV_32FC1);
    CvMat *matB = cvCreateMat(vecPoints.size(), 1, CV_32FC1);
    CvMat *matC = cvCreateMat(3, 1, CV_32FC1);

    // 构造 A
    float *A = (float *)malloc(sizeof(float) * vecPoints.size() * 3);
    for (int i = 0; i < vecPoints.size(); ++i) {
        A[3 * i + 0] = vecPoints[i].x * vecPoints[i].x;
        A[3 * i + 1] = vecPoints[i].x;
        A[3 * i + 2] = 1;
    }
    // 构造 B
    float *B = (float *)malloc(sizeof(float) * vecPoints.size());
    for (int i = 0; i < vecPoints.size(); ++i) {
        B[i] = vecPoints[i].y;
    }
    // 赋值
    cvSetData(matA, A, CV_AUTOSTEP);
    cvSetData(matB, B, CV_AUTOSTEP);
    // 求抛物线参数
    cvSolve(matA, matB, matC, CV_LU);
    // 返回值
    a = matC->data.fl[0];
    b = matC->data.fl[1];
    c = matC->data.fl[2];
    // 释放空间
    if (!A) free(A);
    if (!B) free(B);
}

// 方法二:只能针对三个点进行拟合,多个点要报错,尚且不知道为什么
void fitParabola_2(const std::vector<cv::Point2f> &vecPoints, float &a, float &b, float &c) {

    // 目前只能用三个点进行拟合,后期修复
    if (vecPoints.size() != 3) {
        return;
    }

    // 初始化 Mat
    cv::Mat matA(vecPoints.size(), 3, CV_32FC1);
    cv::Mat matB(vecPoints.size(), 1, CV_32FC1);
    cv::Mat matC(3, 1, CV_32FC1);

    // 构造元素
    for (int i = 0; i < vecPoints.size(); ++i) {
        matA.at<float>(i, 0) = vecPoints[i].x * vecPoints[i].x;
        matA.at<float>(i, 1) = vecPoints[i].x;
        matA.at<float>(i, 2) = 1;
    }
    for (int i = 0; i < vecPoints.size(); ++i) {
        matB.at<float>(i, 0) = vecPoints[i].y;
    }
    cv::solve(matA, matB, matC, CV_LU);

    // 返回值
    a = matC.at<float>(0, 0);
    b = matC.at<float>(0, 1);
    c = matC.at<float>(0, 2);
}

// 方法三
// 参考博客:OpenCV最小二乘法:cv::solve函数
// https://blog.csdn.net/qq_20797273/article/details/83930101
void fitParabola_3(const std::vector<cv::Point2f> &vecPoints, float &a, float &b, float &c) {
    // 代表拟合的是二次方程,取 3 则为三次曲线样条
    int pow_number = 2;
    // 离散点个数
    int N = vecPoints.size();
    // 构造 X
    cv::Mat X = cv::Mat::zeros(pow_number + 1, pow_number + 1, CV_64FC1);
    for (int i = 0; i < pow_number + 1; i++) {
        for (int j = 0; j < pow_number + 1; j++) {
            for (int k = 0; k < N; k++) {
                X.at<double>(i, j) = X.at<double>(i, j) + std::pow(vecPoints[k].x, i + j);
            }
        }
    }
    // 构造 Y
    cv::Mat Y = cv::Mat::zeros(pow_number + 1, 1, CV_64FC1);
    for (int i = 0; i < pow_number + 1; i++) {
        for (int k = 0; k < N; k++) {
            Y.at<double>(i, 0) = Y.at<double>(i, 0) + std::pow(vecPoints[k].x, i) * vecPoints[k].y;
        }
    }

    // 求解 A
    cv::Mat A = cv::Mat::zeros(pow_number + 1, 1, CV_64FC1);
    cv::solve(X, Y, A, CV_LU/*DECOMP_LU*/);

    // 输出结果,如果是三次表达式,则继续倒着往回取值
    a = A.at<double>(2, 0);
    b = A.at<double>(1, 0);
    c = A.at<double>(0, 0);
}


int main() {

    // 随机新建离散点
    std::vector<cv::Point2f> vecPoints;
    vecPoints.emplace_back(1, 1);
    vecPoints.emplace_back(7, 6);
    vecPoints.emplace_back(2, 8);
    vecPoints.emplace_back(10, 36);

    // 分别测试拟合函数
    {
        float a, b, c;
        fitParabola_1(vecPoints, a, b, c);
        cout << a << " " << b << " " << c << endl;
        int x = 2;
        std::cout << "fitParabola_1 - y = " << a *x *x + b *x + c << std::endl;
    }

    {
        // 注意:方法二只能针对三个点,点多要崩溃,正在解决中
        //float a, b, c;
        //fitParabola_2(vecPoints, a, b, c);
        //cout << a << " " << b << " " << c << endl;
        //int x = 2;
        //std::cout << "fitParabola_2 - y = " << a * x * x + b * x + c << std::endl;
    }

    {
        float a, b, c;
        fitParabola_3(vecPoints, a, b, c);
        cout << a << " " << b << " " << c << endl;
        int x = 2;
        std::cout << "fitParabola_3 - y = " << a *x *x + b *x + c << std::endl;
    }

    return 0;
}

运行结果

在这里插入图片描述

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值