ディープラーニングで映像から速度を正確に算出するのに苦労した

![速度表示-01_190224.jpg](https://qiita-image-store.s3.amazonaws.com/0/140663/c6ab47eb-6ff1-a727-1f54-3368a384b94e.jpeg)

# サマリー
　ここではディープラーニングを応用した映像解析による交通量調査システムに車両の速度算出機能を実装した時に苦労した話を書いてます。「GPUがないなら、Teslaを買えばいいのよ？」という内容は以下の通りです。
・そもそもディープラーニングに何ができるか？(省略可)
・速度＝距離÷時間。距離の設定はどうする？
・ラインを2本引いて通過時間を取る？
・速度算出のための要件の整理
・ラインの設定例
・速度算出の課題
・車間距離への応用

# トップ画像とサンプルムービーについて
トップ画像は以下のサンプルムービーのスクリーンショットです。VOLO V3で車両を物体検知して、トラッキングしてIDを割り振ってます。検出された車両は種類ごとに白(普通車)とか赤(トラック)とか青(バス)の枠がついて、その車両がピンクのライン2本を通過すると、速度が黄文字で表示されます。枠上左端の数字はID(ユニーク)です。ピンクのラインはLine3・4が上り車線用(画面左側)、Line1・2が下り車線用(画面右側)となっています。
速度表示のサンプルムービーは[こちら。](https://youtu.be/GE6bYZyplr8)
なおこのトップ画像・サンプルムービーでは後述する課題を説明するため、わざとLine3・Line4が画面下過ぎる設定にしています。このため検出ミスや速度の算出ミスが一部発生しています。またシステムには車間距離表示機能もありますが、ここでは非表示にしております。

# そもそもディープラーニングに何ができるか？
ここではYOLO V3で何ができるか説明します。詳しい方は次にお進みください。
前の記事を読んでいない方に説明すると、このソフト(Traffic BLade-D）ではYOLO V3を使用しています。YOLO V3は物体検出を行うもので、指定したクラス(交通量調査の場合はcarやtruck,busなど）が画面内に映っていると「写真のこの場所にcarが映ってます。自信(スコア)はSで、座標はXYです」なんて報告してくれるだけで、もともと速度検出なんて機能はありません。

もっというとYOLOV3は「映像解析」も本当はできず、できるのは静止画からの検出だけ。YOLO V3がさも映像を解析しているようなデモも見かけますが、あれは静止画単位で処理したものを連続表示しているだけです。よく見ると画面内の人物とかに枠(バウンディングボックス）がついて、「person」とかのクラス名やスコアが表示されているだけです。ID表示がない理由は、「1秒前のこの車と今のこの車は同一のものなので、同じIDふっておくね」なんてことはしてくれないからです。これは自前で実装する必要があります。こういった感じでYOLOV3以外にもいろいろ実装が必要だったため、「交通量調査を映像解析でやろうとして苦労したシリーズ」では以下のような流れになっています。

・物体検出にYOLO V3を使うかSSDを使うか迷ってYOLO V3にした
・静止画ではなく映像として解析するためにトラッキング機能をつけた
・映像内の一部の車両検出精度がイマイチなのでマスク機能をつけて精度を向上させた
・車両が検出できるようになったので速度も算出してやろうと色気を出した（←いまここ）

以下ではYOLO V3を使ってどのように速度を算出したかを説明します。といっても、最初は「どうやって速度を求めるか？」の設計の話が続くため、YOLO V3が次に出てくるのは最後近くの「速度算出の課題」の終わりの方です。まずは速度を求めるためにどのような設計を行ったか説明します。

# 速度＝距離÷時間。距離はどうする？

速度を求める式は、小学校4年生で習います。「映像内での距離と時間が分かれば速度なんて簡単に出るね」と最初は思っていました。ところが持ち込まれたサンプル映像を見てみると、どこで撮影したのかさっぱり分からず、後からでかけて距離を測定するようなこともムリっぽいものが多かったのです(高速道路の映像とか)。

そこで映像内から距離の設定に使えそうな手掛かりを探すことにしました。
見つけたのが道路に描かれている白線(破線)とか直進とかの矢印のいわゆる路面標示ですね。
例えば「40」とかの速度文字は縦が5m、車道中央線や車線境界線の白の破線は国道だと6m塗って9mあける、それ以外だと5m塗って5mあける、の2種類があることが分かりました。
寸法についてはこちら[資料](http://www.thr.mlit.go.jp/sendai/furukoku/pdf/kouji/kouji/kukaku-mame.pdf)とか[こちら](https://www.kictec.co.jp/varieties-road-mark-cat/road-mark-example/)を参考にしております。
撮影場所が分かっている場合、それが国道かどうかを調べるのにはGoogle Mapを使いました。Google Mapには国道か否か、そして航空写真だと縮尺の凡例も書いてあるので、長さを特定するのに役立ちました。

# ラインを2本引いて通過時間を取る？
![20190224092107.jpg](https://qiita-image-store.s3.amazonaws.com/0/140663/da136256-4b0f-96c6-1cc6-41dfd7af90c5.jpeg)

映像内から距離は取れるようになったので、今度はそれをどう使うか？です。
パッと思いつくのはラインを2本引いて、それぞれの通過時間から時間を求めることですね。
映像は30フレーム/秒なので、それぞれのラインを通過した直後のフレーム番号を引き算すると、2本のラインを通過するのに必要だったフレーム数が分かります。これから時間が取れます。

しかしながらこの手法ではいくつか問題がありました。
**１）何をもってラインを通過したとするか？**
当初はバウンディングボックスの上辺か下辺か、とにかく矩形のうち1辺(正確には1辺の両端とも)が最初にラインのX(縦ラインの場合)またはY座標(横ラインの場合)を越えれば「通過」と考えればよい、と思っていましたがうまくいきませんでした。

理由としてはYOLO V3による物体検出が安定せず、同じ車両にもかかわらずフレームごとにバウンディングボックス(枠)のサイズが安定せず、「びょいんびょいん」と枠が暴れることがあったからです。ラインに接近した車両の枠が「びょいんびょいん」して、車両はまだラインを越えていないのに枠だけがラインを越えたり越えなかったりするわけですね。
![バウンディングボックスが暴れてラインを越える.jpg](https://qiita-image-store.s3.amazonaws.com/0/140663/5bf92d5c-ae31-e679-5a65-da8869cc71de.jpeg)
※ID=8のタクシーのバウンディングボックスが暴れて、上辺がLine1を越えている。サンプル動画は[こちら](https://youtu.be/T5oOmqkX5S4)

そこで平均化するために、バウンディングボックスの中心点を取るようにしました。ただし中心点も枠が暴れることで移動するため、「中心点がラインを越えた時は、最初の1回しか取らない」としました。

**２）処理の高速化ができない**
1時間をこえる映像処理となるとそもそもフレーム数が108000もあります。映像サイズ(縦横サイズ)が大きくなるほどYOLO V3の処理は遅くなり、フルHD映像を処理させると11f/secくらいしか出ません。しかも領域指定のマスク処理をかけたりエビデンスの映像をレンダリングさせるとさらにトータルの処理速度が落ちてしまう。

そこで当初は30f/秒では処理せず、10f/secとか15f/secとかの間引きで処理して解析時間を短くしようと思ったのですが、正確なフレーム数を取るためには1/30秒で検出する必要があります。108000フレームから逃げられない…。よってこの問題(フレーム数の間引きによる処理速度向上)の問題はまだ解決していません。

# 速度算出のための要件の整理
もろもろ考えると、速度算出のために必要な要件は以下の通りとなります。

・画面内にラインを2本引く。ラインは始点・終点をXY座標で指定できるものとする。
・ラインは水平・垂直・斜めのいずれでも設定できるようにする
・ラインは2本を1つのセットとし、画面内に複数セットを設定できるようにする(1)。
・ラインのセットは指定するラインのほか、通過順を設定するものとする(2)。

(1)これは上り下りの2車線を同時解析する場合、上り用・下り用で異なるラインを利用できるようにするもの。
(2)2本のラインを上り下りの2車線で共用する場合もあるため、順番を設定できるようにした。もっとも速度は正の値にすればよいため、実際には別記事で解説する車間距離計測のための設定である。

# ラインの設定例
以下は速度表示の[サンプルムービー](https://youtu.be/GE6bYZyplr8)における、速度算出のためのライン関係の設定です。

    [LINE]
    ＃左車線上がライン1・下がライン2、右車線上がライン3・下がライン4
    LINE_1 = 299:900, 1111:797
    LINE_2 = 314:712, 947:651
    LINE_3 = 696:429, 1114:393       
    LINE_4 = 654:387, 1019:359
    
    ＃ラインの組み合わせ。左に書いたラインを先に通過する
    LINESET_1 = LINE_1, LINE_2
    LINESET_2 = LINE_4, LINE_3
    
    ＃各ラインセットの2ライン間の距離(m)
    LINESET-DIST_1 = 5
    LINESET-DIST_2 = 6

# 速度算出の課題

**１）速度算出の精度検証**
今回は簡単にいえば2次元のもの(映像)を三次元に当てはめて、「疑似的な」速度を算出した実装になっています。その手法で得られた速度は本当に正確なのか？というのが一番大きな疑問になりますね。

ただいくつかの映像で検出したサンプルを見る限り、現実の速度とかけ離れている数字は出ていない感じがします(例えば国道を走っている車が時速200km以上など）。
「算出した速度が正確かどうか？」を検証するには、実際に走行している車両の速度を測定し、また映像から算出した速度と比較して答え合わせするのが一番手っ取り早い。車両の速度測定についてはAmazonで15700円くらいで売っている「スピードガン」を使うのが良さそう。

こちらは商品説明を読むと「自動車時速16~322㎞、速度感知可能な最大距離:自動車457m、感知可能方向:向かってくる物体、遠ざかる物体の両方、精度:±1㎞※正面測定時の最高精度、±時速2㎞以上※角度がある場合」とあるので、なんかワクワクしますね。個人的にはこういうガジェットはとても好きです。精度検証を理由にかなり高い確率で買っちゃうと思うので、買ってしまったら別途検証記事を書きますね。しかしそれにしてもスピードガンが15700円程度で買えますか。いい時代になりましたねえ。

**２）速度の意味**
今回の実装では速度を2本のラインを通過する時間から計測するため、正確には「2本のラインを通過する平均時速」を算出することになります。「その瞬間の実速度」ではないわけですね。この測定方法は本来は高速道路のような一定速度で通行している車の速度を計測する用途に向いてますが、実際には国道では前の車が詰まっていると減速したり、信号待ちで車が数珠つなぎになって停車することもしばしば起きます。

要するに、2本のラインの間で極端に速度が落ちたり、停車してしまうような事態が想定されるわけです。この課題はまだ解決できてませんが、停車については例えば車両のステータスを見て、「停車状態」になった場合は速度算出を行わない、などの対応が必要になりそうです。

**３）物体検出精度による問題**
基本的には、2本のラインを越える瞬間を検出するにはYOLO V3による物体検出が成功していることが前提になります。1本目のライン通過の時は検出したけど、2本目のライン通過時はミスってYOLO V3がそもそも車両を検出できませんでした…となると、速度も当然検出できない。例えばこんな感じです。
![検出できない.jpg](https://qiita-image-store.s3.amazonaws.com/0/140663/57ef32ab-a4c9-ea8e-180f-11be7f54bd22.jpeg)
この画像ではトラック(アルミバン)が出てきてますが、運転席もタイヤも見えないので四角いコンテナにしか見えない。右隣の小型トラックも隣のトラックとくっついているので、やはりこの角度の画像ではまだ車両として認識されていません。

これを防ぐためにはYOLO V3のスコア閾値の設定(閾値を下げて検出しやすくする。ただし下げ過ぎると誤検出が増える)のほか、「画面内でラインをどこに設定するか？」の考慮が必要になります。

後者については例えば車両がカメラから遠い距離にある時は、画像自体が小さいためYOLO V3でも検出できない。逆にカメラに近い場所にラインを設定すると、大型車両などでは「車体がすべて画面内に入る前から中心点がラインを通過し始める」という事象が発生してしまう。これは大型バスなどで車体が半分しか画面内に入っていない状況でもYOLO V3が検出するとバウンディングボックスが発生するため、中心点も発生してしまう。これがラインを越えてしまう訳ですね。

これを防ぐためには大型車でも全体が画面におさまる場所以降でラインを設定する必要がありますが、そこに距離算出のちょうどよい路面標示があるとも限りません。もっとも路面標示の件については、撮影場所やアングルを選択できるのであれば回避できるような気がする。撮影計画の際には、路面標示をどこに写すか、という観点を入れるようにしよう。

**４）トラッキング精度による問題**
YOLO V3による「物体検出精度による問題」スタート地点とすると、トラッキングによる精度による問題がゴールとなります。これはつまり、「1本目のラインを通過した車両と、2本目のラインを通過した車両が同一かどうか正しく判定できること」が要件となります。もしYOLO V3で物体検出が正しくできていても、トラッキングが正しくできていないと「2本のラインを通過」として認識できず、速度も算出できないからです。前の記事[「ディープラーニングによる映像解析で車両をトラッキングするのに苦労した」](https://qiita.com/ComputerVision/items/d865f084c98a59349910)はここにつながってくるんですね。

# 車間距離への応用
速度が検知できたなら、次は「前の車との車間距離を取りたい」と思うのは自然な流れですね。
前方車両の速度と、ライン通過の時間(何フレーム目か)が取得できれば、後続車との車間距離は取れそうな気がする。楽勝じゃん！と思って実装にトライしましたが、簡単にはいきませんでした。トホホ。特に「前方車両」が特に簡単ではなかったです。Traffic Blade-Dにはようやく実装出来ましたが、とっても苦労したので車間距離については別記事で紹介する予定です。最後までお読みいただきありがとうございました。

# 関連記事
・[2022年：YOLOXの映像解析で車両の速度をAIで算出して渋滞を判定するのに苦労した～その１：リアルタイム処理編](https://qiita.com/ComputerVision/items/5283a0ebbb59d5a4cb8f)
・[2022年：YOLOXの映像解析で車両の速度をAIで算出して渋滞を判定するのに苦労した～その２：速度算出手法編](https://qiita.com/ComputerVision/items/a86f20673df6b6536345)
・[ディープラーニングで故障車両を発見するのに苦労した]
(https://qiita.com/ComputerVision/items/4f377bc3eb5e601676ac)
・[ディープラーニングによる映像解析で車両をトラッキングするのに苦労した]
(https://qiita.com/ComputerVision/items/d865f084c98a59349910)
・[ディープラーニングで交通量調査の映像解析精度を上げるのに苦労した]
(https://qiita.com/ComputerVision/items/2b5e8ed97610085a20c2)
・[SSD_KerasをWindowsで使って交通量調査できるか試してみた]
(https://qiita.com/ComputerVision/items/0f87b35f5941b7c3c06b)
・[YOLO V3を使った交通量調査ソフトを作って苦労した]
(https://qiita.com/ComputerVision/items/bdbf5c237004195a4fbc)