Spaces:
Running
Running
Commit ·
773dbc3
1
Parent(s): 6dc8b96
Add curated showcase student to DEMO
Browse files- projects/DEMO/展示用學生/01_手勢按鈕挑戰/d5_pinch_challenge_solution.py +216 -0
- projects/DEMO/展示用學生/01_手勢按鈕挑戰/main.py +18 -0
- projects/DEMO/展示用學生/02_臉部3D網格/face_mesh_connections.py +500 -0
- projects/DEMO/展示用學生/02_臉部3D網格/main.py +18 -0
- projects/DEMO/展示用學生/02_臉部3D網格/step1_facemesh.py +129 -0
- projects/DEMO/展示用學生/03_手指空中畫畫/draw_game_student.py +168 -0
- projects/DEMO/展示用學生/03_手指空中畫畫/main.py +18 -0
- projects/DEMO/展示用學生/04_Fire迷宮/link.txt +1 -0
- projects/DEMO/展示用學生/展示備忘.md +18 -0
projects/DEMO/展示用學生/01_手勢按鈕挑戰/d5_pinch_challenge_solution.py
ADDED
|
@@ -0,0 +1,216 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
Week 10 - 魔法手勢:Pinch 偵測與手勢按鈕 (三指捏合挑戰 Template 填空版)
|
| 3 |
+
==================================================================
|
| 4 |
+
本週挑戰目標:
|
| 5 |
+
1. 實作「三指捏合 (Three-Finger Pinch)」:
|
| 6 |
+
必須同時滿足「大拇指 (4) 與食指 (8) 的距離 < Threshold」
|
| 7 |
+
以及「大拇指 (4) 與中指 (12) 的距離 < Threshold」才判定為 Pinch
|
| 8 |
+
2. 用三指碰觸 Pygame 畫面上的按鈕 + 捏合 = 魔法點擊
|
| 9 |
+
3. 使用邊緣觸發 (Edge Detection) 防止重複觸發
|
| 10 |
+
4. 新增 30 秒限時機制,時間到顯示最終得分
|
| 11 |
+
"""
|
| 12 |
+
import pygame
|
| 13 |
+
import cv2
|
| 14 |
+
import mediapipe as mp
|
| 15 |
+
import math
|
| 16 |
+
import sys
|
| 17 |
+
import os
|
| 18 |
+
import random
|
| 19 |
+
|
| 20 |
+
# ============================================
|
| 21 |
+
# 1. 初始化
|
| 22 |
+
# ============================================
|
| 23 |
+
pygame.init()
|
| 24 |
+
WIDTH, HEIGHT = 800, 600
|
| 25 |
+
screen = pygame.display.set_mode((WIDTH, HEIGHT))
|
| 26 |
+
pygame.display.set_caption("Week 10 - 三指魔法手勢按鈕 (填空版) ")
|
| 27 |
+
clock = pygame.time.Clock()
|
| 28 |
+
from mediapipe.tasks import python
|
| 29 |
+
from mediapipe.tasks.python import vision
|
| 30 |
+
|
| 31 |
+
# 攝影機
|
| 32 |
+
cap = cv2.VideoCapture(0)
|
| 33 |
+
|
| 34 |
+
# 【TODO 1】初始化 MediaPipe 手勢偵測器 (Hand Landmarker)
|
| 35 |
+
|
| 36 |
+
try:
|
| 37 |
+
from mediapipe.tasks import python
|
| 38 |
+
from mediapipe.tasks.python import vision
|
| 39 |
+
base_options = python.BaseOptions(model_asset_path=os.path.join(os.path.dirname(os.path.abspath(__file__)), 'hand_landmarker.task'))
|
| 40 |
+
options = vision.HandLandmarkerOptions(base_options=base_options, num_hands=1)
|
| 41 |
+
detector = vision.HandLandmarker.create_from_options(options)
|
| 42 |
+
except Exception as e:
|
| 43 |
+
print(f"Error loading model: {e}")
|
| 44 |
+
pygame.quit()
|
| 45 |
+
sys.exit()
|
| 46 |
+
|
| 47 |
+
# ============================================
|
| 48 |
+
# 2. 遊戲變數
|
| 49 |
+
# ============================================
|
| 50 |
+
# 手指座標
|
| 51 |
+
finger_x, finger_y = 400, 300 # 食指
|
| 52 |
+
thumb_x, thumb_y = 400, 300 # 大拇指
|
| 53 |
+
middle_x, middle_y = 400, 300 # 中指
|
| 54 |
+
|
| 55 |
+
# 距離變數
|
| 56 |
+
dist_thumb_index = 0.0
|
| 57 |
+
dist_thumb_middle = 0.0
|
| 58 |
+
|
| 59 |
+
# Pinch 相關變數
|
| 60 |
+
is_pinching = False
|
| 61 |
+
was_pinching = False # 邊緣觸發
|
| 62 |
+
PINCH_THRESHOLD = 50 # 距離臨界值
|
| 63 |
+
|
| 64 |
+
# 按鈕設定
|
| 65 |
+
button_rect = pygame.Rect(300, 250, 200, 100)
|
| 66 |
+
button_colors = [(100, 100, 220), (220, 100, 100),
|
| 67 |
+
(100, 220, 100), (220, 180, 50)]
|
| 68 |
+
current_color_index = 0
|
| 69 |
+
|
| 70 |
+
# 點擊計數器
|
| 71 |
+
click_count = 0
|
| 72 |
+
|
| 73 |
+
# --- 限時機制變數 ---
|
| 74 |
+
START_TIME = pygame.time.get_ticks()
|
| 75 |
+
GAME_DURATION = 30 # 遊戲限制時間(秒)
|
| 76 |
+
game_state = "PLAYING" # 狀態機
|
| 77 |
+
|
| 78 |
+
# ============================================
|
| 79 |
+
# 3. 主迴圈
|
| 80 |
+
# ============================================
|
| 81 |
+
running = True
|
| 82 |
+
while running:
|
| 83 |
+
for event in pygame.event.get():
|
| 84 |
+
if event.type == pygame.QUIT:
|
| 85 |
+
running = False
|
| 86 |
+
|
| 87 |
+
# --- 讀取攝影機與手勢偵測 ---
|
| 88 |
+
success, img = cap.read()
|
| 89 |
+
is_pinching = False # 每幀重設狀態,防止手離屏時卡死
|
| 90 |
+
|
| 91 |
+
if success:
|
| 92 |
+
img = cv2.flip(img, 1) # 鏡像翻轉
|
| 93 |
+
img_rgb = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
|
| 94 |
+
|
| 95 |
+
mp_image = mp.Image(image_format=mp.ImageFormat.SRGB, data=img_rgb)
|
| 96 |
+
results = detector.detect(mp_image)
|
| 97 |
+
|
| 98 |
+
# === 【TODO 2:挑戰關卡三之手部安全偵測】 ===
|
| 99 |
+
# 1. 判斷手部是否在畫面中 (results.hand_landmarks 是否有資料)
|
| 100 |
+
# 2. 如果在畫面中,取得食指 (8)、大拇指 (4)、中指 (12) 的像素座標
|
| 101 |
+
# 3. 如果手不在畫面中,將 is_pinching 設為 False
|
| 102 |
+
#
|
| 103 |
+
hand_in_frame = False
|
| 104 |
+
if results.hand_landmarks:
|
| 105 |
+
hand_in_frame = True
|
| 106 |
+
for hand_lms in results.hand_landmarks:
|
| 107 |
+
# 食指 (8)
|
| 108 |
+
lm8 = hand_lms[8]
|
| 109 |
+
finger_x = int(lm8.x * WIDTH)
|
| 110 |
+
finger_y = int(lm8.y * HEIGHT)
|
| 111 |
+
|
| 112 |
+
# 大拇指 (4)
|
| 113 |
+
lm4 = hand_lms[4]
|
| 114 |
+
thumb_x = int(lm4.x * WIDTH)
|
| 115 |
+
thumb_y = int(lm4.y * HEIGHT)
|
| 116 |
+
|
| 117 |
+
# 中指 (12)
|
| 118 |
+
lm12 = hand_lms[12]
|
| 119 |
+
middle_x = int(lm12.x * WIDTH)
|
| 120 |
+
middle_y = int(lm12.y * HEIGHT)
|
| 121 |
+
|
| 122 |
+
# === 【TODO 3:挑戰關卡一之三指捏合判定】 ===
|
| 123 |
+
# 提示:計算食指與大拇指距離,以及中指與大拇指距離
|
| 124 |
+
# 兩組距離皆小於 PINCH_THRESHOLD 時,is_pinching 才為 True
|
| 125 |
+
dist_thumb_index = math.hypot(finger_x - thumb_x, finger_y - thumb_y)
|
| 126 |
+
dist_thumb_middle = math.hypot(middle_x - thumb_x, middle_y - thumb_y)
|
| 127 |
+
is_pinching = (dist_thumb_index < PINCH_THRESHOLD) and (dist_thumb_middle < PINCH_THRESHOLD)
|
| 128 |
+
|
| 129 |
+
dist_thumb_index = math.hypot(finger_x - thumb_x, finger_y - thumb_y)
|
| 130 |
+
dist_thumb_middle = math.hypot(middle_x - thumb_x, middle_y - thumb_y)
|
| 131 |
+
is_pinching = (dist_thumb_index < PINCH_THRESHOLD) and (dist_thumb_middle < PINCH_THRESHOLD)
|
| 132 |
+
|
| 133 |
+
# --- 狀態機邏輯處理 ---
|
| 134 |
+
if game_state == "PLAYING":
|
| 135 |
+
# 1. 計算剩餘時間
|
| 136 |
+
current_ticks = pygame.time.get_ticks()
|
| 137 |
+
elapsed_seconds = (current_ticks - START_TIME) / 1000
|
| 138 |
+
remaining_time = max(0, GAME_DURATION - elapsed_seconds)
|
| 139 |
+
|
| 140 |
+
# 2. 檢查時間是否到了
|
| 141 |
+
if remaining_time <= 0:
|
| 142 |
+
game_state = "GAME_OVER"
|
| 143 |
+
|
| 144 |
+
# 3. 邊緣觸發點擊判斷
|
| 145 |
+
if is_pinching and not was_pinching:
|
| 146 |
+
finger_rect = pygame.Rect(finger_x - 5, finger_y - 5, 10, 10)
|
| 147 |
+
if finger_rect.colliderect(button_rect):
|
| 148 |
+
click_count += 1
|
| 149 |
+
current_color_index = (current_color_index + 1) % len(button_colors)
|
| 150 |
+
|
| 151 |
+
# === 【TODO 4:挑戰關卡二之按鈕隨機移動】 ===
|
| 152 |
+
# 提示:當點擊發生時,讓按鈕的 x 和 y 座標隨機分配在畫面內
|
| 153 |
+
|
| 154 |
+
button_rect.x = random.randint(0, WIDTH - button_rect.width)
|
| 155 |
+
button_rect.y = random.randint(0, HEIGHT - button_rect.height)
|
| 156 |
+
print(f" 魔法三指點擊!第 {click_count} 次")
|
| 157 |
+
|
| 158 |
+
# 更新上一幀狀態
|
| 159 |
+
was_pinching = is_pinching
|
| 160 |
+
|
| 161 |
+
# --- 繪製畫面 ---
|
| 162 |
+
screen.fill((20, 20, 35))
|
| 163 |
+
font = pygame.font.SysFont(None, 32)
|
| 164 |
+
large_font = pygame.font.SysFont(None, 64)
|
| 165 |
+
|
| 166 |
+
if game_state == "PLAYING":
|
| 167 |
+
# 繪製按鈕
|
| 168 |
+
btn_color = button_colors[current_color_index]
|
| 169 |
+
pygame.draw.rect(screen, btn_color, button_rect, border_radius=16)
|
| 170 |
+
|
| 171 |
+
btn_text = font.render(f"Clicks: {click_count}", True, (255, 255, 255))
|
| 172 |
+
screen.blit(btn_text, (button_rect.centerx - btn_text.get_width() // 2,
|
| 173 |
+
button_rect.centery - btn_text.get_height() // 2))
|
| 174 |
+
|
| 175 |
+
# 顯示倒數計時
|
| 176 |
+
time_text = font.render(f"Time Left: {remaining_time:.1f}s", True, (255, 220, 100))
|
| 177 |
+
screen.blit(time_text, (WIDTH - time_text.get_width() - 20, 10))
|
| 178 |
+
|
| 179 |
+
# 畫手指游標
|
| 180 |
+
cursor_color = (50, 255, 50) if is_pinching else (255, 50, 50)
|
| 181 |
+
pygame.draw.circle(screen, cursor_color, (finger_x, finger_y), 12) # 食指
|
| 182 |
+
pygame.draw.circle(screen, (255, 255, 255), (thumb_x, thumb_y), 12) # 大拇指
|
| 183 |
+
pygame.draw.circle(screen, (100, 150, 255), (middle_x, middle_y), 12) # 中指
|
| 184 |
+
|
| 185 |
+
# === 【TODO 5:挑戰關卡三之畫面提示】 ===
|
| 186 |
+
# 提示:當手不在畫面中時,在畫面上方顯示警告文字提醒學生將手部放回
|
| 187 |
+
|
| 188 |
+
if not hand_in_frame:
|
| 189 |
+
warning_text = font.render("OUT OF FRAME - PLEASE PUT HANDS BACK", True, (255, 100, 100))
|
| 190 |
+
screen.blit(warning_text, (WIDTH // 2 - warning_text.get_width() // 2, 100))
|
| 191 |
+
|
| 192 |
+
# 顯示調試數值
|
| 193 |
+
small_font = pygame.font.SysFont(None, 24)
|
| 194 |
+
status = small_font.render(f"Finger: ({finger_x}, {finger_y})", True, (150, 150, 150))
|
| 195 |
+
screen.blit(status, (10, 10))
|
| 196 |
+
|
| 197 |
+
dist_text = small_font.render(f"Dist Thumb-Index: {dist_thumb_index:.0f} px", True, (150, 150, 200))
|
| 198 |
+
screen.blit(dist_text, (10, 35))
|
| 199 |
+
|
| 200 |
+
dist_text2 = small_font.render(f"Dist Thumb-Middle: {dist_thumb_middle:.0f} px", True, (150, 150, 200))
|
| 201 |
+
screen.blit(dist_text2, (10, 60))
|
| 202 |
+
|
| 203 |
+
elif game_state == "GAME_OVER":
|
| 204 |
+
# 繪製遊戲結束畫面
|
| 205 |
+
over_text = large_font.render("GAME OVER", True, (255, 50, 50))
|
| 206 |
+
score_text = font.render(f"Total Clicks: {click_count} times", True, (255, 255, 255))
|
| 207 |
+
|
| 208 |
+
screen.blit(over_text, (WIDTH // 2 - over_text.get_width() // 2, HEIGHT // 2 - 50))
|
| 209 |
+
screen.blit(score_text, (WIDTH // 2 - score_text.get_width() // 2, HEIGHT // 2 + 20))
|
| 210 |
+
|
| 211 |
+
pygame.display.update()
|
| 212 |
+
clock.tick(30)
|
| 213 |
+
|
| 214 |
+
cap.release()
|
| 215 |
+
pygame.quit()
|
| 216 |
+
sys.exit()
|
projects/DEMO/展示用學生/01_手勢按鈕挑戰/main.py
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# -*- coding: utf-8 -*-
|
| 2 |
+
"""
|
| 3 |
+
Telearn Arcade Hub 入口檔(自動產生)
|
| 4 |
+
實際執行: d5_pinch_challenge_solution.py
|
| 5 |
+
規格: projects/{班級}/{學生}/{專案}/main.py
|
| 6 |
+
"""
|
| 7 |
+
from pathlib import Path
|
| 8 |
+
import os
|
| 9 |
+
import runpy
|
| 10 |
+
import sys
|
| 11 |
+
|
| 12 |
+
_ROOT = Path(__file__).resolve().parent
|
| 13 |
+
os.chdir(_ROOT)
|
| 14 |
+
if str(_ROOT) not in sys.path:
|
| 15 |
+
sys.path.insert(0, str(_ROOT))
|
| 16 |
+
|
| 17 |
+
# 執行原始學員腳本
|
| 18 |
+
runpy.run_path(str(_ROOT / 'd5_pinch_challenge_solution.py'), run_name="__main__")
|
projects/DEMO/展示用學生/02_臉部3D網格/face_mesh_connections.py
ADDED
|
@@ -0,0 +1,500 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright 2021 The MediaPipe Authors.
|
| 2 |
+
#
|
| 3 |
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
| 4 |
+
# you may not use this file except in compliance with the License.
|
| 5 |
+
# You may obtain a copy of the License at
|
| 6 |
+
#
|
| 7 |
+
# http://www.apache.org/licenses/LICENSE-2.0
|
| 8 |
+
#
|
| 9 |
+
# Unless required by applicable law or agreed to in writing, software
|
| 10 |
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
| 11 |
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 12 |
+
# See the License for the specific language governing permissions and
|
| 13 |
+
# limitations under the License.
|
| 14 |
+
"""MediaPipe FaceMesh connections."""
|
| 15 |
+
|
| 16 |
+
FACEMESH_LIPS = frozenset([(61, 146), (146, 91), (91, 181), (181, 84), (84, 17),
|
| 17 |
+
(17, 314), (314, 405), (405, 321), (321, 375),
|
| 18 |
+
(375, 291), (61, 185), (185, 40), (40, 39), (39, 37),
|
| 19 |
+
(37, 0), (0, 267),
|
| 20 |
+
(267, 269), (269, 270), (270, 409), (409, 291),
|
| 21 |
+
(78, 95), (95, 88), (88, 178), (178, 87), (87, 14),
|
| 22 |
+
(14, 317), (317, 402), (402, 318), (318, 324),
|
| 23 |
+
(324, 308), (78, 191), (191, 80), (80, 81), (81, 82),
|
| 24 |
+
(82, 13), (13, 312), (312, 311), (311, 310),
|
| 25 |
+
(310, 415), (415, 308)])
|
| 26 |
+
|
| 27 |
+
FACEMESH_LEFT_EYE = frozenset([(263, 249), (249, 390), (390, 373), (373, 374),
|
| 28 |
+
(374, 380), (380, 381), (381, 382), (382, 362),
|
| 29 |
+
(263, 466), (466, 388), (388, 387), (387, 386),
|
| 30 |
+
(386, 385), (385, 384), (384, 398), (398, 362)])
|
| 31 |
+
|
| 32 |
+
FACEMESH_LEFT_IRIS = frozenset([(474, 475), (475, 476), (476, 477),
|
| 33 |
+
(477, 474)])
|
| 34 |
+
|
| 35 |
+
FACEMESH_LEFT_EYEBROW = frozenset([(276, 283), (283, 282), (282, 295),
|
| 36 |
+
(295, 285), (300, 293), (293, 334),
|
| 37 |
+
(334, 296), (296, 336)])
|
| 38 |
+
|
| 39 |
+
FACEMESH_RIGHT_EYE = frozenset([(33, 7), (7, 163), (163, 144), (144, 145),
|
| 40 |
+
(145, 153), (153, 154), (154, 155), (155, 133),
|
| 41 |
+
(33, 246), (246, 161), (161, 160), (160, 159),
|
| 42 |
+
(159, 158), (158, 157), (157, 173), (173, 133)])
|
| 43 |
+
|
| 44 |
+
FACEMESH_RIGHT_EYEBROW = frozenset([(46, 53), (53, 52), (52, 65), (65, 55),
|
| 45 |
+
(70, 63), (63, 105), (105, 66), (66, 107)])
|
| 46 |
+
|
| 47 |
+
FACEMESH_RIGHT_IRIS = frozenset([(469, 470), (470, 471), (471, 472),
|
| 48 |
+
(472, 469)])
|
| 49 |
+
|
| 50 |
+
FACEMESH_FACE_OVAL = frozenset([(10, 338), (338, 297), (297, 332), (332, 284),
|
| 51 |
+
(284, 251), (251, 389), (389, 356), (356, 454),
|
| 52 |
+
(454, 323), (323, 361), (361, 288), (288, 397),
|
| 53 |
+
(397, 365), (365, 379), (379, 378), (378, 400),
|
| 54 |
+
(400, 377), (377, 152), (152, 148), (148, 176),
|
| 55 |
+
(176, 149), (149, 150), (150, 136), (136, 172),
|
| 56 |
+
(172, 58), (58, 132), (132, 93), (93, 234),
|
| 57 |
+
(234, 127), (127, 162), (162, 21), (21, 54),
|
| 58 |
+
(54, 103), (103, 67), (67, 109), (109, 10)])
|
| 59 |
+
|
| 60 |
+
FACEMESH_NOSE = frozenset([(168, 6), (6, 197), (197, 195), (195, 5),
|
| 61 |
+
(5, 4), (4, 1), (1, 19), (19, 94), (94, 2), (98, 97),
|
| 62 |
+
(97, 2), (2, 326), (326, 327), (327, 294),
|
| 63 |
+
(294, 278), (278, 344), (344, 440), (440, 275),
|
| 64 |
+
(275, 4), (4, 45), (45, 220), (220, 115), (115, 48),
|
| 65 |
+
(48, 64), (64, 98)])
|
| 66 |
+
|
| 67 |
+
FACEMESH_CONTOURS = frozenset().union(*[
|
| 68 |
+
FACEMESH_LIPS, FACEMESH_LEFT_EYE, FACEMESH_LEFT_EYEBROW, FACEMESH_RIGHT_EYE,
|
| 69 |
+
FACEMESH_RIGHT_EYEBROW, FACEMESH_FACE_OVAL
|
| 70 |
+
])
|
| 71 |
+
|
| 72 |
+
FACEMESH_IRISES = frozenset().union(*[FACEMESH_LEFT_IRIS, FACEMESH_RIGHT_IRIS])
|
| 73 |
+
|
| 74 |
+
FACEMESH_TESSELATION = frozenset([
|
| 75 |
+
(127, 34), (34, 139), (139, 127), (11, 0), (0, 37), (37, 11),
|
| 76 |
+
(232, 231), (231, 120), (120, 232), (72, 37), (37, 39), (39, 72),
|
| 77 |
+
(128, 121), (121, 47), (47, 128), (232, 121), (121, 128), (128, 232),
|
| 78 |
+
(104, 69), (69, 67), (67, 104), (175, 171), (171, 148), (148, 175),
|
| 79 |
+
(118, 50), (50, 101), (101, 118), (73, 39), (39, 40), (40, 73),
|
| 80 |
+
(9, 151), (151, 108), (108, 9), (48, 115), (115, 131), (131, 48),
|
| 81 |
+
(194, 204), (204, 211), (211, 194), (74, 40), (40, 185), (185, 74),
|
| 82 |
+
(80, 42), (42, 183), (183, 80), (40, 92), (92, 186), (186, 40),
|
| 83 |
+
(230, 229), (229, 118), (118, 230), (202, 212), (212, 214), (214, 202),
|
| 84 |
+
(83, 18), (18, 17), (17, 83), (76, 61), (61, 146), (146, 76),
|
| 85 |
+
(160, 29), (29, 30), (30, 160), (56, 157), (157, 173), (173, 56),
|
| 86 |
+
(106, 204), (204, 194), (194, 106), (135, 214), (214, 192), (192, 135),
|
| 87 |
+
(203, 165), (165, 98), (98, 203), (21, 71), (71, 68), (68, 21),
|
| 88 |
+
(51, 45), (45, 4), (4, 51), (144, 24), (24, 23), (23, 144),
|
| 89 |
+
(77, 146), (146, 91), (91, 77), (205, 50), (50, 187), (187, 205),
|
| 90 |
+
(201, 200), (200, 18), (18, 201), (91, 106), (106, 182), (182, 91),
|
| 91 |
+
(90, 91), (91, 181), (181, 90), (85, 84), (84, 17), (17, 85),
|
| 92 |
+
(206, 203), (203, 36), (36, 206), (148, 171), (171, 140), (140, 148),
|
| 93 |
+
(92, 40), (40, 39), (39, 92), (193, 189), (189, 244), (244, 193),
|
| 94 |
+
(159, 158), (158, 28), (28, 159), (247, 246), (246, 161), (161, 247),
|
| 95 |
+
(236, 3), (3, 196), (196, 236), (54, 68), (68, 104), (104, 54),
|
| 96 |
+
(193, 168), (168, 8), (8, 193), (117, 228), (228, 31), (31, 117),
|
| 97 |
+
(189, 193), (193, 55), (55, 189), (98, 97), (97, 99), (99, 98),
|
| 98 |
+
(126, 47), (47, 100), (100, 126), (166, 79), (79, 218), (218, 166),
|
| 99 |
+
(155, 154), (154, 26), (26, 155), (209, 49), (49, 131), (131, 209),
|
| 100 |
+
(135, 136), (136, 150), (150, 135), (47, 126), (126, 217), (217, 47),
|
| 101 |
+
(223, 52), (52, 53), (53, 223), (45, 51), (51, 134), (134, 45),
|
| 102 |
+
(211, 170), (170, 140), (140, 211), (67, 69), (69, 108), (108, 67),
|
| 103 |
+
(43, 106), (106, 91), (91, 43), (230, 119), (119, 120), (120, 230),
|
| 104 |
+
(226, 130), (130, 247), (247, 226), (63, 53), (53, 52), (52, 63),
|
| 105 |
+
(238, 20), (20, 242), (242, 238), (46, 70), (70, 156), (156, 46),
|
| 106 |
+
(78, 62), (62, 96), (96, 78), (46, 53), (53, 63), (63, 46),
|
| 107 |
+
(143, 34), (34, 227), (227, 143), (123, 117), (117, 111), (111, 123),
|
| 108 |
+
(44, 125), (125, 19), (19, 44), (236, 134), (134, 51), (51, 236),
|
| 109 |
+
(216, 206), (206, 205), (205, 216), (154, 153), (153, 22), (22, 154),
|
| 110 |
+
(39, 37), (37, 167), (167, 39), (200, 201), (201, 208), (208, 200),
|
| 111 |
+
(36, 142), (142, 100), (100, 36), (57, 212), (212, 202), (202, 57),
|
| 112 |
+
(20, 60), (60, 99), (99, 20), (28, 158), (158, 157), (157, 28),
|
| 113 |
+
(35, 226), (226, 113), (113, 35), (160, 159), (159, 27), (27, 160),
|
| 114 |
+
(204, 202), (202, 210), (210, 204), (113, 225), (225, 46), (46, 113),
|
| 115 |
+
(43, 202), (202, 204), (204, 43), (62, 76), (76, 77), (77, 62),
|
| 116 |
+
(137, 123), (123, 116), (116, 137), (41, 38), (38, 72), (72, 41),
|
| 117 |
+
(203, 129), (129, 142), (142, 203), (64, 98), (98, 240), (240, 64),
|
| 118 |
+
(49, 102), (102, 64), (64, 49), (41, 73), (73, 74), (74, 41),
|
| 119 |
+
(212, 216), (216, 207), (207, 212), (42, 74), (74, 184), (184, 42),
|
| 120 |
+
(169, 170), (170, 211), (211, 169), (170, 149), (149, 176), (176, 170),
|
| 121 |
+
(105, 66), (66, 69), (69, 105), (122, 6), (6, 168), (168, 122),
|
| 122 |
+
(123, 147), (147, 187), (187, 123), (96, 77), (77, 90), (90, 96),
|
| 123 |
+
(65, 55), (55, 107), (107, 65), (89, 90), (90, 180), (180, 89),
|
| 124 |
+
(101, 100), (100, 120), (120, 101), (63, 105), (105, 104), (104, 63),
|
| 125 |
+
(93, 137), (137, 227), (227, 93), (15, 86), (86, 85), (85, 15),
|
| 126 |
+
(129, 102), (102, 49), (49, 129), (14, 87), (87, 86), (86, 14),
|
| 127 |
+
(55, 8), (8, 9), (9, 55), (100, 47), (47, 121), (121, 100),
|
| 128 |
+
(145, 23), (23, 22), (22, 145), (88, 89), (89, 179), (179, 88),
|
| 129 |
+
(6, 122), (122, 196), (196, 6), (88, 95), (95, 96), (96, 88),
|
| 130 |
+
(138, 172), (172, 136), (136, 138), (215, 58), (58, 172), (172, 215),
|
| 131 |
+
(115, 48), (48, 219), (219, 115), (42, 80), (80, 81), (81, 42),
|
| 132 |
+
(195, 3), (3, 51), (51, 195), (43, 146), (146, 61), (61, 43),
|
| 133 |
+
(171, 175), (175, 199), (199, 171), (81, 82), (82, 38), (38, 81),
|
| 134 |
+
(53, 46), (46, 225), (225, 53), (144, 163), (163, 110), (110, 144),
|
| 135 |
+
(52, 65), (65, 66), (66, 52), (229, 228), (228, 117), (117, 229),
|
| 136 |
+
(34, 127), (127, 234), (234, 34), (107, 108), (108, 69), (69, 107),
|
| 137 |
+
(109, 108), (108, 151), (151, 109), (48, 64), (64, 235), (235, 48),
|
| 138 |
+
(62, 78), (78, 191), (191, 62), (129, 209), (209, 126), (126, 129),
|
| 139 |
+
(111, 35), (35, 143), (143, 111), (117, 123), (123, 50), (50, 117),
|
| 140 |
+
(222, 65), (65, 52), (52, 222), (19, 125), (125, 141), (141, 19),
|
| 141 |
+
(221, 55), (55, 65), (65, 221), (3, 195), (195, 197), (197, 3),
|
| 142 |
+
(25, 7), (7, 33), (33, 25), (220, 237), (237, 44), (44, 220),
|
| 143 |
+
(70, 71), (71, 139), (139, 70), (122, 193), (193, 245), (245, 122),
|
| 144 |
+
(247, 130), (130, 33), (33, 247), (71, 21), (21, 162), (162, 71),
|
| 145 |
+
(170, 169), (169, 150), (150, 170), (188, 174), (174, 196), (196, 188),
|
| 146 |
+
(216, 186), (186, 92), (92, 216), (2, 97), (97, 167), (167, 2),
|
| 147 |
+
(141, 125), (125, 241), (241, 141), (164, 167), (167, 37), (37, 164),
|
| 148 |
+
(72, 38), (38, 12), (12, 72), (38, 82), (82, 13), (13, 38),
|
| 149 |
+
(63, 68), (68, 71), (71, 63), (226, 35), (35, 111), (111, 226),
|
| 150 |
+
(101, 50), (50, 205), (205, 101), (206, 92), (92, 165), (165, 206),
|
| 151 |
+
(209, 198), (198, 217), (217, 209), (165, 167), (167, 97), (97, 165),
|
| 152 |
+
(220, 115), (115, 218), (218, 220), (133, 112), (112, 243), (243, 133),
|
| 153 |
+
(239, 238), (238, 241), (241, 239), (214, 135), (135, 169), (169, 214),
|
| 154 |
+
(190, 173), (173, 133), (133, 190), (171, 208), (208, 32), (32, 171),
|
| 155 |
+
(125, 44), (44, 237), (237, 125), (86, 87), (87, 178), (178, 86),
|
| 156 |
+
(85, 86), (86, 179), (179, 85), (84, 85), (85, 180), (180, 84),
|
| 157 |
+
(83, 84), (84, 181), (181, 83), (201, 83), (83, 182), (182, 201),
|
| 158 |
+
(137, 93), (93, 132), (132, 137), (76, 62), (62, 183), (183, 76),
|
| 159 |
+
(61, 76), (76, 184), (184, 61), (57, 61), (61, 185), (185, 57),
|
| 160 |
+
(212, 57), (57, 186), (186, 212), (214, 207), (207, 187), (187, 214),
|
| 161 |
+
(34, 143), (143, 156), (156, 34), (79, 239), (239, 237), (237, 79),
|
| 162 |
+
(123, 137), (137, 177), (177, 123), (44, 1), (1, 4), (4, 44),
|
| 163 |
+
(201, 194), (194, 32), (32, 201), (64, 102), (102, 129), (129, 64),
|
| 164 |
+
(213, 215), (215, 138), (138, 213), (59, 166), (166, 219), (219, 59),
|
| 165 |
+
(242, 99), (99, 97), (97, 242), (2, 94), (94, 141), (141, 2),
|
| 166 |
+
(75, 59), (59, 235), (235, 75), (24, 110), (110, 228), (228, 24),
|
| 167 |
+
(25, 130), (130, 226), (226, 25), (23, 24), (24, 229), (229, 23),
|
| 168 |
+
(22, 23), (23, 230), (230, 22), (26, 22), (22, 231), (231, 26),
|
| 169 |
+
(112, 26), (26, 232), (232, 112), (189, 190), (190, 243), (243, 189),
|
| 170 |
+
(221, 56), (56, 190), (190, 221), (28, 56), (56, 221), (221, 28),
|
| 171 |
+
(27, 28), (28, 222), (222, 27), (29, 27), (27, 223), (223, 29),
|
| 172 |
+
(30, 29), (29, 224), (224, 30), (247, 30), (30, 225), (225, 247),
|
| 173 |
+
(238, 79), (79, 20), (20, 238), (166, 59), (59, 75), (75, 166),
|
| 174 |
+
(60, 75), (75, 240), (240, 60), (147, 177), (177, 215), (215, 147),
|
| 175 |
+
(20, 79), (79, 166), (166, 20), (187, 147), (147, 213), (213, 187),
|
| 176 |
+
(112, 233), (233, 244), (244, 112), (233, 128), (128, 245), (245, 233),
|
| 177 |
+
(128, 114), (114, 188), (188, 128), (114, 217), (217, 174), (174, 114),
|
| 178 |
+
(131, 115), (115, 220), (220, 131), (217, 198), (198, 236), (236, 217),
|
| 179 |
+
(198, 131), (131, 134), (134, 198), (177, 132), (132, 58), (58, 177),
|
| 180 |
+
(143, 35), (35, 124), (124, 143), (110, 163), (163, 7), (7, 110),
|
| 181 |
+
(228, 110), (110, 25), (25, 228), (356, 389), (389, 368), (368, 356),
|
| 182 |
+
(11, 302), (302, 267), (267, 11), (452, 350), (350, 349), (349, 452),
|
| 183 |
+
(302, 303), (303, 269), (269, 302), (357, 343), (343, 277), (277, 357),
|
| 184 |
+
(452, 453), (453, 357), (357, 452), (333, 332), (332, 297), (297, 333),
|
| 185 |
+
(175, 152), (152, 377), (377, 175), (347, 348), (348, 330), (330, 347),
|
| 186 |
+
(303, 304), (304, 270), (270, 303), (9, 336), (336, 337), (337, 9),
|
| 187 |
+
(278, 279), (279, 360), (360, 278), (418, 262), (262, 431), (431, 418),
|
| 188 |
+
(304, 408), (408, 409), (409, 304), (310, 415), (415, 407), (407, 310),
|
| 189 |
+
(270, 409), (409, 410), (410, 270), (450, 348), (348, 347), (347, 450),
|
| 190 |
+
(422, 430), (430, 434), (434, 422), (313, 314), (314, 17), (17, 313),
|
| 191 |
+
(306, 307), (307, 375), (375, 306), (387, 388), (388, 260), (260, 387),
|
| 192 |
+
(286, 414), (414, 398), (398, 286), (335, 406), (406, 418), (418, 335),
|
| 193 |
+
(364, 367), (367, 416), (416, 364), (423, 358), (358, 327), (327, 423),
|
| 194 |
+
(251, 284), (284, 298), (298, 251), (281, 5), (5, 4), (4, 281),
|
| 195 |
+
(373, 374), (374, 253), (253, 373), (307, 320), (320, 321), (321, 307),
|
| 196 |
+
(425, 427), (427, 411), (411, 425), (421, 313), (313, 18), (18, 421),
|
| 197 |
+
(321, 405), (405, 406), (406, 321), (320, 404), (404, 405), (405, 320),
|
| 198 |
+
(315, 16), (16, 17), (17, 315), (426, 425), (425, 266), (266, 426),
|
| 199 |
+
(377, 400), (400, 369), (369, 377), (322, 391), (391, 269), (269, 322),
|
| 200 |
+
(417, 465), (465, 464), (464, 417), (386, 257), (257, 258), (258, 386),
|
| 201 |
+
(466, 260), (260, 388), (388, 466), (456, 399), (399, 419), (419, 456),
|
| 202 |
+
(284, 332), (332, 333), (333, 284), (417, 285), (285, 8), (8, 417),
|
| 203 |
+
(346, 340), (340, 261), (261, 346), (413, 441), (441, 285), (285, 413),
|
| 204 |
+
(327, 460), (460, 328), (328, 327), (355, 371), (371, 329), (329, 355),
|
| 205 |
+
(392, 439), (439, 438), (438, 392), (382, 341), (341, 256), (256, 382),
|
| 206 |
+
(429, 420), (420, 360), (360, 429), (364, 394), (394, 379), (379, 364),
|
| 207 |
+
(277, 343), (343, 437), (437, 277), (443, 444), (444, 283), (283, 443),
|
| 208 |
+
(275, 440), (440, 363), (363, 275), (431, 262), (262, 369), (369, 431),
|
| 209 |
+
(297, 338), (338, 337), (337, 297), (273, 375), (375, 321), (321, 273),
|
| 210 |
+
(450, 451), (451, 349), (349, 450), (446, 342), (342, 467), (467, 446),
|
| 211 |
+
(293, 334), (334, 282), (282, 293), (458, 461), (461, 462), (462, 458),
|
| 212 |
+
(276, 353), (353, 383), (383, 276), (308, 324), (324, 325), (325, 308),
|
| 213 |
+
(276, 300), (300, 293), (293, 276), (372, 345), (345, 447), (447, 372),
|
| 214 |
+
(352, 345), (345, 340), (340, 352), (274, 1), (1, 19), (19, 274),
|
| 215 |
+
(456, 248), (248, 281), (281, 456), (436, 427), (427, 425), (425, 436),
|
| 216 |
+
(381, 256), (256, 252), (252, 381), (269, 391), (391, 393), (393, 269),
|
| 217 |
+
(200, 199), (199, 428), (428, 200), (266, 330), (330, 329), (329, 266),
|
| 218 |
+
(287, 273), (273, 422), (422, 287), (250, 462), (462, 328), (328, 250),
|
| 219 |
+
(258, 286), (286, 384), (384, 258), (265, 353), (353, 342), (342, 265),
|
| 220 |
+
(387, 259), (259, 257), (257, 387), (424, 431), (431, 430), (430, 424),
|
| 221 |
+
(342, 353), (353, 276), (276, 342), (273, 335), (335, 424), (424, 273),
|
| 222 |
+
(292, 325), (325, 307), (307, 292), (366, 447), (447, 345), (345, 366),
|
| 223 |
+
(271, 303), (303, 302), (302, 271), (423, 266), (266, 371), (371, 423),
|
| 224 |
+
(294, 455), (455, 460), (460, 294), (279, 278), (278, 294), (294, 279),
|
| 225 |
+
(271, 272), (272, 304), (304, 271), (432, 434), (434, 427), (427, 432),
|
| 226 |
+
(272, 407), (407, 408), (408, 272), (394, 430), (430, 431), (431, 394),
|
| 227 |
+
(395, 369), (369, 400), (400, 395), (334, 333), (333, 299), (299, 334),
|
| 228 |
+
(351, 417), (417, 168), (168, 351), (352, 280), (280, 411), (411, 352),
|
| 229 |
+
(325, 319), (319, 320), (320, 325), (295, 296), (296, 336), (336, 295),
|
| 230 |
+
(319, 403), (403, 404), (404, 319), (330, 348), (348, 349), (349, 330),
|
| 231 |
+
(293, 298), (298, 333), (333, 293), (323, 454), (454, 447), (447, 323),
|
| 232 |
+
(15, 16), (16, 315), (315, 15), (358, 429), (429, 279), (279, 358),
|
| 233 |
+
(14, 15), (15, 316), (316, 14), (285, 336), (336, 9), (9, 285),
|
| 234 |
+
(329, 349), (349, 350), (350, 329), (374, 380), (380, 252), (252, 374),
|
| 235 |
+
(318, 402), (402, 403), (403, 318), (6, 197), (197, 419), (419, 6),
|
| 236 |
+
(318, 319), (319, 325), (325, 318), (367, 364), (364, 365), (365, 367),
|
| 237 |
+
(435, 367), (367, 397), (397, 435), (344, 438), (438, 439), (439, 344),
|
| 238 |
+
(272, 271), (271, 311), (311, 272), (195, 5), (5, 281), (281, 195),
|
| 239 |
+
(273, 287), (287, 291), (291, 273), (396, 428), (428, 199), (199, 396),
|
| 240 |
+
(311, 271), (271, 268), (268, 311), (283, 444), (444, 445), (445, 283),
|
| 241 |
+
(373, 254), (254, 339), (339, 373), (282, 334), (334, 296), (296, 282),
|
| 242 |
+
(449, 347), (347, 346), (346, 449), (264, 447), (447, 454), (454, 264),
|
| 243 |
+
(336, 296), (296, 299), (299, 336), (338, 10), (10, 151), (151, 338),
|
| 244 |
+
(278, 439), (439, 455), (455, 278), (292, 407), (407, 415), (415, 292),
|
| 245 |
+
(358, 371), (371, 355), (355, 358), (340, 345), (345, 372), (372, 340),
|
| 246 |
+
(346, 347), (347, 280), (280, 346), (442, 443), (443, 282), (282, 442),
|
| 247 |
+
(19, 94), (94, 370), (370, 19), (441, 442), (442, 295), (295, 441),
|
| 248 |
+
(248, 419), (419, 197), (197, 248), (263, 255), (255, 359), (359, 263),
|
| 249 |
+
(440, 275), (275, 274), (274, 440), (300, 383), (383, 368), (368, 300),
|
| 250 |
+
(351, 412), (412, 465), (465, 351), (263, 467), (467, 466), (466, 263),
|
| 251 |
+
(301, 368), (368, 389), (389, 301), (395, 378), (378, 379), (379, 395),
|
| 252 |
+
(412, 351), (351, 419), (419, 412), (436, 426), (426, 322), (322, 436),
|
| 253 |
+
(2, 164), (164, 393), (393, 2), (370, 462), (462, 461), (461, 370),
|
| 254 |
+
(164, 0), (0, 267), (267, 164), (302, 11), (11, 12), (12, 302),
|
| 255 |
+
(268, 12), (12, 13), (13, 268), (293, 300), (300, 301), (301, 293),
|
| 256 |
+
(446, 261), (261, 340), (340, 446), (330, 266), (266, 425), (425, 330),
|
| 257 |
+
(426, 423), (423, 391), (391, 426), (429, 355), (355, 437), (437, 429),
|
| 258 |
+
(391, 327), (327, 326), (326, 391), (440, 457), (457, 438), (438, 440),
|
| 259 |
+
(341, 382), (382, 362), (362, 341), (459, 457), (457, 461), (461, 459),
|
| 260 |
+
(434, 430), (430, 394), (394, 434), (414, 463), (463, 362), (362, 414),
|
| 261 |
+
(396, 369), (369, 262), (262, 396), (354, 461), (461, 457), (457, 354),
|
| 262 |
+
(316, 403), (403, 402), (402, 316), (315, 404), (404, 403), (403, 315),
|
| 263 |
+
(314, 405), (405, 404), (404, 314), (313, 406), (406, 405), (405, 313),
|
| 264 |
+
(421, 418), (418, 406), (406, 421), (366, 401), (401, 361), (361, 366),
|
| 265 |
+
(306, 408), (408, 407), (407, 306), (291, 409), (409, 408), (408, 291),
|
| 266 |
+
(287, 410), (410, 409), (409, 287), (432, 436), (436, 410), (410, 432),
|
| 267 |
+
(434, 416), (416, 411), (411, 434), (264, 368), (368, 383), (383, 264),
|
| 268 |
+
(309, 438), (438, 457), (457, 309), (352, 376), (376, 401), (401, 352),
|
| 269 |
+
(274, 275), (275, 4), (4, 274), (421, 428), (428, 262), (262, 421),
|
| 270 |
+
(294, 327), (327, 358), (358, 294), (433, 416), (416, 367), (367, 433),
|
| 271 |
+
(289, 455), (455, 439), (439, 289), (462, 370), (370, 326), (326, 462),
|
| 272 |
+
(2, 326), (326, 370), (370, 2), (305, 460), (460, 455), (455, 305),
|
| 273 |
+
(254, 449), (449, 448), (448, 254), (255, 261), (261, 446), (446, 255),
|
| 274 |
+
(253, 450), (450, 449), (449, 253), (252, 451), (451, 450), (450, 252),
|
| 275 |
+
(256, 452), (452, 451), (451, 256), (341, 453), (453, 452), (452, 341),
|
| 276 |
+
(413, 464), (464, 463), (463, 413), (441, 413), (413, 414), (414, 441),
|
| 277 |
+
(258, 442), (442, 441), (441, 258), (257, 443), (443, 442), (442, 257),
|
| 278 |
+
(259, 444), (444, 443), (443, 259), (260, 445), (445, 444), (444, 260),
|
| 279 |
+
(467, 342), (342, 445), (445, 467), (459, 458), (458, 250), (250, 459),
|
| 280 |
+
(289, 392), (392, 290), (290, 289), (290, 328), (328, 460), (460, 290),
|
| 281 |
+
(376, 433), (433, 435), (435, 376), (250, 290), (290, 392), (392, 250),
|
| 282 |
+
(411, 416), (416, 433), (433, 411), (341, 463), (463, 464), (464, 341),
|
| 283 |
+
(453, 464), (464, 465), (465, 453), (357, 465), (465, 412), (412, 357),
|
| 284 |
+
(343, 412), (412, 399), (399, 343), (360, 363), (363, 440), (440, 360),
|
| 285 |
+
(437, 399), (399, 456), (456, 437), (420, 456), (456, 363), (363, 420),
|
| 286 |
+
(401, 435), (435, 288), (288, 401), (372, 383), (383, 353), (353, 372),
|
| 287 |
+
(339, 255), (255, 249), (249, 339), (448, 261), (261, 255), (255, 448),
|
| 288 |
+
(133, 243), (243, 190), (190, 133), (133, 155), (155, 112), (112, 133),
|
| 289 |
+
(33, 246), (246, 247), (247, 33), (33, 130), (130, 25), (25, 33),
|
| 290 |
+
(398, 384), (384, 286), (286, 398), (362, 398), (398, 414), (414, 362),
|
| 291 |
+
(362, 463), (463, 341), (341, 362), (263, 359), (359, 467), (467, 263),
|
| 292 |
+
(263, 249), (249, 255), (255, 263), (466, 467), (467, 260), (260, 466),
|
| 293 |
+
(75, 60), (60, 166), (166, 75), (238, 239), (239, 79), (79, 238),
|
| 294 |
+
(162, 127), (127, 139), (139, 162), (72, 11), (11, 37), (37, 72),
|
| 295 |
+
(121, 232), (232, 120), (120, 121), (73, 72), (72, 39), (39, 73),
|
| 296 |
+
(114, 128), (128, 47), (47, 114), (233, 232), (232, 128), (128, 233),
|
| 297 |
+
(103, 104), (104, 67), (67, 103), (152, 175), (175, 148), (148, 152),
|
| 298 |
+
(119, 118), (118, 101), (101, 119), (74, 73), (73, 40), (40, 74),
|
| 299 |
+
(107, 9), (9, 108), (108, 107), (49, 48), (48, 131), (131, 49),
|
| 300 |
+
(32, 194), (194, 211), (211, 32), (184, 74), (74, 185), (185, 184),
|
| 301 |
+
(191, 80), (80, 183), (183, 191), (185, 40), (40, 186), (186, 185),
|
| 302 |
+
(119, 230), (230, 118), (118, 119), (210, 202), (202, 214), (214, 210),
|
| 303 |
+
(84, 83), (83, 17), (17, 84), (77, 76), (76, 146), (146, 77),
|
| 304 |
+
(161, 160), (160, 30), (30, 161), (190, 56), (56, 173), (173, 190),
|
| 305 |
+
(182, 106), (106, 194), (194, 182), (138, 135), (135, 192), (192, 138),
|
| 306 |
+
(129, 203), (203, 98), (98, 129), (54, 21), (21, 68), (68, 54),
|
| 307 |
+
(5, 51), (51, 4), (4, 5), (145, 144), (144, 23), (23, 145),
|
| 308 |
+
(90, 77), (77, 91), (91, 90), (207, 205), (205, 187), (187, 207),
|
| 309 |
+
(83, 201), (201, 18), (18, 83), (181, 91), (91, 182), (182, 181),
|
| 310 |
+
(180, 90), (90, 181), (181, 180), (16, 85), (85, 17), (17, 16),
|
| 311 |
+
(205, 206), (206, 36), (36, 205), (176, 148), (148, 140), (140, 176),
|
| 312 |
+
(165, 92), (92, 39), (39, 165), (245, 193), (193, 244), (244, 245),
|
| 313 |
+
(27, 159), (159, 28), (28, 27), (30, 247), (247, 161), (161, 30),
|
| 314 |
+
(174, 236), (236, 196), (196, 174), (103, 54), (54, 104), (104, 103),
|
| 315 |
+
(55, 193), (193, 8), (8, 55), (111, 117), (117, 31), (31, 111),
|
| 316 |
+
(221, 189), (189, 55), (55, 221), (240, 98), (98, 99), (99, 240),
|
| 317 |
+
(142, 126), (126, 100), (100, 142), (219, 166), (166, 218), (218, 219),
|
| 318 |
+
(112, 155), (155, 26), (26, 112), (198, 209), (209, 131), (131, 198),
|
| 319 |
+
(169, 135), (135, 150), (150, 169), (114, 47), (47, 217), (217, 114),
|
| 320 |
+
(224, 223), (223, 53), (53, 224), (220, 45), (45, 134), (134, 220),
|
| 321 |
+
(32, 211), (211, 140), (140, 32), (109, 67), (67, 108), (108, 109),
|
| 322 |
+
(146, 43), (43, 91), (91, 146), (231, 230), (230, 120), (120, 231),
|
| 323 |
+
(113, 226), (226, 247), (247, 113), (105, 63), (63, 52), (52, 105),
|
| 324 |
+
(241, 238), (238, 242), (242, 241), (124, 46), (46, 156), (156, 124),
|
| 325 |
+
(95, 78), (78, 96), (96, 95), (70, 46), (46, 63), (63, 70),
|
| 326 |
+
(116, 143), (143, 227), (227, 116), (116, 123), (123, 111), (111, 116),
|
| 327 |
+
(1, 44), (44, 19), (19, 1), (3, 236), (236, 51), (51, 3),
|
| 328 |
+
(207, 216), (216, 205), (205, 207), (26, 154), (154, 22), (22, 26),
|
| 329 |
+
(165, 39), (39, 167), (167, 165), (199, 200), (200, 208), (208, 199),
|
| 330 |
+
(101, 36), (36, 100), (100, 101), (43, 57), (57, 202), (202, 43),
|
| 331 |
+
(242, 20), (20, 99), (99, 242), (56, 28), (28, 157), (157, 56),
|
| 332 |
+
(124, 35), (35, 113), (113, 124), (29, 160), (160, 27), (27, 29),
|
| 333 |
+
(211, 204), (204, 210), (210, 211), (124, 113), (113, 46), (46, 124),
|
| 334 |
+
(106, 43), (43, 204), (204, 106), (96, 62), (62, 77), (77, 96),
|
| 335 |
+
(227, 137), (137, 116), (116, 227), (73, 41), (41, 72), (72, 73),
|
| 336 |
+
(36, 203), (203, 142), (142, 36), (235, 64), (64, 240), (240, 235),
|
| 337 |
+
(48, 49), (49, 64), (64, 48), (42, 41), (41, 74), (74, 42),
|
| 338 |
+
(214, 212), (212, 207), (207, 214), (183, 42), (42, 184), (184, 183),
|
| 339 |
+
(210, 169), (169, 211), (211, 210), (140, 170), (170, 176), (176, 140),
|
| 340 |
+
(104, 105), (105, 69), (69, 104), (193, 122), (122, 168), (168, 193),
|
| 341 |
+
(50, 123), (123, 187), (187, 50), (89, 96), (96, 90), (90, 89),
|
| 342 |
+
(66, 65), (65, 107), (107, 66), (179, 89), (89, 180), (180, 179),
|
| 343 |
+
(119, 101), (101, 120), (120, 119), (68, 63), (63, 104), (104, 68),
|
| 344 |
+
(234, 93), (93, 227), (227, 234), (16, 15), (15, 85), (85, 16),
|
| 345 |
+
(209, 129), (129, 49), (49, 209), (15, 14), (14, 86), (86, 15),
|
| 346 |
+
(107, 55), (55, 9), (9, 107), (120, 100), (100, 121), (121, 120),
|
| 347 |
+
(153, 145), (145, 22), (22, 153), (178, 88), (88, 179), (179, 178),
|
| 348 |
+
(197, 6), (6, 196), (196, 197), (89, 88), (88, 96), (96, 89),
|
| 349 |
+
(135, 138), (138, 136), (136, 135), (138, 215), (215, 172), (172, 138),
|
| 350 |
+
(218, 115), (115, 219), (219, 218), (41, 42), (42, 81), (81, 41),
|
| 351 |
+
(5, 195), (195, 51), (51, 5), (57, 43), (43, 61), (61, 57),
|
| 352 |
+
(208, 171), (171, 199), (199, 208), (41, 81), (81, 38), (38, 41),
|
| 353 |
+
(224, 53), (53, 225), (225, 224), (24, 144), (144, 110), (110, 24),
|
| 354 |
+
(105, 52), (52, 66), (66, 105), (118, 229), (229, 117), (117, 118),
|
| 355 |
+
(227, 34), (34, 234), (234, 227), (66, 107), (107, 69), (69, 66),
|
| 356 |
+
(10, 109), (109, 151), (151, 10), (219, 48), (48, 235), (235, 219),
|
| 357 |
+
(183, 62), (62, 191), (191, 183), (142, 129), (129, 126), (126, 142),
|
| 358 |
+
(116, 111), (111, 143), (143, 116), (118, 117), (117, 50), (50, 118),
|
| 359 |
+
(223, 222), (222, 52), (52, 223), (94, 19), (19, 141), (141, 94),
|
| 360 |
+
(222, 221), (221, 65), (65, 222), (196, 3), (3, 197), (197, 196),
|
| 361 |
+
(45, 220), (220, 44), (44, 45), (156, 70), (70, 139), (139, 156),
|
| 362 |
+
(188, 122), (122, 245), (245, 188), (139, 71), (71, 162), (162, 139),
|
| 363 |
+
(149, 170), (170, 150), (150, 149), (122, 188), (188, 196), (196, 122),
|
| 364 |
+
(206, 216), (216, 92), (92, 206), (164, 2), (2, 167), (167, 164),
|
| 365 |
+
(242, 141), (141, 241), (241, 242), (0, 164), (164, 37), (37, 0),
|
| 366 |
+
(11, 72), (72, 12), (12, 11), (12, 38), (38, 13), (13, 12),
|
| 367 |
+
(70, 63), (63, 71), (71, 70), (31, 226), (226, 111), (111, 31),
|
| 368 |
+
(36, 101), (101, 205), (205, 36), (203, 206), (206, 165), (165, 203),
|
| 369 |
+
(126, 209), (209, 217), (217, 126), (98, 165), (165, 97), (97, 98),
|
| 370 |
+
(237, 220), (220, 218), (218, 237), (237, 239), (239, 241), (241, 237),
|
| 371 |
+
(210, 214), (214, 169), (169, 210), (140, 171), (171, 32), (32, 140),
|
| 372 |
+
(241, 125), (125, 237), (237, 241), (179, 86), (86, 178), (178, 179),
|
| 373 |
+
(180, 85), (85, 179), (179, 180), (181, 84), (84, 180), (180, 181),
|
| 374 |
+
(182, 83), (83, 181), (181, 182), (194, 201), (201, 182), (182, 194),
|
| 375 |
+
(177, 137), (137, 132), (132, 177), (184, 76), (76, 183), (183, 184),
|
| 376 |
+
(185, 61), (61, 184), (184, 185), (186, 57), (57, 185), (185, 186),
|
| 377 |
+
(216, 212), (212, 186), (186, 216), (192, 214), (214, 187), (187, 192),
|
| 378 |
+
(139, 34), (34, 156), (156, 139), (218, 79), (79, 237), (237, 218),
|
| 379 |
+
(147, 123), (123, 177), (177, 147), (45, 44), (44, 4), (4, 45),
|
| 380 |
+
(208, 201), (201, 32), (32, 208), (98, 64), (64, 129), (129, 98),
|
| 381 |
+
(192, 213), (213, 138), (138, 192), (235, 59), (59, 219), (219, 235),
|
| 382 |
+
(141, 242), (242, 97), (97, 141), (97, 2), (2, 141), (141, 97),
|
| 383 |
+
(240, 75), (75, 235), (235, 240), (229, 24), (24, 228), (228, 229),
|
| 384 |
+
(31, 25), (25, 226), (226, 31), (230, 23), (23, 229), (229, 230),
|
| 385 |
+
(231, 22), (22, 230), (230, 231), (232, 26), (26, 231), (231, 232),
|
| 386 |
+
(233, 112), (112, 232), (232, 233), (244, 189), (189, 243), (243, 244),
|
| 387 |
+
(189, 221), (221, 190), (190, 189), (222, 28), (28, 221), (221, 222),
|
| 388 |
+
(223, 27), (27, 222), (222, 223), (224, 29), (29, 223), (223, 224),
|
| 389 |
+
(225, 30), (30, 224), (224, 225), (113, 247), (247, 225), (225, 113),
|
| 390 |
+
(99, 60), (60, 240), (240, 99), (213, 147), (147, 215), (215, 213),
|
| 391 |
+
(60, 20), (20, 166), (166, 60), (192, 187), (187, 213), (213, 192),
|
| 392 |
+
(243, 112), (112, 244), (244, 243), (244, 233), (233, 245), (245, 244),
|
| 393 |
+
(245, 128), (128, 188), (188, 245), (188, 114), (114, 174), (174, 188),
|
| 394 |
+
(134, 131), (131, 220), (220, 134), (174, 217), (217, 236), (236, 174),
|
| 395 |
+
(236, 198), (198, 134), (134, 236), (215, 177), (177, 58), (58, 215),
|
| 396 |
+
(156, 143), (143, 124), (124, 156), (25, 110), (110, 7), (7, 25),
|
| 397 |
+
(31, 228), (228, 25), (25, 31), (264, 356), (356, 368), (368, 264),
|
| 398 |
+
(0, 11), (11, 267), (267, 0), (451, 452), (452, 349), (349, 451),
|
| 399 |
+
(267, 302), (302, 269), (269, 267), (350, 357), (357, 277), (277, 350),
|
| 400 |
+
(350, 452), (452, 357), (357, 350), (299, 333), (333, 297), (297, 299),
|
| 401 |
+
(396, 175), (175, 377), (377, 396), (280, 347), (347, 330), (330, 280),
|
| 402 |
+
(269, 303), (303, 270), (270, 269), (151, 9), (9, 337), (337, 151),
|
| 403 |
+
(344, 278), (278, 360), (360, 344), (424, 418), (418, 431), (431, 424),
|
| 404 |
+
(270, 304), (304, 409), (409, 270), (272, 310), (310, 407), (407, 272),
|
| 405 |
+
(322, 270), (270, 410), (410, 322), (449, 450), (450, 347), (347, 449),
|
| 406 |
+
(432, 422), (422, 434), (434, 432), (18, 313), (313, 17), (17, 18),
|
| 407 |
+
(291, 306), (306, 375), (375, 291), (259, 387), (387, 260), (260, 259),
|
| 408 |
+
(424, 335), (335, 418), (418, 424), (434, 364), (364, 416), (416, 434),
|
| 409 |
+
(391, 423), (423, 327), (327, 391), (301, 251), (251, 298), (298, 301),
|
| 410 |
+
(275, 281), (281, 4), (4, 275), (254, 373), (373, 253), (253, 254),
|
| 411 |
+
(375, 307), (307, 321), (321, 375), (280, 425), (425, 411), (411, 280),
|
| 412 |
+
(200, 421), (421, 18), (18, 200), (335, 321), (321, 406), (406, 335),
|
| 413 |
+
(321, 320), (320, 405), (405, 321), (314, 315), (315, 17), (17, 314),
|
| 414 |
+
(423, 426), (426, 266), (266, 423), (396, 377), (377, 369), (369, 396),
|
| 415 |
+
(270, 322), (322, 269), (269, 270), (413, 417), (417, 464), (464, 413),
|
| 416 |
+
(385, 386), (386, 258), (258, 385), (248, 456), (456, 419), (419, 248),
|
| 417 |
+
(298, 284), (284, 333), (333, 298), (168, 417), (417, 8), (8, 168),
|
| 418 |
+
(448, 346), (346, 261), (261, 448), (417, 413), (413, 285), (285, 417),
|
| 419 |
+
(326, 327), (327, 328), (328, 326), (277, 355), (355, 329), (329, 277),
|
| 420 |
+
(309, 392), (392, 438), (438, 309), (381, 382), (382, 256), (256, 381),
|
| 421 |
+
(279, 429), (429, 360), (360, 279), (365, 364), (364, 379), (379, 365),
|
| 422 |
+
(355, 277), (277, 437), (437, 355), (282, 443), (443, 283), (283, 282),
|
| 423 |
+
(281, 275), (275, 363), (363, 281), (395, 431), (431, 369), (369, 395),
|
| 424 |
+
(299, 297), (297, 337), (337, 299), (335, 273), (273, 321), (321, 335),
|
| 425 |
+
(348, 450), (450, 349), (349, 348), (359, 446), (446, 467), (467, 359),
|
| 426 |
+
(283, 293), (293, 282), (282, 283), (250, 458), (458, 462), (462, 250),
|
| 427 |
+
(300, 276), (276, 383), (383, 300), (292, 308), (308, 325), (325, 292),
|
| 428 |
+
(283, 276), (276, 293), (293, 283), (264, 372), (372, 447), (447, 264),
|
| 429 |
+
(346, 352), (352, 340), (340, 346), (354, 274), (274, 19), (19, 354),
|
| 430 |
+
(363, 456), (456, 281), (281, 363), (426, 436), (436, 425), (425, 426),
|
| 431 |
+
(380, 381), (381, 252), (252, 380), (267, 269), (269, 393), (393, 267),
|
| 432 |
+
(421, 200), (200, 428), (428, 421), (371, 266), (266, 329), (329, 371),
|
| 433 |
+
(432, 287), (287, 422), (422, 432), (290, 250), (250, 328), (328, 290),
|
| 434 |
+
(385, 258), (258, 384), (384, 385), (446, 265), (265, 342), (342, 446),
|
| 435 |
+
(386, 387), (387, 257), (257, 386), (422, 424), (424, 430), (430, 422),
|
| 436 |
+
(445, 342), (342, 276), (276, 445), (422, 273), (273, 424), (424, 422),
|
| 437 |
+
(306, 292), (292, 307), (307, 306), (352, 366), (366, 345), (345, 352),
|
| 438 |
+
(268, 271), (271, 302), (302, 268), (358, 423), (423, 371), (371, 358),
|
| 439 |
+
(327, 294), (294, 460), (460, 327), (331, 279), (279, 294), (294, 331),
|
| 440 |
+
(303, 271), (271, 304), (304, 303), (436, 432), (432, 427), (427, 436),
|
| 441 |
+
(304, 272), (272, 408), (408, 304), (395, 394), (394, 431), (431, 395),
|
| 442 |
+
(378, 395), (395, 400), (400, 378), (296, 334), (334, 299), (299, 296),
|
| 443 |
+
(6, 351), (351, 168), (168, 6), (376, 352), (352, 411), (411, 376),
|
| 444 |
+
(307, 325), (325, 320), (320, 307), (285, 295), (295, 336), (336, 285),
|
| 445 |
+
(320, 319), (319, 404), (404, 320), (329, 330), (330, 349), (349, 329),
|
| 446 |
+
(334, 293), (293, 333), (333, 334), (366, 323), (323, 447), (447, 366),
|
| 447 |
+
(316, 15), (15, 315), (315, 316), (331, 358), (358, 279), (279, 331),
|
| 448 |
+
(317, 14), (14, 316), (316, 317), (8, 285), (285, 9), (9, 8),
|
| 449 |
+
(277, 329), (329, 350), (350, 277), (253, 374), (374, 252), (252, 253),
|
| 450 |
+
(319, 318), (318, 403), (403, 319), (351, 6), (6, 419), (419, 351),
|
| 451 |
+
(324, 318), (318, 325), (325, 324), (397, 367), (367, 365), (365, 397),
|
| 452 |
+
(288, 435), (435, 397), (397, 288), (278, 344), (344, 439), (439, 278),
|
| 453 |
+
(310, 272), (272, 311), (311, 310), (248, 195), (195, 281), (281, 248),
|
| 454 |
+
(375, 273), (273, 291), (291, 375), (175, 396), (396, 199), (199, 175),
|
| 455 |
+
(312, 311), (311, 268), (268, 312), (276, 283), (283, 445), (445, 276),
|
| 456 |
+
(390, 373), (373, 339), (339, 390), (295, 282), (282, 296), (296, 295),
|
| 457 |
+
(448, 449), (449, 346), (346, 448), (356, 264), (264, 454), (454, 356),
|
| 458 |
+
(337, 336), (336, 299), (299, 337), (337, 338), (338, 151), (151, 337),
|
| 459 |
+
(294, 278), (278, 455), (455, 294), (308, 292), (292, 415), (415, 308),
|
| 460 |
+
(429, 358), (358, 355), (355, 429), (265, 340), (340, 372), (372, 265),
|
| 461 |
+
(352, 346), (346, 280), (280, 352), (295, 442), (442, 282), (282, 295),
|
| 462 |
+
(354, 19), (19, 370), (370, 354), (285, 441), (441, 295), (295, 285),
|
| 463 |
+
(195, 248), (248, 197), (197, 195), (457, 440), (440, 274), (274, 457),
|
| 464 |
+
(301, 300), (300, 368), (368, 301), (417, 351), (351, 465), (465, 417),
|
| 465 |
+
(251, 301), (301, 389), (389, 251), (394, 395), (395, 379), (379, 394),
|
| 466 |
+
(399, 412), (412, 419), (419, 399), (410, 436), (436, 322), (322, 410),
|
| 467 |
+
(326, 2), (2, 393), (393, 326), (354, 370), (370, 461), (461, 354),
|
| 468 |
+
(393, 164), (164, 267), (267, 393), (268, 302), (302, 12), (12, 268),
|
| 469 |
+
(312, 268), (268, 13), (13, 312), (298, 293), (293, 301), (301, 298),
|
| 470 |
+
(265, 446), (446, 340), (340, 265), (280, 330), (330, 425), (425, 280),
|
| 471 |
+
(322, 426), (426, 391), (391, 322), (420, 429), (429, 437), (437, 420),
|
| 472 |
+
(393, 391), (391, 326), (326, 393), (344, 440), (440, 438), (438, 344),
|
| 473 |
+
(458, 459), (459, 461), (461, 458), (364, 434), (434, 394), (394, 364),
|
| 474 |
+
(428, 396), (396, 262), (262, 428), (274, 354), (354, 457), (457, 274),
|
| 475 |
+
(317, 316), (316, 402), (402, 317), (316, 315), (315, 403), (403, 316),
|
| 476 |
+
(315, 314), (314, 404), (404, 315), (314, 313), (313, 405), (405, 314),
|
| 477 |
+
(313, 421), (421, 406), (406, 313), (323, 366), (366, 361), (361, 323),
|
| 478 |
+
(292, 306), (306, 407), (407, 292), (306, 291), (291, 408), (408, 306),
|
| 479 |
+
(291, 287), (287, 409), (409, 291), (287, 432), (432, 410), (410, 287),
|
| 480 |
+
(427, 434), (434, 411), (411, 427), (372, 264), (264, 383), (383, 372),
|
| 481 |
+
(459, 309), (309, 457), (457, 459), (366, 352), (352, 401), (401, 366),
|
| 482 |
+
(1, 274), (274, 4), (4, 1), (418, 421), (421, 262), (262, 418),
|
| 483 |
+
(331, 294), (294, 358), (358, 331), (435, 433), (433, 367), (367, 435),
|
| 484 |
+
(392, 289), (289, 439), (439, 392), (328, 462), (462, 326), (326, 328),
|
| 485 |
+
(94, 2), (2, 370), (370, 94), (289, 305), (305, 455), (455, 289),
|
| 486 |
+
(339, 254), (254, 448), (448, 339), (359, 255), (255, 446), (446, 359),
|
| 487 |
+
(254, 253), (253, 449), (449, 254), (253, 252), (252, 450), (450, 253),
|
| 488 |
+
(252, 256), (256, 451), (451, 252), (256, 341), (341, 452), (452, 256),
|
| 489 |
+
(414, 413), (413, 463), (463, 414), (286, 441), (441, 414), (414, 286),
|
| 490 |
+
(286, 258), (258, 441), (441, 286), (258, 257), (257, 442), (442, 258),
|
| 491 |
+
(257, 259), (259, 443), (443, 257), (259, 260), (260, 444), (444, 259),
|
| 492 |
+
(260, 467), (467, 445), (445, 260), (309, 459), (459, 250), (250, 309),
|
| 493 |
+
(305, 289), (289, 290), (290, 305), (305, 290), (290, 460), (460, 305),
|
| 494 |
+
(401, 376), (376, 435), (435, 401), (309, 250), (250, 392), (392, 309),
|
| 495 |
+
(376, 411), (411, 433), (433, 376), (453, 341), (341, 464), (464, 453),
|
| 496 |
+
(357, 453), (453, 465), (465, 357), (343, 357), (357, 412), (412, 343),
|
| 497 |
+
(437, 343), (343, 399), (399, 437), (344, 360), (360, 440), (440, 344),
|
| 498 |
+
(420, 437), (437, 456), (456, 420), (360, 420), (420, 363), (363, 360),
|
| 499 |
+
(361, 401), (401, 288), (288, 361), (265, 372), (372, 353), (353, 265),
|
| 500 |
+
(390, 339), (339, 249), (249, 390), (339, 448), (448, 255), (255, 339)])
|
projects/DEMO/展示用學生/02_臉部3D網格/main.py
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# -*- coding: utf-8 -*-
|
| 2 |
+
"""
|
| 3 |
+
Telearn Arcade Hub 入口檔(自動產生)
|
| 4 |
+
實際執行: step1_facemesh.py
|
| 5 |
+
規格: projects/{班級}/{學生}/{專案}/main.py
|
| 6 |
+
"""
|
| 7 |
+
from pathlib import Path
|
| 8 |
+
import os
|
| 9 |
+
import runpy
|
| 10 |
+
import sys
|
| 11 |
+
|
| 12 |
+
_ROOT = Path(__file__).resolve().parent
|
| 13 |
+
os.chdir(_ROOT)
|
| 14 |
+
if str(_ROOT) not in sys.path:
|
| 15 |
+
sys.path.insert(0, str(_ROOT))
|
| 16 |
+
|
| 17 |
+
# 執行原始學員腳本
|
| 18 |
+
runpy.run_path(str(_ROOT / 'step1_facemesh.py'), run_name="__main__")
|
projects/DEMO/展示用學生/02_臉部3D網格/step1_facemesh.py
ADDED
|
@@ -0,0 +1,129 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
Week 11 - Step 1: Face Mesh Tracking with Connections (Template 填空版)
|
| 3 |
+
===================================================================
|
| 4 |
+
本步驟目標:
|
| 5 |
+
1. 使用 OpenCV 開啟視訊鏡頭
|
| 6 |
+
2. 載入 MediaPipe 臉部特徵偵測器 (face_landmarker.task)
|
| 7 |
+
3. 偵測人臉,並在臉上畫出綠色的 3D 特效網格線與黃色特徵點
|
| 8 |
+
"""
|
| 9 |
+
import cv2
|
| 10 |
+
import mediapipe as mp
|
| 11 |
+
import numpy as np
|
| 12 |
+
import sys
|
| 13 |
+
import os
|
| 14 |
+
|
| 15 |
+
# 1. 初始化攝影機
|
| 16 |
+
cap = cv2.VideoCapture(0)
|
| 17 |
+
if not cap.isOpened():
|
| 18 |
+
print("Error: Could not open camera.")
|
| 19 |
+
sys.exit()
|
| 20 |
+
|
| 21 |
+
# 2. 載入 MediaPipe 臉部偵測器模型 (Face Landmarker)
|
| 22 |
+
model_path=os.path.join(os.path.dirname(os.path.abspath(__file__)), 'face_landmarker.task')
|
| 23 |
+
if not os.path.exists(model_path):
|
| 24 |
+
print(f"Error: Model file not found at {model_path}")
|
| 25 |
+
print("Please make sure face_landmarker.task is in the examples folder.")
|
| 26 |
+
sys.exit()
|
| 27 |
+
|
| 28 |
+
try:
|
| 29 |
+
from mediapipe.tasks import python
|
| 30 |
+
from mediapipe.tasks.python import vision
|
| 31 |
+
base_options = python.BaseOptions(model_asset_path=model_path)
|
| 32 |
+
options = vision.FaceLandmarkerOptions(base_options=base_options, num_faces=1)
|
| 33 |
+
detector = vision.FaceLandmarker.create_from_options(options)
|
| 34 |
+
print("Face Landmarker Tasks API initialized successfully.")
|
| 35 |
+
except Exception as e:
|
| 36 |
+
print(f"Error initializing detector: {e}")
|
| 37 |
+
sys.exit()
|
| 38 |
+
|
| 39 |
+
# 匯入臉部網格連接點定義
|
| 40 |
+
try:
|
| 41 |
+
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..'))
|
| 42 |
+
from face_mesh_connections import FACEMESH_TESSELATION
|
| 43 |
+
except ImportError:
|
| 44 |
+
FACEMESH_TESSELATION = []
|
| 45 |
+
|
| 46 |
+
# 繪圖模式:0 = 僅畫網格線, 1 = 僅畫點點, 2 = 點線都畫
|
| 47 |
+
draw_mode = 0
|
| 48 |
+
|
| 49 |
+
print("Press 'q' in the window to exit, or 'm' to toggle display modes.")
|
| 50 |
+
|
| 51 |
+
# 3. 主迴圈
|
| 52 |
+
while True:
|
| 53 |
+
success, img = cap.read()
|
| 54 |
+
if not success:
|
| 55 |
+
print("Error: Failed to read frame.")
|
| 56 |
+
break
|
| 57 |
+
|
| 58 |
+
# 【TODO 1】畫面水平翻轉,並將 BGR 轉成 MediaPipe 需要的 RGB 格式
|
| 59 |
+
img = cv2.flip(img, 1)
|
| 60 |
+
img_h, img_w, _ = img.shape
|
| 61 |
+
img_rgb = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
|
| 62 |
+
mp_image = mp.Image(image_format=mp.ImageFormat.SRGB, data=img_rgb)
|
| 63 |
+
|
| 64 |
+
|
| 65 |
+
|
| 66 |
+
# 執行臉部偵測
|
| 67 |
+
results = detector.detect(mp_image)
|
| 68 |
+
|
| 69 |
+
# 【TODO 2】如果偵測到臉部,畫出 3D 特效網格線
|
| 70 |
+
# 提示:取出 results.face_landmarks[0],並用迴圈巡覽 FACEMESH_TESSELATION
|
| 71 |
+
# 點的座標 (lm.x, lm.y) 是 0~1 的比例,需要乘上 img_w 和 img_h 換算成像素座標
|
| 72 |
+
if results.face_landmarks:
|
| 73 |
+
face_lms = results.face_landmarks[0]
|
| 74 |
+
num_lms = len(face_lms)
|
| 75 |
+
|
| 76 |
+
if draw_mode == 0 or draw_mode == 2:
|
| 77 |
+
for start_idx, end_idx in FACEMESH_TESSELATION:
|
| 78 |
+
if start_idx < num_lms and end_idx < num_lms:
|
| 79 |
+
lm1 = face_lms[start_idx]
|
| 80 |
+
lm2 = face_lms[end_idx]
|
| 81 |
+
pt1 = (int(lm1.x * img_w), int(lm1.y * img_h))
|
| 82 |
+
pt2 = (int(lm2.x * img_w), int(lm2.y * img_h))
|
| 83 |
+
cv2.line(img, pt1, pt2, (224, 224, 0), 1) # 青綠色線條
|
| 84 |
+
|
| 85 |
+
# 預設執行代碼 (填寫完後學生會將此替換或解除上方註解)
|
| 86 |
+
if results.face_landmarks:
|
| 87 |
+
face_lms = results.face_landmarks[0]
|
| 88 |
+
num_lms = len(face_lms)
|
| 89 |
+
|
| 90 |
+
if draw_mode == 0 or draw_mode == 2:
|
| 91 |
+
for start_idx, end_idx in FACEMESH_TESSELATION:
|
| 92 |
+
if start_idx < num_lms and end_idx < num_lms:
|
| 93 |
+
lm1 = face_lms[start_idx]
|
| 94 |
+
lm2 = face_lms[end_idx]
|
| 95 |
+
pt1 = (int(lm1.x * img_w), int(lm1.y * img_h))
|
| 96 |
+
pt2 = (int(lm2.x * img_w), int(lm2.y * img_h))
|
| 97 |
+
cv2.line(img, pt1, pt2, (224, 224, 0), 1)
|
| 98 |
+
|
| 99 |
+
# 【TODO 3】畫出臉部 468 個黃色特徵點點
|
| 100 |
+
if draw_mode == 1 or draw_mode == 2:
|
| 101 |
+
for lm in face_lms:
|
| 102 |
+
cx = int(lm.x * img_w)
|
| 103 |
+
cy = int(lm.y * img_h)
|
| 104 |
+
cv2.circle(img, (cx, cy), 1, (0, 255, 255), -1) # 黃色小圓點
|
| 105 |
+
|
| 106 |
+
if draw_mode == 1 or draw_mode == 2:
|
| 107 |
+
for lm in face_lms:
|
| 108 |
+
cx = int(lm.x * img_w)
|
| 109 |
+
cy = int(lm.y * img_h)
|
| 110 |
+
cv2.circle(img, (cx, cy), 1, (0, 255, 255), -1)
|
| 111 |
+
|
| 112 |
+
mode_str = "Mesh" if draw_mode == 0 else "Dots" if draw_mode == 1 else "Both"
|
| 113 |
+
cv2.putText(img, f"Face Detected | Mode: {mode_str}", (10, 30), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0, 255, 0), 2)
|
| 114 |
+
else:
|
| 115 |
+
cv2.putText(img, "No Face Detected", (10, 30), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0, 0, 255), 2)
|
| 116 |
+
|
| 117 |
+
# 顯示操作提示文字
|
| 118 |
+
cv2.putText(img, "Press 'm' to toggle mode | 'q' to quit", (10, img_h - 20), cv2.FONT_HERSHEY_SIMPLEX, 0.6, (255, 255, 255), 1)
|
| 119 |
+
|
| 120 |
+
cv2.imshow("Face Mesh Connections", img)
|
| 121 |
+
|
| 122 |
+
key = cv2.waitKey(1) & 0xFF
|
| 123 |
+
if key == ord('q'):
|
| 124 |
+
break
|
| 125 |
+
elif key == ord('m'):
|
| 126 |
+
draw_mode = (draw_mode + 1) % 3
|
| 127 |
+
|
| 128 |
+
cap.release()
|
| 129 |
+
cv2.destroyAllWindows()
|
projects/DEMO/展示用學生/03_手指空中畫畫/draw_game_student.py
ADDED
|
@@ -0,0 +1,168 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import os
|
| 2 |
+
import cv2
|
| 3 |
+
import numpy as np
|
| 4 |
+
import mediapipe as mp
|
| 5 |
+
from mediapipe.tasks import python
|
| 6 |
+
from mediapipe.tasks.python import vision
|
| 7 |
+
|
| 8 |
+
# ============================================
|
| 9 |
+
# Week 9 - 簡單學生版:支援多色切換的 AR 空中寫字板 (純黑畫布版)
|
| 10 |
+
# ============================================
|
| 11 |
+
|
| 12 |
+
# === 【重要觀念 TODO 1:MediaPipe 手部偵測模型初始化】 ===
|
| 13 |
+
# 觀念重點:
|
| 14 |
+
# 1. BaseOptions 用於設定模型檔案的路徑,這裡使用下載好的 'hand_landmarker.task'。
|
| 15 |
+
# 2. HandLandmarkerOptions 用於設定手部偵測的細項設定,例如 num_hands=1 代表最多偵測一隻手。
|
| 16 |
+
# 3. 最後透過 vision.HandLandmarker.create_from_options 建立手部偵測器物件。
|
| 17 |
+
# 請解開下方三行程式碼的註釋:
|
| 18 |
+
base_options = python.BaseOptions(model_asset_path=os.path.join(os.path.dirname(os.path.abspath(__file__)), 'hand_landmarker.task'))
|
| 19 |
+
options = vision.HandLandmarkerOptions(base_options=base_options, num_hands=1)
|
| 20 |
+
detector = vision.HandLandmarker.create_from_options(options)
|
| 21 |
+
|
| 22 |
+
cap = cv2.VideoCapture(0)
|
| 23 |
+
canvas = None
|
| 24 |
+
px, py = 0, 0
|
| 25 |
+
|
| 26 |
+
# 定義幾種高對比的顏色 (格式為 B, G, R)
|
| 27 |
+
COLORS = {
|
| 28 |
+
'red': (0, 0, 255),
|
| 29 |
+
'orange': (0, 165, 255),
|
| 30 |
+
'yellow': (0, 255, 255),
|
| 31 |
+
'green': (0, 255, 0),
|
| 32 |
+
'cyan': (255, 255, 0),
|
| 33 |
+
'dark_blue': (255, 0, 0),
|
| 34 |
+
'light_purple': (164, 97, 237),
|
| 35 |
+
'purple': (63, 0, 127),
|
| 36 |
+
'magenta': (255, 0, 255),
|
| 37 |
+
}
|
| 38 |
+
|
| 39 |
+
# 預設使用螢光綠
|
| 40 |
+
current_color = COLORS['green']
|
| 41 |
+
|
| 42 |
+
print("系統啟動中...")
|
| 43 |
+
print("【操作指南】")
|
| 44 |
+
print("☝️ 單伸食指:下筆寫字")
|
| 45 |
+
print("✌️ 食指+中指:懸停移動 (不寫字)")
|
| 46 |
+
print("⌨️ 按下 '1~9':切換畫筆顏色")
|
| 47 |
+
print("⌨️ 按下 'e':清空畫面")
|
| 48 |
+
print("⌨️ 按下 'q':結束程式")
|
| 49 |
+
|
| 50 |
+
while True:
|
| 51 |
+
success, img = cap.read()
|
| 52 |
+
if not success:
|
| 53 |
+
break
|
| 54 |
+
|
| 55 |
+
# === 【重要觀念 TODO 2:攝影機影像水平翻轉】 ===
|
| 56 |
+
# 觀念重點:
|
| 57 |
+
# 1. 攝影機預設取得的影像與我們的真實動作是相反的(非鏡像)。
|
| 58 |
+
# 2. 在互動式 AR 應用中,為了讓使用者的左手移動時,畫面中的手也往左移動,
|
| 59 |
+
# 必須使用 cv2.flip(img, 1) 將影像水平翻轉,以提供符合直覺的「鏡像體驗」。
|
| 60 |
+
# 請解開下方一行程式碼的註釋:
|
| 61 |
+
img = cv2.flip(img, 1)
|
| 62 |
+
|
| 63 |
+
h, w, c = img.shape
|
| 64 |
+
|
| 65 |
+
# 初始化畫布
|
| 66 |
+
if canvas is None:
|
| 67 |
+
canvas = np.zeros((h, w, c), dtype=np.uint8)
|
| 68 |
+
|
| 69 |
+
# 為了維持畫筆提示點(或懸停圓圈)的動態顯示,且不破壞原本的 canvas 紀錄,
|
| 70 |
+
# 我們在每一訊框都建立一個純黑的暫時畫布來呈現最終畫面。
|
| 71 |
+
display_img = np.zeros((h, w, c), dtype=np.uint8)
|
| 72 |
+
|
| 73 |
+
img_rgb = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
|
| 74 |
+
mp_image = mp.Image(image_format=mp.ImageFormat.SRGB, data=img_rgb)
|
| 75 |
+
results = detector.detect(mp_image)
|
| 76 |
+
|
| 77 |
+
if results.hand_landmarks:
|
| 78 |
+
hand_lms = results.hand_landmarks[0]
|
| 79 |
+
|
| 80 |
+
index_tip = hand_lms[8]
|
| 81 |
+
index_pip = hand_lms[6]
|
| 82 |
+
middle_tip = hand_lms[12]
|
| 83 |
+
middle_pip = hand_lms[10]
|
| 84 |
+
|
| 85 |
+
cx, cy = int(index_tip.x * w), int(index_tip.y * h)
|
| 86 |
+
|
| 87 |
+
index_up = index_tip.y < index_pip.y
|
| 88 |
+
middle_up = middle_tip.y < middle_pip.y
|
| 89 |
+
|
| 90 |
+
if index_up and not middle_up:
|
| 91 |
+
# 狀態:【繪圖模式】
|
| 92 |
+
if px == 0 and py == 0:
|
| 93 |
+
px, py = cx, cy
|
| 94 |
+
|
| 95 |
+
# === 【重要觀念 TODO 3:在 OpenCV Canvas 上繪製軌跡】 ===
|
| 96 |
+
# 觀念重點:
|
| 97 |
+
# 1. cv2.line 用於在影像上繪製直線。
|
| 98 |
+
# 2. 參數說明:畫布(canvas)、起點(px, py)、終點(cx, cy)、顏色(current_color)、線寬(8)。
|
| 99 |
+
# 3. 藉由在每一訊框連續繪製前一個位置到當前位置的線段,即可形成平滑的寫字軌跡。
|
| 100 |
+
# 請解開下方一行程式碼的註釋:
|
| 101 |
+
cv2.line(canvas, (px, py), (cx, cy), current_color, 8)
|
| 102 |
+
|
| 103 |
+
px, py = cx, cy
|
| 104 |
+
|
| 105 |
+
# 筆頭提示點直接畫在暫時的顯示畫布上,避免在黑畫布上找不到筆尖
|
| 106 |
+
cv2.circle(display_img, (cx, cy), 10, current_color, cv2.FILLED)
|
| 107 |
+
|
| 108 |
+
elif index_up and middle_up:
|
| 109 |
+
# 狀態:【懸停模式】
|
| 110 |
+
px, py = 0, 0
|
| 111 |
+
# 懸停提示圈畫在暫時的顯示畫布上,移動時才不會在黑背景中迷路
|
| 112 |
+
cv2.circle(display_img, (cx, cy), 12, (255, 255, 255), 2)
|
| 113 |
+
|
| 114 |
+
else:
|
| 115 |
+
px, py = 0, 0
|
| 116 |
+
else:
|
| 117 |
+
px, py = 0, 0
|
| 118 |
+
|
| 119 |
+
# 為什麼改用 canvas 與 display_img 疊加?
|
| 120 |
+
# 原本是與相機影像 (img) 疊加,現在改與純黑的 display_img 疊加,
|
| 121 |
+
# 這樣畫面上就只會留下寫字的軌跡以及當前的筆頭/懸停提示,完全遮蔽人像背景。
|
| 122 |
+
|
| 123 |
+
# === 【重要觀念 TODO 4:AR 畫面疊加與顯示】 ===
|
| 124 |
+
# 觀念重點:
|
| 125 |
+
# 1. cv2.add 用於將兩個相同大小與類型的影像矩陣進行相加。
|
| 126 |
+
# 2. 這裡將寫字痕跡畫布 (canvas) 與當前的筆頭/懸停提示畫布 (display_img) 疊加。
|
| 127 |
+
# 3. 疊加後使用 cv2.imshow 在視窗中顯示最終成果。
|
| 128 |
+
# 請解開下方兩行程式碼的註釋:
|
| 129 |
+
final_output = cv2.add(canvas, display_img)
|
| 130 |
+
cv2.imshow("Hand Tracking - AR Canvas", final_output)
|
| 131 |
+
|
| 132 |
+
# 鍵盤監聽與控制邏輯
|
| 133 |
+
key = cv2.waitKey(1) & 0xFF
|
| 134 |
+
if key == ord('q'):
|
| 135 |
+
break
|
| 136 |
+
elif key == ord('e'):
|
| 137 |
+
canvas = np.zeros((h, w, c), dtype=np.uint8)
|
| 138 |
+
|
| 139 |
+
elif key == ord('1'):
|
| 140 |
+
current_color = COLORS['red']
|
| 141 |
+
print("已切換為:红色")
|
| 142 |
+
elif key == ord('2'):
|
| 143 |
+
current_color = COLORS['orange']
|
| 144 |
+
print("已切換為:橘色")
|
| 145 |
+
elif key == ord('3'):
|
| 146 |
+
current_color = COLORS['yellow']
|
| 147 |
+
print("已切換為:黃色")
|
| 148 |
+
elif key == ord('4'):
|
| 149 |
+
current_color = COLORS['green']
|
| 150 |
+
print("已切換為:綠色")
|
| 151 |
+
elif key == ord('5'):
|
| 152 |
+
current_color = COLORS['cyan']
|
| 153 |
+
print("已切換為:青色")
|
| 154 |
+
elif key == ord('6'):
|
| 155 |
+
current_color = COLORS['dark_blue']
|
| 156 |
+
print("已切換為:深蓝色")
|
| 157 |
+
elif key == ord('7'):
|
| 158 |
+
current_color = COLORS['light_purple']
|
| 159 |
+
print("已切換為:浅紫色")
|
| 160 |
+
elif key == ord('8'):
|
| 161 |
+
current_color = COLORS['purple']
|
| 162 |
+
print("已切換為:紫色")
|
| 163 |
+
elif key == ord('9'):
|
| 164 |
+
current_color = COLORS['magenta']
|
| 165 |
+
print("已切換為:粉色")
|
| 166 |
+
|
| 167 |
+
cap.release()
|
| 168 |
+
cv2.destroyAllWindows()
|
projects/DEMO/展示用學生/03_手指空中畫畫/main.py
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# -*- coding: utf-8 -*-
|
| 2 |
+
"""
|
| 3 |
+
Telearn Arcade Hub 入口檔(自動產生)
|
| 4 |
+
實際執行: draw_game_student.py
|
| 5 |
+
規格: projects/{班級}/{學生}/{專案}/main.py
|
| 6 |
+
"""
|
| 7 |
+
from pathlib import Path
|
| 8 |
+
import os
|
| 9 |
+
import runpy
|
| 10 |
+
import sys
|
| 11 |
+
|
| 12 |
+
_ROOT = Path(__file__).resolve().parent
|
| 13 |
+
os.chdir(_ROOT)
|
| 14 |
+
if str(_ROOT) not in sys.path:
|
| 15 |
+
sys.path.insert(0, str(_ROOT))
|
| 16 |
+
|
| 17 |
+
# 執行原始學員腳本
|
| 18 |
+
runpy.run_path(str(_ROOT / 'draw_game_student.py'), run_name="__main__")
|
projects/DEMO/展示用學生/04_Fire迷宮/link.txt
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
https://makecode.com/_6TDiueTVV3vV
|
projects/DEMO/展示用學生/展示備忘.md
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# 歷屆作品展示備忘
|
| 2 |
+
|
| 3 |
+
此展示學生是歷屆作品的策展入口,收錄多件作品,不代表全部由同一位學生獨立完成。原有作品檔案保持不動。
|
| 4 |
+
|
| 5 |
+
## 展示順序
|
| 6 |
+
|
| 7 |
+
1. `01_手勢按鈕挑戰`:MediaPipe 手部追蹤與三指捏合遊戲。
|
| 8 |
+
2. `02_臉部3D網格`:MediaPipe 臉部網格/特徵點視覺化。
|
| 9 |
+
3. `03_手指空中畫畫`:以手指追蹤繪圖。
|
| 10 |
+
4. `04_Fire迷宮`:MakeCode Arcade 外部作品。
|
| 11 |
+
|
| 12 |
+
## 展示前檢查
|
| 13 |
+
|
| 14 |
+
- 前三項需要瀏覽器 Webcam。播放器會把鏡頭影格透過 WebSocket 傳到 HF 執行環境處理;請先告知體驗者並取得同意,不要在未告知的情況下讓學生或家長入鏡。
|
| 15 |
+
- 這些作品尚未在目前 HF Space 上逐一完成實機排練。請先檢查鏡頭權限、模型載入、畫面反應及停止後鏡頭是否關閉。
|
| 16 |
+
- `hand_landmarker.task` 與 `face_landmarker.task` 由部署端的共用 `models/` 提供,執行器會連結到專案;若移動或另行打包作品,需一併保留模型來源。
|
| 17 |
+
- MakeCode 作品會在外部 MakeCode Arcade 開啟;操作控制尚未逐項驗證。
|
| 18 |
+
- 本資料夾是原作品的展示副本,沒有改寫原班檔案,也沒有推送部署。
|