RetinaNetを詳しく紹介(一)データ前処理

###背景###
前回物体検出のモデルであるSSDを紹介しました。
今回物体検出の一つモデルRetinaNetを紹介したいと思います
（本文書はこちらのソースコード https://github.com/yhenon/pytorch-retinanet を参考に整理したものです）
本節はデータの前処理を紹介いたします。

###データの種類###
二つがあります
1.画像

```
image(H,W,C)
```

2.目標物体のカテゴリーと範囲を表す座標(Annotations)

```
cx1,cy1,w1,h1,category1
```

###データ前処理の流れ###
####1. Annotationsの前処理####
cx_start,cy_start,width,height
から
cx_start,cy_start,cx_end,cy_end
まで
変更します
![RetinaNet物体検出1.001.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/191439/54c7ef16-527e-d095-ef52-307cdcdaa9cc.jpeg)
図のように、変更されたAnnotationsは左上の開始点と右下の終了点になります。

####2. 画像の前処理####
![RetinaNet物体検出1.002.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/191439/f32ae9e7-06c0-8c40-09e9-1e0271dde4a8.jpeg)
図のより、もともとの画像は次元が(H,W,C)のTensorです
以下の四つのスッテプを経由します。最後のOutputはモデルのinputとしてモデルに入れます。
1. /255処理
2. Normalizaer処理
3. Argumenter処理
4. Resize処理

#####2-1. /255処理#####
![RetinaNet物体検出1.003.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/191439/26ff16f7-5470-3f7d-85c5-31a13dca1c91.jpeg)
図のように、画像データは次元が(H,W,C)のTensorです。各要素は0から255までの値です。
多分、計算量を減らすため、0から255の値を最大値２５５に割り算して、0から1までの値に変更します。


#####2-2. Normalizaer処理#####
![RetinaNet物体検出1.004.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/191439/d71f2d01-6b59-54a7-eb2f-6b2b7a0e1f18.jpeg)
図のように、Cチャンネル画像データの各要素を事前に定められた該当するmeanと引き算して、事前に定められたstdと割り算します。

#####3. Argumenter処理#####
![RetinaNet物体検出1.005.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/191439/276b1726-b2c1-ba8e-9d33-1ba0e70b7c23.jpeg)
データの多様性を保つために、データの水増しが行います。今回は0.5の確率で画像を横逆転します。
そして、関連するAnnotation情報も左右逆転します。

![RetinaNet物体検出1.006.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/191439/2a483ac5-7228-cdbf-8d1f-cab3f1ba1680.jpeg)
図のようにAnnotation情報も左右逆転します。感覚的には、開始点を左上から右上に移動したり、終了点を右下から左下に移動します。

#####4. Resize処理#####
画像データはさまざまがあります。それは入力された画像のサイズによります。
640*480
1024*640
などいろいろサイズがあります。

######4-1. Resizeされるサイズの条件######
それらのサイズを以下の条件を満たすように変形します
***短い辺の長さを608、もしくは長い辺の長さを1024にします***
***短い辺の長さを最長608、長い辺の長さを最長1024のサイズに変形します。***

######4-2. 横幅が小さい場合ケースA######
![RetinaNet物体検出1.007.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/191439/31eaa9cb-b968-4c18-b25c-7e70415cc33a.jpeg)
図のように、予め定められた横幅が608,縦幅が1024の***基準ボックス***があります。まず画像データの横幅を***基準ボックス***の横幅に当てはまってみって、画像データの縦幅を同比例に変形します。
変形された縦幅は***基準ボックス***の縦幅より小さいなら、変形はそのまま成功になります。
scale = ***基準ボックス***の横幅/画像データの横幅

######4-3. 横幅が小さい場合ケースB######
![RetinaNet物体検出1.008.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/191439/924ea61c-f0e4-a41f-6c36-6d8abb434902.jpeg)
4-1とは別に、横幅を当てはまってみて、縦幅は***基準ボックス***の縦幅よりおおきいなら、縦幅を嵌ってみます。
scale = ***基準ボックス***の縦幅/画像データの縦幅


######4-4. 縦幅が小さい場合ケースA######
![RetinaNet物体検出1.009.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/191439/178d7773-8211-0430-e42e-5ad2b2bb1b3f.jpeg)
図のように、画像データの縦幅は横幅とくらべたら、小さい場合。予め定められた横幅が1024,縦幅が608の***基準ボックス***があります。
先に縦幅を608に当てはまってみます。算出された横幅が***基準ボックス***の縦幅1024より小さいなら、そのままにします。


######4-5. 縦幅が小さい場合ケースB######
![RetinaNet物体検出1.010.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/191439/417ba899-b0c2-f3d7-e7c6-db5af43be0b2.jpeg)
図のように、画像データの縦幅は横幅とくらべたら、小さい場合。予め定められた横幅が1024,縦幅が608の***基準ボックス***があります。
先に縦幅を608に当てはまってみます。算出された横幅が***基準ボックス***の縦幅1024より大きいです。それはだめですので、次に横幅を1024に当てはまめます。


######4-6. AnnotationsのResize処理######
![RetinaNet物体検出1.009.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/191439/e6da1e89-5502-d69d-5fd5-013d0cf4dc9e.jpeg)
図のように、画像データのResizeが行うとともに、AnnotationsのResizeも行う必要があります。具体的には4-1、4-2から計算されたscaleとAnnotations掛け算します。

######4-7. Padding処理######
![RetinaNet物体検出1.010.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/191439/b9eceb83-2e15-eb58-902f-2f23313ac45c.jpeg)
最後に、Padding処理を行います。現在の縦幅と横幅を32個の1X1の要素を入れようとします。
どれぐらい1X1の要素を入れるのは横幅と縦幅から32とのMOD計算の結果によります。

```
padding_w = w + 32 - w % 32
padding_h = h + 32 - h % 32
```

###ソースコード###
####test_image_flip####

```python
def test_image_flip(path):
    image = skimage.io.imread(path)  # (H,W,C)

    b = image.astype(np.float32) / 255.0
    skimage.io.imshow(b)
    skimage.io.show()

    c = b[:, ::-1, :]
    skimage.io.imshow(c)
    skimage.io.show()
```

![retina1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/191439/c95377fd-afc4-b35f-1a12-00f98fd9d797.png)

####test_normalization####

```python

def test_normalization(path):
    image = skimage.io.imread(path)  # (H,W,C)

    image = image.astype(np.float32) / 255.0
    skimage.io.imshow(image)
    skimage.io.show()


    mean = np.array([[[0.485, 0.456, 0.406]]])
    std = np.array([[[0.229, 0.224, 0.225]]])
    transformedImage = (image.astype(np.float32) - mean) / std

    skimage.io.imshow(transformedImage)
    skimage.io.show()
```

![retina2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/191439/13d1e221-23e2-628d-2332-9b4865e8dffe.png)


####test_resize####

```python
def test_resize(path):
    smallerLineMaxLength = 608
    largerLineMaxLength = 1024

    image = skimage.io.imread(path)

    skimage.io.imshow(image)
    skimage.io.show()

    imageWidth = image.shape[1]
    imageHeight = image.shape[0]

    matchMin = min(imageWidth, imageHeight)
    matchMax = max(imageWidth, imageHeight)

    scale = smallerLineMaxLength / matchMin

    resizedMax = int(matchMax * scale)

    if resizedMax > largerLineMaxLength:
        scale = largerLineMaxLength / matchMax

    resizedWidth = int(imageWidth * scale)
    resizedHeight = int(imageHeight * scale)
    trasformedImage = skimage.transform.resize(image, (resizedHeight, resizedWidth))

    skimage.io.imshow(trasformedImage)
    skimage.io.show()

    paddingHeight = 32 - int(resizedHeight) % 32
    paddingWidth = 32 - int(resizedWidth) % 32

    newImageStruc = np.zeros((resizedHeight + paddingHeight, resizedWidth + paddingWidth, 3))
    print(newImageStruc.shape)
    newImageStruc[:resizedHeight, :resizedWidth, :] = trasformedImage

    skimage.io.imshow(newImageStruc)
    skimage.io.show()
```

![retina3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/191439/a49a6da9-df96-6ca5-e8c2-5bb09e737146.png)


###まとめ###
今回RetineNet第一節はデータの前処理を紹介しました
次回はモデルを詳しく紹介します