Jump to content
Main menu
Main menu
move to sidebar
hide
Navigation
Main page
Recent changes
Random page
freem
Search
Search
Appearance
Create account
Log in
Personal tools
Create account
Log in
Pages for logged out editors
learn more
Contributions
Talk
Editing
Openai/692fcfd8-4b44-800f-a184-fa098175a6d5
(section)
Add languages
Page
Discussion
English
Read
Edit
Edit source
View history
Tools
Tools
move to sidebar
hide
Actions
Read
Edit
Edit source
View history
General
What links here
Related changes
Special pages
Page information
Appearance
move to sidebar
hide
Warning:
You are not logged in. Your IP address will be publicly visible if you make any edits. If you
log in
or
create an account
, your edits will be attributed to your username, along with other benefits.
Anti-spam check. Do
not
fill this in!
=== Assistant: 好,我一次給你「手勢教學視窗+中/台語語音」的完整版本。 === 好,我一次給你「手勢教學視窗+中/台語語音」的完整版本。 設計重點: * 用 CVZone 做手部偵測與手勢 1~10 辨識 * 用 OpenCV 畫面右側做教學面板:顯示 - 手勢圖片(1~10) - 阿拉伯數字、中文數字、台語 POJ * 用 pygame.mixer 播放語音: - audio/zh_1.mp3(國語) - audio/tai_1.mp3(台語) - 依此類推到 10 : ==== 請在專案資料夾中準備: ==== <syntaxhighlight lang="text">your_project/ │ hand_number_teach.py ← 程式 │ ├─gestures/ ← 手勢教學圖片 │ 1.png │ 2.png │ ... │ 10.png │ └─audio/ ← 語音檔(可自己錄或用 TTS 產生) zh_1.mp3 zh_2.mp3 ... zh_10.mp3 tai_1.mp3 ... tai_10.mp3 </syntaxhighlight> * gestures/1.png~10.png:可用你剛剛那組手勢圖裁切後放進去。 * audio/zh_X.mp3:國語「一、二、三…」 * audio/tai_X.mp3:台語「it、nn̄g、saⁿ…」 ==== <syntaxhighlight lang="python">from cvzone.HandTrackingModule import HandDetector ==== import cv2 import math import os import time import pygame === ========== 初始設定 ========== === cap = cv2.VideoCapture(0) # 0: 內建攝影機 detector = HandDetector(detectionCon=0.7, maxHands=2) === 初始化 pygame 音效 === pygame.mixer.init() === 台語 POJ 對照 === tai_map = { 1: "it", 2: "nn̄g", 3: "saⁿ", 4: "sì", 5: "gō͘", 6: "la̍k", 7: "chhit", 8: "peh", 9: "káu", 10: "chāp", } === 中文數字對照 === zh_map = { 1: "一", 2: "二", 3: "三", 4: "四", 5: "五", 6: "六", 7: "七", 8: "八", 9: "九", 10: "十", } === 讀取手勢教學圖片 === gesture_images = {} for n in range(1, 11): path = f"gestures/{n}.png" if os.path.exists(path): img = cv2.imread(path) gesture_images[n] = img else: print(f"[提示] 找不到手勢示意圖:{path}") === ========= 播放語音 ========= === def play_audio(number): """ 播放對應數字的國語 + 台語語音 audio/zh_N.mp3, audio/tai_N.mp3 會阻塞一下主程式(簡單好懂版) """ for lang in ["zh", "tai"]: fn = f"audio/{lang}_{number}.mp3" if os.path.exists(fn): try: pygame.mixer.music.load(fn) pygame.mixer.music.play() # 等待播放結束 while pygame.mixer.music.get_busy(): time.sleep(0.1) except Exception as e: print(f"[錯誤] 播放 {fn} 失敗:{e}") else: print(f"[提示] 找不到音檔:{fn}") === ========= 手勢判斷邏輯 ========= === def classify_single_hand_number(hand, fingers): """ 根據單手的 fingersUp 結果判斷數字 1~9 hand: 單手資訊 (包含 lmList, bbox, type 等) fingers: [thumb, index, middle, ring, pinky] (0/1) 回傳 (number, label) 或 (None, "None") """ pattern = tuple(fingers) # 固定手勢模式(不含「只伸食指」的情況,留給 1 / 9 特判) mapping = { (0, 1, 1, 0, 0): (2, "2"), # 食 + 中 (1, 1, 1, 0, 0): (3, "3"), # 拇 + 食 + 中 (0, 1, 1, 1, 1): (4, "4"), # 四指伸,拇指收 (1, 1, 1, 1, 1): (5, "5"), # 全部伸出 (1, 0, 0, 0, 1): (6, "6"), # 拇 + 小 (shaka) (1, 1, 0, 0, 1): (7, "7"), # 拇 + 食 + 小 (1, 1, 0, 0, 0): (8, "8"), # 拇 + 食 (手槍狀) } if pattern in mapping: return mapping[pattern] # ---- 特判:1 或 9(只伸食指)---- if pattern == (0, 1, 0, 0, 0): lm_list = hand["lmList"] # 21 個關鍵點 # 食指:MCP = 5, PIP = 6, DIP = 7, TIP = 8 mcp = lm_list[5] pip = lm_list[6] dip = lm_list[7] tip = lm_list[8] # 直線長度 MCP→TIP dist_mcp_tip = math.dist(mcp[:2], tip[:2]) # 折線長度 MCP→PIP→DIP→TIP dist_mcp_pip = math.dist(mcp[:2], pip[:2]) dist_pip_dip = math.dist(pip[:2], dip[:2]) dist_dip_tip = math.dist(dip[:2], tip[:2]) poly_len = dist_mcp_pip + dist_pip_dip + dist_dip_tip # 折線 / 直線 比例,用來粗略分辨「很彎」=9、「較直」=1 if dist_mcp_tip > 0 and poly_len / dist_mcp_tip > 1.25: return (9, "9") else: return (1, "1") # 其他模式先不判斷 return (None, "None") def classify_number(hands): """ 根據全部手部資料判斷數字 1~10 回傳 (number, label, bbox_for_draw) """ if not hands: return None, "No Hand", None # 先判斷是否為「10」:兩手都比 1 if len(hands) >= 2: nums = [] for h in hands[:2]: fingers = detector.fingersUp(h) n, _ = classify_single_hand_number(h, fingers) nums.append(n) if nums[0] == 1 and nums[1] == 1: bbox10 = hands[0]["bbox"] return 10, "10", bbox10 # 否則只看第一隻手 1~9 main_hand = hands[0] bbox = main_hand["bbox"] fingers = detector.fingersUp(main_hand) number, label = classify_single_hand_number(main_hand, fingers) if number is None: return None, "Unknown", bbox else: return number, label, bbox === ========= 主迴圈 ========= === last_number = None # 上一次辨識結果,用來避免重複播音 while cap.isOpened(): success, img = cap.read() if not success: break # 偵測手部 hands, img = detector.findHands(img) number, label, bbox = classify_number(hands) # ===== 教學面板內容 ===== h, w, _ = img.shape panel_width = 320 panel = 255 * np.ones((h, panel_width, 3), dtype='uint8') # 白底 if number is not None and number in tai_map: # 顯示手勢示意圖(若有) if number in gesture_images: g_img = gesture_images[number] # 調整示意圖高度約為畫面一半 gh, gw, _ = g_img.shape scale = min((h '' 0.6) / gh, (panel_width '' 0.9) / gw) new_size = (int(gw '' scale), int(gh '' scale)) g_resized = cv2.resize(g_img, new_size) # 貼到 panel 中央 x_offset = (panel_width - new_size[0]) // 2 y_offset = 20 panel[y_offset:y_offset + new_size[1], x_offset:x_offset + new_size[0]] = g_resized # 文字資訊 text_y = int(h * 0.75) cv2.putText(panel, f"{number}", (30, text_y), cv2.FONT_HERSHEY_SIMPLEX, 1.2, (0, 0, 0), 2) cv2.putText(panel, f"中文:{zh_map[number]}", (30, text_y + 30), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0, 0, 0), 2) cv2.putText(panel, f"台語:{tai_map[number]}", (30, text_y + 60), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0, 0, 0), 2) else: cv2.putText(panel, "請比出 1~10 的手勢", (20, h // 2), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (128, 128, 128), 2) # 在原影像畫出偵測結果 if bbox is not None and number is not None: x, y, w0, h0 = bbox cv2.rectangle(img, (x, y), (x + w0, y + h0), (0, 255, 0), 2) cv2.putText(img, f"{label}", (x, y - 10), cv2.FONT_HERSHEY_PLAIN, 2, (0, 255, 0), 2) # 將攝影機畫面與教學面板合併 import numpy as np # 放在這裡避免一開始沒用被 IDE 抱怨 panel = cv2.resize(panel, (panel_width, h)) combined = cv2.hconcat([img, panel]) cv2.imshow("Hand Number Teaching (1-10)", combined) # 若數字有變化,播放對應語音 if number is not None and number != last_number: last_number = number print("辨識到數字:", number) if number in tai_map: play_audio(number) if cv2.waitKey(1) & 0xFF == ord("q"): break cap.release() cv2.destroyAllWindows() </syntaxhighlight> ==== 1. 先測試無語音版 - 可以先把 play_audio(number) 裡面的程式碼註解掉,只看畫面是否會正確顯示手勢與數字。 ==== # 逐步補上音檔 - 先只錄或產生 zh_1.mp3、tai_1.mp3,測試「1」的播報 - 確定沒問題,再補到 10 # 教學活動玩法 - 學生輪流到鏡頭前比 1~10,螢幕會出現: - 手勢圖 - 阿拉伯數字+中文+台語 - 同時念出國語+台語 - 搭配你之前設計的「數字手勢小卡」、「黑板風講義」一起用很剛好。 如果你願意把你現有的手勢圖片(那 10 張)規格跟檔名告訴我,我也可以幫你調整尺寸建議,讓它貼上去更剛好、不變形。
Summary:
Please note that all contributions to freem are considered to be released under the Creative Commons Attribution-ShareAlike 4.0 (see
Freem:Copyrights
for details). If you do not want your writing to be edited mercilessly and redistributed at will, then do not submit it here.
You are also promising us that you wrote this yourself, or copied it from a public domain or similar free resource.
Do not submit copyrighted work without permission!
Cancel
Editing help
(opens in new window)