OpenCV中直线、曲线和圆的拟合方法

打印 上一主题 下一主题

主题 986|帖子 986|积分 2958

一、直线拟合


  • 最小二乘法直线拟合(cv::fitLine)

    • 原理‌:通过最小化点到直线的垂直间隔平方和,计算最优直线参数‌。
    • 函数参数说明‌:
      1. void cv::fitLine(
      2.     InputArray points,    // 输入点集(2D或3D)
      3.     OutputArray line,    // 输出直线参数(2D为Vec4f,3D为Vec6f)
      4.     int distType,        // 距离类型(如DIST_L2、DIST_L1)
      5.     double param,        // 距离权重参数(通常设为0)
      6.     double reps,         // 径向精度阈值(推荐0.01)
      7.     double aeps          // 角度精度阈值(推荐0.01)
      8. );
      复制代码
      1. ‌distType‌:
      2. DIST_L2:对噪声敏感,适合无异常值数据‌。
      3. DIST_HUBER:鲁棒性较强,可抑制部分异常点‌。
      复制代码
    • 应用场景‌:适用于高精度直线参数提取(如工业检测中的边沿对齐)‌。

二、曲线拟合


  • 多项式曲线拟合

    • 原理‌:利用最小二乘法拟合多项式曲线(如二次、三次多项式),通过解线性方程组获取系数‌。
    • 实现步骤‌:

      • 调用cv::solve解线性方程组,生成多项式系数矩阵‌。
      • 根据系数生成一连曲线点集,用于绘制拟合曲线‌。

    • 参数限定‌:多项式阶数需满足阶数 ≤ 数据点数 - 1‌。

  • 基于Numpy的快速拟合(np.polyfit)

    • 优势‌:简化多项式拟合流程,直接生成多项式方程(需结合OpenCV绘图)‌。
    • 示例‌:
      1. coefficients = np.polyfit(x_points, y_points, degree) # 生成多项式系数
      复制代码

三、圆拟合


  • 二维圆拟合

    • 原理‌:通过最小二乘法或RANSAC算法,计算圆心坐标和半径‌。
    • 实现函数‌:OpenCV未提供直接接口,需自行实现或结合第三方库(如scikit-learn)‌。

  • 三维空间圆拟合

    • 寻衅‌:需处理三维点云数据,通常通过投影到二维平面或利用迭代优化方法实现‌。

四、代码

1.‌最小二乘法直线拟合

  1. #include <opencv2/opencv.hpp>
  2. #include <vector>
  3. using namespace cv;
  4. using namespace std;
  5. int main() {
  6.     // 模拟输入点集(可替换为实际图像边缘点)
  7.     vector<Point2f> points = {{50, 100}, {100, 150}, {150, 200}, {200, 250}};
  8.     // 直线拟合
  9.     Vec4f lineParams;
  10.     fitLine(points, lineParams, DIST_L2, 0, 0.01, 0.01);
  11.     // 解析直线参数:vx, vy 是方向向量,x0, y0 是直线上一点
  12.     float vx = lineParams;
  13.     float vy = lineParams;
  14.     float x0 = lineParams;
  15.     float y0 = lineParams;
  16.     // 计算直线端点(延伸100像素)
  17.     Point pt1(x0 - 100*vx, y0 - 100*vy);
  18.     Point pt2(x0 + 100*vx, y0 + 100*vy);
  19.     // 可视化
  20.     Mat img = Mat::zeros(300, 300, CV_8UC3);
  21.     for (const auto& p : points) {
  22.         circle(img, p, 3, Scalar(0, 255, 0), FILLED); // 绘制输入点
  23.     }
  24.     line(img, pt1, pt2, Scalar(0, 0, 255), 2); // 绘制拟合直线
  25.     imshow("Line Fitting", img);
  26.     waitKey(0);
  27.     return 0;
  28. }
复制代码
2. 曲线拟合

  1. #include <opencv2/opencv.hpp>
  2. #include <vector>
  3. #include <cmath>
  4. using namespace cv;
  5. using namespace std;
  6. int main() {
  7.     // 输入点集(二次曲线y = ax^2 + bx + c 比如:y = 0.5x² + 3x + 10 的采样)
  8.     vector<Point2f> points;
  9.     for (int x = 0; x <= 20; x += 2) {
  10.         float y = 0.5*pow(x,2) + 3*x + 10;
  11.         points.emplace_back(x*10, y*10); // 放大坐标便于显示
  12.     }
  13.     // 构建矩阵方程 Ax = b
  14.     Mat A(points.size(), 3, CV_32F);
  15.     Mat b(points.size(), 1, CV_32F);
  16.     for (size_t i = 0; i < points.size(); i++) {
  17.         float x = points[i].x;
  18.         A.at<float>(i, 0) = x*x;
  19.         A.at<float>(i, 1) = x;
  20.         A.at<float>(i, 2) = 1;
  21.         b.at<float>(i) = points[i].y;
  22.     }
  23.     // 解方程 (A^T * A) * coeff = A^T * b
  24.     Mat coeff;
  25.     solve(A, b, coeff, DECOMP_NORMAL | DECOMP_SVD);
  26.     // 生成拟合曲线点
  27.     vector<Point> curvePoints;
  28.     for (int x = 0; x <= 200; x += 2) {
  29.         float y = coeff.at<float>(0)*x*x + coeff.at<float>(1)*x + coeff.at<float>(2);
  30.         curvePoints.emplace_back(x, cvRound(y));
  31.     }
  32.     // 可视化
  33.     Mat img = Mat::zeros(800, 800, CV_8UC3);
  34.     for (const auto& p : points) {
  35.         circle(img, p, 5, Scalar(0, 255, 0), FILLED);
  36.     }
  37.     polylines(img, curvePoints, false, Scalar(0, 0, 255), 2);
  38.     imshow("Curve Fitting", img);
  39.     waitKey(0);
  40.     return 0;
  41. }
复制代码
关键点‌:


  • 利用 solve() 解多项式方程组
  • 支持恣意次多项式(需修改矩阵维度)
  • 通过 DECOMP_SVD 提高数值稳固性
在C++中,我们可以利用Eigen库或手动实现最小二乘法:
  1. #include <iostream>
  2. #include <vector>
  3. #include <Eigen/Dense>
  4. struct Point {
  5.     double x, y;
  6. };
  7. int main() {
  8.     std::vector<Point> points = {{1, 2}, {2, 5}, {3, 10}, {4, 17}};
  9.     int n = points.size();
  10.     Eigen::MatrixXd A(n, 3); // 二次方程有3个参数a, b, c
  11.     Eigen::VectorXd b(n); // y值
  12.     Eigen::Vector3d coeffs; // 系数向量
  13.     for (int i = 0; i < n; ++i) {
  14.         A(i, 0) = points[i].x * points[i].x; // x^2
  15.         A(i, 1) = points[i].x;               // x
  16.         A(i, 2) = 1;                        // 1 (constant term)
  17.         b(i) = points[i].y;                // y值
  18.     }
  19.     coeffs = A.bdcSvd(Eigen::ComputeThinU | Eigen::ComputeThinV).solve(b);
  20.     std::cout << "Coefficients: " << coeffs.transpose() << std::endl; // a, b, c
  21.     return 0;
  22. }
复制代码
3. 圆拟合

  1. #include <opencv2/opencv.hpp>
  2. #include <vector>
  3. using namespace cv;
  4. using namespace std;
  5. int main() {
  6.     // 生成模拟点集(实际应用中可通过findContours获取轮廓)
  7.     vector<Point2f> points;
  8.     for (int angle = 0; angle < 360; angle += 30) {
  9.         float radian = angle * CV_PI / 180;
  10.         points.emplace_back(100 + 50*cos(radian), 100 + 50*sin(radian));
  11.     }
  12.     // 最小包围圆拟合
  13.     Point2f center;
  14.     float radius;
  15.     minEnclosingCircle(points, center, radius);
  16.     // 可视化
  17.     Mat img = Mat::zeros(200, 200, CV_8UC3);
  18.     for (const auto& p : points) {
  19.         circle(img, p, 3, Scalar(0, 255, 0), FILLED);
  20.     }
  21.     circle(img, center, cvRound(radius), Scalar(0, 0, 255), 1);
  22.     imshow("Circle Fitting", img);
  23.     waitKey(0);
  24.     return 0;
  25. }
复制代码



免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

愛在花開的季節

金牌会员
这个人很懒什么都没写!
快速回复 返回顶部 返回列表