OpenCV轮廓图的一些操作

1.按短边筛选

 原始轮廓图:

import cv2
import numpy as np

# 读取轮廓图
contour_image = cv2.imread('..\\IMGS\\pp_edge.png', cv2.IMREAD_GRAYSCALE)

# 使用cv2.findContours()函数获取所有轮廓
contours, _ = cv2.findContours(contour_image, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)

# 设定的直径阈值
threshold_diameter = 25.0

# 遍历每个轮廓
new_image = np.zeros_like(contour_image)
for contour in contours:
    rect = cv2.minAreaRect(contour)  # 获取最小外接矩形
    diameter = rect[1][0] if rect[1][0] <= rect[1][1] else rect[1][1]    # 计算轮廓的最短尺寸,并获取直径

    # 如果轮廓的直径小于设定的阈值,则忽略该轮廓
    if diameter >= threshold_diameter:
        cv2.drawContours(new_image, [contour], -1, (255, 255, 255))
        print(diameter, end=', ')


cv2.imshow('Filtered Contours', new_image)   # 绘制保留的轮廓
cv2.waitKey(0)
cv2.destroyAllWindows()


 输出结果以及与原图对比:

                                     

2.按长边筛选

import cv2
import numpy as np

# 读取轮廓图
contour_image = cv2.imread('..\\IMGS\\pp_edge2.png', cv2.IMREAD_GRAYSCALE)

# 使用cv2.findContours()函数获取所有轮廓
contours, _ = cv2.findContours(contour_image, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)

# 设定的直径阈值
threshold_diameter = 25.0

# 遍历每个轮廓
new_image = np.zeros_like(contour_image)
for contour in contours:
    diameter = cv2.minEnclosingCircle(contour)[1] * 2   # cv2.minEnclosingCircle():计算一个轮廓的最小包围圆,返回值是圆心坐标和半径

    # 如果轮廓的直径小于设定的阈值,则忽略该轮廓
    if diameter >= threshold_diameter:
        cv2.drawContours(new_image, [contour], -1, (255, 255, 255))
        print(diameter, end=', ')


cv2.imshow('Filtered Contours', new_image)   # 绘制保留的轮廓
cv2.waitKey(0)
cv2.destroyAllWindows()


结果图:

 3.将粘连的轮廓分开

有经过灰度、二值化处理后的图如下,需要将图中的所有轮廓读出,用以进行尺寸测量和数量统计等后续操作。

看得出,图中有两个轮廓 粘连到了一起,如果直接进行查找和统计就会出错。

例如用下面代码将轮廓找出并画出:

import cv2
import numpy as np

input_img = cv2.imread('..\\IMGS\\pp_binary.png', cv2.IMREAD_GRAYSCALE)     # 读取已转换过的轮廓图像
contours = cv2.findContours(input_img, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)[0]   # 查找轮廓
tmp_img = np.zeros_like(input_img)  # 创建空白临时图像
cv2.drawContours(tmp_img, contours, -1, 255)  # 将轮廓绘制在临时图像上
cv2.imshow('OutPut', tmp_img)   # 显示轮廓图
cv2.waitKey(0)
cv2.destroyAllWindows()

输出图如下:

 看得出,轮廓是粘到一起的。为了将轮廓分开,初步的设想是,先用腐蚀的方法,如果存在轮廓粘连,那么腐蚀操作以后,图像就会被分成多个独立的部分,然后将每个独立的部分再进行膨胀操作恢复尺寸,并分别读取每个独立部分的轮廓,这样就实现了粘连轮廓的分离。

代码:

import cv2
import numpy as np

input_img = cv2.imread('..\\IMGS\\pp_binary.png', cv2.IMREAD_GRAYSCALE)     # 读取已转换过的轮廓图像
contours = cv2.findContours(input_img, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)[0]   # 查找轮廓

out_contours = []    # 创建用以输出的轮廓线列表

for contour in contours:      # 针对每一个轮廓进行操作
    rect = cv2.minAreaRect(contour)   # 获取最小外接矩形
    diameter = rect[1][0] if rect[1][0] <= rect[1][1] else rect[1][1]   # 计算轮廓的最短尺寸,并获取直径

    num_kernel = int(diameter / 2.5)   # 定义核的大小
    if num_kernel % 2 == 0:
        num_kernel += 1
    kernel = np.ones((num_kernel, num_kernel), np.uint8)  # 定义核

    tmp_img = np.zeros_like(input_img)   # 创建空白临时图像
    cv2.drawContours(tmp_img, [contour], -1, (255, 255, 255), thickness=cv2.FILLED)    # 将当前的轮廓绘制在临时图像上

    erode_img = cv2.erode(tmp_img, kernel, iterations=1)   # 腐蚀操作,打破连接
    tmp_contours = cv2.findContours(erode_img, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)[0]      # 读取腐蚀操作后的临时图像的轮廓
    if len(tmp_contours) > 1:    # 如果存在轮廓粘连,那么腐蚀操作以后,图像就会被分成多个独立的部分
        for tmp in tmp_contours:
            tmp_img = np.zeros_like(input_img)  # 创建空白临时图像
            cv2.drawContours(tmp_img, [tmp], -1, (255, 255, 255), thickness=cv2.FILLED)  # 将轮廓绘制在临时图像上
            dilate_img = cv2.dilate(tmp_img, kernel, iterations=1)  # 膨胀操作,恢复轮廓尺寸
            _ = cv2.findContours(dilate_img, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)[0]  # 读取膨胀操作后的临时图像的轮廓
            out_contours.extend(_)     # 将膨胀操作后的轮廓加入输出用的轮廓列表
    else:
        out_contours.extend([contour])      # 不存在轮廓粘连,直接将当前轮廓加入输出用的轮廓列表

# 在原图上绘制轮廓
tmp_img = np.zeros_like(input_img)   # 创建空白临时图像,用以输出
cv2.drawContours(tmp_img, out_contours, -1, 255)   # 在临时图像上画出所有轮廓

# 显示结果
mask_img = cv2.cvtColor(tmp_img, cv2.COLOR_GRAY2BGR)    # 掩模图,用以将轮廓叠加在原图上
mask_img[tmp_img == 255] = [0, 255, 0]  # 按照临时图中的白色位置,将掩模图中的对应位置变为绿色
gray_img_3ch = cv2.cvtColor(input_img, cv2.COLOR_GRAY2BGR)   # 转换创建RGB图,用以显示
show_img = cv2.addWeighted(gray_img_3ch, 0.5, mask_img, 1, 0)  # 将掩模图与灰色3通道图叠加输出
cv2.imshow('OutPut', show_img)

cv2.waitKey(0)
cv2.destroyAllWindows()
输出结果:

 粘连的轮廓已经被分开,成为两个相邻的独立轮廓。当然了,由于使用了先腐蚀再膨胀的操作,轮廓尺寸会有一些精度上的损失,但是比起将两个轮廓误统计为一个,误差还是小多了。

题外话,先腐蚀再膨胀看上去很熟悉是吧,不就是开操作吗?直接使用开操作行不行?我的实验结果是不行,会得到类似下面这样的结果:

 

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Firebase Local Emulator Suite详解

文章目录 Firebase Local Emulator Suite 组件安装和使用步骤1. 安装 Firebase CLI2. 初始化 Firebase 项目3. 配置模拟器4. 启动模拟器5. 配置应用程序使用本地模拟器 常见用途 Firebase Local Emulator Suite 是一组本地服务&#xff0c;可以模拟 Firebase 平台的在线服务&am…

《C语言深度解剖》(18):“取整”、“取余“” 和 “取模”的关联与区别?

&#x1f921;博客主页&#xff1a;醉竺 &#x1f970;本文专栏&#xff1a;《C语言深度解剖》 &#x1f63b;欢迎关注&#xff1a;感谢大家的点赞评论关注&#xff0c;祝您学有所成&#xff01; ✨✨&#x1f49c;&#x1f49b;想要学习更多C语言深度解剖点击专栏链接查看&…

word-简历排版

1、确认字体&#xff08;微软雅黑&#xff09;、字号&#xff08;五号/小五&#xff09; 2、设置段间距和行间距、页边距 3、突出各模块标题&#xff0c;增加分格线 4、使用制表位进行对齐&#xff1a;视图-标尺&#xff0c;制表符&#xff08;tab&#xff09;和制表位共同使…

我喜欢的vscode插件

有个更全的&#xff1a;提高编程效率的30个VScode插件 Image preview&#xff08;图片预览&#xff09; any-rule&#xff08;正则表达式大全&#xff09; px to rem & rpx & vw(cssrem)&#xff08;px和rem之间转换&#xff09; 小程序开发助手 Auto Close Tag A…

【C语言】探索文件读写函数的全貌

&#x1f308;个人主页&#xff1a;是店小二呀 &#x1f308;C语言笔记专栏&#xff1a;C语言笔记 &#x1f308;C笔记专栏&#xff1a; C笔记 &#x1f308;喜欢的诗句:无人扶我青云志 我自踏雪至山巅 &#x1f525;引言 本章将介绍文件读取函数的相关知识和展示使用场景&am…

电脑设置密码怎么设置?让你的电脑更安全!

在如今信息化的社会中&#xff0c;保护个人电脑的安全至关重要。设置密码是最基本的电脑安全措施之一&#xff0c;它可以有效防止未经授权的访问和保护个人隐私&#xff0c;可是电脑设置密码怎么设置&#xff1f;本文将介绍三种设置电脑密码的方法&#xff0c;帮助您加强电脑的…

MySQL增删查改初阶

目录 一&#xff0c;数据库操作 1.关键字 show 显示当前数据库有哪些&#xff1a;show databases&#xff1b; 2.创建数据库 3.选中数据库 4.删除数据库 二&#xff0c;表的操作&#xff0c;在选中数据库的基础之上 1.查看表的结构 2.创建表 3.查看当前选中的数据库中…

docker目录挂载失败:Check if the specified host path exists and is the expected type

docker目录挂载失败&#xff1a;Check if the specified host path exists and is the expected type docker目录挂载命令&#xff0c;其目的是为了达到修改linux上的文件同步到容器上&#xff0c;从而实现修改容器的配置文件。 在docker目录挂载或启动容器时报错&#xff0c…

C# :IQueryable IEnumerable

1. IEnumerable namespace System.Collections: public interface IEnumerable {public IEnumerator GetEnumerator (); }public interface IEnumerator {pubilc object Current { get; }public bool MoveNext ();public void Reset (); }IEnumerable 只有一个方法 GetEnumera…

【PHP项目实战训练】——laravel框架的实战项目中可以做模板的增删查改功能(1)

&#x1f468;‍&#x1f4bb;个人主页&#xff1a;开发者-曼亿点 &#x1f468;‍&#x1f4bb; hallo 欢迎 点赞&#x1f44d; 收藏⭐ 留言&#x1f4dd; 加关注✅! &#x1f468;‍&#x1f4bb; 本文由 曼亿点 原创 &#x1f468;‍&#x1f4bb; 收录于专栏&#xff1a…

UE5 Http Server

前言 最近要用UE 作为一个服务器去接收来自外部的请求&#xff0c;从而在UE中处理一些内容&#xff0c;但是之前只做过请求&#xff0c;哪整过这玩意&#xff0c;短期内还得出结果&#xff0c;那怎么搞嘞&#xff0c;本着省事的原则就找找呗&#xff0c;有没有现成的&#xff0…

2020 6.s081——Lab5:Lazy page allocation

再来是千年的千年 不变是眷恋的眷恋 飞越宇宙无极限 我们永不说再见 ——超兽武装 完整代码见&#xff1a;SnowLegend-star/6.s081 at lazy (github.com) Eliminate allocation from sbrk() (easy) 顾名思义&#xff0c;就是去掉sbrk()中调用growproc()的部分。1s完事儿。 Laz…

两数之和 II - 输入有序数组,三数之和

题目一&#xff1a; 代码如下&#xff1a; vector<int> twoSum(vector<int>& numbers, int target) {int left 0;int right numbers.size() - 1;vector<int> ret;while (left < right){int tmp numbers[left] numbers[right];if (tmp target){r…

Mac OS 用户开启 80 端口

开启端口 sudo vim /etc/pf.conf # 开放对应端口 pass out proto tcp from any to any port 8080 # 刷新配置文件 sudo pfctl -f /etc/pf.conf sudo pfctl -e获取本机ip地址 ifconfig en0 | grep inet | grep -v inet6 | awk {print $2}访问指定端口

栈和队列题目练习

本节小编选了两道题来加深对栈和队列的认识理解&#xff01; 有效的括号 方法1&#xff1a;直接用栈的结构&#xff08;动态数组&#xff09; 本题可以用栈这个结构来解答&#xff0c;将(,{,[ 左括号压入栈中&#xff0c;然后取出栈顶元素与右括号),},]匹配。不匹配的话&…

【成品设计】基于STM32的智能婴儿床设计

《基于STM32的智能婴儿床设计》 所需器件&#xff1a; 主控&#xff1a;STM32F103C8T6最小系统板。OLED屏幕&#xff1a;显示系统状态等。按键&#xff1a;自动模式和遥控模式切换 。180度舵机模块&#xff1a;通过0度~90度之间摆动模拟婴儿床的摆动。360度舵机模块&#xff…

为什么要使用动态代理IP?

一、什么是动态代理IP&#xff1f; 动态代理IP是指利用代理服务器来转发网络请求&#xff0c;并通过不断更新IP地址来保护访问者的原始IP&#xff0c;从而达到匿名访问、保护隐私和提高访问安全性的目的。动态代理IP在多个领域中都有广泛的应用&#xff0c;能够帮助用户…

函数调用之栈平衡

一&#xff0c;前言 如约而至&#xff0c;献上c/c在调用函数过程中关于栈平衡的心得&#xff0c;帮助大家了解内存中关于栈空间的分配过程&#xff08;ps:栈平衡通常也被说成堆栈平衡&#xff09;&#xff1b; 话不多说&#xff0c;下面以函数 int __cdecl GetResult(int uPa…

【康耐视国产案例】AI视觉相机创新 加速商超物流数智化转型

连锁商超/零售店正面临着因消费者购物习惯改变等挑战&#xff0c;迎来了以新兴技术崛起而催生的数字化物流体系转型需求。物流行业与AI机器视觉的深度融合&#xff0c;解决了传统机器视觉识别速度慢、环境要求高、定制化部署耗时过多等痛点&#xff0c;大大提高了物流供应链的效…

NPDP(New Product Development Professional)

NPDP&#xff08;New Product Development Professional&#xff09; NPDP考试介绍 NPDP证书介绍