Opencv计算机视觉编程攻略-第七节 提取直线、轮廓和区域
第七节 提取直线、轮廓和区域
- 1.用Canny 算子检测图像轮廓
- 2.用霍夫变换检测直线;
- 3.点集的直线拟合
- 4.提取连续区域
- 5.计算区域的形状描述子
图像的边缘区域勾画出了图像含有重要的视觉信息。正因如此,边缘可应用于目标识别等领域。但是简单的二值边缘分布图有两个主要缺点:
- 检测到的边缘过厚,这加大了识别物体边界的难度
- 也是更重要的,通常不可能找到既低到足以检测到图像中所有重要边缘,又高到足以避免产生太多无关紧要边缘的阈值
1.用Canny 算子检测图像轮廓
canny算子在低阈值边缘分布图上只保留具有连续路径的边缘点,同时把那些边缘点连接到属于高阈值边缘分布图的边缘上。高阈值分布图上的所有边缘点都被保留下来,而低阈值分布图上边缘点的孤立链全部被移除,只要指定适当的阈值,就能获得高质量的轮廓。这种基于两个阈值获得二值分布图的策略被称为滞后阈值化,可用于任何需要用阈值化获得二值分布图的场景,计算复杂度比较高。
另外,Canny 算法用了一个额外的策略来优化边缘分布图的质量。在进行滞后阈值化之前,如果梯度幅值不是梯度方向上的最大值,那么对应的边缘点都会被移除(前面讲过,梯度的方向总是与边缘垂直的)。因此,这个方向上梯度的局部最大值对应着轮廓最大强度的位置。这是一个细化轮廓的运算,它创建的轮廓宽度只有一个像素,这也解释了为什么Canny 轮廓分布图的边缘比较薄。
// 应用Canny 算法
cv::Mat contours;
cv::Canny(image, // 灰度图像contours, // 输出轮廓125, // 低阈值350); // 高阈值

2.用霍夫变换检测直线;
霍夫变换(Hough transform)是一种常用于检测此类具体特征的经典算法。该算法起初用于检测图像中的直线,后来经过扩展,也能检测其他简单的图像结构。

参数ρ 是直线与图像原点(左上角)的距离,θ 是直线与垂直线间的角度。在这种表示法中,图像中的直线有一个0~π(弧度)的角θ,而半径ρ 的最大值是图像对角线的长度。例如下面的一组线:

// 应用Canny 算法
cv::Mat contours;
cv::Canny(image,contours,125,350);
// 用霍夫变换检测直线
std::vector<cv::Vec2f> lines;
cv::HoughLines(contours,lines, 1,PI/180, // 步长60); // 最小投票数
半径步长为1,表示函数将搜索所有可能的半径;角度步长为π/180,表示函数将搜索所有可能的角度对于垂直方向的直线,计算它与图像水平边界(即第一行和最后一行)的交叉点,然后在这两个交叉点之间画线。水平方向的直线也类似,只不过用第一列和最后一列。画线的函数是cv::line。需要注意的是,即使点的坐标超出了图像范围,这个函数也能正确运行,因此没必要检查交叉点是否在图像内部。通过遍历直线向量画出所有直线:
霍夫变换只是寻找图像中边缘像素的对齐区域。因为有些像素只是碰巧排成了直线,所以霍夫变换可能产生错误的检测结果。也可能因为多条参数相近的直线穿过了同一个像素对齐区域,而导致检测出重复的结果。为解决上述问题并检测到线段(即包含端点的直线),人们提出了霍夫变换的改进版。这就是概率霍夫变换,在OpenCV 中通过cv::HoughLinesP 函数实现。
首先,概率霍夫变换在二值分布图上随机选择像素点,而不是系统性地逐行扫描图像。一旦累加器的某个入口达到了预设的最小值,就沿着对应的直线扫描图像,并移除在这条直线上的所有像素点(包括还没投票的像素点)。这个扫描过程还检测可以接受的线段长度。为此,算法定义了两个额外的参数:一个是允许的线段最小长度,另一个是组成连续线段时允许的最大像素间距
cv::HoughLinesP(binary,lines,deltaRho, deltaTheta, minVote,minLength, maxGap);

霍夫变换也能用来检测其他几何物体。事实上,任何可以用一个参数方程来表示的物体,都很适合用霍夫变换来检测。还有一种泛化霍夫变换,可以检测任何形状的物体。
OpenCV 采用的策略是在用霍夫变换检测圆的实现中使用两轮筛选。第一轮筛选使用一个二维累加器,找出可能是圆的位置。因为圆周上像素点的梯度方向与半径的方向是一致的,所以对每个像素点来说,累加器只对沿着梯度方向的入口增加计数(根据预先定义的最小和最大半径值)。一旦检测到可能的圆心(即收到了预定数量的投票),就在第二轮筛选中建立半径值范围的一维直方图。这个直方图的尖峰值就是被检测圆的半径。
cv::GaussianBlur(image,image,cv::Size(5,5),1.5);
std::vector<cv::Vec3f> circles;
cv::HoughCircles(image, circles, cv::HOUGH_GRADIENT,2, // 累加器分辨率(图像尺寸/2)50, // 两个圆之间的最小距离200, // Canny 算子的高阈值100, // 最少投票数25,100); // 最小和最大半径

3.点集的直线拟合
在某些应用程序中,光是检测出图像中的直线还不够,还需要精确地估计直线的位置和方向。
点集的直线拟合是一个经典数学问题。OpenCV 的实现方法是使每个点到直线的距离之和最小化。在众多用于计算距离的函数中,欧几里得距离的计算速度最快,所用参数为cv::DIST_L2。这一选项对应了标准的最小二乘法直线拟合。如果点集中包含了孤立点(即不属于直线的点),可以选用其他距离函数,以减少远距离的点带来的影响。最小化计算的基础是M 估算法技术,它采用迭代方式解决加权最小二乘法问题,其中权重与点到直线的距离成反比。
cv::Vec4f line;
cv::fitLine(points,line,cv::DIST_L2, // 距离类型0, // L2 距离不用这个参数0.01,0.01); // 精度

4.提取连续区域
图像通常包含各种物体,图像分析的目的之一就是识别和提取这些物体。在物体检测和识别程序中,第一步通常就是生成二值图像,找到感兴趣物体所处的位置。不管用什么方式获得二值图像(例如用第4 章的直方图反向投影,或者用第12 章的运动分析),下一个步骤都是从由1 和0 组成的像素集合中提取出物体。
// 用于存储轮廓的向量
std::vector<std::vector<cv::Point>> contours;
cv::findContours(image,contours, // 存储轮廓的向量cv::RETR_EXTERNAL, // 检索外部轮廓cv::CHAIN_APPROX_NONE); // 每个轮廓的全部像素
// 在白色图像上画黑色轮廓
cv::Mat result(image.size(),CV_8U,cv::Scalar(255));
cv::drawContours(result,contours,-1, // 画全部轮廓0, // 用黑色画2); // 宽度为2
5.计算区域的形状描述子
连续区域通常代表着场景中的某个物体。为了识别该物体,或将它与其他图像元素做比较,需要对此区域进行测量,以提取出部分特征
第一个是边界框,用于右下角的区域:
// 测试边界框
cv::Rect r0= cv::boundingRect(contours[0]);
// 画矩形
cv::rectangle(result,r0, 0, 2)
最小覆盖圆的情况也类似,将它用于右上角的区域:
// 测试覆盖圆
float radius;
cv::Point2f center;
cv::minEnclosingCircle(contours[1],center,radius);
// 画圆形
cv::circle(result,center, static_cast<int>(radius),
cv::Scalar(0),2);
计算区域轮廓的多边形逼近的代码如下(位于左侧区域):
// 测试多边形逼近
std::vector<cv::Point> poly;
cv::approxPolyDP(contours[2],poly,5,true);
// 画多边形
cv::polylines(result, poly, true, 0, 2);
注意,多边形绘制函数cv::polylines 与其他画图函数很相似。第三个布尔型参数表示该
轮廓是否闭合(如果闭合,最后一个点将与第一个点相连)。
凸包是另一种形式的多边形逼近(位于左侧第二个区域):
// 测试凸包
std::vector<cv::Point> hull;
cv::convexHull(contours[3],hull);
// 画多边形
cv::polylines(result, hull, true, 0, 2);
最后,计算轮廓矩是另一种功能强大的描述子(在所有区域内部画出重心):
// 测试轮廓矩
// 迭代遍历所有轮廓
itc= contours.begin();
while (itc!=contours.end()) {
// 计算所有轮廓矩
cv::Moments mom= cv::moments(cv::Mat(*itc++));
// 画重心
cv::circle(result,
// 将重心位置转换成整数
cv::Point(mom.m10/mom.m00,mom.m01/mom.m00),
2, cv::Scalar(0),2); // 画黑点
}

相关文章:
Opencv计算机视觉编程攻略-第七节 提取直线、轮廓和区域
第七节 提取直线、轮廓和区域 1.用Canny 算子检测图像轮廓2.用霍夫变换检测直线;3.点集的直线拟合4.提取连续区域5.计算区域的形状描述子 图像的边缘区域勾画出了图像含有重要的视觉信息。正因如此,边缘可应用于目标识别等领域。但是简单的二值边缘分布图…...
中和农信:让金融“活水”精准浇灌乡村沃土
2025年政府工作报告首提“投资于人”概念,并22次提及“金融”,强调要着力抓好“三农”工作,深入推进乡村全面振兴;一体推进地方中小金融机构风险处置和转型发展;扎扎实实落实促进民营经济发展的政策措施,切…...
背包DP总结
牛客周赛 Round 81 E.建筑入门 知识点:完全背包,完全背包的路径转移以及回溯 由题意可以推导出,下层麻将的数字一定大于上层数字,所以我们可以假设一个最基础的麻将塔,也就是: 1 2 2 3 3 3 … 形如这样的&…...
Labview信号采集与多功能分析系统(可仿真)
1.摘要 《Labview信号采集与多功能分析系统》可以实时分析信号的时域特征,例如信号的均值、方差、峰值、峭度等。系统可以进行信号的自相关与互关分析。此系统也可以分析信号的频域特征,包括快速傅里叶变换后的时频特征、短时傅里叶变换STFT后的时频域特…...
【C#使用S7.NET库读取和写入西门子PLC变量】
C#使用S7.NET库读取和写入西门子PLC变量 前言使用S7.NET库读取使用S7.NET库写入 前言 本来想用Wincc的接口给读和写Wincc,但是速度实在太感人了,所以不如直接读和写PLC的变量,这种方式速度瞬间快了不知道多少倍(经测试4000个变量…...
蓝桥杯 游戏 6251 单调队列
传送门 0游戏 - 蓝桥云课 有了单调队列,在求解答案时:当我们需要对最大值的列表和最小值的列表进行俩俩组合,如果直接用两个f0r循环进行匹配,那么时间复杂度太大,容易超时。我们可以进行一个推导,假设最大…...
[250331] Paozhu 发布 1.9.0:C++ Web 框架,比肩脚本语言 | DeaDBeeF 播放器发布 1.10.0
目录 Paozhu 发布 1.9.0:C Web 框架,快速开发,比肩脚本语言DeaDBeeF 音乐播放器发布 1.10.0 版本! Paozhu 发布 1.9.0:C Web 框架,快速开发,比肩脚本语言 Paozhu (炮竹🧨) 是一个功…...
einsum函数
理解专家并行,需要了解einsum函数 import torch# 设置输入张量的维度:s 3 tokens, e 2 experts, c 2 capacity, m 4 embedding dim s, e, c, m 3, 2, 2, 4# 1. 输入 token 的嵌入向量 (s, m) reshaped_input torch.tensor([[1.0, 1.0, 1.0, 1.0],…...
gitee 配置git上传
Git入门?查看 帮助 , Visual Studio / TortoiseGit / Eclipse / Xcode 下如何连接本站, 如何导入仓库 简易的命令行入门教程: Git 全局设置: 以 176fuguM2项目为例 git config --global user.name "堕落圣甲虫" git config --global user.email "11…...
【电子通识】为什么电子元件的规格书常常要看英文版本
在我们查看电子器件规格书的时候,如果有中文版本和英文版本两种供我们选择,那我们常常需要查看英文版本,大家有想过这是为什么吗? 为什么英文规格书很重要? 电子元件规格书(Datasheet)常以英文版…...
Scala基础知识5
面向对象 Scala 的面向对象思想和 Java 的面向对象思想和概念是一致的。 Scala 中语法和 Java 不同,补充了更多的功能。 1.Scala包 基本语法:package 包名 Scala 包的三大作用(和 Java 一样) 1.区分相同名字的类。 2.当类很多…...
【图像处理基石】什么是RAW格式?
在图像处理中,RAW格式是一种未经处理、记录了相机传感器原始数据的图像文件格式。它保留了相机传感器捕捉到的全部原始信息,未经任何压缩或处理,因此被称为“原始数据”(Raw)。以下是关于RAW格式的详细解释:…...
AI Agent 实战:搭建个人在线旅游助手
AI Agent 实战:搭建个人在线旅游助手 本次实验中,我们将继续探索 Agent 的提示词,学习更加规范的提示词撰写方法。 本实验中你将掌握的知识点 使用 Dify 构建 Agent 的方法结构化的提示词撰写技巧变量的使用方法 1. 准备 在新建 Agent 之…...
【JavaScript】十四、轮播图
文章目录 实现一个轮播图,功能点包括: 自动播放鼠标经过暂时播放鼠标离开继续播放点击切换按钮手动切换 div盒子嵌套先写出静态HTML,再使用JS来修改样式和数据,渲染页面: <!DOCTYPE html> <html lang"…...
2025 全球音乐 AI 大模型对决:技术革新引领商业变革浪潮
未来的世界声音: 音乐产业如今正被 AI 技术推动着,开启一场智能革命的全新篇章。无论是旋律的诞生,还是工业化编曲的流程,AI 大模型都在以飞快的速度改变着行业的基本逻辑架构。在中国,昆仑万维依靠 Mureka 系列产品在…...
RabbitMQ详解,RabbitMQ是什么?架构是怎样的?
目录 一,RabbitMQ是什么? 二,RabbitMQ架构 2.1 首先我们来看下RabbitMQ里面的心概念Queue是什么? 2.2 交换器Exchange 2.3 RabbitMQ是什么? 2.4 重点看下优先级队列是什么? 三,RabbitMQ集群 3.1 普通集群模式 3.2 镜像队列集群 一,RabbitMQ是什么? 假设我们程序…...
Java 大视界 -- Java 大数据在智能教育自适应学习平台中的用户行为分析与个性化推荐(169)
💖亲爱的朋友们,热烈欢迎来到 青云交的博客!能与诸位在此相逢,我倍感荣幸。在这飞速更迭的时代,我们都渴望一方心灵净土,而 我的博客 正是这样温暖的所在。这里为你呈上趣味与实用兼具的知识,也…...
智能打印预约系统:微信小程序+SSM框架实战项目
微信小程序打印室预约系统,采用SSM(SpringSpringMVCMyBatis)经典框架组合。 一、系统核心功能详解 1. 智能化管理后台 用户数据看板打印店资源管理预约动态监控服务评价系统 2. 微信小程序端 智能定位服务预约时段选择文件…...
Redisson 操作 Redis Stream 消息队列详解及实战案例
目录 一、Redis Stream 概念 1.Redis消息队列-认识消息队列 2.Redis Stream特点 3.Redis Stream与RabbitMQ等消息队列的比较 二.Redis Stream基本操作命令 1.生产消息 2.消费消息 3.消费者组操作 4.确认消息处理 三、Redisson 操作 Stream 的核心 API 1. 获取流的API…...
【深度学习量化交易19】行情数据获取方式比测(1)——基于miniQMT的量化交易回测系统开发实记
我是Mr.看海,我在尝试用信号处理的知识积累和思考方式做量化交易,应用深度学习和AI实现股票自动交易,目的是实现财务自由~ 目前我正在开发基于miniQMT的量化交易系统——看海量化交易系统。 经常使用MiniQMT的朋友都知道,xtquant的…...
23种设计模式-结构型模式-代理
文章目录 简介问题解决方案代码核心设计要点 总结 简介 代理是一种结构型设计模式,让你能够提供对象的替代品或其占位符。代理控制着对于原对象的访问,并允许在把请求提交给对象前后进行一些处理。 问题 为什么要控制对于某个对象的访问呢?…...
Windows C++ 排查死锁
开发出来应用程序突然间卡死不动,如果其中是因为死锁问题卡列该如何排查 下面是一个简单的死锁例子 #include <iostream> #include <thread> #include <mutex>std::mutex a, b;void function_a() {std::lock_guard<std::mutex> _x(a);std:…...
ctfshow
1,web517 通过输入两个单引号让查询语句正常,判断是什么注入,使用的是什么字符 然后我们通过order by 判断回显位,进行一个联合查询注入 获取数据库名 ctfshow的sqli-labs和本地搭建最大的不同,就是show的flag不在当前…...
【AI论文】什么、如何、何处以及效果如何?大语言模型测试时缩放技术调研
摘要:随着预训练时代对计算(数据和参数)缩放的热情逐渐减退,测试时缩放(Test-Time Scaling, TTS),也被称作“测试时计算”,已成为一个备受瞩目的研究焦点。近期研究表明,…...
大模型学习一:deepseek api 调用实战以及参数介绍
一、说明 DeepSeek(杭州深度求索人工智能基础技术研究有限公司)是一家专注于大语言模型(LLM)研发的中国创新型科技公司,成立于2023年7月17日,由幻方量化孵化。其核心产品包括开源推理模型DeepSeek-R1、多模…...
C++ 编程指南33 - 使用模板来表达适用于多种参数类型的算法
一:概述 在 C 中,模板(Templates)提供了一种强大的泛型编程方式,使代码可以适用于不同的数据类型,而无需重复编写类似的逻辑。模板的主要目标是: 泛化能力(Generality)&a…...
MYSQL实现获取某个经纬度区域内的数据
1.创建表 2.插入表数据 INSERT INTO tf_sys.tf_location(name, longitude, latitude, location) VALUES (资料名称1, 114.437625, 16.016914, ST_GeomFromText(POINT(114.437625 16.016914))); INSERT INTO tf_sys.tf_location(name, longitude, latitude, location) VALUES (…...
《Python实战进阶》No39:模型部署——TensorFlow Serving 与 ONNX
第39集:模型部署——TensorFlow Serving 与 ONNX 摘要 在机器学习项目中,训练好的模型需要被部署到生产环境中才能发挥实际价值。本集聚焦于如何将模型高效地部署到生产环境,涵盖TensorFlow Serving和ONNX两种主流工具的使用方法。我们将从理…...
嵌入式调试进阶:从手动到自动的HardFault破案指南
今天和大家聊聊嵌入式开发中让无数新手头疼,但又避不开的HardFault。 还记得我刚入行那会儿,信誓旦旦改了几行代码,信心爆棚地烧录进板子。结果呢?灯!没!亮!调试器显示程序卡在了HardFault_Han…...
YOLOv11区域检测
TrackZone 使用Ultralytics YOLO11 -Ultralytics YOLO 文档 如何通过Ultralytics YOLO11 在Python 中使用 TrackZone? 只需几行代码,您就可以在特定区域设置对象跟踪,从而轻松将其集成到您的项目中。 import cv2from ultralytics import s…...
