計算機視覺cv2入門之實時手勢檢測

????????前邊我們已經講解了使用cv2進行圖像預處理以及針對實時視頻流文件的操作方法,這里我們通過實時手勢檢測這一案例來學習和實操一下。

大致思路

  1. 根據手勢的種類以及指定手勢圖片數量來構建一個自己的手勢圖片數據集
  2. CNN模型訓練手勢圖片數據集
  3. 使用訓練好的模型進行實時預測

手勢圖片數據集的構建

????????經典的手勢圖片數據集有很多,但是都比較大,下載費時且模型訓練時間長,因此這里我決定自行采集手勢圖片來構建一個小型數據集。手勢圖片的獲取方法比較簡單,就是使用cv2.VideoCapture函數打開攝像頭來進行采集。這里我把我的方法分享給大家。

采集手勢圖片

import cv2
import os
DATASET_DIR='GesturesPhotos'#保存所有待采集手勢的圖片的文件夾的路徑
gesture_kinds=5#手勢種類:單手可以是1-10,我這里是1-5
photo_num=10#圖片數量
classes=list(range(1,gesture_kinds+1,1))#使用1-gesture_kinds來表示所有待預測類別
###############################################
gestures=photo_num//gesture_kinds*classes#photo_num//gesture_kinds=10//5=2,2*[1,2,3,4,5]=[1,2,3,4,5,1,2,3,4,5]
gestures.extend(classes[:photo_num%gesture_kinds])#photo_num%5=10%5=0,extend([:0])相當于extend([])
'''
經過這兩步運算,gestures為長度與圖片數量一致且由類別構成的列表
gestures主要用來標定每次采集的種類
比如,gesture_kinds=5,photo_num=7,手勢種類為5,那么這7次要采集的順序為[1,2,3,4,5,1,2]
'''
###############################################
os.makedirs(DATASET_DIR, exist_ok=True)#exist_ok=True可以避免二次采集時重建新文件夾
def capture_gestures(gesture:str,count:int):'''Args:gesture:每次采集的手勢,要標記在視頻中,防止忘記采集的手勢是多少導致實際類別與真實采集結果不一致從而成為噪聲!\ncount:用來命名每次保存的圖片,這里直接用記錄圖片數量來命名\n'''cv2.namedWindow('Data Collection', cv2.WND_PROP_FULLSCREEN)cv2.setWindowProperty('Data Collection', cv2.WND_PROP_FULLSCREEN, cv2.WINDOW_FULLSCREEN)cap=cv2.VideoCapture(0)print(f'采集手勢{gesture}(按ESC保存并退出)') while True:ret,frame=cap.read()if not ret: breakroi=frame[160:440,50:250]#roi區域,可以自行修改cv2.rectangle(frame, (50,160),(250,440),(0,255,0), 2)#roi區域處繪制方框cv2.putText(frame,text=f'No.{count+1} Photo gesture {gesture}',org=(250,100),fontScale=2,thickness=5,color=(0,0,255),fontFace=1)cv2.imshow(f'Data Collection',frame)key=cv2.waitKey(1)if key==27:#按下ESC保存并退出img_path=f'{DATASET_DIR}/{count}.jpg'cv2.imwrite(img_path,roi)break cap.release()cv2.destroyAllWindows()
for i in range(len(gestures)):capture_gestures(gestures[i],i)

?????????運行上述代碼后,便可以開始采集手勢圖片了,這里我使用上述代碼總共采集了200張圖片用于后續CNN模型的訓練。?

說明

????????采集時,將右手放置在視頻中的綠色框內,盡可能的放置在中央,gesture后的數字表示當前要表示的手勢種類。如果采集時出現錯誤,那么只需要刪除掉原來的圖片,自行指定新的類別(gesture)以及原來圖片的編號,調用一次capture_gestures函數重新采集即可。

采集效果?

采集結果(0-199 40組1-5的手勢圖片)

????????這里我沒有對背景進行太多處理,如果有大佬愿意,可以嘗試將采集到的圖片的背景虛化,突出手掌主體。

?數據預處理

? ? ? ?????這里的數據預處理主要就是將我們的圖像數據劃分訓練集與測試集后轉換為tensor類型的DataLoder。

#數據預處理
from torch.utils.data import Dataset, DataLoader
import torch
import torch.nn as nn
import torch.optim as optim
from torchvision import transforms
class GestureDataset(Dataset):def __init__(self, data_dir=DATASET_DIR,gesture_kinds=gesture_kinds,transform=None):self.data_dir = data_dirself.transform = transformself.image_paths = []self.labels = []# 讀取數據集for img_name in os.listdir(data_dir):if img_name.endswith('.jpg'):self.image_paths.append(os.path.join(data_dir, img_name))self.labels.append(int(img_name.split('.')[0])%gesture_kinds)#0-4對于1-5def __len__(self):return len(self.image_paths)def __getitem__(self, idx):img_path=self.image_paths[idx]image=cv2.imread(img_path)image=cv2.cvtColor(image, cv2.COLOR_BGR2RGB)  # 轉換為RGBlabel=self.labels[idx]if self.transform:image=self.transform(image)return image, labeldef process_data(data_dir=DATASET_DIR, batch_size=4):# 數據預處理transform = transforms.Compose([transforms.ToPILImage(),transforms.Resize((64, 64)),transforms.ToTensor(),transforms.Normalize(mean=[0.5, 0.5, 0.5], std=[0.5, 0.5, 0.5])])dataset=GestureDataset(data_dir, transform=transform)train_size=int(0.8 * len(dataset))test_size=len(dataset) - train_sizetrain_dataset, test_dataset = torch.utils.data.random_split(dataset, [train_size, test_size])train_loader=DataLoader(train_dataset, batch_size=batch_size, shuffle=True)test_loader=DataLoader(test_dataset, batch_size=batch_size, shuffle=False)return train_loader, test_loader

CNN模型訓練

? ? ? ? 考慮到我的數據集比較少且該分類問題比較簡單,所以這里我的模型也沒有太復雜只是使用了2層卷積操作。倘若你的數據集比較大,分類種類比較多,可以嘗試使用一些其他的CNN模型,比如mobilenet,resnet等。

#CNN模型
class GestureCNN(nn.Module):def __init__(self, num_classes=5):super(GestureCNN, self).__init__()self.conv1=nn.Conv2d(3, 16, kernel_size=3, stride=1, padding=1)self.relu=nn.ReLU()self.maxpool=nn.MaxPool2d(kernel_size=2, stride=2)self.conv2=nn.Conv2d(16, 32, kernel_size=3, stride=1, padding=1)self.fc1=nn.Linear(32*16*16, 128)self.fc2=nn.Linear(128, num_classes)def forward(self, x):x=self.conv1(x)x=self.relu(x)x=self.maxpool(x)x=self.conv2(x)x=self.relu(x)x=self.maxpool(x)x=x.view(x.size(0), -1)x=self.fc1(x)x=self.relu(x)x=self.fc2(x)return xdef train_model(train_loader, test_loader, num_epochs=10):device=torch.device('cuda' if torch.cuda.is_available() else 'cpu')model=GestureCNN(num_classes=5).to(device)criterion=nn.CrossEntropyLoss()optimizer=optim.Adam(model.parameters(), lr=0.001)for epoch in range(num_epochs):model.train()running_loss=0.0correct=0total=0for images, labels in train_loader:images=images.to(device)labels=labels.to(device)optimizer.zero_grad()outputs=model(images)loss=criterion(outputs, labels)loss.backward()optimizer.step()running_loss+=loss.item()_, predicted=torch.max(outputs.data, 1)total+=labels.size(0)correct+=(predicted==labels).sum().item()train_loss = running_loss / len(train_loader)train_acc = 100 * correct / total# 測試集評估model.eval()test_correct = 0test_total = 0with torch.no_grad():for images, labels in test_loader:images=images.to(device)labels=labels.to(device)outputs=model(images)_, predicted=torch.max(outputs.data, 1)test_total+=labels.size(0)test_correct+=(predicted==labels).sum().item()test_acc=100*test_correct/test_totalprint(f'Epoch [{epoch+1}/{num_epochs}], 'f'Train Loss: {train_loss:.4f}, 'f'Train Acc: {train_acc:.2f}%, 'f'Test Acc: {test_acc:.2f}%')# 保存模型torch.save(model.state_dict(), 'gesture_cnn.pth')print('訓練完成,模型已保存為 gesture_cnn.pth')return model

實時預測?

? ? ? ? 實時預測的思路是:打開攝像頭,獲取實時視頻流文件中的每一幀圖片中的手勢,使用訓練好的模型預測并將結果標注在視頻流文件的每一幀上。

#實時預測
def realtime_prediction(model_path='gesture_cnn.pth'):device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')#加載模型model = GestureCNN(num_classes=5).to(device)model.load_state_dict(torch.load(model_path))model.eval()#預處理transform=transforms.Compose([transforms.ToPILImage(),transforms.Resize((64, 64)),transforms.ToTensor(),transforms.Normalize(mean=[0.5, 0.5, 0.5], std=[0.5, 0.5, 0.5])])cap=cv2.VideoCapture(0)cv2.namedWindow('Gesture Recognition', cv2.WND_PROP_FULLSCREEN)cv2.setWindowProperty('Gesture Recognition', cv2.WND_PROP_FULLSCREEN, cv2.WINDOW_FULLSCREEN)CLASSES=gestureswith torch.no_grad():while True:ret, frame = cap.read()if not ret: break  # 手勢檢測區域roi = frame[160:440, 50:250]cv2.rectangle(frame, (50, 160), (250, 440), (0, 255, 0), 2)try:input_tensor = transform(cv2.cvtColor(roi, cv2.COLOR_BGR2RGB)).unsqueeze(0).to(device)output = model(input_tensor)_, pred=torch.max(output, 1)probabilities=torch.nn.functional.softmax(output[0], dim=0) confidence, pred=torch.max(probabilities, 0)confidence=confidence.item()*100 #轉換為百分比confidence=round(confidence,2)cv2.putText(frame, f'Prediction: {CLASSES[pred.item()]}', (50, 40), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 0, 255), 2)cv2.putText(frame,f'confidence:{confidence}',(70,70),cv2.FONT_HERSHEY_SIMPLEX,0.5, (0, 0, 255), 2)except Exception as e:print(f"預測錯誤: {e}")cv2.imshow('Gesture Recognition', frame)if cv2.waitKey(1)==27: breakcap.release()cv2.destroyAllWindows()train_loader, test_loader = process_data()
model=train_model(train_loader, test_loader, num_epochs=10)
realtime_prediction()

?

效果:

?

cv2不支持中文字體,因此只能使用英文來標注……?

總結

????????以上便是計算機視覺cv2入門之實時手勢檢測的所有內容,如果你感到本文對你有用,還勞駕各位一鍵三連支持一下博主。

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

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

相關文章

Java 安全:如何防止 SQL 注入與 XSS 攻擊?

Java 安全:如何防止 SQL 注入與 XSS 攻擊? 在 Java 開發領域,安全問題至關重要,而 SQL 注入和 XSS 攻擊是兩種常見的安全威脅。本文將深入探討如何有效防止這兩種攻擊,通過詳細代碼實例為您呈現解決方案。 一、SQL 注…

Itext進行PDF的編輯開發

這周寫了一周的需求,是制作一個PDF生成功能,其中用到了Itext來制作PDF的視覺效果。其中一些功能不是很懂,僅作記錄,若要學習請仔細甄別正確與否。 開始之前,我還是想說,這傻福需求怎么想出來的&#xff0c…

android編譯使用共享緩存

注意 服務器端與客戶端系統的版本號需為Ubuntu20.04ccache版本不能低于4.4執行用戶需要為sudo權限服務器端nfs目錄權限必須為nobody:nogroup 一、服務端配置: 在服務器192.168.60.142上配置 NFS 共享 1.安裝 NFS 服務器: 1 sudo apt-get install nfs…

工作中sql總結

sql總結 場景1分組后失敗的成功數據帶入場景2完全性質的一對一匹配場景3虛擬戶的特殊匹配場景4多對多匹配場景5一對一匹配場景6 一對多匹配 場景1分組后失敗的成功數據帶入 現有一批交易表的數據,根據戶名,日期,金額分組,存在TRA…

QML FontDialog:使用FontDialog實現字體選擇功能

目錄 引言相關閱讀FontDialog基本介紹字體屬性 實例演示項目結構代碼實現Main.qmlmain.cpp 代碼解析運行效果 總結 引言 在桌面應用程序開發中,字體選擇是一個常見的需求。Qt Quick提供了FontDialog組件來實現這一功能。本文將介紹如何在Qt Quick應用程序中使用Fon…

MCP(3):在CherryStudio中使用MCPServer

上一文章講述了如何新建一個MCP Server,并在MCP Inspector完成測試。本文講述如何在CherryStudio中進行測試。 Cherry Studio 是一款由 CherryHQ 開發的多模型支持的 AI 桌面助手,兼容 Windows、Linux 和 macOS 系統,旨在為用戶提供更便捷、…

面試題-鏈表(2)

1.合并兩個有序鏈表: 21. 合并兩個有序鏈表 - 力扣(LeetCode) public ListNode mergeTwoLists(ListNode headA, ListNode headB){ListNode newheadnew ListNode(-1);ListNode curnewhead;while(headA!null&&headB!null){if(headA.va…

微軟Entra新安全功能引發大規模賬戶鎖定事件

誤報觸發大規模鎖定 多家機構的Windows管理員報告稱,微軟Entra ID新推出的"MACE"(泄露憑證檢測應用)功能在部署過程中產生大量誤報,導致用戶賬戶被大規模鎖定。這些警報和鎖定始于昨夜,部分管理員認為屬于誤…

【MATLAB第117期】#源碼分享 | 基于MATLAB的SSM狀態空間模型多元時間序列預測方法(多輸入單輸出)

【MATLAB第117期】#源碼分享 | 基于MATLAB的SSM狀態空間模型多元時間序列預測方法(多輸入單輸出) 引言 本文使用狀態空間模型實現失業率遞歸預測,狀態空間模型(State Space Model, SSM)是一種用于描述動態系統行為的…

谷歌瀏覽器搜索后的頁面總是覆蓋當前頁面

最近將搜索引擎換為谷歌后,發現,每次搜索完的結果頁面總是覆蓋當前頁面,非常不方便,在瀏覽器設置中又找不到類似設置的選項,然后終于在一個博主“如何設置使谷歌瀏覽器打開鏈接自動跳轉到新標簽頁而不是覆蓋當前頁面?…

記錄學習的第三十天

今天終于又開始寫博客了。 還是滑動窗口問題,這段時間不出意外都是這了 上面的思路是我自己做的,但是不知道為什么不行,有沒有大佬能指點一下我。 接下來這道題是進階的。不過我之前的基礎都做的很艱難,道阻且長啊。

QTextDocument 入門

一、QTextDocument QTextDocument 是 Qt 中用于處理富文本文檔的核心類,支持文本格式、圖片、表格等復雜內容。 1. QTextDocument 入門 1.1 基本概念 QTextDocument 是 Qt 中用于處理富文本內容的核心類,它提供了: 結構化文本存儲&#x…

WebRTC服務器Coturn服務器相關測試工具

1、概述 在安裝開源的webrtc服務器coturn服務器后,會附帶安裝coturn的相關工具,主要有以下幾種工具 2、turnadmin工具 說明:服務器命令行工具,提供添加用戶、添加管理員、生成TURN密鑰等功能,turnadmin -h查看詳細用…

如何創建Vue3工程

1.首先下載環境 (默認下好了VS code) Node.js: Node.js 中文網 — 下載 Node.js 選擇要下載的版本 檢查環境: 在命令行中輸入 node ,檢查版本號 2.創建工程 1.找到自己要創建工程的文件目錄,右鍵打開打開終端 在終端輸入創…

基于大模型的肛裂手術全流程預測與治療方案研究報告

目錄 一、引言 1.1 研究背景與意義 1.2 研究目標與創新點 1.3 研究方法與技術路線 二、肛裂概述與大模型技術原理 2.1 肛裂的醫學定義與分類 2.2 肛裂的發病機制與臨床癥狀 2.3 大模型技術簡介 三、大模型在肛裂術前預測的應用 3.1 術前風險因素分析與數據收集 3.2 …

【趣味小游戲】--掃雷游戲

目錄 一.test.c部分 二.game.h部分 三.game.c部分 前言:前面學習了數組和函數等c語言相關知識,這篇文章我們將通過這些知識分為三個文件來完成掃雷游戲; 1.test.c //文件中寫游戲的測試邏輯 2.game.c //文件中寫游戲中函數的實現等 3.game.h. //文件中寫…

【微服務】SpringBoot制作Docker鏡像接入SkyWalking詳解

目錄 一、前言 二、SkyWalking介紹 2.1 SkyWalking是什么 2.2 SkyWalking核心功能 2.3 SkyWalking整體架構 2.4 SkyWalking主要工作流程 三、前置準備 3.1 搭建SkyWalking服務 3.1.1 下載安裝包 3.1.2 上傳服務器目錄 2.1.3 數據庫持久化配置說明 3.1.4 啟動skywalk…

從零開始構建微博爬蟲與數據分析系統

從零開始構建微博爬蟲與數據分析系統 引言 社交媒體平臺蘊含著海量的信息和數據,通過對這些數據的收集和分析,我們可以挖掘出有價值的見解。本文將詳細介紹如何構建一個完整的微博爬蟲和數據分析系統,從數據爬取、清洗、到多維度分析與可視…

深入探索RAG:用LlamaIndex為大語言模型擴展知識,實現智能檢索增強生成

大型語言模型(LLM),如ChatGPT和Llama,在回答問題方面表現出色,但它們的知識僅限于訓練時所獲取的信息。它們無法訪問私有數據,也無法在訓練截止日期之后學習新知識。那么,核心問題就是……我們如…

【延遲雙刪】簡單解析

使用場景:【高并發】情況下的做【更新操作】 什么是延遲雙刪 首次刪除:當需要更新某個數據項時,首先刪除緩存中的該項。 更新數據庫:接著,更新數據庫中的該項。 短暫延遲:然后等待一段很短的時間&#xff…