一、直线拟合
-
最小二乘法直线拟合(
cv::fitLine
)- 原理:通过最小化点到直线的垂直距离平方和,计算最优直线参数。
- 函数参数说明:
void cv::fitLine(InputArray points, // 输入点集(2D或3D)OutputArray line, // 输出直线参数(2D为Vec4f,3D为Vec6f)int distType, // 距离类型(如DIST_L2、DIST_L1)double param, // 距离权重参数(通常设为0)double reps, // 径向精度阈值(推荐0.01)double aeps // 角度精度阈值(推荐0.01) );
distType: DIST_L2:对噪声敏感,适合无异常值数据。 DIST_HUBER:鲁棒性较强,可抑制部分异常点。
- 应用场景:适用于高精度直线参数提取(如工业检测中的边缘对齐)。
二、曲线拟合
-
多项式曲线拟合
- 原理:使用最小二乘法拟合多项式曲线(如二次、三次多项式),通过解线性方程组获取系数。
- 实现步骤:
- 调用
cv::solve
解线性方程组,生成多项式系数矩阵。 - 根据系数生成连续曲线点集,用于绘制拟合曲线。
- 调用
- 参数限制:多项式阶数需满足
阶数 ≤ 数据点数 - 1
。
-
基于Numpy的快速拟合(
np.polyfit
)- 优势:简化多项式拟合流程,直接生成多项式方程(需结合OpenCV绘图)。
- 示例:
coefficients = np.polyfit(x_points, y_points, degree) # 生成多项式系数
三、圆拟合
-
二维圆拟合
- 原理:通过最小二乘法或RANSAC算法,计算圆心坐标和半径。
- 实现函数:OpenCV未提供直接接口,需自行实现或结合第三方库(如
scikit-learn
)。
-
三维空间圆拟合
- 挑战:需处理三维点云数据,通常通过投影到二维平面或使用迭代优化方法实现。
四、代码
1.最小二乘法直线拟合
#include <opencv2/opencv.hpp>
#include <vector>using namespace cv;
using namespace std;int main() {// 模拟输入点集(可替换为实际图像边缘点)vector<Point2f> points = {{50, 100}, {100, 150}, {150, 200}, {200, 250}};// 直线拟合Vec4f lineParams;fitLine(points, lineParams, DIST_L2, 0, 0.01, 0.01);// 解析直线参数:vx, vy 是方向向量,x0, y0 是直线上一点float vx = lineParams;float vy = lineParams;float x0 = lineParams;float y0 = lineParams;// 计算直线端点(延伸100像素)Point pt1(x0 - 100*vx, y0 - 100*vy);Point pt2(x0 + 100*vx, y0 + 100*vy);// 可视化Mat img = Mat::zeros(300, 300, CV_8UC3);for (const auto& p : points) {circle(img, p, 3, Scalar(0, 255, 0), FILLED); // 绘制输入点}line(img, pt1, pt2, Scalar(0, 0, 255), 2); // 绘制拟合直线imshow("Line Fitting", img);waitKey(0);return 0;
}
2. 曲线拟合
#include <opencv2/opencv.hpp>
#include <vector>
#include <cmath>using namespace cv;
using namespace std;int main() {// 输入点集(二次曲线y = ax^2 + bx + c 比如:y = 0.5x² + 3x + 10 的采样)vector<Point2f> points;for (int x = 0; x <= 20; x += 2) {float y = 0.5*pow(x,2) + 3*x + 10;points.emplace_back(x*10, y*10); // 放大坐标便于显示}// 构建矩阵方程 Ax = bMat A(points.size(), 3, CV_32F);Mat b(points.size(), 1, CV_32F);for (size_t i = 0; i < points.size(); i++) {float x = points[i].x;A.at<float>(i, 0) = x*x;A.at<float>(i, 1) = x;A.at<float>(i, 2) = 1;b.at<float>(i) = points[i].y;}// 解方程 (A^T * A) * coeff = A^T * bMat coeff;solve(A, b, coeff, DECOMP_NORMAL | DECOMP_SVD);// 生成拟合曲线点vector<Point> curvePoints;for (int x = 0; x <= 200; x += 2) {float y = coeff.at<float>(0)*x*x + coeff.at<float>(1)*x + coeff.at<float>(2);curvePoints.emplace_back(x, cvRound(y));}// 可视化Mat img = Mat::zeros(800, 800, CV_8UC3);for (const auto& p : points) {circle(img, p, 5, Scalar(0, 255, 0), FILLED);}polylines(img, curvePoints, false, Scalar(0, 0, 255), 2);imshow("Curve Fitting", img);waitKey(0);return 0;
}
关键点:
- 使用
solve()
解多项式方程组 - 支持任意次多项式(需修改矩阵维度)
- 通过
DECOMP_SVD
提高数值稳定性
在C++中,我们可以使用Eigen库或手动实现最小二乘法:
#include <iostream>
#include <vector>
#include <Eigen/Dense>struct Point {double x, y;
};int main() {std::vector<Point> points = {{1, 2}, {2, 5}, {3, 10}, {4, 17}};int n = points.size();Eigen::MatrixXd A(n, 3); // 二次方程有3个参数a, b, cEigen::VectorXd b(n); // y值Eigen::Vector3d coeffs; // 系数向量for (int i = 0; i < n; ++i) {A(i, 0) = points[i].x * points[i].x; // x^2A(i, 1) = points[i].x; // xA(i, 2) = 1; // 1 (constant term)b(i) = points[i].y; // y值}coeffs = A.bdcSvd(Eigen::ComputeThinU | Eigen::ComputeThinV).solve(b);std::cout << "Coefficients: " << coeffs.transpose() << std::endl; // a, b, creturn 0;
}
3. 圆拟合
#include <opencv2/opencv.hpp>
#include <vector>using namespace cv;
using namespace std;int main() {// 生成模拟点集(实际应用中可通过findContours获取轮廓)vector<Point2f> points;for (int angle = 0; angle < 360; angle += 30) {float radian = angle * CV_PI / 180;points.emplace_back(100 + 50*cos(radian), 100 + 50*sin(radian));}// 最小包围圆拟合Point2f center;float radius;minEnclosingCircle(points, center, radius);// 可视化Mat img = Mat::zeros(200, 200, CV_8UC3);for (const auto& p : points) {circle(img, p, 3, Scalar(0, 255, 0), FILLED);}circle(img, center, cvRound(radius), Scalar(0, 0, 255), 1);imshow("Circle Fitting", img);waitKey(0);return 0;
}