MSRNで超解像(MXNet)

  • MXNetの学習済みモデルはこちらからダウンロード可能

github.com

サンプル画像のダウンロード

こちら』からダウンロードして「dog.jpg」の名前で保存
https://s3.amazonaws.com/onnx-mxnet/examples/super_res_input.jpg

実行スクリプト

import numpy as np
import mxnet as mx
from mxnet import image

ctx = mx.cpu()

img = image.imread('dog.jpg')
img = img.astype(np.float32)/255
img = mx.nd.transpose(img, (2,0,1))
img = mx.nd.expand_dims(img, axis=0)

sym, arg_params, aux_params = mx.model.load_checkpoint('MSRN_4x', 0)
model = mx.mod.Module(symbol=sym, label_names=None, context=ctx)
model.bind(for_training=False, data_shapes=[('data', img.shape)])

model.set_params(arg_params, aux_params)

from collections import namedtuple
Batch = namedtuple('Batch', ['data'])

model.forward(Batch([img]), is_train=False)
prob = model.get_outputs()[0].asnumpy()
prob = np.squeeze(prob)

from PIL import Image
prob = (prob.transpose(1,2,0)*255).astype(np.uint8)
img = Image.fromarray(prob)
img.save('MSRN_4x.jpg')

結果の表示

いままでで一番きれいなような気がする。
f:id:touch-sp:20181121183247j:plain

カードをめくるアニメーションを作る

  • 回転している画像を作成する
from PIL import Image
import numpy as np
import cv2

img = cv2.imread('sss10.jpg')
pts0 = np.float32([[256,256],[256,0],[0,0],[0,256]])
pts1 = np.float32([[192,236],[192,20],[64,0],[64,256]])
pts2 = np.float32(([160,216],[160,40],[96,0],[96,256]))
pts3 = np.float32([[160,256],[160,0],[96,40],[96,216]])
pts4 = np.float32(([192,256],[192,0],[64,20],[64,236]))

M1 = cv2.getPerspectiveTransform(pts0,pts1)
M2 = cv2.getPerspectiveTransform(pts0,pts2)
M8 = cv2.getPerspectiveTransform(pts0,pts3)
M9 = cv2.getPerspectiveTransform(pts0,pts4)

dst1 = cv2.warpPerspective(img,M1,(256,256),borderValue=(255,255,255))
dst2 = cv2.warpPerspective(img,M2,(256,256),borderValue=(255,255,255))
dst8 = cv2.warpPerspective(img,M8,(256,256),borderValue=(255,255,255))
dst9 = cv2.warpPerspective(img,M9,(256,256),borderValue=(255,255,255))

img = Image.new('RGB', (256, 256), (0, 0, 0))
img.save('sss5.jpg')
img = np.array(img)

M3 = cv2.getPerspectiveTransform(pts0,pts3)
M4 = cv2.getPerspectiveTransform(pts0,pts4)
M6 = cv2.getPerspectiveTransform(pts0,pts1)
M7 = cv2.getPerspectiveTransform(pts0,pts2)

dst3 = cv2.warpPerspective(img,M3,(256,256),borderValue=(255,255,255))
dst4 = cv2.warpPerspective(img,M4,(256,256),borderValue=(255,255,255))
dst6 = cv2.warpPerspective(img,M6,(256,256),borderValue=(255,255,255))
dst7 = cv2.warpPerspective(img,M7,(256,256),borderValue=(255,255,255))

cv2.imwrite('sss1.jpg', dst1)
cv2.imwrite('sss2.jpg', dst2)
cv2.imwrite('sss3.jpg', dst3)
cv2.imwrite('sss4.jpg', dst4)
cv2.imwrite('sss6.jpg', dst6)
cv2.imwrite('sss7.jpg', dst7)
cv2.imwrite('sss8.jpg', dst8)
cv2.imwrite('sss9.jpg', dst9)
  • GIFとして保存
from PIL import Image

images = []

for i in range(10):
    images.append(Image.open('sss%d.jpg'%i))

images[0].save('out.gif', save_all=True, append_images=images[1:], duration=250)
  • 結果

f:id:touch-sp:20181119172236g:plain

  • 追記

Visual Studio(C#)のユーザーフォームアプリケーションにおいてpictureBox上にGIFを表示させると無限に動く。
それを回避するためには単純に画像を順番に表示すればよい。

        bool ura1 = true;

        private void pictureBox1_Click(object sender, EventArgs e)
        {
            if (ura1)
            {
                for (int i = 6; i < 11; i++)
                {
                    pictureBox1.Image = Image.FromFile("sss" + i.ToString() + ".jpg");
                    pictureBox1.Refresh();
                    Thread.Sleep(100);
                }
                ura1 = false;
            }
            else
            {
                for (int i = 1; i < 6; i++)
                {
                    pictureBox1.Image = Image.FromFile("sss" + i.ToString() + ".jpg");
                    pictureBox1.Refresh();
                    Thread.Sleep(100);
                }
                ura1 = true;
            }

Pythonで簡単なスクレイピング

import requests, bs4
import argparse
import os
from mxnet.gluon import utils

parser = argparse.ArgumentParser()
parser.add_argument('--ID', help='棋士番号', type=int, required=True)
args = parser.parse_args()

num = args.ID
print(str(num))

utils.download('https://www.shogi.or.jp/images/player/pro/%d.jpg' %num)
res = requests.get('https://www.shogi.or.jp/player/pro/%d.html'%num)
res.encoding = res.apparent_encoding
soup = bs4.BeautifulSoup(res.text, "html.parser")

elems_jp = soup.select('.nameTtl .jp')
print(elems_jp[0].text)

elems_en = soup.select('.nameTtl .en')
print(elems_en[0].text)

player =  str(num) + ',' + elems_jp[0].text + ',' + elems_en[0].text

if not os.path.isfile('players.txt'):
    with open('players.txt', mode='w') as f:
        f.write(player)
else:
    with open('players.txt', mode='a') as f:
        f.write('\n' + player)
  • MXNetがインストールされていたら画像のダウンロードは「requests」より「gluon.utils」を使ったほうが楽

MXNet 備忘録(3)

「mx.io.NDArrayIter」の仕様が変わったらしい
github.com

Change the timing of shuffling. Previously, it shuffles only during the initialization, which didn't meet training needs.
・Changes
shuffle when calling the reset.

「shuffle」について

テスト用コード

import mxnet as mx

A = []

for i in range(6):
    a = mx.nd.array([i])
    a = mx.nd.expand_dims(a, axis=0)
    A.append(a)

batch_size = 3

train_data = mx.io.NDArrayIter(data=[mx.nd.concat(*A, dim=0)], 
                             batch_size=batch_size, 
                             shuffle=True)

epoch = 2
for i in range(1, epoch+1):
    print('epoch %d'%i)
    train_data.reset()
    for batch in train_data:
        print(batch.data[0])

結果

epoch 1

[[5.]
 [4.]
 [1.]]
<NDArray 3x1 @cpu(0)>

[[3.]
 [2.]
 [0.]]
<NDArray 3x1 @cpu(0)>
epoch 2

[[0.]
 [3.]
 [4.]]
<NDArray 3x1 @cpu(0)>

[[1.]
 [5.]
 [2.]]
<NDArray 3x1 @cpu(0)>

reset毎にshuffleが実行される

「last_batch_handle」について

  • 「last_batch_handle='discard'」

テスト用コード

import mxnet as mx

A = []

for i in range(5):
    a = mx.nd.array([i])
    a = mx.nd.expand_dims(a, axis=0)
    A.append(a)

batch_size = 3

train_data = mx.io.NDArrayIter(data=[mx.nd.concat(*A, dim=0)], 
                             batch_size=batch_size, 
                             shuffle=False,
                             last_batch_handle='discard')

epoch = 2
for i in range(1, epoch+1):
    print('epoch %d'%i)
    train_data.reset()
    for batch in train_data:
        print(batch.data[0])

結果

epoch 1

[[0.]
 [1.]
 [2.]]
<NDArray 3x1 @cpu(0)>
epoch 2

[[0.]
 [1.]
 [2.]]
<NDArray 3x1 @cpu(0)>

切り捨てられる

  • 「last_batch_handle='pad'」(デフォルト)

テスト用コード

import mxnet as mx

A = []

for i in range(5):
    a = mx.nd.array([i])
    a = mx.nd.expand_dims(a, axis=0)
    A.append(a)

batch_size = 3

train_data = mx.io.NDArrayIter(data=[mx.nd.concat(*A, dim=0)], 
                             batch_size=batch_size, 
                             shuffle=False,
                             last_batch_handle='pad')

epoch = 2
for i in range(1, epoch+1):
    print('epoch %d'%i)
    train_data.reset()
    for batch in train_data:
        print(batch.data[0])

結果

epoch 1

[[0.]
 [1.]
 [2.]]
<NDArray 3x1 @cpu(0)>

[[3.]
 [4.]
 [0.]]
<NDArray 3x1 @cpu(0)>
epoch 2

[[0.]
 [1.]
 [2.]]
<NDArray 3x1 @cpu(0)>

[[3.]
 [4.]
 [0.]]
<NDArray 3x1 @cpu(0)>

最初に戻ってPaddingされる

  • 「last_batch_handle='roll_over'」

テスト用コード

import mxnet as mx

A = []

for i in range(5):
    a = mx.nd.array([i])
    a = mx.nd.expand_dims(a, axis=0)
    A.append(a)

batch_size = 3

train_data = mx.io.NDArrayIter(data=[mx.nd.concat(*A, dim=0)], 
                             batch_size=batch_size, 
                             shuffle=False,
                             last_batch_handle='roll_over')

epoch = 2
for i in range(1, epoch+1):
    print('epoch %d'%i)
    train_data.reset()
    for batch in train_data:
        print(batch.data[0])

結果

epoch 1

[[0.]
 [1.]
 [2.]]
<NDArray 3x1 @cpu(0)>
epoch 2

[[3.]
 [4.]
 [0.]]
<NDArray 3x1 @cpu(0)>

[[1.]
 [2.]
 [3.]]
<NDArray 3x1 @cpu(0)>

次のエポックに持ち越される

WindowsでPytorchのCycleGANを使ってみる

はじめに

github.com

  • 今回はWindowsでhorse2zebraのデモのみ行った。
  • Linux or macOSが前提と書かれているがWindowsでも動く(ただしデータのダウンロードに少し骨が折れる)
  • あらかじめこのGitHubページからZIPファイルをダウンロードして解凍しておく。

環境

Windows10 Pro 64bit
NVIDIA GeForce GTX1080
CUDA9.2
cudnn7.2.1
Python3.6.6(venv使用)

バージョン確認(pip freeze)

  • 「torch」「torchvision」のインストールはこちらを参照
  • 「dominate」「scipy」「visdom」をpipで追加インストールした
  • データのダウンロードにMXNetを使用したので「mxnet-cu92」がインストールされている
  • venvを使用しているので「pylint」がインストールされている
astroid==2.0.4
certifi==2018.10.15
chardet==3.0.4
colorama==0.4.0
dominate==2.3.4
graphviz==0.8.4
idna==2.6
isort==4.3.4
lazy-object-proxy==1.3.1
mccabe==0.6.1
mxnet-cu92==1.3.1b20180927
numpy==1.14.6
Pillow==5.3.0
pylint==2.1.1
pyzmq==17.1.2
requests==2.18.4
scipy==1.1.0
six==1.11.0
torch==0.4.1
torchfile==0.1.0
torchvision==0.2.1
tornado==5.1.1
typed-ast==1.1.0
urllib3==1.22
visdom==0.1.8.5
websocket-client==0.53.0
wrapt==1.10.11

データのダウンロード

  • 自分はMXNetを使用したがなんでも良い
from mxnet.gluon import utils

dataset = 'horse2zebra'

url = 'http://efrosgans.eecs.berkeley.edu/cyclegan/pretrained_models/%s.pth' % (dataset)
utils.download(url)

URL='https://people.eecs.berkeley.edu/~taesung_park/CycleGAN/datasets/%s.zip' % (dataset)
utils.download(URL)
  • ダウンロードされた「horse2zebra.pth」は「latest_net_G.pth」に名前を変更して「./checkpoints/horse2zebra_pretrained/latest_net_G.pth」に保存(存在しないフォルダは手動で作る)
  • ダウンロードされた「horse2zebra.zip」を手動で解凍する

実行

python test.py --dataroot horse2zebra/testA --name horse2zebra_pretrained --model test

CPUのみの場合には「--gpu_ids -1」をつける

GluonNLPを使ってみる

はじめに

  • 以前にGloveやFastTextを試してみた。

touch-sp.hatenablog.com
touch-sp.hatenablog.com

  • 今回はGluonNLPでGloveの学習済みモデルを使ってみた。

環境

Windows7 64bit
Python 3.6.6
GPUなし

バージョン確認(pip freeze)

astroid==2.0.4
certifi==2018.10.15
chardet==3.0.4
colorama==0.4.0
gluonnlp==0.4.0
graphviz==0.8.4
idna==2.6
isort==4.3.4
lazy-object-proxy==1.3.1
mccabe==0.6.1
mxnet==1.3.1b20181018
numpy==1.14.6
pylint==2.1.1
requests==2.18.4
six==1.11.0
typed-ast==1.1.0
urllib3==1.22
wrapt==1.10.11

実行スクリプト

import mxnet as mx
import gluonnlp as nlp

def norm_vecs_by_row(x):
    return x / mx.nd.sqrt(mx.nd.sum(x * x, axis=1) + 1E-10).reshape((-1,1))

def get_knn(vocab, k, word_vec):
    word_vec = word_vec.reshape((-1, 1))
    vocab_vecs = norm_vecs_by_row(vocab.embedding.idx_to_vec)
    dot_prod = mx.nd.dot(vocab_vecs, word_vec)
    indices = mx.nd.topk(dot_prod.reshape((-1, )), k=k, ret_typ='indices')
    indices = [int(i.asscalar()) for i in indices]
    return vocab.to_tokens(indices)

glove = nlp.embedding.create('glove', source='glove.6B.100d')

vocab = nlp.Vocab(nlp.data.Counter(glove.idx_to_token))
vocab.set_embedding(glove)

word_embedding = vocab.embedding['bad', 'worst', 'big']
word_vec = word_embedding[1] - word_embedding[0] + word_embedding[2]

get_knn(vocab, 3, word_vec)

学習済みモデルの種類

  • 「glove」「word2vec」「fasttext」それぞれ用意されている
  • 以下で確認できる
gluonnlp.embedding.list_sources('glove')
gluonnlp.embedding.list_sources('word2vec')
gluonnlp.embedding.list_sources('fasttext')

GluonとONNX

はじめに

touch-sp.hatenablog.com
以前このようなものを書いた。
その後、ONNX形式で保存されたモデルをGluonモデルとして読み込めたら便利だなと思っていたら、実は簡単にできたという話。

環境

Windows7 64bit
Python 3.6.6
GPUなし

バージョン確認(pip freeze)

astroid==2.0.4
certifi==2018.10.15
chardet==3.0.4
colorama==0.4.0
graphviz==0.8.4
idna==2.6
isort==4.3.4
lazy-object-proxy==1.3.1
mccabe==0.6.1
mxnet==1.3.1b20181018
numpy==1.14.6
onnx==1.3.0
Pillow==5.3.0
protobuf==3.6.1
pylint==2.1.1
requests==2.18.4
six==1.11.0
typed-ast==1.1.0
typing==3.6.6
typing-extensions==3.6.6
urllib3==1.22
wrapt==1.10.11

学習済みモデルのダウンロード

  • こちら』から学習済みモデルをダウンロード

サンプル画像のダウンロード

  • こちら』からサンプル画像をダウンロード

実行ファイル

import numpy as np
from PIL import Image
import mxnet as mx
import mxnet.contrib.onnx as onnx_mxnet

#データの読み込み
input_image_dim = 224
img = Image.open('super_res_input.jpg').resize((input_image_dim, input_image_dim))
img_ycbcr = img.convert("YCbCr")
img_y, IMG_CB, IMG_CR = img_ycbcr.split()
INPUT_IMG = np.array(img_y)[np.newaxis, np.newaxis, :, :]

#モデルの読み込み
model = onnx_mxnet.onnx2mx.import_to_gluon.import_to_gluon('super_resolution.onnx', ctx=mx.cpu())

#モデルの適応
output = model(mx.nd.array(INPUT_IMG))

#画像の保存
output = mx.nd.squeeze(output)
img_out_y = Image.fromarray(output.asnumpy().clip(0,255).astype(np.uint8), mode='L')
result_img = Image.merge(
        "YCbCr", [img_out_y,
                  IMG_CB.resize(img_out_y.size, Image.BICUBIC),
                  IMG_CR.resize(img_out_y.size, Image.BICUBIC)]).convert("RGB")
result_img.save("super_res_output.jpg")

おまけ

どのようなデータを入力するべきかを知るには以下の1行を実行すれば良い

>>> onnx_mxnet.get_model_metadata('super_resolution.onnx')
{'input_tensor_data': [('1', (1, 1, 224, 224))], 'output_tensor_data': [('27', (1, 1, 672,
672))]}