#サマリー
ここでは現在と過去の画像(映像)を比較して、一定時間停止している車両を自動で検出するためにどのような実装をしたか解説しています。使った技術はOpenCVの背景差分法の各アルゴリズムと、物体検知の定番YoloV3です。サマリーは以下の通りです。
・この検証の目的
・故障車を見つけよう!・・・そして最初の挫折
・発想の転換とディープラーニングの使いどころ
・背景差分法でいい感じに検出できた!
・今後の課題
#この検証の目的
せっかくディープラーニングでいろいろ遊べるようになったので、「カメラで道路を定期的に撮影して、故障車や不法駐車の車を自動的に見つけることができれば面白いかも?」と思って、いろいろ考えてみました。例えば同じカメラアングルで撮影された現在の画像と10分ほど過去の画像を比較して、両方の画像に映っている車両があれば、故障もしくは不法駐車の車として判定できるわけですね。なんだ、簡単そう!
なお実際には事故の前後の映像や画像なんて簡単には手に入らないので、iPhoneで道路を適当に撮影して信号待ちの車とかずっと路肩に路駐している車を検出できれば成功としています。
これはこの記事のトップ画像にも使っているのですが、完全に停止しているのはトップ画像で緑の枠がついている3台だけです。あとは微妙に動いていたり、早く動いたりしています。ゆっくりでも素早くでも動いている車両は対象外にして2枚の画像で共通して停止している車両のみを自動検出するのがこの記事での目標です。・・・パトカーがずっと路駐しているな。ガンガン検出していますけど、ただの偶然で他意はありませんのでお気になさらず。
#故障車を見つけよう!・・・そして最初の挫折
最初に私が書いた設計書はこんな感じでした。
(1) 過去画像と現在画像で共通する部分を抜き出す。
(2) (1)から明らかに車道以外の部分など、解析に不要な部分を削除する。
(3) 残った部分に共通部分が車両かどうかの判定を行い、車両部分だけ残す。
(4) 検出した車両の台数や座標、結果画像などを出力して終了
なんだ、簡単そうだね。車両かどうかの判定には、OpenCVのカスケードファイルでも使うか。GPU使わなくていいしね。車両検出用のカスケードファイルも公開されているし楽勝そう・・・。と最初は思っていました。カスケードファイルについては、こちらの記事を参考にさせていただきました。
しかし残念ながらそうは問屋が卸してくれませんでした。
この設計の問題点としては
(1)過去画像と現在画像で共通する部分を抜き出す。
⇒晴れの日は10分もたてば太陽の位置も変わって陰影が変わるし、ピカピカに磨かれた車はそばを他の車が通るだけで、反射の影響を受ける。つまりそんなに簡単に共通部分は抜き出せない。
(3)残った部分に共通部分が車両かどうかの判定を行い、車両部分だけ残す。
⇒車両のカスケードファイルは、セダンタイプの車を正面から検出するのには役立つが、それ以外の検出はイマイチだった。
特に(1)が深刻でしたね。
過去画像と現在画像をピクセル単位で比較し(明度基準)、ある程度の閾値内にあるピクセルを「共通する部分」として抜き出してみましたが、どんなに設定をイジってもキレイな画像にはなりませんでした。
#発想の転換とディープラーニングの使いどころ
仕方がないので、発想を変えました。
(1)車両が映っていない「基準画像」を用意する。
(2)基準画像と過去画像で比較し、差分1を取る。この差分は車など移動体しか映っていないはず。
(3)基準画像と現在画像で比較し、差分2を取る。この差分にも車など移動体しか映っていないはず。
(4)差分画像1・2から共通している「エリア」を共通画像として抜き出す。
(5)共通画像をディープラーニングにかけて、car,bus,truckなどのクラスで物体検出できたものが故障車両(候補)のはず!?
一回失敗しているので、謙虚な気持ちで実験しました。
そしたらいい感じで検出できそうだったので、この「3枚画像方式」でやることにしました。3枚画像というのは、過去画像・現在画像・基準画像の3枚を使うからです。基準画像とはこんな感じですね。
この基準画像は車が映っていない画像を切り貼りして作成しました。撮影中ずーっと停止していたパトカーはフォトショップの魔法で消えていただきました。
なお当初は下記の画像のように日差しがある画像を基準画像として使いましたが、日差しの違いとかをばんばん検出するのであきらめて日差しがない画像で構成しました。
物体検出にはYoloV3を使用しました。ある程度小さい画像(カメラから遠い位置の車両など)でも検出できるからです。
YoloV3を使ったポイントとしては、最初の設計では**「過去画像と現在画像の共通部分から車両を検出する」だったのが、「基準画像からの差分を過去画像、現在画像のそれぞれから作り、2枚の差分画像から共通画像を作って車両を検出する」**に変わってます。
日本語で書くとあまり違いがないように見えますが後で説明する共通画像の作り方にも工夫があり、うまく検出できるようになりました。
#背景差分法でいい感じに検出できた!
OpenCVで用意されている背景差分法はそもそも「画像Aと画像Bを比較して違いを抽出する」ためのもので、「画像Aと画像Bで動きがないものを見つける」という目的には普通は使いません。それでいろいろ工夫しました。
小さな説明画像でも分かりやすいサンプルとして、新しい過去画像(左)と現在画像(右)を使います。
これらの画像では、パトカーと赤い車が停止車両となります。
まずは過去画像と基準画像で背景差分を取ってみます。
まあまあうまく差分が取れています。本当なら車両だけが抜けて欲しかったけど、車両が存在することで影が生まれたり照り返しがあったりして車両周辺の道路部分も差分となっています。
現在画像と基準画像でも差分を取ってみましょう。
これもいい感じです。両端の通行人も差分として取得していますが、これは後工程でカットします。
なおOpenCVでは背景差分法に複数のアルゴリズムが用意されており、私もいろいろと使い分けました。
背景差分法のいろんなアルゴリズムについては、こちらの記事を参考にさせていただきました。やっぱりGSOCがいい感じでしたね。
最後に二つの差分から共通部分を抜き出すと、このようになります。
ちゃんと赤い車とパトカーだけが残ってますね。
なおこの共通画像の作り方にもコツがあります。それはピクセルの色が似ているとか考えず、単に「ピクセルのある部分の和を抜きだす」方法で作りました。これが意外とうまくいった(結果は上図の通り)。
ただ道路が真っ白だと見た人がビックリするので、共通画像(停止車両のみ)にYoloV3を適用して車両を検出し、その結果と基準画像(車両なし)をいい感じに合成するとこうなります。
停止車両だけが残って、いい感じに検出できましたね。
#今後の課題
ざっくり実装について説明しましたが、画像の組み合わせや設定によっては検討課題が見つかりました。
###ゴースト対策
事故などで停止しているのではないのだけど、渋滞してノロノロ運転になっていることもあります。10分で車1台分くらいしか進まない、とかね。こういうケースを想定して、過去画像と現在画像の時間差が短い場合にどうなるかテストしてみました。過去画像(左)と現在画像(右)はこんな感じ。
差分画像はそれぞれこんな感じ。やはり車が「まんべんなく」道路上にあると差分も多くなって差分が差分でなくなる傾向がありますね。これは背景差分法のアルゴリズムを変更するか、別のやり方を考える必要があるかも?
共通画像をとってYoloV3で検出するとこんな感じ(基準画像合成済み)。2台検出していますね(正解はパトカーの1台のみ)。
ここでのポイントは左下の赤と白の車両は人間の目にはくっきり見えてますが、YoloV3はcarやtruckと認識していない点。これらは本来は共通画像として残らないはずですが、車両の影や環境光の反射で差分の領域が大きくなってしまったため、ピクセルがスカスカの状態でなんとなく残っているんですね。こういう状態のものを**「ゴースト」**と個人的に呼んでいます。
幸いなことに、YoloV3はゴーストを検出しない。しかも赤い車とバスのゴーストが重なってぐちゃぐちゃになると、さらに認識しない。しかし問題点もあり、ゴースト同士がうまい具合に重なると車両として認識してしまう。今回、パトカー以外に認識している車がそれですね。ただ検出スコアはパトカーが0.94に対してゴーストは0.54と低め。よって**今回はYoloV3の検出閾値を0.65以上とかに設定すると、ゴーストによるノイズは除去できそう。**ゴーストの根本解決が必要な場合は、差分画像を作成する段階で例えばMask R-CNNでセグメンテーションして、車両の輪郭だけ切り抜くとかの対応が必要になりそうです。でもこの実装だと処理時間かかるしなー。
###渋滞時のゴースト対策
10分間で1mも進まないようなスゴイ渋滞の時は、どうなるか?結論から言うと、ゴーストがリアル(YoloV3的に)に発生して対策は難しい。
スゴイ渋滞をビデオ撮影する趣味はないので、過去と現在の時間差を短くしてテストしてみました。過去画像と現在画像はこんな感じ。一番左の車線はほとんど動きがありませんね。
共通画像を作ってYoloV3で検出し、基準画像と合成するとこうなります。
ここでは4台検出していますが、正解はもちろんパトカーの一台だけです。一番左の赤い車なんてゴーストが重なっちゃってタイヤが3対ありそうだしナンバープレートも2枚あるし人間の目には「車のようで車ではない」と感じますが、YoloV3はスコア0.77でcarと検出しています。他にも左から2番目の車線ではcarを2台検出していて、スコアは上から0.58と0.71。うーむ、YoloV3のスコアが0.65以上とかにしてもダメですね。
ただよく考えると、故障車や不法駐車の検出を目的とすると、4台もそのような車が同時発生するのはそもそもおかしいので、「検出車両が3台以上の時は渋滞を疑う」というフラグを付けてもいいかもしれません。あ、でも玉突き事故とかあるか…。その場合は、「同一車線外に」という条件を付けるか。車線の検出は速度検出の時に使ったマスク画像を使うと簡単にできそうだし。でも条件が複雑になると嫌ですねえ。
###ゴーストが分裂して実体化!
上記の例では差分や共通画像を求めた際に、背景は白で塗りつぶしています。白を選択したのは何となくですが、日本では白っぽい車が多いので、うまくいくことが多いように思います。ただ今回テストした映像は昼間に撮影したものなので、夜間の映像ならどうなの?と思って背景色を黒にしてテストしてみたところ、過去画像と現在画像の差分はこうなります。
これで共通画像を作成すると、なぜか消えるべき車両が増えてしまう。背景色が白の場合はもちろん増えないので、**「背景色が黒で車両の色も黒の時はゴーストが分裂して実体化する」**ということになりますね。これは逆をいうと今回の黒い車が白い車だったときに同じことが発生する可能性がある。差分を作るときは、背景を白とか黒とかではなく透明にした方がいいかなあ?でもそうすると容量のでかいPNG専用(1枚3MBくらい?)になっちゃって、JPG保存できない。撮影タイミングが10分毎でもカメラ100か所でログを1か月とか保存するとストレージをかなり圧迫しますね。うーん、解決方法を考えよう・・・。
#まとめ
という訳で、課題はいくつかあるものの基本的な実装は成功しました。
今回は一連の映像からの交通量調査というテーマからは少し外れてますが、興味深いテーマだったのでトライしてみました。背景差分法とかYoloV3とかいろんな便利道具が使えるので、組み合わせをいろいろ考えるのは楽しいですね。本当は前の記事で「ディープラーニングで映像から速度を正確に算出するのに苦労した」の検証としてスピードガンで実測値を取ったことの記事を先に書かなきゃいけないんですけど、ちょっと飽きてきたので気分転換でこっちを先に書きました。他にも車間距離の算出や異なる地点で撮影した映像から、同じ車両をピックアップする方法などいろいろとネタはありますので、頑張って記事にしますね。最後までお読みいただきありがとうございました。
#関連記事
・[ディープラーニングで映像から速度を正確に算出するのに苦労した]
(https://qiita.com/ComputerVision/items/d5358632209c67dab325)
・ディープラーニングによる映像解析で車両をトラッキングするのに苦労した
・ディープラーニングで交通量調査の映像解析精度を上げるのに苦労した
・SSD_KerasをWindowsで使って交通量調査できるか試してみた
・YOLO V3を使った交通量調査ソフトを作って苦労した