TensorFlowのResizeがよくわからなかったので視覚的にまとめてみた

# はじめに
TensorFlowにはresizeするための関数がいくつもある。

調べてみるとTensorFlowでresize用の関数は以下の6つのようだ。

* tf.image.resize_images
* tf.image.resize_area
* tf.image.resize_bicubic
* tf.image.resize_bilinear
* tf.image.resize_nearest_neighbor
* tf.image.resize_image_with_crop_or_pad

ドキュメントを読むと説明は書いてあるのだが、いまいちピンとこない。

なので実際に試して視覚的に動作を理解したいと思う。

結論から言うと、tf.image.resize_imagesが以下の4つの機能を含んでいた。

* tf.image.resize_area
* tf.image.resize_bicubic
* tf.image.resize_bilinear
* tf.image.resize_nearest_neighbor

なので、試すのは以下の2つだけにする。

* tf.image.resize_images
* tf.image.resize_image_with_crop_or_pad

使う画像はサイズが256x256のレナさんとサイズが256x170のねこを使うことにする。
<img width="569" alt="スクリーンショット 2016-09-06 11.17.15.png" src="https://qiita-image-store.s3.amazonaws.com/0/77079/4a741f75-fa7c-6a89-38b3-62f92e92da58.png">


では一つずつ試してみよう。

# tf.image.resize_images(images, new_height, new_width, method=0, align_corners=False)
resize_imagesは画像を指定したmethodでnew_height x new_widthにリサイズしてくれる関数。

入力画像としては4Dのテンソル[batch, height, width, channels]か3Dのテンソル[height, width, channels]を与えることができる。4Dで与えれば画像の一括変換が可能。

返り値には、4Dのテンソル[batch, new_height, new_width, channels]か3Dのテンソル[new_height, new_width, channels]が返却される。こちらは入力のテンソルに対応して変化する。


## まずは縮小してみる
以下の設定をして画像を128x128に縮小してみる。

```py
tf.image.resize_images(image, 128, 128)
```
その結果が以下の通り。
<img width="298" alt="スクリーンショット 2016-09-06 11.21.07.png" src="https://qiita-image-store.s3.amazonaws.com/0/77079/f899b9bc-3a36-46be-a6e9-f0feccecd23a.png">

レナは問題なく縮小できているが、ねこの方は歪んでしまっている。
このように、**元のアスペクト比がnew_width、new_heightと同じでない場合はリサイズ画像が歪む。**これを避けるためには、resize_image_with_crop_or_padを使う。こちらについては後ほど試してみる。

## methodを変えてみる
resize_images関数は以下の4つのmethodをとることができる:

* ResizeMethod.BILINEAR: Bilinear補間(デフォルト)
* ResizeMethod.NEAREST_NEIGHBOR: Nearest neighbor補間
* ResizeMethod.BICUBIC: Bicubic補間
* ResizeMethod.AREA: Area補間

一つずつ試して見た結果が以下の通り:
<img width="1006" alt="スクリーンショット 2016-09-06 12.08.59.png" src="https://qiita-image-store.s3.amazonaws.com/0/77079/d054a3b6-a16f-9c73-dbfc-f5db7489eab4.png">

拡大するとぼやけ具合が元画像と比べて微妙に違うことがわかる。それぞれ使いどころがあるようだ。

## align_corners
以下はalign_cornersがFalseの場合とTrueの場合の画像:
<img width="424" alt="スクリーンショット 2016-09-06 13.20.00.png" src="https://qiita-image-store.s3.amazonaws.com/0/77079/22f9dc68-324e-320b-3ac2-0190b582a6c0.png">

正直違いはよくわからないのだが、Trueだと入力を(new_height - 1) / (height - 1)に再スケールし、Falseだとnew_height / heightにスケールするようだ。Trueにすることで正確に入力と出力のすべての4隅の位置を正確に合わせるらしい。何のためにするのかはよくわからない。


# tf.image.resize_image_with_crop_or_pad(image, target_height, target_width)
resize_image_with_crop_or_pad関数は、画像を指定したサイズ(target_height x target_width)になるようにトリミングand/orパディングしてくれる関数。

画像の中央をトリミングまたは黒画像をパディングすることによって、target_widthとtarget_heightに画像のサイズを変更します。

幅または高さがそれぞれ指定されたtarget_widthまたはtarget_heightよりも大きい場合、画像の中央をトリミングします。以下は256x170の猫画像をtarget_heightとtarget_widthに128x128を指定してリサイズした場合です。画像中央がトリミングされていることがわかります。
<img width="446" alt="スクリーンショット 2016-09-06 13.53.39.png" src="https://qiita-image-store.s3.amazonaws.com/0/77079/a6f90ec5-a5b9-b76f-2344-6751422a6037.png">


幅または高さがtarget_widthまたはtarget_heightよりも小さい場合、黒画像を埋め込みます。
以下は256x170の猫画像をtarget_heightとtarget_widthに196x196を指定してリサイズした場合です。上下にパディングされつつ画像中央がトリミングされています。
<img width="502" alt="スクリーンショット 2016-09-06 13.57.54.png" src="https://qiita-image-store.s3.amazonaws.com/0/77079/bb4fe13c-f1b8-c259-ed56-441b60286801.png">

# おまけ
resize_image_with_crop_or_padとresize_imagesを組み合わせることで、CroppingせずにPaddingだけ施した画像の縮小ができる。
![test.jpg](https://qiita-image-store.s3.amazonaws.com/0/77079/3a38e811-7ac4-3764-37f8-4a69e83ffefc.jpeg)

やり方
1. 画像のサイズを取得
2. resize_image_with_crop_or_pad関数のtarget_heightとtarget_widthに長辺のサイズを指定
3. パディングされた画像をresize_imagesで縮小

# 参考
[TensorFlow/image](https://www.tensorflow.org/versions/r0.10/api_docs/python/image.html)