「もう鏡はいらない?カメラとPythonで作るリアルタイム登壇フィードバック」
登壇の練習をしていると、いつも悩むのが
「表情や動きが自分では分からない」という点でした。
自分では笑顔のつもりでも、実際にビデオを見返すと固い表情に見えたり、
逆に身振り手振りが大きすぎて落ち着きがない印象になってしまうこともあります。
「もっと客観的に、自分の練習を数値で見られたらいいのに」
そんな思いから、カメラで自分を撮影し、リアルタイムで「笑顔」と「動き」を数値化してくれる仕組みを作ってみました。
難しいことは避け、できるだけシンプルに。
Python とオープンソースのライブラリ(OpenCV・MediaPipe・NumPy)を使えば、
意外と短いコードで「数値化によるフィードバック」が実現できました。
この記事では、その流れを再現できるように整理しています。
同じように「登壇の練習をもっとデジタルで見える化したい」と考えている方に役立てば幸いです。
ここで紹介する仕組みは 「まずは動かしてみる」段階 のものです。
プレゼン練習を数値化できると「新しい視点」が得られる一方で、実際の表情や体感とのずれも感じました。
そのあたりの課題や学びについては、記事の最後にまとめています。
まずは「こういうアプローチができるんだ」と試す入口として読んでいただければと思います。
🛠 必要ライブラリのインストール(Windows)
前提:Python 3.11 は入っている想定です(PATH もOK)。
以降は コマンドプロンプト(黒い画面)で操作します。
-
コマンドプロンプトを開く
- Windowsキーを押して「
cmd」→ Enter - 画面左に
C:\Users\(あなたの名前)>と出ればOK
- Windowsキーを押して「
-
作業フォルダをデスクトップに作成(分かりやすい場所)
cd %USERPROFILE%\Desktop
mkdir presentation_practice
cd presentation_practice
- 必要ライブラリをインストール(3つだけ)
pip install opencv-python mediapipe numpy
- うまくいけば最後に Successfully installed ... と表示されます
- ネットワークが厳しい環境では、テザリングなどを利用してください
📸 リアルタイムで「笑顔」と「動き」を検出(準備〜実行)
① スクリプトファイルを作る
1.いま開いている コマンドプロンプト で、メモ帳を起動して新規作成:
notepad realtime_feedback.py
「この場所にファイルがありません。作成しますか?」→ はい
2.開いたメモ帳に 下のコードを丸ごとコピペ → Ctrl+S で保存 → メモ帳は閉じてOK
import cv2, mediapipe as mp, numpy as np, csv, time
from collections import deque
from datetime import datetime
mp_face = mp.solutions.face_mesh
mp_pose = mp.solutions.pose
# カメラ解像度はPCに合わせて調整可(重い場合は 640x360 に)
cap = cv2.VideoCapture(0)
cap.set(cv2.CAP_PROP_FRAME_WIDTH, 960)
cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 540)
# 数値のブレを抑える移動平均(直近15フレーム)
smile_buf = deque(maxlen=15)
motion_buf = deque(maxlen=15)
# 録画ON/OFF(rキー)でCSV保存
record = False
csv_f = None
writer = None
def color_for_smile(s):
if np.isnan(s): return (0,255,255) # 未検出=黄
if s >= 0.60: return (0,200,0) # 良好=緑
if s >= 0.40: return (0,180,255) # まずまず=橙
return (0,0,255) # 要改善=赤
with mp_face.FaceMesh(refine_landmarks=True) as face, mp_pose.Pose() as pose:
t0 = time.time()
while True:
ok, frame = cap.read()
if not ok: break
h, w = frame.shape[:2]
rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
# --- Smile(口角距離/顔スケール)---
f = face.process(rgb)
smile = np.nan
if f.multi_face_landmarks:
lm = f.multi_face_landmarks[0].landmark
L = np.array([lm[291].x*w, lm[291].y*h]) # 左口角
R = np.array([lm[61].x*w, lm[61].y*h]) # 右口角
mouth = np.array([lm[13].x*w, lm[13].y*h])
eyes = np.array([lm[168].x*w,lm[168].y*h])
scale = np.linalg.norm(eyes - mouth) + 1e-6
smile = np.linalg.norm(R - L) / scale
smile_buf.append(smile); smile_avg = float(np.nanmean(smile_buf))
# --- Motion(上半身キーポイントの散らばり)---
p = pose.process(rgb)
motion = np.nan
if p.pose_landmarks:
pts = [(l.x, l.y) for l in p.pose_landmarks.landmark[:25]]
motion = float(np.nanstd(pts))
motion_buf.append(motion); motion_avg = float(np.nanmean(motion_buf))
# 画面表示:左上に生の値、右上に移動平均
cv2.putText(frame, f"Smile:{smile:.2f} Motion:{motion:.4f}", (30,40),
cv2.FONT_HERSHEY_SIMPLEX, 0.9, (0,255,0), 2)
cv2.putText(frame, f"AVG Smile:{smile_avg:.2f}", (w-320,40),
cv2.FONT_HERSHEY_SIMPLEX, 0.9, color_for_smile(smile_avg), 2)
cv2.putText(frame, f"AVG Motion:{motion_avg:.4f}", (w-320,80),
cv2.FONT_HERSHEY_SIMPLEX, 0.9, (0,255,0), 2)
# RECインジケータ(録画中は左下に赤表示)
if record:
cv2.rectangle(frame, (10, h-35), (120, h-10), (0,0,255), -1)
cv2.putText(frame, "REC", (20, h-15),
cv2.FONT_HERSHEY_SIMPLEX, 0.6, (255,255,255), 2)
cv2.imshow("Smile+Motion (r: record, q/Esc: quit)", frame)
k = cv2.waitKey(1) & 0xFF
# rキーでCSV記録ON/OFF
if k == ord('r'):
record = not record
if record:
if csv_f is None:
csv_f = open("smile_motion_log.csv", "w", newline="", encoding="utf-8")
writer = csv.writer(csv_f)
writer.writerow(["datetime","elapsed_sec","smile","smile_avg","motion","motion_avg"])
print("REC ON")
else:
if csv_f: csv_f.flush()
print("REC OFF")
# 終了
if k == ord('q') or k == 27: # q or Esc
break
# 録画中ならCSVに追記
if record and writer:
writer.writerow([datetime.now().isoformat(timespec='seconds'),
f"{time.time()-t0:.2f}", f"{smile:.4f}", f"{smile_avg:.4f}",
f"{motion:.6f}", f"{motion_avg:.6f}"])
cap.release()
if csv_f: csv_f.close()
cv2.destroyAllWindows()
② スクリプトを実行する(カメラ起動)
コマンドプロンプト(いまのウィンドウ)で、次を実行:
python realtime_feedback.py
画面が開いたら…
左上:その瞬間の数値(Smile / Motion)
右上:移動平均(安定値)
r:録画開始/停止(CSV保存)
q または Esc:終了

③ CSVを確認(Excelで可視化用)
終了すると、同じフォルダに smile_motion_log.csv ができます
そのまま ダブルクリックでExcel で開けます(中身は smile, smile_avg, motion, motion_avg などの列)

④ Excelでグラフを作る(完成)
ExcelでCSVを開く
B列(elapsed_sec) を横軸に、D列(smile_avg) と F列(motion_avg) を縦軸に選択
挿入 → 散布図(折れ線) をクリック
→ 笑顔と動きの推移が一目で分かります

📝 まとめ:入門者がやってみて分かったことと今後の課題
今回の取り組みで、登壇練習において 「笑顔」や「動き」を数値で見える化できる環境 を自分の手で作ることができました。
私はPythonも機械学習も初心者ですが、ステップを一つずつ進めればここまで形になることが実感できました。
「自分は笑っているつもり」「動いているつもり」といった 主観と客観のズレ を減らす第一歩になったと感じます。
ただ、実際にやってみていくつかの課題も残りました。
-
数値と実際の表情が必ずしも一致しない
照明条件やカメラの角度によって、笑顔スコアが低く出たり、高く出すぎたりすることがありました。
→ 対策:照明を正面から当てる、カメラを目線の高さにするなど工夫が必要です。 -
動き(Motion)の数値が揺れやすい
ほんの少しの体の揺れでも「動き」として検出されるため、実際のジェスチャーと一致しない場合がありました。
→ 対策:Motionは「絶対値で良し悪しを決める」よりも、「リハーサル①と②で比較して改善できているか」に使うのが適切だと思います。 -
実用化には精度改善が必要
今回はあくまで「第一歩のプロトタイプ」。
本番で「数値が低い=印象が悪い」とは必ずしも言えません。
→ 今後は「声の抑揚」や「話すスピード」なども組み合わせることで、より実用的な登壇練習支援ツールにできると感じました。
🔮 今回の取組で得られた気付き
- 「完璧に動かなくても、自分の練習を客観視するきっかけになる」
- 「自分でコードを書き換えて改善できる余地があると学べる」
- 「数値が正解ではなく、改善のヒントを与えてくれる存在だと捉えるのが大事」
👉 今回のプロジェクトは、初心者でも 登壇練習をデジタルで補助できる可能性 を示してくれました。
これから改良を重ねれば、より実践的に役立つツールに成長させていけると思います!