ラインカウンター・アルゴリズム解説

1. 概要
本アルゴリズムは、弊社が交通量調査や特異点判定に利用しているオブジェクト認識の結果をどのように定量化しているかの解説ブログとなります。発見した個体ごとに一意のIDを割り当てつつ追跡し、かつ任意に定義した線分(ライン)を横断した際にログ出力・CSV保存・画面表示のフラッシュ通知を行うものです。 計算負荷を抑えるため「3フレームに1回しか推論を行わない」とか「一度の横断検知後はクールダウン期間を設ける」などの工夫についても言及しています。
2. 重心ベースの追跡
この追跡アルゴリズム は、各フレームごとに検出されたバウンディングボックスの重心を以下の式で算出し、
c_i = ((x_{min,i}+x_{max,i})/2,\;(y_{min,i}+y_{max,i})/2)
、
前フレームにトラッキング中の重心リスト \{o_j\}
と入力重心リスト \{c_i\}
のユークリッド距離行列
D_{j,i} = \|o_j - c_i\|_2
を求めます。以下の手順でマッチングを行います。
- 各行(既存オブジェクト)に対し最小距離の列(入力重心)を貪欲に割り当て
- マッチしなかった既存オブジェクトは「消失カウンタ」をインクリメント
- 消失カウンタが閾値
maxDisappeared
を超えたら deregister(ID解放) - マッチしなかった入力重心は新規オブジェクトとして register(ID登録)
この方式により、簡潔ながらも連続フレーム間での個体の移動を滑らかに追跡できます。
3. 線分交差判定の数学的定式化
任意に定義した線分を端点 P_1=(x_1,y_1), P_2=(x_2,y_2)
で表し、オブジェクトの重心 C=(x,y)
が「線を横断した瞬間」を以下の二段階で検出します。
3.1 線分上への射影パラメータ
t = ((C - P1)·(P2 - P1)) / ‖P2 - P1‖²
if 0 ≤ t ≤ 1: 線分上に射影されている
t
を用いることで、重心の線分への最短投影点が線分区間内かどうかを判定します。
3.2 符号付き外積による左右判定
side = (x2 - x1)*(y - y1) - (y2 - y1)*(x - x1)
sign = +1 if side>0 else -1 if side<0 else 0
side
の符号で、重心が線の左側か右側かを判定します。
最後に前フレームの符号 prev
と現在 curr
を比較して反転していれば横断とみなします。
4. フレームスキップによる計算負荷軽減
毎フレーム推論・トラッキングすると高い計算コストがかかります。
そこで定数 FRAME_SKIP
(例:3)を設け、frame_idx % FRAME_SKIP == 1
のフレームのみ「推論→トラッキング→横断検知→ログ」を実行し、それ以外のフレームでは直前のトラッキング結果とライン表示のみを行うことで負荷を 1/FRAME_SKIP に低減します。
5. クールダウン機構で誤検知防止
重心位置のわずかな揺れ(ジッタ)が原因で、同一オブジェクトが同一線分を短期間に何度も「横断した」と誤検知されることがあります。
そこで、横断検知時に cooldowns[(objID, line_idx)] = COOLDOWN_FRAMES
を設定し、そのキーの
カウンタがゼロになるまで再検知を抑制します(例:5フレーム)。
6. 白線フラッシュによる視認性向上
横断を検知した瞬間は、画面上の該当ラインを数フレーム白くフラッシュさせることで検知タイミングを視覚的に強調します。
flash_counters[line_idx] = FLASH_FRAMES
をセットし、描画時に >0
なら白、そうでなければ赤とし、各描画ループでインクリメントします。
7. ジッタ除去フィルター(任意オプション)
より厳密に重心の揺れを抑えるには、重心座標に指数移動平均(EMA)フィルターを掛けてもよいでしょう。
C_t' = α * C_t + (1-α) * C_{t-1}'
ただし本実装では FRAME_SKIP とクールダウン機構で十分な精度が得られています。
8. まとめ
本アルゴリズムは、
- AI物体検出
- 中心座標監視とID管理と追跡
- 線分への射影+符号付き外積での交差判定
- FRAME_SKIPによる計算削減
- COOLDOWNでの誤検知抑制
- FLASHでの視覚フィードバック
という複数の工夫を組み合わせ、リアルタイム性と高精度検知を両立しています。 これにより「誰が」「いつ」「どの線を」「どちら方向に」横断したかを効率的にログ・可視化可能です。 © 株式会社ビー・ナレッジ・デザイン