LoginSignup
2
4

More than 3 years have passed since last update.

Julia / Plots によるプロット画像化の過程を追う

Last updated at Posted at 2020-08-15

知りたいこと

using Plots

plt = plot([sin, cos])
savefig(plt, "sincos.png")

sincos.png

上の例において、プロットを画像化する際の処理を詳しく調べたい。

plt の内部構造も気になるところだが、その点については機会を改めたい。

output.jl

savefig

savefig(plt, "sincos.png")

ファイル名の拡張子に応じて、対応する関数 (この場合は png) に処理を受け渡す。

png

png(plt, "sincos.png")

PNG の MIME を持たせて Base.show に処理を受け渡す。

Base.show

https://github.com/JuliaPlots/Plots.jl/blob/master/src/output.jl#L210
(様々な MIME に対してメタプログラミングで定義されている)

io = open("sincos.png", "w")
show(io, MIME("image/png"), plt)

本来は、名前の通りに表示を目的とした関数である。
https://docs.julialang.org/en/v1/base/io-network/#Base.show-Tuple{IO,Any,Any}

ファイルへの書き込みは IO にデータを流し込むという処理であり、表示と共通化できるので、 Base.show でまとめて請け負っているのだろう。

prepare_output (ビューポイントの設定?)の後、内部関数 _show に処理を受け渡す。

--
【補足】
例えば、

plt = plot([sin, cos])

などで直接プロットが表示される場合も、暗黙的に呼ばれる display(plt) からこの show に処理が移譲される。

backends/gr.jl

内部関数 _show について説明していく。
https://github.com/JuliaPlots/Plots.jl/blob/master/src/backends/gr.jl#L1968

バックエンドに応じた分岐

この _show で、バックエンドごとに処理が分岐する。バックエンドの情報を plt 変数の型パラメータで保持し、多重ディスパッチしている。

typeof(plt)
# Result: Plots.Plot{Plots.GRBackend}

_show(io, MIME("image/png"), plt)

# backends/gr.jl
function _show(io::IO, ::MIME{Symbol("image/png")}, plt::Plot{GRBackend})
    # 省略
end

本記事ではバックエンドを GR として進めていくが、他のバックエンドでもそれぞれ _show が定義されている。プロット時には、バックエンドに対応した _show メソッドが呼ばれることになる。各バックエンドでの _show の定義は backends ディレクトリに含まれている。

_show の定義を得る

そのままでは読みにくいので、 quote で囲んで補間しよう。
https://docs.julialang.org/en/v1/manual/metaprogramming/#Interpolation-1

mime, fmt = "image/png" => "png"

quote
    function _show(io::IO, ::MIME{Symbol($mime)}, plt::Plot{GRBackend})
        GR.emergencyclosegks()
        filepath = tempname() * "." * $fmt
        env = get(ENV, "GKSwstype", "0")
        ENV["GKSwstype"] = $fmt
        ENV["GKS_FILEPATH"] = filepath
        gr_display(plt, $fmt)
        GR.emergencyclosegks()
        write(io, read(filepath, String))
        rm(filepath)
        if env != "0"
            ENV["GKSwstype"] = env
        else
            pop!(ENV,"GKSwstype")
        end
    end
end

出力の quote を外して、適当にコメントを追加した。

function _show(io::IO, ::MIME{Symbol("image/png")}, plt::Plot{GRBackend})
    GR.emergencyclosegks()
    filepath = tempname() * "." * "png"  # データのやり取りに使用する一時ファイル名
    env = get(ENV, "GKSwstype", "0")     # 設定の退避
    ENV["GKSwstype"] = "png"             # フォーマットを環境変数で設定
    ENV["GKS_FILEPATH"] = filepath       # 出力ファイル名も同様
    gr_display(plt, "png")               # プロットを画像化して一時ファイルに保存
    GR.emergencyclosegks()
    write(io, read(filepath, String))    # 画像を読み出して IO に出力
    rm(filepath)                         # 一時ファイルを削除
    if env != "0"
        ENV["GKSwstype"] = env           # 設定を元に戻す
    else
        pop!(ENV, "GKSwstype")
    end
end

GR の環境変数を用いた設定一覧
https://gr-framework.org/environment_variables.html

2
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
4