Deep Learning(深層学習)のための画像の標準化，リサイズ，分割，結合処理．

##1.　背景
　衛星画像から建物のSegmentaion処理のために，対象となる衛星画像の標準化，リサイズおよび分割処理を行い，処理（Prediction）後に，結果の画像の結合処理が必要です．
　いつもは，ImageMagickで処理していましたが，メインのプログラム（Python)と同じ処理でできれば一度ですむため，pythonでの画像の前後処理のコードを作りました．
 調べてみたら，同じ目的でまとまった記事（特に，プログラム初心者のための）がなかったため，同じように画像の前処理・後処理をPythonでおこないたい方の参考になれば幸いです．

　関連ページ
[・衛星画像のSegmentation（セグメンテーション）により建物地図を作成する．](https://qiita.com/nigo1973/items/78dea24fd997674ca8b9)
[・無料で衛星画像を入手する方法](https://qiita.com/nigo1973/items/9bb6a11caac8e3e1e850)

##2. 標準処理
　衛星画像の条件が同じであれば問題ありませんが，異なる衛星画像やタイミングがことなると，画像の信号強度（明暗）に違いがでてしまい，解析精度が大きく低下します．そのため，画像の信号を同じ条件にそろえるための，標準処理（正規化）が必要になります．
　例えば，画像の信号は8ビットの0から255の数字で表されており，その画像の条件によっては，0から100まで等，フルスケール（ダイナミックレンジ）を使われていないことがあります．その場合，例えば0から100までの範囲でつかっていたものを，0から255までとして処理することで，入力画像の信号強度の条件を揃える処理が必要です．
　この標準処理は以下のコードで行います．

```python:normalize.py
#画像の規格化処理関数
def normalize(arr):
    arr = arr.astype('float')
    for i in range(3):
        minval = arr[...,i].min()
        maxval = arr[...,i].max()
        if minval != maxval:
            arr[...,i] -= minval
            arr[...,i] *= (255.0/(maxval-minval))
    return arr
```
後ほど全体の処理コードを紹介しますが，RGBの３つの色に対して，対象画像の信号強度の分布（範囲）を取得し，それをフルスケールの255で規格化するnormalize関数をここで定義していまう．

##3. リサイズ
　[・衛星画像のSegmentation（セグメンテーション）により建物地図を作成する．](https://qiita.com/nigo1973/items/78dea24fd997674ca8b9)で用いているDeep LearningによるSegmentationの入力画像のサイズは，256☓256になります．対象画像（テスト画像）をこのサイズにリサイズして使っても良いのですが，分解能が高い衛星画像をわざわざ低解像度に落として用いることは，情報量が少なくなり，目的の建物地図が求められません．
　そこで，対象画像のサイズに近いサイズで，かつDeep Learning処理のための256の整数倍のサイズに画像をリサイズします．ここでは，3072☓3072のサイズへのリサイズの方法を紹介します．

```python:resize.py
from PIL import Image

#画像サイズを3072にリサイズ．
img_size = 3072

# 画像の読み込み
im=Image.open(DIR_TESTS+"/test.jpg")

#画像のリサイズ・標準処理
img_resize=im.resize((img_size,img_size))

```

画像処理ライブラリPillow(PIL)のImageモジュールにresize処理が組み込まれていますので，それを使っています．ここでの対象画像は，DIR_TESTSで定義したフォルダのtest.jpgを読んでいます．
画像のリサイズ処理は，PILパッケージを使えば簡単に処理できます．

##４. 画像分割
　次に読み込んだ画像を，Deep Learningで処理するために256☓256サイズに分割します．

```python:crop.py


height = 256
width = 256
img_size = 3072

#画像の分割処理関数
def ImgSplit(im):
    # 読み込んだ画像を２５６*２５６のサイズで１４４枚に分割する

    buff = []
    # 縦の分割枚数
    for h1 in range(int(img_size/height)):
        # 横の分割枚数
        for w1 in range(int(img_size/width)):
            w2 = w1 * height
            h2 = h1 * width
            print(w2, h2, width + w2, height + h2)
            c = im.crop((w2, h2, width + w2, height + h2))
            buff.append(c)
    return buff

#画像の分割処理の実行
    hi=0
    for ig in ImgSplit(new_img):
        hi=hi+1
        print(hi)
        # 保存先フォルダの指定
        ig.save(DIR_OUTPUTS+"/"+str(hi)+".png")

```
　分割処理も，画像処理ライブラリPillow(PIL)のImageモジュールにあるcropを用いて実行します．実行後の画像ファイルは"1.png，2.png"と数字のファイル名で保存していきます．ここでは．3072☓3072サイズのものが256☓256で分割されるため，144の画像ファイルが作られます．画像の保存フォルダは，ここではDIR_OUTPUTSて定義された場所になります．入力や出力されるファイルのアドレスについては，”６．まとめ”で説明します．

##5. 画像結合
　最後に，Deep Learningが実行され，出力された画像の結合です．ここでは，先に分割された144の画像ファイルの結合を例として紹介します．

```python:merge.py
from natsort import natsorted

height = 256
width = 256
img_size=3072

#画像の結合処理関数
def ImageMerge():
    image_list = os.listdir(DIR_OUTPUTS)
    image_list_sort=natsorted(image_list)

    n=int(img_size/width)

    total_width = width * n
    max_height = height * n

    new_im = Image.new("RGB", (int(total_width), int(max_height)))

    h = 0
    w = 0

    for i in range(int(n*n)):
        new_im.paste(Image.open(DIR_OUTPUTS + "/"+ image_list_sort[i]), (w*width,h*height))
        w = w +1
       if w % int(n) == 0:
           w = w-int(n)
           h = h +1
         
    new_im.save(DIR_OUTPUTS + '/test_total.jpg')

```
 この結合関数を定義するのに時間がかかったのですが，それは画像ファイルを読むこんだリストをつくるところでした．
　対象となるフォルダから画像ファイルを読み込み処理しただけのリストは，以下のようにバラバラなファイルの並びになります．

```python:output1
['16.png', '96.png', '115.png', '23.png',..... 'test_total.jpg', '143.png', '26.png']

```
 そこで，OSパッケージにあるsort処理を行うと以下の結果となります．

```python:output2
['1.png', '10.png', '100.png', '101.png', '102.png', '103.png', '104.png', '105.png', '106.png', ....]

```
 "1.png, 2.png, 3.png..."と並んでほしいのに，"1.png, 10.png, 100.png..."となってしまい，この順番で画像を結合すると間違った画像ができあがってしまいます．
　そこで，目的の数字順にファイルがリスト化するために，いくつか方法はありますが，ここでnatsort パッケージをインストールし，natsortedで処理します．
　インストールは，いつもpipコマンドで以下を実行します．

 pip install natsort

これにより，以下のリストを得ることができます．

```python:output3
['1.png', '2.png', '3.png', '4.png', '5.png', '6.png' ....]

```
　これで目的のファイル順序でのリストができましたので，ここから縦12，横12のファイルの結合（144☓144）を行います．

```python:newimage.py
 new_im = Image.new("RGB", (int(total_width), int(max_height)))
```
　画像の結合は，上記で新しい画像のフィールドを作り，ここの各位置に画像を配置することで作ります．パズルのイメージです．
　最後に，結合後の画像を，同じフォルダに”test_total.jpg”として保存します．

##6. まとめ
 全体のコードは以下となります．

```python:image.py
from PIL import Image
import os
import sys
import numpy as np
from natsort import natsorted

#対象画像（TESTS)と出力画像（Output)のフォルダ指定．
DIR_TESTS = os.path.join('..', 'TestData')
DIR_OUTPUTS = os.path.join('..', 'OutPuts')

#分割する画像サイズ，リサイズ画像のサイズ指定
height = 256
width = 256
img_size=3072


#画像の規格化処理関数
def normalize(arr):
    arr = arr.astype('float')
    # Do not touch the alpha channel
    for i in range(3):
        minval = arr[...,i].min()
        maxval = arr[...,i].max()
        if minval != maxval:
            arr[...,i] -= minval
            arr[...,i] *= (255.0/(maxval-minval))
    return arr

#画像の分割処理関数
def ImgSplit(im):
    # 読み込んだ画像を２５６*２５６のサイズで１４４枚に分割する

    buff = []
    # 縦の分割枚数
    for h1 in range(int(img_size/height)):
        # 横の分割枚数
        for w1 in range(int(img_size/width)):
            w2 = w1 * height
            h2 = h1 * width
            print(w2, h2, width + w2, height + h2)
            c = im.crop((w2, h2, width + w2, height + h2))
            buff.append(c)
    return buff

#画像の結合処理関数
def ImageMerge():
    image_list = os.listdir(DIR_OUTPUTS)
    #print(image_list)
    image_list_sort1=sorted(image_list)
    #print(image_list_sort1)
    image_list_sort=natsorted(image_list)
    #print(image_list_sort)

    n=int(img_size/width)

    total_width = width * n
    max_height = height * n

    new_im = Image.new("RGB", (int(total_width), int(max_height)))

    h = 0
    w = 0

    for i in range(int(n*n)):
        new_im.paste(Image.open(DIR_OUTPUTS + "/"+ image_list_sort[i]), (w*width,h*height))
        w = w +1
        if w % int(n) == 0:
           w = w-int(n)
           h = h +1

    new_im.save(DIR_OUTPUTS + '/test_total.jpg')

if __name__ == '__main__':
    # 画像の読み込み
    im=Image.open(DIR_TESTS+"/test.jpg")

    #画像のリサイズ・標準処理
    img_resize=im.resize((img_size,img_size))
    arr = np.array(img_resize)
    new_img = Image.fromarray(normalize(arr).astype('uint8'))

    #画像の分割処理の実行
    hi=0
    for ig in ImgSplit(new_img):
        hi=hi+1
        # 保存先フォルダの指定
        ig.save(DIR_OUTPUTS+"/"+str(hi)+".png")

    #画像結合処理の実行
    ImageMerge()

```

mainで全体の処理を行っています．
フォルダの構成は,　[・衛星画像のSegmentation（セグメンテーション）により建物地図を作成する．](https://qiita.com/nigo1973/items/78dea24fd997674ca8b9)での利用を想定したため，以下となります

---src：コード，プログラム
|
---TestData：対象画像
|
---OutPuts：分割後，結合後の画像

コード（image.py)はsrcフォルダにあります．

　このコードを，衛星画像のSegmentation処理するmainコードに追加することで，１コマンドでDeep Learningの画像の前処理から後処理まで行います．
　ここまでできましたので，次はこの衛星画像からの建物SegmentationのWebアプリを作ってみます．これによって，任意の衛星画像からWebブラウザを用いて建物Segmentation情報を作ることができます．
　公開にすれば誰でもできますので，知人の海外の宇宙機関にも紹介して，彼らにも使ってもらおうと思います．