【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.hqwc.cn/news/206300.html

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

相关文章

echarts实现如下图功能代码

这里写自定义目录标题 const option {tooltip: {trigger: axis},legend: {left: "-1px",top:15px,type: "scroll",icon:rect,data: [{name:1, textStyle:{color: theme?"#E5EAF3":#303133,fontSize:14}}, {name: 2, textStyle:{color: theme…

监控摄像头连接NAS,实现监控管理一体化

嗯?你问干嘛要把摄像头连到NAS? 小马给家里安了个监控摄像头 本意是想家里有啥事也能查监控 却没想到这些监控不仅存储回放有限制 要想更多功能还是得多花钱 恰好,我有铁威马NAS 打开Surveillance Manager 轻松搭建网络摄像头管理系统 …

安防监控视频融合平台EasyCVR定制化页面开发

安防监控EasyCVR视频汇聚平台基于云边端智能协同,支持海量视频的轻量化接入与汇聚、转码与处理、全网智能分发、视频集中存储等。安防视频平台EasyCVR拓展性强,视频能力丰富,具体可实现视频监控直播、视频轮播、视频录像、云存储、回放与检索…

用HALCON标定助手对相机进行标定

任务要求: 已知相机镜头焦距f为8mm,相机单个CCD像素在水平和竖直两个方向上的尺寸均为3.75微米,相机为普通透光镜头和面阵相机,对相机进行标定,测量相机的内外参数。 操作步骤: 1. 在HALCON中运行gen_ca…

专业pdf编辑工具PDF Expert mac中文版特点介绍

PDF Expert mac是一款专业的PDF编辑和阅读工具。它可以帮助用户在Mac、iPad和iPhone等设备上查看、注释、编辑、填写和签署PDF文档。 PDF Expert mac软件特点 PDF编辑:PDF Expert提供了丰富的PDF编辑功能,包括添加、删除、移动、旋转、缩放、裁剪等操作…

焦炉加热系统简述

烟道吸力 焦炉负压烘炉分烟道的吸力会影响立火道温度,具体影响因素如下: 烟道吸力过大会导致热量被抽走,使立火道温度降低。烟道吸力不足会导致烟气在烘炉内停留时间过长,使热量无法充分利用,也会导致立火道温度降低…

【Windows 常用工具系列 13 -- Confluence 如何快速输入代码块 code block】

文章目录 Confluence 如何快速输入代码块方法二 Confluence 如何快速输入代码块 在使用使用 confluence 进行文档编辑时,有时需要贴上部分代码,但是直接贴代码在 confluence上,显示效果不是太好看,所以confluence 给我们提供了符…

sklearn模型中预测值的R2_score为负数

目录 正文评论区参考链接 正文 Sklearn.metrics下面的r2_score函数用于计算R(确定系数:coefficient of determination)。它用来度量未来的样本是否可能通过模型被很好地预测。 分值为 1 表示最好,但我们在使用过程中&#xff0c…

剧情继续:马斯克曝出OpenAI前员工举报信,董事会与奥特曼谈判回归

丰色 发自 凹非寺 量子位 | 公众号QbitAI 经过4天的极限拉扯、反转再反转,奥特曼有可能重新回归了。 据知情人士透露,OpenAI董事会正与奥特曼进行一场“富有成效”的新谈判。 如果奥特曼回到OpenAI,他将继续担任CEO。 与此同时&#xff0c…

ROS设置DHCP option121

配置时,了解格式很关键,16进制填写格式如下: 将要访问的IPV4地址:192.168.100.0/24 192.168.30.254 转换为:掩码 目标网段 网关 0x18c0a864c0a81efe,0不用填写 ROS配置如下图: 抓…

modbus协议及modbus TCP协议

一、Modbus协议 1.起源 Modbus由Modicon公司于1979年开发,是一种工业现场总线协议标准。 Modbus通信协议具有多个变种,其中有支持串口,以太网多个版本,其中最著名的是Modbus RTU(通信效率最高,基于串口&am…

Golang版本处理Skywalking Trace上报数据

Tips: 中间记录了解决问题的过程,如不感兴趣可直接跳至结尾 首先去es里查询skywalking trace的元数据 可以拿到一串base64加密后的data_binary(直接解密不能用,会有乱码,可参考https://github.com/apache/skywalking/issues/7423) 对data_b…