手势详解
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了手势详解相关的知识,希望对你有一定的参考价值。
参考技术A 1.这3个属性是作用于GestureRecognizers(手势识别)与触摸事件之间联系的属性。实际应用中好像很少会把它们放到一起,大多都只是运用手势识别,所以这3个属性应该很少会用到。2.对于触摸事件,window只会有一个控件来接收touch。这个控件是首先接触到touch的并且重写了触摸事件方法(一个即可)的控件
3.手势识别和触摸事件是两个独立的事,只是可以通过这3个属性互相影响,不要混淆。
4手势是view外部来添加 , touch是view内部处理,两个是分开,且手势优先级比touch高
UIView *view = [gestureRecognizer view];// 这个view是手势所属的view,也就是增加手势的那个view
(1)在默认情况下(即这3个属性都处于默认值的情况下)(这些属性是 手势对它自己的view!!!)
如果触摸window,首先由window上最先符合条件的控件(该控件记为hit-test
view)接收到该touch并触发触摸事件touchesBegan。同时如果某个控件的手势识别器接收到了该touch,就会进行识别。手势识别成功之后发送触摸事件touchesCancelled给hit-testview,hit-test
view不再响应touch。(即打断 往上传递的响应链条)
(2)cancelsTouchesInView:(默认yes)
默认为YES,这种情况下当手势识别器识别到touch之后,会发送touchesCancelled给hit-testview以取消hit-test view对touch的响应,这个时候只有手势识别器响应touch。
当设置成NO时,手势识别器识别到touch之后不会发送touchesCancelled给hit-test,这个时候手势识别器和hit-test view均响应touch。
(3)delaysTouchesBegan:(默认no)
默认是NO,这种情况下当发生一个touch时,手势识别器先捕捉到到touch,然后发给hit-testview,两者各自做出响应。
如果设置为YES,手势识别器在识别的过程中(注意是识别过程),不会将touch发给hit-test
view,即hit-testview不会有任何触摸事件。!!只有在识别失败之后才会将touch发给hit-testview,这种情况下hit-test
view的响应会延迟约0.15ms。
(4)delaysTouchesEnded:(默认yes)
默认为YES。这种情况下发生一个touch时,在手势识别成功后,发送给touchesCancelled消息给hit-testview,手势识别失败时,会延迟大概0.15ms,期间没有接收到别的touch才会发送touchesEnded。如果设置为NO,则不会延迟,即会立即发送touchesEnded以结束当前触摸。
1.UIGestureRecognizerStateBegan
2.UIGestureRecognizerStateChanged
3.UIGestureRecognizerStateEnded
4.UIGestureRecognizerStateCancelled
5.UIGestureRecognizerStateFailed
6.UIGestureRecognizerStateRecognized = UIGestureRecognizerStateEnded
- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer
// 询问delegate,两个手势是否同时接收消息,返回YES同事接收。返回NO,不同是接收(如果另外一个手势返回YES,则并不能保证不同时接收消息)the default implementation returns NO。
// 这个函数一般在一个手势接收者要阻止另外一个手势接收自己的消息的时候调用
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer
// 返回YES,则允许对这个touch对象审核,NO,则不允许。
// 这个方法在touchesBegan:withEvent:之前调用,为一个新的touch对象进行调用
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch
1:[tapGesture requireGestureRecognizerToFail:swipeGesture]
swipe判断失败后 才判断tap
2:(代理方法)-
(BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer
shouldRequireFailureOfGestureRecognizer:(UIGestureRecognizer
*)otherGestureRecognizer
这里返回YES,代表跟别的手势共存;如果返回NO,不一定代表不共存(可能另一个手势返回yes就可以共存,只要两个手势任一返回yes就可以)
3:-
(BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer
shouldRequireFailureOfGestureRecognizer:(UIGestureRecognizer
*)otherGestureRecognizer
另外一个手势识别fail的时候,才会识别自己
4-
(BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer
shouldBeRequiredToFailByGestureRecognizer:(UIGestureRecognizer
*)otherGestureRecognizer
我被另外一个手势变成Fail
(6)button 是用这个方法发送时间(补充)
- (void)sendAction:(SEL)action to:(id)target forEvent:(UIEvent *)event
圣诞纯情手势告白(Mediapipe基本使用&手势识别详解)
前言
本来不想写这个的,但是转了一圈国内mediapipe 的比较少教程。没有那么全面,所以这边还是记录一下吧。
环境安装
如果你是 Anconada 那么你不需要安装,但如果不是,你只需要输入以下指令
pip install mediapipe
再次之前你必须掌握 python3
pyharm 使用
opencv 基本使用
快速上手(手势捕捉)
这边我们举一个快烂大街的东西。
import mediapipe as mp
import cv2
cap = cv2.VideoCapture(0)
mpHand = mp.solutions.hands #mp的手部捕捉
Hand = mpHand.Hands() #找到图片当中的手
mphanddraw = mp.solutions.drawing_utils #绘制工具
while True:
flag,img = cap.read()
RGBImage = cv2.cvtColor(img,cv2.COLOR_BGR2RGB) #把图片进行转换
result = Hand.process(RGBImage)
if(result.multi_hand_landmarks): #如果有手,那么就会得到手的列表记录了手的坐标
for handlist in result.multi_hand_landmarks:
mphanddraw.draw_landmarks(img,handlist,mpHand.HAND_CONNECTIONS)
#绘制手的关节 HAND_CONNECTIONS 把点连接起来
cv2.imshow("Hands",img)
if(cv2.waitKey(1)==ord("q")):
break
cap.release()
cv2.destroyAllWindows()
注释应该说得很清楚了,这里就还不阐述了。
获取手的坐标
从刚才的效果你可以发现,handlist 包含了我们的一只手的完整坐标,并且可以绘制21个点
那么事实上 handlist 就是21个点的编号和坐标(这个坐标是按照百分比来算的),每个点对应下面的图
所以我们可以非常清楚的去捕捉到我们手的状态。
接下来我们来对我们的手进行重新绘制。
import mediapipe as mp
import cv2
cap = cv2.VideoCapture(0)
mpHand = mp.solutions.hands #mp的手部捕捉
Hand = mpHand.Hands() #找到图片当中的手
mphanddraw = mp.solutions.drawing_utils #绘制工具
while True:
flag,img = cap.read()
RGBImage = cv2.cvtColor(img,cv2.COLOR_BGR2RGB) #把图片进行转换
result = Hand.process(RGBImage)
if(result.multi_hand_landmarks): #如果有手,那么就会得到手的列表记录了手的坐标
for handlist in result.multi_hand_landmarks:
for id,lm in enumerate(handlist.landmark):
h,w,c = img.shape
cx,cy = int(lm.x * w),int(lm.y * h)
cv2.circle(img,(cx,cy),15,(255,0,255),cv2.FILLED)
mphanddraw.draw_landmarks(img,handlist,mpHand.HAND_CONNECTIONS,)
#绘制手的关节 HAND_CONNECTIONS 把点连接起来
cv2.imshow("Hands",img)
if(cv2.waitKey(1)==ord("q")):
break
cap.release()
cv2.destroyAllWindows()
之后的话我们可以对代码进行优化,我们知道我们可以直接获取一个手掌的每一个关节的坐标,在我们的图片上,这样一来我们就可以对我们手的姿态进行预测,判断。这样一来就好玩了。
例如这样:
import mediapipe as mp
import cv2
import math
cap = cv2.VideoCapture(0)
cap.set(3,1280)
cap.set(4,720)
mpHand = mp.solutions.hands #mp的手部捕捉
Hand = mpHand.Hands() #找到图片当中的手
mphanddraw = mp.solutions.drawing_utils #绘制工具
while True:
flag,img = cap.read()
RGBImage = cv2.cvtColor(img,cv2.COLOR_BGR2RGB) #把图片进行转换
result = Hand.process(RGBImage)
if(result.multi_hand_landmarks): #如果有手,那么就会得到手的列表记录了手的坐标
hands_data = result.multi_hand_landmarks
for handlist in hands_data:
h, w, c = img.shape
shizhi_postion = (int(handlist.landmark[8].x*w),int(handlist.landmark[8].y*h))
muzhi_postion = (int(handlist.landmark[4].x * w), int(handlist.landmark[4].y * h))
cv2.line(img, muzhi_postion, shizhi_postion, (255, 0, 0), 5)
location = int(shizhi_postion[0]-muzhi_postion[0])**2\\
+ int(shizhi_postion[1]-muzhi_postion[1])**2
location = int(math.sqrt(location))
showpostion = (int(((muzhi_postion[0]+shizhi_postion[0])/2)) ,int(((muzhi_postion[1]+shizhi_postion[1])/2)))
cv2.putText(img, str(location), showpostion, cv2.FONT_HERSHEY_PLAIN, 1, (255, 0, 255), 1)
for id,lm in enumerate(handlist.landmark):
cx,cy = int(lm.x * w),int(lm.y * h)
# 我们标注出大拇指和食指的距离
cv2.circle(img,(cx,cy),15,(255,0,255),cv2.FILLED)
cv2.putText(img,str(id),(cx,cy),cv2.FONT_HERSHEY_PLAIN,1,(0,255,255),1)
mphanddraw.draw_landmarks(img,handlist,mpHand.HAND_CONNECTIONS,)
#绘制手的关节 HAND_CONNECTIONS 把点连接起来
cv2.imshow("Hands",img)
if(cv2.waitKey(1)==ord("q")):
break
cap.release()
cv2.destroyAllWindows()
所以就好玩了。
返回参数详解
这个返回参数很重要,看前面的例子是吧。
首先 hands_data 是多只手的总体坐标,几只手在屏幕上,那么 len()就是几
handlist 是包含了一只手的21个点
handlist 包含了 [点1,点2…]
所以么获取 拇指头就是 handlist.landmark[4].x * w
所以就ok了。
不同算子
这个其实我也不太好说那玩意叫啥,这里的话我先叫他算子。
那么这个有啥用呢,其实就是使用不同的算法模型,来帮助我们提取不同的特征,以便处理。
这里可以看到这里有很多不同的算子。
调用方式都是类似的,但是处理的方式可能有些许不同。
当然我们这里还是先探讨关于 hands这个东西改怎么玩。后面我们再来说说其他的玩意,到本系列最后说不定可以做一个手势系统,用我们身体的姿态来控制电脑,例如我们可以使用我们的手来充当我们的鼠标(右手)等等,那玩意应该会比较酷!或者我们结合VR游戏驱动,我们将不需要游戏手柄,只需要一个像素良好的摄像头,并且由于 mediapipe 是直接在CPU当中运行的,这就意味着我们可以在没有显卡 GPU 的设备运行,Google官方也说过这玩意可以用到移动端,linux等。
ok 继续,关于前面的代码,我们其实已经可以来做一个简单的音量控制器了,我们只需要对手指间距进行换算即可。不够这个任然不是我们的主题,我们今天的主题其实是如何识别出我们的手势,例如识别
1 ~ 5。
手势识别案例
OK ,那么接下来开始我们的案例,这个案例其实就是
Opencv 快速使用(基础使用&手势识别)
的
部分,当时这个部分是直接copy的,结果发现这哥们的其实是抄我发的那个视频的代码(吐槽一波写的真烂,有很严重的 OOM 问题。所以我打算自己写一个算了,反正这个挺好玩的)
手指状态判断
首先我们不难发现其实,其实对比官方给出的手指图片以及我前面的那个手指的图片。我们只需要对比关节的坐标就可以判断手有没有立起来。
相当直观是吧。
但是这里其实还有个问题,那就是大拇指的问题。
由于大拇指太 短了 所以没办法这样直接判断,所以我们需要去使用x 的坐标来判断我们的手。但是这样一来又出现问题了,那就是我们左右手的构造有点不一样。
所以我们有时候得去判断一下我们那个手是左手还是右手
判断左右手的方式相当简单,看 1 5 号 X 的相对位置就好了。但是还有个问题那就是手心手背的问题但是其实手心手背的投影和那个左右手的投影是一样的。
编码
import mediapipe as mp
import cv2
import math
cap = cv2.VideoCapture(0)
mpHand = mp.solutions.hands #mp的手部捕捉
Hand = mpHand.Hands() #找到图片当中的手
mphanddraw = mp.solutions.drawing_utils #绘制工具
TipsId = [4,8,12,16,20] #定点的坐标
while True:
flag,img = cap.read()
RGBImage = cv2.cvtColor(img,cv2.COLOR_BGR2RGB) #把图片进行转换
result = Hand.process(RGBImage)
if(result.multi_hand_landmarks): #如果有手,那么就会得到手的列表记录了手的坐标
hands_data = result.multi_hand_landmarks
for handlist in hands_data:
h, w, c = img.shape
fingers = []
#判断大拇指的开关
if(handlist.landmark[TipsId[0]-3].x < handlist.landmark[TipsId[0]+1].x):
if handlist.landmark[TipsId[0]].x > handlist.landmark[TipsId[0]-1].x:
fingers.append(0)
else:
fingers.append(1)
else:
if handlist.landmark[TipsId[0]].x < handlist.landmark[TipsId[0] - 1].x:
fingers.append(0)
else:
fingers.append(1)
# 判断其他手指
for id in range(1,5):
if(handlist.landmark[TipsId[id]].y > handlist.landmark[TipsId[id]-2].y):
fingers.append(0)
else:
fingers.append(1)
# 获得手指个数
totoalfingle = fingers.count(1)
cv2.putText(img,str(totoalfingle),(50,50),cv2.FONT_HERSHEY_PLAIN,
5,(255,255,255),5)
# 这个只是绘制手指关节的,可以忽略这段代码
for id,lm in enumerate(handlist.landmark):
cx,cy = int(lm.x * w),int(lm.y * h)
cv2.circle(img,(cx,cy),15,(255,0,255),cv2.FILLED)
cv2.putText(img,str(id),(cx,cy),cv2.FONT_HERSHEY_PLAIN,1,(0,255,255),1)
mphanddraw.draw_landmarks(img,handlist,mpHand.HAND_CONNECTIONS,)
cv2.imshow("Hands",img)
if(cv2.waitKey(1)==ord("q")):
break
cap.release()
cv2.destroyAllWindows()
效果
升级版(圣诞表白器)
接下来到此我们应该就已经结束了,但是突然想起来那啥,我们其实不仅可以显示文字在我们的文字,还可以叠加图片,
SO MAYBE IT CAN WORK SOME INTERESTING THINGS SUCH AS
CONFESS TO SOMEBODY WHICH I CAN NOT USE IT RIGHT NOW!
FUCK,叫我狗粮制造机,谢谢!
效果
代码
import mediapipe as mp
import cv2
import os
cap = cv2.VideoCapture(0)
cap.set(3,1280)
cap.set(4,720)
mpHand = mp.solutions.hands #mp的手部捕捉
Hand = mpHand.Hands() #找到图片当中的手
mphanddraw = mp.solutions.drawing_utils #绘制工具
MediaPath = "Media"
picsdir = os.listdir(MediaPath)
pics = []
for pic in picsdir:
img = cv2.imread(f"MediaPath/pic")
pics.append(img)
TipsId = [4,8,12,16,20] #定点的坐标
while True:
flag,img = cap.read()
RGBImage = cv2.cvtColor(img,cv2.COLOR_BGR2RGB) #把图片进行转换
result = Hand.process(RGBImage)
if(result.multi_hand_landmarks): #如果有手,那么就会得到手的列表记录了手的坐标
hands_data = result.multi_hand_landmarks
for handlist in hands_data:
h, w, c = img.shape
fingers = []
#判断大拇指的开关
if(handlist.landmark[TipsId[0]-2].x < handlist.landmark[TipsId[0]+1].x):
if handlist.landmark[TipsId[0]].x > handlist.landmark[TipsId[0]-1].x:
fingers.append(0)
else:
fingers.append(1)
else:
if handlist.landmark[TipsId[0]].x < handlist.landmark[TipsId[0] - 1].x:
fingers.append(0)
else:
fingers.append(1)
# 判断其他手指
for id in range(1,5):
if(handlist.landmark[TipsId[id]].y > handlist.landmark[TipsId[id]-2].y):
fingers.append(0)
else:
fingers.append(1)
# 获得手指个数,绘制图片
totoalfingle = fingers.count(1)
coverpic = pics[totoalfingle-1]
hc, wc, cc = coverpic.shape
img[0:wc,0:hc] = coverpic
# 这个只是绘制手指关节的,可以忽略这段代码
for id,lm in enumerate(handlist.landmark):
cx,cy = int(lm.x * w),int(lm.y * h)
cv2.circle(img,(cx,cy),15,(255,0,255),cv2.FILLED)
mphanddraw.draw_landmarks(img,handlist,mpHand.HAND_CONNECTIONS,)
cv2.imshow("Hands",img)
if(cv2.waitKey(1)==ord("q")):
break
cap.release()
cv2.destroyAllWindows()
图片自己准备去(如果有人表白成功了记得踹我)
总结
最基本的使用其实就是这样的,后面你想怎么架构,就怎么架构,这个无所谓,按照这个模板来套就可以了。
以上是关于手势详解的主要内容,如果未能解决你的问题,请参考以下文章
圣诞纯情手势告白(Mediapipe基本使用&手势识别详解)