【机器视觉案例】(9) AI视觉,手势控制电脑键盘,附python完整代码
各位同學好,今天和大家分享一下如何使用 opencv+mediapipe 完成遠程手勢控制電腦鍵盤。感興趣的可以看一下我前面一篇手勢控制電腦鼠標:https://blog.csdn.net/dgvv4/article/details/122268203?spm=1001.2014.3001.5501, 把這兩個結合起來去打游戲會不會很有意思呢。先放張圖看效果。
這里用百度搜索欄做測試,搜索框中的內容和opencv顯示窗口上的內容是同步打印出來的。
工作原理:如果檢測到食指指尖關鍵點坐標在某個按鍵框的范圍內部,那么該按鍵顯示綠色;如果食指指尖和中指指尖之間的距離小于規定距離,就認為是點擊食指指尖所在的按鍵,按鍵變為紅色;設置1秒內同一個按鍵只能點擊一次,避免重復出現多個相同按鍵值。
1. 安裝工具包
# 安裝工具包
pip install opencv-contrib-python # 安裝opencv
pip install mediapipe # 安裝mediapipe
# pip install mediapipe --user #有user報錯的話試試這個
pip install cvzone # 安裝cvzone
pip install pynput # 鍵盤控制單元# 導入工具包
import numpy as np
import cv2
from cvzone.HandTrackingModule import HandDetector # 手部追蹤方法
import mediapipe as mp
import time
from pynput.keyboard import Controller # 鍵盤控制模塊
21個手部關鍵點信息如下,本節我們主要研究食指指尖"8"和中指指尖"12"的坐標信息。
2. 手部關鍵點檢測,制作虛擬鍵盤
2.1 手部關鍵點檢測
(1) cvzone.HandTrackingModule.HandDetector() ? 手部關鍵點檢測方法
參數:
mode: 默認為 False,將輸入圖像視為視頻流。它將嘗試在第一個輸入圖像中檢測手,并在成功檢測后進一步定位手的坐標。在隨后的圖像中,一旦檢測到所有 maxHands 手并定位了相應的手的坐標,它就會跟蹤這些坐標,而不會調用另一個檢測,直到它失去對任何一只手的跟蹤。這減少了延遲,非常適合處理視頻幀。如果設置為 True,則在每個輸入圖像上運行手部檢測,用于處理一批靜態的、可能不相關的圖像。
maxHands: 最多檢測幾只手,默認為 2
detectionCon: 手部檢測模型的最小置信值(0-1之間),超過閾值則檢測成功。默認為 0.5
minTrackingCon: 坐標跟蹤模型的最小置信值 (0-1之間),用于將手部坐標視為成功跟蹤,不成功則在下一個輸入圖像上自動調用手部檢測。將其設置為更高的值可以提高解決方案的穩健性,但代價是更高的延遲。如果 mode 為 True,則忽略這個參數,手部檢測將在每個圖像上運行。默認為 0.5
它的參數和返回值類似于官方函數 mediapipe.solutions.hands.Hands()
(2)cvzone.HandTrackingModule.HandDetector.findHands() ? ?找到手部關鍵點并繪圖
參數:
img: 需要檢測關鍵點的幀圖像,格式為BGR
draw: 是否需要在原圖像上繪制關鍵點及識別框
flipType: 圖像是否需要翻轉,當視頻圖像和我們自己不是鏡像關系時,設為True就可以了
返回值:
hands: 檢測到的手部信息,包含:21個關鍵點坐標,檢測框坐標及寬高,檢測框中心坐標,檢測出是哪一只手。
img: 返回繪制了關鍵點及連線后的圖像
2.2 繪制虛擬鍵盤
本節只創建鍵盤上的部分按鍵用于演示,定義類Button不需要一個一個單獨繪制矩形按鍵。使用一個循環,分別對每個按鍵實例化,將實例化結果保存在 buttonList 列表中。在第(6)步繪制鍵盤時,逐個取出實例化對象,在窗口上繪制30個按鍵。
代碼如下:
import cv2
from cvzone.HandTrackingModule import HandDetector # 導入手部檢測模塊#(1)捕捉電腦攝像頭
cap = cv2.VideoCapture(0) # 0代表自己的電腦攝像頭,1代表外接攝像頭
cap.set(3, 1280) # 設置顯示框的寬1280
cap.set(4, 720) # 設置顯示框的高720#(2)接收手部檢測方法
detector = HandDetector(mode=False, # 視頻流圖像 maxHands=1, # 最多檢測一只手detectionCon=0.5, # 最小檢測置信度minTrackCon=0.5) # 最小跟蹤置信度#(3)創建一個類用于構造鍵盤按鍵
class Button:# 初始化,按鍵的左上坐標pos(列表類型),文本信息text(字符串類型),按鍵的寬高sizedef __init__(self, pos:list, text:str, size=[90,90]):# 分配屬性self.pos = posself.text = textself.size = size# 在類的內部定義方法def draw(self, img):x1, y1 = self.pos # 矩形框的左上角坐標w, h = self.size # 矩形框的寬高# img畫板,矩形框左上角坐標,矩形框右下角坐標,顏色,-1代表顏色填充cv2.rectangle(img, (x1, y1), (x1+w, y1+h), (255,0,0), -1)# 美化一下矩形框cv2.rectangle(img, (x1, y1), (x1+w, y1+h), (255,255,0), 4)# 在矩形框上顯示字符串信息cv2.putText(img, self.text, (x1+25, y1+65), cv2.FONT_HERSHEY_COMPLEX, 1.8, (255,255,255), 3)# 創建一個列表存放鍵盤上每個按鍵的文本信息
keys = [['Q','W','E','R','T','Y','U','I','O','P'],['A','S','D','F','G','H','J','K','L',';'],['Z','X','C','V','B','N','M',',','.','/']]# 存放每一個按鍵的信息
buttonList = []# 通過循環來實例化所有按鍵
for i in range(3): # 鍵盤文本列表有三行# 分別實例化每一行的按鈕信息 for x, key in enumerate(keys[i]): #返回每個元素的索引和值# 確定每個按鍵的左上角坐標位置px = 115*x + 30 + 40*i #水平方向每兩個按鍵之間間隔115,每次換行縮進40,初始位置x=30py = 115*i + 50 # 豎直方向每兩個按鍵之間間隔115,初始位置y=50# 將實例化后的對象存放在列表中buttonList.append(Button([px, py], key))# 每次換行后px坐標重置x = 0#(4)處理每一幀圖像
while True:# 返回是否讀取成功,和讀取的幀圖像success, img = cap.read()# 翻轉圖像,讓自己和攝像頭呈鏡像關系img = cv2.flip(img, 1) # 1代表水平翻轉,0代表上下翻轉#(5)檢測手部關鍵點# 檢測手部關鍵點信息,返回手部信息hands,繪制關鍵點后的圖像imghands, img = detector.findHands(img, flipType=False)#(6)繪制鍵盤for i in range(3*len(keys[0])): # 一共有3行10列個按鍵# 調用類中的繪圖方法,顯示每個鍵盤按鍵buttonList[i].draw(img)#(7)顯示圖像cv2.imshow('video', img)# 每幀圖像滯留時間,ESC鍵退出if cv2.waitKey(1) & 0xFF==27:break# 釋放視頻資源
cap.release()
cv2.destroyAllWindows()
手部檢測結果及虛擬鍵盤如下圖所示。
3. 鎖定按鍵位置
從下面代碼的第(6)步開始是鎖定鍵盤按鍵的位置。通過 detector.findHands() 返回手部檢測信息hands列表,由0或1或2個字典組成,如果檢測到一只手就返回一個字典。字典中包含:lmList?代表21個手部關鍵點的像素坐標;bbox?代表檢測框的左上角坐標和框的寬高;center?代表檢測框的中心點的像素坐標;type?代表檢測出的是左手還是右手。
只需要知道食指指尖坐標 lmList[8] 在哪個按鍵的范圍內,并計算食指指尖和中指指尖之間的距離,距離小于某個值認為是點擊按鍵。距離計算方法,detector.findDistance(pt1, pt2, img) 傳入兩個關鍵點坐標。返回值為 distance 代表兩個關鍵點之間的距離, info 代表指尖連線的起點、中點、終點, img 代表繪制指尖連線后的圖像。
在上述代碼中補充:
import cv2
from cvzone.HandTrackingModule import HandDetector # 導入手部檢測模塊#(1)捕捉電腦攝像頭
cap = cv2.VideoCapture(0) # 0代表自己的電腦攝像頭,1代表外接攝像頭
cap.set(3, 1280) # 設置顯示框的寬1280
cap.set(4, 720) # 設置顯示框的高720#(2)接收手部檢測方法
detector = HandDetector(mode=False, # 視頻流圖像 maxHands=1, # 最多檢測一只手detectionCon=0.5, # 最小檢測置信度minTrackCon=0.5) # 最小跟蹤置信度#(3)創建一個類用于構造鍵盤按鍵
class Button:# 初始化,按鍵的左上坐標pos(列表類型),文本信息text(字符串類型),按鍵的寬高sizedef __init__(self, pos:list, text:str, size=[90,90]):# 分配屬性self.pos = posself.text = textself.size = size# 在類的內部定義方法,默認內部深藍色填充,邊框為淺藍色def draw(self, img, colorIn, colorBd):x1, y1 = self.pos # 矩形框的左上角坐標w, h = self.size # 矩形框的寬高# img畫板,矩形框左上角坐標,矩形框右下角坐標,顏色,-1代表顏色填充cv2.rectangle(img, (x1, y1), (x1+w, y1+h), colorIn, -1)# 美化一下矩形框cv2.rectangle(img, (x1, y1), (x1+w, y1+h), colorBd, 4)# 在矩形框上顯示字符串信息cv2.putText(img, self.text, (x1+25, y1+65), cv2.FONT_HERSHEY_COMPLEX, 1.8, (255,255,255), 3)# 創建一個列表存放鍵盤上每個按鍵的文本信息
keys = [['Q','W','E','R','T','Y','U','I','O','P'],['A','S','D','F','G','H','J','K','L',';'],['Z','X','C','V','B','N','M',',','.','/']]# 存放每一個按鍵的信息
buttonList = []# 矩形按鍵的顏色
colorIn = (255,0,0) # 按鍵內部填充的顏色
colorBd = (255,255,0) # 按鍵的邊框顏色 # 通過循環來實例化所有按鍵
for i in range(3): # 鍵盤文本列表有三行# 分別實例化每一行的按鈕信息 for x, key in enumerate(keys[i]): #返回每個元素的索引和值# 確定每個按鍵的左上角坐標位置px = 115*x + 30 + 40*i #水平方向每兩個按鍵之間間隔115,每次換行縮進40,初始位置x=30py = 115*i + 50 # 豎直方向每兩個按鍵指尖間隔115,初始位置y=50# 將實例化后的對象存放在列表中buttonList.append(Button([px, py], key))# 每次換行后px坐標重置x = 0#(4)處理每一幀圖像
while True:# 返回是否讀取成功,和讀取的幀圖像success, img = cap.read()# 翻轉圖像,讓自己和攝像頭呈鏡像關系img = cv2.flip(img, 1) # 1代表水平翻轉,0代表上下翻轉#(5)繪制鍵盤for i in range(3*len(keys[0])): # 一共有3行10列個按鍵# 調用類中的繪圖方法,顯示每個鍵盤按鍵buttonList[i].draw(img, colorIn, colorBd)#(6)檢測手部關鍵點# 檢測手部關鍵點信息,返回手部信息hands,繪制關鍵點后的圖像imghands, img = detector.findHands(img, flipType=False)# 如果檢測到手了,才執行下一步if hands:# 獲取21個手部關鍵點信息lmList = hands[0]['lmList']# 獲取食指指尖關鍵點坐標x1, y1 = lmList[8]# 獲取中指指尖關鍵點坐標x2, y2 = lmList[12]#(7)遍歷所有的按鍵,檢查食指指尖在哪個按鍵的范圍內for index, button in enumerate(buttonList): # botton存放的是類實例化后的對象# 所在矩形的左上坐標和寬高x0, y0 = button.posw, h = button.size# 如果食指指尖在某個矩形框中,改變鍵盤按鍵顏色if x0<=x1<=x0+w and y0<=y1<=y0+h:# 按鍵內部填充顏色cv2.rectangle(img, (x0, y0), (x0+w, y0+h), (0,255,0), -1)# 按鍵邊框顏色cv2.rectangle(img, (x0, y0), (x0+w, y0+h), (0,0,255), 4)# 按鍵上的字符串cv2.putText(img, button.text, (x0+25, y0+65), cv2.FONT_HERSHEY_COMPLEX, 1.8, (255,255,255), 3)#(8)計算食指和中指指尖之間的距離distance, _, img = detector.findDistance((x1,y1), (x2,y2), img)# 如果指尖距離小于80,認為是點擊按鍵if distance<50:# 點擊按鍵改變按鍵顏色cv2.rectangle(img, (x0, y0), (x0+w, y0+h), (0,0,255), -1)# 按鍵上的字符串cv2.putText(img, button.text, (x0+25, y0+65), cv2.FONT_HERSHEY_COMPLEX, 1.8, (255,255,255), 3)#(9)顯示圖像cv2.imshow('video', img)# 每幀圖像滯留時間,ESC鍵退出if cv2.waitKey(1) & 0xFF==27:break# 釋放視頻資源
cap.release()
cv2.destroyAllWindows()
顯示結果如下,如果食指在某個按鍵矩形中,并且食指和中指之間的距離大于規定值,那么這個按鍵填充綠色,邊界框變紅色。食指在某個按鍵矩形中,并且食指和中指之間的距離小于規定值,那么這個按鍵和邊界框都填充紅色。
4. 激活鍵盤按鍵
下面的第(9)步,為了驗證點擊按鍵時?opencv 畫面上的點擊內容和百度搜索框的輸入內容是否同步,在opencv畫面上創建一個矩形框用來顯示字符。
通過 keyboard.press() 獲得鍵盤響應,輸入值是鍵盤上的某個字符,表示點擊鍵盤上的該字符。
由于每一幀播放的非常快,可能只點擊了一次按鍵,卻打印出來很多相同的字符。使用休眠函數?time.sleep(t)?,每點擊一次按鍵就暫停程序的執行,暫停 t 秒時間。??
import cv2
from cvzone.HandTrackingModule import HandDetector # 導入手部檢測模塊
from time import sleep
from pynput.keyboard import Controller # 鍵盤控制單元#(1)捕捉電腦攝像頭
cap = cv2.VideoCapture(0) # 0代表自己的電腦攝像頭,1代表外接攝像頭
cap.set(3, 1280) # 設置顯示框的寬1280
cap.set(4, 720) # 設置顯示框的高720#(2)接收手部檢測方法
detector = HandDetector(mode=False, # 視頻流圖像 maxHands=1, # 最多檢測一只手detectionCon=0.5, # 最小檢測置信度minTrackCon=0.5) # 最小跟蹤置信度# 接收鍵盤控制單元
keyboard = Controller()#(3)創建一個類用于構造鍵盤按鍵
class Button:# 初始化,按鍵的左上坐標pos(列表類型),文本信息text(字符串類型),按鍵的寬高sizedef __init__(self, pos:list, text:str, size=[90,90]):# 分配屬性self.pos = posself.text = textself.size = size# 在類的內部定義方法,默認內部深藍色填充,邊框為淺藍色def draw(self, img, colorIn, colorBd):x1, y1 = self.pos # 矩形框的左上角坐標w, h = self.size # 矩形框的寬高# img畫板,矩形框左上角坐標,矩形框右下角坐標,顏色,-1代表顏色填充cv2.rectangle(img, (x1, y1), (x1+w, y1+h), colorIn, -1)# 美化一下矩形框cv2.rectangle(img, (x1, y1), (x1+w, y1+h), colorBd, 4)# 在矩形框上顯示字符串信息cv2.putText(img, self.text, (x1+25, y1+65), cv2.FONT_HERSHEY_COMPLEX, 1.8, (255,255,255), 3)# 創建一個列表存放鍵盤上每個按鍵的文本信息
keys = [['Q','W','E','R','T','Y','U','I','O','P'],['A','S','D','F','G','H','J','K','L',';'],['Z','X','C','V','B','N','M',',','.','/']]# 保存最終的輸出結果
finalText = ''# 存放每一個按鍵的信息
buttonList = []# 矩形按鍵的顏色
colorIn = (255,0,0) # 按鍵內部填充的顏色
colorBd = (255,255,0) # 按鍵的邊框顏色 # 通過循環來實例化所有按鍵
for i in range(3): # 鍵盤文本列表有三行# 分別實例化每一行的按鈕信息 for x, key in enumerate(keys[i]): #返回每個元素的索引和值# 確定每個按鍵的左上角坐標位置px = 115*x + 30 + 40*i #水平方向每兩個按鍵之間間隔115,每次換行縮進40,初始位置x=30py = 115*i + 50 # 豎直方向每兩個按鍵指尖間隔115,初始位置y=50# 將實例化后的對象存放在列表中buttonList.append(Button([px, py], key))# 每次換行后px坐標重置x = 0#(4)處理每一幀圖像
while True:# 返回是否讀取成功,和讀取的幀圖像success, img = cap.read()# 翻轉圖像,讓自己和攝像頭呈鏡像關系img = cv2.flip(img, 1) # 1代表水平翻轉,0代表上下翻轉#(5)繪制鍵盤for i in range(3*len(keys[0])): # 一共有3行10列個按鍵# 調用類中的繪圖方法,顯示每個鍵盤按鍵buttonList[i].draw(img, colorIn, colorBd)#(6)檢測手部關鍵點# 檢測手部關鍵點信息,返回手部信息hands,繪制關鍵點后的圖像imghands, img = detector.findHands(img, flipType=False)# 如果檢測到手了,才執行下一步if hands:# 獲取21個手部關鍵點信息lmList = hands[0]['lmList']# 獲取食指指尖關鍵點坐標x1, y1 = lmList[8]# 獲取中指指尖關鍵點坐標x2, y2 = lmList[12]#(7)遍歷所有的按鍵,檢查食指指尖在哪個按鍵的范圍內for index, button in enumerate(buttonList): # botton存放的是類實例化后的對象# 所在矩形的左上坐標和寬高x0, y0 = button.posw, h = button.size# 如果食指指尖在某個矩形框中,改變鍵盤按鍵顏色if x0<=x1<=x0+w and y0<=y1<=y0+h:# 按鍵內部填充顏色cv2.rectangle(img, (x0, y0), (x0+w, y0+h), (0,255,0), -1)# 按鍵邊框顏色cv2.rectangle(img, (x0, y0), (x0+w, y0+h), (0,0,255), 4)# 按鍵上的字符串cv2.putText(img, button.text, (x0+25, y0+65), cv2.FONT_HERSHEY_COMPLEX, 1.8, (255,255,255), 3)#(8)計算食指和中指指尖之間的距離distance, _, img = detector.findDistance((x1,y1), (x2,y2), img)# 如果指尖距離小于50,認為是點擊按鍵if distance<50:# 點擊按鍵改變按鍵顏色cv2.rectangle(img, (x0, y0), (x0+w, y0+h), (0,0,255), -1)# 按鍵上的字符串cv2.putText(img, button.text, (x0+25, y0+65), cv2.FONT_HERSHEY_COMPLEX, 1.8, (255,255,255), 3)# 點擊鍵盤上某個按鍵keyboard.press(button.text) # 鍵盤上的某個符號,'A'# 在文本框中顯示該字符finalText += button.text# 點擊一次后,0.2秒之后才能再點一次sleep(0.2)#(9)創建虛擬文本框# 文本框內部cv2.rectangle(img, (100, 450), (700, 550), (255,255,255), -1)# 文本框邊框cv2.rectangle(img, (100, 450), (700, 550), (0,0,0), 5) # 按鍵上的字符串cv2.putText(img, finalText, (110, 525), cv2.FONT_HERSHEY_COMPLEX, 1.8, (0,0,0), 3)#(10)顯示圖像cv2.imshow('video', img)# 每幀圖像滯留時間,ESC鍵退出if cv2.waitKey(1) & 0xFF==27:break# 釋放視頻資源
cap.release()
cv2.destroyAllWindows()
顯示結果如圖,當食指在某個按鍵的范圍內,并且指尖距離大于規定值,認為是搜索按鍵,按鍵變成綠色,邊界框變成紅色;如果指尖距離小于規定值,認為是點擊按鍵,按鍵內部和邊框都變成紅色。使用百度搜索框測試,能夠實現同步輸入。
總結
以上是生活随笔為你收集整理的【机器视觉案例】(9) AI视觉,手势控制电脑键盘,附python完整代码的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【面向对象编程】(1) 类实例化的基本方
- 下一篇: 【面向对象编程】(2) 类属性的定义及使