谷歌出了一个开源的、跨平台的、可定制化的机器学习解决方案工具包,给在线流媒体(当然也可以用于普通的视频、图像等)提供了机器学习解决方案。感兴趣的同学可以打开这个网址了解详情:mediapipe.dev/
它提供了手势、人体姿势、人脸、物品等识别和追踪功能,并提供了C++、Python、JavaScript等编程语言的工具包以及iOS、Android平台的解决方案,今天我们就来看一下如何使用MediaPipe提供的手势识别来写一个Python代码识别手势中的数字:0-5 ,来控制Arduino板上RGB灯带。
【MediaPipe手】
感知手的形状和动作的能力可能是在各种技术领域和平台上改善用户体验的重要组成部分。例如,它可以构成手语理解和手势控制的基础,并且还可以在增强现实中将数字内容和信息覆盖在物理世界之上。虽然自然而然地出现在人们手中,但是强大的实时手感知力无疑是一项具有挑战性的计算机视觉任务,因为手经常相互遮挡自己或彼此(例如手指/手掌遮挡和握手),并且缺乏高对比度模式。 MediaPipe Hands是一种高保真手和手指跟踪解决方案。它采用机器学习(ML)来从单个帧中推断出手的21个3D界标。当前的最新方法主要依靠强大的桌面环境进行推理,而我们的方法可以在手机上实现实时性能,甚至可以扩展到多只手。
【判断手势代码】
-
- 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)
-
- if cnt==0:
- for i in range(0,7):
- np[i] = (0, 0 ,0)#设置RGB灯为黑色
-
- if cnt==1:
- np[0] = (255, 0 ,0) #设置RGB灯0为红色
- for i in range(1,7):
- np[i] = (0, 0 ,0)
- if cnt==2:
- np[0] = (0, 255 ,0) #设置RGB灯0为绿色
- np[1] = (0, 255 ,0) #设置RGB灯1为绿色
- for i in range(2,7):
- np[i] = (0, 0 ,0)
-
- if cnt==3:
- np[0] = (0, 0 ,255) #设置RGB灯0为蓝色
- np[1] = (0, 0 ,255) #设置RGB灯1为蓝色
- np[2] = (0, 0 ,255) #设置RGB灯2为蓝色
- for i in range(3,7):
- np[i] = (0, 0 ,0)
-
- if cnt==4:
- np[0] = (255, 255 ,0) #设置RGB灯0为黄色
- np[1] = (255, 255 ,0) #设置RGB灯1为黄色
- np[2] = (255, 255 ,0) #设置RGB灯2为黄色
- np[3] = (255, 255 ,0) #设置RGB灯3为黄色
- for i in range(4,7):
- np[i] = (0, 0 ,0)
- if cnt==5:
- np[0] = (0, 255 ,255) #设置RGB灯0为青色
- np[1] = (0, 255 ,255) #设置RGB灯1为青色
- np[2] = (0, 255 ,255) #设置RGB灯2为青色
- np[3] = (0, 255 ,255) #设置RGB灯3为青色
- np[4] = (0, 255 ,255) #设置RGB灯4为青色
- for i in range(5,7):
- np[i] = (0, 0 ,0)
-
复制代码
其中涉及色光三原色色光三原色,即R(红Red)、G(绿Green)、B(蓝Blue),RGB三色混合在一起会变成白色。
【识别手指数】
【程序代码】
-
- import cv2
- from handutil import HandDetector
- import numpy as np
- from pinpong.board import Board,Pin,NeoPixel
- NEOPIXEL_PIN = Pin.D7
- PIXELS_NUM = 7 #灯数
- Board("uno").begin() #初始化,选择板型和端口号,不输入端口号则进行自动识别
- np = NeoPixel(Pin(NEOPIXEL_PIN), PIXELS_NUM)
-
-
- # 打开摄像头
- cap = cv2.VideoCapture(0)
- # 创建一个手势识别对象
- detector = HandDetector()
-
-
-
-
- # 指尖列表,分别代表大拇指、食指、中指、无名指和小指的指尖
- 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)
-
- if cnt==0:
- for i in range(0,7):
- np[i] = (0, 0 ,0)#设置RGB灯为黑色
-
- if cnt==1:
- np[0] = (255, 0 ,0) #设置RGB灯0为红色
- for i in range(1,7):
- np[i] = (0, 0 ,0)
- if cnt==2:
- np[0] = (0, 255 ,0) #设置RGB灯0为绿色
- np[1] = (0, 255 ,0) #设置RGB灯1为绿色
- for i in range(2,7):
- np[i] = (0, 0 ,0)
-
- if cnt==3:
- np[0] = (0, 0 ,255) #设置RGB灯0为蓝色
- np[1] = (0, 0 ,255) #设置RGB灯1为蓝色
- np[2] = (0, 0 ,255) #设置RGB灯2为蓝色
- for i in range(3,7):
- np[i] = (0, 0 ,0)
-
- if cnt==4:
- np[0] = (255, 255 ,0) #设置RGB灯0为黄色
- np[1] = (255, 255 ,0) #设置RGB灯1为黄色
- np[2] = (255, 255 ,0) #设置RGB灯2为黄色
- np[3] = (255, 255 ,0) #设置RGB灯3为黄色
- for i in range(4,7):
- np[i] = (0, 0 ,0)
- if cnt==5:
- np[0] = (0, 255 ,255) #设置RGB灯0为青色
- np[1] = (0, 255 ,255) #设置RGB灯1为青色
- np[2] = (0, 255 ,255) #设置RGB灯2为青色
- np[3] = (0, 255 ,255) #设置RGB灯3为青色
- np[4] = (0, 255 ,255) #设置RGB灯4为青色
- for i in range(5,7):
- np[i] = (0, 0 ,0)
-
- cv2.imshow('Image', img)
-
- k = cv2.waitKey(1)
- if k == ord('q'):
- break
-
- cap.release()
- cv2.destroyAllWindows()
复制代码
程序中用到的类文件:handutil.py-
- 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
复制代码
【演示视频】
|