【OpenCV实现图像:使用OpenCV进行物体轮廓排序】

文章目录

    • 概要
    • 读取图像
    • 获取轮廓
    • 轮廓排序
    • 小结

概要

在图像处理中,经常需要进行与物体轮廓相关的操作,比如计算目标轮廓的周长、面积等。为了获取目标轮廓的信息,通常使用OpenCV的findContours函数。然而,一旦获得轮廓信息后,可能会发现轮廓的顺序是无序的,如下图左侧所示:
在这里插入图片描述

在这个图中,每个轮廓都被找到,但它们的顺序是混乱的,这使得难以对每个目标进行准确的测量和分析。为了解决这个问题,我们需要对轮廓进行排序,以便更方便地进行后续处理。
在这里插入图片描述

读取图像

开始读取图像并生成其边缘检测图。

import cv2
import numpy as np# 读取图像
image = cv2.imread('img_4.png')# 初始化累积边缘图
accumEdged = np.zeros(image.shape[:2], dtype='uint8')# 对每个通道进行边缘检测
for chan in cv2.split(image):chan = cv2.medianBlur(chan, 11)edged = cv2.Canny(chan, 50, 200)accumEdged = cv2.bitwise_or(accumEdged, edged)# 显示边缘检测图
cv2.imshow('Edge Map', accumEdged)
cv2.waitKey(0)
cv2.destroyAllWindows()

在这里插入图片描述

获取轮廓

opencv-python中查找图像轮廓的API为:findContours函数,该函数接收二值图像作为输入,可输出物体外轮廓、内外轮廓等等。

import cv2
import numpy as np# 读取图像
image = cv2.imread('img_4.png')# 初始化累积边缘图
accumEdged = np.zeros(image.shape[:2], dtype='uint8')# 对每个通道进行边缘检测
for chan in cv2.split(image):chan = cv2.medianBlur(chan, 11)edged = cv2.Canny(chan, 50, 200)accumEdged = cv2.bitwise_or(accumEdged, edged)# 显示边缘检测图
cv2.imshow('Edge Map', accumEdged)# 寻找图像轮廓
cnts, _ = cv2.findContours(accumEdged.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cnts = sorted(cnts, key=cv2.contourArea, reverse=True)[:5]# 复制原始图像
orig = image.copy()# 对未排序的轮廓进行可视化
for (i, c) in enumerate(cnts):orig = cv2.drawContours(orig, [c], -1, (0, 255, 0), 2)cv2.putText(orig, f'Contour #{i+1}', (10, 30*(i+1)), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0, 0, 255), 2)# 显示未排序的轮廓可视化结果
cv2.imshow('Unsorted Contours', orig)
cv2.imwrite("./Unsorted_Contours.jpg", orig)cv2.waitKey(0)
cv2.destroyAllWindows()

在这里插入图片描述
在OpenCV的不同版本中,cv2.findContours 函数的返回值形式有所不同。在OpenCV 2.X版本中,函数返回两个值,而在OpenCV 3以上版本中,返回三个值。为了适配这两种版本,可以实现一个名为 grab_contours 的函数,根据不同的版本选择正确的轮廓返回位置。以下是该函数的代码:

def grab_contours(cnts):# 如果 cv2.findContours 返回的轮廓元组长度为 '2',则表示使用的是 OpenCV v2.4、v4-beta 或 v4-officialif len(cnts) == 2:cnts = cnts[0]# 如果轮廓元组长度为 '3',则表示使用的是 OpenCV v3、v4-pre 或 v4-alphaelif len(cnts) == 3:cnts = cnts[1]return cnts

这个函数简单地检查返回的轮廓元组的长度,根据长度选择正确的轮廓返回位置。通过使用这个函数,可以确保代码在不同版本的OpenCV中都能正确地工作。

轮廓排序

通过上述步骤,得到了图像中的所有物体的轮廓,接下来定义函数sort_contours函数来实现对轮廓进行排序操作,该函数接受method参数来实现按照不同的次序对轮廓进行排序,比如从左往右,或者从右往左.

import cv2
import numpy as npdef sort_contours(cnts, method='left-to-right'):reverse = Falsei = 0if method == 'right-to-left' or method == 'bottom-to-top':reverse = Trueif method == 'bottom-to-top' or method == 'top-to-bottom':i = 1boundingBoxes = [cv2.boundingRect(c) for c in cnts](cnts, boundingBoxes) = zip(*sorted(zip(cnts, boundingBoxes), key=lambda b: b[1][i], reverse=reverse))return (cnts, boundingBoxes)def draw_contour(image, c, i):M = cv2.moments(c)cX = int(M["m10"] / M["m00"])cY = int(M["m01"] / M["m00"])cv2.drawContours(image, [c], -1, (0, 255, 0), 2)cv2.putText(image, f'#{i+1}', (cX - 20, cY), cv2.FONT_HERSHEY_SIMPLEX, 1, (255, 255, 255), 2)return image# 读取图像
image = cv2.imread('img_4.png')# 初始化累积边缘图
accumEdged = np.zeros(image.shape[:2], dtype='uint8')# 对每个通道进行边缘检测
for chan in cv2.split(image):chan = cv2.medianBlur(chan, 11)edged = cv2.Canny(chan, 50, 200)accumEdged = cv2.bitwise_or(accumEdged, edged)# 显示边缘检测图
cv2.imshow('Edge Map', accumEdged)# 寻找图像轮廓
cnts, _ = cv2.findContours(accumEdged.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cnts = sorted(cnts, key=cv2.contourArea, reverse=True)[:5]# 复制原始图像
orig = image.copy()# 对未排序的轮廓进行可视化
for (i, c) in enumerate(cnts):orig = draw_contour(orig, c, i)
cv2.imshow('Unsorted Contours', orig)# 轮廓排序
(cnts, boundingboxes) = sort_contours(cnts, method='left-to-right')# 对排序后的轮廓进行可视化
for (i, c) in enumerate(cnts):image = draw_contour(image, c, i)
cv2.imshow('Sorted Contours', image)cv2.waitKey(0)
cv2.destroyAllWindows()

在这里插入图片描述
下面是对其中的函数 sort_contours 的解释:

def sort_contours(cnts, method='left-to-right'):# 初始化反转标志和排序索引reverse = Falsei = 0# 处理反向排序if method == 'right-to-left' or method == 'bottom-to-top':reverse = True# 如果按照 y 而不是 x 对外接框进行排序if method == 'bottom-to-top' or method == 'top-to-bottom':i = 1# 获取轮廓的外接矩形框boundingBoxes = [cv2.boundingRect(c) for c in cnts]# 使用 lambda 函数按照指定的坐标轴对外接框进行排序(cnts, boundingBoxes) = zip(*sorted(zip(cnts, boundingBoxes), key=lambda b: b[1][i], reverse=reverse))return (cnts, boundingBoxes)

函数接受轮廓列表 cnts 和排序方法 method 作为参数。首先初始化了反转标志和排序索引,根据指定的排序方法设置这些标志。然后,使用列表推导式和 cv2.boundingRect 函数获取每个轮廓的外接矩形框。最后,通过使用 sorted 函数和 zip 函数,根据外接框的 x 或 y 坐标进行排序。

在排序的结果中,cnts 存储了按照指定方法排序后的轮廓,而 boundingBoxes 存储了相应的外接矩形框。这两个结果作为元组返回给调用函数。

在主调用部分,代码如下:

# 使用 sort_contours 函数对轮廓进行排序
(cnts, boundingboxes) = sort_contours(cnts, method=args['method'])
# 遍历排序后的轮廓并绘制
for (i, c) in enumerate(cnts):image = draw_contour(image, c, i)
# 显示排序后的结果
cv2.imshow('Sorted', image)
cv2.waitKey(0)

在这里,调用了 sort_contours 函数,并将返回的排序后的轮廓存储在 cnts 中。然后,通过遍历这些排序后的轮廓,并调用 draw_contour 函数绘制,最后使用 cv2.imshow 显示排序后的结果。

小结

边缘检测:通过中值模糊和Canny边缘检测对图像进行处理,生成累积边缘图。轮廓查找:使用 cv2.findContours 函数找到累积边缘图中的轮廓,并按面积降序排序。未排序轮廓可视化:将未排序的轮廓绘制在原始图像上,并标注轮廓编号。轮廓排序函数 sort_contours:实现根据指定方法对轮廓进行排序,可选择从左到右、从右到左、从上到下或从下到上排序。轮廓排序和可视化:使用排序函数对轮廓进行排序,然后将排序后的轮廓绘制在原始图像上,同时标注轮廓编号。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/159686.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Java8新特性 ----- Lambda表达式和方法引用/构造器引用详解

前言 在讲一下内容之前,我们需要引入函数式接口的概念 什么是函数式接口呢? 函数式接口:有且仅有一个抽象方法的接口 java中函数式编程的体现就是Lambda表达式,你可以认为函数式接口就是适用于Lambda表达式的接口. 也可以加上注解来在编译层次上限制函数式接口 Fun…

视频云存储EasyCVR平台国标接入获取通道设备未回复是什么原因?该如何解决?

安防视频监控/视频集中存储/云存储/磁盘阵列EasyCVR平台可拓展性强、视频能力灵活、部署轻快,可支持的主流标准协议有国标GB28181、RTSP/Onvif、RTMP等,以及支持厂家私有协议与SDK接入,包括海康Ehome、海大宇等设备的SDK等。平台既具备传统安…

vue 获取元素的大小信息

在模板中为元素添加一个ref属性&#xff1a; <template> <div ref"myElement">Hello, World!</div> </template> 然后&#xff0c;在组件的setup函数中使用ref获取元素&#xff0c;并获取元素的信息&#xff1a; vue2获取方式&#xf…

【mysql】1153 - Got a packet bigger than ‘max_allowed_packet‘ bytes

执行mysql 语句出现&#xff1a;1153 - Got a packet bigger than max_allowed_packet bytes&#xff1b; 1153-得到一个大于“max_allowed_packet”字节的数据包。 数据包太大了怎么办。该配置吧。 查看max_allowed_packet show global variables like max_allowed_packet;…

Java SPI机制

目录 什么是spi API与SPI区别 SPI实现案例 SPI应用场景 1. JDBC场景 2. ShardingSphere场景 3. Spring 场景 4. SLF4J 日志门面 场景 Java.util.spi下提供了SPI机制&#xff0c;SPI机制&#xff08;Service Provider Interface)其实源自服务提供者框架&#xff08;Serv…

IT变更管理实现服务台高效协同

在当今数字化时代&#xff0c;IT变更管理是IT管理员在服务台中必须面对的重要挑战之一。随着技术的不断发展和市场的快速变化&#xff0c;管理员需要定期进行IT系统和流程的变更&#xff0c;在确保业务稳定性的同时还需提高效率和准确率。 1、全方位的变更计划 IT中应该有一个全…

[开源]Web端的P2P文件传输工具,简单安全高效的P2P文件传输服务

一、开源项目简介 小鹿快传 - 在线P2P文件传输工具 小鹿快传是一款Web端的P2P文件传输工具&#xff0c;使用了WebRTC技术实现P2P连接和文件传输。 二、开源协议 使用MIT开源协议 三、界面展示 产品截图 四、功能概述 简单安全高效的P2P文件传输服务 小鹿快传是一款Web端…

虚拟摇杆OnJoystickMove未被调用,角色不移动

更改interaction type 为 event notification

12、人工智能、机器学习、深度学习的关系

很多年前听一个机器学习的公开课,在Q&A环节,一个同学问了老师一个问题“机器学习和深度学习是什么关系”? 老师先没回答,而是反问了在场的同学,结果问了2-3个,没有人可以回答的很到位,我当时也是初学一脸懵,会场准备的小礼品也没有拿到。 后来老师解释“机器学习和…

git 把仓库的子模块也拉取下来

要拉取仓库的子模块&#xff0c;可以使用以下命令&#xff1a; git clone --recursive <repository_url>这个命令会将仓库以及所有子模块都克隆下来。 如果已经克隆了主仓库但没有克隆子模块&#xff0c;可以使用以下命令去初始化子模块&#xff1a; git submodule up…

回转窑无线测温系统解决方案

方案概述&#xff08;以回转窑监测为例&#xff09; 回转窑通常使用在热电厂、钢铁厂、冶金厂及干燥设备行业。而这些行业一般具有高温、潮湿、粉尘、高压、不便布线或现场无法提供电源等环境特点。同时&#xff0c;回转窑在工作中&#xff0c;具有旋转、高温等特点。为此&…

[CLickhouse] 学习小计

1.建表 正常按照如下语句进行建表&#xff0c;但会报错说缺少ORDER BY 或 PRIMARY KEY CREATE TABLE omds.a (x String COMMENT ,y String COMMENT ,z String ) ENGINE MergeTree(); 若我们仅需建一个没有主键的表&#xff0c;直接使用如下建表语句即可&#xff1a; CREATE…

使用netty实现WebSocket协议通信

服务器与浏览器之间实现通信&#xff0c;一般都是由浏览器发起http请求&#xff0c;服务端对http请求进行响应&#xff0c;要实现服务端主动向浏览器推送数据&#xff0c;一般采用的方案都是websocket主动推送&#xff0c;或者前端实现轮询方式拉取数据&#xff0c;轮询方式多少…

技术or管理?浅谈测试人员的未来职业发展

我们在工作了一段时间之后&#xff0c;势必会感觉到自己已经积累了一些工作经验了&#xff0c;会开始考虑下一阶段的职业生涯会如何发展。测试人员在职业生涯中的不确定因素还是不少的&#xff0c;由于其入门门槛不高&#xff0c;不用学习太多技术性知识即可入行&#xff0c;所…

RubbleDB: CPU-Efficient Replication with NVMe-oF

RubbleDB: CPU-Efficient Replication with NVMe-oF 前言 这是ATC2023的文章&#xff0c;作者来自哥伦比亚大学这篇工作在LSM-tree多副本存储的场景下&#xff0c;利用NVMe-oF技术避免了LSM-tree副本上的重复合并&#xff0c;减少了CPU开销。 Introduction 为了提供高可用性…

VR全景校园:不被简单定义的校园展示,看的不止“一面”

学校的宣传&#xff0c;还是仅仅依靠一部宣传片来定义的吗&#xff1f;如今&#xff0c;在这个时代&#xff0c;VR全景技术已经越来越成熟了&#xff0c;并逐渐融入了我们的日常生活中&#xff0c;通过VR全景校园&#xff0c;我们可以在网上真实地感受校园的优美环境&#xff0…

Windows + VS2022超详细点云库(PCL1.8.1)配置

本文在结合多位CSDN大佬的步骤&#xff0c;记录以下最全的点云配置过程&#xff0c;防止走弯路&#xff08;并在最后配上PCL环境配置成功的测试代码-彩色兔子&#xff09; 一、PCL介绍 PCL概述_pcl技术_一杯盐水的博客-CSDN博客 二、准备工作&#xff08;PCL版本的下载&…

聊聊logback的ThresholdFilter

序 本文主要研究一下logback的ThresholdFilter ThresholdFilter ch/qos/logback/classic/filter/ThresholdFilter.java public class ThresholdFilter extends Filter<ILoggingEvent> {Level level;Overridepublic FilterReply decide(ILoggingEvent event) {if (!is…

html a标签资源下载

主要用作关卡编辑器生成配置下载&#xff0c;其中存储路径为浏览器默认下载路径 code /*** fileDownload* param content 文件内容&#xff0c;-str* param fileName 文件名&#xff0c;-file/level1.json*/downloadByBlob(content: string, fileName: string){document.creat…

C++ 修饰符、存储类、运算符、循环、判断

一、C修饰符类型&#xff1a; C允许在char、int、double数据类型前放置修饰符。 数据类型修饰符&#xff1a; ◆ signed&#xff1a;表示变量可以存储负数。对于整型变量来说&#xff0c;signed 可以省略&#xff0c;因为整型变量默认为有符号类型。 ◆ unsigned&#xff1…