Day05 手勢辨識 (過濾不雅手勢) Hand Posture Recognition
🟡 先看看程式的效果
今天的程式是利用 OpenCV 和 MediaPipe 進行即時的手部偵測, 並在偵測到使用者舉起中指時, 於畫面中對應的區域添加馬克賽效果。
Today’s program uses OpenCV and MediaPipe for real-time hand detection. When it detects the user raising their middle finger, it adds a mosaic effect to the corresponding area on the screen.
🟡 學習目標
了解如何在特定的節點上顯示馬克賽之類的東西。
🟡 程式碼
請先下載 “Hand Posture Recognition 手勢辨識 (過濾不雅手勢).py”
請按此下載
'''
Python + AI in 21 days
https://jasonworkshop.com/python
Designed by Jason Workshop
[請勿修改以上內容]
---
預備工作:
首先,請確保已安裝 opencv-python, mediapipe 模組
如不確定可直接在 Windows 的 cmd prompt 執行以下指定
pip install opencv-python mediapipe
'''
import cv2
import mediapipe as mp
import math
# 初始化 MediaPipe Hands 模組
mp_hands = mp.solutions.hands
hands = mp_hands.Hands(static_image_mode=False, max_num_hands=1, min_detection_confidence=0.7)
# static_image_mode:是否靜態模式,若為 True 則每張圖像都會獨立處理
# max_num_hands:最多手部偵測數量
# min_detection_confidence:手部檢測的最低可信度
mp_drawing = mp.solutions.drawing_utils # 繪製工具
# 儲存手部關鍵點的 x 和 y 座標
x = [0 for _ in range(20)]
y = [0 for _ in range(20)]
# 對指定區域進行馬克賽處理
def mosaic(img, scale=30):
h, w, _ = img.shape
new = cv2.resize(img, (h // scale, w // scale)) # 縮小圖像
return cv2.resize(new, (h, w), interpolation=cv2.INTER_NEAREST) # 重新放大圖像
# 計算兩個關鍵點之間的距離
def dist(a, b):
return math.sqrt((x[a] - x[b]) ** 2 + (y[a] - y[b]) ** 2)
# 檢測手指的狀態並返回二進制表示
def detect(hand_landmarks):
# 更新手部關鍵點的 x 和 y 座標
for i in range(0, 20):
x[i] = hand_landmarks.landmark[i].x
y[i] = hand_landmarks.landmark[i].y
# 初始化手指狀態計數
finger1 = finger2 = finger3 = finger4 = finger5 = 0
# 檢測各手指是否伸出
if abs(dist(0, 1) + dist(1, 2) + dist(2, 3) + dist(3, 4) - dist(0, 4)) < 0.025:
finger1 = 1 # 大拇指
if abs(dist(5, 6) + dist(6, 7) + dist(7, 8) - dist(5, 8)) < 0.02:
finger2 = 2 # 食指
if abs(dist(0, 9) + dist(9, 10) + dist(10, 11) + dist(11, 12) - dist(0, 12)) < 0.02:
finger3 = 4 # 中指
if abs(dist(0, 13) + dist(13, 14) + dist(14, 15) + dist(15, 16) - dist(0, 16)) < 0.02:
finger4 = 8 # 無名指
if abs(dist(0, 17) + dist(17, 18) + dist(18, 19) - dist(0, 19)) < 0.02:
finger5 = 16# 小指
# 在畫面上顯示手指狀態
cv2.putText(frame, f"{finger1} {finger2} {finger3} {finger4} {finger5}", (20, 40), cv2.FONT_HERSHEY_SIMPLEX, 0.75, (255, 255, 255))
cv2.putText(frame, str(finger1 + finger2 + finger3 + finger4 + finger5), (20, 80), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 255, 255))
# 返回手指狀態的二進制表示
return format(finger1 + finger2 + finger3 + finger4 + finger5, "05b")
# 開啟攝影機
cap = cv2.VideoCapture(0)
while cap.isOpened():
ret, frame = cap.read() # 讀取攝影機畫面
if not ret:
print("無法讀取攝影機畫面")
break
# 轉換 BGR 圖像到 RGB
rgb_frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
# 偵測手部
result = hands.process(rgb_frame)
# 繪製手部關鍵點和連線
if result.multi_hand_landmarks:
for hand_landmarks in result.multi_hand_landmarks:
finger = detect(hand_landmarks) # 將手指狀態以二進制方式表示
mp_drawing.draw_landmarks(
frame,
hand_landmarks,
mp_hands.HAND_CONNECTIONS,
landmark_drawing_spec=mp_drawing.DrawingSpec(color=(0, 255, 0), thickness=2, circle_radius=2), # 關鍵點樣式
connection_drawing_spec=mp_drawing.DrawingSpec(color=(255, 255, 255), thickness=1, circle_radius=2) # 連線樣式
)
# 如果中指舉起,則添加馬克賽效果
if finger == "00100":
img_mosaic = frame.copy() # 複製當前畫面
finger_tip = hand_landmarks.landmark[10] # 獲取中指尖的關鍵點
# 計算馬克賽區域的邊界
sx, ex = max(0, int(finger_tip.x * frame.shape[1] - 100)), min(frame.shape[1], int(finger_tip.x * frame.shape[1] + 100))
sy, ey = max(0, int(finger_tip.y * frame.shape[0] - 100)), min(frame.shape[0], int(finger_tip.y * frame.shape[0] + 100))
# 添加馬克賽效果
img_mosaic[sy:ey, sx:ex] = mosaic(img_mosaic[sy:ey, sx:ex])
frame = img_mosaic # 更新畫面
# 顯示圖像
cv2.imshow('Hand Landmarks Detection', frame)
# 按下 ESC 鍵退出
if cv2.waitKey(1) & 0xFF == 27:
break
# 釋放攝影機並關閉所有視窗
cap.release()
cv2.destroyAllWindows()
🟡 小小挑戰一下
大家可以嘗試進行一些修改或改良喔! 例如:
✌️大家知道這次的馬克賽是用了甚麼簡單而粗暴的方式去運算出來? 這樣的話能否逆轉? 還有沒有其他產生馬克賽效果的方法?
😁 明天見!