?

MediaPipe 手勢識別:“猜拳”游戲(基礎篇)

2023-05-30 10:48牟奕炫
電腦報 2023年18期
關鍵詞:賦值食指指尖

牟奕炫

在第14期的《基于MediaPipe的Python編程手勢識別應用》一文中,我們借助MediaPipe實現了手部21個關鍵點的精準識別。MediaPipe不僅可以判斷識別各個獨立的關鍵點,如果結合某些點的區域劃分進行相關的邏輯運算,就能夠非常方便地進行很多手勢信息的“解讀”,比如識別0-9十個數,進而在樹莓派中實現簡易“猜拳”游戲。

1.五個指尖關鍵點與“凸包”區域

為了對單手所表示的十個數進行手勢識別,判斷五個指尖關鍵點(4、8、12、16、20)與手掌心范圍的相互位置是非常重要的環節。手掌心范圍的界定可以通過“凸包”(convexhull)來實現,建立列表變量round_points,其值為[0,1,2,3,6,10,14,19,18,17,10],表示從手腕根部0開始,向上沿大拇指依次經過1、2、3點位,轉至食指的6、中指的10、無名指的14,最終從小拇指的19、18、17點位返回至手腕根部0,如此便構建了一個包含手掌心在內的閉合區域(如圖1)。

通過對五個指尖點是否在“凸包”內的判斷(單獨的某個指尖或是幾個指尖的不同組合),就能夠表示出0-9這十個數字,并且將相關的代碼封裝成函數。

2.手勢數字的判斷函數

導入“mediapipeasmp”“cv2”“numpyasnp”“math”庫模塊。

計算兩個矢量角度finger_angle(point1,point2)函數:借助numpy庫再通過兩次數學計算,建立變量two_angle,賦值為“np.dot(point1,point2)/(np.sqrt(np.sum(point1**2))*np.sqrt(np.sum(point2**2)))”,再賦值為“np.arccos(two_angle)/math.pi*180”,最后將該值返回即可。

判斷并返回手勢信息finger_sign(tip_finger,list_data)函數的編寫:1和9的共同點是均通過單根食指(直立或彎曲)來表示,判斷條件是“iflen(tip_finger)==1andtip_finger[0]==8:”(其中的tip_finger存儲的是“凸包”外的指尖關鍵點),意思是“凸包”外只檢測到有一個指尖(即一根手指)并且該指尖關鍵點是8(即食指指尖);建立兩個矢量point1、point2,賦值為“list_data[6]-list_data[7]”“list_data[8]-list_data[7]”,分別計算食指關鍵點6至7、8至7的矢量值;再通過調用函數為變量two_angle賦值:“finger_angle(point1,point2)”,并且進行“iftwo_angle<160:”的判斷,如果該角度值小于160度則認定為“彎曲的食指”,表示手勢識別的結果是數字9;條件不成立,則認定為“直立的食指”,變量finger_sign存儲的手勢識別結果即為數字1。

兩根手指可以表示2、6和8三種情況。對數字2的判斷條件為“eliflen(tip_finger)==2andtip_finger[0]==8andtip_finger[1]==12:”,意思是共有兩個指尖在“凸包”外,并且對應的指尖關鍵點分別是8(食指指尖)和12(中指指尖),表示伸出了食指和中指,對應的手勢識別數字為2。數字6對應的兩個指尖關鍵點是拇指指尖4和小指指尖20:“eliflen(tip_finger)==2andtip_finger[0]==4andtip_finger[1]==20:”,而數字8對應的則是拇指指尖4和食指指尖8。

數字3和7涉及三根手指。3的判斷條件為“eliflen(tip_finger)==3andtip_finger[0]==8andtip_finger[1]==12andtip_finger[2]==16:”,意思是共有三個指尖在“凸包”外,關鍵點是8(食指指尖)、12(中指指尖)和16(無名指指尖);數字7的判斷條件為“eliflen(tip_finger)==3andtip_finger[0]==4andtip_finger[1]==8andtip_finger[2]==12:”,對應的指尖關鍵點除了8(食指指尖)和12(中指指尖)外,用4(拇指指尖)代替了16(無名指指尖)。

數字4的判斷條件為“eliflen(tip_finger)==4andtip_finger[0]==8andtip_finger[1]==12andtip_finger[2]==16andtip_finger[3]==20:”,即檢測到有四個指尖處于“凸包”外;

數字5的判斷條件為“eliflen(tip_finger)==5:”,表示檢測到五個指尖全部處于“凸包”外;

數字0的判斷條件為“eliflen(tip_finger)==0:”,表示在“凸包”外沒有檢測到任何一個指尖關鍵點。

如果以上if和elif十種可能性均不符合條件的話,則認定沒有檢測到有效的數字,變量finger_sign值為空("");最后打印輸出提示信息并將finger_sign返回:“print("檢測到的手勢數字為:",finger_sign)”、“returnfinger_sign”(如圖2)。(源代碼請至壹零社公眾號下載。)

3.編寫main()主程序代碼

參考第14期代碼,調用攝像頭進行檢測對象的定義等相關初始化操作:“camera=cv2.VideoCapture(0)”“mpHands=mp.solutions.hands”“hands=mpHands.Hands()”“mpDraw=mp.solutions.drawing_utils”;在“whileTrue:”循環結構中,先讀取攝像頭所捕獲的畫面信息(包括畫面的寬度和高度值)、將BGR模式轉換為RGB模式等操作,再進行所有指尖關鍵點二維坐標值的采集:建立變量list_data(賦值為空列表),通過“foriinrange(21):”循環,獲取對應的(x,y)坐標值:“x,y=int(hand.landmark[i].x*w),int(hand.landmark[i].y*h)”,并將其追加至list_data中:“list_data.append([x,y])”。

接著進行“凸包”區域的界定,包括三個語句:“list_data=np.array(list_data,dtype=np.int32)”“round_points=[0,1,2,3,6,10,14,19,18,17,10]”和“hull_data=cv2.convexHull(list_data[round_points,:])”,再通過語句“cv2.polylines(img,[hull_data],True,(0,0,255),3)”實現“凸包”的紅色線框繪制;然后,進行“凸包”區域外指尖關鍵點的查找及畫面結果信息的顯示標注:變量tip_list值為“[4,8,12,16,20]”,對應五個指尖的關鍵點編號,在“foriintip_list:”循環中先建立變量position,賦值為“(int(list_data[i][0]),int(list_data[i][1]))”,即點的坐標;建立變量dist,賦值為“cv2.pointPolygonTest(hull_data,position,True)”,作用是檢測這些點是否在“凸包”內,判斷條件“ifdist<0:”成立的話,說明關鍵點在“凸包”外,則將該數據追加:“tip_finger.append(i)”,循環結束后完成所有處于“凸包”外的點的收集。然后建立變量draw_finger_sign,調用函數“finger_sign(tip_finger,list_data)”,參數tip_finger和list_data分別表示“凸包”外關鍵點的列表和關鍵點的(x,y)坐標;語句“cv2.putText(img,'%s'%(draw_finger_sign),(530,120),cv2.FONT_HERSHEY_SIMPLEX,5,(0,0,255),4,cv2.LINE_AA)”實現的功能是在視頻畫面的右上角位置顯示輸出手勢識別的數字(紅色);下面的“foriintip_list:”循環作用是將五個指尖關鍵點進行二次描繪,先獲?。▁,y)坐標值:“int(hand.landmark[i].x*w),int(hand.landmark[i].y*h)”,再使用粉紅色繪制圓點:“cv2.circle(img,(x,y),7,(255,0,255),-1)”。

最后,進行視頻窗口名稱設置、熱鍵退出、攝像頭資源的釋放及窗口的關閉等操作。

4.測試

將程序保存為“[01]Recognize_Number.py”,按F5鍵運行測試;程序能夠快速準確地進行手勢識別——提示信息顯示有“檢測到的手勢數字為:X”,同時攝像頭畫面右上角也同步顯示有該數字。

猜你喜歡
賦值食指指尖
L-代數上的賦值
治理“指尖亂像”不宜一散了之
虔誠之花在指尖綻放
指尖上的生活,指尖上的美
磨 刀
強賦值幺半群上的加權Mealy機與加權Moore機的關系*
指尖童話
利用賦值法解決抽象函數相關問題オ
平衡尺子
拇食指巨指癥1例
91香蕉高清国产线观看免费-97夜夜澡人人爽人人喊a-99久久久无码国产精品9-国产亚洲日韩欧美综合