Julia の REPL ででも画像をリッチにプレビューしたいんです(Sixel.jl 入門).

# 本日は

- よく知られているように(?) Mac でよく使われる iTerm2 では [`imgcat` というコマンド](https://iterm2.com/documentation-images.html) を用いて画像をターミナルの上でプレビューすることができます.

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/135978/3b80cf72-5798-d9f1-011a-1a630206fc94.png)

- SixelGraphics に対応していれば iTerm2 以外でもできます:
  - 参考: [iTerm2以外でもimgcatをしたい！！](https://yukineko.me/archives/819) 

こんな感じのことを Julia の REPL でもしたいですよね？ もちろん REPL の [Julian mode](https://docs.julialang.org/en/v1/stdlib/REPL/#The-Julian-mode) から [Shell mode](https://docs.julialang.org/en/v1/stdlib/REPL/#man-shell-mode) へ遷移してやることもできます.

```julia
julia> # これが Julian mode
julia> ; # ここでセミコロンを入力
shell> # これがシェル
shell> ls
sin.png
shell> imgcat sin.png
shell> # Ctrl-C を入力して Julian mode に遷移する
julia> # 戻ってきた
```

が，これをするには REPL のモードを変更する必要があり一手間かかります. Julian mode の状態 (`julia> `) で直接描画できないのでしょうか? :thinking: 

# REPL でもできらぁ！

ここで [Sixel.jl](https://github.com/johnnychen94/Sixel.jl) に登場してもらいます. そうすると下記のようになります:

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/135978/4209e252-e958-d2db-5eac-1b69aa4a7111.png)

できてますね. (`testimage` 関数の呼び出しでエラーが起きた場合は `pkg> add ImageIO` をやってみてください)

`sixel_encode` は `using Sixel` とロードすることで使えます. この関数の入力の型は `RGB{N0f8}` を要素とする配列を受け付けることができます. [Images.jl](https://github.com/JuliaImages/Images.jl) をはじめとるす Julia 画像処理周りでよく出てきます.

```julia
julia> m = testimage("mandril_color")
512×512 Array{RGB{N0f8},2} with eltype ColorTypes.RGB{FixedPointNumbers.N0f8}:
 RGB{N0f8}(0.651,0.545,0.243)  RGB{N0f8}(0.22,0.188,0.137)   …  RGB{N0f8}(0.671,0.741,0.443)
 RGB{N0f8}(0.416,0.451,0.196)  RGB{N0f8}(0.545,0.392,0.169)     RGB{N0f8}(0.459,0.486,0.224)
 RGB{N0f8}(0.294,0.251,0.125)  RGB{N0f8}(0.482,0.42,0.188)      RGB{N0f8}(0.38,0.29,0.157)
 RGB{N0f8}(0.227,0.29,0.176)   RGB{N0f8}(0.541,0.518,0.212)     RGB{N0f8}(0.196,0.29,0.165)
 RGB{N0f8}(0.384,0.286,0.165)  RGB{N0f8}(0.549,0.482,0.2)       RGB{N0f8}(0.282,0.325,0.157)
 RGB{N0f8}(0.286,0.29,0.125)   RGB{N0f8}(0.282,0.294,0.125)  …  RGB{N0f8}(0.671,0.741,0.451)
 ⋮                                                           ⋱
 RGB{N0f8}(0.388,0.38,0.384)   RGB{N0f8}(0.345,0.412,0.482)     RGB{N0f8}(0.255,0.294,0.263)
 RGB{N0f8}(0.322,0.357,0.29)   RGB{N0f8}(0.349,0.416,0.478)     RGB{N0f8}(0.286,0.294,0.251)
 RGB{N0f8}(0.353,0.416,0.478)  RGB{N0f8}(0.443,0.514,0.514)     RGB{N0f8}(0.294,0.314,0.329)
 RGB{N0f8}(0.455,0.541,0.631)  RGB{N0f8}(0.451,0.58,0.506)      RGB{N0f8}(0.29,0.294,0.224)
 RGB{N0f8}(0.451,0.588,0.667)  RGB{N0f8}(0.455,0.537,0.62)   …  RGB{N0f8}(0.251,0.224,0.251)
 RGB{N0f8}(0.451,0.588,0.667)  RGB{N0f8}(0.455,0.537,0.62)      RGB{N0f8}(0.251,0.224,0.251)
```

グレースケールのも対応しています:

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/135978/ea85e979-5823-26f3-8276-33824d49e4cb.png)

こんな感じでお使いのターミナルがリッチな表現に対応していれば Julia の REPL上でもいい感じにできます.

# グラフのプレビューできます？

Plots.jl の結果を REPL で確認したいですよね？ もちろん [UnicodePlots.jl](https://github.com/JuliaPlots/UnicodePlots.jl) をバックエンドとして使うこともできます.

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/135978/4d5a4e10-e91d-9435-1341-877b788557a2.png)

できますが, もっとカラフルに見せたいところです.

# できらぁ！

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/135978/9df4b67c-e749-097e-7f43-2822c1f6d319.png)

下記のコードをコピーして REPL に貼り付ければOKです.

```julia
using FileIO, Sixel, Plots
gr()
buf = IOBuffer()
show(buf, MIME("image/png"), plot(sin, size=(1000, 750)))
buf |> load |> sixel_encode
```

- `show` 関数で `plot(sin)` というプロットオプジェクトを png にて保存するために必要なデータを出力させます. この `MIME` は
マルチパーパス・インターネット・メール・エクステンションの略です. Julia のオブジェクトがどのように表示されるのか・どのロジックを選択するのかを知るにはYouTube のリンクの説明がわかりやすいです. 英語を翻訳した日本語字幕も表示させることができます.

https://www.youtube.com/watch?v=S1Fb5oNhhbc

- さて，show 関数によって出力されたデータはディスクにダンプさせる(= savefig などでファイルに一旦保存する)のではなく, メモリに上に乗っけておきます. 出力を流し込む先を標準出力 stdout ではなく `buf=IOBuffer()` オブジェクトに出力を流しておきます. 
- さらに `buf` に格納されたデータを `FileIO.load` 関数で読み込みます. その結果を `sixel_encode` でエンコードすればめでたしめでたし．

# ローカルのファイルを読み込んで表示

もちろんファイルに保存している png データを読み込んで表示させることもできますね．

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/135978/58006e9f-e0e5-2a17-42e1-1cd908054783.png)

素晴らしい.

# `sixel_encode` の入力がめんどくさい?

何回も `sixel_encode` を入力するのが面倒な場合は [ImageInTerminal.jl](https://github.com/JuliaImages/ImageInTerminal.jl) をロードすることで解決できます. これにより 画像オブジェクトを REPL での表示を要素を表示するテキストのものからリッチにな表示にするように内部で表示をコントロールしてくれます.

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/135978/655bbd92-2ccd-aee5-888b-ab65159a3454.png)

# Appendix

## お使いのターミナルがうまく表示されない？

例えば Mac デフォルトの `Terminal` だと少し残念な表示になります.

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/135978/9d93f5d3-31cc-fdff-5928-695a53ceaed9.png)

これはデフォルトのターミナルが sixel format に対応してないからです.
リッチにプレビューしたい場合は下記の関数が `true` を返す必要があります.

```julia
julia> Sixel.is_sixel_supported()
false
```

万が一 `true` にもかかわらず表示がリッチにならない場合は Sixel.jl にバグレポートを出すと良いでしょう.