阅读 1891

Python玩人工智能:识别手势数字

用Python编程识别手势数字

谷歌出了一个开源的、跨平台的、可定制化的机器学习解决方案工具包,给在线流媒体(当然也可以用于普通的视频、图像等)提供了机器学习解决方案。感兴趣的同学可以打开这个网址了解详情:mediapipe.dev/

image-20210422112313073

它提供了手势、人体姿势、人脸、物品等识别和追踪功能,并提供了C++、Python、JavaScript等编程语言的工具包以及iOS、Android平台的解决方案,今天我们就来看一下如何使用MediaPipe提供的手势识别来写一个Python代码识别手势中的数字:0-5 。

hand_crops.png

准备工作

电脑需要安装Python3,建议安装Python3.8.x的版本。除此之外,还需要安装Opencv-Python、MediaPipe以及numpy几个工具包,可以使用pip进行安装:

pip install mediapipe numpy opencv-python
复制代码

我的电脑是Python3.8.3,各工具包版本是:

mediapipe==0.8.3.1
numpy==1.20.2
opencv-python==4.5.1.48
复制代码

准备6张图片,分别是6张手的图片。

image-20210424155201904.png

编写程序

  1. 编写一个handutil.py模块,这个handutil模块有一个HandDetector类,提供了检测手势、获取手势数据的方法。代码如下,详细解释看代码注释:
import cv2
import mediapipe as mp


class HandDetector():
    '''
    手势识别类
    '''
    def __init__(self, mode=False, max_hands=2, detection_con=0.5, track_con=0.5):
        '''
        初始化
        :param mode: 是否静态图片,默认为False
        :param max_hands: 最多几只手,默认为2只
        :param detection_con: 最小检测信度值,默认为0.5
        :param track_con: 最小跟踪信度值,默认为0.5
        '''
        self.mode = mode
        self.max_hands = max_hands
        self.detection_con = detection_con
        self.track_con = track_con

        self.hands = mp.solutions.hands.Hands(self.mode, self.max_hands, self.detection_con, self.track_con)

    def find_hands(self, img, draw=True):
        '''
        检测手势
        :param img: 视频帧图片
        :param draw: 是否画出手势中的节点和连接图
        :return: 处理过的视频帧图片
        '''
        imgRGB = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
        # 处理图片,检测是否有手势,将数据存进self.results中
        self.results = self.hands.process(imgRGB)
        if draw:
            if self.results.multi_hand_landmarks:
                for handlms in self.results.multi_hand_landmarks:
                    mp.solutions.drawing_utils.draw_landmarks(img, handlms, mp.solutions.hands.HAND_CONNECTIONS)
        return img

    def find_positions(self, img, hand_no=0):
        '''
        获取手势数据
        :param img: 视频帧图片
        :param hand_no: 手编号(默认第1只手)
        :return: 手势数据列表,每个数据成员由id, x, y组成,代码这个手势位置编号以及在屏幕中的位置
        '''
        self.lmslist = []
        if self.results.multi_hand_landmarks:
            hand = self.results.multi_hand_landmarks[hand_no]
            for id, lm in enumerate(hand.landmark):
                h, w, c = img.shape
                cx, cy = int(lm.x * w), int(lm.y * h)
                self.lmslist.append([id, cx, cy])

        return self.lmslist
复制代码
  1. 编写另一个fingercount.py代码,在这个代码中,调用handutil.py的HandDetector类提供的方法,获取手势数据,每个手势数据由3个数字组成:id, x, y,分别代表手势中某个点以及这个点的x\y坐标位置。下图是手势识别中每个id对应手的部位说明。

hand_landmarks.png

从上图可知:4, 8, 12, 16, 20分别代表大拇指、食指、中指、无名指和小指的指尖。完整代码如下:

import cv2
from handutil import HandDetector

# 打开摄像头
cap = cv2.VideoCapture(1)
# 创建一个手势识别对象
detector = HandDetector()

# 6张手的图片,分别代表0~5
finger_img_list = [
    'fingers/0.png',
    'fingers/1.png',
    'fingers/2.png',
    'fingers/3.png',
    'fingers/4.png',
    'fingers/5.png',
]
finger_list = []
for fi in finger_img_list:
    i = cv2.imread(fi)
    finger_list.append(i)

# 指尖列表,分别代表大拇指、食指、中指、无名指和小指的指尖
tip_ids = [4, 8, 12, 16, 20]

while True:
    success, img = cap.read()

    if success:
        # 检测手势
        img = detector.find_hands(img, draw=True)
        # 获取手势数据
        lmslist = detector.find_positions(img)
        if len(lmslist) > 0:
            fingers = []
            for tid in tip_ids:
                # 找到每个指尖的位置
                x, y = lmslist[tid][1], lmslist[tid][2]
                cv2.circle(img, (x, y), 10, (0, 255, 0), cv2.FILLED)
                # 如果是大拇指,如果大拇指指尖x位置大于大拇指第二关节的位置,则认为大拇指打开,否则认为大拇指关闭
                if tid == 4:
                    if lmslist[tid][1] > lmslist[tid - 1][1]:
                        fingers.append(1)
                    else:
                        fingers.append(0)
                # 如果是其他手指,如果这些手指的指尖的y位置大于第二关节的位置,则认为这个手指打开,否则认为这个手指关闭
                else:
                    if lmslist[tid][2] < lmslist[tid - 2][2]:
                        fingers.append(1)
                    else:
                        fingers.append(0)
            # fingers是这样一个列表,5个数据,0代表一个手指关闭,1代表一个手指打开
            # 判断有几个手指打开
            cnt = fingers.count(1)
            # 找到对应的手势图片并显示
            finger_img = finger_list[cnt]
            w, h, c = finger_img.shape
            img[0:w, 0:h] = finger_img
            cv2.rectangle(img, (200, 0), (300, 100), (0, 255, 0), cv2.FILLED)
            cv2.putText(img, str(cnt), (200, 100), cv2.FONT_HERSHEY_DUPLEX, 5, (0, 0, 255))

        cv2.imshow('Image', img)

    k = cv2.waitKey(1)
    if k == ord('q'):
        break

cap.release()
cv2.destroyAllWindows()

复制代码

运行代码,我们可以看到能够识别手势中的数字,并显示对应的图片和数字了。

image-20210424155406430.png

image-20210424155433600.png

image-20210424155459651.png

image-20210424155517619.png

image-20210424155550759.png

image-20210424155607893.png

欢迎关注”编程玩家俱乐部“公众号,学习更多好玩又有趣的编程。

文章分类
人工智能
文章标签