はじめに
Elixir Livebook で画像処理やグラフ表示をしているとき、結果を横に並べて比較したい時がありますよね
というわけで画像やグラフを横に並べる方法です
タブ切り替えで表示する場合はこちら
実行環境
- Elixir: 1.14.2 OTP 24
- Livebook: 0.8.0
セットアップ
画像とグラフの表示に最低限必要なモジュールをインストールします
Mix.install([
{:nx, "~> 0.4"},
{:kino, "~> 0.8"},
{:kino_vega_lite, "~> 0.1.7"}
])
画像を並べる
指定した色の画像を生成する関数を用意します
generate_image = fn color ->
{height, width} = {160, 160}
color
|> Nx.tensor(type: :u8)
|> Nx.tile([height, width, 1])
|> Kino.Image.new()
end
例えば以下のように書いた場合、一番最後の画像だけが表示されてしまいます
generate_image.([255, 0, 0])
generate_image.([0, 255, 0])
generate_image.([0, 0, 255])
generate_image.([255, 255, 0])
generate_image.([255, 0, 255])
generate_image.([0, 255, 255])
以下のように Kino.render
を入れると、途中でも結果を表示することができます
ただし、縦に並ぶので多くなると見にくいです
generate_image.([255, 0, 0])
generate_image.([0, 255, 0])
generate_image.([0, 0, 255])
|> Kino.render()
generate_image.([255, 255, 0])
generate_image.([255, 0, 255])
|> Kino.render()
generate_image.([0, 255, 255])
表示したいものを配列に入れて Kino.Layout.grid
に渡します
そのとき、 columns
に列数を指定しておくと、その分横に並んで見やすくなります
[
generate_image.([255, 0, 0]),
generate_image.([0, 255, 0]),
generate_image.([0, 0, 255]),
generate_image.([255, 255, 0]),
generate_image.([255, 0, 255]),
generate_image.([0, 255, 255]),
]
|> Kino.Layout.grid(columns: 3)
当然、 Enum.map
でパイプしてもいいので、パラメータ変化に伴う結果の変化を並べて表示したいときなどに有効です
[
[0, 0, 0], [255, 0, 0], [0, 255, 0], [0, 0, 255],
[255, 255, 0], [255, 0, 255], [0, 255, 255], [255, 255, 255]
]
|> Enum.map(&generate_image.(&1))
|> Kino.Layout.grid(columns: 4)
boxed
(デフォルトは false) に true を指定すると、出力全体の外側に枠が付きます
gap
で各出力の間隔を指定できます
[
[255, 0, 0], [0, 255, 0], [0, 0, 255],
[255, 255, 0], [255, 0, 255], [0, 255, 255]
]
|> Enum.map(&generate_image.(&1))
|> Kino.Layout.grid(columns: 3, boxed: true, gap: 60)
ネストする(入れ子にする)こともできるため、複雑な形にすることも可能です
[
generate_image.([255, 0, 0]),
generate_image.([0, 0, 255]),
[
generate_image.([255, 0, 0]),
generate_image.([0, 0, 255]),
generate_image.([0, 0, 255]),
generate_image.([255, 0, 0]),
]
|> Kino.Layout.grid(columns: 2, gap: 10),
generate_image.([0, 0, 255]),
generate_image.([255, 0, 0]),
]
|> Kino.Layout.grid(columns: 5, gap: 10)
グラフの場合
$ y = x^p $ のグラフをプロットする関数を用意します
plot = fn p ->
x = Nx.iota({10})
y = Nx.power(x, p)
VegaLite.new(width: 180)
|> VegaLite.data_from_values(%{x: Nx.to_flat_list(x), y: Nx.to_flat_list(y)})
|> VegaLite.mark(:line)
|> VegaLite.encode_field(:x, "x", type: :quantitative)
|> VegaLite.encode_field(:y, "y", type: :quantitative)
end
画像と同じように、 Kino.Layout.grid
で並べることができます
0..8
|> Enum.map(&plot.(&1))
|> Kino.Layout.grid(columns: 3)
まとめ
Kino.Layout.grid
によって画像やグラフを自在に並べることができました
もちろん他の出力も全て同様に扱うことが可能なので、様々な応用で表現力を向上させましょう