本帖最后由 云天 于 2021-6-19 23:29 编辑
谷歌出了一个开源的、跨平台的、可定制化的机器学习解决方案工具包,给在线流媒体(当然也可以用于普通的视频、图像等)提供了机器学习解决方案。感兴趣的同学可以打开这个网址了解详情:mediapipe.dev/
它提供了手势、人体姿势、人脸、物品等识别和追踪功能,并提供了C++、Python、JavaScript等编程语言的工具包以及iOS、Android平台的解决方案,今天我们就来看一下如何使用MediaPipe提供的手势识别,在Mind+中来写一个Python代码,用两个手指来控制Arduino上的RGB灯带。
【MediaPipe手】
感知手的形状和动作的能力可能是在各种技术领域和平台上改善用户体验的重要组成部分。例如,它可以构成手语理解和手势控制的基础,并且还可以在增强现实中将数字内容和信息覆盖在物理世界之上。虽然自然而然地出现在人们手中,但是强大的实时手感知力无疑是一项具有挑战性的计算机视觉任务,因为手经常相互遮挡自己或彼此(例如手指/手掌遮挡和握手),并且缺乏高对比度模式。
MediaPipe Hands是一种高保真手和手指跟踪解决方案。它采用机器学习(ML)来从单个帧中推断出手的21个3D界标。当前的最新方法主要依靠强大的桌面环境进行推理,而我们的方法可以在手机上实现实时性能,甚至可以扩展到多只手。我们希望将这种手感功能提供给更广泛的研究和开发社区,将导致出现创造性的用例,刺激新的应用程序和新的研究途径。
【pinpong python库】pinpong库是一套控制开源硬件主控板的Python库,基于Firmata协议并兼容MicroPython语法,5分钟即可让你上手使用Python控制开源硬件。借助于pinpong库,直接用Python代码就能给各种常见的开源硬件编程。其原理是给开源硬件烧录一个特定的固件,使开源硬件可以通过串口与电脑通讯,执行各种命令。当前PinPong库正在快速更新中,已支持Arduino系列uno、leonardo、mega2560,ESP32系列掌控板(handpy)以及micro:bit板,传感器支持50+。https://pinpong.readthedocs.io/zh_CN/latest/ 【两指控制(一)】 利用大拇指和食指的间距来控制亮灯数 欧氏距离是最易于理解的一种距离计算方法,源自欧氏空间中两点间的距离公式。
(1)二维平面上两点a(x1,y1)与b(x2,y2)间的欧氏距离:
-
- import cv2
- import math
- from handutil import HandDetector
- import numpy as np
- import time
- from pinpong.board import Board,Pin,NeoPixel #导入neopixel类
- Board("uno").begin() #初始化,选择板型(uno、leonardo、xugu)和端口号,不输入端口号则进行自动识别
- NEOPIXEL_PIN = Pin(Pin.D7)
- PIXELS_NUM = 7 #灯数
- np = NeoPixel(NEOPIXEL_PIN,PIXELS_NUM)
- # 打开摄像头
- cap = cv2.VideoCapture(0)
- # 创建一个手势识别对象
- detector = HandDetector()
-
- # 指尖列表,分别代表大拇指、食指、中指、无名指和小指的指尖
- tip_ids = [4, 8, 12, 16, 20]
- lenght=0
- num=0
- while True:
- success, img = cap.read()
-
- if success:
- # 检测手势
- img = detector.find_hands(img, draw=True)
- # 获取手势数据
- lmslist = detector.find_positions(img)
- if len(lmslist) > 0:
- fingersX = []
- fingersY = []
- for tid in tip_ids:
- # 找到每个指尖的位置
- x, y = lmslist[tid][1], lmslist[tid][2]
- cv2.circle(img, (x, y), 8, (255, 255, 0), cv2.FILLED)
- #记录大拇指的位置
- if tid == 4:
- fingersX.append(x)
- fingersY.append(y)
- #记录食指的位置
- if tid == 8:
- fingersX.append(x)
- fingersY.append(y)
- #利用两点间距离公式
- lenght=math.sqrt((fingersX[0]-fingersX[1])**2+(fingersY[0]-fingersY[1])**2)
-
-
- for num in range(0,int(lenght/210*7)):
-
- if num>6:
- num=6
- np[num] = (255, 0 ,0)
- if num!=6:
- for k in range(num,7):
- np[k] = (0, 0 ,0)
- cv2.line(img, (fingersX[0], fingersY[0]), (fingersX[1], fingersY[1]), (0, 0, 255), 4,8)
- print(lenght)
-
- cv2.imshow('Image', img)
-
- k = cv2.waitKey(1)
- if k == ord('q'):
- break
-
- cap.release()
- cv2.destroyAllWindows()
复制代码
【两指控制(二)】 利用大拇指和食指的倾斜角来控制亮灯数 -
-
- import cv2
- import math
- from handutil import HandDetector
- import numpy as np
- import time
- from pinpong.board import Board,Pin,NeoPixel #导入neopixel类
- Board("uno").begin() #初始化,选择板型(uno、leonardo、xugu)和端口号,不输入端口号则进行自动识别
- NEOPIXEL_PIN = Pin(Pin.D7)
- PIXELS_NUM = 7 #灯数
- np = NeoPixel(NEOPIXEL_PIN,PIXELS_NUM)
- # 打开摄像头
- cap = cv2.VideoCapture(0)
- # 创建一个手势识别对象
- detector = HandDetector()
-
- # 指尖列表,分别代表大拇指、食指、中指、无名指和小指的指尖
- tip_ids = [4, 8, 12, 16, 20]
- angle=0
- num=0
- while True:
- success, img = cap.read()
-
- if success:
- # 检测手势
- img = detector.find_hands(img, draw=True)
- # 获取手势数据
- lmslist = detector.find_positions(img)
- if len(lmslist) > 0:
- fingersX = []
- fingersY = []
- for tid in tip_ids:
- # 找到每个指尖的位置
- x, y = lmslist[tid][1], lmslist[tid][2]
- cv2.circle(img, (x, y), 8, (255, 255, 0), cv2.FILLED)
-
- if tid == 4:
- fingersX.append(x)
- fingersY.append(y)
-
- if tid == 8:
- fingersX.append(x)
- fingersY.append(y)
- if fingersX[0]-fingersX[1]!=0:#计算倾斜角
- angle=math.degrees(math.atan((fingersY[0]-fingersY[1])/(fingersX[0]-fingersX[1])))
- else:
- angle=90
-
- for num in range(0,int(abs(angle)/90*7)):
-
- if num>6:
- num=6
- np[num] = (255, 0 ,0)
- if num!=6:
- for k in range(num,7):
- np[k] = (0, 0 ,0)
- cv2.line(img, (fingersX[0], fingersY[0]), (fingersX[1], fingersY[1]), (0, 0, 255), 4,8)
- print(angle)
-
- cv2.imshow('Image', img)
-
- k = cv2.waitKey(1)
- if k == ord('q'):
- break
-
- cap.release()
- cv2.destroyAllWindows()
复制代码
【手势识别类】 以上Python程序中用到的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
复制代码
|