#SVGとPNGのデータセット ① (塩尻MLもくもく会#09)
ラスタ形式からベクタ形式を推論する研究が、様々な分野で行われているようです。
- Learning to Simplify: Fully Convolutional Networks for Rough Sketch Cleanup
- Semantic Segmentation for Line Drawing Vectorization Using
Neural Networks - Raster-to-Vector: Revisiting Floorplan Transformation
- SvgAI — Training artificial intelligent agent to use SVG editor
ラスタからベクタへの学習が簡単に試せるデータセットがあったら面白いので、MNISTのサンプルを少し弄るだけで使えそうなものを作りたいと思います。
###PythonでSVGを出力する
参考 : SVGをお手軽に出力するならQtが最強のライブラリではないか?
こちらを参考にさせて頂きました。
PythonでのSVG描画はsvgwriteが有名ですが今回はこちらの方法を利用しました。
まずSVGを描画するための要素をnumpy.random
でランダムに生成します。
今回は、
0から9までの数字
14から84までのサイズ
15種類のフォント
でランダムに2つ選び、
上下左右に位置をずらして、横に2枚並べて描画します。
from __future__ import unicode_literals, print_function, absolute_import
import os
import sys
import numpy as np
import PySide
from PySide.QtCore import *
from PySide.QtGui import *
from PySide.QtSvg import *
from PIL import Image
app = QApplication(sys.argv)
fontlist = ['Axis', 'Arial', 'Bahnscrift', 'Cambria', 'Century', 'Consolas', 'Courier', 'Georgia', 'Lucida', 'Myriad', 'Rockwell', 'Segoe', 'Symbol', 'Tahoma', 'TimesNewRoman']
def random_elements(n):
rd_text = np.random.randint(0, 9, (n, 2))
rd_size = np.random.randint(14, 84, (n, 2))
rd_font = np.random.choice(fontlist, n*2).reshape(n, 2)
rd_posi_x1 = np.random.randint(7, 35, (n, 1))
rd_posi_y1 = np.random.randint(14, 63, (n, 1))
rd_posi_w1 = np.random.randint(16, 56, (n, 1))
rd_posi_h1 = np.random.randint(16, 56, (n, 1))
rd_posi_x2 = np.random.randint(49, 70, (n, 1))
rd_posi_y2 = np.random.randint(14, 63, (n, 1))
rd_posi_w2 = np.random.randint(16, 56, (n, 1))
rd_posi_h2 = np.random.randint(16, 56, (n, 1))
rd_stack = np.concatenate((rd_text, rd_size, rd_font, rd_posi_x1, rd_posi_y1, rd_posi_w1, rd_posi_h1, rd_posi_x2, rd_posi_y2, rd_posi_w2, rd_posi_h2), axis=1)
return rd_stack
このパラメーターをPySideに渡してSVGを描画・保存をします。
def draw_svg(rd, n, width, dir_svg):
for i in range(n):
svg_gen = QSvgGenerator()
svg_gen.setFileName(dir_svg + str(i) + ".svg")
svg_gen.setSize(QSize(width, width))
svg_gen.setViewBox(QRect(0, 0, width, width))
painter = QPainter()
painter.begin(svg_gen)
text1 = str(rd[i,0])
text2 = str(rd[i,1])
size1 = int(rd[i,2])
size2 = int(rd[i,3])
font1 = str(rd[i,4])
font2 = str(rd[i,5])
posi1 = np.array(rd[i,6:10], dtype=int)
posi2 = np.array(rd[i,10:14], dtype=int)
x1, y1, w1, h1 = posi1
x2, y2, w2, h2 = posi2
rect1 = QRect(x1, y1, w1, h1)
painter.setPen(Qt.green)
painter.setFont(QFont(font1, size1))
painter.drawText(rect1, Qt.AlignCenter, text1)
rect2 = QRect(x2, y2, w2, h2)
painter.setPen(Qt.blue)
painter.setFont(QFont(font2, size2))
painter.drawText(rect2, Qt.AlignCenter, text2)
painter.end()
###SVGをPNGに変換する
参考 : PythonでSVGをPNGに変換する
こちらにあるPySideを用いてPNGへ変換する方法を参考にしました。
SVGの変換ライブラリはCairoSVGが有名ですが、インストールが上手くいかなかったためPySideの機能を使いました。
def svg2png(n, width, dir_svg, dir_png):
for i in range(n):
file_svg = dir_svg + str(i) + ".svg"
file_png = dir_png + str(i) + ".png"
r = QSvgRenderer(file_svg)
i = QImage(width, width, QImage.Format_ARGB32)
p = QPainter(i)
p.fillRect(0, 0, width, width, QColor(0, 0, 0))
#p.eraseRect(0, 0, width, width)
r.render(p)
i.save(file_png)
p.end()
###PNGをnumpy配列に変換する
PILでPNGを読み込んで、numpy.asarray
でnumpy配列に変換します。
def png2numpy(n, dir_png):
image_list = []
for i in range(n):
file_png = dir_png + str(i) + ".png"
pil = Image.open(file_png)
num = np.asarray(pil)
image_list.append(num)
image_numpy = np.array(image_list)
return image_numpy
###1つの関数にまとめる
random_elements
draw_svg
svg2png
png2numpy
以上の4つをまとめて、generate_datasets
という関数にします。
def generate_datasets(n, width, dir_svg, dir_png):
rd = random_elements(n)
draw_svg(rd, n, width, dir_svg)
svg2png(n, width, dir_svg, dir_png)
image_numpy = png2numpy(n, dir_png)
return image_numpy, rd
###訓練用データセット
訓練用に600枚のデータセットを作成します。
同じディレクトリにtrainフォルダ、その下のsvgフォルダとpngフォルダをos.mkdir()
で作ります。
そこを作業ディレクトリにしてSVGとPNGが生成されます。
PNGはnumpy配列に変換されtrain_image
に代入されます。
train_signal
はSVGの描画に用いたパラメーターです。これを教師データにします。
width
は画像サイズを決めるパラメーターです。
n = 600
width = 112
os.mkdir("./train")
os.mkdir("./train/svg")
os.mkdir("./train/png")
dir_svg = "train/svg/"
dir_png = "train/png/"
train_image, train_signal = generate_datasets(n, width, dir_svg, dir_png)
###テスト用データセット
テストデータセットをtestフォルダに100枚作成します。
test_image
test_signal
に生成されたnumpy配列を代入します。
n = 100
width = 112
os.mkdir("./test")
os.mkdir("./test/svg")
os.mkdir("./test/png")
dir_svg = "test/svg/"
dir_png = "test/png/"
test_image, test_signal = generate_datasets(n, width, dir_svg, dir_png)
###numpy配列の保存
numpy.save("ファイル名", 配列名)
で .npy
の拡張子に保存します。
np.save("train_image", train_image)
np.save("train_signal", train_signal)
np.save("test_image", test_image)
np.save("test_image", test_signal)
pngフォルダに以下のようなpngファイルが生成されました。
今回は以上になります。
次回では、このnumpy配列を使ってMNISTのサンプルコードを多少変えただけのモデルで学習を行いたいと思います。