AIで歩行者量（通行量）調査をするシステムを設計したら苦労した（編集中）

![Test02_Title_S.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/140663/d11e2b86-fbc0-8065-d98c-d06e1e4dbddd.jpeg)

#サマリー

**※(2020/3/7更新)　本記事は[AIで通行量調査の映像解析精度を上げるのに苦労した](https://qiita.com/ComputerVision/items/fc64317006c25f37cc3e)の設計に特化したものですが、書いてる途中に機能追加や仕様変更を行ったため、内容が古くなっています。余裕を見て書き直しますので、参考程度だと思ってください。**

　ここではAI技術を応用した通行量調査（歩行者量のカウント）システムを開発した時に、特に設計面で苦労した話を書いてます。書いてる途中にボリュームが大きくなったり、問題解決など異なる視点でも書いてみたくなったので、複数記事に分けることにしました。本記事の内容は以下の通りです。
**・交通量調査と歩行者通行量調査の違い
・基本的なカウント方法の設計
・詳細なカウント方法の設計とノイズ対策
**

#サンプルムービーとシステム概要
通行量調査の***サンプルムービーは[こちら。](https://youtu.be/rQEvaJ_Nz7I)***
トップ画像はサンプルムービーのスクリーンショットです。システムのベースとしたのはGitHubで1.1KのStarを集めた[「deep_sort_yolov3」](https://github.com/Qidian213/deep_sort_yolov3)です。
システムの概要としては、撮影済みのムービーファイルを読み込み、VOLO V3で歩行者（クラスはperson）を物体検知してdeep sortでトラッキングしてます(トラッキング方法はdlibも試してます)。
水色と黄色のラインは本記事で「カウンターライン」と呼んでいるもので、これを歩行者が横切ると歩行者をカウントします。カウンターラインが２本あるのは、黄色の点字ブロックの上と下に分けて方向別に通行量を調べたかったからです。ただしちょっと黄色いカウンターラインの位置が良くなかったかも？

#まずは先行システムの調査
すでに良いシステム（特に無料公開されているもの）があれば作っても無駄なので、先行しているシステムを調査しました。
日本では[「歩行者通行量調査はAIに代替できるか！？ AIUEOが実証実験」](https://ainow.ai/2019/04/05/167072/)の記事がすぐヒットしますね。GitHubで古いところだとStar85を集めた[Pedestrians detection and tracking using OpenCV on Python](https://github.com/blacksteed232/PedestrianCounter)などがあります。これらに共通するのは画面内にラインを引いて、それを超えたときにカウントしているみたいですね。これは交通量調査用のソフトを調べたときも同じ傾向がありました。ただ調べてもそのものズバリのソフトが無料公開されているわけではなさそう。


#やってみて分かった交通量調査と通行量調査の違い
交通量調査は車両のカウントです。以前の私の記事[「ディープラーニングで交通量調査の映像解析精度を上げるのに苦労した」](https://qiita.com/ComputerVision/items/2b5e8ed97610085a20c2)でも書いておりますが、なんだかんだ言って車両は基本的には車線を守ってまっすぐにしか走りません（日本では）。それでも[トラッキングには苦労した](https://qiita.com/ComputerVision/items/d865f084c98a59349910)のですが、今から思えば歩行者に比べるとトラッキングは楽勝でした。
では歩行者でどの点に苦労したかというと、
**・車両に比べて画面内の密度が固くそれぞれのサイズが小さい(物体検出しにくい）
・移動方向が定まらない（皆が一定方向に歩く品川駅の映像は特別だと思ってください）
・対象のサイズがマチマチで集団行動しやすい（子連れの家族などは重なって個別検出できない）
・移動方向の自由度が高い（店先で待っているお客の列や、それを避ける歩行者など）**

これらの「歩行者困ったな」エッセンスがぎゅーと詰まったのが、***以下の[映像](https://youtu.be/RAW2rVGEcNo)です。***どのようにして問題点を解決したか、説明します。
![Test03_S.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/140663/afb9fbca-9887-3523-d0e9-f017db654ce2.jpeg)

#基本的なカウント方法の設計
物体検出とトラッキングはdeep_sort_yolov3を使えばできるので、ここでは通行量をカウントするにあたって「どのような方針で」カウントするように設計したかを述べます。
それは「完全なトラッキングをあきらめた」。はい、これに尽きます。
本来であれば、映像内に登場した歩行者が画面外に消えるまで、同一IDでトラッキングし続けるのが正しく美しいのですが、deep_sortのパラメータをいくらいじってもカルマンフィルタ使っても、重なったり見えなくなったりで歩行者を正しくトラッキングし続けるのはムリでした。そこで方針を転換して、以下のようにカウントするようにしました。
**・移動している歩行者をカウント対象とする
・画面内にラインを最小限引いて、それを超えたときにカウントする
・ラインをどのように超えるかで方向別にカウントする
・誤検出やトラッキングミスによるカウントノイズを除去する**

これはつまり画面の中でぼーっと立っている（動かない）歩行者はカウントしない、という大胆な切り捨てですね。ライン（この記事ではカウンターラインと称しています）方式を採用したのは、「ラインを超える前後だけ正しくトラッキングできるようにしよう」と割り切ったためです。
ただしライン方式だと画面内に複数カウンターラインを設置することで、部分領域の通行量を調べることができるかもしれない。記事トップの画像にあるように、通路を上下で分けたりね。

#詳細なカウント方法の設計とノイズ対策
もう少し細かく説明すると、歩行者のカウントは以下のように行ってます。
**・前の映像フレームと現在のフレームで同じIDを持つトラッカー枠の組合せを探す
・同一IDのトラッカー枠の組が見つかったら、枠の中心点をそれぞれ結んで軌跡とする
・軌跡がカウンターラインと交差したらカウントする
・軌跡にベクトルを持たせてカウンターラインをどの方向で越えたか記録する**

実際には軌跡はこんな感じになります。
![Test04軌跡サンプル抜粋.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/140663/a24de303-9271-ffe1-653b-8cbb47af953e.jpeg)
歩いている方向が同じだと、だいたい同じような方向を向いた軌跡が描画されます。ただしそれはトラッカーが美しく正しくトラッキングしてくれた場合に限ってであり、混雑した映像だと近くにいたまったく別の歩行者を「同じIDだ」と言い張ることが多いです。ムービーを見ても、混雑している場所では新しく登場した歩行者が画面外に消えるまでまったく同じIDのまま--というのは珍しいです。

そこでカウント時のノイズ対策として以下の条件を設定しました。
**・軌跡がカウンターラインと交差する際の角度に閾値を設ける
・軌跡がカウンターラインと交差する際の長さに閾値を設ける
・同一IDは複数回カウンターラインを通過しても1度しかカウントしない
・トラッキングのマッチング対象となる除外領域を設定する**
それぞれを詳しく解説してみましょう。

###軌跡がカウンターラインと交差する際の角度に閾値を設ける
最初に疑問に感じるとしたら、「どうやってお手本の角度を設定するのか？」です。
軌跡の角度から、だいたいの「正しいと思われる角度」の推定ができるため、ここでは「カウンターライン」に対して「方向ライン」というものを設定します。方向ラインは両端に方向名をそれぞれ1つ持っています。
![カウンターラインと方向ライン_説明付.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/140663/49775b20-9db6-adb5-771d-2ab13cdcc075.jpeg)

そして上の図でお分かりかと思いますが、カウンターラインと方向ラインの交差角が「歩行者がカウンターラインと交差するときのお手本の角度」になっています。いちいち角度計ったりするのは面倒なので、設定ファイル上ではカウンターラインと交差ラインの始点終点のXY座標を指定するだけです。

そして「お手本の角度」が設定できたら、次は角度の閾値を設けます。
![カウンターラインとの交差角閾値説明.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/140663/b978c4ca-d5b0-a7f8-1923-949a967c9a00.jpeg)
上の図では緑色の軌跡(1)～(6)のうち、カウントするのは(3), (4), (5)の3本だけです。
(1)は交差角度の閾値を満たしておらず、(2)と(6)はそもそもカウンターラインと交差していないので、カウント対象外になっています。

なおこの交差角度の閾値設定ですが、どのような映像にも適用できるわけではありません。歩行者が進む方向がある程度決まっているものには有効ですが、進行方向がランダムの場合は使えないと思います。また私の設計では、１つのカウンターラインに設定できる方向ラインは1つまでとしているため、方向別に集計したいような場合はカウンターラインを複数設定して少しずつ方向ラインの交差角度を変えて、「その閾値の方向ラインでカウントできたか？」を調べると、進行方向の角度特定ができそう。

###軌跡がカウンターラインと交差する際の長さに閾値を設ける
軌跡の長さでカウントする・しないを制限するのは、シンプルな理由からです。そもそも軌跡の長さとは、歩行者の移動速度を意味しています。どの歩行者も映像上のフレーム差分は一定なので、軌跡が短い＝速度が遅い・軌跡が長い＝速度が速いと言えます。
歩行者は立ち止まることがあるのでカウンターライン付近で軌跡が短いことは除外対象にはなりませんが、長すぎる軌跡はトラッキングミスの可能性が高いです。例えばトラッカーが遠くにいる別の歩行者と誤ってマッチングさせたような場合です。これは排除した方がカウントミスを防げますね。
ただ問題となるのは「どれくらいの長さの軌跡を除外するか？」ですが、私はログに軌跡の長さと角度を出すようにして、そこから有効な値の範囲の平均値を求めるようにしました。解析1回分は無駄になってしまいますが、こちらの方が合理的だと考えています。

###同一IDは複数回カウンターラインを通過しても1度しかカウントしない
![ユニークIDカウント_S.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/140663/8cafbe5a-8d2e-a2db-72de-1cb0a067b61e.jpeg)
上の画像では「水色のカウンターライン近くに入店待ちの行列ができている」という状況になっており、待っている人が少し歩を進めるたびにカウンターラインをジグザクに超えて多重カウントが発生してしまうですね。他にも清掃の係員が行ったり来たりする場合もあります。これらの状況では同一IDによる複数回カウントを除去する・しないを設定できた方が良いですね。ログに「どのIDでカウントしたか？」を残せば、重複チェックは簡単に実装できます。
ただ私は「カウンターラインが複数ある場合、同じIDでもカウンターラインごとに別カウントする」という設計にしました。たぶんこれは歩行者が完全にトラッキングできた場合に、カウンターラインを複数設定することで経路を特定できるのでは？・・という夢を捨てきれていないせいですね。

###トラッキングのマッチング対象となる除外領域を設定する
![Test05_S.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/140663/a6183dab-4618-81d6-a2fd-6a07345f8d24.jpeg)
これはマスク画像を設定して、その領域では軌跡を描かないようにしてトラッキングミスを低減させる、という作戦です。
「カウンターラインを超えるときだけ正しくトラッキングできればいい」を体現しているような仕様ですね。
上の画像ではグレイのオーバーラップ部分が除外領域になっており、この部分では軌跡を描画していません。またこの画像ではマスクによる除外領域を分かりやすく見せるため、マスク画像を映像にオーバーラップさせています。***映像は[こちら](https://youtu.be/IWlByICDsxg)***。
実をいうとこういった映像で除外領域を設けるメリットというのはあまりないのですが、例えば自動改札など「一人ずつ通過する」ようなシーンで特定の出口のみカウントしたいような場合には、威力を発揮すると思います。カウンターラインである程度の絞り込みはできますが、除外領域を設けてトラッキングミスを減らし、さらに軌跡の角度や長さで有効カウントを絞り込んでいくことで、より精度アップにつながる（のではないかな？）と考えています。


最後までお読みいただき、ありがとうございました。