mathjax

2013年8月30日金曜日

reStructuredText からmarkdownへの変換

reStructuredText からmarkdownへの変換

ソースファイル
前回、ノートブックファイルをreStructured text(rst)に変換することによって、画像ファイルが取り出せるという事を書きました。 その際、画像ファイルの対応箇所を見つけるのに、rstファイルをテキストエディタで開く必要がありました。
その続編として、rstがmarkdownに変換できる事が分かりましたので、紹介しておきます。(コメントを追加しましたので、参照下さい。)
ノートブックを他の形式に変換するnbconvertでは内部でpandocというソフトを使っているのですが、これにはパイソン版(正確には薄いラッパーと呼ぶそうです)があります。 これをpipなどを使ってインストールしておきます。 すると以下のように簡単なコマンドでmarkdown(md)に変換できました。
In [1]:
import pypandoc
output = pypandoc.convert('SVG2PNG.rst', 'md')
ただ、残念ながらそのままではノートブックでは読めませんでした。画像ファイルへのリンクの仕方が微妙に違うようです。
詳細は不明なのですが。
参考サイト
ともかくも、"files/"というのを付ければよさそうなので、以下のコマンドを書きました。 正規表現というモジュール(re)を用いて、該当箇所を書き換えます。
今回は、svgファイルだけに関心があるので、そこだけを書き換えますが、pngファイルも同様に可能です。
また、画像ファイルへのリンクの手前に次の画像はこういうファイル名だよ、と注釈を挿入しました。
In [2]:
import re
output2=re.sub(r'(!\[image\]\()(.+\.(svg|SVG)\))',r'</br><FONT color="red">(the next image is \2</br>\n\n\1files/\2</FONT> ',output)
#output2=re.sub(r'(!\[image\]\()(.+\.(png|svg|PNG|SVG)\))',r'**(the next image is \2**\n\n\1files/\2 ',output)
#pngを含める場合は上記のようにする。
出来上がった文字列(output2)をmarkdownセルに書き込めば良いのですが、簡単な方法が見つからなかったので、以下のように一旦ファイルに落としてから、読み出しました。
In [3]:
f = open('tmp.md', 'w') # 書き込みモードで開く
f.write(output2) # 引数の文字列をファイルに書き込む
f.close()
%load tmp.md
(以下、一部抜粋)
In[6]: .. code:: python
x=np.linspace(0,2*np.pi) plt.plot(x,np.sin(x)) plt.figure()
plt.plot(x,np.cos(x))
(the next image is SVG2PNG_files2PNG_8_1.svg)

(the next image is SVG2PNG_files2PNG_8_2.svg)



(以上、抜粋終わり)

まとめ

上のセルはcodeセルとして出力されますので、手動でMarkdownセルに変更しました。 また、長いので、SVGファイルがある周辺だけを抜粋しました。 このようにすれば、画像ファイルとノートブックとの対応は見やすいと思います。

なお、ブログに貼り付けるためにこのノートはnbconvertでhtmlに変換していますが、その際にローカルなSVGとのリンクは切れてしまっているので、代わりにPNGファイルを貼り込んであります。

nbconvertが改良されれば、nbconvertで変換したmarkdownで済む話なのかも知れませんが、参考まで。

2013年8月29日木曜日

SVGファイルの利用

SVGファイルの利用

ソースファイル

Ipython Notebook内にMatplotlibで描画されるグラフは、標準ではpngと呼ばれる、ビットマップイメージです。 この形式はブラウザ上に表示するには適していますが、そのままの設定で印刷物にすると解像度が十分ではありません。

plt.savefig()というコマンドで実行時に高解像度の画像ファイルに変換する事は可能で、 恐らく、それが一般的な使用方法として想定されているものと思います。 しかし、この方法だと画像保存のために演算を初めからしなおして、必要な箇所にplt.savefig()を追加していく必要があります。 計算時間が掛かる場合など、このようなやり方を避けたい場合もあると思います。

その解決方法には2つあると思います。 一つ目は、初めから高解像度のpngファイルにするという方法です。 これは、ipython notebook用のmatplotlibの設定を変える必要があります。

そのために、今回はipython notebookをpylabの引数無しで起動しています。

Matplotlibの設定を変えるには、pylabの実行前に以下のようなコマンドを実行する必要があります。

In [1]:
%config InlineBackend.rc = {'font.size': 20, 'figure.figsize': (12.0, 8.0),'figure.facecolor': 'white', 'savefig.dpi': 72, 'figure.subplot.bottom': 0.125, 'figure.edgecolor': 'white'}
In [2]:
%pylab inline
Populating the interactive namespace from numpy and matplotlib

In [3]:
x=np.linspace(0,2*np.pi)
plt.plot(x,np.sin(x))
plt.figure()
plt.plot(x,np.cos(x))
Out[3]:
[<matplotlib.lines.Line2D at 0x3fda830>]

デフォルトの設定(私の場合にはC:.ipythondefaultnotebook_config.py)の値と比べて、'font.size'と 'figure.figsize'を大きくしました。 ところが、こうすると線が細すぎます。 つまり、このようなやり方だと折角Matplotlibにおいて、グラフを綺麗に見せるために設定されている様々な初期設定を、色々と変えなければならなくなる可能性があります。

また、ブラウザで見る際には見た目が大きすぎます。

2つ目の方法は画像形式をSVGに変える事で、これが本命です。

In [4]:
%config InlineBackend.rc = {'font.size': 10, 'figure.figsize': (6.0, 4.0),'figure.facecolor': 'white', 'savefig.dpi': 72, 'figure.subplot.bottom': 0.125, 'figure.edgecolor': 'white'}

まずは設定を戻しておきます。

In [5]:
%config InlineBackend.figure_format = 'svg'
%pylab inline
Populating the interactive namespace from numpy and matplotlib

In [6]:
x=np.linspace(0,2*np.pi)
plt.plot(x,np.sin(x))
plt.figure()
plt.plot(x,np.cos(x))
Out[6]:
[<matplotlib.lines.Line2D at 0x40c4ed0>]

この方法だと、ブラウザ上で大きく表示されていませんが、全体の表示を拡大(「ctrl」+「+」)すると解像度が高い事が分かります。 ベクターグラフィックなので、当然ですが。

この埋め込まれたSVGファイルを取り出す方法として二つの方法を紹介します。

一つはInternetExplorer(IE)を用いる方法です。 ノートブックファイル(".ipynb")やnbconvertで作成したhtmlファイルをIEで開き、SVGファイルの上でマウスを右クリックすれば、svgファイルで保存することが出来ます。 その際、pngファイルやbitmapファイルでも保存できますが、高い解像度の画像にはなりませんでした。 一つのノートブックから数個のグラフを保存するにはこの方法が良いでしょう。

二つ目は、nbconvertを用いる方法です。nbconvertでrst(reStructured Text)というファイル形式にすると、画像ファイルは別フォルダに保存されます。

In [7]:
!ipython nbconvert --to rst SVG2PNG
[NbConvertApp] Using existing profile dir: u'C:\\Users\\mk\\.ipython\\profile_default'
[NbConvertApp] Converting notebook SVG2PNG.ipynb to rst
[NbConvertApp] Support files will be in SVG2PNG_files\
[NbConvertApp] Loaded template rst.tpl
[NbConvertApp] Making directory SVG2PNG_files
[NbConvertApp] Writing 7211 bytes to SVG2PNG.rst

その際、画像ファイルのファイル名には自動的に番号が振られますので、どのファイルがどのグラフに対応しているのかがすぐには分かりません。

In [8]:
!dir SVG2PNG_files\*.SVG
 ドライブ C のボリューム ラベルがありません。
 ボリューム シリアル番号は 4655-D599 です

 C:\Users\mk\Documents\Python\ipython_notebook\SVG2PNG_files のディレクトリ

2013/08/30  13:04            16,980 SVG2PNG_8_1.svg
2013/08/30  13:04            16,981 SVG2PNG_8_2.svg
               2 個のファイル              33,961 バイト
               0 個のディレクトリ  669,050,675,200 バイトの空き領域

似たようなグラフがある場合には出来上がった".rst"という拡張子のファイルをエディタで開いてみて下さい。 rstはipython notebookで採用されているmarkdownと似たような軽量マークアップ言語なので、エディタでみればノートブックとの対応は完全につくはずです。

nbconvertはmarkdownにも変換できるとされているのですが、なぜか私が変換すると、画像ファイルへのリンクが出来ませんでした。 まだバグがあるのだと思います。 markdownへの変換が出来れば、IpythonNotebookに貼り付ける事で対応がより分かりやすくなるのですが。。

SVGからPNGへの変換

さて、出来上がったSVGファイルを高解像度のPNGファイルにしたいという方も多いと思います。 SVGというのは国際標準に基づいた形式なのですが、現実には対応ソフトが少ないのです。 PNGへの変換ソフトとして、Inkscapeを紹介します。 これはWindowsでも簡単に動作するオープンソースのソフトです。 このソフトはSVGのファイルを作成、編集するのが本来の目的なのですが、ファイル変換ソフトとしても使えます。

InkscapeはPythonのソフトではないのですが、コマンドラインからGUI無しでファイル変換を実行できます。 そこでsubprocessというpythonモジュールを用いて、ipython notebook上から実行してみましょう。

まずは、変換するファイルの入ったフォルダを指定します。

In [9]:
def get_directory():
    import Tkinter
    import tkFileDialog
    import tkMessageBox
    root=Tkinter.Tk()
    root.withdraw()
    tkMessageBox.showinfo('showinfo','ディレクトリ名を取得します。')
    dirname=tkFileDialog.askdirectory()
    return dirname
image_folder=get_directory()
print image_folder
C:/Users/mk/Documents/Python/ipython_notebook/SVG2PNG_files

フォルダ内のsvgファイル名を取得します。

In [10]:
import glob
import os.path
filenames = glob.glob(image_folder+'/*.svg')
filenames_without_ext=[] 
for filename in filenames:
    #print file
    root, ext = os.path.splitext(filename)
    #print root
    filenames_without_ext.append(root)
for filename in filenames_without_ext:
    print filename
C:/Users/mk/Documents/Python/ipython_notebook/SVG2PNG_files\SVG2PNG_8_1
C:/Users/mk/Documents/Python/ipython_notebook/SVG2PNG_files\SVG2PNG_8_2

Inkscapeをsubprocessで動かして、SVGファイルをPNGファイルに変換します。この段階で解像度を選択できます。

In [11]:
import subprocess
for file in filenames_without_ext:
    args= '"c:\Program Files\Inkscape\Inkscape.com" -z -e ' +file+'.png '+ '-w 1024 '+file+'.svg '
    #上の-w 1024の数字で横幅のピクセル数が決まる。
    #-h 600 などと縦のピクセル数で指定することも可能。
    p=subprocess.Popen(args,stdout=subprocess.PIPE)
    for i in p.stdout.read().split('\n'):
        print i
    if p.wait()==0:
        print u"変換成功!"
Background RRGGBBAA: ffffff00

Area 0:0:472.5:315 exported to 1024 x 683 pixels (195.048 dpi)

Bitmap saved as: C:/Users/mk/Documents/Python/ipython_notebook/SVG2PNG_files\SVG2PNG_8_1.png


変換成功!
Background RRGGBBAA: ffffff00

Area 0:0:472.5:315 exported to 1024 x 683 pixels (195.048 dpi)

Bitmap saved as: C:/Users/mk/Documents/Python/ipython_notebook/SVG2PNG_files\SVG2PNG_8_2.png


変換成功!

変換結果を表示します。

In [12]:
from IPython.core.display import Image, display
for file in filenames_without_ext:
    filename=file+'.png'
    print filename
    display(Image(filename=filename,width=512))
    #そのまま表示すると大きすぎるので縮小して表示する。なぜか、nbconvert後のhtmlでは元に戻ってしまうので、貼り直しました。
    
C:/Users/mk/Documents/Python/ipython_notebook/SVG2PNG_files\SVG2PNG_8_1.png

C:/Users/mk/Documents/Python/ipython_notebook/SVG2PNG_files\SVG2PNG_8_2.png

まとめ

SVG形式は殆どのブラウザが対応しています。また、グラフによってはファイルサイズを小さくできます。 ですから、デフォルトでSVGになるように、初期設定ファイル(ipython_notebook_config.py)を変更しておいても良いと思います。 デメリットはブラウザ上で画像だけを拡大するというのが出来なくなる程度でしょう。 また、今回は自分自身の中でnbconvertや変換を行いましたが、この作業は別のノートブック上で行った方が実際には混乱が少ないと思います。