5.0 KiB
5.0 KiB
頭部角度(Yaw)の平滑化フィルタと静止時ゆらぎ
静止していてもYawが変化する問題に対する調査記録と、選択可能なフィルタの説明。
設定(config/default.yaml の head_pose)
| キー | 既定値 | 内容 |
|---|---|---|
smoothing_enabled |
true | false で平滑化なし(none と同じ) |
smoothing_method |
ema |
none / ema / median_ema / hampel_ema / one_euro / median_one_euro |
ema_alpha |
0.4 | EMA係数。median_ema・hampel_ema でも使用 |
smoothing_window |
5 | 窓幅(サンプル数)。窓の時間幅は姿勢更新レートに依存する |
hampel_threshold |
3.0 | Hampel判定のしきい値(頑健σの倍数) |
one_euro_min_cutoff / one_euro_beta / one_euro_d_cutoff |
1.0 / 0.0 / 1.0 | One Euro Filter のパラメータ |
filter_reset_gap_sec |
0.0 | 姿勢サンプルがこれより離れたらフィルタ状態をリセット(0で無効) |
既定値は従来動作(ema, α=0.4)と同じ。新キーはYAMLに書かなくてもよい(既定値が使われる)。
フィルタ本体は src/yaw_filters.py。
計測と比較の手順
- 正面静止で30〜60秒ログを取る(
data/logs/face/*_face_frames.csv。raw_yaw列が生値)。 python -m src.yaw_noise_report <csv> --track-id N --t-start 4 --t-end 44 --csv out.csv- 各フィルタのSD・最大変動幅・フレーム間ジッタを比較する。 遅れは静止データからは測れないので、階段・ランプ入力のシミュレーションまたは実際の振り向き試行で確認する。
計測結果(2026-10-01, 研究室, 正面・3m・静止, 4〜44秒, n=714, 約17.8Hz, 1280x720)
| フィルタ | SD | 最大変動幅 | 階段90%遅れ | ランプ遅れ |
|---|---|---|---|---|
| 生Yaw | 6.27° | 49.6° | 0 | 0 |
| ema 0.4(従来) | 3.77° | 28.1° | 0.22秒 | 0.06秒 |
| median5 + ema 0.4 | 1.92° | 17.1° | 0.34秒 | 0.17秒 |
| median5 + one_euro(1.0, β=0.02) | 1.96° | 20.0° | 0.22秒 | 0.11秒 |
| hampel w7 k3 + ema 0.4 | 2.17° | 21.9° | 0.39秒 | - |
- 遅れは、サンプル間隔0.056秒で、0→30°のステップ入力が27°に達するまでの時間(ランプは60°/sで増加する入力に対する遅れ)。
- 1か所・1距離・1人の静止データによる比較。実際の振り向き中の挙動は未検証。
- 現行(ema 0.4)の出力は、静止していても3σ=11.3°、最大変動幅28°あり、
subtle(10°)・weak(20°)のしきい値に達しうる。
分かったこと
- 生Yawの分散の大部分は、フレームごとの独立なノイズと、単発の+側スパイク(約5%)による。 中央値系フィルタ(median / Hampel)が単発スパイクに有効。
- ROI・bboxのフレーム間ゆらぎは0.3〜0.5px程度で、原因ではない。
- 再投影誤差はスパイクと相関せず(外れ値フレーム1.59px、その他1.58px)、除外基準には使えない。
- 顔が小さい(3mで目尻間距離が約24px)ため、ランドマーク0.5px程度の誤差で、Yawの標準偏差は4〜5°程度になる (シミュレーションで確認。実測の6.3°と同水準)。ROIを拡大しても元画素数は増えない。 → 精度を上げるには、距離を縮める・解像度を上げる・望遠にするなど、顔を大きく映す対策が最も効く。
useExtrinsicGuess(前フレームの解を初期値にする)はシミュレーション上で効果なし。実装していない。- 焦点距離の仮定(現行はフレーム幅=1280px、実際は約800px相当と推定)は、Yawへの影響が1°未満。
注意点(要確認)
- Yawの符号が反転している可能性が高い。
src/head_pose.pyのMODEL_POINTSはYを下向きに直した一方で zの符号は据え置かれ、目・口が鼻先より手前にある(奥行きが反転した)顔になっている。 シミュレーションでは、実際の頭部回転に対して推定Yawの符号が反転した(真値+15°→推定-15°、大きさは保たれる)。turn_detection.signage_yaw_directionが実測に合わせて設定されていれば結果は一貫するが、 「正のYaw=物理的にどちら向きか」を前提にした解釈・報告では注意が必要。 モデルを直す場合は符号が変わるので、signage_yaw_directionと過去ログの扱いも同時に見直すこと。 - 正面を向いた静止時の平均が約-19.3°だった。立ち位置は画像中心から約31px(約1.4°)しか離れておらず、 位置では説明できない。カメラのレンズでなくプレビュー画面を見ていた可能性などを、レンズに目印を付けて再測定して確認する。
- 一部の設定値(
median_one_euroの β など)は、この静止データのノイズ規模に合わせたもの。距離が変わったら再評価する。