机器视觉应用-直线拟合

有哪些方法拟合直线?

最小二乘法(Least Squares)

最小二乘法是一种常用的线性回归方法,用于找到使得数据点到拟合直线的垂直距离的平方和最小的直线。

适用场景:适用于误差主要集中在y方向的情况。

实现方法:

#include <opencv2/opencv.hpp>
#include <vector>

using namespace std;
using namespace cv;

Vec4f leastSquaresFit(const vector<Point2f>& points) {
    Vec4f line;
    fitLine(points, line, DIST_L2, 0, 0.01, 0.01);
    return line;
}

RANSAC(Random Sample Consensus)

RANSAC是一种迭代方法,用于从包含异常值的数据中估计模型参数。它通过随机抽样找到内点最多的模型。

适用场景:适用于数据中存在较多异常点的情况。

实现方法:

#include <opencv2/opencv.hpp>
#include <vector>
#include <random>

using namespace std;
using namespace cv;

Vec4f ransacFit(const vector<Point2f>& points, int maxIterations = 1000, float distanceThreshold = 2.0) {
    vector<Point2f> bestInliers;
    Vec4f bestLine;
    random_device rd;
    mt19937 gen(rd());
    uniform_int_distribution<> dis(0, points.size() - 1);

    for (int i = 0; i < maxIterations; ++i) {
        // 随机选择两个点
        int idx1 = dis(gen);
        int idx2 = dis(gen);
        while (idx2 == idx1) {
            idx2 = dis(gen);
        }
        Point2f p1 = points[idx1];
        Point2f p2 = points[idx2];

        // 计算直线方程
        float a = p1.y - p2.y;
        float b = p2.x - p1.x;
        float c = p1.x * p2.y - p2.x * p1.y;

        // 找到内点
        vector<Point2f> inliers;
        for (const auto& point : points) {
            float dist = fabs(a * point.x + b * point.y + c) / sqrt(a * a + b * b);
            if (dist < distanceThreshold) {
                inliers.push_back(point);
            }
        }

        // 更新最佳模型
        if (inliers.size() > bestInliers.size()) {
            bestInliers = inliers;
            bestLine[0] = a;
            bestLine[1] = b;
            bestLine[2] = p1.x;
            bestLine[3] = p1.y;
        }
    }

    // 用最佳内点拟合直线
    fitLine(bestInliers, bestLine, DIST_L2, 0, 0.01, 0.01);
    return bestLine;
}

Theil-Sen估计是一种稳健的统计方法,通过计算所有点对斜率的中位数来拟合直线。

适用场景:适用于存在噪声和异常值的情况。

实现方法:

#include <opencv2/opencv.hpp>
#include <vector>
#include <algorithm>

using namespace std;
using namespace cv;

Vec4f theilSenEstimator(const vector<Point2f>& points) {
    vector<float> slopes;
    for (size_t i = 0; i < points.size(); ++i) {
        for (size_t j = i + 1; j < points.size(); ++j) {
            if (points[j].x != points[i].x) {
                float slope = (points[j].y - points[i].y) / (points[j].x - points[i].x);
                slopes.push_back(slope);
            }
        }
    }
    sort(slopes.begin(), slopes.end());
    float medianSlope = slopes[slopes.size() / 2];
    float medianIntercept = 0;
    for (const auto& point : points) {
        medianIntercept += point.y - medianSlope * point.x;
    }
    medianIntercept /= points.size();

    Vec4f line;
    line[0] = medianSlope;
    line[1] = -1;
    line[2] = 0;
    line[3] = medianIntercept;
    return line;
}

Hough变换(Hough Transform)

Hough变换是一种图像处理技术,通过在参数空间中投票找到最佳拟合直线。

适用场景:适用于图像中的线检测,尤其是在噪声较多的情况下。

实现方法:

#include <opencv2/opencv.hpp>
#include <vector>

using namespace std;
using namespace cv;

vector<Vec2f> houghTransform(const Mat& img, double rho = 1, double theta = CV_PI / 180, int threshold = 100) {
    vector<Vec2f> lines;
    HoughLines(img, lines, rho, theta, threshold);
    return lines;
}

线性回归(Linear Regression)

线性回归是通过最小化误差平方和来找到最佳拟合直线的参数。

适用场景:适用于误差均匀分布的情况。

实现方法:

#include <opencv2/opencv.hpp>
#include <vector>

using namespace std;
using namespace cv;

Vec4f linearRegression(const vector<Point2f>& points) {
    Mat X(points.size(), 2, CV_32F);
    Mat y(points.size(), 1, CV_32F);

    for (size_t i = 0; i < points.size(); ++i) {
        X.at<float>(i, 0) = points[i].x;
        X.at<float>(i, 1) = 1;
        y.at<float>(i, 0) = points[i].y;
    }

    Mat w;
    solve(X, y, w, DECOMP_SVD);

    Vec4f line;
    line[0] = w.at<float>(0, 0);
    line[1] = -1;
    line[2] = 0;
    line[3] = w.at<float>(1, 0);
    return line;
}

一个实战例程:

import numpy as np
import matplotlib.pyplot as plt
from sklearn.linear_model import RANSACRegressor

# 1. 生成或读取数据
np.random.seed(0)
n_samples = 100
n_outliers = 20

# 生成直线上的点 y = 2 * x + 1
X = np.linspace(-5, 5, n_samples).reshape(-1, 1)
y = 2 * X.flatten() + 1

# 添加一些噪声
y += np.random.normal(size=y.shape)

# 添加一些异常点
X[:n_outliers] = np.random.uniform(-5, 5, n_outliers).reshape(-1, 1)
y[:n_outliers] = np.random.uniform(-20, 20, n_outliers)

# 2. 应用RANSAC算法去除异常点
ransac = RANSACRegressor()
ransac.fit(X, y)
inlier_mask = ransac.inlier_mask_
outlier_mask = np.logical_not(inlier_mask)

# 3. 用剩下的点拟合直线
line_X = np.arange(-5, 6)
line_y_ransac = ransac.predict(line_X.reshape(-1, 1))

# 4. 可视化结果
plt.scatter(X[inlier_mask], y[inlier_mask], color='yellowgreen', marker='.', label='Inliers')
plt.scatter(X[outlier_mask], y[outlier_mask], color='red', marker='.', label='Outliers')
plt.plot(line_X, line_y_ransac, color='cornflowerblue', linewidth=2, label='RANSAC regressor')
plt.legend(loc='best')
plt.xlabel('Input')
plt.ylabel('Response')
plt.show()


输出:
在这里插入图片描述

上述方法各有优劣,选择适当的方法需要根据具体应用场景和数据特性进行权衡。例如,RANSAC和Theil-Sen估计适用于异常值较多的情况,而最小二乘法和线性回归则适用于误差主要集中在y方向且误差较小的情况。Hough变换适用于图像中的线检测,尤其是在噪声较多的情况下。

  • 7
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

0010000100

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值