@[TOC]

前言

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

需求效果

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

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

项目结构

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

媒体资源

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

“隔空”画板喜迎2022(Opencv & mediapipe 手势识别应用之空手画图)
你自己去截个图,命名规则就canvas可画下载是从上到github直播平台永久回家下 依次为 0,1,2,3,approach4.jpg 注意这里的格式为jpg 然后 尺寸是 1280 x 720 这个自己注意转换一下
“隔空”画板喜迎2022(Opencv & mediapipe 手势识别应用之空手画图)

HandDetecter 工具

手势寻找

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

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

检查手指起立

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

“隔空”画板喜迎2022(Opencv & mediapipe 手势识别应用之空手画图)
他的逻辑是,首先是检查你有木有手掌出现在摄像头github开放私库上面(被检测出来)。如果有,那么就会依次从大拇指到小拇指去检查手指有没有立起来,然后直接把当前的手指头的坐标写进列表里面。 这里注意的就是我们只允许一只手在一个画面画图,这里的话你可以看到直接 return 了,所以每次拿到的就是最新出现在画面的手。

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

检查某根手指起立

“隔空”画板喜迎2022(Opencv & mediapipe 手势识别应用之空手画图)
这个就是拿到我们CheckHandUp() 返回的列表,由于我们是按照顺序来检查手指的起立情况的,所以我们直接根据索引就可以知道每个拇指的起立情况。并且我们是的顺序不管是左右手都是一样的。

Window

显示窗口

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

“隔空”画板喜迎2022(Opencv & mediapipe 手势识别应用之空手画图)
这里就是几个方法
“隔空”画板喜迎2022(Opencv & mediapipe 手势识别应用之空手画图)
下面是调用
“隔空”画板喜迎2022(Opencv & mediapipe 手势识别应用之空手画图)

UI媒体资源

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

“隔空”画板喜迎2022(Opencv & mediapipe 手势识别应用之空手画图)
Windows PrintUI()这个方法调用了嘛。

Controller

手势规则

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

“隔空”画板喜迎2022(Opencv & mediapipe 手势识别应用之空手画图)
核心appointment逻辑就是判断那个手指的位置嘛,看看有没有伸出来,然后手指的位置大概在哪。

绘图

“隔空”画板喜迎2022(Opencv & mediapipe 手势识别应用之空手画图)
“隔空”画板喜迎2022(Opencv & mediapipe 手势识别应用之空手画图)
两个方法,一个是在画板把图片画出来,一个是在我们的图片中显示。

绘图画板

这个是比较重要的逻辑部分,首先由于我们要记录上一次的绘制图形,所以我们不github下载可能直canvas2七彩的素描接在我们摄像头返回的图片上面去保存,因为那APP个背景是实时刷新的,保存不了,所以我们需要一个windows无法连接到打印机图片是静态的。

“隔空”画板喜迎2022(Opencv & mediapipe 手势识别应用之空手画图)
然后合并图application片 这个方法是在Window类canvas什么意思里面的 合并的话也是为了做了一些特殊处理,保证合并后图片不会太奇怪。
“隔空”画板喜迎2022(Opencv & mediapipe 手势识别应用之空手画图)

github中文官网网页整代码

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

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(self,img,ID):
        if (self.SourseUi):
            hui, wui, cui = self.SourseUi[ID].shape
            img[0:hui,0:wui] = self.SourseUi[ID]
            return img
        else:
            return None
    def show(self,name,img):
        self.cv.imshow(name,img)
    def addImg(self,img1,img2):
        #合并两个图片,img2为需要被合并的图片
        imgGray = self.cv.cvtColor(img2,self.cv.COLOR_BGR2GRAY) #对画布图片进行灰度处理,降低合并后的差异
        _,imgInv = self.cv.threshold(imgGray,50,255,self.cv.THRESH_BINARY_INV)
        imgInv = self.cv.cvtColor(imgInv,self.cv.COLOR_GRAY2BGR) # 处理后重新转换会彩色图片
        img1 = self.cv.bitwise_and(img1,imgInv)
        img1 = self.cv.bitwise_or(img1,img2)
        return img1
    def release(self):
        self.cap.release()
        self.cv.destroyAllWindows()
    def __str__(self):
        print("For Show img")

总结

提前祝大家新年快乐~