Deep Learning で航空写真から自動車をカウントする

# TL;DR

* COWCと呼ばれるデータセットを用いて、航空写真から自動車をカウントする畳み込みニューラルネットワークを学習した
* 物体検出によって個々の自動車を検出するのではなく、画像からダイレクトに自動車の台数を予測するアプローチの有効性を検証した
* 特に自動車が多い状況下での精度に改善の余地があるものの、それなりに正しく自動車をカウントできるモデルを学習することができた
* [Code on GitHub](https://github.com/motokimura/cowc_car_counting)

![example_00.png](https://qiita-image-store.s3.amazonaws.com/0/156561/ae61880f-3f61-3aa4-e063-efd079fcbdf3.png)

# はじめに

[「Deep Learning を使って衛星画像から建物を検出する」](https://qiita.com/motokimura/items/a84711761f2aeb132a84)でも触れましたが、今、**機械学習による衛星画像および航空写真の自動解析**に注目が集まりつつあります。

今回は、COWCと呼ばれるデータセットを使って、**航空写真から自動車の台数をカウントする畳み込みニューラルネットワーク(CNN)**を学習してみます。

なお、今回使用したソースコードはすべて[GitHub](https://github.com/motokimura/cowc_car_counting)で公開しています。

## 関連研究と応用例

株式会社PASCO(国内の大手測量会社)が、衛星画像から自動車の台数をカウントするCNNを開発しています(→PASCOの[PDF資料](https://www.pasco.co.jp/press/2017/download/PPR20171030J.pdf))。

記事中では、**"国内外の港湾のモータープールに駐車している輸出入台数を観測することで、高潮等の被害を最小限に抑え、且つ被災時の影響を迅速に把握する"** などの活用事例が紹介されています。

それ以外にも、地上での直接計測を必要とせず、

* **工場や娯楽施設において車の出入りから稼働能力を割り出す**
* **港湾の駐車車両の台数から車の輸出状況などの経済指標を推定する**

などの用途も考えられるでしょう。

# COWCデータセット

学習と評価に用いるデータセットCOWCについて簡単に説明します。

**COWC(Cars Overhead With Context)**は、車1台1台の位置がアノテーションされた複数の航空写真からなるデータセットです。以下のような特徴があります：

* 航空写真の地上分解能15cm(1ピクセルが地上の15cmに相当する解像度)
* カナダ(Toronto), ニュージーランド(Selwyn), ドイツ(Potsdam/Vaihingen), アメリカ(Columbus/Utah) の4ヶ国/6都市にまたがるデータ
* 航空写真に写っている一般自動車 **32,716台** を点としてアノテーション
* 自動車と間違いやすそうな物体58,247個も負例として同様にアノテーション

COWCの一部を下図に示します。左は航空写真の一部を切り取ったもの、中央は航空写真に自動車のアノテーションをプロットしたもの、右は負例のアノテーションをプロットしたものです。個々の自動車と負例の中心が、それぞれ点としてアノテーションされています。
![a2.png](https://qiita-image-store.s3.amazonaws.com/0/156561/9256e2f4-4bb4-00f7-95fc-21a1f9bd53fd.png)

[公式HP](https://gdo152.llnl.gov/cowc/)から、COWCデータセットと解説論文(ECCV2016にて発表)にアクセスできます。

# 手法

CNNを使って航空写真から自動車をカウントする具体的な方法を説明します。

COWCの航空写真の大きさは縦横数千ピクセル程度とCNNで扱うには大きすぎるので、sliding-windowによって**固定サイズのパッチ画像を切り出してCNNに入力することで、そのパッチ内に存在する車両の台数をカウントする**ことにします。

パッチ画像内における車をカウントする方法として最初に思いつくのは、Faster-RCNNやSSDなどの物体検出系のCNNを用いて個々の車を検出するアプローチでしょう。

しかしながら、普通に物体検出をするだけだとあまり面白くないので、**写っている車の台数に応じてパッチ画像を分類する**というアプローチを取ることにします(特定の駐車場や道路上の車両をカウントするという用途を想定するのであれば、パッチ画像内に存在する車両の台数さえわかればよいので必ずしも個々の自動車を検出する必要はありません)。

検出ではなく**分類**によって自動車をカウントする意味について一つ捕捉しておくと、(単純に面白そうだからという理由以外にも)**アノテーションコストが削減できる**という利点があります。解像度の高い航空写真や衛星画像は一般に高価であり、解像度が低い画像(例えば、1ピクセルあたり1m程度の画像)を使わざるを得ない場合があります。このような**低解像の画像では自動車のbounding boxを正確にアノテーションすることは困難ですが、自動車の大体の位置を"点"としてアノテーションすることは比較的容易です**。分類モデルを学習する際には画像内の自動車の台数がわかればよいので、後者の"点"のアノテーションさえあれば学習を行うことができます。

# データの準備

CNNを学習および評価するためのデータについて、その作成方法を説明します。

## 学習データ(train/validation)とテストデータの分割

まず、6都市のデータを学習データ(train/validation)とテストデータに分割する方法について説明します。各都市におけるラベル付けがされている車両の台数を計算したところ、以下のような結果が得られました。
![ダウンロード.png](https://qiita-image-store.s3.amazonaws.com/0/156561/cde4c0db-2174-5a2f-f90a-fe64a5525da1.png)

一番左のグラフが、各都市でラベル付けがされている自動車の台数を棒グラフで表したものです。これを見ると、Utahのデータに全体の半数以上の自動車が集中していることがわかります。テストデータのボリュームは大きくしておきたいので、思い切ってこの**Utahのデータをテストデータとし、それ以外の5都市のデータを学習データ(train/validation)として使用**することにしました。

## 学習データの作成

次に、Utah以外の5都市のデータからCNNの学習データ(パッチ画像と正解ラベル)を作成する方法について説明します。なお、以降で説明するパッチサンプリングの方法は[COWCの元論文](https://gdo152.llnl.gov/cowc/mundhenk_et_al_eccv_2016.pdf)での提案を踏襲しています。

前述のとおり、COWCでは車両および負例の中心位置がそれぞれラベルとして提供されます。これらの**車両および負例の位置を中心として、航空写真から固定サイズのパッチ画像を切り出します**。このパッチ画像とパッチ画像内に含まれる車両の台数(=正解ラベル)をCNNの学習データとします。

また、学習データをtrainデータ(CNNの重みを更新する用のデータ)とvalidationデータ(CNNの重みの更新には使わず、学習をどこで打ち切るかを決めたり、ハイパーパラメータの決定に使うデータ)に分割するため、**航空写真上で1024ピクセル間隔のグリッドを定義し、そのグリッドを構成するセル(1024×1024ピクセル)を3:1の割合でtrain/validationに割り当てる**ことにしました。trainセルに属する車両・負例から抽出されたパッチ画像はtrainデータに、validationセルに属する車両・負例から抽出されたパッチ画像はvalidationデータに割り当てられます。

(特に車両や負例が密集しているような場所などで)**trainパッチ同士もしくはvalidatinパッチ同士が一部オーバーラップすることは許容しますが、trainパッチとvalidationパッチがオーバーラップを持つことが無いように両パッチを抽出**します(下図を参照)。
![grid.png](https://qiita-image-store.s3.amazonaws.com/0/156561/29a0b5f2-a07b-af27-2af9-0db9c665ea8e.png)
(上図：[COWCの元論文](https://gdo152.llnl.gov/cowc/mundhenk_et_al_eccv_2016.pdf)より引用。青い部分/赤い部分がそれぞれtrainパッチ/validationパッチの抽出箇所)

**CNNに入力するパッチ画像の大きさですが、試行錯誤の結果、縦横96ピクセルと設定**しました。1ピクセルがおよそ15cmほどの解像度なので14.4m四方に相当することになります。ただし、学習時のデータ拡張としてランダムクロップを行うため、trainパッチのサイズは上下左右端にそれぞれ8ピクセルの余裕を持たせた縦横96+8×2=112ピクセルとして切り出すことにします(validationパッチはランダムクロップを行わないため縦横96ピクセル)。

また、パッチ画像内の自動車の台数の数え方(=正解ラベルの定義の仕方)ですが、確実に視認できる車両のみをカウントしたいので**画像の左右端・上下端から8ピクセル(=1.2m)よりも内側にある車両のみをカウント**することにしました。

# CNNの設計と学習

今回は、**ResNet50**を学習させてみます。
以降では、分類クラス数の設定と学習上の設定などについて詳細を説明します。

## 分類クラス数の設定

準備した学習データの分布から、分類クラス数(パッチ内でカウントできる最大の車両数)を設定します。

trainデータについて、正解ラベル(パッチ内の有効領域における車両の台数)の分布を可視化すると下図のようになります。車両台数が多くなるにつれてパッチの数は急激に減少しており、かなりいびつな分布になっていることがわかります。
![histogram2.png](https://qiita-image-store.s3.amazonaws.com/0/156561/911226fe-1cf3-43cf-65e6-b917fe08c38e.png)

特に、10台以上車両が存在するパッチの数は20を下回っており上手く学習ができる可能性が低いため、10台以上車両が存在するパッチについては9台のクラスとしてまとめてしまうことにします。つまり、**分類クラスは0~9台までの10クラスとして設定**することになります。

## 学習

学習を効率的に行うため、ImageNetでpretrainしたモデルを用いて(最後の全結合層を除く)ResNet50の重みを初期化したあと、前述の学習データによってResNet50の学習を行います。

学習時、最適化手法としてMomentum SGDを使用しました。学習率は0.01からはじめて40エポックで0.001に下げ、さらに60エポック目で0.0001まで下げて80エポックまで学習を行いました。momentumは0.9に設定しました。また、バッチサイズは32に設定し、データ拡張として画像の左右・上下のランダム反転およびランダムクロップを行います。以上の実装にはChainerを使用しました。

以下にTensorBoardに出力した学習曲線を掲載します。

![tensorboard_00.png](https://qiita-image-store.s3.amazonaws.com/0/156561/e7e04c33-9c9a-49ea-2bb2-7c702c5c900f.png)


およそ70エポックほどでvalidationデータに対するaccuracyがピークに達したので、70エポックまで学習したモデルを用いてテストデータに対する評価を行います。

# 評価

Utahのテストデータを用いて学習したモデルの評価を行います。

なお、テストデータの航空写真上で車両のカウントを行う際には、**sliding-widnowによって航空写真から96×96ピクセルのパッチ画像を切り出してCNNに入力**します。ただし、前述のとおり画像の上下左右端8ピクセルに不感領域が設けられている関係で、**stride=96-8×2=80ピクセルとしてパッチを切り出していきます**。

## 定性評価

Utahのテストデータの中でも特に自動車が多く分布している Salt Lake City 付近の航空写真について、カウントした結果を可視化してみました。

![ダウンロード.png](https://qiita-image-store.s3.amazonaws.com/0/156561/120053ae-b35a-4c08-c511-b80e407590fc.png)

上図では、CNNによってカウントされた自動車の台数をヒートマップで表しています。駐車場などの自動車が多く分布していそうな場所がハイライトされているように見えます。

この一部を拡大して、さらにカウントされた自動車の台数を表示してみます(下図)。

![ダウンロード (2).png](https://qiita-image-store.s3.amazonaws.com/0/156561/33fc7224-0b12-8268-8402-cf5d50b40d31.png)

![scene_05.png](https://qiita-image-store.s3.amazonaws.com/0/156561/249cbbe3-1726-0437-b62d-1d690df8ba28.png)

![scene_02.png](https://qiita-image-store.s3.amazonaws.com/0/156561/73d9e910-b577-696f-7690-4cf59bb5241a.png)

自動車が特に多く分布している駐車場のシーンをピックアップして表示していますが、おおむね正しく自動車をカウントできています。また、建物の屋根に存在する突起や路上の障害物など、自動車と見間違いやすそうな物体の誤カウントも上のシーンではほとんど見られません。

ただし、パッチ毎に分類を行うというアプローチの欠点として、**パッチとパッチの切れ目に自動車が存在する場合、同じ自動車がダブルカウントされたり、どちらのパッチでもカウントされなかったりする**ことがあります。このくらいの精度でも長期的なトレンド(台数の相対的な変化)を把握する等の用途では十分な気もしますが、より精度を求めるのであれば個々の自動車を検出できる物体検出系のCNNを使うのが良いのかもしれません。

## 定量評価

以下の表に、Utahの航空写真それぞれに対する評価結果と、それら全体に対する評価結果をまとめています。
表を見ると、分類精度(Accuracy)が97％を超えており非常に高精度なモデルに思えます。が、これは全クラスに対する平均分類精度であり、テストデータのパッチの大多数は自動車がゼロ台のパッチなので、パッチを常にゼロ台のクラスに分類してもそれなりに精度が出てしまいます。
![acc2.png](https://qiita-image-store.s3.amazonaws.com/0/156561/1c566da0-3793-b66c-f1d4-2a4a508b78b7.png)

自動車が1台以上存在するパッチを正しく分類できているか確認するため、混同行列(confusion matrix)を計算してみます(下図)。混同行列を見てみると、**自動車がゼロ台のパッチに関してはかなり高い精度で分類ができていますが、自動車の台数が多いパッチほどその周辺のクラスに誤分類されやすい**傾向が見られます。
![confusion.png](https://qiita-image-store.s3.amazonaws.com/0/156561/ce3256f1-c7be-3b43-3eab-61d1013d1bb9.png)

誤分類の傾向をさらに詳しく見るために、自動車の台数(正解クラス)毎に、ネットワークによって予測(分類)された自動車台数の平均値と平均二乗後誤差(RMSE)を集計してプロットしてみました(下図)。
![err_fig.png](https://qiita-image-store.s3.amazonaws.com/0/156561/d21fa4ac-f6e5-2ca6-e33f-6cae3b10f51c.png)

これを見てみると、やはり**自動車の台数が増える毎にばらつき(RMSE)が大きくなっており、かつ台数を過小評価(under estimate)する傾向が強くなっている**ことがわかります。

原因としては、**自動車の台数が多いパッチ画像が学習データにあまり含まれていない**ことが考えられます。今回のような画像分類のアプローチでもっと高精度にカウントを行うためには、少数クラスに対するデータ拡張を行うなどしてデータの分布の偏りを低減したり、学習時のミニバッチ内のクラス分布をコントロールしたりするなどの工夫が必要になりそうです。

# まとめと課題

COWCデータセットを用いて航空写真から自動車の台数をカウントするCNNを学習しました。今回は、画像内の自動車の台数に応じて画像を分類するCNN(ResNet50)を学習・評価し、(個々の自動車を検出するのではなく)画像からダイレクトに自動車の台数を予測する手法の有効性を検証しました。

現状、駐車場の稼働状況のトレンド(台数の相対的な変化)を把握するなどの用途としては使えそうな精度を得られていますが、特に自動車の台数が多いときに台数を過小評価してしまう傾向が見られました。台数を過小評価してしまう原因として、学習データが非常に偏った分布になっていることが挙げられます。今後は、このような偏った分布のデータで上手く分類モデルを学習する方法を模索するのと並行して、物体検出系のCNNを使ったときにどのくらいの精度が出るのかを検証したいと思います。

# (おまけ) Class Activation Mappingによる可視化

CNNが画像のどこに着目して分類を行ったのかを可視化する手法として、[Class Activation Mapping (CAM)](http://cnnlocalization.csail.mit.edu/) が提案されています。

実際、自動車に着目して分類できているかどうかを検証するため、CAMを使った可視化を行ってみました(下図)。以下にお見せしている結果はごく一部ですが、全体的に自動車が集まっている箇所に着目して分類を行えています。
![cam_00.png](https://qiita-image-store.s3.amazonaws.com/0/156561/c79fd43b-ab30-881f-e3af-7cee15d93c9c.png)
![cam_01.png](https://qiita-image-store.s3.amazonaws.com/0/156561/97af16f9-503d-25aa-775e-bb0985cb914d.png)
![cam_02.png](https://qiita-image-store.s3.amazonaws.com/0/156561/824aa769-88f8-c22a-ae1c-402eacf8fee5.png)
![cam_03.png](https://qiita-image-store.s3.amazonaws.com/0/156561/758886d8-6814-ac1c-aef7-50d9837ba64b.png)

期待どおりの結果ですが、学習時には自動車の台数しか与えていないのに(＝自動車1台1台の位置は与えていないのに)、しっかりと自動車に着目して分類が行えているというのは面白いですね。