图像预处理 Tricks【1】:Contours

news2025/1/17 14:11:49

系列文章目录


文章目录

  • 系列文章目录
  • 前言
  • 1. cv2.findContours()
    • 1.1. 方法概述
    • 1.2. cv2.findContours()
      • 1.2.1. 轮廓检索模式
      • 1.2.2. 轮廓逼近方法
  • 2. cv2.drawContours()
    • 2.1. 方法概述
    • 2.2. cv2.drawContours()
  • 3. cv2.contourArea()
    • 3.1. 方法概述
    • 3.2. cv2.contourArea()
    • 3.3. 存在的问题
  • 总结


前言

轮廓可以简单地理解为连接所有连续点(沿物体边界)的曲线,这些点通常具有相同的颜色或强度。 轮廓在图像分析中具有重要意义,是物体形状分析和对象检测和识别的有用工具,是理解图像语义信息的重要依据。

本文主要介绍了在 opencv 中,一些重要的用于处理物体轮廓的方法。


1. cv2.findContours()

1.1. 方法概述

cv2.findContours() 函数是 OpenCV 库的一部分,它被广泛用于计算机视觉任务。这个函数用于检测和寻找图像中的轮廓(边界)。

通常,为了提高物体轮廓检测的准确率,首先要将彩色图像或者灰度图像处理成二值图像(灰度图)或者使用 Canny 边缘检测算法对原图像进行一次滤波处理,这样可以在不丢失轮廓信息的前提下降低图像语义信息的复杂度,更有助于我们准确地分析物体轮廓。

简而言之,寻找轮廓的过程更像是在黑色的背景中,寻找白色物体边界的过程。

1.2. cv2.findContours()

contours, hierarchy = cv2.findContours(image, mode, method)
  • 参数含义
    • imgae:单通道二值图像,白色是前景(二值图像或经过Canny算法处理之后的图像)
    • mode:轮廓检索模式
    • method:轮廓逼近方法
  • 返回值含义
    • contours
      • 一个包含了图像中所有轮廓的list对象。其中每一个独立的轮廓信息以边界点坐标 ( x , y ) (x, y) (x,y) 的形式储存在 numpy 数组中
      • [1, 712, 1, 2]
        • contours.shape[0]:这代表了在图像中发现的轮廓线的总数
        • contours[i].shape[0]:这代表第 i i i 个轮廓的点的数量;每个轮廓线由多个点组成,这个维度给出了轮廓线的长度
        • contours[i].shape[1] :这代表用于表示轮廓线中每个点的维度(坐标)的数量;在大多数情况下,这将是 2 2 2,表示每个点的 ( x , y ) (x, y) (x,y) 坐标
    • hierarchy
      • 一个包含有关轮廓层级的 4 个值的数组 [Next, Previous, First Child, Parent]
        • Next:与当前轮廓处于同一层级的下一条轮廓
        • Previous:与当前轮廓处于同一层级的上一条轮廓
        • First_Child:当前轮廓的第一条子轮廓
        • Parent:当前轮廓的父轮廓

1.2.1. 轮廓检索模式

轮廓检索模式的详细概念如下所示:

轮廓检索模式作用
cv2.RETR_LIST这是最简单的一种寻找方式,它不建立轮廓间的子属关系,也就是所有轮廓都属于同一层级;以 List 形式输出轮廓信息
cv2.RETR_TREE完整建立轮廓的层级从属关系,以树结构输出轮廓信息
cv2.RETR_EXTERNAL只寻找最高层级的轮廓,即外轮廓
cv2.RETR_CCOMP把所有的轮廓只分为 2 个层级,不是外层的就是里层的;输出两层轮廓信息,即内外两个边界,上面一层为外边界,里面一层为内孔的边界信息

基于下面的图,来解释图像的轮廓层级:

在这里插入图片描述
图中总共有 8 条轮廓,2 和 2a 分别表示外层和里层的轮廓,3 和 3a 同理,从图中可以发现:

  1. 轮廓 0、1、2 是最外层的轮廓,我们可以说它们处于同一轮廓等级:0级
  2. 轮廓 2a 是轮廓 2 的子轮廓,反过来说 2 是 2a 的父轮廓;轮廓 2a 算一个等级:1级
  3. 同样 3 是 2a 的子轮廓,轮廓 3 处于一个等级:2级
  4. 类似的,3a 是 3 的子轮廓
  • cv2.RETR_RETR_LIST
    • 这是最简单的一种寻找方式,它不建立轮廓间的子属关系,也就是所有轮廓都属于同一层级
    • hierarchy 中的后两个值 [First_Child, Parent] 都为 − 1 -1 1
    • 因为没有从属关系,所以轮廓 0 0 0 的下一条是 1 1 1,$14 的下一条是 2 2 2
    • 如果不需要轮廓层级信息的话,更推荐使用 cv.RETR_LIST,因为性能更好
contours, hierarchy = cv2.findContours(thresh, cv2.RETR_LIST, 2)
print(hierarchy)
# 结果如下
[[[ 1 -1 -1 -1]
  [ 2  0 -1 -1]
  [ 3  1 -1 -1]
  [ 4  2 -1 -1]
  [ 5  3 -1 -1]
  [ 6  4 -1 -1]
  [ 7  5 -1 -1]
  [-1  6 -1 -1]]]

在这里插入图片描述

  • cv2.RETR_TREE
    • Next
      • 对轮廓 0 来说,与 0 处于同一层级的下一条轮廓是 1,所以 Next = 1
      • 同理,对轮廓 1 来说,Next=2
      • 对轮廓 2 来说,没有与它同一层级的下一条轮廓了,此时 Next = -1
    • Previous
      • 对轮廓 1 来说,Previous = 0
      • 对轮廓 2 来说,Previous = 1
      • 对轮廓 2a 来说,没有上一条轮廓了,所以 Previous = -1
    • First_Child
      • 对轮廓 2 来说,第一条子轮廓就是轮廓 2a,所以 First_Child = 2a
      • 对轮廓 3 来说,First_Child = 3a
    • Parent
      • 对轮廓 2a 来说,父轮廓是 2,Parent = 2
      • 对轮廓 2 来说,没有父轮廓,Parent = 0
contours, hierarchy = cv2.findContours(thresh, cv2.RETR_TREE, 2)
print(hierarchy)
# 结果如下
[[[ 6 -1  1 -1]
  [-1 -1  2  0]
  [-1 -1  3  0]
  [-1 -1  4  2]
  [ 5 -1 -1  3]
  [-1  4 -1  3]
  [ 7  5 -1 -1]
  [-1  6 -1 -1]]]

在这里插入图片描述

  • cv2.RETR_EXTERNAL
    • 这种方式只寻找最高层级的轮廓,也就是它只会找到前面我们所说的 3 条 0 级轮廓
contours, hierarchy = cv.findContours(thresh, cv.RETR_EXTERNAL, 2)
print(len(contours), hierarchy, sep='\n')
# 结果如下
3
[[[ 1 -1 -1 -1]
  [ 2  0 -1 -1]
  [-1  1 -1 -1]]]

在这里插入图片描述

  • cv2.RETR_CCOMP
    • 把所有的轮廓只分为 2 个层级,不是外层的就是里层
    • 上图中,括号里面 1 代表外层轮廓,2 代表里层轮廓
    • 对于轮廓 2
      • Next 就是 4,Previous 是 1
      • 它有里层的轮廓 3,所以 First_Child = 3
      • 但因为只有两个层级,它本身就是外层轮廓,所以 Parent = -1
contours, hierarchy = cv.findContours(thresh, cv.RETR_CCOMP, 2)
print(hierarchy)
# 结果如下
[[[ 1 -1 -1 -1]
  [ 2  0 -1 -1]
  [ 4  1  3 -1]
  [-1 -1 -1  2]
  [ 6  2  5 -1]
  [-1 -1 -1  4]
  [ 7  4 -1 -1]
  [-1  6 -1 -1]]]

1.2.2. 轮廓逼近方法

轮廓逼近方法作用
cv2.CHAIN_APPROX_NONE存储所有边界点
cv2.CHAIN_APPROX_SIMPLE不保存轮廓中水平、垂直、对角的线段,只保存轮廓的角点
cv2.CHAIN_APPROX_TX89_L1使用 teh-Chini 近似算法
cv2.CHAIN_APPROX_TC89_KCOS使用 teh-Chini 近似算法
  • 一般来说,后面两种方法不常见
  • cv2.CHAIN_APPROX_NONE 见下图左;cv2.CHAIN_APPROX_SIMPLE 见下图右

在这里插入图片描述


2. cv2.drawContours()

2.1. 方法概述

在通过 cv2.findContours() 找到物体的轮廓后,有时候需要可视化物体的轮廓以判断是否符合预期,这个时候就需用到 opencv 包含的另一个重要方法 cv2.drawContours()cv2.drawContours() 可以在 image 上用指定的颜色和线的宽度画出使用 cv2.findContours() 获得的物体轮廓。

2.2. cv2.drawContours()

cv2.drawContours(image, contours, contourIdx, color, thickness=None, lineType=None, hierarchy=None, maxLevel=None, offset=None)
  • 参数含义
    • image
      • 这是要绘制轮廓线的输入图像
      • 通常是一个 RGB 格式的图像,或者是一个灰度图
    • contours
      • 是你想在图像上绘制的轮廓线的列表
      • 它应该是一个 Python 列表,其中每个轮廓被表示为一个 ( x , y ) (x, y) (x,y) 坐标的 NumPy 数组
    • contourIdx
      • 这个参数指定要画的轮廓的索引
      • 通常使用 -1 来绘制所有的轮廓
    • color
      • 这个参数指定了轮廓线的颜色
      • 对于彩色图像,颜色应该被指定为代表 BGR 值的三个整数的元组(需要注意是 BGR 而不是 RGB,与使用 cv2.imread() 方法读进来的彩色图像通道相同)
      • 对于灰度图像,颜色值是一个代表强度的标量
    • thickness
      • 这个参数指定轮廓线的厚度,单位是像素
      • 使用一个负值来填充轮廓线

3. cv2.contourArea()

3.1. 方法概述

cv2.contourArea() 函数是 OpenCV 提供的一个实用函数,用于计算一个轮廓的面积。它接受一个轮廓作为输入,并返回该轮廓的面积。

3.2. cv2.contourArea()

area = cv2.contourArea(contours, oriented=false)
  • 参数含义
    • contours
      • 要计算面积的输入轮廓
      • 应该是一个由 ( x , y ) (x, y) (x,y) 坐标组成的 NumPy 数组表示的单一轮廓
    • oriented
      • 面向区域标识符。有默认值 false
      • 若为 true,该函数返回一个带符号的面积值,正负取决于轮廓的方向(顺时针还是逆时针)
      • 若为 false,表示以绝对值返回。
  • 返回值
    • area
      • 是输出变量,用于存储计算出的轮廓所围成的面积
      • 它是一个浮点值,以像素为单位表示面积

3.3. 存在的问题

需要注意的是 cv2.contourArea() 计算得到的面积,可能会比预期的面积稍微小一点,这是因为 cv2.contourArea() 通过取连通域边界像素中心点,连接起来,成为一个轮廓,导致一周得边界像素点丢失,即求得得面积比真实得面积少了一圈

比如下图,真实面积 4 × 4 = 16 4 \times 4 = 16 4×4=16,而 cv2.contourArea() 则只计算红线内的面积,只有 3 × 3 = 9 3 \times 3 = 9 3×3=9

在这里插入图片描述

因此,cv2.contourArea() 可能将一些轮廓的面积计算为 0,如下图所示

在这里插入图片描述

一个简单的解决方法为

 rect = cv2.minAreaRect(contours)  #最小外接矩形
 box = cv2.boxPoints(rect)
 box = np.int0(box)
 area = cv2.contourArea(box)

总结

本文主要记录了一些博主最近在实验过程中遇到的与 label 轮廓相关的处理函数,如果后面有需要,会继续更新相关的函数。

参考博客
参考博客

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

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

相关文章

java springboot整合MyBatis联合查询

前面文章 java springboot整合MyBatis做数据库查询操作写了springboot整合MyBatis的方法 并演示了基础查询的语法 根据id查 那么 我们这次来演示联合查询 我们staff 表 内容如下 每条数据 对应的都有一个departmentid 这是 department部门表的外键id department表内容如下 如…

Redis 分布式缓存

分布式缓存 单点 Redis 的问题及解决 数据丢失:实现Redis数据持久化并发能力:搭建主从集群,实现读写分离存储能力:搭建分片集群,利用插槽机制实现动态扩容故障恢复能力:利用哨兵机制,实现健康…

Linux系统编程(进程基础知识讲解)

文章目录 前言一、进程的概念二、进程的生命周期三、进程树四、进程的创建五、一个进程可以执行几个程序?六、子进程中调用execve函数总结 前言 本篇文章来讲解Linux中的进程,进程在Linux中是非常重要的一个知识点,掌握好进程是非常重要的。…

postgresql源码学习(56)—— explain是如何快速估算pg表行数的

当我们需要大致知道表行数,但又不需要很精确时,可以采用以下方法 一、 统计信息 pg_class.reltuples 最简便的方法是利用pg_class.reltuples,类似oracle的num_rows postgres# select reltuples::numeric from pg_class where relnamepgbenc…

VUE 2X 表单数据过滤器 ⑨

目录 文章有误请指正,如果觉得对你有用,请点三连一波,蟹蟹支持✨ V u e j s Vuejs Vuejs收集表单数据过滤器 使用 C o o k i e Cookie Cookie 影响总结 文章有误请指正,如果觉得对你有用,请点三连一波,蟹蟹…

【计算机组成原理】RISC-V模型机的有限状态控制器设计

目录 一、RISC-V模型机的目标指令集 二、RISC-V模型机的部件设计 三、运算及传送指令的数据通路设计 四、访存指令的数据通路设计 五、转移类指令的数据通路设计 六、RISC-V模型机控制单元CU的有限状态机设计 一、RISC-V模型机的目标指令集 取指令并译码:根据…

编译原理笔记16:自下而上语法分析(3)构造 DFA、DFA 对下一步分析的指导(有效项目)

目录 由 NFA 用子集法构造 DFA由 LR(0) 项目直接构造识别活前缀的 DFA构造 DFA求拓广文法 GCLOSURE & GO例: 构造 DFA DFA 指导下一步分析有效项目 看了前面的内容,我们已经了解到:分析表和驱动器算法,是 LR 分析器的核心。 …

实训四:索引与视图 - SQL视图(teachingdb数据库)

SQL视图的定义与操纵 第1关:创建视图任务描述相关知识视图的定义创建视图 编程要求测试说明参考代码 第2关:创建视图-练习一任务描述相关知识编程要求测试说明参考代码 第1关:创建视图 任务描述 本关任务:建立计算机系的学生的视…

团体程序设计天梯赛-练习集L1篇⑧

🚀欢迎来到本文🚀 🍉个人简介:Hello大家好呀,我是陈童学,一个与你一样正在慢慢前行的普通人。 🏀个人主页:陈童学哦CSDN 💡所属专栏:PTA 🎁希望各…

C语言scanf/fscanf/sscnaf和printf/fprintf/sprintf的区别

总结 1.scanf/printf 是标准输入输出流函数(键盘、屏幕)。 2.fscanf/fprintf 适用于所有输入输出流(文件、键盘、屏幕…)。 3.sscanf/sprintf 是把格式化的数据写入某个字符串中,从某个字符串中读取格式化的数据。 第一组:scanf/printf scanf/printf是…

Oracle数据库从入门到精通系列之十八:详细总结Oracle数据库核心知识点

Oracle数据库从入门到精通系列之十八:详细总结Oracle数据库核心知识点 一、Oracle数据库核心概念二、Oracle非容器数据库三、Oracle容器数据库四、容器数据库和非容器数据库的区别五、Oracle数据库多租户六、Oracle数据库多租户数据库模型七、Oracle数据库类型八、O…

实训四:索引与视图 - MySQL开发技巧 - 索引

MySQL开发技巧 - 索引 任务描述相关知识索引是什么索引的分类索引的创建和删除查询表中索引 编程要求测试说明代码参考: 任务描述 本关任务:按照要求完成索引的创建。 相关知识 为了完成本关任务,你需要掌握: 索引是什么&#…

【Leetcode60天带刷】day31回溯算法——455.分发饼干 ,376. 摆动序列 , 53. 最大子序和

​ 题目: 455. 分发饼干 假设你是一位很棒的家长,想要给你的孩子们一些小饼干。但是,每个孩子最多只能给一块饼干。 对每个孩子 i,都有一个胃口值 g[i],这是能让孩子们满足胃口的饼干的最小尺寸;并且每块…

Android 13(T) - binder阅读(3)- binder相关的类

原先准备在binder阅读(3)中记录ServiceManager的使用,但是写着写着发现,如果不了解和binder相关的类,那么阅读起来将会由很多困惑,所以就先来记录binder相关的类了。记录完发现特别凌乱…先就这样吧。 1 UM…

【致敬未来的攻城狮计划】打卡3:点亮LED

点亮LED 本文主要参考文章:【致敬未来的攻城狮计划】— 连续打卡第十一天:FSP固件库开发点亮第一个灯。_嵌入式up的博客-CSDN博客 在32阶段我们已经接触过类似做法了。初始化引脚模式(可以手动库函数,或者在工具包图形化界面里配…

实训四:索引与视图 - MySQL开发技巧 - 视图

MySQL开发技巧 - 视图 任务描述相关知识视图的定义创建视图操作视图删除视图 编程要求测试说明参考代码 任务描述 本关任务:通过学习视图,创建一个单表视图和一个多表视图。 相关知识 为了完成本关任务,你需要掌握: 视图的定义…

工地扬尘智能监测系统 yolov7

工地扬尘智能监测系统通过yolov7网络算法模型技术,实时监测工地施工中的扬尘情况。工地扬尘智能监测系统利用AI视频智能分析技术,并将数据传输到数据中心进行分析。YOLOv7 的发展方向与当前主流的实时目标检测器不同,研究团队希望它能够同时支…

数据库管理-第八十四期 X10M来了(20230624)

数据库管理 2023-06-24 第八十四期 X10M来了1 Intel -> AMD2 PMEM -> XRMEM3 DDR4 -> DDR54 Flash cards总结 第八十四期 X10M来了 在第四十三期的时候,我曾经憧憬过Exadata X10M的到来,Oracle于6月22日正式公布Exadata X10M系列。其实5月已经…

chatgpt赋能python:Python在电气行业中的应用——从数据分析到自动化控制

Python在电气行业中的应用——从数据分析到自动化控制 介绍 Python语言作为一种高级编程语言,越来越受到电气行业的关注。随着互联网、物联网以及大数据时代的到来,电气行业需要将传统的工业控制与现代化的数据分析、智能决策等技术相结合,…

Java——《面试题——Dobbo篇》

前文 java——《面试题——基础篇》 Java——《面试题——JVM篇》 Java——《面试题——多线程&并发篇》 Java——《面试题——Spring篇》 Java——《面试题——SpringBoot篇》 Java——《面试题——MySQL篇》​​​​​​ Java——《面试题——SpringCloud》 目录…