- MediaPipe 能即時追蹤每隻手的 21 個骨架特徵點(x, y, z 座標),這些座標就是手語辨識的最佳特徵輸入
- 將 MediaPipe 的手部座標餵給簡單的分類器(Random Forest 或小型神經網路),就能達到 95%+ 的 ASL 字母辨識準確率
- 整個流程可在 Google Colab 上用靜態圖片完成——不需要 webcam、不需要 GPU
- 2026 年的前沿:Google 的 SignGemma 已能做到句子級 ASL 即時翻譯,Transformer 架構在連續手語辨識上表現更優
一、手語翻譯為什麼這麼難?
在正式開箱之前,先聊聊為什麼手語翻譯是電腦視覺中的一個硬題目。
手語不只是「用手比出字母」這麼簡單。完整的手語包含手形(hand shape)、手的位置(location)、手的移動方向(movement)、手掌朝向(palm orientation),還有臉部表情和身體姿勢[3]。這五個面向同時在傳達訊息,任何一個改變都可能改變意思。
所以,「手語翻譯」其實是一個光譜:
- 最簡單:靜態字母辨識(fingerspelling)——一張圖對應一個字母,跟圖像分類沒什麼兩樣
- 中等:單詞手語辨識——需要處理手勢的動態軌跡
- 最難:連續手語翻譯——需要理解語法結構、上下文、表情
今天我們要做的是最簡單的那一層:ASL(美式手語)字母辨識。但別小看它——這是整個手語翻譯 pipeline 的第一塊磚,而且做起來其實很有成就感。
二、核心思路:MediaPipe 手部追蹤 + 分類器
我們的做法分兩步:
- MediaPipe 手部追蹤[1]:從圖片中偵測手部,萃取 21 個骨架特徵點的座標(共 21 × 3 = 63 個數值)
- 分類器:把這 63 個數值餵給 Random Forest 或神經網路,讓它學會「哪種座標組合對應哪個字母」
為什麼不直接用 CNN 看圖片?因為 MediaPipe 已經幫你把「手長什麼樣」抽象成了 21 個點的座標[8],這些座標對光線、膚色、背景的變化都有一定的魯棒性。相比讓 CNN 自己從像素學特徵,先用 MediaPipe 萃取骨架再分類,通常更穩定、訓練也更快。
三、環境準備:Google Colab 設置
打開 Google Colab,新建 Notebook。這次 CPU 就夠了——MediaPipe 的推論在 CPU 上就能跑到即時速度,分類器訓練也只需要幾秒。
3.1 安裝 MediaPipe
# 安裝 MediaPipe 和必要套件
!pip install mediapipe -q
# 下載 MediaPipe 手部追蹤模型
import urllib.request
import os
os.makedirs("models", exist_ok=True)
model_url = "https://storage.googleapis.com/mediapipe-models/hand_landmarker/hand_landmarker/float16/latest/hand_landmarker.task"
model_path = "models/hand_landmarker.task"
if not os.path.exists(model_path):
urllib.request.urlretrieve(model_url, model_path)
print(f"模型下載完成: {model_path}")
import mediapipe as mp
from mediapipe.tasks import python
from mediapipe.tasks.python import vision
import numpy as np
import matplotlib.pyplot as plt
import cv2
print(f"MediaPipe version: {mp.__version__}")
3.2 準備 ASL 字母資料集
我們使用 Sign Language MNIST 資料集[10]——它是 MNIST 的手語版,包含 24 個 ASL 靜態字母(排除需要動態的 J 和 Z),每張圖 28×28 像素。
# 下載 Sign Language MNIST 資料集
# 如果你有 Kaggle API key,可以直接下載
# 這裡我們用替代方式
import pandas as pd
# 從 Kaggle 公開 URL 下載(或手動上傳)
# 方法一:使用 kagglehub
!pip install kagglehub -q
import kagglehub
path = kagglehub.dataset_download("datamunge/sign-language-mnist")
print(f"資料集路徑: {path}")
# 載入 CSV
train_df = pd.read_csv(os.path.join(path, "sign_mnist_train.csv"))
test_df = pd.read_csv(os.path.join(path, "sign_mnist_test.csv"))
print(f"訓練集: {train_df.shape}")
print(f"測試集: {test_df.shape}")
# 拆分標籤和像素
y_train = train_df["label"].values
x_train = train_df.drop("label", axis=1).values.reshape(-1, 28, 28).astype(np.uint8)
y_test = test_df["label"].values
x_test = test_df.drop("label", axis=1).values.reshape(-1, 28, 28).astype(np.uint8)
# 標籤對照:0=A, 1=B, ..., 24=Y(跳過 9=J)
label_map = {i: chr(65 + i + (1 if i >= 9 else 0)) for i in range(25)}
print(f"標籤對照: {label_map}")
# 看看資料長什麼樣
fig, axes = plt.subplots(2, 8, figsize=(16, 4))
for i, ax in enumerate(axes.flat):
idx = np.random.choice(np.where(y_train == i)[0])
ax.imshow(x_train[idx], cmap="gray")
ax.set_title(f"{label_map[i]}", fontsize=14, fontweight="bold")
ax.axis("off")
plt.suptitle("ASL Alphabet Samples", fontsize=16)
plt.tight_layout()
plt.show()
四、Step 1:用 MediaPipe 萃取手部特徵點
這是整個流程的關鍵步驟——把每張圖片轉成 21 個特徵點的座標向量。
# ★ 核心:用 MediaPipe HandLandmarker 萃取手部骨架座標 ★
# 建立 HandLandmarker
base_options = python.BaseOptions(model_asset_path="models/hand_landmarker.task")
options = vision.HandLandmarkerOptions(
base_options=base_options,
num_hands=1, # ASL 字母通常只用一隻手
min_hand_detection_confidence=0.3, # 降低閾值,因為圖片較小
min_tracking_confidence=0.3
)
detector = vision.HandLandmarker.create_from_options(options)
def extract_landmarks(image_gray):
"""
從 28x28 灰階圖萃取 21 個手部特徵點。
回傳 63 維向量(21 點 × 3 座標),若偵測失敗回傳 None。
"""
# 放大圖片(28x28 太小,MediaPipe 需要更大的輸入)
img_resized = cv2.resize(image_gray, (256, 256), interpolation=cv2.INTER_CUBIC)
# 轉成 RGB 3 通道
img_rgb = cv2.cvtColor(img_resized, cv2.COLOR_GRAY2RGB)
# 建立 MediaPipe Image
mp_image = mp.Image(image_format=mp.ImageFormat.SRGB, data=img_rgb)
# 偵測
result = detector.detect(mp_image)
if not result.hand_landmarks:
return None
# 取第一隻手的 21 個特徵點
landmarks = result.hand_landmarks[0]
coords = []
for lm in landmarks:
coords.extend([lm.x, lm.y, lm.z])
return np.array(coords, dtype=np.float32)
# 測試一張圖
test_lm = extract_landmarks(x_train[0])
if test_lm is not None:
print(f"特徵向量維度: {test_lm.shape}")
print(f"前 6 個值 (x,y,z × 2 點): {test_lm[:6]}")
else:
print("這張圖偵測不到手(正常,不是每張都能偵測到)")
4.1 批量萃取特徵
Sign Language MNIST 的圖片只有 28×28 像素,很小。即使放大到 256×256,MediaPipe 也不是每張都能成功偵測到手。這是正常的——我們只用成功偵測的樣本來訓練。
# ★ 批量萃取訓練集和測試集的特徵 ★
from tqdm import tqdm
def batch_extract(images, labels, desc="Processing"):
"""批量萃取特徵點,回傳特徵矩陣和對應標籤。"""
features = []
valid_labels = []
failed = 0
for i in tqdm(range(len(images)), desc=desc):
lm = extract_landmarks(images[i])
if lm is not None:
features.append(lm)
valid_labels.append(labels[i])
else:
failed += 1
print(f" 成功: {len(features)}, 失敗: {failed}, 成功率: {len(features)/len(images)*100:.1f}%")
return np.array(features), np.array(valid_labels)
# 為了節省時間,先用子集測試(你可以改成全量)
TRAIN_SIZE = 5000 # 全量: len(x_train)
TEST_SIZE = 1000 # 全量: len(x_test)
# 隨機取樣
train_idx = np.random.choice(len(x_train), TRAIN_SIZE, replace=False)
test_idx = np.random.choice(len(x_test), TEST_SIZE, replace=False)
print("萃取訓練集特徵...")
X_train_lm, y_train_lm = batch_extract(x_train[train_idx], y_train[train_idx], "Train")
print("\n萃取測試集特徵...")
X_test_lm, y_test_lm = batch_extract(x_test[test_idx], y_test[test_idx], "Test")
print(f"\n訓練特徵矩陣: {X_train_lm.shape}")
print(f"測試特徵矩陣: {X_test_lm.shape}")
五、Step 2:訓練分類器
手部特徵點萃取完了,接下來把它餵給分類器。我們用兩種方法:一個簡單的 Random Forest,一個稍微進階的神經網路。
5.1 方法 A:Random Forest(快又穩)
# ★ Random Forest 分類器——2 秒就訓練完 ★
from sklearn.ensemble import RandomForestClassifier
from sklearn.metrics import classification_report, accuracy_score
rf = RandomForestClassifier(
n_estimators=200,
max_depth=30,
random_state=42,
n_jobs=-1 # 用所有 CPU 核心
)
rf.fit(X_train_lm, y_train_lm)
y_pred_rf = rf.predict(X_test_lm)
acc_rf = accuracy_score(y_test_lm, y_pred_rf)
print(f"Random Forest 準確率: {acc_rf:.4f}")
print(f"\n分類報告:")
print(classification_report(
y_test_lm, y_pred_rf,
target_names=[label_map[i] for i in sorted(set(y_test_lm))],
zero_division=0
))
5.2 方法 B:小型神經網路(更高精度)
# ★ 小型 MLP 分類器——精度更高 ★
import tensorflow as tf
from tensorflow import keras
num_classes = len(set(y_train_lm))
model = keras.Sequential([
keras.layers.Input(shape=(63,)), # 21 點 × 3 座標
keras.layers.Dense(256, activation="relu"),
keras.layers.BatchNormalization(),
keras.layers.Dropout(0.3),
keras.layers.Dense(128, activation="relu"),
keras.layers.BatchNormalization(),
keras.layers.Dropout(0.3),
keras.layers.Dense(64, activation="relu"),
keras.layers.Dense(num_classes, activation="softmax")
])
model.compile(
optimizer="adam",
loss="sparse_categorical_crossentropy",
metrics=["accuracy"]
)
model.summary()
# 訓練
history = model.fit(
X_train_lm, y_train_lm,
epochs=50,
batch_size=32,
validation_split=0.15,
verbose=1
)
# 評估
loss, acc_mlp = model.evaluate(X_test_lm, y_test_lm)
print(f"\nMLP 準確率: {acc_mlp:.4f}")
# 視覺化訓練過程
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(12, 4))
ax1.plot(history.history["accuracy"], label="訓練")
ax1.plot(history.history["val_accuracy"], label="驗證")
ax1.set_title("Accuracy")
ax1.legend()
ax1.grid(True, alpha=0.3)
ax2.plot(history.history["loss"], label="訓練")
ax2.plot(history.history["val_loss"], label="驗證")
ax2.set_title("Loss")
ax2.legend()
ax2.grid(True, alpha=0.3)
plt.suptitle("MLP Training History")
plt.tight_layout()
plt.show()
六、Step 3:視覺化預測結果
# ★ 隨機抽樣看預測結果 ★
n_show = 12
indices = np.random.choice(len(X_test_lm), n_show, replace=False)
# 用 MLP 預測
preds = model.predict(X_test_lm[indices], verbose=0)
pred_labels = np.argmax(preds, axis=1)
fig, axes = plt.subplots(2, 6, figsize=(18, 6))
for i, ax in enumerate(axes.flat):
true_label = label_map[y_test_lm[indices[i]]]
pred_label = label_map[pred_labels[i]]
confidence = np.max(preds[i]) * 100
# 找到原始圖片索引
orig_idx = test_idx[np.where(y_test_lm == y_test_lm[indices[i]])[0][0]]
ax.imshow(x_test[orig_idx], cmap="gray")
color = "green" if true_label == pred_label else "red"
ax.set_title(f"預測: {pred_label} ({confidence:.0f}%)\n實際: {true_label}", color=color, fontsize=11)
ax.axis("off")
plt.suptitle("ASL 字母辨識結果", fontsize=14, fontweight="bold")
plt.tight_layout()
plt.show()
七、進階:視覺化 MediaPipe 手部骨架
來看看 MediaPipe 到底在每張圖上偵測到了什麼:
# ★ 視覺化手部骨架特徵點 ★
from mediapipe import solutions
def draw_hand_landmarks(image_gray):
"""在圖片上繪製 MediaPipe 偵測到的手部骨架。"""
img_resized = cv2.resize(image_gray, (256, 256), interpolation=cv2.INTER_CUBIC)
img_rgb = cv2.cvtColor(img_resized, cv2.COLOR_GRAY2RGB)
mp_image = mp.Image(image_format=mp.ImageFormat.SRGB, data=img_rgb)
result = detector.detect(mp_image)
annotated = img_rgb.copy()
if result.hand_landmarks:
for hand_lm in result.hand_landmarks:
# 手動繪製特徵點
h, w = annotated.shape[:2]
for lm in hand_lm:
cx, cy = int(lm.x * w), int(lm.y * h)
cv2.circle(annotated, (cx, cy), 4, (0, 255, 0), -1)
# 繪製骨架連線
connections = solutions.hands.HAND_CONNECTIONS
for conn in connections:
start = hand_lm[conn[0]]
end = hand_lm[conn[1]]
sx, sy = int(start.x * w), int(start.y * h)
ex, ey = int(end.x * w), int(end.y * h)
cv2.line(annotated, (sx, sy), (ex, ey), (255, 200, 0), 2)
return annotated
# 繪製幾個字母的手部骨架
fig, axes = plt.subplots(2, 6, figsize=(18, 6))
sample_labels = [0, 1, 2, 3, 4, 5, 7, 11, 14, 17, 20, 24] # A,B,C,D,E,F,H,L,O,R,U,Y
for i, ax in enumerate(axes.flat):
if i < len(sample_labels):
lbl = sample_labels[i]
idx = np.random.choice(np.where(y_train == lbl)[0])
annotated = draw_hand_landmarks(x_train[idx])
ax.imshow(annotated)
ax.set_title(f"ASL: {label_map[lbl]}", fontsize=13, fontweight="bold")
ax.axis("off")
plt.suptitle("MediaPipe 手部骨架特徵點", fontsize=14, fontweight="bold")
plt.tight_layout()
plt.show()
你會看到 MediaPipe 在每張手部圖片上標出了 21 個綠色點(指尖、指節、手腕),並用黃色線段連接成骨架。這些座標就是我們餵給分類器的特徵——比原始像素更抽象、更有意義。
八、替代方法:直接用 CNN(不經過 MediaPipe)
如果你想省掉 MediaPipe 這一步,直接讓 CNN 從圖片學習也是完全可行的:
# ★ 純 CNN 方法——直接學像素特徵 ★
x_train_cnn = x_train[..., None].astype("float32") / 255.0
x_test_cnn = x_test[..., None].astype("float32") / 255.0
num_classes_all = len(set(y_train))
cnn_model = keras.Sequential([
keras.layers.Input(shape=(28, 28, 1)),
keras.layers.Conv2D(32, 3, activation="relu", padding="same"),
keras.layers.MaxPooling2D(2),
keras.layers.Conv2D(64, 3, activation="relu", padding="same"),
keras.layers.MaxPooling2D(2),
keras.layers.Conv2D(128, 3, activation="relu", padding="same"),
keras.layers.GlobalAveragePooling2D(),
keras.layers.Dense(128, activation="relu"),
keras.layers.Dropout(0.4),
keras.layers.Dense(num_classes_all, activation="softmax")
])
cnn_model.compile(optimizer="adam", loss="sparse_categorical_crossentropy", metrics=["accuracy"])
cnn_model.fit(x_train_cnn, y_train, epochs=15, batch_size=64, validation_split=0.1, verbose=1)
loss, acc_cnn = cnn_model.evaluate(x_test_cnn, y_test)
print(f"\n純 CNN 準確率: {acc_cnn:.4f}")
純 CNN 在 Sign Language MNIST 上通常能達到 92–97% 的準確率。跟 MediaPipe + 分類器的方法比起來,各有優缺:
| 方法 | 優點 | 缺點 |
|---|---|---|
| MediaPipe + 分類器 | 對背景、光線變化更魯棒;特徵可解釋 | 依賴 MediaPipe 偵測成功率;小圖偵測率低 |
| 純 CNN | 端到端訓練,不依賴外部偵測器 | 對背景敏感;需要更多資料做泛化 |
在真實世界的應用中,MediaPipe 路線通常更實用——因為你的輸入不會是 28×28 的小灰階圖,而是 webcam 或手機相機的高解析度影像,MediaPipe 在這種場景下的偵測率非常高。
九、2026 年的當下,有更好的選擇嗎?
9.1 Google SignGemma:句子級手語翻譯
如果說我們今天做的是「字母級」辨識,Google 的 SignGemma[6] 則是直接跳到了「句子級」翻譯。它使用 Vision Transformer 分析手形、臉部表情和動態軌跡,再透過 Gemini Nano 語言模型生成英文翻譯。
SignGemma 用超過 10,000 小時的 ASL 影片訓練,能做到即時、端到端的手語→英文翻譯。目前以 ASL 為主,多語言支援正在開發中。這是 2026 年最前沿的手語翻譯方案,但目前還在 preview 階段,且只支援 ASL。
9.2 Transformer 架構:連續手語辨識的主流
如果你想做比字母更進階的手語辨識(例如單詞或短句),Transformer[5] 已經取代 LSTM 成為主流架構[7]。最新的研究顯示,Transformer + CNN 混合架構在連續手語辨識上能達到 89.3% 的準確率,顯著優於傳統的 CNN + LSTM(82.5%)。
# Transformer 架構的概念示意(非完整訓練碼,但展示核心結構)
import tensorflow as tf
from tensorflow import keras
def build_sign_transformer(seq_length=30, num_landmarks=63, num_classes=24):
"""
用 Transformer 做時序手語辨識。
輸入:連續 30 幀的手部骨架座標(30 × 63)
輸出:手語單詞分類
"""
inputs = keras.layers.Input(shape=(seq_length, num_landmarks))
# 位置編碼
x = keras.layers.Dense(128)(inputs)
positions = tf.range(start=0, limit=seq_length, delta=1)
pos_embedding = keras.layers.Embedding(seq_length, 128)(positions)
x = x + pos_embedding
# Transformer Encoder Blocks
for _ in range(3):
# Multi-Head Self-Attention
attn = keras.layers.MultiHeadAttention(num_heads=4, key_dim=32)(x, x)
attn = keras.layers.Dropout(0.1)(attn)
x = keras.layers.LayerNormalization()(x + attn)
# Feed-Forward
ff = keras.layers.Dense(256, activation="relu")(x)
ff = keras.layers.Dense(128)(ff)
ff = keras.layers.Dropout(0.1)(ff)
x = keras.layers.LayerNormalization()(x + ff)
# 分類頭
x = keras.layers.GlobalAveragePooling1D()(x)
x = keras.layers.Dense(64, activation="relu")(x)
outputs = keras.layers.Dense(num_classes, activation="softmax")(x)
return keras.Model(inputs, outputs)
model_transformer = build_sign_transformer()
model_transformer.summary()
print("\nTransformer 參數量:", f"{model_transformer.count_params():,}")
這個 Transformer 模型接受的輸入是連續多幀的手部座標序列(例如 30 幀),所以它能捕捉手勢的動態變化——這是靜態分類器做不到的。如果你要辨識 J、Z 這種需要手部移動的字母,就必須走這條路。
9.3 工具選擇總覽
| 方案 | 適合場景 | 輸入 | 精度 | 複雜度 |
|---|---|---|---|---|
| MediaPipe + RF/MLP | 靜態字母辨識、教學 | 單張圖片 | 95%+ | 低 |
| 純 CNN | 圖片分類、快速原型 | 單張圖片 | 92–97% | 低 |
| MediaPipe + Transformer | 動態手勢、單詞辨識 | 影片序列 | 89%+ | 中 |
| Google SignGemma | 句子級即時翻譯 | 即時影片 | 最高 | 高(API) |
十、完整程式碼(一鍵複製版)
# === MediaPipe ASL 手語字母辨識 完整版 ===
# 環境:Google Colab(CPU 即可)
# Step 1: 安裝
!pip install mediapipe kagglehub -q
# Step 2: 匯入
import mediapipe as mp
from mediapipe.tasks import python
from mediapipe.tasks.python import vision
import numpy as np
import pandas as pd
import os, urllib.request
# Step 3: 下載模型
os.makedirs("models", exist_ok=True)
urllib.request.urlretrieve(
"https://storage.googleapis.com/mediapipe-models/hand_landmarker/hand_landmarker/float16/latest/hand_landmarker.task",
"models/hand_landmarker.task"
)
# Step 4: 下載 Sign Language MNIST
import kagglehub
path = kagglehub.dataset_download("datamunge/sign-language-mnist")
train_df = pd.read_csv(os.path.join(path, "sign_mnist_train.csv"))
test_df = pd.read_csv(os.path.join(path, "sign_mnist_test.csv"))
y_train = train_df["label"].values
x_train = train_df.drop("label", axis=1).values.reshape(-1, 28, 28).astype(np.uint8)
y_test = test_df["label"].values
x_test = test_df.drop("label", axis=1).values.reshape(-1, 28, 28).astype(np.uint8)
# Step 5: 建立 HandLandmarker
import cv2
base_options = python.BaseOptions(model_asset_path="models/hand_landmarker.task")
options = vision.HandLandmarkerOptions(
base_options=base_options, num_hands=1,
min_hand_detection_confidence=0.3, min_tracking_confidence=0.3
)
detector = vision.HandLandmarker.create_from_options(options)
def extract_landmarks(img_gray):
img = cv2.resize(img_gray, (256, 256), interpolation=cv2.INTER_CUBIC)
img = cv2.cvtColor(img, cv2.COLOR_GRAY2RGB)
result = detector.detect(mp.Image(image_format=mp.ImageFormat.SRGB, data=img))
if not result.hand_landmarks:
return None
coords = []
for lm in result.hand_landmarks[0]:
coords.extend([lm.x, lm.y, lm.z])
return np.array(coords, dtype=np.float32)
# Step 6: 萃取特徵(用子集加快速度)
from tqdm import tqdm
def batch_extract(images, labels):
feats, labs = [], []
for i in tqdm(range(len(images))):
lm = extract_landmarks(images[i])
if lm is not None:
feats.append(lm)
labs.append(labels[i])
return np.array(feats), np.array(labs)
idx_tr = np.random.choice(len(x_train), 5000, replace=False)
idx_te = np.random.choice(len(x_test), 1000, replace=False)
X_tr, y_tr = batch_extract(x_train[idx_tr], y_train[idx_tr])
X_te, y_te = batch_extract(x_test[idx_te], y_test[idx_te])
# Step 7: 訓練 Random Forest
from sklearn.ensemble import RandomForestClassifier
from sklearn.metrics import accuracy_score
rf = RandomForestClassifier(n_estimators=200, max_depth=30, random_state=42, n_jobs=-1)
rf.fit(X_tr, y_tr)
print(f"準確率: {accuracy_score(y_te, rf.predict(X_te)):.4f}")
十一、常見問題 FAQ
Q:MediaPipe 在 28×28 的小圖上偵測率很低怎麼辦?
這是預期行為。MediaPipe 的手部偵測模型設計給正常解析度的圖片使用(至少 224×224 以上)[1]。我們把圖放大到 256×256 已經是折衷方案。如果你用的是高解析度的手語照片(例如 Kaggle 的 ASL Alphabet 資料集[9],每張 200×200),偵測率會好很多。
Q:為什麼跳過了 J 和 Z?
因為 J 和 Z 在 ASL 中需要手部移動才能表示——J 是手指畫一個弧線,Z 是在空中寫 Z 字形。單張靜態圖片無法捕捉這種動態資訊。要辨識 J 和 Z,你需要影片序列 + 時序模型(如 Transformer 或 LSTM)。
Q:這個方法能用在其他手語系統嗎?例如台灣手語(TSL)?
架構完全通用——MediaPipe 的手部追蹤不分手語系統。你只需要替換訓練資料集。但要注意,台灣手語的公開資料集相對稀少,你可能需要自己收集資料。
Q:可以做成即時 webcam 版嗎?
可以,但不建議在 Colab 上做(webcam 支援有限制)。如果你在本機 Python 環境中,可以用 OpenCV 的 cv2.VideoCapture(0) 抓取 webcam 畫面,每一幀都跑 MediaPipe → 分類器,就能實現即時手語辨識。在一般筆電上跑到 30 FPS 不是問題。
十二、結語:從 21 個點到理解一門語言
今天我們做了一件滿酷的事:用 MediaPipe 把一隻手拆解成 21 個點,然後教一個分類器從這些點的排列方式去辨識 ASL 字母。整個流程不到 100 行核心程式碼,準確率卻能到 95% 以上。
但請記住,這只是手語翻譯漫長旅程的起點[3]。從字母到單詞、從單詞到句子、從句子到自然對話,每一步的複雜度都是指數級增長。好消息是,2026 年的工具已經比幾年前好太多了——Google 的 SignGemma[6] 已經在做句子級翻譯,Transformer 架構讓時序建模不再那麼痛苦[5]。
手語是全球超過 7,000 萬聽障人士的主要溝通方式[4]。讓 AI 能理解手語,不只是一個技術問題——它是一個無障礙問題、一個平等問題。每一個嘗試做手語辨識的人,不管做到什麼程度,都在為這個方向貢獻一份力量。
打開 Colab,把程式碼貼進去,看看你的 AI 能認出幾個字母吧。



