【MediaPipe】(4) AI视觉,远程手势调节电脑音量,附python完整代码
各位同學好,今天和大家分享一下如何使用MediaPipe完成手勢調節電腦音量,先放張圖看效果。
注意!!
本節需要用到手部關鍵點的實時跟蹤,我已經在之前的文章中詳細寫過了,本節會直接使用,有疑問的同學可以看我的這一篇文章:【MediaPipe】(1) AI視覺,手部關鍵點實時跟蹤,附python完整代碼
1. 導入工具包,編寫主程序
# 安裝opencv
pip install opencv-contrib-python
# 安裝mediapipe
pip install mediapipe
# pip install mediapipe --user  #有user報錯的話試試這個# 安裝之后導入各個包
import cv2  #opencv
import mediapipe as mp
import time 
把工具包導入后,編寫主程序,主程序只讀取視頻圖像,將每一幀圖像傳給自定義函數。所有的手勢處理均在自定義函數中完成。
handtracking是存放自定義函數的.py文件名。cap.read()讀取視頻幀圖像,每執行一次,就讀取一幀圖像,返回值succes中存放視頻是否成功被打開,img存放每一幀圖像信息。
htm.handDetector() 中?handDetector()?是自定義的?handtracking.py?文件中定義的函數,將讀取的圖像數據img傳給這個函數。
import cv2
import numpy as np
import time
import handtracking as htm#(1)獲取攝像頭
cap = cv2.VideoCapture(0) # 0代表自己電腦的攝像頭
cap.set(3, 1080)  # 設置相機圖像寬度1080
cap.set(4, 720)  # 設置相機圖像高度720pTime = 0  # 處理第一幀圖像的起始時間#(3)處理每一幀圖像
while True:# 返回是否打開攝像頭,以及每一幀的圖像success, img = cap.read()# 調用手部關鍵點檢測函數# 返回拇指"4"和食指"8"的坐標信息,存放在lmList中# 返回圖像img,已經在食指和拇指關鍵點上畫圈# 返回音量volimg, lmList  = htm.handDetector(img) # 記錄處理每幀圖像所花的時間cTime = time.time()fps = 1/(cTime-pTime)  # 計算fpspTime = cTime  # 更新下一幀圖像處理的起始時間# 把fps值顯示在圖像上,img畫板;fps變成字符串;顯示的位置;設置字體;字體大小;字體顏色;線條粗細cv2.putText(img, f'FPS: {str(int(fps))}', (10,50), cv2.FONT_HERSHEY_COMPLEX, 2, (0,255,0), 3) # 顯示圖像,輸入窗口名及圖像數據cv2.imshow('image', img)    if cv2.waitKey(1) & 0xFF==27:  #每幀滯留1毫秒后消失,ESC鍵退出break# 釋放視頻資源
cap.release()
cv2.destroyAllWindows()
 
2. 拇指和食指間的連線
新定義一個文件?handtracking.py?,放在和主程序的相同文件夾下。
下面的程序中(1)和(2)部分有不明白的,可看一下我的手部關鍵點檢測的文章:【MediaPipe】(1) AI視覺,手部關鍵點實時跟蹤,附python完整代碼
手部關鍵點標記如下圖所示:
下面我解釋一下如何繪制指尖連線,需要繪制拇指和食指間的連線,首先獲取這兩個關鍵點的坐標,每一個索引index對應一個關鍵點的xy坐標,每一幀圖像有21個索引,也就有21個關鍵點坐標。由上圖可知,拇指對應的索引為4,食指對應的索引為8,分別將其坐標表示為(x1, y1)和(x2, y2),接下去我們只需要處理這兩個關鍵點即可。
使用 cv2.circle() 以這兩個關鍵點為圓心畫圓,在屏幕上突出顯示出兩個關鍵點。使用 cv2.line() 給出線段的起點和終點坐標,繪制兩指間的連線。
import mediapipe as mp
import cv2#(1)創建檢測手部關鍵點的方法
mpHands = mp.solutions.hands  #接收方法
hands = mpHands.Hands(static_image_mode=False, #靜態追蹤,低于0.5置信度會再一次跟蹤max_num_hands=2, # 最多有2只手min_detection_confidence=0.6, # 最小檢測置信度min_tracking_confidence=0.5)  # 最小跟蹤置信度 # 創建檢測手部關鍵點和關鍵點之間連線的方法
mpDraw = mp.solutions.drawing_utils# 存放坐標信息
lmList = []#(2)對傳入的每一幀圖像處理
def handDetector(img):# 把圖像傳入檢測模型,提取信息results = hands.process(img)# 檢查每幀圖像是否有多只手,一一提取它們if results.multi_hand_landmarks: #如果沒有手就是Nonefor handlms in results.multi_hand_landmarks:# 繪制關鍵點及連線,mpHands.HAND_CONNECTIONS繪制手部關鍵點之間的連線mpDraw.draw_landmarks(img, handlms, mpHands.HAND_CONNECTIONS) # 獲取每個關鍵點的索引和坐標for index, lm in enumerate(handlms.landmark):# 將xy的比例坐標轉換成像素坐標h, w, c = img.shape # 分別存放圖像長\寬\通道數# 中心坐標(小數),必須轉換成整數(像素坐標)cx ,cy =  int(lm.x * w), int(lm.y * h) #比例坐標x乘以寬度得像素坐標#(3)分別處理拇指"4"和食指"8"的像素坐標if index == 4:x1, y1 = cx, cy    if index == 8:                x2, y2 = cx, cy# 打印坐標信息print("4", x1, y1, ", 8", x2, y2)# 保存坐標點lmList.append([[x1,y1],[x2,y2]])# 在食指和拇指關鍵點上畫圈,img畫板,坐標(cx,cy),半徑5,紅色填充cv2.circle(img, (x1,y1), 12, (255,0,0), cv2.FILLED)cv2.circle(img, (x2,y2), 12, (255,0,0), cv2.FILLED)# 在拇指和食指中間畫一條線段,img畫板,起點和終點坐標,顏色,線條寬度cv2.line(img, (x1,y1), (x2,y2), (255,0,255), 3)# 拇指和食指的中點,像素坐標是整數要用//cx, cy = (x1+x2)//2, (y1+y2)//2# 在中點畫一個圈cv2.circle(img, (cx,cy), 12, (255,0,0), cv2.FILLED)# 返回處理后的圖像,及關鍵點坐標return img, lmList 
效果如下:
3. 控制電腦音量
首先我們獲取音量控制模塊volume,音量的調節范圍在[-65.25, 0]之間,音量最大為0
# 導入音量控制模塊
from ctypes import cast, POINTER
from comtypes import CLSCTX_ALL
from pycaw.pycaw import AudioUtilities, IAudioEndpointVolume# 獲取音響設備
devices = AudioUtilities.GetSpeakers()
interface = devices.Activate(IAudioEndpointVolume._iid_, CLSCTX_ALL, None)
volume = cast(interface, POINTER(IAudioEndpointVolume))
# volume.GetMute()  # 靜音
# volume.GetMasterVolumeLevel()  # 獲取主音量級
volRange = volume.GetVolumeRange()  # 音量范圍(-65.25, 0.0)# 設置最值音量
minVol = volRange[0]  # 元素:-65.25
maxVol = volRange[1]  # 元素:0 
接著我們修改第2節的代碼內容,添加音量控制模塊。通過拇指和食指之間連線的長度來調節音量??赏ㄟ^勾股定理計算(x1,y1), (x2,y2)之間的長度。通過打印線段長度,發現線段最長為300,最短為50。而音量的范圍是[-65,0],因此,我們將線段長度映射到音量長度使用映射函數np.interp(),[50,300]==>[-65,0],該函數的用法:numpy.interp()用法_hfutdog的博客-CSDN博客_np.interp
設置音量控制器 volume.SetMasterVolumeLevel(vol, None)?,其中vol為映射后的線段長度。
#(1)創建檢測手部關鍵點的方法
mpHands = mp.solutions.hands  #接收方法
hands = mpHands.Hands(static_image_mode=False, #靜態追蹤,低于0.5置信度會再一次跟蹤max_num_hands=2, # 最多有2只手min_detection_confidence=0.6, # 最小檢測置信度min_tracking_confidence=0.5)  # 最小跟蹤置信度 # 創建檢測手部關鍵點和關鍵點之間連線的方法
mpDraw = mp.solutions.drawing_utils# 存放坐標信息
lmList = []#(2)對傳入的每一幀圖像處理,給出音量范圍
def handDetector(img):# 把圖像傳入檢測模型,提取信息results = hands.process(img)# 檢查每幀圖像是否有多只手,一一提取它們if results.multi_hand_landmarks: #如果沒有手就是Nonefor handlms in results.multi_hand_landmarks:# 繪制關鍵點及連線,mpHands.HAND_CONNECTIONS繪制手部關鍵點之間的連線mpDraw.draw_landmarks(img, handlms, mpHands.HAND_CONNECTIONS) # 獲取每個關鍵點的索引和坐標for index, lm in enumerate(handlms.landmark):# 將xy的比例坐標轉換成像素坐標h, w, c = img.shape # 分別存放圖像長\寬\通道數# 中心坐標(小數),必須轉換成整數(像素坐標)cx ,cy =  int(lm.x * w), int(lm.y * h) #比例坐標x乘以寬度得像素坐標#(3)分別處理拇指"4"和食指"8"的像素坐標if index == 4:x1, y1 = cx, cy    if index == 8:                x2, y2 = cx, cy# 打印坐標信息# print("4", x1, y1, ", 8", x2, y2)# 保存坐標點lmList.append([[x1,y1],[x2,y2]])# 在食指和拇指關鍵點上畫圈,img畫板,坐標(cx,cy),半徑5,紅色填充cv2.circle(img, (x1,y1), 12, (255,0,0), cv2.FILLED)cv2.circle(img, (x2,y2), 12, (255,0,0), cv2.FILLED)# 在拇指和食指中間畫一條線段,img畫板,起點和終點坐標,顏色,線條寬度cv2.line(img, (x1,y1), (x2,y2), (255,0,255), 3)# 拇指和食指的中點,像素坐標是整數要用//cx, cy = (x1+x2)//2, (y1+y2)//2# 在中點畫一個圈cv2.circle(img, (cx,cy), 12, (255,0,0), cv2.FILLED)#(4)基于長度控制音量# 計算線段之間的長度,勾股定理計算平方和再開根length = math.hypot(x2-x1, y2-y1)# print(length)# 線段長度最大300,最小50,轉換到音量范圍,最小-65,最大0# 將線段長度變量length從[50,300]轉變成[-65,0]vol = np.interp(length, [50,300], [minVol, maxVol])print('vol:',vol, 'length:', length)# 設置電腦主音量volume.SetMasterVolumeLevel(vol, None)  if length < 50:  # 距離小于50改變中心圓顏色綠色cv2.circle(img, (cx,cy), 12, (0,255,0), cv2.FILLED)# 返回處理后的圖像,及關鍵點坐標return img, lmList
 
如下圖所示,隨著手部線段變化,音量也隨著變化
4. 設置虛擬音量條
為了能更直觀的展現出音量隨著指尖距離的變化,設置虛擬的音量條,這樣就不用總是打開音量控制面板看結果。因此我們在上面的代碼中補充。
創建虛擬音量條的變量volBar,它的映射范圍和vol不同,volBar的映射范圍是虛擬音量框的高。從[50,300]映射到[400,150],確保填充可以在矩形框中變動。
# 導入音量控制模塊
from ctypes import cast, POINTER
from comtypes import CLSCTX_ALL
from pycaw.pycaw import AudioUtilities, IAudioEndpointVolume# 獲取音量設備
devices = AudioUtilities.GetSpeakers()
interface = devices.Activate(IAudioEndpointVolume._iid_, CLSCTX_ALL, None)
volume = cast(interface, POINTER(IAudioEndpointVolume))
# volume.GetMute()  # 靜音
# volume.GetMasterVolumeLevel()  # 獲取主音量級
volRange = volume.GetVolumeRange()  # 音量范圍(-65.25, 0.0)# 設置最小音量
minVol = volRange[0]  # 元素:-65.25
maxVol = volRange[1]  # 元素:0#(1)創建檢測手部關鍵點的方法
mpHands = mp.solutions.hands  #接收方法
hands = mpHands.Hands(static_image_mode=False, #靜態追蹤,低于0.5置信度會再一次跟蹤max_num_hands=2, # 最多有2只手min_detection_confidence=0.6, # 最小檢測置信度min_tracking_confidence=0.5)  # 最小跟蹤置信度 # 創建檢測手部關鍵點和關鍵點之間連線的方法
mpDraw = mp.solutions.drawing_utils#(2)存放坐標信息
lmList = []# 對傳入的每一幀圖像處理,給出音量范圍
def handDetector(img):# 把圖像傳入檢測模型,提取信息results = hands.process(img)# 檢查每幀圖像是否有多只手,一一提取它們if results.multi_hand_landmarks: #如果沒有手就是Nonefor handlms in results.multi_hand_landmarks:# 繪制關鍵點及連線,mpHands.HAND_CONNECTIONS繪制手部關鍵點之間的連線mpDraw.draw_landmarks(img, handlms, mpHands.HAND_CONNECTIONS) # 獲取每個關鍵點的索引和坐標for index, lm in enumerate(handlms.landmark):# 將xy的比例坐標轉換成像素坐標h, w, c = img.shape # 分別存放圖像長\寬\通道數# 中心坐標(小數),必須轉換成整數(像素坐標)cx ,cy =  int(lm.x * w), int(lm.y * h) #比例坐標x乘以寬度得像素坐標#(3)分別處理拇指"4"和食指"8"的像素坐標if index == 4:x1, y1 = cx, cy    if index == 8:                x2, y2 = cx, cy# 打印坐標信息# print("4", x1, y1, ", 8", x2, y2)# 保存坐標點lmList.append([[x1,y1],[x2,y2]])# 在食指和拇指關鍵點上畫圈,img畫板,坐標(cx,cy),半徑5,紅色填充cv2.circle(img, (x1,y1), 12, (255,0,0), cv2.FILLED)cv2.circle(img, (x2,y2), 12, (255,0,0), cv2.FILLED)# 在拇指和食指中間畫一條線段,img畫板,起點和終點坐標,顏色,線條寬度cv2.line(img, (x1,y1), (x2,y2), (255,0,255), 3)# 拇指和食指的中點,像素坐標是整數要用//cx, cy = (x1+x2)//2, (y1+y2)//2# 在中點畫一個圈cv2.circle(img, (cx,cy), 12, (255,0,0), cv2.FILLED)#(4)基于長度控制音量# 計算線段之間的長度,勾股定理計算平方和再開根length = math.hypot(x2-x1, y2-y1)# print(length)# 線段長度最大300,最小50,轉換到音量范圍,最小-65,最大0# 將線段長度變量length從[50,300]轉變成[-65,0]vol = np.interp(length, [50,300], [minVol, maxVol])print('vol:',vol, 'length:', length)# 虛擬音量調的映射,如果和vol一樣音量調填充不滿volBar = np.interp(length, [50,300], [400,150])  #映射到150-400# print('volbar',volBar)# 設置電腦主音量volume.SetMasterVolumeLevel(vol, None)  if length < 50:  # 距離小于50改變中心圓顏色綠色cv2.circle(img, (cx,cy), 12, (0,255,0), cv2.FILLED)#(5)畫出矩形音量條,img畫板,起點和終點坐標,顏色,線寬cv2.rectangle(img, (50,150), (85,400), (0,0,255), 3)# 用音量的幅度作為填充矩形條的高度,像素坐標是整數cv2.rectangle(img, (50,int(volBar)), (85,400), (0,0,255), cv2.FILLED)# 把音量值寫上去,坐標(50-5,150-10)避免數字遮擋框text_vol = 100 * (volBar-150)/(400-150)   # 音量歸一化再變成百分數cv2.putText(img, f'{str(int(text_vol))}%', (50-5,150-10), cv2.FONT_HERSHEY_COMPLEX, 1, (255,0,0), 2)# 返回處理后的圖像,及關鍵點坐標return img, lmList 
結果如下,24%代表的是矩形框中白色未填充部分。
拇指和食指的坐標點存放在 lmList 中,把它打印出來看一下
總結
以上是生活随笔為你收集整理的【MediaPipe】(4) AI视觉,远程手势调节电脑音量,附python完整代码的全部內容,希望文章能夠幫你解決所遇到的問題。
                            
                        - 上一篇: 【MediaPipe】(3) AI视觉,
 - 下一篇: 【机器视觉案例】(5) AI视觉,远程手