超详细的OpenCV手势识别数字教程

本篇是在上上篇(python 手势识别)的基础上多加了一部分函数,来显示识别手势表示的数字,并将识别结果显示在屏幕上

大概思路是:利用OpenCV(cv2)和mediapipe包,对图像做出一系列处理,比如图像的翻转、图像中手势的识别、手的21个结点的显示和坐标计算,最后通过对每个手指的指尖位置和第二个关节的x坐标(大拇指)或y坐标(其他四个手指)的大小判断来识别该手指是张开还是闭合,最后在代码中放入六张图片来显示0、1、2、3、4、5

代码运行成功后大概是这样的:(以数字0为例)

hand.py

import cv2
# 从handms文件中导入HandDetector类
from handms import HandDetector


# 打开摄像头
cap = cv2.VideoCapture(0)
# 创建一个手势识别对象
detector = HandDetector()
# 6张手的图片,分别代表0~5
finger_img_list = [
    'D:/project1/0.jpg',
    'D:/project1/1.jpg',
    'D:/project1/2.jpg',
    'D:/project1/3.jpg',
    'D:/project1/4.jpg',
    'D:/project1/5.jpg',
]
# 创建一个列表来存放表示0~5的图片
finger_list = []
# 遍历finger_img_list列表
for gesture in finger_img_list:
    # 用cv2.imread()函数把图片依次取出来
    i = cv2.imread(gesture)
    # 把取出来的图片存到finger_list列表中
    finger_list.append(i)
# 指尖地表值,分别代表大拇指、食指、中指、无名指和小指的指尖
tip_ids = [4, 8, 12, 16, 20]

while True:
    # 不断取出从摄像头捕获的视频图像,返回两个数,一个是是否成功取出,另一个是捕获到的图像
    success, img = cap.read()
    # 如果成功取出
    if success:
        # 先翻转图像,因为从摄像头取出图像是翻转,我们需要镜像的
        img = cv2.flip(img, 1)
        # 调用HandDetector类中的find_hands成员方法,识别出img中的手,并画出21个手势地标
        img = detector.find_hands(img, draw=True)
        # 调用find_positions成员方法,计算出21个手势地标的索引值和坐标
        sun = detector.find_positions(img)
        # 如果sun中有元素
        if len(sun) > 0:
            # 先定义一个空列表,用户存放0和1,0表示手指闭合,1表示手指张开
            fingers = []
            # 遍历5根手指的指尖
            for tid in tip_ids:
                # 取出指尖的x坐标和y坐标
                x, y = sun[tid][1], sun[tid][2]
                # 在指尖上画一个圆
                cv2.circle(img, (x, y), 10, (0, 255, 0), cv2.FILLED)
                # 如果是大拇指
                if tid == 4:
                    # 如果大拇指指尖的x位置大于第二个关节的第二个位置,则认为大拇指打开
                    if sun[tid][1] > sun[tid - 1][1]:
                        # 给fingers列表添加一个1元素,表示一个手指张开,识别到的数字就加一
                        fingers.append(1)
                    else:
                        # 给fingers列表添加一个0元素,表示一个手指闭合,识别到的数字就不变
                        fingers.append(0)
                # 如果是其他手指
                else:
                    # 如果这些手指的指尖的y位置大于第二关节的位置,则认为这个手指打开,否则认为这个手指关闭
                    if sun[tid][2] < sun[tid - 2][2]:
                        fingers.append(1)
                    else:
                        fingers.append(0)
            # 判断有几个手指打开
            cnt = fingers.count(1)
            # 找出对应的数字图片,并显示
            finger_img = finger_list[cnt]
            # 计算所要添加图片的长和宽
            w, h, c = finger_img.shape
            # 在img图像的左上角添加图片
            img[0:w, 0:h] = finger_img
            # 在对应img的对应坐标位置添加一个矩形(绿色),并填充该矩形
            cv2.rectangle(img, (0, 200), (100, 300), (0, 255, 0), cv2.FILLED)
            # 在矩形中添加识别到的数字(红色)
            cv2.putText(img, str(cnt), (0, 300), cv2.FONT_HERSHEY_DUPLEX, 5, (0, 0, 255), 2)
        # 将img显示在以Image命名的窗口
        cv2.imshow('Image', img)
    # 等待按下一个键,并将该键的ASCII码值赋值给变量k
    k = cv2.waitKey(1)
    # 如果按下的是q,则退出循环
    if k == ord('q'):
        break
# 释放VideoCapture对象,防止程序占用摄像头,以释放资源
cap.release()
# 关闭所有由OpenCV创建的窗口
cv2.destroyAllWindows()

handms.py

import cv2
import mediapipe as mp


class HandDetector:

    def __init__(self):
        # 先定义results和handsjh两个成员变量,先将其定义为空
        self.results = None
        self.handsjh = None
        # 给mp.solutions.hands.Hands()函数一个较为简单的名字hands,使后续代码比较简短
        self.hands = mp.solutions.hands.Hands()

    def find_hands(self, img, draw=True):
        # 将img图像由BGR转换成RGB
        imgRGB = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
        # 处理手部检测和追踪的数据,并返回一个mp.solutions.hands.Hands()对象,其中包含了检测到的手部数据
        self.results = self.hands.process(imgRGB)
        if draw:
            # 确定是否有多只手的检测结果
            if self.results.multi_hand_landmarks:
                # 遍历multi_hand_landmarks中的数据
                for handlms in self.results.multi_hand_landmarks:
                    # 画出21个手势地标并其连起来
                    mp.solutions.drawing_utils.draw_landmarks(img, handlms, mp.solutions.hands.HAND_CONNECTIONS)
        return img

    def find_positions(self, img, hand_no=0):
        # 先将handsjh定义为空列表
        self.handsjh = []
        if self.results.multi_hand_landmarks:
            # 选择索引值为hand_no的手部地标数据,并将其赋值给变量hand
            hand = self.results.multi_hand_landmarks[hand_no]
            # 使用enumerate()函数对该手的每一个地标进行遍历,并将地标的索引值赋值给id,,并将每个地标的x、y值赋值给lm
            for id, lm in enumerate(hand.landmark):
                # 计算图像img的长和宽
                h, w, c = img.shape
                # 计算每个地标在img图像对应位置的x、y坐标
                cx, cy = int(lm.x * w), int(lm.y * h)
                # 将计算到的数据存放到handsjh列表中
                self.handsjh.append([id, cx, cy])
        return self.handsjh

注:该代码只有在左手手心朝屏幕和右手手背朝屏幕时,识别到的数字才是正确的,具体原因等你读懂了代码就知道是什么原因了

扩展

对新增的两个函数做出一下解释:


cv2.rectangle()

一个用于在图像上绘制矩形框的函数。它可以用于标记、选择或突出显示图像中的特定区域。
以下是cv2.rectangle()函数的参数说明:

必需参数:

img:图像数据,可以是一个整数,表示图像的灰度值,或者是一个三维数组,表示图像的 RGB 颜色值。
pt1:矩阵的顶点。
pt2:与pt1相对的矩阵顶点。

可选参数:
color:矩阵颜色。
thickness:构成矩形的线条的粗细,如果为负值则为填充矩形。
lineType:行类型,可以为 4、8 以及 16。
shift:点坐标中的小数位数。
这个函数将在给定的图像上绘制一个矩形,其左上角坐标为pt1,右下角坐标为pt2。你可以根据需要调整这些坐标来指定矩形的位置和大小。

cv2.putText()

 OpenCV 库提供的一个用于在图像上添加文本的函数。它可以将文本信息显示在图像上,常用于图像的标注、注释或可视化。
以下是cv2.putText()函数的参数说明:

必需参数:
img:图像数据,可以是一个整数,表示图像的灰度值,或者是一个三维数组,表示图像的 RGB 颜色值。
text:要添加的文本内容。

可选参数:
org:文本的初始位置。这是一个包含文本左下角坐标的元组(x,y)。
fontFace:字体的名称。
fontScale:字体的大小。
color:文本的颜色。
thickness:文本的线条粗细。
lineType:文本的线型。
bottomLeftOrigin:如果为真,则坐标(0,0)表示图像的左下角。否则,它表示图像的左上角。
使用cv2.putText()函数,你可以在图像上指定的位置添加文本,并设置文本的字体、大小、颜色等属性。

  • 11
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
要使用OpenCV进行手势识别,您可以使用以下步骤: 1. 捕获视频流并将其转换为灰度图像。 2. 使用阈值化方法将图像二值化,将手部区域从背景分离出来。 3. 对二值化图像进行形态学操作,如开运算和闭运算,以减少噪声并将手部区域连接。 4. 通过查找手的轮廓来确定手的位置。 5. 将手的位置标准化,并将其调整为特定大小的图像。 6. 使用机器学习算法或其他方法对数字手势进行分类。 下面是一个简单的示例代码,可以识别数字“0”到“5”的手势: ```python import cv2 import numpy as np # 加载训练数据 data = np.loadtxt('gesture_train.txt', np.float32) train, label = data[:, 1:], data[:, 0] # 创建KNN分类器 knn = cv2.ml.KNearest_create() knn.train(train, cv2.ml.ROW_SAMPLE, label) # 打开摄像头 cap = cv2.VideoCapture(0) while True: ret, frame = cap.read() if not ret: break # 将图像转换为灰度图像 gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) # 对图像进行阈值化 _, thresh = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU) # 进行形态学操作 kernel = np.ones((3, 3), np.uint8) opening = cv2.morphologyEx(thresh, cv2.MORPH_OPEN, kernel, iterations=2) closing = cv2.morphologyEx(opening, cv2.MORPH_CLOSE, kernel, iterations=2) # 查找手的轮廓 contours, _ = cv2.findContours(closing, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) if len(contours) > 0: contour = max(contours, key=cv2.contourArea) if cv2.contourArea(contour) > 1000: # 获取手的边界矩形 x, y, w, h = cv2.boundingRect(contour) # 标准化手的大小 hand_roi = cv2.resize(closing[y:y+h, x:x+w], (64, 64), interpolation=cv2.INTER_AREA) # 转换为一维数组 hand_roi = hand_roi.reshape((1, -1)).astype(np.float32) # 进行预测 _, result, _, _ = knn.findNearest(hand_roi, k=1) # 绘制结果 cv2.rectangle(frame, (x, y), (x + w, y + h), (0, 255, 0), 2) cv2.putText(frame, str(int(result[0][0])), (x, y - 10), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 255, 0), 2) # 显示图像 cv2.imshow('frame', frame) if cv2.waitKey(1) == ord('q'): break cap.release() cv2.destroyAllWindows() ``` 在上面的代码中,我们使用KNN算法对手势进行分类。训练数据存储在“gesture_train.txt”文件中,并包含手势的标签和特征向量。在实时视频中,我们首先将图像转换为灰度图像,然后使用阈值化方法将图像二值化。接下来,我们对二值化图像进行形态学操作,并查找手的轮廓。如果手的面积大于1000像素,则将手的位置标准化,并对其进行分类。最后,我们绘制手的边界矩形和分类结果。 请注意,这只是一个简单的示例代码,可能需要根据您的应用程序进行修改和优化。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

孙鲜生

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值