使用OpenCV和mediapipe实现手部信息抓取

目录

运行效果

挨行解读

(1)初始化MediaPipe Hand模块

(2)打开摄像头

(3)初始化计时器

(4)开始程序主题部分

(5)读取视频帧

(6)将图像转换为RGB格式(MediaPipe要求输入为RGB格式)

(7)使用MediaPipe Hands模块处理图像

(8)绘制手部关键点及骨骼线

(9)计算FPS

(10)显示处理后的图像

(11)检测按键,按下ESC键退出循环

(12)最后来释放资源退出程序

总体代码

总结


运行效果

由于用两只手我就没法按键盘截图了,就放上一只手的效果吧~~~

挨行解读

(1)初始化MediaPipe Hand模块

mp_hands = mp.solutions.hands

        这一行代码是导入了 mediapipe 库中的 hands 模块,然后通过 mp_hands 变量来引用这个模块。

        说的详细一点的话就是 mediapipe.solutions.hands 模块包含了 Hands 类,该类提供了对手部关键点检测的功能。通过使用这个类,咱们就可以轻松地在图像或视频流中检测手的位置、手指的姿势等信息。

        这样我们就可以在代码中使用 mp_hands 来引用 mediapipe.solutions.hands 模块,使代码更easy!!!

hands = mp_hands.Hands()

        这一行代码创建了一个 mediapipe 库中 Hands 类的实例,这样我们就可以使用 mediapipe 提供的手部关键点检测功能。

        说的详细一点就是 mp_hands.Hands() 的调用初始化了一个手部关键点检测器,可以在输入的图像或视频流中检测手的位置、手指的姿势等信息。这个实例化的对象(hands 变量)具有方法 process(),可以用来对图像进行处理并获得手部关键点的检测结果。

        

mp_drawing = mp.solutions.drawing_utils

        这一行代码导入了 mediapipe 库中的 drawing_utils 模块,并使用 mp_drawing 变量引用了这个模块。

       mediapipe.solutions.drawing_utils 模块包含了一些辅助的函数,可以在图像上绘制关键点、连接线等。这样就可以绘制出手部的效果。

      

(2)打开摄像头

cap = cv2.VideoCapture(0)

        这一行代码使用 OpenCV 中的 cv2.VideoCapture 类创建了一个视频捕捉对象 cap,并打开了系统默认的摄像头(通常是编号为 0 的摄像头)。

   cv2.VideoCapture(0) 传递了参数 0,意思是要打开系统中的第一个摄像头。如果我们有多个摄像头的话,可以通过更改参数来选择不同的摄像头,例如 cv2.VideoCapture(1) 表示打开第二个摄像头。

(3)初始化计时器

fps_time = time.time()

  fps_time = time.time() 我们通过这一行代码初始化一个变量 fps_time,其目的是记录程序开始运行时的时间戳。

(4)开始程序主题部分

while cap.isOpened():

  while cap.isOpened(): 表示在视频捕捉对象 cap 处于打开状态时,循环将一直执行。循环内的代码将不断读取视频帧并进行处理,直到视频捕捉对象被释放或关闭。

       cap.isOpened() 返回一个布尔值,指示视频捕捉对象是否成功打开。如果返回 True,表示对象成功打开,循环会继续执行;如果返回 False,表示对象未成功打开或已经关闭,循环将退出。

        我们这个循环结构是一个非常常见的视频处理模式,这样就可以持续地从摄像头中读取帧并进行处理,直到程序结束。

(5)读取视频帧

    # 读取视频流中的一帧ret, frame = cap.read()if not ret:break    # 读取视频流中的一帧
  1. ret, frame = cap.read(): 这一行代码使用 cap.read() 方法来读取视频的一帧。cap.read() 返回两个值,第一个是布尔值 ret,表示读取是否成功;第二个是图像帧 frame,是一个表示图像数据的 NumPy 数组。

  2. if not ret: break: 这一行检查 ret 的值,如果 retFalse,表示读取失败(可能是因为视频结束或发生了错误),那么我们就跳出循环,结束程序。

(6)将图像转换为RGB格式(MediaPipe要求输入为RGB格式)

frame_rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)

(7)使用MediaPipe Hands模块处理图像

results = hands.process(frame_rgb)

        这一行代码使用了 mediapipe 库中 Hands 类的 process 方法来处理图像帧,即 hands.process(frame_rgb)。我来挨个解释一下:

  1. hands 是我们之前通过 mp_hands.Hands() 创建的 ,它提供了手部关键点检测的功能。

  2. frame_rgb 是一个图像帧的 NumPy 数组,表示图像数据。注意了mediapipe 库要求输入图像为 RGB 格式。

  3. hands.process()Hands 类中的一个方法,用于处理输入的图像帧。它会对图像进行手部关键点检测,并返回一个包含检测结果的对象。

  4. results 是接收检测结果的变量。通过调用 hands.process(frame_rgb),检测结果将被存储在 results 中。这个结果包括了检测到的手的位置、手指关键点的坐标等信息。

        因此,这一行代码的目的是使用 mediapipe 库提供的手部关键点检测功能,处理输入的图像帧,并将检测结果存储在 results 变量中,然后我们后续的代码就可以通过results来实现可视化。

(8)绘制手部关键点及骨骼线

    if results.multi_hand_landmarks:for hand_landmarks in results.multi_hand_landmarks:mp_drawing.draw_landmarks(frame, hand_landmarks, mp_hands.HAND_CONNECTIONS)# 获取手指关键点finger_points = [hand_landmarks.landmark[i] for i in [4, 8, 12, 16, 20]]# 绘制手指骨骼线for i in range(4):start_point = (int(finger_points[i].x * frame.shape[1]), int(finger_points[i].y * frame.shape[0]))end_point = (int(finger_points[i + 1].x * frame.shape[1]), int(finger_points[i + 1].y * frame.shape[0]))cv2.line(frame, start_point, end_point, (255, 0, 255), 2)

        这段代码的主要作用是处理手部关键点检测的结果,将检测到的手部关键点和手指骨骼线绘制在图像上。挨个来看:

  1. if results.multi_hand_landmarks:这个条件语句检查是否检测到了不只一只手。results.multi_hand_landmarks 是一个列表,每个元素代表一个检测到的手部的关键点坐标。

  2. for hand_landmarks in results.multi_hand_landmarks: 这个循环遍历每个检测到的手部,其中 hand_landmarks 包含了该手部的关键点坐标。

  3. mp_drawing.draw_landmarks(frame, hand_landmarks, mp_hands.HAND_CONNECTIONS): 这一行代码使用 drawing_utils 模块的 draw_landmarks 函数在图像上绘制手部关键点和手指骨骼线。frame 是要绘制的图像,hand_landmarks 是该手部的关键点坐标,mp_hands.HAND_CONNECTIONS 表示绘制手部关键点之间的连接线。

  4. finger_points = [hand_landmarks.landmark[i] for i in [4, 8, 12, 16, 20]]: 这一行代码获取手部关键点中特定手指的坐标,我们这里选择的是食指。landmark 对象包含了手部关键点的三维坐标(x、y、z)。

  5. for i in range(4): ...: 这个循环遍历食指的关键点,然后再绘制手指骨骼线。在每次迭代中,获取相邻两个关键点的坐标,然后使用 cv2.line() 绘制一条连接这两个点的线段。

        这样,我们就实现了可视化手部的姿势。

(9)计算FPS

 # 计算帧率current_time = time.time()fps = 1 / (current_time - fps_time)fps_time = current_time# 显示帧率cv2.putText(frame, f'FPS: {int(fps)}', (10, 30), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 255, 0), 2)

        挨个解释一下

  1. current_time = time.time(): 这一行获取当前时间戳,表示当前时刻的时间。

  2. fps = 1 / (current_time - fps_time): 这一行计算帧率。帧率是每秒处理的图像帧数。计算公式是总帧数除以总时间,这里使用时间差来估算每秒的帧数。

  3. fps_time = current_time: 更新 fps_time,将其设置为当前时间戳,以便在下一帧时可以继续计算帧率。

  4. cv2.putText(...): 这一行使用 OpenCV 的 putText 函数在图像上绘制帧率信息。具体参数为:

    • frame: 要绘制文本的图像。
    • f'FPS: {int(fps)}': 要显示的文本,包括字符串 "FPS: " 和计算得到的帧率值。
    • (10, 30): 文本的位置,即左上角的坐标。
    • cv2.FONT_HERSHEY_SIMPLEX: 字体类型。
    • 1: 字体大小。
    • (0, 255, 0): 文本颜色,这里是绿色,RGB嘛g就是绿色哪个单词的首字母。
    • 2: 文本的线宽。

        这样就可以实时显示FPS了,就很爽~~~^_^

(10)显示处理后的图像

    cv2.imshow('Hand grasping', frame)

使用 OpenCV 的 imshow 函数来在窗口中显示图像。

  1. 'Hand grasping': 这是窗口的标题,即显示图像窗口的名称。

  2. frame: 这是咱要在窗口中显示的图像。frame 是通过摄像头捕捉到的一帧图像,或者经过处理后的图像。

  3. cv2.imshow('Hand grasping', frame): 这一行代码将图像 frame 显示在窗口中,窗口标题为 'Hand grasping'。imshow 函数用于在窗口中显示图像。

(11)检测按键,按下ESC键退出循环

    if cv2.waitKey(1) & 0xFF == 27:break

    

  1. cv2.waitKey(1): 这一行代码等待键盘输入,参数 1 表示等待 1 毫秒。函数返回按键的 ASCII 值,如果没有按键输入,返回 -1

  2. & 0xFF == 27: 这个条件语句检查按键的 ASCII 值是否等于 27。ASCII 值为 27 的键是 ESC 键(Escape 键)。

  3. if ...: break: 如果用户按下 ESC 键,即条件为真,那么程序会执行 break 语句,跳出循环,从而结束程序的执行。

        意思就是按下ESC就退出

(12)最后来释放资源退出程序

  1. cap.release(): 这一行代码释放了通过 cv2.VideoCapture(0) 打开的视频捕获对象的资源。

  2. cv2.destroyAllWindows(): 这一行代码关闭所有通过 OpenCV 创建的窗口。如果关闭时出现问题,可能会在程序结束后窗口继续存在。那么我们通过调用 destroyAllWindows()来确保在程序退出时所有图形窗口都肯定会被关闭。


总体代码

import cv2
import mediapipe as mp
import time# 初始化MediaPipe Hand模块
mp_hands = mp.solutions.hands
hands = mp_hands.Hands()
mp_drawing = mp.solutions.drawing_utils# 打开摄像头
cap = cv2.VideoCapture(0)# 初始化计时器
fps_time = time.time()while cap.isOpened():# 读取视频流中的一帧ret, frame = cap.read()if not ret:break# 将图像转换为RGB格式(MediaPipe要求输入为RGB格式)frame_rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)# 使用MediaPipe Hands模块处理图像results = hands.process(frame_rgb)# 绘制手部关键点及骨骼线if results.multi_hand_landmarks:for hand_landmarks in results.multi_hand_landmarks:mp_drawing.draw_landmarks(frame, hand_landmarks, mp_hands.HAND_CONNECTIONS)# 获取手指关键点finger_points = [hand_landmarks.landmark[i] for i in [4, 8, 12, 16, 20]]# 绘制手指骨骼线for i in range(4):start_point = (int(finger_points[i].x * frame.shape[1]), int(finger_points[i].y * frame.shape[0]))end_point = (int(finger_points[i + 1].x * frame.shape[1]), int(finger_points[i + 1].y * frame.shape[0]))cv2.line(frame, start_point, end_point, (255, 0, 255), 2)# 计算帧率current_time = time.time()fps = 1 / (current_time - fps_time)fps_time = current_time# 显示帧率cv2.putText(frame, f'FPS: {int(fps)}', (10, 30), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 255, 0), 2)# 显示处理后的图像cv2.imshow('Hand grasping', frame)# 检测按键,按下ESC键退出循环if cv2.waitKey(1) & 0xFF == 27:break# 释放资源
cap.release()
cv2.destroyAllWindows()

总结

以后再总结~!~~~~ヾ( ̄▽ ̄)Bye~Bye~

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

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

相关文章

美团分布式 ID 框架 Leaf 介绍和使用

一、Leaf 在当今日益数字化的世界里,软件系统的开发已经成为了几乎所有行业的核心。然而,随着应用程序的规模不断扩大,以及对性能和可扩展性的需求不断增加,传统的软件架构和设计模式也在不断地面临挑战。其中一个主要挑战就是如…

LeetCode---386周赛

题目列表 3046. 分割数组 3047. 求交集区域内的最大正方形面积 3048. 标记所有下标的最早秒数 I 3049. 标记所有下标的最早秒数 II 一、分割数组 这题简单的思维题,要想将数组分为两个数组,且分出的两个数组中数字不会重复,很显然一个数…

2024第二次培训:win11系统下使用nginx、JDK、mysql搭建基于vue2、java前后端分离的web应用运行环境

一.背景 公司安排了带徒弟的任务,给培训写点材料。前面分开介绍了mysql、jdk、nginx的安装,都只是零星的介绍,只能算零散的学习。学习了有什么用呢?能解决什么问题?能完成什么工作? 今天我们要用之前的几篇…

高中数学:分式函数值域的求法

一、求值域的两种基本思路 1、根据函数图像和定义域求出值域。 难点:画出函数图像 2、研究函数单调性和定义域求出值域。 本篇主要讲一下,画图法求值域 二、函数图像画法 高中所学的分式函数,基本由反比例函数平移得到。 复杂分式函数图…

web漏洞与规避

文章目录 一、XSS 跨站脚本攻击1.1 XSS攻击的主要类型反射型XSS存储型XSSDOM型XSS 1.2 前端开发如何应对XSS 二、CSRF 跨站请求伪造2.1 CSRF例子2.2 前端开发如何应对CSRF 三、SQL 注入3.1 前端如何防御SQL注入 四、前端如何使用CSP 一、XSS 跨站脚本攻击 攻击者通过在受害者的…

Day12:信息打点-Web应用源码泄漏开源闭源指纹识别GITSVNDS备份

目录 开源-CMS指纹识别源码获取方式 闭源-习惯&配置&特性等获取方式 闭源-托管资产平台资源搜索监控 思维导图 章节点 Web:语言/CMS/中间件/数据库/系统/WAF等 系统:操作系统/端口服务/网络环境/防火墙等 应用:APP对象/API接口/微…

【Python】进阶学习:pandas--如何根据指定条件筛选数据

【Python】进阶学习:pandas–如何根据指定条件筛选数据 🌈 个人主页:高斯小哥 🔥 高质量专栏:Matplotlib之旅:零基础精通数据可视化、Python基础【高质量合集】、PyTorch零基础入门教程👈 希望…

synchrosized 的可重入特性、死锁、哲学家就餐问题以及解决死锁的方法等干货

文章目录 💐synchrosized的可重入特性关于死锁:哲学家就餐问题💡如何避免/解决死锁 💐synchrosized的可重入特性 可重入特性:当一个线程针对一个对象同时加锁多次,不会构成死锁,这样的特性称为…

NoSQL--虚拟机网络配置

目录 1.初识NoSQL 1.1 NoSQL之虚拟机网络配置 1.1.1 首先,导入预先配置好的NoSQL版本到VMware Workstation中 1.1.2 开启虚拟机操作: 1.1.2.1 点击开启虚拟机: 1.1.2.2 默认选择回车CentOS Linux(3.10.0-1127.e17.x86_64) 7 …

今日Arxiv最热NLP大模型论文:Llama-2上下文扩大48倍的方法来了,港大发布,无需训练

引言:大语言模型的长上下文理解能力 在当今的人工智能领域,大语言模型(Large Language Models,简称LLMs)的长上下文理解能力成为了一个重要的研究方向。这种能力对于模型来说至关重要,因为它使得LLMs能够有…

精品SSM的教学管理系统课程作业成绩

《[含文档PPT源码等]精品基于SSM的教学管理系统[包运行成功]》该项目含有源码、文档、PPT、配套开发软件、软件安装教程、项目发布教程、包运行成功! 软件开发环境及开发工具: Java——涉及技术: 前端使用技术:HTML5,CSS3、Jav…

<网络安全>《63 微课堂<第3课 旁路部署和串行部署是什么?>》

1、串联和并联概念 串联和并联是物理学上的概念。 串联电路把元件逐个顺次连接起来组成的电路。如图,特点是:流过一个元件的电流同时也流过另一个。 并联电路把元件并列地连接起来组成的电路,如图,特点是:干路的电流…