加入星計(jì)劃,您可以享受以下權(quán)益:

  • 創(chuàng)作內(nèi)容快速變現(xiàn)
  • 行業(yè)影響力擴(kuò)散
  • 作品版權(quán)保護(hù)
  • 300W+ 專業(yè)用戶
  • 1.5W+ 優(yōu)質(zhì)創(chuàng)作者
  • 5000+ 長(zhǎng)期合作伙伴
立即加入

基于計(jì)算機(jī)視覺(jué)(opencv)的運(yùn)動(dòng)計(jì)數(shù)(運(yùn)動(dòng)輔助)系統(tǒng)-源碼+注釋+報(bào)告

08/22 11:42
1242
服務(wù)支持:
技術(shù)交流群

完成交易后在“購(gòu)買(mǎi)成功”頁(yè)面掃碼入群,即可與技術(shù)大咖們分享疑惑和經(jīng)驗(yàn)、收獲成長(zhǎng)和認(rèn)同、領(lǐng)取優(yōu)惠和紅包等。

虛擬商品不可退

當(dāng)前內(nèi)容為數(shù)字版權(quán)作品,購(gòu)買(mǎi)后不支持退換且無(wú)法轉(zhuǎn)移使用。

加入交流群
掃碼加入
獲取工程師必備禮包
參與熱點(diǎn)資訊討論
放大
實(shí)物圖
相關(guān)方案
  • 方案介紹
    • 基于計(jì)算機(jī)視覺(jué)(opencv)的運(yùn)動(dòng)計(jì)數(shù)系統(tǒng)
  • 相關(guān)文件
  • 推薦器件
  • 相關(guān)推薦
  • 電子產(chǎn)業(yè)圖譜
申請(qǐng)入駐 產(chǎn)業(yè)圖譜

基于計(jì)算機(jī)視覺(jué)(opencv)的運(yùn)動(dòng)計(jì)數(shù)系統(tǒng)

Notice:本文的源碼均為開(kāi)源代碼,(由于文章篇幅有限,文章中的源碼均是偽代碼),需要源碼的小伙伴可以先關(guān)注我,私信或評(píng)論與我聯(lián)系,大家一起學(xué)習(xí)

1、項(xiàng)目簡(jiǎn)介

1.1研究背景及意義

隨著全民健身熱潮的興起,越來(lái)越多的人積極參加健身鍛煉,但由于缺乏科學(xué)的運(yùn)動(dòng)指導(dǎo),使健身難以取得相應(yīng)的效果。據(jù)市場(chǎng)調(diào)查顯示,沒(méi)有產(chǎn)品可以自動(dòng)分析健身運(yùn)動(dòng)并提供指導(dǎo)。而近年深度神經(jīng)網(wǎng)絡(luò)在人體姿態(tài)識(shí)別上已經(jīng)取得了巨大的成功,針對(duì)這個(gè)現(xiàn)象,本課程設(shè)計(jì)制作了一個(gè)基于MediaPipe的運(yùn)動(dòng)計(jì)數(shù)系統(tǒng)。該系統(tǒng)主要內(nèi)容包括對(duì)于單人人體關(guān)鍵點(diǎn)的檢測(cè),關(guān)鍵點(diǎn)的連接,以及運(yùn)動(dòng)健身關(guān)鍵點(diǎn)的角度變化來(lái)實(shí)現(xiàn)對(duì)俯臥撐和深蹲的計(jì)數(shù)。

1.2項(xiàng)目設(shè)計(jì)方案

系統(tǒng)框架如下圖所示,本系統(tǒng)將視頻或者攝像頭實(shí)時(shí)捕捉的俯臥撐或者深蹲的視頻一步步進(jìn)行處理,首先我們獲得俯臥撐和深蹲的訓(xùn)練樣本,之后將訓(xùn)練樣本轉(zhuǎn)換為k-NN分類訓(xùn)練集,將預(yù)測(cè)的landmarks轉(zhuǎn)移儲(chǔ)存至CSV文件中,之后進(jìn)行KNN算法分類。最后實(shí)現(xiàn)重復(fù)計(jì)數(shù)將結(jié)果顯示在面板中。
在這里插入圖片描述

具體方案如下:
(1)收集目標(biāo)練習(xí)的圖像樣本并對(duì)其進(jìn)行姿勢(shì)預(yù)測(cè)
(2)將獲得的姿態(tài)標(biāo)志轉(zhuǎn)換為適合 k-NN 分類器的數(shù)據(jù),并使用這些數(shù)據(jù)形成訓(xùn)練集
(3)執(zhí)行分類本身,然后進(jìn)行重復(fù)計(jì)數(shù)
(4)面板顯示結(jié)果
在這里插入圖片描述

2、程序設(shè)計(jì)

2.1 程序設(shè)計(jì)方案
2.1.1 建立訓(xùn)練樣本

為了建立一個(gè)好的分類器,應(yīng)該為訓(xùn)練集收集適當(dāng)?shù)臉颖荆豪碚撋厦總€(gè)練習(xí)的每個(gè)最終狀態(tài)大約有幾百個(gè)樣本(例如,俯臥撐和深蹲的“向上”和“向下”位置),收集的樣本涵蓋不同的攝像機(jī)角度、環(huán)境條件、身體形狀和運(yùn)動(dòng)變化,這一點(diǎn)很重要,能做到這樣最好。但實(shí)際中如果嫌麻煩的話每種狀態(tài)15-25張左右都可以,然后拍攝角度要注意多樣化,最好每隔15度拍攝一張。
具體代碼如下:

訓(xùn)練樣本圖片格式
 Required structure of the images_in_folder:
   fitness_poses_images_in/
     squat_up/
       image_001.jpg
       image_002.jpg
       ...
     squat_down/
       image_001.jpg
       image_002.jpg
2.1.2 獲取歸一化landmarks

要將樣本轉(zhuǎn)換為 k-NN 分類器訓(xùn)練集,在給定圖像上運(yùn)行 BlazePose 模型,并將預(yù)測(cè)的landmarks轉(zhuǎn)儲(chǔ)到 CSV 文件中。此外,Pose Classification Colab (Extended)通過(guò)針對(duì)整個(gè)訓(xùn)練集對(duì)每個(gè)樣本進(jìn)行分類,提供了有用的工具來(lái)查找異常值(例如,錯(cuò)誤預(yù)測(cè)的姿勢(shì))和代表性不足的類別(例如,不覆蓋所有攝像機(jī)角度)。之后,能夠在任意視頻上測(cè)試該分類器。fitness_poses_csvs_out文件夾里面的csv文件就是使用拍攝的訓(xùn)練樣本提取出來(lái)的深蹲和俯臥撐的訓(xùn)練集文件,有了該文件后就可以直接運(yùn)行本項(xiàng)目。生成的CSV文件如圖:
在這里插入圖片描述
具體代碼如下:

class FullBodyPoseEmbedder(object):
    Converts 3D pose landmarks into 3D embedding."""
    def __init__(self, torso_size_multiplier):
         
           乘數(shù)應(yīng)用于軀干以獲得最小的身體尺寸
        
    def __call__(self, landmarks):
        
           Normalizes pose landmarks and converts to embedding
           獲取 landmarks.歸一化姿勢(shì)landmarks并轉(zhuǎn)換為embedding
           具有形狀 (M, 3) 的姿勢(shì)embedding的 Numpy 數(shù)組,其中“M”是“_get_pose_distance_embedding”中定義的成對(duì)距離的數(shù)量。
           return embedding
    def _normalize_pose_landmarks(self, landmarks):
           Normalizes landmarks translation and scale.歸一化landmarks的平移和縮放
      
          return landmarks
    def _get_pose_center(self, landmarks):
          Calculates pose center as point between hips.將姿勢(shì)中心計(jì)算為臀部之間的點(diǎn)。
          return center
    def _get_pose_size(self, landmarks, torso_size_multiplier):
           Calculates pose size.計(jì)算姿勢(shì)大小。
           它是下面兩個(gè)值的最大值:
              * 軀干大小乘以`torso_size_multiplier`
              * 從姿勢(shì)中心到任何姿勢(shì)地標(biāo)的最大距離
           這種方法僅使用 2D landmarks來(lái)計(jì)算姿勢(shì)大小分別計(jì)算
           臀部中心、兩肩中心、軀干尺寸作為最小的身體尺寸到姿勢(shì)中心的最大距離.
           return max(torso_size * torso_size_multiplier, max_dist)
    def _get_pose_distance_embedding(self, landmarks):
           Converts pose landmarks into 3D embedding.
           將姿勢(shì)landmarks轉(zhuǎn)換為 3D embedding.
           我們使用幾個(gè)成對(duì)的 3D 距離來(lái)形成姿勢(shì)embedding。 所有距離都包括帶符號(hào)的 X 和 Y 分量。
           我們使用不同類型的對(duì)來(lái)覆蓋不同的姿勢(shì)類別。 Feel free to remove some or add new.
2.1.3 使用KNN算法分類

用于姿勢(shì)分類的 k-NN 算法需要每個(gè)樣本的特征向量表示和一個(gè)度量來(lái)計(jì)算兩個(gè)這樣的向量之間的距離,以找到最接近目標(biāo)的姿勢(shì)樣本。
為了將姿勢(shì)標(biāo)志轉(zhuǎn)換為特征向量,使用預(yù)定義的姿勢(shì)關(guān)節(jié)列表之間的成對(duì)距離,例如手腕和肩膀、腳踝和臀部以及兩個(gè)手腕之間的距離。由于該算法依賴于距離,因此在轉(zhuǎn)換之前所有姿勢(shì)都被歸一化以具有相同的軀干尺寸和垂直軀干方向。
可以根據(jù)運(yùn)動(dòng)的特點(diǎn)選擇所要計(jì)算的距離對(duì)(例如,引體向上可能更加關(guān)注上半身的距離對(duì))。
在這里插入圖片描述
為了獲得更好的分類結(jié)果,使用不同的距離度量調(diào)用了兩次 KNN 搜索:
首先,為了過(guò)濾掉與目標(biāo)樣本幾乎相同但在特征向量中只有幾個(gè)不同值的樣本(這意味著不同的彎曲關(guān)節(jié)和其他姿勢(shì)類),使用最小坐標(biāo)距離作為距離度量,然后使用平均坐標(biāo)距離在第一次搜索中找到最近的姿勢(shì)簇。
最后,應(yīng)用指數(shù)移動(dòng)平均(EMA) 平滑來(lái)平衡來(lái)自姿勢(shì)預(yù)測(cè)或分類的任何噪聲。不僅搜索最近的姿勢(shì)簇,而且計(jì)算每個(gè)姿勢(shì)簇的概率,并將其用于隨著時(shí)間的推移進(jìn)行平滑處理。

class PoseSample(object):
    獲取捕捉到的圖像landmarks
class PoseClassifier(object):
    根據(jù)捕捉到圖像的landmarks,對(duì)圖像進(jìn)行分類
       *根據(jù)提供的訓(xùn)練圖片,將訓(xùn)練的圖片分類,例如蹲起或者俯臥撐,分為兩類,放入不同文件夾
       *根據(jù)不同類別的圖片分別生成.csv的文件
    訓(xùn)練圖片的文件結(jié)構(gòu):
          neutral_standing.csv
          pushups_down.csv
          pushups_up.csv
          squats_down.csv
          ...
    csv文件結(jié)構(gòu):
          sample_00001,x1,y1,z1,x2,y2,z2,....
          sample_00002,x1,y1,z1,x2,y2,z2,....
        return pose_samples
    def find_pose_sample_outliers(self):
        針對(duì)整個(gè)數(shù)據(jù)庫(kù)對(duì)每個(gè)樣本進(jìn)行分類
           *找出目標(biāo)姿勢(shì)中的異常值
           *為目標(biāo)找到最近的姿勢(shì)
           *如果最近的姿勢(shì)具有不同的類別或多個(gè)姿勢(shì)類別被檢測(cè)為最近,則樣本是異常值
        return outliers
    def __call__(self, pose_landmarks):
        對(duì)給定的姿勢(shì)進(jìn)行分類。
        分類分兩個(gè)階段完成:
           *按 MAX 距離選取前 N 個(gè)樣本。 它允許刪除與給定姿勢(shì)幾乎相同但有一些關(guān)節(jié)在向一個(gè)方向彎曲的樣本。
           *按平均距離選擇前 N 個(gè)樣本。 在上一步移除異常值后, 我們可以選擇在平均值上接近的樣本。
           *按平均距離過(guò)濾,去除異常值后,我們可以通過(guò)平均距離找到最近的姿勢(shì)
        return result
2.1.4 計(jì)數(shù)實(shí)現(xiàn)

為了計(jì)算重復(fù)次數(shù),該算法監(jiān)控目標(biāo)姿勢(shì)類別的概率。深蹲的“向上”和“向下”終端狀態(tài):
當(dāng)“下”位姿類的概率第一次通過(guò)某個(gè)閾值時(shí),算法標(biāo)記進(jìn)入“下”位姿類。
一旦概率下降到閾值以下(即起身超過(guò)一定高度),算法就會(huì)標(biāo)記“向下”姿勢(shì)類別,退出并增加計(jì)數(shù)器
為了避免概率在閾值附近波動(dòng)(例如,當(dāng)用戶在“向上”和“向下”狀態(tài)之間暫停時(shí))導(dǎo)致幻像計(jì)數(shù)的情況,用于檢測(cè)何時(shí)退出狀態(tài)的閾值實(shí)際上略低于用于檢測(cè)狀態(tài)退出的閾值。

動(dòng)作計(jì)數(shù)器
class RepetitionCounter(object):
    # 計(jì)算給定目標(biāo)姿勢(shì)類的重復(fù)次數(shù)

    def __init__(self, class_name, enter_threshold=6, exit_threshold=4):
        self._class_name = class_name

        # 如果姿勢(shì)通過(guò)了給定的閾值,那么我們就進(jìn)入該動(dòng)作的計(jì)數(shù)
        self._enter_threshold = enter_threshold
        self._exit_threshold = exit_threshold

        # 是否處于給定的姿勢(shì)
        self._pose_entered = False

        # 退出姿勢(shì)的次數(shù)
        self._n_repeats = 0

    @property
    def n_repeats(self):
        return self._n_repeats

    def __call__(self, pose_classification):
        # 計(jì)算給定幀之前發(fā)生的重復(fù)次數(shù)
        # 我們使用兩個(gè)閾值。首先,您需要從較高的位置上方進(jìn)入姿勢(shì),然后您需要從較低的位置下方退出。
        # 閾值之間的差異使其對(duì)預(yù)測(cè)抖動(dòng)穩(wěn)定(如果只有一個(gè)閾值,則會(huì)導(dǎo)致錯(cuò)誤計(jì)數(shù))。

        # 參數(shù):
        #   pose_classification:當(dāng)前幀上的姿勢(shì)分類字典
        #         Sample:
        #         {
        #             'squat_down': 8.3,
        #             'squat_up': 1.7,
        #         }

        # 獲取姿勢(shì)的置信度.
        pose_confidence = 0.0
        if self._class_name in pose_classification:
            pose_confidence = pose_classification[self._class_name]

        # On the very first frame or if we were out of the pose, just check if we
        # entered it on this frame and update the state.
        # 在第一幀或者如果我們不處于姿勢(shì)中,只需檢查我們是否在這一幀上進(jìn)入該姿勢(shì)并更新?tīng)顟B(tài)
        if not self._pose_entered:
            self._pose_entered = pose_confidence > self._enter_threshold
            return self._n_repeats

        # 如果我們處于姿勢(shì)并且正在退出它,則增加計(jì)數(shù)器并更新?tīng)顟B(tài)
        if pose_confidence < self._exit_threshold:
            self._n_repeats += 1
            self._pose_entered = False

        return self._n_repeats
2.1.5 界面實(shí)現(xiàn)

利用Python的QtDesinger來(lái)進(jìn)行設(shè)計(jì)界面,添加攝像頭捕捉區(qū)域,以及捕捉到的圖像的最高點(diǎn)和最低點(diǎn)的變化曲線區(qū)域,和計(jì)算運(yùn)動(dòng)次數(shù)并顯示的區(qū)域。整體采用了水平的布局,添加按鈕以及界面關(guān)閉提示。攝像頭則采用英文狀態(tài)下,輸入“q”來(lái)進(jìn)行退出,方法很簡(jiǎn)單。具體運(yùn)行結(jié)果如下圖:
在這里插入圖片描述

class Ui_MainWindow(object):
    def __init__(self):
        self.RowLength = 0

    def setupUi(self, MainWindow):
        MainWindow.setObjectName("MainWindow")
        MainWindow.resize(1213, 680)
        設(shè)置窗體固定大小
        MainWindow.setToolButtonStyle(QtCore.Qt.ToolButtonIconOnly)
        圖片區(qū)域
        self.centralwidget.setObjectName("centralwidget")
        設(shè)置窗口大小
        self.tableWidget = QtWidgets.QTableWidget(self.scrollAreaWidgetContents_1)
設(shè)置布局
        設(shè)置窗口大小及顏色
        self.tableWidget.setObjectName("tableWidget")
        self.tableWidget.setColumnCount(6)
        設(shè)置1列的寬度
        設(shè)置2列的寬度
        設(shè)置3列的寬度
        設(shè)置4列的寬度
        設(shè)置5列的寬度
        設(shè)置6列的寬度
        self.tableWidget.setRowCount(self.RowLength)
        隱藏垂直表頭
        self.tableWidget.setEditTriggers(QAbstractItemView.NoEditTriggers)
        self.tableWidget.raise_()
        self.scrollArea_4.setWidget(self.scrollAreaWidgetContents_4)
        MainWindow.setCentralWidget(self.centralwidget)
        self.statusbar = QtWidgets.QStatusBar(MainWindow)
        self.statusbar.setObjectName("statusbar")
        MainWindow.setStatusBar(self.statusbar)
        self.retranslateUi(MainWindow)
        QtCore.QMetaObject.connectSlotsByName(MainWindow)
獲取當(dāng)前工程文件位置
    def retranslateUi(self, MainWindow):
        _translate = QtCore.QCoreApplication.translate
        設(shè)置UI界面標(biāo)題為:基于姿態(tài)估計(jì)的運(yùn)動(dòng)計(jì)數(shù)系統(tǒng)
        設(shè)置左側(cè)UI界面橫坐標(biāo)為:最高點(diǎn)與最低點(diǎn)高度變化曲線
        設(shè)置左側(cè)UI界面標(biāo)題為:攝像頭捕捉
        設(shè)置右側(cè)UI界面標(biāo)題為:計(jì)數(shù)區(qū)域
        self.scrollAreaWidgetContents_1.show()
2.1.6 入口函數(shù)main

有兩種選擇模式,可以從本地上傳視頻進(jìn)行檢測(cè),也可以調(diào)用攝像頭進(jìn)行實(shí)時(shí)檢測(cè)。具體的模式選擇可以通過(guò)輸入選擇的數(shù)字進(jìn)行選擇:

if __name__ == "__main__":
    app = QtWidgets.QApplication(sys.argv)
    調(diào)用主窗口函數(shù)
    調(diào)用UI界面
    while True:
        menu = int(input("請(qǐng)輸入檢測(cè)模式(數(shù)字):1. 從本地導(dǎo)入視頻檢測(cè)t2. 調(diào)用攝像頭檢測(cè)n"))
        if 輸入數(shù)字為1:
            flag = int(input("請(qǐng)輸入檢測(cè)的運(yùn)動(dòng)類型(數(shù)字):1. 俯臥撐t2. 深蹲n"))
            video_path = input("請(qǐng)輸入視頻路徑:")
            tp.trainset_process(flag)
            vp.video_process(video_path, flag)
            continue
        elif 輸入數(shù)字為2:
            flag = int(input("請(qǐng)輸入檢測(cè)的運(yùn)動(dòng)類型(數(shù)字):1. 俯臥撐t2. 深蹲n"))
            print("n按英文狀態(tài)下的q或esc退出攝像頭采集")
            tp.trainset_process(flag)
            vc.process(flag)
            continue
        elif 輸入數(shù)字為3:
            break
        else:
            print("輸入錯(cuò)誤,請(qǐng)重新輸入!")
            continue
    sys.exit(app.exec_())

3、成果展示

由我們組成員分別對(duì)系統(tǒng)的鏡頭采集功能和視頻采集功能進(jìn)行了實(shí)驗(yàn),當(dāng)選擇鏡頭采集功能時(shí),利用opencv調(diào)用電腦攝像頭進(jìn)行采集畫(huà)面。
在這里插入圖片描述
如圖所示為打開(kāi)攝像頭界面,左邊是攝像頭的捕捉區(qū)域,并附帶檢測(cè)屏幕中人體最高點(diǎn)和最低點(diǎn)的曲線變化圖;當(dāng)曲線發(fā)生階躍性突變,且達(dá)到一定的峰值,攝像頭中的右上方的數(shù)字就會(huì)進(jìn)行計(jì)數(shù)。上圖選擇的模式為蹲起計(jì)數(shù),同時(shí)我們也添加了俯臥撐計(jì)數(shù)的模式,只需在模式選擇下進(jìn)行調(diào)整即可。模型會(huì)根據(jù)選擇的模式,自動(dòng)進(jìn)行分類。
在這里插入圖片描述
上圖可以清晰的看出測(cè)試者在經(jīng)過(guò)蹲起測(cè)試之后截下的圖片,一共測(cè)試了16次蹲起,曲線形象的記錄下了每一次的過(guò)程,藍(lán)色表示最高點(diǎn)的變化,黃色表示最低點(diǎn)的變化。
在這里插入圖片描述

上圖采用的是對(duì)視頻中的運(yùn)動(dòng)進(jìn)行計(jì)數(shù),與攝像頭調(diào)用是類似的方法,此方法可以直接生成結(jié)果視頻,可視化的效果對(duì)計(jì)數(shù)結(jié)果進(jìn)行驗(yàn)證。

4、結(jié)論及展望

本設(shè)計(jì)實(shí)現(xiàn)的人體姿態(tài)識(shí)別網(wǎng)絡(luò)模型雖然可以在一般設(shè)備上運(yùn)行,并且基本達(dá)到了實(shí)時(shí)檢測(cè)的效果,但總體來(lái)說(shuō)檢測(cè)速度還是偏慢。目前所達(dá)到的水平還有待提升,需要在保證精度的情況下,進(jìn)一步進(jìn)行輕量化。

本文中的人體姿態(tài)識(shí)別的過(guò)程中攝像頭不移動(dòng)并且固定在一個(gè)位置,因此只能檢測(cè)到特定區(qū)域的人體動(dòng)作姿態(tài),如果人體所在位置超出該攝像頭的覆蓋范圍,那么系統(tǒng)就無(wú)法識(shí)別人體的動(dòng)作姿態(tài)。所以下一步可以考慮將攝像機(jī)安裝在機(jī)器人上,通過(guò)激光雷達(dá)和目標(biāo)跟蹤等技術(shù)控制機(jī)器人對(duì)人體進(jìn)行實(shí)時(shí)跟蹤,通過(guò)移動(dòng)的攝像頭實(shí)時(shí)捕捉人體的動(dòng)作姿態(tài),可以在更大范圍內(nèi)更加有效的識(shí)別人體動(dòng)作姿態(tài)。

至此,文章主要內(nèi)容介紹完畢,項(xiàng)目除上述主要模塊以外,還包含訓(xùn)練數(shù)據(jù)集等文件,需要源碼的朋友關(guān)注再私信與我聯(lián)系,本人看到一定回復(fù)?。。。?/strong>

博客主頁(yè):https://blog.csdn.net/weixin_51141489,需要源碼或相關(guān)資料實(shí)物的友友請(qǐng)關(guān)注、點(diǎn)贊,私信吧!

  • 聯(lián)系方式.txt

推薦器件

更多器件
器件型號(hào) 數(shù)量 器件廠商 器件描述 數(shù)據(jù)手冊(cè) ECAD模型 風(fēng)險(xiǎn)等級(jí) 參考價(jià)格 更多信息
NC7WZ14P6X 1 Fairchild Semiconductor Corporation Inverter, LVC/LCX/Z Series, 2-Func, 1-Input, CMOS, PDSO6, 1.25 MM, ROHS COMPLIANT, EIAJ, SC-88A, SC-70, 6 PIN
$0.34 查看
CB3-3C-4M0000 1 CTS Corporation HCMOS/TTL Output Clock Oscillator, 4MHz Nom, GREEN, CERAMIC PACKAGE-4

ECAD模型

下載ECAD模型
$1.45 查看
ASEMPC-25.000MHZ-Z-T 1 Abracon Corporation MEMS OSC XO 25.0000MHZ CMOS SMD

ECAD模型

下載ECAD模型
$2.46 查看

相關(guān)推薦

電子產(chǎn)業(yè)圖譜