以前、別記事にてアスキーアート関連の論文をまとめました
アスキーアート関連の論文を探してみた
その中で「アスキーアート分類手法の比較検討」にて「文字単位で画像特徴量(HOG)を抽出し...」というのをやってみたくて色々模索した記事です。この記事ではttfファイルをpngで出力するところまでやります。最終的な目標は機械学習によるAAの分類です。
本記事で紹介する方法は以下の手順を踏んでttfをpngに変換します。
- fonttoolsを使ってttfファイルのグリフ一覧を読み込み
- SVGPathPenを使って全グリフをSVGに変換する
- cairosvgを使ってSVGをPNGに変換する
- PNGをリサイズして縮小する※必要ないかも...
興味ある方は以下、手順を記載するので読み進めてください。なお、1〜2の内容は以下のブログを参考にさせていただいています。めちゃくちゃ詳しく書いてあるのでfonttools知らない方は是非とも読んでみてください。
fontTools のペンを使ってグリフのアウトラインを取得する
※以下の手順はjupyter notebookを利用することを想定しています。
1. fonttoolsを使ってttfファイルのグリフ一覧を読み込み
まず、Pythonでttfファイルを扱うためにfonttoolsを使用します。インストール方法はpipから
pip install fonttools
fonttoolsでフォントを読み込んでグリフを取得するために以下の処理を行います。
from fontTools.ttLib import TTFont
font = TTFont('/Users/eskey/workspace/aahub/ml/Saitamaar.ttf')
glyph_set = font.getGlyphSet()
cmap = font.getBestCmap()
glyph_setがグリフオブジェクト、cmapがUnicodeとグリフの対応テーブルになります。
2. SVGPathPenを使って全グリフをSVGに変換する
グリフ情報の準備が済んだのでSVGに変換します。まずは、ファイル出力用にディレクトリを作成します。
import os
os.mkdir("output_svg")
os.mkdir("output_png")
os.mkdir("output_resize")
次に、全グリフをSVGに変換する関数を記述します。
from textwrap import dedent
from fontTools.pens.svgPathPen import SVGPathPen
# GlyphをSVGに変換
def save_all_glyph_as_svg(font):
glyph_set = font.getGlyphSet()
cmap = font.getBestCmap()
output_path = "output_svg/"
for c in cmap:
glyph_name = cmap[c]
glyph = glyph_set[glyph_name]
svg_path_pen = SVGPathPen(glyph_set)
glyph.draw(svg_path_pen)
ascender = font['OS/2'].sTypoAscender
descender = font['OS/2'].sTypoDescender
width = glyph.width
height = ascender - descender
content = dedent(f'''\
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 {-ascender} {width} {height}">
<g transform="scale(1, -1)">
<path d="{svg_path_pen.getCommands()}"/>
</g>
</svg>
''')
with open(output_path + str(c) + ".svg", 'w') as f:
f.write(content)
上記関数はcmapテーブルをループで回して全てのグリフをSVGとして保存する関数です。
save_all_glyph_as_svg(font)
実行するとoutput_svg
フォルダにSVGが保存されます。
3. cairosvgを使ってSVGをPNGに変換する
全グリフをSVGで保存できたら次はPNGに変換します。cariosvgもpipでインストールできます。
pip install cairosvg
さて、cariosvgにはSVGをPNGに変換するsvg2pngという関数が用意されています。
svg2png(url="SVGのパス", write_to="出力先のパス")
先ほどのoutput_svg
の全SVGをPNGに変換する関数を作成します。
from cairosvg import svg2png
import os
# SVGをPNGに変換する
def convert_all_svg_to_png():
path = "output_svg"
files = os.listdir(path)
files_file = [f for f in files if os.path.isfile(os.path.join(path, f))]
for fname in files_file:
bname, ext = os.path.splitext(fname)
try:
svg2png(url='output_svg/' + bname + '.svg', write_to='output_png/' + bname + '.png')
except:
print(fname)
return
上記処理ではtry〜 exceptでsvg2pngの例外処理を行っています。これはsvg2pngがゼロ幅スペースのユニコードで異常終了するからです。以下がsvg2pngで落ちると思われるコードになります。
uni200B
uniE9D7
uniE9D6
uniE9D5
uni035F
uni035D
uni035E
uni0009
では、関数を実行します。
convert_all_svg_to_png()
- PNGをリサイズして縮小する
上記までの処理でttfをpngに変換する目的は達成していますが、この状態だと非常にサイズがでかいです。
状況に応じて画像をリサイズする必要があるかもしれませんので以下処理を書き残しておきます。以下の関数はoutput_png
フォルダの全画像ファイルのサイズを20/1のサイズに変更します。
import os
import glob
from PIL import Image
# PNGをリサイズする
path = "output_png"
files = os.listdir(path)
files_file = [f for f in files if os.path.isfile(os.path.join(path, f))]
for fname in files_file:
bname, ext = os.path.splitext(fname)
if ext != ".png":
continue
img = Image.open("./output_png/" + bname + ext)
img_resize = img.resize((int(img.width/20), int(img.height/20)))
img_resize.save("./output_resize/" + bname + "_herf" + ext)
これで、20分の1のサイズで文字ファイルを全部画像にすることができました。このあと、HOGを抽出してクラスタリングしたり、AAからうまいことHOGで表せるようにしたいと思います。
それでは。