当前位置: 首页 > news >正文

【python】OpenCV—findContours(4.6)

在这里插入图片描述

文章目录

  • 1、功能描述
  • 2、代码实现
  • 3、效果展示
  • 4、完整代码
  • 5、涉及到的库函数
    • cv2.inRange
  • 6、参考

1、功能描述

给出一张仅含有手指的图片,判断图片中有多少根手指

2、代码实现

导入库函数,图像预处理

import numpy as np
import cv2 as cv
img = cv.imread("./5.png")
mask_img = skinmask(img)
cv.imwrite("mask_img.jpg", mask_img)

其中 def skinmask 实现如下

def skinmask(img):hsvim = cv.cvtColor(img, cv.COLOR_BGR2HSV)lower = np.array([0, 48, 80], dtype = "uint8")upper = np.array([20, 255, 255], dtype = "uint8")skinRegionHSV = cv.inRange(hsvim, lower, upper)cv.imwrite("skinRegionHSV.jpg", skinRegionHSV)blurred = cv.blur(skinRegionHSV, (2,2))ret, thresh = cv.threshold(blurred,0,255,cv.THRESH_BINARY)return thresh

先转化为 HSV 彩色模式,然后 cv2.inRange 二值化处理,接着模糊,二值化

skinRegionHSV.jpg

在这里插入图片描述

mask_img.jpg
在这里插入图片描述

接下里计算轮廓和凸包

contours, hull = getcnthull(mask_img)cv.drawContours(img, [contours], -1, (255,255,0), 2)
cv.imwrite("contours.jpg", img)cv.drawContours(img, [hull], -1, (0, 255, 255), 2)
cv.imwrite("hull.jpg", img)

其中 def getcnthull 实现如下

def getcnthull(mask_img):contours, hierarchy = cv.findContours(mask_img, cv.RETR_TREE, cv.CHAIN_APPROX_SIMPLE)contours = max(contours, key=lambda x: cv.contourArea(x))hull = cv.convexHull(contours)return contours, hull

可以看到凸包是通过计算最大面积的轮廓计算得到的

轮廓 contours.jpg

在这里插入图片描述

凸包 hull.jpg

在这里插入图片描述

接着计算凸缺陷,也即介于凸包和轮廓间的区域

defects = getdefects(contours)

其中 def getdefects 的实现如下

def getdefects(contours):hull = cv.convexHull(contours, returnPoints=False)defects = cv.convexityDefects(contours, hull)return defects

遍历凸缺陷,计算角度,判断是否为手指夹缝,输出手指个数

if defects is not None:cnt = 0for i in range(defects.shape[0]):  # calculate the angles, e, f, d = defects[i][0]start = tuple(contours[s][0])end = tuple(contours[e][0])far = tuple(contours[f][0])a = np.sqrt((end[0] - start[0]) ** 2 + (end[1] - start[1]) ** 2)b = np.sqrt((far[0] - start[0]) ** 2 + (far[1] - start[1]) ** 2)c = np.sqrt((end[0] - far[0]) ** 2 + (end[1] - far[1]) ** 2)angle = np.arccos((b ** 2 + c ** 2 - a ** 2) / (2 * b * c))  #      cosine theoremif angle <= np.pi / 2:  # angle less than 90 degree, treat as fingerscnt += 1cv.circle(img, far, 4, [0, 0, 255], -1)if cnt > 0:cnt = cnt+1cv.putText(img, str(cnt), (0, 50), cv.FONT_HERSHEY_SIMPLEX,1, (255, 0, 0) , 2, cv.LINE_AA)
cv.imshow("img", img)
cv.imwrite(f"result_{str(cnt)}.jpg", img)
cv.waitKey()
cv.destroyAllWindows()

其中 angle 的计算原理如下

在这里插入图片描述

当然仔细看代码这图和代码中对应的字母有差异,代码中 start 到 end 是 a 对应上图的 c,代码中 start 到 far 和 end 到 far 为 b 和 c 对应上图的 a 和 b,

在这里插入图片描述

小于 90 度我们才认为是手指

3、效果展示

5 根手指

输入
在这里插入图片描述

输出

在这里插入图片描述

4 根手指

在这里插入图片描述

在这里插入图片描述

3 根手指

在这里插入图片描述
在这里插入图片描述

2 根手指

在这里插入图片描述

在这里插入图片描述

1 根手指

在这里插入图片描述
在这里插入图片描述
翻车

在这里插入图片描述

在这里插入图片描述

可以看到几个缺点

十分依赖前处理,前处理不行,轮廓不对,后续结果判断则会相应的出现问题

只有一个手指的时候,或者握拳的时候,这套流程应该是判断不出来有几根手指的

4、完整代码

import cv2
import numpy as np
import cv2 as cvdef skinmask(img):hsvim = cv.cvtColor(img, cv.COLOR_BGR2HSV)lower = np.array([0, 48, 80], dtype = "uint8")upper = np.array([20, 255, 255], dtype = "uint8")skinRegionHSV = cv.inRange(hsvim, lower, upper)cv.imwrite("skinRegionHSV.jpg", skinRegionHSV)blurred = cv.blur(skinRegionHSV, (2,2))ret, thresh = cv.threshold(blurred,0,255,cv.THRESH_BINARY)return threshdef getcnthull(mask_img):contours, hierarchy = cv.findContours(mask_img, cv.RETR_TREE, cv.CHAIN_APPROX_SIMPLE)contours = max(contours, key=lambda x: cv.contourArea(x))hull = cv.convexHull(contours)return contours, hulldef getdefects(contours):hull = cv.convexHull(contours, returnPoints=False)defects = cv.convexityDefects(contours, hull)return defectsimg = cv.imread("./5.png")
mask_img = skinmask(img)
cv.imwrite("mask_img.jpg", mask_img)contours, hull = getcnthull(mask_img)cv.drawContours(img, [contours], -1, (255,255,0), 2)
cv.imwrite("contours.jpg", img)cv.drawContours(img, [hull], -1, (0, 255, 255), 2)
cv.imwrite("hull.jpg", img)defects = getdefects(contours)if defects is not None:cnt = 0for i in range(defects.shape[0]):  # calculate the angles, e, f, d = defects[i][0]start = tuple(contours[s][0])end = tuple(contours[e][0])far = tuple(contours[f][0])a = np.sqrt((end[0] - start[0]) ** 2 + (end[1] - start[1]) ** 2)b = np.sqrt((far[0] - start[0]) ** 2 + (far[1] - start[1]) ** 2)c = np.sqrt((end[0] - far[0]) ** 2 + (end[1] - far[1]) ** 2)angle = np.arccos((b ** 2 + c ** 2 - a ** 2) / (2 * b * c))  #      cosine theoremif angle <= np.pi / 2:  # angle less than 90 degree, treat as fingerscnt += 1cv.circle(img, far, 4, [0, 0, 255], -1)if cnt > 0:cnt = cnt+1cv.putText(img, str(cnt), (0, 50), cv.FONT_HERSHEY_SIMPLEX,1, (255, 0, 0) , 2, cv.LINE_AA)
cv.imshow("img", img)
cv.imwrite(f"result_{str(cnt)}.jpg", img)
cv.waitKey()
cv.destroyAllWindows()

5、涉及到的库函数

关于 cv2.convexHullcv2.convexityDefects 的使用说明可以参考 【python】OpenCV—findContours(4.2)

cv2.inRange

cv2.inRange 是 OpenCV 库中的一个函数,用于检查数组元素是否位于两个指定的范围之间。这个函数通常用于图像处理和计算机视觉任务中,特别是在进行图像阈值处理、颜色空间转换后的颜色过滤等场景。

cv2.inRange(src, lowerb, upperb, dst=None)
  • src: 输入数组(通常是图像),它应该是一个单通道或多通道的数组。
  • lowerb: 范围的下限(包含),与 src 同类型、同大小的数组,或者是一个标量值。
  • upperb: 范围的上限(不包含),与 src 同类型、同大小的数组,或者是一个标量值。
  • dst: 输出数组,与 src 同大小、同类型。函数将结果存储在这里。如果为 None,则会自动创建一个与 src 同大小、同类型的数组。

工作原理

  • 对于 src 中的每个元素,cv2.inRange 会检查它是否位于 [lowerb, upperb) 范围内(注意,上限是开区间,即不包括上限值)。如果元素值在这个范围内,则对应的 dst 中的元素被设置为 255(对于 8 位图像),否则被设置为 0。这样,dst 数组最终会是一个二值图像(只有 0 和 255 的值),其中白色(255)表示满足条件的像素,黑色(0)表示不满足条件的像素。

应用场景

  • 颜色过滤:在 HSV 颜色空间中,可以通过 cv2.inRange 来提取特定颜色的物体。例如,提取图像中的红色部分。
  • 阈值处理:在灰度图像中,可以用来提取特定亮度范围内的像素。
  • 背景分割:如果背景的颜色或亮度与前景有明显的区别,可以用这个函数来分离背景和前景。

示例代码

以下是一个简单的示例,展示如何使用 cv2.inRange 来提取图像中的红色部分:

import cv2
import numpy as np# 读取图像
image = cv2.imread('example.jpg')# 转换到 HSV 颜色空间
hsv = cv2.cvtColor(image, cv2.COLOR_BGR2HSV)# 定义红色的 HSV 范围
lower_red = np.array([0, 120, 70])
upper_red = np.array([10, 255, 255])
mask1 = cv2.inRange(hsv, lower_red, upper_red)lower_red = np.array([170, 120, 70])
upper_red = np.array([180, 255, 255])
mask2 = cv2.inRange(hsv, lower_red, upper_red)# 合并两个掩码
mask = mask1 | mask2# 使用掩码提取红色部分
result = cv2.bitwise_and(image, image, mask=mask)# 显示结果
cv2.imshow('Original Image', image)
cv2.imshow('Red Extracted', result)
cv2.waitKey(0)
cv2.destroyAllWindows()

这个示例首先读取一张图像,然后将其转换到 HSV 颜色空间。接着,定义了两个红色的 HSV 范围(因为红色在 HSV 空间中跨越了两个范围),并使用 cv2.inRange 生成两个掩码。之后,合并这两个掩码,并使用掩码提取图像中的红色部分。最后,显示原始图像和提取的红色部分。

6、参考

  • 凸缺陷 convexityDefects
  • 基于OpenCV的手掌检测和手指计数
  • https://github.com/madhav240/hand-detection-and-finger-counting/blob/master/finger_counting_video.py

相关文章:

【python】OpenCV—findContours(4.6)

文章目录 1、功能描述2、代码实现3、效果展示4、完整代码5、涉及到的库函数cv2.inRange 6、参考 1、功能描述 给出一张仅含有手指的图片&#xff0c;判断图片中有多少根手指 2、代码实现 导入库函数&#xff0c;图像预处理 import numpy as np import cv2 as cv img cv.im…...

【C++】——多态

一.多态的概念 1.多态 多态(polymorphism)的概念&#xff1a;通俗的来说&#xff0c;就是多种形态。多态分为静态多态(编译时多态)和动态多态(运行时多态)&#xff0c;而我们讲的多态大部分都是动态多态。 静态多态主要就是我们前面了解过的函数模板和函数重载&#xff0c;它…...

Web前端开发--HTML语言

文章目录 前言1.介绍2.组成3.基本框架4.常见标签4.1双标签4.1.1.标题标签4.2.2段落标签4.1.3文本格式化标签4.1.4超链接标签4.1.5视频标签4.1.6 音频标签 4.2单标签4.2.1换行标签和水平线标签4.2.2 图像标签 5.表单控件结语 前言 生活中处处都有网站&#xff0c;无论你是学习爬…...

AI驱动的网络空间智能对抗;无人集群系统,多体协同算法创新和故障智能预警

目录 AI驱动的网络空间智能对抗 认知与认知域安全 认知攻击-杀伤链 PPDR主动安全框架 短视频内容分析 不良视频鉴别:人工+智能 舆情监测 非介入式监测 大模型对新闻内容审查与播报 无人集群系统,多体协同算法创新和故障智能预警 一、无人集群系统概述 二、多体协…...

推荐一款SSD硬盘优化器:Auslogics SSD Optimizer Pro

SSD Optimizer Pro 是一款专为优化固态硬盘 (SSD) 性能而设计的专业工具&#xff0c;旨在最大化 SSD 的效率&#xff0c;延长硬盘使用寿命。凭借简便的操作界面和强大的优化功能&#xff0c;SSD Optimizer Pro 可以让用户充分利用 SSD 的优势&#xff0c;从而获得更高的系统性能…...

k8s-service、endpoints、pod之间是怎么进行网络互通的

k8s-service、endpoints、pod之间是怎么进行网络互通的 1、service2、endpoints3、service、endpoints、pod通信图4、不通服务pod内部间访问 1、service 在K8S中&#xff0c;Service是一种抽象&#xff0c;定义了一组Pod的逻辑集合和访问这些Pod的策略。首先&#xff0c;我们需…...

Go语言开发商城管理后台-GoFly框架商城插件已发布 需要Go开发商城的朋友可以来看看哦!

温馨提示&#xff1a;我们分享的文章是给需要的人&#xff0c;不需要的人请绕过&#xff0c;文明浏览&#xff0c;误恶语伤人&#xff01; 前言 虽然现在做商城的需求不多&#xff0c;但有很多项目中带有商城功能&#xff0c;如社区医院系统有上服务套餐、理疗产品需求、宠物…...

【51单片机】UART串口通信原理 + 使用

学习使用的开发板&#xff1a;STC89C52RC/LE52RC 编程软件&#xff1a;Keil5 烧录软件&#xff1a;stc-isp 开发板实图&#xff1a; 文章目录 串口硬件电路UART串口相关寄存器 编码单片机通过串口发送数据电脑通过串口发送数据控制LED灯 串口 串口是一种应用十分广泛的通讯接…...

高性能分布式缓存Redis-高可用部署

一、主从架构搭建 为什么要进行主从架构搭建&#xff0c;一台redis不行吗&#xff1f; ①、持久化后的数据只在一台机器上&#xff0c;因此当硬件发生故障时&#xff0c;比如主板或CPU坏了&#xff0c;这时候无法重启服务器&#xff0c;有什么办法可以保证服务器发生故障时数…...

如何使用XSL-FO生成PDF格式的电子发票的技术博文示例

目录 使用 XSL-FO 生成电子发票 PDF&#xff1a;从布局设计到优化为什么选择 XSL-FO&#xff1f;1. 初始设置2. 标题区块3. 买卖方信息4. 商品明细表格5. 合计信息6. 优化代码结构与布局7. 生成 PDF 文件8. 示例总结 使用 XSL-FO 生成电子发票 PDF&#xff1a;从布局设计到优化…...

TDengine 签约山东港,赋能港口数字化转型

随着全球港口物流数字化进程的加速&#xff0c;港口运营面临日益复杂的数据管理挑战&#xff0c;从能源管理、设备监控到运营安全保障&#xff0c;各类数据需要及时存储并高效分析。山东港在信息化建设过程中&#xff0c;数字化综合管理平台的性能和查询功能一度受到瓶颈制约。…...

基于YOLO11/v10/v8/v5深度学习的煤矿传送带异物检测系统设计与实现【python源码+Pyqt5界面+数据集+训练代码】

《------往期经典推荐------》 一、AI应用软件开发实战专栏【链接】 项目名称项目名称1.【人脸识别与管理系统开发】2.【车牌识别与自动收费管理系统开发】3.【手势识别系统开发】4.【人脸面部活体检测系统开发】5.【图片风格快速迁移软件开发】6.【人脸表表情识别系统】7.【…...

mysql-workbench 导入csv格式数据报错:Unhandled exception: Could not determine delimiter

xlsx文件中第二行某个单元格有换行符&#xff0c;csv文件中用双引号包起来了&#xff0c;但是python 在采样的时候&#xff0c;只读了前两行&#xff0c;readline可不认识csv的规则。csv文件可以识别双引号包起来的换行符是单元格内部的换行&#xff0c;python的readline识别不…...

使用Python简单实现客户端界面

服务端实现 import threading import timeimport wx from socket import socket, AF_INET, SOCK_STREAMclass LServer(wx.Frame):def __init__(self):wx.Frame.__init__(self, None, id1002, titleL服务器端界面, poswx.DefaultPosition, size(400, 450))# 窗口中添加面板pl …...

15分钟学 Go 第 43 天:前端与Go的结合

第43天&#xff1a;前端与Go的结合 目标&#xff1a;了解Go如何与前端交互&#xff0c;前端使用Vue.js 在现代Web开发中&#xff0c;Go语言常用于后端开发&#xff0c;而Vue.js是一个流行的前端框架&#xff0c;用于构建用户界面。结合二者&#xff0c;可以构建高效、可维护的…...

解决SRS推送webrtc流卡顿问题

目录 1.问题描述2.原因分析3.ffmpeg去掉B帧的方法3.1 命令行推流3.2 ffmpeg源码推流 1.问题描述 使用ffmpeg通过rtmp协议推流给SRS&#xff0c;然后浏览器通过webrtc拉取播放流&#xff0c;经多次测试发现webrtc播放流总是卡顿&#xff0c;而拉取rtmp流是正常的。 2.原因分析…...

GDPU Andriod移动应用 Broadcast Receiver

聆听广播&#xff0c;跟着节拍吧。 计时器 新建一个名为PhoneStateMonitor的工程&#xff1b; 实现一个应用运行时长的计时器&#xff0c;并在界面上刷新计数器&#xff0c;要求包括&#xff1a; &#xff08;1&#xff09;在Layout中包含两个TextView控件&#xff0c;横向分…...

CSP/信奥赛C++刷题训练:经典例题 - 栈(1):洛谷P3056 :[USACO12NOV] Clumsy Cows S

CSP/信奥赛C刷题训练&#xff1a;经典例题 - 栈&#xff08;1&#xff09;&#xff1a;洛谷P3056 &#xff1a;[USACO12NOV] Clumsy Cows S 题目描述 Bessie the cow is trying to type a balanced string of parentheses into her new laptop, but she is sufficiently clums…...

WiFi一直获取不到IP地址是怎么回事?

在当今这个信息化时代&#xff0c;WiFi已成为我们日常生活中不可或缺的一部分。无论是家庭、办公室还是公共场所&#xff0c;WiFi都为我们提供了便捷的无线互联网接入。然而&#xff0c;有时我们可能会遇到WiFi连接后无法获取IP地址的问题&#xff0c;这不仅影响了我们的网络使…...

蓝牙BLE开发——iOS 每次写入数据超过200字节报错?

iOS 写入数据超过200字节报错 文章目录 iOS 写入数据超过200字节报错官方建议&#xff1a;报错问题解决 writeblecharacteristicvalue 官方建议&#xff1a; 并行调用多次会存在写失败的可能性。APP不会对写入数据包大小做限制&#xff0c;但系统与蓝牙设备会限制蓝牙4.0单次…...

UE5 学习系列(二)用户操作界面及介绍

这篇博客是 UE5 学习系列博客的第二篇&#xff0c;在第一篇的基础上展开这篇内容。博客参考的 B 站视频资料和第一篇的链接如下&#xff1a; 【Note】&#xff1a;如果你已经完成安装等操作&#xff0c;可以只执行第一篇博客中 2. 新建一个空白游戏项目 章节操作&#xff0c;重…...

在rocky linux 9.5上在线安装 docker

前面是指南&#xff0c;后面是日志 sudo dnf config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo sudo dnf install docker-ce docker-ce-cli containerd.io -y docker version sudo systemctl start docker sudo systemctl status docker …...

AtCoder 第409​场初级竞赛 A~E题解

A Conflict 【题目链接】 原题链接&#xff1a;A - Conflict 【考点】 枚举 【题目大意】 找到是否有两人都想要的物品。 【解析】 遍历两端字符串&#xff0c;只有在同时为 o 时输出 Yes 并结束程序&#xff0c;否则输出 No。 【难度】 GESP三级 【代码参考】 #i…...

[ICLR 2022]How Much Can CLIP Benefit Vision-and-Language Tasks?

论文网址&#xff1a;pdf 英文是纯手打的&#xff01;论文原文的summarizing and paraphrasing。可能会出现难以避免的拼写错误和语法错误&#xff0c;若有发现欢迎评论指正&#xff01;文章偏向于笔记&#xff0c;谨慎食用 目录 1. 心得 2. 论文逐段精读 2.1. Abstract 2…...

使用van-uploader 的UI组件,结合vue2如何实现图片上传组件的封装

以下是基于 vant-ui&#xff08;适配 Vue2 版本 &#xff09;实现截图中照片上传预览、删除功能&#xff0c;并封装成可复用组件的完整代码&#xff0c;包含样式和逻辑实现&#xff0c;可直接在 Vue2 项目中使用&#xff1a; 1. 封装的图片上传组件 ImageUploader.vue <te…...

HBuilderX安装(uni-app和小程序开发)

下载HBuilderX 访问官方网站&#xff1a;https://www.dcloud.io/hbuilderx.html 根据您的操作系统选择合适版本&#xff1a; Windows版&#xff08;推荐下载标准版&#xff09; Windows系统安装步骤 运行安装程序&#xff1a; 双击下载的.exe安装文件 如果出现安全提示&…...

【Java_EE】Spring MVC

目录 Spring Web MVC ​编辑注解 RestController RequestMapping RequestParam RequestParam RequestBody PathVariable RequestPart 参数传递 注意事项 ​编辑参数重命名 RequestParam ​编辑​编辑传递集合 RequestParam 传递JSON数据 ​编辑RequestBody ​…...

关键领域软件测试的突围之路:如何破解安全与效率的平衡难题

在数字化浪潮席卷全球的今天&#xff0c;软件系统已成为国家关键领域的核心战斗力。不同于普通商业软件&#xff0c;这些承载着国家安全使命的软件系统面临着前所未有的质量挑战——如何在确保绝对安全的前提下&#xff0c;实现高效测试与快速迭代&#xff1f;这一命题正考验着…...

GO协程(Goroutine)问题总结

在使用Go语言来编写代码时&#xff0c;遇到的一些问题总结一下 [参考文档]&#xff1a;https://www.topgoer.com/%E5%B9%B6%E5%8F%91%E7%BC%96%E7%A8%8B/goroutine.html 1. main()函数默认的Goroutine 场景再现&#xff1a; 今天在看到这个教程的时候&#xff0c;在自己的电…...

Monorepo架构: Nx Cloud 扩展能力与缓存加速

借助 Nx Cloud 实现项目协同与加速构建 1 &#xff09; 缓存工作原理分析 在了解了本地缓存和远程缓存之后&#xff0c;我们来探究缓存是如何工作的。以计算文件的哈希串为例&#xff0c;若后续运行任务时文件哈希串未变&#xff0c;系统会直接使用对应的输出和制品文件。 2 …...