使用OpenCV和MediaPipe实现姿态识别!

news2024/12/24 11:24:36

081b94d788bd594d88f1be089f3b635e.gif

大家好,我是小F~

MediaPipe是一款由Google开发并开源的数据流处理机器学习应用开发框架。

它是一个基于图的数据处理管线,用于构建使用了多种形式的数据源,如视频、音频、传感器数据以及任何时间序列数据。

MediaPipe通过将各个感知模型抽象为模块并将其连接到可维护的图中来解决这些问题。

项目地址:

https://github.com/google/mediapipe

今天小F就给大家介绍一下,如何使用MediaPipe实现姿态识别!

ff8827cab41419601df2c841eb13bf57.png

通过这项技术,我们可以结合摄像头,智能识别人的行为,然后做出一些处理。

比如控制电脑音量,俯卧撑计数,坐姿矫正等功能。

/ 01 /

依赖安装

使用的Python版本是3.9.7。

需要安装以下依赖。

mediapipe==0.9.2.1
numpy==1.23.5
opencv-python==4.7.0.72

使用pip命令进行安装,环境配置好后,就可以来看姿态识别的情况了。

有三种,包含全身、脸部、手部的姿态估计。

/ 02 /

全身姿态估计

首先是人体姿态估计,一次只能跟踪一个人。

并且会在人的身体上显示33个对应的坐标点。

具体代码如下。

import os
import time
import cv2 as cv
import mediapipe as mp


class BodyPoseDetect:
    def __init__(self, static_image=False, complexity=1, smooth_lm=True, segmentation=False, smooth_sm=True, detect_conf=0.5, track_conf=0.5):
        self.mp_body = mp.solutions.pose
        self.mp_draw = mp.solutions.drawing_utils
        self.body = self.mp_body.Pose(static_image, complexity, smooth_lm, segmentation, smooth_sm, detect_conf, track_conf)

    def detect_landmarks(self, img, disp=True):
        img_rgb = cv.cvtColor(img, cv.COLOR_BGR2RGB)
        results = self.body.process(img_rgb)
        detected_landmarks = results.pose_landmarks

        if detected_landmarks:
            if disp:
                self.mp_draw.draw_landmarks(img, detected_landmarks, self.mp_body.POSE_CONNECTIONS)
        return detected_landmarks, img

    def get_info(self, detected_landmarks, img_dims):
        lm_list = []
        if not detected_landmarks:
            return lm_list

        height, width = img_dims
        for id, b_landmark in enumerate(detected_landmarks.landmark):
            cord_x, cord_y = int(b_landmark.x * width), int(b_landmark.y * height)
            lm_list.append([id, cord_x, cord_y])

        return lm_list


def main(path, is_image):
    if is_image:
        detector = BodyPoseDetect(static_image=True)
        ori_img = cv.imread(path)

        img = ori_img.copy()
        landmarks, output_img = detector.detect_landmarks(img)
        info_landmarks = detector.get_info(landmarks, img.shape[:2])
        # print(info_landmarks[3])

        cv.imshow("Original", ori_img)
        cv.imshow("Detection", output_img)
        cv.waitKey(0)

    else:
        detector = BodyPoseDetect()
        cap = cv.VideoCapture(path)
        prev_time = time.time()
        cur_time = 0

        frame_width = int(cap.get(3))
        frame_height = int(cap.get(4))
        out = cv.VideoWriter('output.avi', cv.VideoWriter_fourcc('M', 'J', 'P', 'G'), 10, (frame_width, frame_height))  # 保存视频

        while True:
            ret, frame = cap.read()
            if not ret:
                print("Video Over")
                break

            img = frame.copy()
            landmarks, output_img = detector.detect_landmarks(img)
            info_landmarks = detector.get_info(landmarks, img.shape[:2])

            cur_time = time.time()
            fps = 1/(cur_time - prev_time)
            prev_time = cur_time
            cv.putText(output_img, f'FPS: {str(int(fps))}', (10, 70), cv.FONT_HERSHEY_COMPLEX_SMALL, 2, (0, 50, 170), 2)

            cv.namedWindow('Original', cv.WINDOW_NORMAL)  # 窗口大小可设置
            cv.resizeWindow('Original', 580, 330)  # 重设大小
            cv.namedWindow('Detection', cv.WINDOW_NORMAL)  # 窗口大小可设置
            cv.resizeWindow('Detection', 580, 330)  # 重设大小

            out.write(output_img)

            cv.imshow("Original", frame)
            cv.imshow("Detection", output_img)
            if cv.waitKey(1) & 0xFF == ord('q'):
                break

        cap.release()

    cv.destroyAllWindows()


if __name__ == "__main__":

    # is_image = True
    # media_path = '.\\Data\\Images\\running.jpg'
    is_image = False
    media_path = '.\\Data\\Videos\\basketball.mp4'

    if os.path.exists(os.path.join(os.getcwd(), media_path)):
        main(media_path, is_image)
    else:
        print("Invalid Path")

运行代码后,结果如下。

8cf7319c94fe06eba1118ada2643bfe2.png

左侧是原图,右侧是检测结果。

其中代码里的is_image参数表示是否为图片或视频

media_path参数则表示的是源文件的地址。

我们还可以看视频的检测效果,具体如下。

f4b36413d5d00f8b75ee6f3a330acc3c.gif

效果还不错。

/ 03 /

脸部识别跟踪

第二个是脸部,MediaPipe可以在脸部周围画一个网格来进行检测和跟踪。

具体代码如下。

import os
import time
import argparse
import cv2 as cv
import mediapipe as mp

class FaceDetect:
    def __init__(self, static_image=False, max_faces=1, refine=False, detect_conf=0.5, track_conf=0.5):
        self.draw_utils = mp.solutions.drawing_utils
        self.draw_spec = self.draw_utils.DrawingSpec(color=[0, 255, 0], thickness=1, circle_radius=2)
        self.mp_face_track = mp.solutions.face_mesh
        self.face_track = self.mp_face_track.FaceMesh(static_image, max_faces, refine, detect_conf, track_conf)

    def detect_mesh(self, img, disp=True):
        results = self.face_track.process(img)
        detected_landmarks = results.multi_face_landmarks

        if detected_landmarks:
            if disp:
                for f_landmarks in detected_landmarks:
                    self.draw_utils.draw_landmarks(img, f_landmarks, self.mp_face_track.FACEMESH_CONTOURS, self.draw_spec, self.draw_spec)

        return detected_landmarks, img

    def get_info(self, detected_landmarks, img_dims):
        landmarks_info = []
        img_height, img_width = img_dims
        for _, face in enumerate(detected_landmarks):
            mesh_info = []
            for id, landmarks in enumerate(face.landmark):
                x, y = int(landmarks.x * img_width), int(landmarks.y * img_height)
                mesh_info.append((id, x, y))
            landmarks_info.append(mesh_info)

        return landmarks_info

def main(path, is_image=True):
    print(path)
    if is_image:
        detector = FaceDetect()
        ori_img = cv.imread(path)
        img = ori_img.copy()
        landmarks, output = detector.detect_mesh(img)
        if landmarks:
            mesh_info = detector.get_info(landmarks, img.shape[:2])
            # print(mesh_info)

        cv.imshow("Result", output)
        cv.waitKey(0)

    else:
        detector = FaceDetect(static_image=False)
        cap = cv.VideoCapture(path)
        curr_time = 0
        prev_time = time.time()

        frame_width = int(cap.get(3))
        frame_height = int(cap.get(4))
        out = cv.VideoWriter('output.avi', cv.VideoWriter_fourcc('M', 'J', 'P', 'G'), 10, (frame_width, frame_height))  # 保存视频

        while True:
            ret, frame = cap.read()
            if not ret:
                print("Video Over")
                break

            img = frame.copy()
            landmarks, output = detector.detect_mesh(img)
            if landmarks:
                mesh_info = detector.get_info(landmarks, img.shape[:2])
                # print(len(mesh_info))

            curr_time = time.time()
            fps = 1/(curr_time - prev_time)
            prev_time = curr_time
            cv.putText(output, f'FPS: {str(int(fps))}', (10, 70), cv.FONT_HERSHEY_COMPLEX_SMALL, 2, (0, 50, 170), 2)
            cv.namedWindow('Result', cv.WINDOW_NORMAL)  # 窗口大小可设置
            cv.resizeWindow('Result', 580, 330)  # 重设大小

            out.write(output)

            cv.imshow("Result", output)
            if cv.waitKey(20) & 0xFF == ord('q'):
                break

        cap.release()
    cv.destroyAllWindows()


if __name__ == "__main__":

    # is_image = True
    # media_path = '.\\Data\\Images\\human_2.jpg'
    is_image = False
    media_path = '.\\Data\\Videos\\humans_3.mp4'

    if os.path.exists(os.path.join(os.getcwd(), media_path)):
        main(media_path, is_image)
    else:
        print("Invalid Path")

效果如下。

7b3071cacaeb7706eee2e69d27ec2d81.png

/ 04 /

手部跟踪识别

最后一个是手部,可以同时跟踪2只手并且在手部显示相应的坐标点。

具体代码如下。

import os
import time
import argparse
import cv2 as cv
import mediapipe as mp

class HandPoseDetect:
    def __init__(self, static_image=False, max_hands=2, complexity=1, detect_conf=0.5, track_conf=0.5):
        self.mp_hands = mp.solutions.hands
        self.mp_draw = mp.solutions.drawing_utils
        self.hands = self.mp_hands.Hands(static_image, max_hands, complexity, detect_conf, track_conf)

    def detect_landmarks(self, img, disp=True):
        img_rgb = cv.cvtColor(img, cv.COLOR_BGR2RGB)
        results = self.hands.process(img_rgb)
        detected_landmarks = results.multi_hand_landmarks

        if detected_landmarks:
            if disp:
                for h_landmark in detected_landmarks:
                    self.mp_draw.draw_landmarks(img, h_landmark, self.mp_hands.HAND_CONNECTIONS)
        return detected_landmarks, img

    def get_info(self, detected_landmarks, img_dims, hand_no=1):
        lm_list = []
        if not detected_landmarks:
            return lm_list

        if hand_no > 2:
            print('[WARNING] Provided hand number is greater than max number 2')
            print('[WARNING] Calculating information for hand 2')
            hand_no = 2
        elif hand_no < 1:
            print('[WARNING] Provided hand number is less than min number 1')
            print('[WARNING] Calculating information for hand 1')

        if len(detected_landmarks) < 2:
            hand_no = 0
        else:
            hand_no -= 1

        height, width = img_dims
        for id, h_landmarks in enumerate(detected_landmarks[hand_no].landmark):
            cord_x, cord_y = int(h_landmarks.x * width), int(h_landmarks.y * height)
            lm_list.append([id, cord_x, cord_y])

        return lm_list

def main(path, is_image=True):
    if is_image:
        detector = HandPoseDetect(static_image=True)
        ori_img = cv.imread(path)

        img = ori_img.copy()
        landmarks, output_img = detector.detect_landmarks(img)
        info_landmarks = detector.get_info(landmarks, img.shape[:2], 2)
        # print(info_landmarks)

        cv.imshow("Landmarks", output_img)
        cv.waitKey(0)

    else:
        detector = HandPoseDetect()
        cap = cv.VideoCapture(path)
        prev_time = time.time()
        cur_time = 0

        frame_width = int(cap.get(3))
        frame_height = int(cap.get(4))
        out = cv.VideoWriter('output.avi', cv.VideoWriter_fourcc('M', 'J', 'P', 'G'), 10, (frame_width, frame_height))  # 保存视频

        while True:
            ret, frame = cap.read()
            if not ret:
                print("Video Over")
                break

            img = frame.copy()
            landmarks, output_img = detector.detect_landmarks(img)
            info_landmarks = detector.get_info(landmarks, img.shape[:2], 2)
            # print(info_landmarks)

            cur_time = time.time()
            fps = 1/(cur_time - prev_time)
            prev_time = cur_time
            cv.putText(output_img, f'FPS: {str(int(fps))}', (10, 70), cv.FONT_HERSHEY_COMPLEX_SMALL, 2, (0, 50, 170), 2)

            cv.namedWindow('Original', cv.WINDOW_NORMAL)  # 窗口大小可设置
            cv.resizeWindow('Original', 580, 330)  # 重设大小
            cv.namedWindow('Detection', cv.WINDOW_NORMAL)  # 窗口大小可设置
            cv.resizeWindow('Detection', 580, 330)  # 重设大小

            out.write(output_img)

            cv.imshow("Detection", output_img)
            cv.imshow("Original", frame)
            if cv.waitKey(1) & 0xFF == ord('q'):
                break

        cap.release()

    cv.destroyAllWindows()


if __name__ == "__main__":
    is_image = False
    media_path = '.\\Data\\Videos\\piano_playing.mp4'

    if os.path.exists(os.path.join(os.getcwd(), media_path)):
        main(media_path, is_image)
    else:
        print("Invalid Path")

结果如下所示。

a40c6a3287dee062506b3dbe8579479c.gif

/ 05 /

总结

以上操作,就是MediaPipe姿态识别的部分内容。

当然我们还可以通过MediaPipe其它的识别功能,来做出有趣的事情。

比如结合摄像头,识别手势动作,控制电脑音量。这个大家都可以自行去学习。

相关文件及代码都已上传,公众号回复【姿态识别】即可获取。

万水千山总是情,点个 👍 行不行

推荐阅读

4f9889dc7324dfeb0df456498061252f.jpeg

5c42db312a9dd2d844b4f605966b1d68.jpeg

2e8a57ad48eb0c06433c0fc6d851d0ec.jpeg

···  END  ···

6ccde4257894b6c53107e132e17090f9.jpeg

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/636070.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

表示学习(Representation Learning) Part1--Pretext Text

文章目录 Representation LearningInferring structure&#xff08;推断结构&#xff09; Transformation predictionRotation predictionRelative transformation prediction ReconstructionDenoising AutoencodersContext encodersColorizationSplit-brain encoders Instance…

屏幕录像视频录制编辑软件TechSmith Camtasia 2023 for Mac 简体中文版

TechSmith Camtasia for Mac 中文版 是一款专业的屏幕录像视频录制编辑软件&#xff0c;非常容易就可以获得精彩的截屏视频。创建引人注目的培训&#xff0c;演示和演示视频。Camtasia 屏幕录制软件简化&#xff0c;直观&#xff0c;让您看起来像专业人士。利用Camtasia&#x…

SpringMVC 学习总结

&#x1f389;&#x1f389;&#x1f389;点进来你就是我的人了博主主页&#xff1a;&#x1f648;&#x1f648;&#x1f648;戳一戳,欢迎大佬指点! 欢迎志同道合的朋友一起加油喔&#x1f93a;&#x1f93a;&#x1f93a; 目录 1. 什么是 Spring MVC 1.1 Spring、Spring MV…

Dockerfile创建镜像

一、Docker镜像的创建 创建镜像有三种方法&#xff0c;分别为【基于已有镜像创建】、【基于本地模板创建】以及【基于Dockerfile创建】。 1.1 基于现有镜像创建 &#xff08;1&#xff09;首先启动一个镜像&#xff0c;在容器里做修改docker run -it centos:7 /bin/bash …

旧手机卖掉之前我们需要做这几个操作

随着科技的不断进步&#xff0c;人们使用的电子产品也在不断地迭代更新。当我们不再使用旧手机时&#xff0c;卖掉它可以省下一笔开支&#xff0c;但也需要注意保护个人隐私数据。因此&#xff0c;在售卖二手手机之前&#xff0c;正确清除旧手机中的历史数据变得至关重要。 首先…

Java网络开发(Tomcat)——从同步到异步 从jsp 到 js + axios + vue 实现 数据分页显示 数据增删改查

目录 引出一些固定的东西1.固定的响应格式2.name 变成 v-model 进行双向绑定3.下拉框选中--:value"type.id"4.vue导包固定写法5.script固定写法6.axios的get请求7.axios的post请求8.前端美化&#xff1a; 数据分页显示1.后端改成resp响应2.前端的修改要点&#xff08…

揭秘报表新玩法!标配插件不再单调,如何用柱形图插件让你的报表瞬间高大上!

摘要&#xff1a;本文由葡萄城技术团队于CSDN原创并首发。转载请注明出处&#xff1a;葡萄城官网&#xff0c;葡萄城为开发者提供专业的开发工具、解决方案和服务&#xff0c;赋能开发者。 前言 图表作为一款用于可视化数据的工具&#xff0c;可以帮助我们更好的分析和理解数…

flutter自定义系列之简单的K线图绘制

上篇文章讲了flutter自定义的相关流程&#xff0c; 今天继续练习下flutter的自定义K线&#xff1a; 我们可以通过自定义Painter来实现一个简单的K线图界面&#xff1a; 创建一个自定义的Painter&#xff0c;用于绘制K线图&#xff1a; import dart:ui;import package:flutte…

聊聊多线程

摘要 开发过程中&#xff0c;总会遇到一些并发安全问题。本文总结出常用的数据结构哪些是安全的&#xff0c;哪些是不安全的以及他们为什么是不安全。 java中sychronize锁的原理&#xff1a; 常见的数据结构 类型 数据结构是否安全ArrayList数组 不安全HashMap数…

Mocha Pro:AdjustTrack 模块

跟踪时由于缺乏细节或有障碍物阻挡&#xff0c;跟踪点发生了漂移&#xff0c;或者一个或多个跟踪点可能会离开画面&#xff0c;此时可考虑使用 AdjustTrack &#xff08;调整跟踪&#xff09;模块手动设置关键帧来获得更精准的跟踪数据。 尤其是当要利用表面 Surface区域进行插…

随机数组归并问题

1 问题 生成两个任意的随机数组&#xff0c;并将这两个数组按照数字大小按顺序归并到一个新数组中。 2 方法 思路&#xff1a;定义三个数组&#xff0c;两个数组自己输入值&#xff0c;第三个数组用来作归并后的数组&#xff0c;先将两个数组的值全部赋给第三个数组&#xff0c…

极简主义的远程文件浏览器Mikochi

什么是 Mikochi &#xff1f; Mikochi 是一个远程文件浏览器&#xff0c;用于自托管服务器 / NAS。它允许您浏览远程文件夹、上传文件、删除、重命名、下载和流式传输文件到 VLC/mpv。它带有一个由 JavaScript/Preact 提供支持的 Web 界面&#xff0c;以及一个内置于 Go/Gin 中…

ChatGPT 教我用 200 行代码写一个简版 Vue 框架 - OpenTiny

AI 是未来最好的老师 最近&#xff0c;我正在准备一份关于 Vue 基础的学习材料。期间我突发奇想&#xff1a;能否利用现在热门的 ChatGPT 帮我创建学习内容&#xff1f;其实 Vue 本身不难学&#xff0c;特别是基础用法&#xff0c;但是&#xff0c;如果你想深入掌握 Vue&#…

数据挖掘(7.1)--数据仓库

目录 引言 一、数据库 1.简介 2.数据库管理系统(DBMS) 二、数据仓库 数据仓库特征 数据仓库作用 数据仓库和DBMS对比 分离数据仓库和数据库 引言 数据仓库的历史可以追溯到20世纪60年代&#xff0c;当时计算机领域的主要工作是创建运行在主文件上的单个应用&#xff0…

LaravelPHP笔记-响应头去掉(隐藏)X-Powered-By

最近想搞个小项目&#xff0c;后端先用PHP&#xff0c;框架是Laravel但http响应头如下&#xff1a; 头带有X-Powered-By: PHP/7.3.33&#xff0c;这样很不安全&#xff0c;应该要隐藏&#xff0c;查了下百度。都是一个抄一个。 在代码中添加&#xff1a; header_remove(x-pow…

【几分醉意赠书活动 - 02期】 | 《前端系列丛书》

个人主页&#xff1a; 几分醉意的CSDN博客主页_传送门 个人主页&#xff1a; 陈老板的CSDN博客主页_传送门 赠书活动 | 第二期 本期好书推荐&#xff1a;《前端系列丛书》 粉丝福利&#xff1a;书籍赠送&#xff1a;共计送出30本 参与方式&#xff1a;关注公众号&#xff1a;码…

Flutter控件封装之轮播图Banner

Flutter中实现轮播图的方式有很多种&#xff0c;比如使用三方flutter_swiper&#xff0c;card_swiper等等&#xff0c;使用这些三方&#xff0c;可以很快很方便的实现一个轮播图展示&#xff0c;基本上也能满足我们日常的开发需求&#xff0c;如果说&#xff0c;想要一些定制化…

CloudFlare系列--使用第三方来自定义CDN的IP(笨牛简洁版)

原文网址&#xff1a;CloudFlare系列--使用第三方来自定义CDN的IP(笨牛简洁版)_IT利刃出鞘的博客-CSDN博客 简介 说明 本文介绍CloudFlare的CDN如何自定义第三方IP。 概述 CloudFlare官网接入域名的方式只能是 NS 接入&#xff0c;这样默认DNS服务器只能改为CloudFlare的D…

第3章 需求分析

第3章 需求分析 3.1 需求分析任务 3.1.1 确定对系统的综合要求 1. 功能需求 通过需求分析应该划分出必须完成的所有功能。 2. 性能需求 性能需求指定系统必须满足的定时约束或容量约束 3. 可靠性和可用性需求 可靠性需求定量地指定系统的可靠性 可用性与可靠性密切相关&…

北京某金融公司面试题,精选10道讲解!

你好&#xff0c;我是田哥 面试造火箭工作拧螺丝&#xff0c;最近一位朋友在面试中被问到各种各样的分布式微服务的面试题&#xff0c;也回答上来了。可是&#xff0c;等正式入职后&#xff0c;发现这家公司居然全部是使用单体项目&#xff0c;完全没有分布式微服务的东东&…