Jetson Nanoでスーパーレゾリューション

# はじめに

この記事はJetson Nanoを使って解像度の低い画像から解像度の高い画像を作る「スーパーレゾリューション」を試したものである。いつも使っているboss.jpgの画像がピンボケしているのが気になっていたので実験。

ソースコードは下記のものを利用。
https://github.com/idealo/image-super-resolution

# ディレクトリ構成

「$HOME/Documents/SuperResolution」以下に環境を構築する。最終的には下記のようになる。

```bash
├── Documents/
│   ├── SuperResolution/
│   │   ├── image-super-resolution/
│   │   ├── super_res.py
│   │   ├── Pipfile
│   │   └── Pipfile.lock
```

# 事前準備

「[Jetson Nanoの実験環境のまとめ](https://qiita.com/yamamo-to/items/fc8b91062cb4e115c600)」でセットアップを行なった後、追加で次の作業を行う。

## aptによる関連パッケージのインストール

まずはaptで関連パッケージのインストール。

- `git-lfs`がないとgit lfsのサブコマンドが使えず重みをダウンロードできない
- 他のパッケージは`numpy`および`scipy`のために必要

```bash:ターミナル
apt install git-lfs libfreetype6-dev libblas-dev liblapack-dev libatlas-base-dev gfortran
```

## Pipenvにより環境の構築

$HOME/Documents/SuperResolution以下にPipenvを使って環境を構築する。

```bash:ターミナル
yamamo-to@jetson-nano:~$ mkdir Documents/SuperResolution
yamamo-to@jetson-nano:~$ cd Documents/SuperResolution
yamamo-to@jetson-nano:~/Documents/SuperResolution$
```

「[Jetson NanoにPipenvでTensorFlowをインストール](https://qiita.com/yamamo-to/items/8da855f09c3dd5af1fd8)」と同じように、~/Documents/SuperReoslution以下にPipfileを作成。ただし全く同じではなく指定されているバージョンを用いるよう微修正。

```:Pipfile
[[source]]
name = "pypi"
url = "https://pypi.org/simple"
verify_ssl = true

[[source]]
name = "nvidia"
url = "https://developer.download.nvidia.com/compute/redist/jp/v42"
verify_ssl = true

[dev-packages]

[packages]
numpy = {version="==1.16.2", index="pypi"}
grpcio = {version="*", index="pypi"}
absl-py = {version="*", index="pypi"}
py-cpuinfo = {version="*", index="pypi"}
psutil = {version="*", index="pypi"}
portpicker = {version="*", index="pypi"}
six = {version="*", index="pypi"}
mock = {version="*", index="pypi"}
requests = {version="*", index="pypi"}
gast = {version="*", index="pypi"}
h5py = {version="*", index="pypi"}
astor = {version="*", index="pypi"}
termcolor = {version="*", index="pypi"}
tensorflow_gpu = {version="==1.13.1+nv19.5", index="nvidia"}
imageio = {version="*", index="pypi"}
Keras = {version="==2.2.4", index="pypi"}
tqdm = {version="*", index="pypi"}

[requires]
python_version = "3.6"

[pipenv]
allow_prereleases = true
```
Pipfileを置いた後、`pipenv install`で構築する。

```bash:ターミナル
yamamo-to@jetson-nano:~/Documents/SuperResolution$ pipenv install
```

## ISRのインストール

Image Super Resolution(ISR)パッケージをインストールする。公式の手順では`pip install ISR`が推奨されているがTensorFlowのバージョンミスマッチにより失敗する。そのためソースコードからインストールする。

```bash:ターミナル
yamamo-to@jetson-nano:~/Documents/SuperResolution$ git clone https://github.com/idealo/image-super-resolution.git
yamamo-to@jetson-nano:~/Documents/SuperResolution$ cd image-super-resolution
yamamo-to@jetson-nano:~/Documents/SuperResolution/image-super-resolution$
```

重みをダウンロードする。

```bash:ターミナル
yamamo-to@jetson-nano:~/Documents/SuperResolution/image-super-resolution$ git lfs pull
```

setup.pyを編集してinstall_requiresから'tensorflow==1.13.1'を削除する。diffを取ると下記の通り。

```python:setup.py
$ git diff
diff --git a/setup.py b/setup.py
index df108a8..e49dd6c 100644
--- a/setup.py
+++ b/setup.py
@@ -16,7 +16,7 @@ setup(
     description='Image Super Resolution',
     long_description=long_description,
     license='Apache 2.0',
-    install_requires=['imageio', 'Keras==2.2.4', 'numpy==1.16.2', 'tensorflow==1.13.1', 'tqdm'],
+    install_requires=['imageio', 'Keras==2.2.4', 'numpy==1.16.2', 'tqdm'],
     extras_require={
         'tests': ['pytest==4.3.0', 'pytest-cov==2.6.1'],
         'docs': ['mkdocs==1.0.4', 'mkdocs-material==4.0.2'],
```

編集後、以下のようにインストールする。

```bash:ターミナル
yamamo-to@jetson-nano:~/Documents/SuperResolution/image-super-resolution$ pipenv run python3 setup.py install
```

# 実験

$HOME/Documents/SuperResolutionで作業を行う。

```bash:ターミナル
cd ~/Documents/SuperResolution
```

## ソースコードの準備

以下のような`super_res.py`を作成する[^2]。

[^2]: このプログラムは一度掴んだGPUのメモリが解放されない問題があるので、一回実行したら再起動が必要である。

```python:super_res.py
import argparse
import numpy as np
from PIL import Image
from ISR.models import RDN


def main():
    parser = argparse.ArgumentParser()
    parser.add_argument('weights_hdf5')
    parser.add_argument('input_image')
    parser.add_argument('output_image')
    args = parser.parse_args()

    img = Image.open(args.input_image)
    lr_img = np.array(img)

    rdn = RDN(arch_params={'C':6, 'D':20, 'G':64, 'G0':64, 'x':2})
    rdn.model.load_weights(args.weights_hdf5)

    sr_img = rdn.predict(lr_img)
    new_img = Image.fromarray(sr_img)

    new_img.save(args.output_image)


if __name__ == '__main__':
    main()
```

## 入力画像の準備

boss.jpgに対して実行するとOOM(Out Of Memory)エラーが発生するので、切り出して小さな画像 boss_crop.jpg を使う。

```bash:ターミナル
convert boss.jpg -crop 250x240+150+50 boss_crop.jpg
```

## プログラムの実行

先ほど作成した`super_res.py`を実行する。実行文が長くなっているのは重みファイルが深いところにあるため。

```bash:ターミナル
pipenv run python3 super_res.py image-super-resolution/weights/sample_weights/rdn-C6-D20-G64-G064-x2/ArtefactCancelling/rdn-C6-D20-G64-G064-x2_ArtefactCancelling_epoch219.hdf5 boss_crop.jpg boss_hires.jpg
```

実行ログに「メモリが足らないよ」という警告が散々出るが`this is not a failure`を信じることにする。

![image_super_resolution.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/252224/dfe14b62-2ca8-a696-cec3-1c3cdde9ee60.png)

## 結果

成功すると入力画像の倍のサイズの画像が作成される。比較のためにオリジナル画像をImageMagickのconvertでresizeした画像も合わせて掲載する。

クロップ画像(250x240)
![boss_crop.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/252224/a11c8e46-bcf3-9294-a34c-4837772e1b4e.jpeg)

クロップ画像縦横2倍(500x480) by ImageMagick
![boss_crop_double.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/252224/9df7c9de-d9e4-1829-f93b-72affd2bd9f3.jpeg)

スーパーレゾリューション(500x480)
![boss_hires.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/252224/e1181f48-fe74-0ece-fefd-18add97b9f8c.jpeg)

差が分かりにくいので差分(スーパーレゾリューション - クロップ画像縦横2倍)を取る。

![boss_diff.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/252224/bc541358-f424-e750-8758-08e0da326dcd.jpeg)

うん、それなりに違うようだ。

# そもそも論

スーパーレゾリューションに期待してしていたのは(いつも使っているboss.jpgの)「ピンボケ画像が綺麗になる」ということであり、拡大して綺麗になることではない。ソリューションの選択ミスだったかも知れない。

それならば手動でオートエンコーダしてみよう、ということで画像を一旦半分にして、このスーパレゾリューションに掛けてみる。

![boss_crop.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/252224/a11c8e46-bcf3-9294-a34c-4837772e1b4e.jpeg)![boss_hires2.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/252224/ffc1d9f9-b3da-7f72-806c-dfb41469af76.jpeg)
(左)オリジナル画像 (右)画像を一旦半分にしてスーパーレゾリューション

細かい部分では「障子の模様が変わった」「カタカナが読めない」などあるものの、それなりに改善されたように見える。

# まとめ

Jetson Nanoを使ってスーパーレゾリューションを行い、画像を一度半分にしてスーパーレゾリューションを適用することにより、ピンボケ画像の改善が見られた。