95 lines
7.9 KiB
Markdown
95 lines
7.9 KiB
Markdown
# 頭部角度(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で無効) |
|
||
| `smoothing_window_sec` | 0.0 | 0より大きいと、中央値系フィルタの窓を「直近N秒」で指定する(`smoothing_window` より優先)。更新レートが変わってもフィルタの強さが変わらない |
|
||
| `max_reprojection_error_ratio` | 0.0 | 再投影誤差が「目尻間距離×この値」を超えた姿勢を棄却(0で無効)。正常フレームの比率は約0.07、破綻フレームは0.4以上 |
|
||
|
||
既定値は従来動作(`ema`, α=0.4)と同じ。新キーはYAMLに書かなくてもよい(既定値が使われる)。
|
||
フィルタ本体は `src/yaw_filters.py`。
|
||
|
||
## 計測と比較の手順
|
||
|
||
1. 正面静止で30〜60秒ログを取る(`data/logs/face/*_face_frames.csv`。`raw_yaw` 列が生値)。
|
||
2. `python -m src.yaw_noise_report <csv> --track-id N --t-start 4 --t-end 44 --csv out.csv`
|
||
3. 各フィルタの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°)のしきい値に達しうる。
|
||
|
||
## 追加計測(2026-10-02, 研究室, 正面・3m・静止+移動, median5+EMA 0.4設定時のログ, 約23.1Hz)
|
||
|
||
静止区間(フレーム250〜1390, 49.3秒):
|
||
|
||
| フィルタ | SD | 最大変動幅 | 平均から8°超の外れ | 階段90%遅れ |
|
||
|---|---:|---:|---:|---:|
|
||
| 生Yaw | 9.02° | 72.6° | - | 0 |
|
||
| ema 0.4(旧デフォルト) | 5.70° | 54.3° | 11.5% | 0.17秒 |
|
||
| median5 + ema 0.4 | 3.50° | 38.8° | 2.5% | 0.26秒 |
|
||
| median 0.45秒 + ema 0.5 | 2.27° | 31.0° | 0.5% | 0.35秒 |
|
||
| **median 0.55秒 + ema 0.5** | **1.85°** | **9.9°** | **0.0%** | 0.39秒 |
|
||
|
||
- 実機の `yaw` 列は、生Yawにオフラインで同じフィルタをかけた結果と一致した(最大差 9e-7)。
|
||
- 生Yawの+側スパイクは8.7%。連続長は1フレーム46回・2連続17回・3連続3回・4連続1回・6連続1回。
|
||
中央値は、窓n個で(n-1)/2連続までのスパイクを除去できる。median5(n=5)は2連続まで。
|
||
- 移動しながら向く区間(フレーム218〜240)で、生Yawは-68.7°→-23.1°。90%到達は
|
||
median5+EMA0.4が0.76秒、旧デフォルトが0.67秒、median 0.55秒+EMA0.5が0.80秒(50%到達は0.31/0.09/0.40秒)。
|
||
窓を広げても、実際の振り向きでの追加の遅れは小さい。
|
||
- 再投影誤差/目尻間距離の比は、正常フレームで p99 でも0.12〜0.19。0.4以上のフレームはすべて、
|
||
目尻間距離が20px以下の小さい顔(入退場の移動中)で、Yawも破綻していた(例: 比104.9でYaw -1.0°、比150でYaw -66.7°)。
|
||
比0.3で除外すると、静止区間のフレームは1つも除外されず、破綻フレームだけが落ちる。
|
||
静止時のスパイクとは相関しない(スパイク1.58px、その他1.57px)。
|
||
- 静止中の中央値からの逸脱が10°を超えた状態の連続時間は、旧デフォルトで最長0.27秒、median5で0.22秒、
|
||
median 0.55秒窓ではさらに短い。`min_turn_duration_sec`(0.3秒)との余裕は、窓を広げるほど大きくなる。
|
||
|
||
## 分かったこと
|
||
|
||
- 生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` と過去ログの扱いも同時に見直すこと。
|
||
- 正面を向いた静止時のYaw中央値が、2回の計測(2026-10-01, 10-02)とも約-20°だった(bbox中心はそれぞれ671px, 680px。
|
||
画像中心640pxから約30〜40px、約2°しか離れていない)。立ち位置では説明できない。
|
||
レンズとプレビュー画面は物理的に離れているため、プレビューを見ていた可能性は除外されている。
|
||
原因は未解明(3Dモデル点と実際の顔の不一致、頭部の実際の向きなど)。
|
||
距離を変えて(例: 1.5m)測り、オフセットが距離で変わるか(小さい顔による見かけの偏り)、
|
||
変わらないか(系統的なずれ)を確認する。振り向き検出は基準Yawとの差を使うので、固定のオフセットは影響しない。
|
||
- 一部の設定値(`median_one_euro` の β など)は、この静止データのノイズ規模に合わせたもの。距離が変わったら再評価する。
|