Day08 追蹤手部位置及動作並顯示魔法特效
🟡 先看看程式的效果
今天的程式是使用 OpenCV 和 MediaPipe 進行即時手部偵測,並在偵測到手部張開時,顯示旋轉的魔法陣特效。
Today’s program uses OpenCV and MediaPipe for real-time hand detection, and when it detects an open hand, it displays a rotating magic circle effect.
🟡 學習目標
在畫面貼上圖片於指定位置並使它不停旋轉。
🟡 程式碼
請先下載 “追蹤手部位置及動作並顯示魔法特效.py” 及 “magic.png”
請按此下載
'''
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 numpy as np
import math
# 初始化 MediaPipe Hands 模組
mp_hands = mp.solutions.hands
hands = mp_hands.Hands(static_image_mode=False, max_num_hands=2, min_detection_confidence=0.7)
# static_image_mode:是否靜態模式,若為 True 則每張圖像都會獨立處理
# max_num_hands:最多手部偵測數量
# min_detection_confidence:手部檢測的最低可信度
mp_drawing = mp.solutions.drawing_utils # 繪製工具
# 開啟攝影機
cap = cv2.VideoCapture(0)
def draw_glowing_line(frame, start_point, end_point, color=(148, 97, 189), thickness=4):
# 畫出一條看起來會發光的線條
cv2.line(frame, start_point, end_point, color, thickness)
cv2.line(frame, start_point, end_point, (255, 255, 255), thickness - 3)
# 載入需要顯示的圖片
magic_image = cv2.imread('magic.png', cv2.IMREAD_UNCHANGED)
# 檢查圖片是否讀取成功
if magic_image is None:
print("Error: Unable to load image. Please check the file path and name.")
exit()
# 調整圖片大小,例如將寬度和高度調整為 300x300
new_width = 300
new_height = 300
magic_image = cv2.resize(magic_image, (new_width, new_height), interpolation=cv2.INTER_AREA)
'''
# 載入需要顯示的圖片
magic2_image = cv2.imread('magic2.png', cv2.IMREAD_UNCHANGED)
# 檢查圖片是否讀取成功
if magic_image is None:
print("Error: Unable to load image. Please check the file path and name.")
exit()
# 調整圖片大小,例如將寬度和高度調整為 300x300
new_width = 300
new_height = 300
magic2_image = cv2.resize(magic2_image, (new_width, new_height), interpolation=cv2.INTER_AREA)
'''
def is_hand_open(hand_landmarks):
# 判斷手掌是否張開,這裡簡單使用大拇指和食指的距離作為判斷依據
thumb_tip = hand_landmarks.landmark[4]
index_finger_tip = hand_landmarks.landmark[8]
distance = math.sqrt((thumb_tip.x - index_finger_tip.x) ** 2 + (thumb_tip.y - index_finger_tip.y) ** 2)
return distance > 0.1 # 這個閾值可以根據實際情況調整
def overlay_image_alpha(img, img_overlay, pos, alpha_mask):
"""Overlay `img_overlay` on top of `img` at the position specified by
`pos` and blend using `alpha_mask`.
"""
x, y = pos
# Image ranges
y1, y2 = max(0, y), min(img.shape[0], y + img_overlay.shape[0])
x1, x2 = max(0, x), min(img.shape[1], x + img_overlay.shape[1])
# Overlay ranges
y1o, y2o = max(0, -y), min(img_overlay.shape[0], img.shape[0] - y)
x1o, x2o = max(0, -x), min(img_overlay.shape[1], img.shape[1] - x)
# Exit if nothing to do
if y1 >= y2 or x1 >= x2 or y1o >= y2o or x1o >= x2o:
return
# Blend overlay within the determined ranges
img_crop = img[y1:y2, x1:x2]
img_overlay_crop = img_overlay[y1o:y2o, x1o:x2o]
alpha = alpha_mask[y1o:y2o, x1o:x2o, np.newaxis]
alpha_inv = 1.0 - alpha
img_crop[:] = alpha * img_overlay_crop + alpha_inv * img_crop
angle = 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:
#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 is_hand_open(hand_landmarks):
# 計算手心的位置
palm_x = int(hand_landmarks.landmark[9].x * frame.shape[1])
palm_y = int(hand_landmarks.landmark[9].y * frame.shape[0])
# 旋轉圖片
angle += 5 # 每幀旋轉 5 度
rotation_matrix = cv2.getRotationMatrix2D((magic_image.shape[1] / 2, magic_image.shape[0] / 2), angle, 1)
rotated_image = cv2.warpAffine(magic_image, rotation_matrix, (magic_image.shape[1], magic_image.shape[0]))
# 取得 alpha 通道作為透明度遮罩
alpha_mask = rotated_image[:, :, 3] / 255.0
rotated_image = rotated_image[:, :, :3]
# 疊加圖片
overlay_image_alpha(frame, rotated_image, (palm_x - rotated_image.shape[1] // 2, palm_y - rotated_image.shape[0] // 2), alpha_mask)
# 定義需要畫線的關鍵點對
landmark_pairs = [(0, 4), (0, 8), (0, 12), (0, 16), (0, 20), (4, 8), (4, 12), (4, 16), (4, 20)]
# 畫出橙色的線條
for start, end in landmark_pairs:
start_point = (int(hand_landmarks.landmark[start].x * frame.shape[1]),
int(hand_landmarks.landmark[start].y * frame.shape[0]))
end_point = (int(hand_landmarks.landmark[end].x * frame.shape[1]),
int(hand_landmarks.landmark[end].y * frame.shape[0]))
draw_glowing_line(frame, start_point, end_point)
# 顯示圖像
cv2.imshow('Hand Landmarks Detection', frame)
# 按下 ESC 鍵退出
if cv2.waitKey(1) & 0xFF == 27:
break
# 釋放攝影機並關閉所有視窗
cap.release()
cv2.destroyAllWindows()
🟡 小小挑戰一下
大家可以嘗試進行一些修改或改良喔! 例如:
✌️轉用其他圖檔作特效。
✌️根據不同的手勢 (例如: 🤘👌✌️) 顯示不同的特效。
😁 明天見!