各位同学好,今天和大家分享一下如何使用 opencv+mediapipe 完成手势移动虚拟物体,可自定义各种形状的物体,通过手势搭积木。先放张图看效果。

规则:食指在某个物体内部,并且中指指尖和食指指尖的距离小于规定值指尖连线的中点变成绿色,认为是选中物体,物体变成红色。可以移动物体物体中点随着食指的位置移动,物体移动到指定位置后,指尖距离大于规定值,物体停下,变成淡蓝色。


1. 安装工具包

# 安装工具包
pip install opencv-contrib-python  # 安装opencv
pip install mediapipe  # 安装mediapipe
# pip install mediapipe --user  #有user报错的话试试这个
pip install cvzone  # 安装cvzone# 导入工具包
import cv2
from cvzone.HandTrackingModule import HandDetector  # 手部追踪方法
import time
import math
import random

21个手部关键点信息如下,本节我们主要研究食指根部"5"小指根部'17'的坐标信息。


2. 检测手部关键点

(1) cvzone.HandTrackingModule.HandDetector()  是手部关键点检测方法

参数:

mode: 默认为 False,将输入图像视为视频流。它将尝试在第一个输入图像中检测手,并在成功检测后进一步定位手的坐标。在随后的图像中,一旦检测到所有 maxHands 手并定位了相应的手的坐标,它就会跟踪这些坐标,而不会调用另一个检测,直到它失去对任何一只手的跟踪。这减少了延迟,非常适合处理视频帧。如果设置为 True,则在每个输入图像上运行手部检测,用于处理一批静态的、可能不相关的图像。

maxHands: 最多检测几只手,默认为 2

detectionCon: 手部检测模型的最小置信值(0-1之间),超过阈值则检测成功。默认为 0.5

minTrackingCon: 坐标跟踪模型的最小置信值 (0-1之间),用于将手部坐标视为成功跟踪,不成功则在下一个输入图像上自动调用手部检测。将其设置为更高的值可以提高解决方案的稳健性,但代价是更高的延迟。如果 mode 为 True,则忽略这个参数,手部检测将在每个图像上运行。默认为 0.5

它的参数和返回值类似于官方函数 mediapipe.solutions.hands.Hands()

(2)cvzone.HandTrackingModule.HandDetector.findHands()    找到手部关键点并绘图

参数:

img: 需要检测关键点的帧图像,格式为BGR

draw: 是否需要在原图像上绘制关键点及识别框

flipType: 图像是否需要翻转,当视频图像和我们自己不是镜像关系时,设为True就可以了

返回值:

hands: 检测到的手部信息,由0或1或2个字典组成的列表。如果检测到两只手就是由两个字典组成的列表。字典中包含:21个关键点坐标,检测框坐标及宽高,检测框中心坐标,检测出是哪一只手。

img: 返回绘制了关键点及连线后的图像

代码如下

import cv2
from cvzone.HandTrackingModule import HandDetector
import time
import math#(1)捕获摄像头
cap = cv2.VideoCapture(0) # 捕获电脑摄像头
cap.set(3, 1280)  # 设置显示窗口宽度1280
cap.set(4, 720)   # 显示窗口高度720pTime = 0  # 处理第一帧图像的起始时间#(2)接收手部检测方法
detector = HandDetector(mode=False, # 静态图模式,若为True,每一帧都会调用检测方法,导致检测很慢maxHands=1, # 最多检测几只手detectionCon=0.8, # 最小检测置信度minTrackCon=0.5)  # 最小跟踪置信度#(3)处理每一帧图像
while True:# 返回图像是否读取成功,以及读取的帧图像imgsuccess, img = cap.read()#(4)获取手部关键点信息# 检测手部信息,返回手部关键点信息hands字典,绘制关键点和连线后的图像imghands, img = detector.findHands(img)print(hands)#(5)图像显示# 计算FPS值cTime = time.time()  # 处理一帧图像所需的时间fps = 1/(cTime-pTime) pTime = cTime  # 更新处理下一帧的起始时间# 把fps值显示在图像上,img画板,显示字符串,显示的坐标位置,字体,字体大小,颜色,线条粗细cv2.putText(img, str(int(fps)), (50,70), cv2.FONT_HERSHEY_PLAIN, 3, (255,0,0), 3)# 显示图像,输入窗口名及图像数据# cv2.namedWindow("img", 0)  # 窗口大小可手动调整cv2.imshow('img', img)    if cv2.waitKey(1) & 0xFF==27:  #每帧滞留1毫秒后消失,ESC键退出break# 释放视频资源
cap.release()
cv2.destroyAllWindows()

打印检测到的手部关键点信息hands列表lmList中存放21个手部关键点的像素坐标,bbox中存放检测框的左上角坐标和框的宽高center存放检测框的中心坐标type检测的是左手还是右手

-----------------------------------------------------------------
[{'lmList': [[227, 607], [335, 585], [439, 515], [508, 440], [563, 384], [434, 384], [491, 292], [520, 231], [543, 176], [380, 349], [423, 241], [445, 169], [459, 106], [320, 336], [347, 228], [368, 156], [387, 94], [250, 339], [255, 245], [264, 183], [279, 126]],'bbox': (227, 94, 336, 513),
'center': (395, 350),
'type': 'Right'}]
[{'lmList': [[219, 628], [324, 605], [427, 532], [489, 451], [540, 390], [424, 401], [483, 310], [511, 250], [532, 195], [369, 366], [415, 263], [436, 192], [449, 129], [308, 353], [340, 250], [362, 181], [382, 120], [238, 358], [248, 268], [261, 209], [278, 154]],
'bbox': (219, 120, 321, 508),
'center': (379, 374),
'type': 'Right'}]
-----------------------------------------------------------------

图像显示结果如下:


3. 绘制虚拟物体

首先,在提取视频帧图像之前先设置好虚拟物体的初始位置,将每个矩形的左上坐标点[ptx, pty]保存在一个列表中 ptList.append([ptx, pty]),因为后续移动物体时,每次移动需要改变单个物体的位置,而其他物体的位置不变,保存在列表中易于后需更改坐标位置。

在读取视频帧图像之后通过for循环遍历每个矩形的左上坐标 pt,在图像上绘制出来。为了显示的清晰一些,采用半透明矩形透明度 alphaReserve 等于0时全颜色填充,等于1时无填充。

import cv2
import time
from cvzone.HandTrackingModule import HandDetector  # 导入手部检测模块#(1)视频捕获
cap = cv2.VideoCapture(0)  # 0代表电脑自带的摄像头
cap.set(3, 1280)  # 设置图像显示窗口的宽
cap.set(4, 720)   # 设置图像显示窗口的高pTime = 0  # 处理一帧图像的初始时间color = (255,255,0)  # 可移动物体的默认颜色w, h = 150, 150   # 矩形宽和高#(2)在频幕上构造物体的函数
def creObj(img, color, ptx, pty, w, h):# 透明矩形参数设置alphaReserve = 0.6  # 透明度BChannel, GChannel, RChannel = color  # 设置矩形颜色 yMin, yMax = pty, pty+h  # 矩形框的y坐标范围xMin, xMax = ptx, ptx+w  # 矩形框的y坐标范围# 绘制透明矩形img[yMin:yMax, xMin:xMax, 0] = img[yMin:yMax, xMin:xMax, 0] * alphaReserve + BChannel * (1 - alphaReserve)img[yMin:yMax, xMin:xMax, 1] = img[yMin:yMax, xMin:xMax, 1] * alphaReserve + GChannel * (1 - alphaReserve)img[yMin:yMax, xMin:xMax, 2] = img[yMin:yMax, xMin:xMax, 2] * alphaReserve + RChannel * (1 - alphaReserve)# 美化边界框line = 35 # 边缘线段长度cv2.rectangle(img, (ptx,pty), (ptx+w,pty+h), (255,0,255), 2) # 边框cv2.line(img, (ptx,pty), (ptx,pty+line), (0,255,255), 5)  # 左上角cv2.line(img, (ptx,pty), (ptx+line,pty), (0,255,255), 5) cv2.line(img, (ptx+w,pty), (ptx+w-line,pty), (0,255,255), 5)  # 右上角cv2.line(img, (ptx+w,pty), (ptx+w,pty+line), (0,255,255), 5)   cv2.line(img, (ptx,pty+h), (ptx+line,pty+h), (0,255,255), 5)  # 左下角cv2.line(img, (ptx,pty+h), (ptx,pty+h-line), (0,255,255), 5) cv2.line(img, (ptx+w,pty+h), (ptx+w-line,pty+h), (0,255,255), 5)  # 右下角cv2.line(img, (ptx+w,pty+h), (ptx+w,pty+h-line), (0,255,255), 5) # 返回绘制后的图像return img#(3)接收手部检测方法
detector = HandDetector(mode=False,  # 视频流maxHands=1,  # 最多检测一只手detectionCon=0.8,  # 手部检测的最小置信度minTrackCon=0.5)   # 手部跟踪的最小置信度#(4)在屏幕上创建初始矩形
ptList = []  # 存放每个矩形的左上角坐标# 通过循环创建9个矩形,初始排列方式为3行3列
for i in range(3):  # 3行for j in range(3):  # 3列# 指定每个矩形的左上角坐标ptx = 200 * j + 100  # x坐标,起始位置为x=100,水平方向两个矩形间隔200个像素pty = 200 * i + 100  # y坐标,起始位置为y=100,每次换行下移200个像素# 将每个矩形的左上角坐标保存起来ptList.append([ptx, pty])#(5)处理每一帧视频图像
while True:# 返回是否读取成功和读取的图像success, img = cap.read()# 图像翻转,呈镜像关系img = cv2.flip(img, flipCode=1)  # 1代表水平翻转,0代表竖直翻转#(6)手部关键点检测# 返回检测到的手部关键点信息,以及绘制关键点后的图像hands, img = detector.findHands(img, flipType=False)  # 由于上面翻转过图像了,这里就设置flipType不翻转   #(7)绘制可移动物体for pt in ptList:  # 遍历所有矩形的左上角        img = creObj(img, color, pt[0], pt[1], w, h)#(8)显示图像# 记录执行时间      cTime = time.time()      # 计算fpsfps = 1/(cTime-pTime)# 重置起始时间pTime = cTime# 把fps显示在窗口上;img画板;取整的fps值;显示位置的坐标;设置字体;字体比例;颜色;厚度cv2.putText(img, str(int(fps)), (10,70), cv2.FONT_HERSHEY_PLAIN, 3, (255,0,0), 3)# 显示图像cv2.imshow('image', img)  #窗口名,图像变量if cv2.waitKey(1) & 0xFF==27:  #每帧滞留1毫秒后消失break# 释放视频资源
cap.release()
cv2.destroyAllWindows()

图像显示结果如下:


4. 移动物体

从下面代码的第(7)步开始,计算食指指尖 lmList[8]中指指尖 lmList[12] 之间的距离,并绘制指尖连线cv2.line。使用计算平方和再开根的方法math.sqrt(),计算指尖距离。如果像素距离小于80就认为是选择该物体,指尖连线中点变成绿色

第(8)步确定食指在哪个矩形的内部。遍历所有的矩形的左上角坐标ptList,如果食指关键点坐标在某个矩形框内部,就记录下该矩形所在列表中的索引changed = index,接下来改变这个矩形的坐标位置。

找到了食指在哪个矩形内之后,如果指尖距离小于规定值,代表移动该物体,让该矩形的中点落在食指关键点的位置 cx, cy = finTip,那么矩形就可以跟着食指一起移动了,并且,更改每一帧的矩形的左上坐标 ptList[index] = pt,时刻改变矩形在屏幕上的位置。

有时食指关键点会在几个矩形的内部,这样的话,这几个矩形的中点都变成了食指指尖关键点,使这几个矩形都重合在一起,为了避免这种情况,当我们选择了一个矩形时,就 break 断开当前循坏,不再判断食指在哪个物体内部,这样就可以每次只移动一个物体。

import cv2
import time
import math
from cvzone.HandTrackingModule import HandDetector  # 导入手部检测模块#(1)视频捕获
cap = cv2.VideoCapture(0)  # 0代表电脑自带的摄像头
cap.set(3, 1280)  # 设置图像显示窗口的宽
cap.set(4, 720)   # 设置图像显示窗口的高pTime = 0  # 处理一帧图像的初始时间changed = None # 初始状态不需要改变矩形颜色color = (255,255,0)  # 可移动物体的默认颜色medColor = (255,0,0)  # 中指和食指指尖中点的初始颜色w, h = 150, 150   # 矩形宽和高#(2)在频幕上构造物体的函数
def creObj(img, color, ptx, pty, w, h):# 透明矩形参数设置alphaReserve = 0.6  # 透明度BChannel, GChannel, RChannel = color  # 设置矩形颜色 yMin, yMax = pty, pty+h  # 矩形框的y坐标范围xMin, xMax = ptx, ptx+w  # 矩形框的y坐标范围# 绘制透明矩形img[yMin:yMax, xMin:xMax, 0] = img[yMin:yMax, xMin:xMax, 0] * alphaReserve + BChannel * (1 - alphaReserve)img[yMin:yMax, xMin:xMax, 1] = img[yMin:yMax, xMin:xMax, 1] * alphaReserve + GChannel * (1 - alphaReserve)img[yMin:yMax, xMin:xMax, 2] = img[yMin:yMax, xMin:xMax, 2] * alphaReserve + RChannel * (1 - alphaReserve)# 美化边界框line = 35 # 边缘线段长度cv2.rectangle(img, (ptx,pty), (ptx+w,pty+h), (255,0,255), 2) # 边框cv2.line(img, (ptx,pty), (ptx,pty+line), (0,255,255), 5)  # 左上角cv2.line(img, (ptx,pty), (ptx+line,pty), (0,255,255), 5) cv2.line(img, (ptx+w,pty), (ptx+w-line,pty), (0,255,255), 5)  # 右上角cv2.line(img, (ptx+w,pty), (ptx+w,pty+line), (0,255,255), 5)   cv2.line(img, (ptx,pty+h), (ptx+line,pty+h), (0,255,255), 5)  # 左下角cv2.line(img, (ptx,pty+h), (ptx,pty+h-line), (0,255,255), 5) cv2.line(img, (ptx+w,pty+h), (ptx+w-line,pty+h), (0,255,255), 5)  # 右下角cv2.line(img, (ptx+w,pty+h), (ptx+w,pty+h-line), (0,255,255), 5) # 返回绘制后的图像return img#(3)接收手部检测方法
detector = HandDetector(mode=False,  # 视频流maxHands=1,  # 最多检测一只手detectionCon=0.8,  # 手部检测的最小置信度minTrackCon=0.5)   # 手部跟踪的最小置信度#(4)在屏幕上创建初始矩形
ptList = []  # 存放每个矩形的左上角坐标# 通过循环创建9个矩形,初始排列方式为3行3列
for i in range(3):  # 3行for j in range(3):  # 3列# 指定每个矩形的左上角坐标ptx = 200 * j + 100  # x坐标,起始位置为x=100,水平方向两个矩形间隔200个像素pty = 200 * i + 100  # y坐标,起始位置为y=100,每次换行下移200个像素# 将每个矩形的左上角坐标保存起来ptList.append([ptx, pty])#(4)处理每一帧视频图像
while True:# 返回是否读取成功和读取的图像success, img = cap.read()# 图像翻转,呈镜像关系img = cv2.flip(img, flipCode=1)  # 1代表水平翻转,0代表竖直翻转#(5)手部关键点检测# 返回检测到的手部关键点信息,以及绘制关键点后的图像hands, img = detector.findHands(img, flipType=False)  # 由于上面翻转过图像了,这里就设置flipType不翻转   #(6)绘制可移动物体for index, pt in enumerate(ptList):  # 遍历所有矩形的左上角# 如果索引等于需要改变的矩形索引,就改变该矩形的颜色,否则就不变if index == changed:color = (0,0,255)else:color = (255,255,0)img = creObj(img, color, pt[0], pt[1], w, h)#(7)计算食指和中指间的距离    if hands:  # 如果检测到手部信息才接下去执行# 将该只手的21个关键点坐标提取出来,hands是字典存放手信息lmList = hands[0]['lmList']# 获取食指指尖的坐标(像素坐标)finTip = lmList[8]  # 存放x和y坐标# 获取中指指尖坐标checkTip = lmList[12]# 绘制食指和中指指尖的连线cv2.line(img, finTip, checkTip, (255,0,0), 9)cv2.circle(img, finTip, 15, (255,0,0), cv2.FILLED) # 以食指尖为圆心画圆cv2.circle(img, checkTip, 15, (255,0,0), cv2.FILLED) # 以中指尖为圆心画圆# 以两指尖的中点为圆心画圆,如果距离小于规定值,颜色改变cv2.circle(img, ((finTip[0]+checkTip[0])//2, (finTip[1]+checkTip[1])//2), 15, medColor, cv2.FILLED) # 计算食指和中指间的距离distance = math.sqrt((finTip[0]-checkTip[0])**2 + (finTip[1]-checkTip[1])**2)# 如果距离小于80认为是选择物体,指尖指尖中点的颜色改变if distance < 80:medColor = (0,255,0)else:  # 如果大于80,就重置指尖中点颜色medColor = (255,0,0)#(8)判断食指指尖在哪个矩形的内部for index, pt in enumerate(ptList):  # 遍历所有矩形的左上角坐标ptx, pty = pt  # 提取每个矩形左上角的x和y坐标     # finTip保存食指指尖的x和y坐标if ptx<=finTip[0]<=ptx+w and pty<=finTip[1]<=pty+h:  # 如果食指指尖在某个矩形框内部# 记录下该矩形的索引changed = index# 如果食指和中指间的距离小于80,那就认为是移动物体if distance < 80:# 让物体中心点位于指尖位置cx, cy = finTip# 改变左上角坐标pt = [cx-w//2, cy-h//2]# 改变列表中的该索引对应的左上角坐标ptList[index] = pt# 找到了就退出循环,代表一次只移动一个矩形break# 如果物体移动到了指定位置,那就松手else:  # 重置矩形的颜色color = (255,255,0)  changed = None  #(7)显示图像# 记录执行时间      cTime = time.time()      # 计算fpsfps = 1/(cTime-pTime)# 重置起始时间pTime = cTime# 把fps显示在窗口上;img画板;取整的fps值;显示位置的坐标;设置字体;字体比例;颜色;厚度cv2.putText(img, str(int(fps)), (10,70), cv2.FONT_HERSHEY_PLAIN, 3, (255,0,0), 3)# 显示图像cv2.imshow('image', img)  #窗口名,图像变量if cv2.waitKey(1) & 0xFF==27:  #每帧滞留1毫秒后消失break# 释放视频资源
cap.release()
cv2.destroyAllWindows()

没选择物体时:

移动物体时:

【机器视觉案例】(10) AI视觉搭积木,手势移动虚拟物体,附python完整代码相关推荐

  1. 【机器视觉案例】(11) 眨眼计数器,人脸关键点检测,附python完整代码

    各位同学好,今天和大家分享一下如何使用 mediapipe+opencv 实现眨眼计数器.先放张图看效果. 下图左侧为视频图像,右侧为平滑后的人眼开合比曲线.以左眼为例,若眼眶上下边界的距离与左右边界 ...

  2. 【MediaPipe】(1) AI视觉,手部关键点实时跟踪,附python完整代码

    各位同学好,今天和大家分享一下如何使用MediaPipe完成手部关键点实时检测跟踪.先放张图看效果,15代表FPS值. 1. 导入工具包 # 安装opencv pip install opencv-c ...

  3. 【机器视觉案例】(9) AI视觉,手势控制电脑键盘,附python完整代码

    各位同学好,今天和大家分享一下如何使用 opencv+mediapipe 完成远程手势控制电脑键盘.感兴趣的可以看一下我前面一篇手势控制电脑鼠标:https://blog.csdn.net/dgvv4 ...

  4. 【机器视觉案例】(5) AI视觉,手势调节物体尺寸,附python完整代码

    各位同学好,今天和大家分享一下如何使用opencv+mediapipe完成远程手势调节图片尺寸的案例.先放张图看效果.当拇指和食指竖起时,根据食指间的连线的长度自由缩放图片尺寸.图片的中点始终位于指尖 ...

  5. 【机器视觉案例】(5) AI视觉,远程手势控制虚拟计算器,附python完整代码

    各位同学好,今天和大家分享一下如何使用MediaPipe+Opencv完成虚拟计算器,先放张图看效果.FPS值为29,食指和中指距离小于规定阈值则认为点击按键,为避免重复数字出现,规定每20帧可点击一 ...

  6. 【MediaPipe】(4) AI视觉,远程手势调节电脑音量,附python完整代码

    各位同学好,今天和大家分享一下如何使用MediaPipe完成手势调节电脑音量,先放张图看效果. 注意!! 本节需要用到手部关键点的实时跟踪,我已经在之前的文章中详细写过了,本节会直接使用,有疑问的同学 ...

  7. 【机器视觉案例】(6) AI视觉,距离测量,自制AI小游戏,附python完整代码

    各位同学好,今天和大家分享一下如何使用 opencv + mediapipe 创建一个AI视觉小游戏,先放图看效果. 游戏规则,用手按下屏幕上的圆形按钮,每按一次后松开,按钮就随机出现在屏幕上的一个位 ...

  8. 【神经网络】(2) 网络优化,案例:服装图像分类,附python完整代码

    各位同学好,今天和大家分享以下TensorFlow2.0深度学习中对神经网络的优化方法,包括动量.学习率.dropout.交叉验证.正则化.本节使用数学公式对网络进行优化,增加网络的灵活性. 以下代码 ...

  9. 【深度学习】(5) 简单网络,案例:服装图片分类,附python完整代码

    1. 数据获取 使用系统内部的服装数据集构建神经网络.首先导入需要的库文件,x和y中保存训练集的图像和目标.x_test和y_test中保存测试集需要的图像和目标.(x, y)及(x_test, y_ ...

最新文章

  1. html,xml_网页开发_爬虫_笔记
  2. win7下设置cmd操作mysql的环境变量
  3. Python Numpy包安装
  4. 试题 F: 特别数的和 第十届蓝桥杯
  5. python html parse
  6. Python:list 和 array的对比以及转换时的注意事项
  7. 20180316 数组所占空间
  8. 初识云计算:历史、服务、架构
  9. Terry的学习笔记--ASP.NET MVC 4 HELLO WORLD添加视图(View)
  10. Zend Studio使用Xdebug调试
  11. 发现 nios2-elf-gcc 一处 bug
  12. Django生命周期,FBV,CBV
  13. SuperCard与GBA
  14. Web 3D 的初临
  15. 电容或电感的电压_为什么并联电容器可以提高功率因数,而串联不行?
  16. win10显示rpc服务器不可用,win10系统RpC服务器不可用的详细办法
  17. 【场外衍生品系列】雪球结构定价研究
  18. PRML读书笔记 第十一章 采样方法(1)
  19. 新东方雅思词汇---8.1、reckon
  20. 基于Excel数据库的Cadence元件库管理

热门文章

  1. No view found for id 0x7f0900d8
  2. 微信小程序picker 轮滑1-100的实现
  3. lambda表达式树
  4. Go 学习推荐 —(Go by example 中文版、Go 构建 Web 应用、Go 学习笔记、Golang常见错误、Go 语言四十二章经、Go 语言高级编程)
  5. JAVA面试相关基础知识
  6. [BZOJ1602] [Usaco2008 Oct] 牧场行走 (LCA)
  7. css样式之边框和内外边距
  8. js 打开窗口window.open
  9. 【转】[退役]纪念我的ACM——headacher@XDU
  10. There is no Citrix MetaFrame server configured on the specified address错误的解决方法