【AI十行代码系列】1.手势关键点追踪-MediaPipe Python

【TLAIP系列简介】Ten-Lines-AI-Projects

         现阶段有非常多优秀的开源的AI工程,为了有更好的可扩展性,接口往往设计得十分复杂,这需要投入一定的时间和精力来处理,对于不熟悉或者刚入门的小伙伴,这可能需要花很久。本系列设计的出发点也很纯粹,进一步降低小伙伴们挑选和使用轮子的时间,让更多小伙伴能更快地验证算法效果,算法满足效果了再扒开看看,不满意直接看下一个。有任何问题和建议欢迎随时评论,目前系列里的十行代码会按照下列方式进行:

import  关键py
1.定义好输入
2.网络初始化配置
3.开始跑并输出结果
4.可视化结果

        包含空格和一些必要的说明,估计正好十行。哈哈哈哈(不能再少了,再少我的强迫症就不能忍受了)。

        如果遇到什么很牛逼的AI工程或者自己不想跑的但是一眼看上去效果很好的工程,欢迎留言交流哈,有空可以一并整理到这个工程,一键可运行,无繁杂使用过程。


【正文】

        本文的主角是谷歌大名鼎鼎的MediaPipe,其中最出圈的就是手势追踪功能,市面上见到的非常多手势交互的应用都是基于谷歌这个仓库做的。当然,后续的仓库和文章中也会更新其他AI功能。惯例先放结果(完整仓库代码和测试视频、图片请直接拖到最后):

十行关键代码

正好十行,一行都不多。

from utils.hand_tracking_mediapipe import InputData, InitHandTracking, ShowResult

# 初始化输入源, file支持数字(相机)以及视频文件路径,图片路径或文件夹路径
input_data = InputData(file="test/hand_tracking.mp4") # InputData() #默认调用相机  # InputData(file="test/imgs")
# 初始化手势追踪tracker
hand_track = InitHandTracking(use_static_mode = input_data.use_img_list)
# 获取图像以及结果的generator
run_hand_tracking_result = hand_track.run_hand_tracking(input_data.get_next_img())
# 显示结果, ESC退出,图片模式按任意键继续
ShowResult(input_data.wait_key).show_result(run_hand_tracking_result)

InputData用来处理各种输入,目前兼容下列各种情况的输入(是不是很良心):

  • 系统相机,输入相机编号即可,例如 InputData(file=0)
  • 一个视频文件,输入视频路径即可,例如 InputData(file="test/hand_tracking.mp4")
  • 一张图片,输入图片路径即可,例如 InputData(file="test/imgs/0.jpg")
  • 一个包含很多图像的目录,输入目录路径即可,例如 InputData(file="test/imgs")

InitHandTracking用来初始化网络的参数,主要参数如下:

  • use_static_mode 图片模型还是视频模式,如果是图片,会先运行检测算法,否则直接追踪
  • hand_num 最多同时追踪的手的数量
  • detect_conf 检测网络的置信度(默认0.5就行,一般不用改)
  • track_conf 追踪网络的置信度(默认0.5就行,一般不用改)

ShowResult用来可视化结果

  • waitkey_mode 默认1,会刷新显示视频;输入0的话会阻塞图像显示,按下任意键继续

环境配置和使用说明

配置conda(可选)

conda create -n mediapipe python=3.6
conda activate mediapipe

安装必要的依赖包

pip install opencv-python
pip install mediapipe==0.8.3

配置好环境后,直接运行 “运行这个.py" 即可 

功能代码剖析

        Mediepipe封装地非常好了,实际封装下来代码量不大,都粘上来给大家看看(可以直接跳到最后下载源码及测试视频),后续系列代码量比较大的时候可能就不放全了。

必要的库

import os

import cv2
import mediapipe as mp

输入模块InputData

        这款大家可以给点建议,看看有没有要补充的,目前把我自己常用的几种数据输入模式都兼容了。图片、图片文件夹、视频、相机输入有兴趣看看代码。

class InputData:
    def __init__(self, file=0):
        self.cap = None
        self.img_list = []
        self.img_id = 0
        self.img_type_list = {'jpg', 'bmp', 'png', 'jpeg', 'rgb', 'tif', 'webp'}
        self.deal_with_input(file)
        self.use_img_list = len(self.img_list) > 0
        self.wait_key = 0 if self.use_img_list else 1
        self.use_static_mode = self.use_img_list

    def gen_img_list(self, path):
        for item in os.listdir(path):
            if item.split(".")[-1] in self.img_type_list:
                self.img_list.append(os.path.join(path, item))

    def deal_with_input(self, file):
        path_valid = False
        if isinstance(file, int):
            # use camera
            self.cap = cv2.VideoCapture(file)
            path_valid = True
        elif isinstance(file, str):
            if os.path.isdir(file):
                # use img list
                self.gen_img_list(file)
                if len(self.img_list) > 0:
                    path_valid = True
                else:
                    print("no images in", file)
            elif os.path.isfile(file):
                if file.split(".")[-1] in self.img_type_list:
                    # only one image
                    self.img_list.append(file)
                    path_valid = True
                else:
                    self.cap = cv2.VideoCapture(file)
                    if self.cap.isOpened():
                        print("video path is", file)
                        path_valid = True
                    else:
                        print("video path is not valid, path is:", file)
        if not path_valid:
            print("Invalid input! Use camera 0 instead!")
            self.cap = cv2.VideoCapture(0)

    def get_next_img(self):
        if self.use_img_list:
            while self.img_id < len(self.img_list):
                img = cv2.imread(self.img_list[self.img_id])
                self.img_id += 1
                if img is not None:
                    yield img
            yield None
        else:
            while True:
                _, img = self.cap.read()
                if img is None:
                    yield None
                    break
                yield img

    def release_cap(self):
        if self.cap:
            self.cap.release()

网络相关的功能模块InitHandTracking

        MediePipe封装得非常好了,网络相关的代码非常简洁,配置好手的数量和模式就行了。其他模式都很直观,use_static_mode这里多说两句:

        Mediepipe本质上是一个tracking by detection的框架,检测到手后都一直运行关键点追踪即可.所以这里use_static_mode如果设置为True,输入的图像会跑一次手势检测再运行一次关键点追踪;如果设置为False,后一张图像会基于前一张图像的结果进行追踪,前一张的手追踪丢失时才会运行检测算法。

class InitHandTracking:
    def __init__(self, use_static_mode=False, hand_num=2, detect_conf=0.5, track_conf=0.5):
        self.use_static_mode = use_static_mode
        self.hand_num = hand_num
        self.detect_conf = detect_conf
        self.track_conf = track_conf
        self.hands = None
        self.init_network()

    def init_network(self):
        self.hands = mp.solutions.hands.Hands(
            static_image_mode=self.use_static_mode,
            max_num_hands=self.hand_num,
            min_detection_confidence=self.detect_conf,
            min_tracking_confidence=self.track_conf
        )
        print("init hand tracking down")

    def run_hand_tracking(self, get_nex_img):
        while True:
            img_origin = next(get_nex_img)
            if img_origin is None:
                yield [None, None]
                break
            img = cv2.cvtColor(img_origin, cv2.COLOR_BGR2RGB)
            yield [img_origin, self.hands.process(img)]

可视化相关的模块ShowResult

        可视化这块直接复用的MediaPipe自带的绘制模块,大家有需求我也可以改下,比如标个号什么的?233

class ShowResult:
    def __init__(self, waitkey_mode=1):
        self.waitkey = waitkey_mode

    def show_result(self, run_hand_tracking):
        while True:
            img, results = next(run_hand_tracking)
            if img is None:
                break
            if results.multi_hand_landmarks:
                for hand_landmarks in results.multi_hand_landmarks:
                    mp.solutions.drawing_utils.draw_landmarks(
                        img,
                        hand_landmarks,
                        mp.solutions.hands.HAND_CONNECTIONS)
                # Flip the image horizontally for a selfie-view display.
            cv2.imshow('MediaPipe-Hands', img)
            if cv2.waitKey(self.waitkey) & 0xFF == 27:
                break

实际运行结果及简单点评

配置下输入InputData(file="test/hand_tracking.mp4"),直接可以看到效果:

 做手势算法也挺久了,简单点评下MediePipe手势追踪的优缺点吧:

优点

  • 五分钟上手能看到效果,十分钟就能入门,支持python、C++甚至JS
  • 支持安卓、IOS、和PC
  • 常用和简单手势关键点回归效果尚可,能达到做Demo的程度
  • 基于关键点识别类别就非常容易了,相信你们自己也能完成(要是不想做,抽空我也可以水一篇)
  • 开源的手势里面比这效果好的也有,但肯定没这个好用,很适合入门

缺点

  • 关键点精度还不够高,不少姿态会稳定出错,抖动感挺明显
  • 在某些姿态下关键点很容易出错,甚至”抖成一团“
  • 目前来看还远达不到自然交互的程度

拓展阅读

如果看了效果想进一步尝试,可以仔细研究下面两个链接

写在最后

AI十行代码仓库地址(最重要的→)链接 ,后续功能也会在这里更新,欢迎提建议或需求,哈哈,本文相关所有代码和资源也可以在这里下载

如果文章对你稍微有些帮助的话,麻烦来个三连(疯狂暗示)。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

朱铭德

五毛也是爱٩(●´৺`●)૭

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值