William Zhang2026-09-27

MediaPipe Hand Landmarker による両手ピンチ 9 フィルタデモ

MediaPipe Tasks Hand Landmarker モデルを用い、CPU (XNNPACK アクセラレーション) 上で 両手の 21 キーポイントとピンチ状態をリアルタイム検出。「両手が同時にピンチする」を 四角形 ROI に直接マッピングし、ROI 内のピクセルに対して 9 種類の画像フィルタを リアルタイムに適用します。

CVHand TrackingMediaPipeGithub

reComputer-RK / Docker / RKNN / YOLO に基づくビジュアルフィルタデモ

https://github.com/doublelf/filter_game


プロジェクト概要

本プロジェクトは 9 種類のフィルタを一つの FILTER_LIST 辞書に集約し、容易に拡張できるようにしています。これにより、191 行のフィルタ専用スタジオを実現しています: gesture.py が両手ピンチを検出 → filter_game.FilterGame が四角形の頂点を維持 → filters.apply_filter が ROI のピクセルを処理します。


クイックスタート

1. リポジトリのクローンとディレクトリ移動

bash
git clone https://github.com/doublelf/filter_game
cd filter_game

2. 依存関係のインストール

bash
pip install -r requirements.txt

requirements.txt には次の 4 つの依存関係が固定されています:

text
opencv-python>=4.8.0
mediapipe>=0.10.14
numpy>=1.26.0
Pillow>=10.4.0

9 種類のフィルタのうち、EMBOSS / CONTOUR / POSTERIZE は Pillow が提供します。INVERT / NEON / GRAY / SEPIA / HUE_SHIFT / PIXELATE は OpenCV + NumPy で実装されており、Pillow がなくても動作します(Pillow がない場合は元画像を返します。filters.py:36 の _pil_filter_safe を参照)。

3. ゲーム起動

bash
python main.py                  # デフォルトはカメラ 0、水平反転を有効
python main.py --cam 1          # 2 台目のカメラを使用
python main.py --no-flip        # 水平反転を無効化
python main.py --no-landmarks   # キーポイントオーバーレイを無効化(デフォルトは有効)
python main.py --hide-trails    # ジェスチャートレイルを無効化(デフォルトは有効)

起動すると最初に「Filter Studio」メニュー画面が表示され、いずれかのキーを押すとゲームが始まります。

4. 操作方法

操作プレイヤーの入力プログラムの反応
片手でピンチ任意の手がピンチを開始人差し指先端にゴールドの拡散リング
両手でピンチ両手が同時にピンチ両方のアンカーの中点に拡散リング + トースト CONNECTED
ピンチ維持 + 移動両手をドラッグ四角形 (anchor_l, anchor_r, current_l, current_r) 内部のピクセルに現在のフィルタを適用
片手を離す片手を離す四角形が消え、IDLE に戻る
横スワイプでフィルタ切替ピンチしていない任意の手で画面を横切るようにスワイプSwipeIndicator のサイドグラデーション帯 + トースト Filter N/9 NAME
キーボードでフィルタ切替1–9 で直接 / [ ] または , . で次/前トースト Filter N/9 NAME
いつでもM でメニューに戻るカメラはキャプチャを継続、ゲームループのみ終了
いつでもESC で終了カメラを解放、MediaPipe を閉じ、ウィンドウを破棄

カメラなし環境での実行

recomputer-rk3576-devkit は工場出荷時に USB カメラが接続されていません。本プロジェクトは、カメラなしでもエンドツーエンドで動作するように 3 つのツールを同梱しています:

tools/verify_all.py — 8 ステップ検証スイート

bash
cd /home/seeed/hand/filter_game
python3 -m tools.verify_all

各ステップを出力します:

  • モジュールのインポート (gesture / filter_game / filters / overlay / utils)
  • utils のジオメトリ・テキストヘルパー
  • 9 種類すべてのフィルタが正しい shape と dtype を出力
  • FilterGame のステートマシン + SwipeTracker の横スワイプ検出
  • aarch64 上で本物の MediaPipe Hand Landmarker がロードされる(XNNPACK delegate、約 110 ms のウォームアップ)
  • HandOverlay.add_event + レンダリング (both_connected / filter_switch / swipe)
  • 合成された両手パイプラインが 120 フレームで完走し、FilterGame が少なくとも一度 BOTH_PINCH に到達する

tools/run_synthetic.py — オフラインレンダリング (PNG)

bash
python3 -m tools.run_synthetic --frames 200 --out /tmp/filter_demo
ls /tmp/filter_demo | wc -l      # → 200

完全な FilterGame + HandOverlay パイプラインを駆動し、次を出力します:

Code
events total=N by_type={'both_connected': 1, ...}
BOTH_PINCH frames: 1   DRAG frames: 146

各フレームを out/filter_XXXX.png に書き出します。カメラがない環境では、ffmpeg で mp4 に連結します:

bash
ffmpeg -framerate 30 -i /tmp/filter_demo/filter_%04d.png -c:v libx264 -pix_fmt yuv420p filter.mp4

tools/synthetic_source.py + tools/fake_tracker.py

合成ビデオソースと合成 HandTracker で、cv2.VideoCapture / gesture.HandTracker とインターフェース等価です。後で本物のカメラに切り替えるには、次の 2 行を置き換えるだけです:

python
cap = cv2.VideoCapture(args.cam)               # SyntheticHandSource を置き換え
tracker = HandTracker(num_hands=2)              # SyntheticHandTracker を置き換え

これで tools/run_synthetic.py が本物のカメラドライバスクリプトに昇格します。