OpenCV实战系列——拟合直线

article/2025/10/5 22:17:26

OpenCV实战——拟合直线

    • 0. 前言
    • 1. 直线拟合
    • 2. 完整代码
    • 相关链接

0. 前言

在某些计算机视觉应用中,不仅要检测图像中的线条,还要准确估计线条的位置和方向。本节将介绍如何找到最适合给定点集的线。

1. 直线拟合

首先要做的是识别图像中可能沿直线对齐的点,可以使用霍夫变换检测到的线段。使用 cv::HoughLinesP 检测到的线段 lines 包含在向量 std::vector<cv::Vec4i> 中。

(1) 要提取可能的点集,比如说,第 1 条线段,我们可以在黑色图像上绘制一条白线,并将其与用于检测线条的 Canny 轮廓图像相交:

int n = 0;
// 提取探测到的第一条线段的轮廓像素
cv::Mat oneline(image.size(), CV_8U, cv::Scalar(0));
cv::line(oneline, cv::Point(li[n][0], li[n][1]), cv::Point(li[n][2], li[n][3]), cv::Scalar(255), 3);
cv::bitwise_and(contours, oneline, oneline);

以上代码可以获得只包含可以与指定线关联的点,为了引入一些公差,我们绘制了一条一定粗细的线(线宽为 3),因此,在定义的邻域内的所有点可以都被接受。结果如下图所示(为了便于观察,反转图像像素值):

线关联的点
(2) 通过双循环将这个集合中点的坐标插入到 cv::Point 函数的 std::vector 中(也可以使用浮点坐标,即 cv::Point2f):

std::vector<cv::Point> points;
//  迭代像素以获得所有点位置
for (int y=0; y<oneline.rows; y++) {uchar* rowPtr = oneline.ptr<uchar>(y);for (int x=0; x<oneline.cols; x++) {if (rowPtr[x]) {points.push_back(cv::Point(x, y));}}
}

(3) 通过调用 cv::fitLine 函数很容易找到最佳拟合线段:

// 拟合直线
cv::Vec4f line;
cv::fitLine(points, line, cv::DIST_L2, 0, 0.01, 0.01);

以上代码以单位方向向量( cv::Vec4f 的前两个值)和线上一个点的坐标( cv::Vec4f 的后两个值)的形式提供线段方程的参数。对于示例结果,方向向量为 (0.83, 0.55),点坐标的值为 (366.1, 289.1)cv::fitLine 函数的最后两个参数用于指定所需线段参数的精度。

(4) 线段方程可以用于计算某些属性,作为说明,我们可以在图像上绘制拟合的线。例如,绘制一个长度为 100 像素、宽度为 3 像素的黑色线段:

int x0 = line[2];
int y0 = line[3];
int x1 = x0+100*line[0];
int y1 = y1+100*line[1];
// 绘制直线
cv::line(image, cv::Point(x0, y0), cv::Point(x1, y1), 0, 2);

绘制结果如下图所示:

拟合直线
将一组点拟合为一条线是数学中的一个经典问题,cv::fitLine 函数通过最小化每个点到直线的距离总和来拟合直线。
可以使用不同的距离函数,欧几里得距离( CV_DIST_L2 ) 是最容易计算的距离,其对应于标准最小二乘线拟合。当点集中包含异常值(即不属于该线的点)时,可以选择其他异常点影响较小的距离函数。最小化基于 M 估计技术,该技术迭代地解决加权最小二乘问题,其权重与距离直线的距离成反比。
使用 cv::fitLine 函数,还可以将一组 3D 点集拟合为一条直线,此时,输入是一组 cv::Point3icv::Point3f 类型数据,输出类型为 std::Vec6fcv::fitEllipse 函数可以将一组 2D 点拟合为椭圆,其返回一个旋转矩形( cv::RotatedRect 实例),椭圆内接于该矩形内:

cv::RotatedRect rrect= cv::fitEllipse(cv::Mat(points));
cv::ellipse(image,rrect,cv::Scalar(0));

cv::ellipse 函数用来绘制计算出的椭圆函数图像。

2. 完整代码

库文件 linefinder.hedgedetector.h 可以参考线条检测一节,主函数 fitLine.cpp 代码如下所示:

#include <iostream>
#include <vector>
#include <opencv2/core/core.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/highgui/highgui.hpp>
#include "linefinder.h"
#include "edgedetector.h"#define PI 3.1415926int main() {// 读取输入图像cv::Mat image = cv::imread("road.jpg", 0);if (!image.data) return 0;cv::namedWindow("Original Image");cv::imshow("Original Image", image);// 计算 SobelEdgeDetector ed;ed.computeSobel(image);cv::namedWindow("Sobel (orientation)");cv::imshow("Sobel (orientation)", ed.getSobelOrientationImage());cv::imwrite("ori.png", ed.getSobelOrientationImage());// 低阈值 Sobelcv::namedWindow("Sobel (low threshold)");cv::imshow("Sobel (low threshold)", ed.getBinaryMap(125));// 高阈值 Sobelcv::namedWindow("Sobel (high threshold)");cv::imshow("Sobel (high threshold)", ed.getBinaryMap(350));// 应用 Canny 算法cv::Mat contours;cv::Canny(image,    // 灰度图像contours,   // 输出图像125,        // 低阈值350);       // 高阈值cv::namedWindow("Canny Contours");cv::imshow("Canny Contours", 255-contours);// 霍夫变换std::vector<cv::Vec2f> lines;cv::HoughLines(contours, lines, 1, PI/180, 50);// 绘制检测结果cv::Mat result(contours.rows, contours.cols, CV_8U, cv::Scalar(255));image.copyTo(result);std::cout << "Lines detected: " << lines.size() << std::endl;std::vector<cv::Vec2f>::const_iterator it = lines.begin();while (it!=lines.end()) {float rho = (*it)[0];float theta = (*it)[1];if (theta < PI/4. || theta > 3.*PI/4.) {    // 竖线// 直线与图像第一行交点cv::Point pt1(rho/cos(theta), 0);// 直线与图像最后一行交点cv::Point pt2((rho-result.rows*sin(theta))/cos(theta), result.rows);cv::line(result, pt1, pt2, cv::Scalar(255), 1);} else {        // 横线// 直线与图像第一列交点cv::Point pt1(0, rho/sin(theta));// 直线与图像最后一列交点cv::Point pt2(result.cols, (rho-result.cols*cos(theta))/sin(theta));cv::line(result, pt1, pt2, cv::Scalar(255), 1);}std::cout << "line: (" << rho << "," << theta << ")" << std::endl;++it;}cv::namedWindow("Lines with Hough");cv::imshow("Lines with Hough", result);// 创建 LineFinder 实例LineFinder ld;// 设置概率霍夫变换ld.setLineLengthAndGap(100, 20);ld.setMinVote(60);// 直线检测std::vector<cv::Vec4i> li = ld.findLines(contours);ld.drawDetectedLines(image);cv::namedWindow("Lines with HoughP");cv::imshow("Lines with HoughP", image);std::vector<cv::Vec4i>::const_iterator it2 = li.begin();while (it2 != li.end()) {std::cout << "(" << (*it2)[0] << ", " << (*it2)[1] << ") - (" << (*it2)[2] << ", " << (*it2)[3] << ")" << std::endl;++it2;}image = cv::imread("road.jpg", 0);int n = 0;cv::line(image, cv::Point(li[n][0], li[n][1]), cv::Point(li[n][2], li[n][3]), cv::Scalar(255), 5);cv::namedWindow("One line of the Image");cv::imshow("One line of the Image", image);// 提取探测到的第一条线段的轮廓像素cv::Mat oneline(image.size(), CV_8U, cv::Scalar(0));cv::line(oneline, cv::Point(li[n][0], li[n][1]), cv::Point(li[n][2], li[n][3]), cv::Scalar(255), 3);cv::bitwise_and(contours, oneline, oneline);cv::namedWindow("One line");cv::imshow("One line", 255-oneline);std::vector<cv::Point> points;//  迭代像素以获得所有点位置for (int y=0; y<oneline.rows; y++) {uchar* rowPtr = oneline.ptr<uchar>(y);for (int x=0; x<oneline.cols; x++) {if (rowPtr[x]) {points.push_back(cv::Point(x, y));}}}// 拟合直线cv::Vec4f line;cv::fitLine(points, line, cv::DIST_L2, 0, 0.01, 0.01);std::cout << "line: (" << line[0] << ", " << line[1] << ") (" << line[2] << ", " << line[3] << std::endl;// 直线上的点int x0 = line[2];int y0 = line[3];int x1 = x0+100*line[0];int y1 = y0+100*line[1];image = cv::imread("road.jpg", 0);// 绘制直线cv::line(image, cv::Point(x0, y0), cv::Point(x1, y1), 0, 2);cv::namedWindow("Fitted line");cv::imshow("Fitted line", image);cv::waitKey();return 0;
}

转:https://blog.csdn.net/LOVEmy134611/article/details/128808279?spm=1001.2014.3001.5501


http://chatgpt.dhexx.cn/article/eOHdhjDR.shtml

相关文章

Halcon 拟合直线

本文用 Halcon 的矩阵操作实现最小二乘拟合直线 *首先随机生成一组数据 Mx : [100:10:500] tuple_length(Mx, len) tuple_gen_const(len, 5, r) Ma : 2 Mb : 40 tuple_rand(len, noise) My : Ma * Mx Mb * noise gen_circle(ContCircle, My, Mx, r)接下来用矩阵进行最小二乘求…

边缘分析及直线和圆拟合——第5讲

一、直线和圆拟合概述 直线和圆的拟合,是视觉项目中非常常见的需求,但是对新手来说,在Halcon中实现却比较困难。 其基本思路都是: ① 分割出边缘,得到XLD(gen_contours_skeleton_xld、edges_sub_pix等) ② 将XLD进行分割、筛选(segment_contours_xld、select_conto…

【拟合专题】直线拟合

闲来无事&#xff0c;整理下拟合方面的一些方法&#xff08;部分内容参考gloomyfish、Grooveboy等博客&#xff0c;在此先行谢过&#xff09; 直线拟合方法主流方法有最小二乘、Hough两种&#xff0c;其他如Halcon上的最小距离也是最小二乘的思想&#xff0c;其他如Hough变换和…

直线拟合的三种方法

近日考虑直线拟合相关的知识&#xff0c;大概有所了解&#xff0c;所以打算进行一些总结。 直线拟合常用的三种方法&#xff1a; 一、最小二乘法进行直线拟合 二、梯度下降法进行直线拟合 三、高斯牛顿&#xff0c;列-马算法进行直线拟合 一、使用最多的就是最小二乘法&…

手把手教你整合最优雅SSM框架:SpringMVC + Spring + MyBatis

我们看招聘信息的时候&#xff0c;经常会看到这一点&#xff0c;需要具备SSH框架的技能&#xff1b;而且在大部分教学课堂中&#xff0c;也会把SSH作为最核心的教学内容。 但是&#xff0c;我们在实际应用中发现&#xff0c;SpringMVC可以完全替代Struts&#xff0c;配合注解的…

F-LOAM

欢迎访问我的博客首页。 F-LOAM 1. 传感器模型与特征提取1.1 传感器模型1.2 特征提取 2. 运动估计与畸变补偿2.1 匀速运动模型2.2 祛畸变 3. 位姿估计4. 地图创建与畸变补偿更新5. 附录5.1 位姿变换的传递性 6. 参考 1. 传感器模型与特征提取 1.1 传感器模型 机械式三维激光雷达…

MinIO入门-02 SpringBoot 整合MinIO并实现文件上传

SpringBoot 整合MinIO并实现文件上传 1、依赖 <!-- https://mvnrepository.com/artifact/io.minio/minio --> <dependency><groupId>io.minio</groupId><artifactId>minio</artifactId><version>8.3.9</version> </depen…

Invalid bound statement (not found): com.lyf.eduservice.mapper.EduCourseMapper.getpublishInfo

目录 问题分析&#xff1a; 解决方案&#xff1a; 问题分析&#xff1a; dao层编译后只有class文件&#xff0c;没有mapper.xml&#xff0c;因为maven工程在默认情况下src/main/java目录下的所有资源文件是不发布到target目录下的 解决方案&#xff1a; 第一步、在项目的po…

ELF

目录 一&#xff0c;目标文件格式 二&#xff0c;ELF 1&#xff0c;分段 2&#xff0c;工具 3&#xff0c;查看目标文件内容 一&#xff0c;目标文件格式 编译链接 https://blog.csdn.net/nameofcsdn/article/details/116654835 目标文件和可执行文件的逻辑结构是类似的…

littlefs

1、littlefs主要用在微控制器和flash上&#xff0c;是一种嵌入式文件系统。主要有3个特点&#xff1a; 1)、掉电恢复 在写入时即使复位或者掉电也可以恢复到上一个正确的状态。 2)、擦写均衡 有效延长flash的使用寿命 3)、有限的RAM/ROM 节省ROM和RAM空间 2、已有的文件系…

luffy-(13)

内容概览 支付宝支付介绍支付宝支付二次封装订单相关表设计生成订单接口支付前端支付宝回调接口 支付宝支付介绍 """ 项目中有需要在线支付功能,可以使用支付宝支付(沙箱环境)微信支付(需要有备案过的域名)云闪付我们的项目以支付宝支付为例支付流程使用官方…

【FLFL】

论文记录 1. 3.3《基于区块链的联邦学习技术综述》2. 3.4《Swarm Learning for decentralized and confidential clinical machine learning》3. 3.8《Blockchained On-Device Federated Learning》4. 3.11《FLchain: Federated Learning via MEC-enabled Blockchain Network》…

LYF95101A 是一款高性能、高集成度、具有快速

LYF95101A 概述 LYF95101A是一款高性能、高集成度、具有快速 关断特性的单通道同步整流控制器。支持CCM, QR和 DCM的多模式工作。通过智能的控制MOSFET的开通 和关断&#xff0c;可替代反激变换器次级整流的肖特基二极管 来实现效率的提高。 LYF95101A 内置自供电电路&#xff…

渗透测试简介

病毒&#xff1a;是在计算机程序中插入的破坏计算机功能或者数据的代码&#xff0c;能影响计算机使用&#xff0c;能自我复制的一组计算机指令或者程序代码&#xff1b; ●木马&#xff1a;是比较流行的病毒文件&#xff0c;与一般的病毒不同&#xff0c;它不会自我繁殖&#x…

介绍模糊测试(Fuzz Testing,Fuzzing)

介绍模糊测试&#xff08;Fuzz Testing&#xff0c;Fuzzing&#xff09; 一、什么是模糊测试&#xff1f; 模糊测试是一种自动或半自动的测试技术&#xff0c;常被用来发现软件/操作系统/网络的代码中的错误和安全性问题&#xff0c;其中用于输入随机的数据和不合法的数据被称…

#学习笔记4#软件测试基础——测试阶段划分、黑盒测试的一些知识

今天主要看了以下几个方面的知识点&#xff0c;基本都是纯理论&#xff0c;本文只做知识点总结&#xff0c;具体内容要看转载 1.软件测试阶段划分&#xff0c;分为4个阶段&#xff1a;单元测试、集成测试、系统测试、验收测试 单元测试是方法类的覆盖&#xff0c;主要是由开…

辅助程序实现黑盒自动化测试的常见问题

背景 辅助程序&#xff08;Accessibility&#xff09;在大多数机型上具有重启设备后被激活的特性&#xff0c;可以完成Android测试框架&#xff08;Uiautomator1.0、Uiautomator2.0&#xff09;无法实现的功能。本文介绍如何搭建辅助程序和如何利用辅助程序进行黑盒测试。并总…

Android 13 Camera ITS 环境搭建(从Python安装到环境配置详解)

Python 版本 根据CameraITS.pdf 以及报错信息提示需要python版本在3.7.9及以上&#xff0c;本次使用的是3.8.10。 Python 安装 1.官方网站下载Python源码 wget https://www.python.org/ftp/python/3.8.10/Python-3.8.10.tgz 2.进入到下载路径&#xff0c;解压Python文件 …

黑盒测试常见错误类型说明及解决方法有哪些?

目录 1、用户界面错误 2、遗漏信息 3、错误的、误导的或令人迷惑的信息 1、用户界面错误 功能性 易用性&#xff08;用户学习使用程序的时间和记住怎样使用程序的时间&#xff09; 执行速度&#xff08;多数是启动速度&#xff0c;查询速度&#xff0c;刷新速度及响应速度…

浅析黑盒测试与白盒测试

这里写自定义目录标题 黑盒测试黑盒测试常见的测试&#xff1a;黑盒测试常用的方法&#xff1a;等价类划分&#xff1a;边界值分析&#xff1a;因果图分析法、错误推断法&#xff1a; 白盒测试白盒测试常见的方法&#xff1a; 黑盒测试与白盒测试的优缺点二者的优点二者的缺点 …