计算机视觉入门(基础篇:利用mediapipe进行手部识别)

本代码基于 Advance Computer Vision with Python 进行修改,更加适合中国宝宝体质

我的相关代码及数据集已经上传GitHub仓库,欢迎使用 Advance-Computer-Vision-with-Python

代码1:Basics.py

import cv2
import mediapipe as mp
import time

# 打开摄像头
cap = cv2.VideoCapture(0)  # 0是默认摄像头

# 初始化手部检测模块
mpHands = mp.solutions.hands  # 引用 MediaPipe 的手部解决方案模块
hands = mpHands.Hands()  # 创建一个 Hands 对象,用于检测和跟踪手部关键点
mpDraw = mp.solutions.drawing_utils  # 引用绘图工具,用于在图像上绘制检测到的手部关键点和连接线

# 初始化时间变量用于计算帧率
pTime = 0  # 表示前一帧的时间,previous time
cTime = 0  # 表示当前帧的时间,current time
# cTime - pTime 计算时间差,从而计算帧率。最后将 cTime 赋值给 pTime,以便在下一次循环时使用

while True:
    # 读取摄像头图像
    success, img = cap.read()
    # success:一个布尔值,表示是否成功读取帧
    # img:读取的图像帧,如果读取失败,这个值可能为空

    # 水平翻转图像
    img = cv2.flip(img, 1)
    # 第 0 维表示垂直方向(高度),对应图像的行数,上下
    # 第 1 维表示水平方向(宽度),对应图像的列数,左右

    # 将图像从 BGR 格式转换为 RGB 格式
    imgRGB = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
    # BGR 是图像在 OpenCV 中的默认颜色格式,代表蓝色(Blue)、绿色(Green)、红色(Red)。这种格式与通常使用的 RGB(红、绿、蓝)顺序相反。转换为 RGB 是因为许多图像处理库(如 MediaPipe)使用这种格式进行处理

    # 处理图像以检测手部
    results = hands.process(imgRGB)

    # 如果检测到手部
    if results.multi_hand_landmarks:
        # 遍历检测到的每只手
        for handLms in results.multi_hand_landmarks:
            # results.multi_hand_landmarks 会返回一个列表,其中包含检测到的每只手的关键点信息。如果检测到多只手,它会包含多个元素,每个元素代表一只手的所有关键点

            # 遍历手部关键点
            for id, lm in enumerate(handLms.landmark):  
                # enumerate 返回一个迭代器,每次迭代返回一个包含索引和值的元组
                # id 是手部关键点的索引,lm 是 landmark 的缩写,表示手部关键点的坐标信息

                # 获取图像的尺寸
                h, w, c = (img.shape) 
                # img.shape 返回一个包含图像维度的元组,具体包括:高度(行数)、宽度(列数)、通道数(如 RGB 图像的通道数为 3)

                # 计算关键点在图像中的坐标
                cx, cy = int(lm.x * w), int(lm.y * h)
                # lm.x 和 lm.y 是关键点的归一化坐标,范围在 0 到 1 之间。通过乘以图像的宽度和高度,可以将它们转换为图像中的像素坐标

                print(id, cx, cy)

                # 在关键点处画一个圆圈
                cv2.circle(img, (cx, cy), 15, (255, 0, 255), -1)
                # img 表示要绘制图像的地方,(cx, cy) 圆心的坐标,15 圆的半径
                # (255, 0, 255) 圆的颜色(BGR格式),这里是紫色
                # 红色:(0, 0, 255)、绿色:(0, 255, 0)、蓝色:(255, 0, 0)、黄色:(0, 255, 255)、青色:(255, 255, 0)、品红:(255, 0, 255)、白色:(255, 255, 255)、黑色:(0, 0, 0)
                # cv2.FILLED 或 -1 填充圆的实心样式,也可以为具体的数字(值为边框厚度)

            # 绘制手部关键点和连接线
            mpDraw.draw_landmarks(img, handLms, mpHands.HAND_CONNECTIONS)
            # img 要绘制的图像,handLms 手部关键点的坐标
            # mpHands.HAND_CONNECTIONS 定义手部关键点之间的连接关系,用于绘制骨架结构

    # 计算帧率
    cTime = time.time()
    fps = 1 / (cTime - pTime)
    pTime = cTime

    # 在图像上显示帧率
    cv2.putText(img, str(int(fps)), (10, 70), cv2.FONT_HERSHEY_PLAIN, 3, (255, 0, 255), 3)
    # img 要绘制文本的地方,str(int(fps)) 要显示的文本内容,这里是帧率的整数部分
    # (10, 70) 文本的左下角坐标,cv2.FONT_HERSHEY_PLAIN 字体样式
    # 3 字体大小,(255, 0, 255) 文本颜色(紫色,BGR格式),3 文本的粗细
    # cv2.putText 不支持关键字传参,必须按照顺序提供参数

    # 显示图像
    cv2.imshow("Image", img)  # 在窗口中显示图像,窗口标题为“Image”
    cv2.waitKey(1) # 等待键盘事件,参数为 1 表示等待 1 毫秒
    # 它也允许图像窗口响应用户输入(如关闭窗口)

    # 检测退出键
    if cv2.waitKey(1) & 0xFF == ord("q"):  # ord('q') 获取字符 'q' 的 ASCII 值
        # cv2.waitKey(1) & 0xFF 用来读取键盘输入
        # cv2.waitKey(1) 返回的是一个 32 位整数,其中低 8 位是实际的键值,& 0xFF 是一个位运算,用于提取这 8 位
        # "低 8 位"指的是一个数值的二进制表示中最右边的 8 位。这些位表示数值的较小部分,与"高 8 位"(最左边的 8 位)相对,后者表示数值的较大部分。对于 32 位整数来说,低 8 位用于表示键盘输入的实际键值
        break

cap.release()  # 释放摄像头资源
cv2.destroyAllWindows()  # 关闭所有 OpenCV 窗口

代码2:HandTrackingModule.py

import cv2
import mediapipe as mp
import time


# 定义手部检测类
class handDetector:
    def __init__(self, mode=False, maxHands=2, detectionCon=0.5, trackCon=0.5):
        # 初始化参数
        self.mode = mode  # 静态图像模式
        self.maxHands = maxHands  # 最大检测手数
        self.detectionCon = detectionCon  # 检测置信度
        self.trackCon = trackCon  # 跟踪置信度
        self.mpHands = mp.solutions.hands  # Mediapipe手部解决方案
        self.hands = self.mpHands.Hands(
            static_image_mode=self.mode,
            max_num_hands=self.maxHands,
            min_detection_confidence=self.detectionCon,
            min_tracking_confidence=self.trackCon,
        )
        self.mpDraw = mp.solutions.drawing_utils  # 用于绘制手部连接

    def findHands(self, img, draw=True):
        # 将图像从BGR转换为RGB
        imgRGB = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
        # 处理图像,检测手部
        self.results = self.hands.process(imgRGB)
        # 如果检测到手部
        if self.results.multi_hand_landmarks:
            # 遍历每个手部
            for handLms in self.results.multi_hand_landmarks:
                if draw:
                    # 绘制手部连接
                    self.mpDraw.draw_landmarks(
                        img, handLms, self.mpHands.HAND_CONNECTIONS
                    )
        return img

    def findPosition(self, img, handNo=0, draw=True):
        # 初始化列表存储手部位置
        lmList = []
        # 如果检测到手部
        if self.results.multi_hand_landmarks:
            # self.results 是在 findHands 方法中定义的。由于 findHands 方法在 findPosition 方法之前被调用,因此 self.results 会被正确地初始化并存储检测结果
            # 这种设计依赖于调用顺序,我们得确保在调用 findPosition 之前已经调用过 findHands,否则 self.results 可能没有数据,导致 findPosition 无法正常工作
            # 在 Python 中,self 参数用于引用类的实例。只要在类的方法中通过 self 定义了属性(例如 self.results),该属性就可以在同一个类的其他方法中访问和使用。这样可以在不同的方法之间共享数据

            # 获取指定手部
            myHand = self.results.multi_hand_landmarks[handNo]
            # 遍历每个关键点
            for id, lm in enumerate(myHand.landmark):
                # 获取图像尺寸
                h, w, c = img.shape
                # 计算关键点在图像中的位置
                cx, cy = int(lm.x * w), int(lm.y * h)
                lmList.append([id, cx, cy])
                if draw:
                    # 在图像上绘制关键点
                    cv2.circle(img, (cx, cy), 1, (255, 0, 255), -1)
                    # 由于现有的 findHands,再有的 handDetector,所以“关键点”的图层在“手部连接”之上,前一个程序则相反

        return lmList


# 主函数
def main():
    pTime = 0  # 前一帧时间
    cTime = 0  # 当前时间
    cap = cv2.VideoCapture(0)  # 打开摄像头
    detector = handDetector()  # 创建手部检测器
    while True:
        success, img = cap.read()  # 读取摄像头图像
        img = cv2.flip(img, 1)  # 水平翻转图像
        img = detector.findHands(img)  # 检测手部
        lmList = detector.findPosition(img)  # 获取手部关键点位置
        if len(lmList) != 0:
            print(lmList[4])  # 打印大拇指指尖位置
        cTime = time.time()
        fps = 1 / (cTime - pTime)  # 计算帧率
        pTime = cTime
        # 在图像上显示帧率
        cv2.putText(
            img, str(int(fps)), (10, 70), cv2.FONT_HERSHEY_PLAIN, 3, (255, 0, 255), 3
        )
        cv2.imshow("Image", img)  # 显示图像
        if cv2.waitKey(1) & 0xFF == ord("q"):
            break  # 按下'q'退出


if __name__ == "__main__":
    main()

mp.solutions.hands

mpHands = mp.solutions.hands 引用 MediaPipe 的手部解决方案模块

hands = mpHands.Hands() 创建一个 Hands 对象,用于检测和跟踪手部关键点

results = hands.process(imgRGB) 处理图像以检测手部

for handLms in results.multi_hand_landmarks results.multi_hand_landmarks会返回一个列表,其中包含检测到的每只手的关键点信息

for id, lm in enumerate(handLms.landmark) id 是手部关键点的索引,lm 是 landmark 的缩写,表示手部关键点的坐标信息

lm.x 和 lm.y 是关键点的归一化坐标,范围在 0 到 1 之间。通过乘以图像的宽度和高度,可以将它们转换为图像中的像素坐标

mpHands.Hands() 对象的参数:

static_image_mode: 是否将每帧作为静态图像处理
max_num_hands: 最大检测手数
min_detection_confidence: 检测置信度阈值
min_tracking_confidence: 跟踪置信度阈值

mp.solutions.drawing_utils

mpDraw = mp.solutions.drawing_utils 引用绘图工具,用于在图像上绘制检测到的手部关键点和连接线

mpDraw.draw_landmarks(img, handLms, mpHands.HAND_CONNECTIONS) 绘制手部关键点和连接线,img 要绘制的图像,handLms 手部关键点的坐标,mpHands.HAND_CONNECTIONS 定义手部关键点之间的连接关系,用于绘制骨架结构

pycache文件夹

当导入一个 .py 文件时,Python 会编译它,并将编译后的字节码存储在 pycache 目录中。这有助于提高程序的执行效率,因为下次运行时可以直接使用编译后的字节码,而不必重新编译源代码

“Cache” 是指缓存,一种用于临时存储数据的机制,以便更快速地访问。缓存可以减少数据的重复计算或从慢速存储设备读取的次数,从而提高程序性能

对于HandTrackingModule.py而言,当你在其他文件中导入这个模块时,只有类 handDetector 和其中的方法会被使用,而 main() 函数不会,main() 函数只有在直接运行该脚本时才会执行

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值