基于OpenCV+MediaPipe手部追蹤

一、技術棧

1. OpenCV(Open Source Computer Vision Library)

  • 性質:開源計算機視覺(Library)

  • 主要功能

    • 圖像/視頻的基礎處理(讀取、裁剪、濾波、色彩轉換等)

    • 特征檢測(邊緣、角點等)

    • 攝像頭標定、目標跟蹤等

  • 在項目中的作用

    • 負責視頻流的捕獲(cv2.VideoCapture

    • 圖像格式轉換(cv2.cvtColor

    • 最終結果的渲染顯示(cv2.imshow

2. MediaPipe

  • 性質:由Google開發的跨平臺機器學習框架(Framework)

  • 主要功能

    • 提供預訓練的端到端模型(如手部關鍵點、人臉網格、姿態估計等)

    • 專注于實時感知任務(低延遲、移動端優化)

  • 在項目中的作用

    • 調用mediapipe.solutions.hands模型實現21個手部關鍵點檢測

    • 輸出關鍵點坐標,并通過mpDraw可視化

二、手部關鍵點檢測

?(一)初始化

cap = cv2.VideoCapture(0)  # 通過OpenCV調用攝像頭設備。參數0:默認攝像頭(筆記本內置攝像頭)。
mpHands = mp.solutions.hands  # MediaPipe的手部關鍵點檢測模型(21個關鍵點)
hands = mpHands.Hands()  # 創建模型實例
mpDraw = mp.solutions.drawing_utils  # MediaPipe提供的繪圖工具,用于在圖像上繪制關鍵點和連線。
handLmsStyle = mpDraw.DrawingSpec(color=(0, 0, 255), thickness=5)  # 點的樣式
handConStyle = mpDraw.DrawingSpec(color=(0, 255, 0), thickness=10)  # 線的樣式
pTime = 0
cTime = 0

(二)關鍵點檢測

ret, img = cap.read()  # 從攝像頭持續讀取視頻幀。OpenCV默認格式為BGR格式if ret:imgRGB = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)  # 格式轉換,MediaPipe模型要求輸入為RGB格式# 手部關鍵點檢測result = hands.process(imgRGB)# print(result.multi_hand_landmarks)imgHeight = img.shape[0]imgWidth = img.shape[1]

?重要代碼解釋:

 result = hands.process(imgRGB)
  • 底層過程:

????????圖像輸入MediaPipe手部模型

????????模型輸出包含:

????????????????multi_hand_landmarks:21個關鍵點的歸一化坐標(0~1之間)

????????????????multi_handedness:左右手判斷

  • result 數據結構

????????類型:List(列表)

????????內容:每個元素代表一只手的21個關鍵點數據(因此result.multi_hand_landmarks最多? ? ? ? ? ? ? ? ? 有兩個元素)

????????層級關系:

result.multi_hand_landmarks[0]  # 第1只手.landmark[0]                  # 第1個關鍵點.x                          # 歸一化x坐標 (0.0~1.0).y                          # 歸一化y坐標 (0.0~1.0).z                          # 相對深度(值越小越靠近攝像頭)

(三)可視化

         # 關鍵點可視化if result.multi_hand_landmarks:for handLms in result.multi_hand_landmarks:  # 遍歷每只檢測到的手mpDraw.draw_landmarks(img, handLms, mpHands.HAND_CONNECTIONS, handLmsStyle,handConStyle)  # 繪制手部關鍵點和骨骼連線for i, lm in enumerate(handLms.landmark):  # 遍歷21個關鍵點xPos = int(lm.x * imgWidth)  # 將歸一化x坐標轉換為像素坐標yPos = int(lm.y * imgHeight)  # 將歸一化y坐標轉換為像素坐標cv2.putText(img, str(i), (xPos - 25, yPos + 5), cv2.FONT_HERSHEY_SIMPLEX, 0.4, (0, 0, 255),2)  # 在關鍵點旁標注索引數字if i == 4:cv2.circle(img, (xPos, yPos), 20, (166, 56, 56), cv2.FILLED)print(i, xPos, yPos)  # 用深藍色實心圓高亮標記拇指尖

??重要代碼解釋:

mpDraw.draw_landmarks(img, handLms, mpHands.HAND_CONNECTIONS, handLmsStyle, handConStyle)
  • 功能:繪制手部關鍵點和骨骼連線

  • 參數詳解

    • img:目標圖像(OpenCV格式)

    • handLms:當前手的關鍵點數據

    • mpHands.HAND_CONNECTIONS:預定義的關鍵點連接關系(如點0-1相連,點1-2相連等)

    • handLmsStyle:關鍵點繪制樣式(紅色圓點,厚度5)

    • handConStyle:連接線樣式(綠色線條,厚度10)

xPos = int(lm.x * imgWidth)  # 將歸一化x坐標轉換為像素坐標
yPos = int(lm.y * imgHeight) # 將歸一化y坐標轉換為像素坐標
  • 坐標轉換公式

像素坐標 = 歸一化坐標 × 圖像尺寸

????????示例:

????????????????若圖像寬度imgWidth=640,某點lm.x=0.5 → xPos=320

????????????????若圖像高度imgHeight=480,某點lm.y=0.25 → yPos=120

cv2.putText(img, str(i), (xPos - 25, yPos + 5), cv2.FONT_HERSHEY_SIMPLEX, 0.4, (0, 0, 255),2)  # 在關鍵點旁標注索引數字

(四)完整代碼

import cv2
import mediapipe as mp
import timecap = cv2.VideoCapture(0)  # 通過OpenCV調用攝像頭設備。參數0:默認攝像頭(筆記本內置攝像頭)。
mpHands = mp.solutions.hands  # MediaPipe的手部關鍵點檢測模型(21個關鍵點)
hands = mpHands.Hands()  # 創建模型實例
mpDraw = mp.solutions.drawing_utils  # MediaPipe提供的繪圖工具,用于在圖像上繪制關鍵點和連線。
handLmsStyle = mpDraw.DrawingSpec(color=(0, 0, 255), thickness=5)  # 點的樣式
handConStyle = mpDraw.DrawingSpec(color=(0, 255, 0), thickness=10)  # 線的樣式
pTime = 0
cTime = 0while True:ret, img = cap.read()  # 從攝像頭持續讀取視頻幀。OpenCV默認格式為BGR格式if ret:imgRGB = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)  # 格式轉換,MediaPipe模型要求輸入為RGB格式# 手部關鍵點檢測result = hands.process(imgRGB)# print(result.multi_hand_landmarks)imgHeight = img.shape[0]imgWidth = img.shape[1]# 關鍵點可視化if result.multi_hand_landmarks:for handLms in result.multi_hand_landmarks:  # 遍歷每只檢測到的手mpDraw.draw_landmarks(img, handLms, mpHands.HAND_CONNECTIONS, handLmsStyle,handConStyle)  # 繪制手部關鍵點和骨骼連線for i, lm in enumerate(handLms.landmark):  # 遍歷21個關鍵點xPos = int(lm.x * imgWidth)  # 將歸一化x坐標轉換為像素坐標yPos = int(lm.y * imgHeight)  # 將歸一化y坐標轉換為像素坐標cv2.putText(img, str(i), (xPos - 25, yPos + 5), cv2.FONT_HERSHEY_SIMPLEX, 0.4, (0, 0, 255),2)  # 在關鍵點旁標注索引數字if i == 4:cv2.circle(img, (xPos, yPos), 20, (166, 56, 56), cv2.FILLED)print(i, xPos, yPos)  # 用深藍色實心圓高亮標記拇指尖cTime = time.time()fps = 1 / (cTime - pTime)pTime = cTimecv2.putText(img, f"FPS:{int(fps)}", (30, 50), cv2.FONT_HERSHEY_SIMPLEX, 1, (255, 0, 0), 3)cv2.imshow('img', img)if cv2.waitKey(1) == ord('q'):break

三、識別手指個數

(一)識別原理

1. 四指的判斷

# 處理食指到小指
for i in range(1, 5):if handLms.landmark[fingerTips[i]].y < handLms.landmark[fingerTips[i] - 2].y:fingerState.append(1)  # 伸出else:fingerState.append(0)  # 彎曲

關鍵點:當食指遠端指間關節(DIP,索引點8)在圖像坐標系中的垂直位置高于近端指間關節(PIP,索引點6)時,即滿足:y8<y6。

2. 拇指的判斷

        # 鏡像翻轉修正左右手問題img = cv2.flip(img, 1)...# 處理拇指(默認掌心朝鏡頭)if handType == 'Right':  # 對于右手if handLms.landmark[fingerTips[0]].x < handLms.landmark[fingerTips[0] - 1].x:fingerState.append(1)  # 右手拇指伸出else:fingerState.append(0)  # 右手拇指彎曲else:  # 對于左手if handLms.landmark[fingerTips[0]].x > handLms.landmark[fingerTips[0] - 1].x:fingerState.append(1)  # 左手拇指伸出else:fingerState.append(0)  # 左手拇指彎曲

鏡像翻轉的必要性:
MediaPipe基于深度卷積神經網絡(CNN)架構,通過學習手部關鍵點的空間分布模式來區分左手和右手。因此會將拇指在圖像左側的手識別為物理右手。而攝像頭原始畫面中物理右手拇指實際位于右側,因此必須通過cv2.flip(img, 1)水平鏡像翻轉圖像,才能使MediaPipe正確識別手型。

坐標判斷的底層邏輯:
所有關鍵點坐標均基于鏡像翻轉后的圖像空間,物理右手在翻轉后的坐標系中表現為thumb_tip.x < thumb_ip.x。MediaPipe內部已自動處理坐標轉換,開發者直接使用檢測到的歸一化坐標即可,無需額外計算原始坐標。

(二)完整代碼

import cv2
import mediapipe as mp
import timecap = cv2.VideoCapture(0)
mpHands = mp.solutions.hands
hands = mpHands.Hands()
mpDraw = mp.solutions.drawing_utils
handLmsStyle = mpDraw.DrawingSpec(color=(0, 0, 255), thickness=5)  # 關鍵點樣式
handConStyle = mpDraw.DrawingSpec(color=(0, 255, 0), thickness=10)  # 連接線樣式
pTime = 0# 定義手指關鍵點
fingerTips = [4, 8, 12, 16, 20]  # 拇指、食指、中指、無名指、小指的指尖關鍵點索引while True:ret, img = cap.read()if ret:# 鏡像翻轉修正左右手問題img = cv2.flip(img, 1)imgRGB = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)result = hands.process(imgRGB)imgHeight, imgWidth, _ = img.shapeif result.multi_hand_landmarks:for handLms, handInfo in zip(result.multi_hand_landmarks, result.multi_handedness):mpDraw.draw_landmarks(img, handLms, mpHands.HAND_CONNECTIONS, handLmsStyle, handConStyle)# 獲取手的類型:左手還是右手handType = handInfo.classification[0].labelhandLabel = "Right Hand" if handType == 'Right' else "Left Hand"# 手勢計數fingerState = []  # 記錄每根手指是否伸出# 處理食指到小指for i in range(1, 5):if handLms.landmark[fingerTips[i]].y < handLms.landmark[fingerTips[i] - 2].y:fingerState.append(1)  # 伸出else:fingerState.append(0)  # 彎曲# 處理拇指(默認掌心朝鏡頭)if handType == 'Right':  # 對于右手if handLms.landmark[fingerTips[0]].x < handLms.landmark[fingerTips[0] - 1].x:fingerState.append(1)  # 右手拇指伸出else:fingerState.append(0)  # 右手拇指彎曲else:  # 對于左手if handLms.landmark[fingerTips[0]].x > handLms.landmark[fingerTips[0] - 1].x:fingerState.append(1)  # 左手拇指伸出else:fingerState.append(0)  # 左手拇指彎曲# 計算伸出的手指數量fingerCount = sum(fingerState)# 在圖像上顯示手指數量cv2.putText(img, f"{handLabel}: {fingerCount} Fingers", (50, 100),cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 255, 0), 3)# 計算 FPScTime = time.time()fps = 1 / (cTime - pTime)pTime = cTimecv2.putText(img, f"FPS:{int(fps)}", (30, 50), cv2.FONT_HERSHEY_SIMPLEX, 1, (255, 0, 0), 3)cv2.imshow('Hand Tracking', img)if cv2.waitKey(1) == ord('q'):breakcap.release()
cv2.destroyAllWindows()

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/diannao/76290.shtml
繁體地址,請注明出處:http://hk.pswp.cn/diannao/76290.shtml
英文地址,請注明出處:http://en.pswp.cn/diannao/76290.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

機器學習ML極簡指南

機器學習是現代AI的核心&#xff0c;從推薦系統到自動駕駛&#xff0c;無處不在。但每個智能應用背后&#xff0c;都離不開那些奠基性的模型。本文用最簡練的方式拆解核心機器學習模型&#xff0c;助你面試時對答如流&#xff0c;穩如老G。 線性回歸 線性回歸試圖通過"最…

裝飾器模式:如何用Java打扮一個對象?

引言裝飾器模式具體實例共有接口類具體被裝飾類抽象裝飾器類具體裝飾器類 測試裝飾器模式的實際應用Java I/O 體系游戲開發中的角色裝備系統 總結 引言 在生活中&#xff0c;我們都知道一句話&#xff0c;“人靠衣裝馬靠鞍”&#xff0c;如果想要讓自己在別人眼里看起來更加好…

【Easylive】HikariCP 介紹

【Easylive】項目常見問題解答&#xff08;自用&持續更新中…&#xff09; 匯總版 HikariCP 是目前 Java 生態中最快、最輕量級的高性能 JDBC 連接池&#xff0c;被 Spring Boot 2.x 及更高版本選為 默認數據庫連接池。它的名字來源于日語“光”&#xff08;Hikari&#xf…

清晰易懂的Cursor實現AI編程從安裝到實戰TodoList開發

一、Cursor簡介與安裝部署 什么是Cursor&#xff1f; Cursor是一款基于AI的智能代碼編輯器&#xff0c;它集成了強大的AI編程助手功能&#xff0c;能夠通過自然語言交互幫助開發者生成、優化和調試代碼。與傳統的代碼編輯器不同&#xff0c;Cursor可以理解你的編程意圖&#…

【Django】教程-2-前端-目錄結構介紹

【Django】教程-1-安裝創建項目目錄結構介紹 3. 前端文件配置 3.1 目錄介紹 在app下創建static文件夾, 是根據setting中的配置來的 STATIC_URL ‘static/’ templates目錄&#xff0c;編寫HTML模板&#xff08;含有模板語法&#xff0c;繼承&#xff0c;{% static ‘xx’ …

注意!ChatGPT 全新 AI 圖像功能延遲對免費用戶開放

2025 年 3 月 25 日&#xff0c;OpenAI 正式宣布在 ChatGPT 中推出基于 GPT-4o 模型的全新原生圖像生成功能。 這一功能允許用戶通過對話生成和編輯圖像&#xff0c;支持從寫實風格到插圖風格的多種形式。OpenAI 首席執行官薩姆?奧特曼&#xff08;Sam Altman&#xff09;在社…

優化webpack打包體積思路

Webpack 打包過大的問題通常會導致頁面加載變慢&#xff0c;影響用戶體驗。可以從代碼優化、依賴優化、構建優化等多個角度入手來減少打包體積&#xff1a; 代碼優化 &#xff08;1&#xff09;按需加載&#xff08;代碼拆分&#xff09; ① 路由懶加載 如果你的項目使用 Vu…

HarmonyOS Next~鴻蒙元服務開發指南:核心功能與實踐

HarmonyOS Next&#xff5e;鴻蒙元服務開發指南&#xff1a;核心功能與實踐 一、元服務核心概念 原子化服務定義 元服務&#xff08;原子服務&#xff09;是鴻蒙系統的核心架構單元&#xff0c;具備獨立業務能力的輕量化服務模塊&#xff0c;支持免安裝、跨設備調用和智能分發…

git錯誤:fatal: detected dubious ownership in repository at xxxxxx

1、報錯說明 這個錯誤通常是由于Git倉庫目錄的擁有者或權限問題引起的。Git檢測到倉庫目錄的所有權可能存在不一致或不安全的情況。 通常導致此報錯的可能原因&#xff1a; &#xff08;1&#xff09;文件或目錄的擁有者不一致&#xff1a; 倉庫目錄中的某些文件或子目錄可能…

【計算機網絡】OSI七層模型完全指南:從比特流到應用交互的逐層拆解

OSI模型 導讀一、概念二、模型層次結構2.1 物理層&#xff08;Physical Layer&#xff09;2.2 數據鏈路層&#xff08;Data Link Layer&#xff09;?2.3 ?網絡層&#xff08;Network Layer&#xff09;?2.4 ?傳輸層&#xff08;Transport Layer&#xff09;?2.5 ?會話層&…

零基礎被迫參加CTF比賽?CTF高頻解題技巧與經驗分享

CTF&#xff08;Capture The Flag&#xff09;比賽中的高頻解題技巧通常涵蓋了以下幾類技術&#xff0c;涉及從逆向工程、二進制漏洞利用到Web安全、密碼學等多個領域。以下是一些高頻解題技巧&#xff1a; 1. 逆向工程&#xff08;Reverse Engineering&#xff09; 靜態分析&a…

markdown 文件轉 word

將 Markdown 文件轉換為 Word 文檔&#xff0c;可以使用多種方法。以下是幾種常見的方法&#xff1a; 方法1&#xff1a;使用在線轉換工具 有許多在線服務可以將 Markdown 文件轉換為 Word 文檔。例如&#xff1a; Pandoc - 一個非常流行的命令行工具&#xff0c;也可以用來轉…

【第十三屆“泰迪杯”數據挖掘挑戰賽】【2025泰迪杯】【思路篇】A題解題全流程(持續更新)

【第十三屆“泰迪杯”數據挖掘挑戰賽】【2025泰迪杯】A題解題全流程-思路&#xff08;持續更新&#xff09; 寫在前面&#xff1a; 1、A題、C題將會持續更新&#xff0c;陸續更新發布文章 2、賽題交流咨詢Q群&#xff1a;1037590285 3、全家桶依舊包含&#xff1a; 代碼、…

T11 TensorFlow入門實戰——優化器對比實驗

&#x1f368; 本文為&#x1f517;365天深度學習訓練營 中的學習紀錄博客&#x1f356; 原作者&#xff1a;K同學啊 | 接輔導、項目定制 一、前期準備 1. 導入數據 # Import the required libraries import pathlib import matplotlib.pyplot as plt import tensorflow as t…

Docker部署sprintboot后端項目

創建Docker網絡 docker network create icjs 部署Redis docker run -d \--network icjs \--name redis \-p 6379:6379 \redis:latest數據持久化 docker run --restartalways --network icjs -p 6379:6379 --name redis -v /opt/docker/redis/redis.conf:/etc/redis/redis.c…

01小游戲

問題描述 小明得到了一個長度為 nn 的字符串 ss &#xff0c;該字符串都是由數字 00 和 11 組成&#xff0c;并且下標從 11 開始&#xff0c;小明現在需要對這個字符串進行 qq 次操作&#xff0c;每次操作包含以下兩種操作之一&#xff1a; 操作 11 &#xff1a;小明查詢該字符…

Androidstudio開發,實現商品分類

文章目錄 1. 功能需求2. 代碼實現過程1. 編寫布局文件2. 創建商品分類&#xff08;Adapter&#xff09;適配器3. 實現商品分類Activity4. 在res/values/ 下新建 array.xml &#xff0c;用于添加商品分類數據5. 效果演示 6. 關于作者其它項目視頻教程介紹 1. 功能需求 顯示商品分…

Linux快速安裝docker和docker-componse步驟

在 CentOS 7 上安裝 Docker 和 Docker Compose 的步驟如下&#xff1a; 1. 安裝 Docker 1.1. 更新系統 首先&#xff0c;確保你的系統是最新版本&#xff1a; sudo yum update -y1.2. 安裝必要的包 安裝 yum-utils&#xff0c;這是管理 YUM 源的工具&#xff1a; sudo yu…

VBA代碼解決方案第二十三講 EXCEL中,如何刪除工作表中的空白行

《VBA代碼解決方案》(版權10028096)這套教程是我最早推出的教程&#xff0c;目前已經是第三版修訂了。這套教程定位于入門后的提高&#xff0c;在學習這套教程過程中&#xff0c;側重點是要理解及掌握我的“積木編程”思想。要靈活運用教程中的實例像搭積木一樣把自己喜歡的代碼…

Pytorch--tensor.view()

在 PyTorch 中&#xff0c;tensor.view() 是一個常用的方法&#xff0c;用于改變張量&#xff08;Tensor&#xff09;的形狀&#xff08;shape&#xff09;&#xff0c;但不會改變其數據本身。它類似于 NumPy 的 reshape()&#xff0c;但有一些關鍵區別。 1. 基本用法 import …