
AICountの画面
(※プライバシー保護のため、実際の映像をイラスト化しています)
右側の表示は上から
Traffic Density : 渋滞指数(独自指標)
car,motorcycle,bus,truck : 検出する対象(歩行者や自転車も選べます)
R_B : レッド(R)の線を跨いで、ブルー(B)の線を跨いだ車両の数でその後ろの数字は上記の検出する対象ごとにカウントしています。例えば19,0,0,6であればcarが19台、truckが6台という意味。
Frame : 解析中フレーム/解析動画の総フレーム数(解析の進捗%)
Time : このフレームの時間
はじめに
前回の記事では、YOLOによる物体検出とByteTrackによる追跡を組み合わせ、ビデオ内の車や人をリアルタイムで追いかける技術の基礎を解説しました。しかし、検出した物体をただ追跡するだけでは、具体的なデータは得られません。本当の価値は、その追跡データをいかにして「意味のある数値」に変換するかにあります。
「どの方向に何台の車が通過したのか?」「この交差点は今、渋滞しているのか?」「信号待ちの平均時間はどれくらいか?」――こうした問いに答えるためには、追跡後の「数値化」のプロセスが不可欠です。
この記事では、私の長年の試行錯誤で築き上げた実用的な機能を公開します。この記事を読んで、私のオリジナルアプリに興味を持たれた方は是非コメントを下さい!
- 方向別カウント: シンプルな直線から複雑な交差点まで、車両の移動方向を正確にカウントする方法。
- 渋滞指数の算出: 交通の流れの「質」を捉え、混雑度を定量的に評価するインテリジェリジェントな手法。
- 滞在時間の計測: 車両が画面内に留まった時間を計測し、信号待ちなどの分析を可能にする技術。
- 巨大ファイルの回避策: 数時間に及ぶ長時間解析でも、ストレージを圧迫しないスマートなファイル管理術。
——————————————————————————–
1. 移動方向をどう見分けるか?
交通量調査において、単に「そこに車がいる」という情報だけでは不十分です。「どの車線から来て、どの方向へ向かったのか」また「それが何時なのか?」という方向別時系列データが重要で、これをデータ化できるところが、道路ラボオリジナル「AICount」の最大の売りなのです。単なる存在検知のみならず、オブジェクトの動きを定量化する。つまり映像をデータ化する高度かつ実用的なソフトが出来上がったのです。
ここでは、ユーザーの目的や調査対象の道路形状に応じて選択可能な、2つの異なる方向別カウントシステム (crossing_system) について詳しく見ていきましょう。
1.1. シンプルな一本線方式 (crossing_system = 1)
これは最も直感的で設定が簡単なカウント方式です。画面上に一本の仮想的な線 (line_start, line_end) を引き、オブジェクトがその線を横切る瞬間を捉えて、その時間と共にカウントします。単路部での交通量計測に用います。また、施設の入り口にラインを設定することで、入場者数と退場者数を時系列データとして記録することが出来ます。
ちょっと、プログラムの中に踏み込んで、移動方向を判定するロジックを紹介します。
- ベクトルA: 画面に引かれた黄色のカウントラインそのもの。始点から終点に向かう矢印だと考えてください。
- ベクトルB: 車が直前のフレームから現在のフレームへと移動した、ごく短い動きを表す矢印です。
まず、ベクトルAとベクトルBが交差したかどうかを判別します。交差していれば、次に登場するのが外積の計算です。
外積を計算すると、ベクトルBがベクトルAに対して「左側」にあるのか「右側」にあるのかを、正負の符号だけで判定できます。このスクリプトでは、外積の計算結果が正か負かを見るだけで、オブジェクトが線のどちら側からどちら側へ移動したかを機械的に判断しているのです。
外積 > 0の場合:LtoR(Left to Right) としてカウント外積 < 0の場合:RtoL(Right to Left) としてカウント
利点と限界 この方式の最大の利点は、設定が非常にシンプルであることです。直線道路の上下線の交通量を計測するなど、単純なユースケースに最適です。


シンプルな1本線 時間帯別交通量の出力例
1.2.交差点交通に対応する四本線方式 (crossing_system = 2)
交差点での車両の動きを分析するために設計されたのが、この四本線方式です。画面上に4つの点 (p1, p2, p3, p4) を設定し、それらを結んで作られる4色のライン(赤、青、黄、紫)で囲まれたエリアを監視します。
具体例を通して、その3ステップを見ていきましょう。
- 最初の交差を記録: ID
101の車が、まず赤のラインを横切って交差点に進入したとします。スクリプトは即座に「ID 101は赤ラインを最初に通過した」とfirst_cross[101] = 'R'のように記録します。 - 移動方向を確定: 次に、ID
101が交差点を通過し、青のラインを横切ってエリアから出たとします。スクリプトはこれを確認し、記録しておいた最初のライン ('R') とは異なることを確認します。これにより、「赤から青への移動 (R_B)」が確定し、その時刻とともにカウント情報がCSVに記録されます。1カウントあたり1行追加されます。 - 記録をリセット: カウントが完了すると同時に、スクリプトは
first_cross[101]の記録を消去し、リセットします。これにより、もしID101の車が再びエリアに進入した場合でも、新たな移動シーケンスとして正確に追跡する準備が整います。
この手法は、複雑な経路解析を必要とせず、効率的に交差点での右左折(例: 赤→青:R_B)や直進(例: 赤→黄:R_Y)といった動きをデータ化できる設計です。最終的に出力されるcrossing_log-*.csvファイルのStartColorとEndColor列には、この方式で判定された移動の始点と終点の色が記録され、交差点における方向別カウントを可能にします。



方向別時間帯別交通量の出力例
これら2つのカウント方式は、分析したい道路の形状や取得したいデータの粒度に応じて選択できます。
そして、単に通過台数を数えるだけでなく、交通の「質」にも目を向けることで、さらに深い洞察が得られます。それが次に解説する「渋滞指数」です。
2. 道路の混雑具合を可視化する「渋滞指数」
道路ラボオリジナル「AICount」は交通量をカウントするだけではありません。交通の「質」、つまり渋滞を定量化することにチャレンジしました。
これは、道路計画や交通制御において非常に価値のあるデータとなります。しかし、渋滞は速度、密度、流れなど多くの要因が絡む複雑な現象です。これをカメラの映像から判断するためにはかなり工夫が必要でした。
ここでの課題は、「バウンディングボックスの移動情報だけを使い、計算コストを低く抑えつつ、渋滞を効果的に示す指標をどう設計するか」という点にあります。
このスクリプトは、「車両の移動距離がほぼゼロの場合、渋滞している」というシンプルな仮説に基づき、この課題に対する巧妙な解決策を実装しています。
渋滞指数(個別ID)= 1 / (1 + (distance / frame.shape[1]) * 500)
この式が私のたどり着いた答えでした。
- 正規化 (
distance / frame.shape[1]): まず、各車両の1フレームあたりの移動距離 (distance) を、動画の幅 (frame.shape[1]) で割っています。これは、動画の解像度(HD、4Kなど)に依存しない、普遍的な指標にするための正規化です。この一手間が、どのような映像ソースに対してもロジックの安定性を保つ鍵となります。 - 感度の増幅 (
* 500): 正規化された値に500という係数を掛けています。これにより、微小な動きの変化が最終的な指数に与える影響を増幅させ、渋滞の兆候をより敏感に捉えられるようになります。 - ゼロ除算の回避 (
1 + ...): 分母に1を足しているのは、車両が完全に停止 (distance = 0) した場合でも、分母がゼロになって計算が破綻するのを防ぐための、プログラミングにおける標準的なテクニックです。
1つのIDがピタッと止まっていたら1になります。最大で1です。1つのIDは0~1の値を持ちます。少しでも動くと0.3とか0.2などと、極端に1から離れた値を持ちます。つまり、ノロノロでも進んでいればこの渋滞指数(個別ID)は小さい値を持ちます。
この計算を、追跡中の全車両に対して行い、渋滞指数(個別ID)を累積します。これにより、渋滞指数(congestion_index)を算出します。
動きが鈍い車両が多いほど、この合計値は大きくなります。
この渋滞指数(congestion_index)が例えば 10 ぐらいであれば、10台程度の車両がピタッと静止していることになります。
そして、設定した間隔(例: 10フレーム)が経過するたびに、この累積値をフレーム数で割って平均化し、「渋滞指数」として congestion_index-*.csv に時系列で記録します。なぜこのように移動平均を取るかというと、この指標はバウンディングボックスが少しでも動くと1から極端に離れた数値をとるため、不安定な値をとり、バウンディングボックスの揺れにも大きく影響するからです。
この指標を時系列でグラフ化すると、交通の流れが悪くなる時刻が一目瞭然です。


渋滞指数(Traffic Density)の出力例
このアプローチは、渋滞という現象をカメラでとらえている情報だけを基に、わかりやすく可視化するという、画期的な指標だと考えています。
さて、車両の「動きの鈍さ」だけでなく、「どれくらいの時間そこにいたか」という視点もまた、交通分析に新たな次元をもたらします。
3. 「滞在時間」から見えてくるもの
あるオブジェクトが検出エリア内にどれくらいの時間存在したかを計測する「滞在時間」は、交通分析において非常に有用な指標です。例えば、交差点での平均信号待ち時間、店舗前の駐車スペースでの平均滞在時間、あるいは特定のエリアでの人や車両の滞留状況など、さまざまなインサイトを引き出すことができます。
このスクリプトは、各オブジェクトが画面に登場してからカウントされる(エリアを離れる)までの時間をLifetimeSecとして秒単位で正確に計測します。その計測方法は、以下の4ステップに基づいています。
- Step 1: 初登場の記録: 新しい追跡IDを持つオブジェクトが初めて検出されると、その瞬間のフレーム番号が
track_first_seen_frameという辞書に、IDと紐付けて記録されます。 - Step 2: 旅立ちの記録: そのオブジェクトがカウントラインを横切り、交通量としてカウントされるイベントが発生します。
- Step 3: 差分の計算: イベント発生時のフレーム番号と、Step 1で記録された初登場時のフレーム番号の差分を計算します。これにより、オブジェクトが画面内に存在したフレーム数が求まります。
- Step 4: 時間への変換: 最後に、このフレーム数の差分を実世界の「秒」に変換します。スクリプトは、動画全体の長さと総フレーム数を基に、「1フレームあたりの秒数 (
lifetime_per_frame_sec)」を算出します。この値とStep 3で求めた存在フレーム数を掛け合わせることで、正確な滞在時間(秒)が得られます。
この LifetimeSec は、crossing_log-*.csv ファイルの最後の列に出力され、交通分析に新たな深みを与えます。
【重要】滞在時間計測の精度について ここで一点、極めて重要な注意点があります。LifetimeSec の算出精度は、ユーザーが設定ファイルで提供する start_time_str と end_time_str の正確さに完全に依存します。スクリプトは、この2つの時刻の差 (total_duration) を総フレーム数 (total_frames) で割ることで lifetime_per_frame_sec を計算します。もし、これらの時刻が動画の実際の長さを反映していない場合、算出される滞在時間は意味をなさなくなります。正確な分析のためには、必ず解析対象ビデオの総再生時間と一致するよう、これらのパラメータを設定してください。
データを記録し、CSVファイルに出力する話が出たところで、次は長時間の動画データを扱う上での非常に実践的な課題、すなわち「ファイルサイズの管理」について解説します。
4. 巨大出力ファイルを作らない仕組み
24時間体制で交通量をモニタリングするなど、長時間にわたるビデオ解析を行う際、出力動画ファイルやログファイルが、気づけば数ギガバイト、時には数十ギガバイトという巨大なサイズに膨れ上がってしまいます。これはストレージを圧迫するだけでなく、ファイルの転送や後の確認作業を非常に困難にします。
このスクリプトは、そうした実用上の問題を解決するため、出力ファイルを自動的に分割管理する仕組みを内蔵しています。
動画出力ファイル (output_*.mp4) の分割
動画ファイルの分割は、ファイルサイズに基づいています。
- スクリプト内の
file_size_limit変数で、1ファイルあたりの最大サイズが0.95 * 1024 * 1024 * 1024バイト、つまり0.95GiB(ギビバイト)に定義されています。これは約1.02GBに相当します。 - 解析中、フレームを動画ファイルに書き込むたびに現在のファイルサイズがチェックされます。
- ファイルサイズがこのしきい値を超えた瞬間、スクリプトは現在書き込み中のファイルを自動的に閉じ、
file_counterを1つ増やして新しいファイル(例:output_1.mp4→output_2.mp4)への書き込みを開始します。
このシンプルなプロセスにより、巨大な単一ファイルが生成されるのを防ぎ、扱いやすいサイズのファイル群として結果を保存することができます。
CSVログファイルの分割
同様の配慮は、テキストベースのログファイルにもなされています。こちらはサイズではなく、日付を基準に分割されます。
_get_crossing_writer_for_timeおよび_get_congestion_writer_for_timeという関数が、ログを書き込むたびにタイムスタンプを確認します。- 日付が変わると(例: 1月1日の23:59から1月2日の00:00へ)、新しい日付用のCSVファイルが自動的に作成されます。これにより、
crossing_log-*.csvとcongestion_index-*.csvの両方が、日ごとに整理されたファイルとして出力されます。
このファイル管理機能により、ユーザーはストレージの制約やファイルハンドリングの困難さを心配することなく、長時間の連続解析を安心して実行できます。これは、研究や業務で本ツールを活用する上で、非常に重要な実用性の向上と言えるでしょう。
5. まとめ
本記事では、単なる物体追跡の技術を、実用的な交通流分析ツールへと昇華させるための4つの核心的な「数値化」技術を解説しました。
- 方向別カウント: ベクトルの外積や複数ラインの通過順序を利用した状態管理により、単純な往来から複雑な交差点の動きまでを効率的かつ正確にデータ化しました。
- 渋滞指数: 車両の微細な動きを正規化された移動距離の逆数として捉え、計算コストの低い代替指標で道路全体の混雑度という「交通の質」を可視化しました。
- 滞在時間: オブジェクトの出現から退出までのフレーム数を計測し、実時間へ変換することで、信号待ちなどの滞留状況を分析するための新たなデータ軸を提供しました。
- ファイル管理: 長時間解析における巨大ファイルの生成を防ぐ自動分割機能により、ツールの実用性と安定性を大幅に向上させました。
これらの技術が組み合わさることで、生のビデオデータから以下のような価値あるアウトプットが自動的に生成されます。
- 注釈付き動画ファイル (
output_*.mp4): IDやバウンディングボックスが描画された動画。解析結果を視覚的にレビューし、直感的に理解するための強力なツールです。 - 横断記録CSV (
crossing_log-*.csv): どのオブジェクトが、いつ、どの方向へ移動し、どれくらい滞在したかの詳細データ。交通流のパターン分析や統計処理の基礎となります。 - 渋滞指数CSV (
congestion_index-*.csv): 時間帯ごとの交通の混雑度を時系列で記録したデータ。ラッシュアワーの特定や交通制御施策の効果測定に活用できます。
AIによる画像・動画解析プロジェクトに取り組む上で、単に「物体を検出する」だけでなく、「検出した結果からどのような指標が抽出できるか」がお分かりいただけたでしょうか。ぜひ、このスクリプトを応用し、あなたのプロジェクトでより高度なデータ分析に挑戦してみてください。
プログラムの提供方法は会員専用のダウンロードページに記載しています。→リンク