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: 下面給你 兩支完整程式: === 下面給你 兩支完整程式: # hand_gesture_1_10.py → 主程式:CVZone 手勢 1~10 + 教學面板 + 播放語音 # generate_audio_gtts.py → 一次產出 audio/zh_''.mp3、audio/tai_''.mp3 的小工具 你只要把這兩支丟到同一個資料夾,再配合我之前給你的 gestures_1_10.zip 解壓,就可以直接玩。 ==== : ==== <syntaxhighlight lang="python"># hand_gesture_1_10.py === 手勢 1~10 辨識+教學面板+國語/台語語音 === from cvzone.HandTrackingModule import HandDetector import cv2 import math import os import time import pygame import numpy as np === ========== 初始設定 ========== === === 0: 內建攝影機,如有外接可改成 1、2... === cap = cv2.VideoCapture(0) # Windows 上若遇到錯誤,可改成 cv2.VideoCapture(0, cv2.CAP_DSHOW) === 手部偵測器(最多 2 隻手,為了偵測 10) === 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: "chap", } === 中文數字對照 === zh_map = { 1: "一", 2: "二", 3: "三", 4: "四", 5: "五", 6: "六", 7: "七", 8: "八", 9: "九", 10: "十", } === ========= 讀取手勢示意圖 ========= === gesture_images = {} for n in range(1, 11): path = os.path.join("gestures", f"{n}.png") if os.path.exists(path): img_g = cv2.imread(path) if img_g is not None: gesture_images[n] = img_g else: print(f"[提示] 找不到手勢示意圖:{path}") === ========= 播放語音 ========= === def play_audio(number): """ 播放對應數字的國語 + 台語語音 audio/zh_N.mp3, audio/tai_N.mp3 """ for lang in ["zh", "tai"]: fn = os.path.join("audio", f"{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.05) except Exception as e: print(f"[錯誤] 播放 {fn} 失敗:{e}") else: print(f"[提示] 找不到音檔:{fn}") === ========= 手勢判斷(單手 1~9) ========= === 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) # 轉成 tuple 方便比對 # 固定手勢模式(不含「只伸食指」,那留給 1/9 特判) mapping = { (0, 1, 1, 0, 0): (2, "2"), # 食 + 中 (1, 1, 1, 0, 0): (3, "3"), # 拇 + 食 + 中(有時會跟 7 打架,可依實際調整) (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") === ========= 判斷整體數字(包含 10) ========= === 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 # 讓示意圖高度約為畫面 60%,寬度約 panel 的 90% scale = min((h '' 0.6) / gh, (panel_width '' 0.9) / gw) new_w = int(gw * scale) new_h = int(gh * scale) g_resized = cv2.resize(g_img, (new_w, new_h)) # 貼到 panel 置中(上方) x_offset = (panel_width - new_w) // 2 y_offset = 20 panel[y_offset:y_offset + new_h, x_offset:x_offset + new_w] = 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, bw, bh = bbox cv2.rectangle(img, (x, y), (x + bw, y + bh), (0, 255, 0), 2) cv2.putText(img, f"{label}", (x, y - 10), cv2.FONT_HERSHEY_PLAIN, 2, (0, 255, 0), 2) # 將攝影機畫面與教學面板合併(左右排列) 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 in tai_map and number != last_number: last_number = number print("辨識到數字:", number) play_audio(number) # 按下 q 離開 if cv2.waitKey(1) & 0xFF == ord("q"): break cap.release() cv2.destroyAllWindows() </syntaxhighlight> ==== : ==== <syntaxhighlight lang="python"># generate_audio_gtts.py """ 使用說明: # 先安裝 gTTS: pip install gTTS # 確保此檔案與 hand_gesture_1_10.py 在同一層。 # 執行: python generate_audio_gtts.py # 會在 audio/ 底下產生: * zh_1.mp3 ~ zh_10.mp3 (國語) * tai_1.mp3 ~ tai_10.mp3(暫以國語念 POJ 文字,可自行換成更好的台語 TTS 或錄音覆蓋) """ from gtts import gTTS import os numbers_zh = { 1: "一", 2: "二", 3: "三", 4: "四", 5: "五", 6: "六", 7: "七", 8: "八", 9: "九", 10: "十", } numbers_tai = { 1: "it", 2: "nn̄g", 3: "saⁿ", 4: "sì", 5: "gō͘", 6: "la̍k", 7: "chhit", 8: "peh", 9: "káu", 10: "chap", } out_dir = "audio" os.makedirs(out_dir, exist_ok=True) === 產生國語音檔 === for n, word in numbers_zh.items(): text = f"{word}" # 只念數字本身 tts = gTTS(text=text, lang="zh-TW") fn = os.path.join(out_dir, f"zh_{n}.mp3") print("產生:", fn) tts.save(fn) === 產生「台語」音檔(實際仍用 zh-TW TTS 念 POJ 文字,可日後錄音覆蓋) === for n, word in numbers_tai.items(): text = f"台語 {word}" tts = gTTS(text=text, lang="zh-TW") fn = os.path.join(out_dir, f"tai_{n}.mp3") print("產生:", fn) tts.save(fn) print("全部產生完成!") </syntaxhighlight> ==== 最後確認你的專案看起來像這樣: ==== <syntaxhighlight lang="text">你的資料夾/ │ hand_gesture_1_10.py │ generate_audio_gtts.py │ ├─gestures/ │ 1.png │ 2.png │ ... │ 10.png │ └─audio/ ← 執行 generate_audio_gtts.py 後自動產生 zh_1.mp3 ... zh_10.mp3 tai_1.mp3 ... tai_10.mp3 </syntaxhighlight> 如果你跑 hand_gesture_1_10.py 又遇到錯誤(模組、路徑、decode、攝影機),把錯誤訊息貼給我,我直接幫你對著那一行修掉。
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)