“隔空“”画板喜迎2022(Opencv & mediapipe 手势识别应用之空手画图)

Posted HUTEROX

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了“隔空“”画板喜迎2022(Opencv & mediapipe 手势识别应用之空手画图)相关的知识,希望对你有一定的参考价值。

前言

在熬夜搞完计组实验报告后,我Huterox又又回来了。那么今天其实是对上一篇博客
圣诞纯情手势告白(Mediapipe基本使用&手势识别详解)再次进阶(不会真的有人以为这篇博客是写来向妹纸表白的吧,开玩笑里面都是干货呀!)那么同样的里面有很多原理在今天的博文中都是在那篇博客有写到的,怎么玩的都说的很清楚了。那么现在我要做的其实就只是对我上次写的最原始的代码进行封装就好了,然后加一点功能。

需求效果

这个直接看下面的效果就知道了

项目结构

媒体资源

这里我要说一下的是那个媒体资源,由于这个Demo比较简单我就没上传 github ,也不想上传百度云盘了。那就这样,就是这张图片

你自己去截个图,命名规则就是从上到下 依次为 0,1,2,3,4.jpg 注意这里的格式为jpg
然后 尺寸是 1280 x 720 这个自己注意转换一下

HandDetecter 工具

手势寻找

接下来我就慢慢来说说这个每一个工具干嘛的,有哪些玩意。
首先手势寻找那就是 调用算子嘛

检查手指起立

接下来由于我们需要跟踪我们的食指,和中指,所以我们需要得到那些竖起来的 食指。那么这个检查的话也很简单,我们在开头说的那篇博客里面已经说了原理,这里我就不复述了。这里说明一下的是,我们这个返回的参数。

他的逻辑是,首先是检查你有木有手掌出现在摄像头上面(被检测出来)。如果有,那么就会依次从大拇指到小拇指去检查手指有没有立起来,然后直接把当前的手指头的坐标写进列表里面。
这里注意的就是我们只允许一只手在一个画面画图,这里的话你可以看到直接 return 了,所以每次拿到的就是最新出现在画面的手。

flag 是有木有立起来
x 是 x坐标
y 是 y坐标

检查某根手指起立


这个就是拿到我们CheckHandUp() 返回的列表,由于我们是按照顺序来检查手指的起立情况的,所以我们直接根据索引就可以知道每个拇指的起立情况。并且我们是的顺序不管是左右手都是一样的。

Window

显示窗口

这个就是显示我们整个的窗口,也就是这个界面:

这里就是几个方法

下面是调用

UI媒体资源

这个就是先前那个说的UI资源嘛。

Windows PrintUI()这个方法调用了嘛。

Controller

手势规则

这个就是规定我们的手势怎么放嘛,首先
食指和中指一起是模式现在,不画画。只有一个食指伸出来那就去画画


核心逻辑就是判断那个手指的位置嘛,看看有没有伸出来,然后手指的位置大概在哪。

绘图



两个方法,一个是在画板把图片画出来,一个是在我们的图片中显示。

绘图画板

这个是比较重要的逻辑部分,首先由于我们要记录上一次的绘制图形,所以我们不可能直接在我们摄像头返回的图片上面去保存,因为那个背景是实时刷新的,保存不了,所以我们需要一个图片是静态的。

然后合并图片
这个方法是在Window类里面的
合并的话也是为了做了一些特殊处理,保证合并后图片不会太奇怪。

完整代码

基本的项目解释部分就OK了,那么接下来依次上代码

ConterCenter.py

from Utils.HandDetecter import HandDetected
from Utils.SourseDetecter import SourseDetecter
from Utils.Window import Window
import numpy
class Controller(object):
    def __init__(self):
        self.HandDetected = HandDetected()
        self.window_w = 1300
        self.window_h = 720
        self.Window = Window(width=self.window_w,height=self.window_h)
        self.SourseDetecter = SourseDetecter()
        self.ID = 0
        self.PenColor = (0, 0, 255) #默认颜色
        self.PenPostion = (0,0)
        self.Canvas = numpy.zeros((self.window_h,self.window_w,3),numpy.uint8)
        self.PenSize = 15

    def ControllerFingerTrack(self,result,img):
        # 我们负责跟踪食指和中指
        postions = self.HandDetected.CheckHandUp(result)
        if(postions):
            indexFinger = postions[self.HandDetected.INDEXFINGER]
            middleFinger = postions[self.HandDetected.MIDDLEFINGER]

            if(indexFinger.get('flag') and middleFinger.get('flag') ):
                #食指,中指立起来了开始检测是否选择功能
                indexFinger_x ,indexFinger_y =int( indexFinger.get("x")*self.window_w),int (indexFinger.get("y")*self.window_h )
                middleFinger_x,middleFinger_y = int( middleFinger.get("x")*self.window_w),int(middleFinger.get("y")*self.window_h )

                center_x = int((indexFinger_x+middleFinger_x)/2)
                center_y = int((indexFinger_y+middleFinger_y)/2)

                self.Window.cv.circle(img, (center_x,center_y), 35, self.PenColor, self.Window.cv.FILLED)

                #开始具体的功能模块
                if(0<=center_y and center_y<=125):
                    if(center_x>=125 and center_x<=260):
                        self.ID = 1
                        self.PenSize = 15
                        self.PenColor=(0,0,255)
                    if(center_x>=365 and center_x<=505):
                        self.ID = 2
                        self.PenSize = 15
                        self.PenColor = (255,0,0)
                    if(center_x>=615 and center_x<=755):
                        self.ID = 3
                        self.PenSize=15
                        self.PenColor=(0,255,0)
                    if(center_x>=1065 and center_x<=1205):
                        self.ID = 4
                        self.PenSize= 30
                        self.PenColor=(0,0,0)

        return img


    def DrawPenController(self,result,img):
        # 开始绘制图形
        postions = self.HandDetected.CheckHandUp(result)
        h, w, c = img.shape
        if (postions):
            indexFinger = postions[self.HandDetected.INDEXFINGER]
            middleFinger = postions[self.HandDetected.MIDDLEFINGER]


            if (indexFinger.get('flag') and not middleFinger.get('flag')):
                indexFinger_x, indexFinger_y = int(indexFinger.get("x") * self.window_w), int(
                    indexFinger.get("y") * self.window_h)
                self.Window.cv.circle(img, (indexFinger_x, indexFinger_y), self.PenSize, self.PenColor, self.Window.cv.FILLED)

                if (self.PenPostion == (0,0)):
                    self.PenPostion = (indexFinger_x,indexFinger_y)

                self.Window.cv.line(self.Canvas,self.PenPostion,(indexFinger_x,indexFinger_y),self.PenColor,self.PenSize)
                self.PenPostion = (indexFinger_x,indexFinger_y)

            else:
                self.PenPostion = (0, 0)

        return img


    def ShowDrawWay(self,img):
        h, w, c = img.shape
        self.Canvas = self.Window.cv.resize(self.Canvas, (w, h))
        img = self.Window.addImg(img, self.Canvas)
        # img = self.Window.cv.addWeighted(img,0.5,self.Canvas,0.5,0)
        return img

    def Main(self):

        while True:

            flag,img =  self.Window.cap.read()
            img = self.Window.cv.flip(img, 1)
            result,img = self.HandDetected.findHands(img,True)


            img = self.ControllerFingerTrack(result,img)

            img = self.DrawPenController(result,img)
            img = self.ShowDrawWay(img)
            img = self.Window.PrintUi(img, self.ID)

            self.Window.show("Canvas",img)
            if(self.Window.cv.waitKey(1)==ord("q")):
                return


if __name__=="__main__":
    Controller = Controller()
    Controller.Main()


HandDetecter.py

import mediapipe as mp
import cv2
import os
from Utils.SourseDetecter import SourseDetecter
class HandDetected(object):
    def __init__(self):
        self.mpHand = mp.solutions.hands
        self.Hand = self.mpHand.Hands()
        self.mphanddraw = mp.solutions.drawing_utils
        self.THUMB = 0
        self.INDEXFINGER=1
        self.MIDDLEFINGER=2
        self.RIGHTFINGER=3
        self.PINKY=4


    def findHands(self,img,flag=False,name="Hands"):
        #你只需要将从摄像头获取的图片传入这个函数即可
        rgbimg = cv2.cvtColor(img,cv2.COLOR_BGR2RGB)
        result = self.Hand.process(rgbimg)
        if(flag):
            self.MarkHands(result,img,name)
        return result,img

    def GetHandPoints(self,result):
        pass

    def FindUPFings(self,result,ID):
        #改方法适合一次性查看一个手指的状况,不适合同时跟踪多个手指
        if(result.multi_hand_landmarks):
            fingers = self.CheckHandUp(result)
            if(fingers[ID].get('flag')):
                return fingers[ID]

    def CheckHandUp(self,result):

        TipsId = [4, 8, 12, 16, 20]  # 定点的坐标
        hands_data = result.multi_hand_landmarks
        if (hands_data):
            for handlist in hands_data:
                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:
                        data = "flag":0,"x":int(handlist.landmark[TipsId[0]].x),"y":handlist.landmark[TipsId[0]].y
                        fingers.append(data)
                    else:
                        data = "flag": 1, "x": int(handlist.landmark[TipsId[0]].x), "y": handlist.landmark[TipsId[0]].y
                        fingers.append(data)
                else:
                    if handlist.landmark[TipsId[0]].x < handlist.landmark[TipsId[0] - 1].x:
                        data = "flag": 0, "x": handlist.landmark[TipsId[0]].x, "y": handlist.landmark[TipsId[0]].y
                        fingers.append(data)
                    else:
                        data = "flag": 1, "x": handlist.landmark[TipsId[0]].x, "y": handlist.landmark[TipsId[0]].y
                        fingers.append(data)
                # 判断其他手指
                for id in range(1,5):
                    if(handlist.landmark[TipsId[id]].y > handlist.landmark[TipsId[id]-2].y):
                        data = "flag": 0, "x": handlist.landmark[TipsId[id]].x, "y": handlist.landmark[TipsId[id]].y
                        fingers.append(data)
                    else:
                        data = "flag": 1, "x": handlist.landmark[TipsId[id]].x,
                                "y": handlist.landmark[TipsId[id]].y
                        fingers.append(data)


                return fingers
        else:
            return None


    def MarkHands(self,result,img,name):
        #是否对检测到的手部进行标注,之后返回标注好的图片
        hands_data = result.multi_hand_landmarks
        if (hands_data):

            for handlm in hands_data:
                h, w, c = img.shape
                self.mphanddraw.draw_landmarks(img, handlm, self.mpHand.HAND_CONNECTIONS, )



    def __str__(self):
        print("HandDetected for Identify Hands")

if __name__=="__main__":
    pass

SourseDetecter.py

import os
import cv2
import time

class SourseDetecter(object):
    def __init__(self):
        pass

    @staticmethod
    def GetUiImgs(path):
        if(os.path.exists(path)):
            imgs = os.listdir(path)
            imgFiles = []
            for img in imgs:

                imgFile = cv2.imread(f"path/img")

                imgFiles.append(imgFile)

            return imgFiles

        else:

            raise FileNotFoundError("Not is File")
if __name__=="__main__":

    root_path = os.path.abspath(os.path.dirname(os.path.dirname(__file__)))+"\\\\"
    img2 = SourseDetecter.GetUiImgs(root_path+"Media")[0]

Window.py

import cv2
import os
from Utils.SourseDetecter import SourseDetecter
class Window(object):
    def __init__(self,sourse=0,width = 640,height = 480):

        self.cv = cv2
        self.cap = self.cv.VideoCapture(sourse)
        self.cap.set(3,width)
        self.cap.set(4,height)
        self.SourseUi=[]
        root_path = os.path.abspath(os.path.dirname(os.path.dirname(__file__))) + "\\\\"
        try:
            self.SourseUi = SourseDetecter.GetUiImgs(root_path+"Media")
        except Exception as e:
            self.SourseUi = []
            print(e)


    def PrintUi以上是关于“隔空“”画板喜迎2022(Opencv & mediapipe 手势识别应用之空手画图)的主要内容,如果未能解决你的问题,请参考以下文章

隔空打印 ipp lpd区别

隔空投送为啥显示真实姓名

苹果和安卓如何隔空传照片

iOS系统“隔空投送”功能无法正常使用的解决办法!

基于Python的这个库,我实现了“隔空操物“

基于Python的这个库,我实现了“隔空操物“