毕业设计-基于机器视觉的深蹲检测识别-TensorFlow-opencv

news2024/11/20 18:37:09

目录

前言

课题背景和意义

实现技术思路

实现效果图样例


前言


    📅大四是整个大学期间最忙碌的时光,一边要忙着备考或实习为毕业后面临的就业升学做准备,一边要为毕业设计耗费大量精力。近几年各个学校要求的毕设项目越来越难,有不少课题是研究生级别难度的,对本科同学来说是充满挑战。为帮助大家顺利通过和节省时间与精力投入到更重要的就业和考试中去,学长分享优质的选题经验和毕设项目与技术思路。

🚀对毕设有任何疑问都可以问学长哦!

大家好,这里是海浪学长毕设专题,本次分享的课题是

🎯基于机器视觉的深蹲检测识别

课题背景和意义

深蹲是一项健身运动,是练大腿肌肉的王牌动作,坚持做还会起到减肥的作用。深蹲被认为是增强腿部和臀部力量和围度,以及发展核心力量(core strength)必不可少的练习。深蹲时,有明确阶段和大幅度变化的基本运动,可以通过机器视觉技术来实现对深蹲的检测识别。

实现技术思路

需要的库文件

import io
from PIL import ImageFont
from PIL import ImageDraw
import csv
import cv2
from matplotlib import pyplot as plt
import numpy as np
import os
from PIL import Image
import sys
import tqdm
from mediapipe.python.solutions import drawing_utils as mp_drawing
from mediapipe.python.solutions import pose as mp_pose

技术步骤

  1. 收集目标练习的图像样本并对其进行姿势预测,
  2. 将获得的姿态标志转换为适合 k-NN 分类器的数据,并使用这些数据形成训练集,
  3. 执行分类本身,然后进行重复计数。

训练样本

为了建立一个好的分类器,应该为训练集收集适当的样本:理论上每个练习的每个最终状态大约有几百个样本,收集的样本涵盖不同的摄像机角度、环境条件、身体形状和运动变化,这一点很重要,能做到这样最好。但实际中如果嫌麻烦的话每种状态15-25张左右都可以,然后拍摄角度要注意多样化,最好每隔15度拍摄一张。

获取归一化的landmarks

要将样本转换为 k-NN 分类器训练集,我们可以在给定图像上运行 BlazePose 模型,并将预测的landmarks转储到 CSV 文件中。此外,Pose Classification Colab (Extended)通过针对整个训练集对每个样本进行分类,提供了有用的工具来查找异常值(例如,错误预测的姿势)和代表性不足的类别(例如,不覆盖所有摄像机角度)。
 

# 人体姿态编码模块
class FullBodyPoseEmbedder(object):
    """Converts 3D pose landmarks into 3D embedding."""

    def __init__(self, torso_size_multiplier=2.5):
        # Multiplier to apply to the torso to get minimal body size.
        self._torso_size_multiplier = torso_size_multiplier

        # Names of the landmarks as they appear in the prediction.
        self._landmark_names = [
            'nose',
            'left_eye_inner', 'left_eye', 'left_eye_outer',
            'right_eye_inner', 'right_eye', 'right_eye_outer',
            'left_ear', 'right_ear',
            'mouth_left', 'mouth_right',
            'left_shoulder', 'right_shoulder',
            'left_elbow', 'right_elbow',
            'left_wrist', 'right_wrist',
            'left_pinky_1', 'right_pinky_1',
            'left_index_1', 'right_index_1',
            'left_thumb_2', 'right_thumb_2',
            'left_hip', 'right_hip',
            'left_knee', 'right_knee',
            'left_ankle', 'right_ankle',
            'left_heel', 'right_heel',
            'left_foot_index', 'right_foot_index',
        ]

    def __call__(self, landmarks):
        """Normalizes pose landmarks and converts to embedding

        Args:
          landmarks - NumPy array with 3D landmarks of shape (N, 3).

        Result:
          Numpy array with pose embedding of shape (M, 3) where `M` is the number of
          pairwise distances defined in `_get_pose_distance_embedding`.
        """
        assert landmarks.shape[0] == len(self._landmark_names), 'Unexpected number of landmarks: {}'.format(
            landmarks.shape[0])

        # Get pose landmarks.
        landmarks = np.copy(landmarks)

        # Normalize landmarks.
        landmarks = self._normalize_pose_landmarks(landmarks)

        # Get embedding.
        embedding = self._get_pose_distance_embedding(landmarks)

        return embedding

    def _normalize_pose_landmarks(self, landmarks):
        """Normalizes landmarks translation and scale."""
        landmarks = np.copy(landmarks)

        # Normalize translation.
        pose_center = self._get_pose_center(landmarks)
        landmarks -= pose_center

        # Normalize scale.
        pose_size = self._get_pose_size(landmarks, self._torso_size_multiplier)
        landmarks /= pose_size
        # Multiplication by 100 is not required, but makes it eaasier to debug.
        landmarks *= 100

        return landmarks


    def _get_pose_size(self, landmarks, torso_size_multiplier):
        """Calculates pose size.

        It is the maximum of two values:
          * Torso size multiplied by `torso_size_multiplier`
          * Maximum distance from pose center to any pose landmark
        """
        # This approach uses only 2D landmarks to compute pose size.
        landmarks = landmarks[:, :2]

        # Hips center.
        left_hip = landmarks[self._landmark_names.index('left_hip')]
        right_hip = landmarks[self._landmark_names.index('right_hip')]
        hips = (left_hip + right_hip) * 0.5

        # Shoulders center.
        left_shoulder = landmarks[self._landmark_names.index('left_shoulder')]
        right_shoulder = landmarks[self._landmark_names.index('right_shoulder')]
        shoulders = (left_shoulder + right_shoulder) * 0.5

        # Torso size as the minimum body size.
        torso_size = np.linalg.norm(shoulders - hips)

        # Max dist to pose center.
        pose_center = self._get_pose_center(landmarks)
        max_dist = np.max(np.linalg.norm(landmarks - pose_center, axis=1))

        return max(torso_size * torso_size_multiplier, max_dist)

    def _get_pose_distance_embedding(self, landmarks):
        """Converts pose landmarks into 3D embedding.

        We use several pairwise 3D distances to form pose embedding. All distances
        include X and Y components with sign. We differnt types of pairs to cover
        different pose classes. Feel free to remove some or add new.

        Args:
          landmarks - NumPy array with 3D landmarks of shape (N, 3).

        Result:
          Numpy array with pose embedding of shape (M, 3) where `M` is the number of
          pairwise distances.
        """
        embedding = np.array([
            # One joint.

            self._get_distance(
                self._get_average_by_names(landmarks, 'left_hip', 'right_hip'),
                self._get_average_by_names(landmarks, 'left_shoulder', 'right_shoulder')),

            self._get_distance_by_names(landmarks, 'left_shoulder', 'left_elbow'),
            self._get_distance_by_names(landmarks, 'right_shoulder', 'right_elbow'),

            self._get_distance_by_names(landmarks, 'left_elbow', 'left_wrist'),
            self._get_distance_by_names(landmarks, 'right_elbow', 'right_wrist'),

            self._get_distance_by_names(landmarks, 'left_hip', 'left_knee'),
            self._get_distance_by_names(landmarks, 'right_hip', 'right_knee'),

            self._get_distance_by_names(landmarks, 'left_knee', 'left_ankle'),
            self._get_distance_by_names(landmarks, 'right_knee', 'right_ankle'),

            # Two joints.

            self._get_distance_by_names(landmarks, 'left_shoulder', 'left_wrist'),
            self._get_distance_by_names(landmarks, 'right_shoulder', 'right_wrist'),

            self._get_distance_by_names(landmarks, 'left_hip', 'left_ankle'),
            self._get_distance_by_names(landmarks, 'right_hip', 'right_ankle'),

            # Four joints.

            self._get_distance_by_names(landmarks, 'left_hip', 'left_wrist'),
            self._get_distance_by_names(landmarks, 'right_hip', 'right_wrist'),

            # Five joints.

            self._get_distance_by_names(landmarks, 'left_shoulder', 'left_ankle'),
            self._get_distance_by_names(landmarks, 'right_shoulder', 'right_ankle'),

            self._get_distance_by_names(landmarks, 'left_hip', 'left_wrist'),
            self._get_distance_by_names(landmarks, 'right_hip', 'right_wrist'),

            # Cross body.

            self._get_distance_by_names(landmarks, 'left_elbow', 'right_elbow'),
            self._get_distance_by_names(landmarks, 'left_knee', 'right_knee'),

            self._get_distance_by_names(landmarks, 'left_wrist', 'right_wrist'),
            self._get_distance_by_names(landmarks, 'left_ankle', 'right_ankle'),

            # Body bent direction.

            # self._get_distance(
            #     self._get_average_by_names(landmarks, 'left_wrist', 'left_ankle'),
            #     landmarks[self._landmark_names.index('left_hip')]),
            # self._get_distance(
            #     self._get_average_by_names(landmarks, 'right_wrist', 'right_ankle'),
            #     landmarks[self._landmark_names.index('right_hip')]),
        ])

        return embedding

    def _get_average_by_names(self, landmarks, name_from, name_to):
        lmk_from = landmarks[self._landmark_names.index(name_from)]
        lmk_to = landmarks[self._landmark_names.index(name_to)]
        return (lmk_from + lmk_to) * 0.5

    def _get_distance_by_names(self, landmarks, name_from, name_to):
        lmk_from = landmarks[self._landmark_names.index(name_from)]
        lmk_to = landmarks[self._landmark_names.index(name_to)]
        return self._get_distance(lmk_from, lmk_to)

    def _get_distance(self, lmk_from, lmk_to):
        return lmk_to - lmk_from


使用KNN算法分类

用于姿势分类的 k-NN 算法需要每个样本的特征向量表示和一个度量来计算两个这样的向量之间的距离,以找到最接近目标的姿势样本。

为了将姿势标志转换为特征向量,我们使用预定义的姿势关节列表之间的成对距离,例如手腕和肩膀、脚踝和臀部以及两个手腕之间的距离。由于该算法依赖于距离,因此在转换之前所有姿势都被归一化以具有相同的躯干尺寸和垂直躯干方向。

 可以根据运动的特点选择所要计算的距离对(例如,引体向上可能更加关注上半身的距离对)。
为了获得更好的分类结果,使用不同的距离度量调用了两次 k-NN 搜索:
首先,为了过滤掉与目标样本几乎相同但在特征向量中只有几个不同值的样本(这意味着不同的弯曲关节和其他姿势类),使用最小坐标距离作为距离度量,
然后使用平均坐标距离在第一次搜索中找到最近的姿势簇。

最后,我们应用指数移动平均(EMA) 平滑来平衡来自姿势预测或分类的任何噪声。为此,我们不仅搜索最近的姿势簇,而且计算每个姿势簇的概率,并将其用于随着时间的推移进行平滑处理。

# 姿态分类结果平滑
class EMADictSmoothing(object):
    """Smoothes pose classification."""

    def __init__(self, window_size=10, alpha=0.2):
        self._window_size = window_size
        self._alpha = alpha

        self._data_in_window = []

    def __call__(self, data):
        """Smoothes given pose classification.

        Smoothing is done by computing Exponential Moving Average for every pose
        class observed in the given time window. Missed pose classes arre replaced
        with 0.

        Args:
          data: Dictionary with pose classification. Sample:
              {
                'pushups_down': 8,
                'pushups_up': 2,
              }

        Result:
          Dictionary in the same format but with smoothed and float instead of
          integer values. Sample:
            {
              'pushups_down': 8.3,
              'pushups_up': 1.7,
            }
        """
        # Add new data to the beginning of the window for simpler code.
        self._data_in_window.insert(0, data)
        self._data_in_window = self._data_in_window[:self._window_size]

        # Get all keys.
        keys = set([key for data in self._data_in_window for key, _ in data.items()])

        # Get smoothed values.
        smoothed_data = dict()
        for key in keys:
            factor = 1.0
            top_sum = 0.0
            bottom_sum = 0.0
            for data in self._data_in_window:
                value = data[key] if key in data else 0.0

                top_sum += factor * value
                bottom_sum += factor

                # Update factor.
                factor *= (1.0 - self._alpha)

            smoothed_data[key] = top_sum / bottom_sum

        return smoothed_data

计数器计数

为了计算重复次数,该算法监控目标姿势类别的概率。深蹲的“向上”和“向下”终端状态:

当“下”位姿类的概率第一次通过某个阈值时,算法标记进入“下”位姿类。
一旦概率下降到阈值以下(即起身超过一定高度),算法就会标记“向下”姿势类别,退出并增加计数器。
为了避免概率在阈值附近波动(例如,当用户在“向上”和“向下”状态之间暂停时)导致幻像计数的情况,用于检测何时退出状态的阈值实际上略低于用于检测状态退出的阈值。

实现效果图样例

 

我是海浪学长,创作不易,欢迎点赞、关注、收藏、留言。

毕设帮助,疑难解答,欢迎打扰!

最后

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

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

相关文章

LeetCode刷题复盘笔记—一文搞懂完全背包之377. 组合总和 Ⅳ问题(动态规划系列第十二篇)

今日主要总结一下动态规划完全背包的一道题目,377. 组合总和 Ⅳ 题目:377. 组合总和 Ⅳ Leetcode题目地址 题目描述: 给你一个由 不同 整数组成的数组 nums ,和一个目标整数 target 。请你从 nums 中找出并返回总和为 target 的…

[附源码]计算机毕业设计基于web的羽毛球管理系统

项目运行 环境配置: Jdk1.8 Tomcat7.0 Mysql HBuilderX(Webstorm也行) Eclispe(IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持)。 项目技术: SSM mybatis Maven Vue 等等组成,B/S模式 M…

Python软件编程等级考试一级——20220915

Python软件编程等级考试一级——20220915理论单选题判断题实操第一题第二题理论 单选题 1、表达式len(“学史明理增信 ,读史终生受益”) > len(" reading history will benefit you ")的结果是? A、0 B、True C、False D、1 2、表达…

SLMi333国内首款兼容光耦带DESAT保护功能的隔离式栅极驱动器

SLMi333国内首款兼容光耦带DESAT保护功能的隔离式栅极驱动器,内置快速去饱和(DESAT)故障检测功能,米勒钳位功能,漏极开路故障反馈,软关断功能以及可选择的自恢复模式,兼容光耦隔离驱动器,一款高…

安装mongodb6

一、安装mongodb6.0.2 1.官网下载社区版 https://www.mongodb.com/ 2.双击下载的文件,按步骤安装 选择custom 自定义安装 改一下安装地址,路径最好不要带空格 Install MongoD as a Service 作为服务方式安装 Run the service as Network Service…

SuperMap iClient for Leaflet对EPSG:4509图加载滑动查询

作者:John SuperMap iClient for Leaflet对EPSG:4509地图加载&滑动查询 在WebGIS开发使用中,我们会遇到地图显示不了,以及查询到数据显示不出的问题,因此本文就以EPSG:4509为例介绍该坐标系地图加载和查询。 1、EPSG:4509地图…

数据分析案例:基于水色图像的水质识别

大数据分析课程、大数据分析班、大数据案例等,围绕大数据展开讲解。 数据分析案例:基于水色图像的水质识别,通过学习本案例,可以掌握图像切割、特征提取、模型构建和模型评价的主要方法和技能,并为后续相关课程学习及将…

蚂蚁面试官:Zookeeper 的选举流程是怎样的?我当场懵逼了

​ 编辑切换为居中 添加图片注释,不超过 140 字(可选) 面试经常会遇到面试官问 Zookeeper 的选举原理,我心想,问这些有啥用吗?又不要我造火箭! 每次面试也只知道个大概,并没有深究…

分布式电源接入对配电网影响的研究(Matlab代码实现)

👨‍🎓个人主页:研学社的博客 💥💥💞💞欢迎来到本博客❤️❤️💥💥 🏆博主优势:🌞🌞🌞博客内容尽量做到思维缜…

vue3 + element 从0到1搭建前端基础框架

一、框架搭建 框架代码 个人博客 往往从0到1开发项目时发觉无从下手,或者很可能一步一个坑,因为大多基础框架公司已经搭建完毕的,新加入的成员也都是在此基础上进行功能模块的拓展。网上也鲜有详尽的全流程参考,多是某个局部功能的…

【Vue】webpack的基本使用

✍️ 作者简介: 前端新手学习中。 💂 作者主页: 作者主页查看更多前端教学 🎓 专栏分享:css重难点教学 Node.js教学 从头开始学习 ajax学习 文章目录webpack的学习目标前端工程化 小白眼中的前端开发 vs 实际的前端开发 什么是前端工程…

CISP考试大纲/范围

CISP考试主要是考CISP知识体系大纲,分别为信息安全保障、信息安全技术、信息安全管理、信息安全工程和信息安全标准法规这五大知识类,每个知识类根据其逻辑划分为多个知识体,每个知识体包含多个知识域,每个知识域由一个或多个知识…

Java项目:SSM失物招领管理系统

作者主页:源码空间站2022 简介:Java领域优质创作者、Java项目、学习资料、技术互助 文末获取源码 项目介绍 主要功能包括: 用户发布失物,或者招领失物,管理员对用户,失物信息进行增删改查。 环境需要 1…

新课程教学杂志新课程教学杂志社新课程教学编辑部2022年第19期目录

核心素养 核心素养视域下的历史教学设计——以“清朝君主专制的强化”为例 王威; 1-3 新中考背景下文本分析能力与核心素养的培育 黄嫄; 4-5《新课程教学》投稿:cn7kantougao163.com 基于核心素养的物理教学评价改良 李红; 6-7 初中语文综合性学习的…

Metabase学习教程:系统管理-6

Metabase可扩展性 扩展Metabase以支持更多人和数据库的最佳实践。 Metabase是一个可扩展的、经过实战的软件,被成千上万的公司用来提供高质量的自助服务分析。它通过水平扩展支持高可用性,而且它是开箱即用的高效工具:一台拥有4gb内存的单核…

vue.js axios 数据不刷新

getServerList(){axios.get(/server/showList).then(function(response){this.servers response.data // 不刷新console.log(response.data)}).catch(function (error) {console.log(error);}); } 打印this:this不是vue对象修改为:getServerList(){axi…

Mysql各种缓冲区的功能及之间的联系

buffer poolmysql数据存放在磁盘里面,如果每次查询都直接从磁盘里面查询,会影响性能,因此需要内存态缓存池。另外缓存池的淘汰机制不是基础LRU,而是是改进版LRU,防止大量临时缓存挤出热点数据。buffer pool读缓存分为老…

代码随想录算法训练营第五十三天| LeetCode1143. 最长公共子序列、LeetCode1035. 不相交的线、LeetCode53. 最大子数组和

一、LeetCode1143. 最长公共子序列 1:题目描述(1143. 最长公共子序列) 给定两个字符串 text1 和 text2,返回这两个字符串的最长 公共子序列 的长度。如果不存在 公共子序列 ,返回 0 。 一个字符串的 子序列 是指这样一…

Leetcode 1687. 从仓库到码头运输箱子 [四种解法] 动态规划 从朴素出发详细剖析优化步骤

你有一辆货运卡车,你需要用这一辆车把一些箱子从仓库运送到码头。这辆卡车每次运输有 箱子数目的限制 和 总重量的限制 。给你一个箱子数组 boxes 和三个整数 portsCount, maxBoxes 和 maxWeight ,其中 boxes[i] [ports​​i​, weighti] 。ports​​i …

网页制作课作业基于HTML+CSS+JavaScript+jquery仿慕课网教学培训网站设计实例 企业网站制作

常见网页设计作业题材有 个人、 美食、 公司、 学校、 旅游、 电商、 宠物、 电器、 茶叶、 家居、 酒店、 舞蹈、 动漫、 服装、 体育、 化妆品、 物流、 环保、 书籍、 婚纱、 游戏、 节日、 戒烟、 电影、 摄影、 文化、 家乡、 鲜花、 礼品、 汽车、 其他等网页设计题目, A…