pix2pix でタイル地図を変換してみた！

# 概要
* [pix2pix-tensorflow](https://github.com/affinelayer/pix2pix-tensorflow)で地図タイルを扱うためのツールを公開しました → [pix2pix for Map tiles](https://github.com/naro-41605)
* 学習データとして，地図タイル，WMS等を指定できます．また複数のレイヤを学習データとして指定することができます．
* サンプルとして，二時期，計四種類のLandsat7号の画像と地理院標高PNGタイル画像を学習データとし，教師画像を植生図とした変換を行ってみました．

# はじめに
[pix2pix](https://github.com/phillipi/pix2pix)は，ご存じの通り画像から画像への変換を行うDeep Learningの1つです．一対の画像を準備することにより，画像間変換が可能となります．pix2pixのサンプルでも空中写真から地図への変換例ががありあす。

<img width="786" alt="pix2pix sample" src="https://github.com/phillipi/pix2pix/raw/master/imgs/examples.jpg">
pix2pixでの画像変換例。左下が空中写真から地図への変換です。

この変換のための画像を準備するのが大変なのですが，地理空間情報では，地図タイルという形で256*256の画像が大量に整備されています．
そこで，地図タイル画像を[pix2pix](https://github.com/phillipi/pix2pix)で簡単に扱えるようにするツールをつかって，衛星画像と標高データから，植生図への変換を試みました．

# [pix2pix for Map tiles](https://github.com/naro-41605)
[pix2pix for Map tiles](https://github.com/naro-41605)は，[pix2pix-tensorflow](https://github.com/affinelayer/pix2pix-tensorflow)で地図タイルを扱うための各種ツールです．特徴としては，

* WEB上で公開されている地図タイルのアドレスと`{Z}/{X}/{Y}.ext`を指定することにより，学習用のデータを生成できる
* 学習用データとして，複数のソースを指定することができる．

という点があります．現在公開しているツールは，

DataSetMake_tfwiter.py：地図タイルやWMSから学習のためのデータを生成するスクリプト．
pix2pix_multi.py：上記で作ったデータを持ちいて，学習を行うためのプログラム．

の2つです．

# [pix2pix for Map tiles](https://github.com/naro-41605)をつかった画像変換例
## 使用した画像
 [pix2pix for Map tiles](https://github.com/naro-41605)を使った変換を行ってみました．学習データとして使用したのは，

* Landsat7号PanSharpend画像 Path:107 Row:035 2000年5月16日撮影 [産総研LandBrowser提供](http://landbrowser.geogrid.org/landbrowser/index.html)
* Landsat7号band62画像 Path:107 Row:035 2000年5月16日撮影 [産総研LandBrowser提供](http://landbrowser.geogrid.org/landbrowser/index.html)
* Landsat7号PanSharpend画像 Path:107 Row:035 2000年12月26日撮影 [産総研LandBrowser提供](http://landbrowser.geogrid.org/landbrowser/index.html)
* Landsat7号band62画像 Path:107 Row:035 2000年12月26日撮影 [産総研LandBrowser提供](http://landbrowser.geogrid.org/landbrowser/index.html)
* [国土地理院標高PNGタイル](https://maps.gsi.go.jp/development/demtile.html)

の五種類で，教師データは，

* [環境省第六～七回植生図](http://gis.biodic.go.jp/webgis/sc-006.html)　[エコリス地図タイル提供](http://map.ecoris.info/)

です．学習に用いた範囲は，地図タイルで「Z:12，X:3635～3648，Y:1600～1613」になります．LandBrowserではWMSで提供されたデータを，国土地理院標高PNGタイルでは地図タイルとして提供されるデータを使用しています．

以下に，学習に使用したデータのイメージを表示します．

<img width="786" alt="2017-10-15 15_28_42-input_image0.png ‎- フォト.png" src="https://qiita-image-store.s3.amazonaws.com/0/39797/da666e31-2091-b17e-62b5-e4054f324083.png">
Landsat7号PanSharpend画像 Path:107 Row:035 2000年5月16日撮影 [産総研LandBrowser提供](http://landbrowser.geogrid.org/landbrowser/index.html)

<img width="786" alt="2017-10-15 15_30_06-input_image1.png ‎- フォト.png" src="https://qiita-image-store.s3.amazonaws.com/0/39797/a91152f6-7891-c90f-44d8-37e1e86e0988.png">

Landsat7号band62画像 Path:107 Row:035 2000年5月16日撮影 [産総研LandBrowser提供](http://landbrowser.geogrid.org/landbrowser/index.html)

<img width="786" alt="2017-10-15 15_30_14-input_image2.png ‎- フォト.png" src="https://qiita-image-store.s3.amazonaws.com/0/39797/b6bbd217-f2d1-3666-4002-e54a9ab23413.png">
Landsat7号PanSharpend画像 Path:107 Row:035 2000年12月26日撮影 [産総研LandBrowser提供](http://landbrowser.geogrid.org/landbrowser/index.html)

<img width="786" alt="2017-10-15 15_30_21-input_image3.png ‎- フォト.png" src="https://qiita-image-store.s3.amazonaws.com/0/39797/a6917a88-ad36-7937-9c4a-f662142a7512.png">
Landsat7号band62画像 Path:107 Row:035 2000年12月26日撮影 [産総研LandBrowser提供](http://landbrowser.geogrid.org/landbrowser/index.html)

<img width="786" alt="2017-10-15 15_30_27-input_image4.png ‎- フォト.png" src="https://qiita-image-store.s3.amazonaws.com/0/39797/09b8b821-0187-7d1b-d89d-2b565e0cb2fc.png">
[国土地理院標高PNGタイル](https://maps.gsi.go.jp/development/demtile.html)

<img width="786" alt="2017-10-15 15_30_32-target_image.png ‎- フォト.png" src="https://qiita-image-store.s3.amazonaws.com/0/39797/876eadee-c71e-adc7-b1e6-9995b9036dc2.png">
[環境省第六～七回植生図](http://gis.biodic.go.jp/webgis/sc-006.html)　[エコリス地図タイル提供](http://map.ecoris.info/)

##学習用データの作成
学習用データの作成には，`DataSetMake_tfwiter.py`を使用します．このスクリプトの実行には，学習対象となる地図タイルのアドレスを指定したJSON形式のファイルが必要になります．今回は，`jsonLan2Vege.txt`として以下の内容で作成しました．

~~~
{
 "targetURL": {
               "url": "http://map.ecoris.info/tiles/vege67/", 
               "type": "tile", 
               "format": "{z}/{x}/{y}.png"
              }, 
 "inputURL": [
              {
               "url": "http://ows8.geogrid.org/land7uswms/LE71070352000137HAJ00?LAYERS=default&TRANSPARENT=TRUE&", 
               "type": "wms", 
               "format": "SERVICE=WMS&VERSION=1.1.1&REQUEST=GetMap&FORMAT=image/png&BBOX={miny},{minx},{maxy},{maxx}&SRS=EPSG:4612&width={output_height}&height={output_height}&ua=landbrowser"
               },
              {
               "url": "http://ows8.geogrid.org/land7uswms/LE71070352000137HAJ00?LAYERS=BAND62COLOR&TRANSPARENT=TRUE&", 
               "type": "wms", 
               "format": "SERVICE=WMS&VERSION=1.1.1&REQUEST=GetMap&FORMAT=image/png&BBOX={miny},{minx},{maxy},{maxx}&SRS=EPSG:4612&width={output_height}&height={output_height}&ua=landbrowser"
               },
              {
               "url": "http://ows8.geogrid.org/land7uswms/LE71070352000361EDC00?LAYERS=default&TRANSPARENT=TRUE&", 
               "type": "wms", 
               "format": "SERVICE=WMS&VERSION=1.1.1&REQUEST=GetMap&FORMAT=image/png&BBOX={miny},{minx},{maxy},{maxx}&SRS=EPSG:4612&width={output_height}&height={output_height}&ua=landbrowser"
               },
              {
               "url": "http://ows8.geogrid.org/land7uswms/LE71070352000361EDC00?LAYERS=BAND62COLOR&TRANSPARENT=TRUE&", 
               "type": "wms", 
               "format": "SERVICE=WMS&VERSION=1.1.1&REQUEST=GetMap&FORMAT=image/png&BBOX={miny},{minx},{maxy},{maxx}&SRS=EPSG:4612&width={output_height}&height={output_height}&ua=landbrowser"
               },
              {
               "url": "http://cyberjapandata.gsi.go.jp/xyz/dem_png/", 
               "type": "tile", 
               "format": "{z}/{x}/{y}.png"
              }
             ]
}
~~~

データの作成は，以下のコマンドで実行します．

`python DataSetMake_tfwiter.py 3635 3648 1600 1613 12 --outputPath train --inputJson jsonVege.txt`

なお，データ作成中に学習データと教師データのチャンネル数が表示されます．このチャンネル数は，以降の学習の実行，学習結果の確認で使用するので，メモしてください．

作成が成功すると，trainディレクトリ内に，`1.tfrecords，2.tfrecords，・・・・`という形で学習用データが生成されます．また，実行したフォルダには，`input_image0.png，input_image1.png，input_image2.png，input_image3.png，input_image4.png`という形で学習用データを結合した画像が，`target_image.png`として教師データを結合した画像が出力されます．

##学習の実行
学習の実行は，`pix2pix_multi.py`で`--mode train`を指定して行います．今回は

~~~
python pix2pix_multi.py --mode train --max_epochs 200 --input_dir train --input_ch 20 --target_ch 4 --output_dir classify --GPUdevice 0
~~~

として行いました．ここで，学習データは`--input_dir train`で指定し，学習結果は`--output_dir classify`に出力されます．また`--max_epochs 200`で学習回数を，`--GPUdevice 0`で学習に使用するGPUを指定します．学習データと教師データのチャンネル数は，それぞれ`--input_ch 20 --target_ch 4`として指定します．  

##学習結果の確認
学習結果の確認は，`pix2pix_multi.py`で`--mode test`を指定して行います．今回は

~~~
python pix2pix_multi.py --mode test --output_dir output --input_dir train --checkpoint classify --input_ch 20 --target_ch 4
~~~

として実行しました．ここで，`--input_dir train`は確認に使用するデータを，`--input_ch 20 --target_ch 4`は学習データと教師データのチャンネル数を指定します．`--checkpoint classify`では，学習したモデルを格納したディレクトリを指定し，比較結果は`--output_dir output`に出力されます．結果は以下のような形で出力されます．

<img width="635" alt="2017-10-15 16_10_56-Program Manager.png" src="https://qiita-image-store.s3.amazonaws.com/0/39797/5d838237-d193-145a-dcb3-44168251bcca.png">

今回の試験結果は， https://naro-41605.github.io/pix2pix_map_tiles/lad2veg/output/index.html で確認できます． 

# おわりに
今回使用したスクリプトでは，複数のタイル画像を指定してpix2pixで学習させることができます．地図タイル形式で公開されているデータであれば，様々な用途で利用できると思うので，是非活用してもらえればと思います．

また，今後，以下のような点が改善できればいいなーと，思います．

* 出力のデータ形式がRGBになっているが，例えば主題図であればRGBではなく，カテゴリー値（8bitデータとか）として分類を行うようにする．
* 学習済みのモデルを，他の地域で実行させる機能
 * たとえば，QGISで表示させた 範囲のデータを分類するとか
* 学習データとして，ベクタタイルに対応できるといいなー，とか．


# おまけ・環境構築について
## 動作確認環境
本作業は、以下の環境において行いました。

| 分類 | バージョン |
----|---- 
| OS | Ubuntu 14.04 LTR 64bit |
| フレームワーク等 | Python 2.7.6 |
|  | tensorflow 1.0.0 |
|  | CUDA 8.0 （GPU環境のみ）|
|  | CuDNN V5 （GPU環境のみ）|

## インストール方法
1.  実行に必要な各パッケージをインストールします。

~~~
sudo apt-get update
sudo apt-get install python-pip python-dev libfreetype6-dev pkg-config liblapack-dev gfortran libtiff5-dev libjpeg8-dev zlib1g-dev liblcms2-dev libwebp-dev tcl8.6-dev tk8.6-dev python-tk python-scipy

sudo pip install \
    appdirs==1.4.0 \
    funcsigs==1.0.2 \
    google-api-python-client==1.6.2 \
    google-auth==0.7.0 \
    google-auth-httplib2==0.0.2 \
    google-cloud-core==0.22.1 \
    google-cloud-storage==0.22.0 \
    googleapis-common-protos==1.5.2 \
    httplib2==0.10.3 \
    mock==2.0.0 \
    numpy==1.12.0 \
    oauth2client==4.0.0 \
    packaging==16.8 \
    pbr==1.10.0 \
    protobuf==3.2.0 \
    pyasn1==0.2.2 \
    pyasn1-modules==0.0.8 \
    pyparsing==2.1.10 \
    rsa==3.4.2 \
    six==1.10.0 \
    uritemplate==3.0.0 \
    requests\
    cython\
    pandas\
    scikit-image\
    pillow
~~~

２.  Tensorflowをインストールします。

* CPUのみを使用する場合
~~~
sudo pip install --upgrade https://storage.googleapis.com/tensorflow/linux/cpu/tensorflow-1.0.0-cp27-none-linux_x86_64.whl
~~~

* GPUも使用する場合
~~~
sudo pip install --upgrade https://storage.googleapis.com/tensorflow/linux/gpu/tensorflow_gpu-1.0.0-cp27-none-linux_x86_64.whl
~~~

３.  任意の場所に`DataSetMake_tfwiter.py`および`pix2pix_multi.py`を配置する。

以上で、インストールは完了です。

## 実行方法
### 地図タイル取得先URLの設定方法

まず、地図タイルを取得するURL指定するファイルをJSON形式で作成します。作成するJSON形式ファイルの内容は以下の通りです。

~~~
{
 "targetURL": 教師データとなる地図タイルの取得先
, 
 "inputURL": [
               入力データとなる地図タイルの取得先１
              , 
               入力データとなる地図タイルの取得先２
             ]
}

~~~

| 変数 | 説明 |
----|---- 
| "targetURL" | 教師データとなるタイルの取得先URL |
| "inputURL" | 入力する地図タイルの取得先URL。[]の中に取得先をカンマ(,)区切りで複数指定することができます。|

教師データと入力データそれぞれの地図タイル取得先を設定します。地図タイル取得先の設定については以下を参照してください。

~~~
{
"url": 地図タイル取得先のベースURL , 
 "type": 取得する地図タイルの種類 , 
 "format": 取得する地図タイルの形式 
}
~~~

| 変数 | 説明 |
----|---- 
| "url" | 地図タイルの取得先のベースURL |
| "type" | 取得する地図タイルの種類。地図タイル形式やWMTSは"tile"、WMSは"wms"になります。|
| "format" | 拡張子やタイル座標、WMTS やWMSのパラメータなどの設定。詳細については下記の各タイル取得設定例を参照してください。 |


#### タイル地図形式の地図タイル取得先設定例(国土地理院 全国最新写真（シームレス）)
~~~
{
"url": "http://cyberjapandata.gsi.go.jp/xyz/seamlessphoto/", 
 "type": "tile", 
 "format": "{z}/{x}/{y}.jpg"
}
~~~
* タイル地図形式の"format"では主としてタイル座標と拡張子やWMTSのパラメータ等を設定します。取得先の形式に従って設定してください。
* タイル座標については以下のように記述してください。
	* {z} : ズームレベル、{x} : タイルのX座標、{y} : タイルのY座標


#### WMS形式の地図タイル取得先設定例(地震ハザードステーション 地すべり地形分布図WMSサービス)
~~~
{
"url": "http://www.j-shis.bosai.go.jp/map/wms/landslide?", 
"type": "wms", 
"format": "SERVICE=WMS&VERSION=1.3.0&REQUEST=GetMap&BBOX={minx},{miny},{maxx},{maxy}&CRS=EPSG:4612&WIDTH={output_width}&HEIGHT={output_height}&LAYERS=L-V3-S300&FORMAT=image/png&TRANSPARENT=FALSE"
}
~~~
* WMS形式の"format"ではWMSのパラメータ設定を行ないます。取得先の形式に従って設定してください。
* WMSのパラメータの中で取得範囲指定を行なうBBOX、取得する画像のサイズを指定するWIDTH、HEIGHTについては以下のように記述してください。
~~~
 BBOX={minx},{miny},{maxx},{maxy}
 WIDTH={output_width}
 HEIGHT={output_height}
~~~

### 学習用データセットの作成
学習用データセットに使用する`DataSetMake_tfwiter.py`の実行形式およびパラメータは以下の通りです。

~~~
python DataSetMake_tfwiter.py "images_x_start" "images_x_end" "images_y_start" "images_y_end" "zoom_level"
                              --inputJson "INPUTJSON"
                              --outputPath "OUTPUTPATH"
~~~
| 引数 | 説明 |
----|---- 
| "images_x_start" | 指定する範囲の始点となるタイルのx方向の位置 |
| "images_x_end" | 指定する範囲の始点となるタイルのx方向の位置 |
| "images_y_start" | 指定する範囲の始点となるタイルのy方向の位置 |
| "images_y_end" | 指定する範囲の終点となるタイルのy方向の位置 |
| --inputJson "INPUTJSON" | 地図タイル取得先URLを設定したjson形式のファイルを指定します。デフォルトは"./jsonSample.txt"|
| --outputPath "OUTPUTPATH" | データセットの出力先ディレクトリの指定。ディレクトリがない場合は自動生成します。デフォルトは"Data" |

* 実行中はタイルの取得先URLと入力データのチャンネル数（input channel）、教師データのチャンネル数（target channel）等が表示されます。各チャンネル数は `pix2pix_multi.py` 実行時に入力するので確認するようにしてください。
* 実行後、--outputPathで指定したディレクトリ内に{通し番号}.tfrecordsが取得したタイルの枚数分生成されます。また、プログラムを実行したディレクトリ内に取得したタイルを繋げた、input_image{通し番号}.png、target_image{通し番号}.pngが生成されます。

### 学習用の実行
学習の実行に使用する`pix2pix_multi.py`の実行形式および主なパラメータは以下の通りです。

~~~
pix2pix_multi.py  --input_dir "INPUT_DIR" 
                  --mode {train,test,export} 
                  --output_dir "OUTPUT_DIR"
                  --checkpoint "CHECKPOINT"
                  --max_steps "MAX_STEPS"
		  --max_epochs "MAX_EPOCHS"
                  --progress_freq "PROGRESS_FREQ"
		  --save_freq "SAVE_FREQ"
		  --ngf "NGF"
                  --input_ch "INPUT_CH"
		  --target_ch "TARGET_CH"
		  --GPUdevice "GPUDEVICE"		  
~~~
| 変数 | 説明 |
----|----
| --input_dir| 学習用データセットがあるディレクトリ。 |
| --mode |プログラムの実行モード。学習をする際には、"train"を指定。 |
| --output_dir | モデルの出力先ディレクトリ |
| --checkpoint | 読み込む学習済みモデルがあるディレクトリ。指定しない場合、新規で学習を行なう。 |
| --max_epochs | 学習回数|
| --progress_freq | 学習時に学習状況を表示するステップ数。デフォルトは50 |
| --save_freq | モデルを保存するステップ数。デフォルトは100。 |
| --ngf | 生成器の第一層のフィルター数。デフォルトは64。 |
| --ndf | 判別器の第一層のフィルター数。デフォルトは64。 |
| --input_ch | 入力データのチャンネル数。デフォルトは4。 |
| --target_ch | 教師データのチャンネル数。デフォルトは4。 |
| --GPUdevice | 実行するGPUの番号を指定します。デフォルトは0。複数のGPUがあるマシンを使用する場合に任意のGPUを指定して実行します。GPUがない場合はCPUを使用して実行されます。 |

* 実行中に`--save_freq`で指定したステップごとに指定した保存先に学習モデル `model-{ステップ数}.data -00000-of-00001、model-{ステップ数}.index、model-{ステップ数}.meta` が出力されます。

### 参考
この学習プログラムは、[pix2pix-tensorflow](https://github.com/affinelayer/pix2pix-tensorflow)を基に作成しています。学習プログラムの詳細については基となったプログラムの配布ページを参照してください。

また、今回のサンプルは[lad2veg](https://github.com/NARO-41605/pix2pix_map_tiles/tree/master/lad2veg)にアップロードしてあります。