Elixir Livebook 0.8.0 の新機能 Kino.Input.image の画像入力が便利！

## はじめに

Livebook 0.8.0 がリリースされました :rocket:

https://github.com/livebook-dev/livebook

その中の新機能 `Kino.Input.image` で、画像をローカルから簡単にアップロードできるようになりました！

## セットアップ

Kino の 0.8 と、画像表示などのために Nx 、 Evision をインストールします

```elixir
Mix.install([
  {:nx, "~> 0.4"},
  {:evision, "~> 0.1"},
  {:kino, "~> 0.8"}
])
```

## 画像選択

`Kino.Input.image` を実行すると、画像アップロード用の入力エリアが表示されます

```elixir
image_input = Kino.Input.image("IMAGE")
```

![image.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1485835/a3f0e758-1dec-4a00-3b0a-f08cc8c20179.gif)

画像をドラッグ&ドロップ、もしくは入力エリアをクリックしてファイル選択すると、選択した画像が出力結果に表示されます

## 画像表示

選択した画像を取得してみます

```elixir
image = Kino.Input.read(image_input)
```

![スクリーンショット 2022-12-09 17.13.22.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1485835/d9267c6e-09a6-de75-6090-e6eaa4bf9b97.png)

デフォルトだとこのように `:rgb` の形式で取得されます

この場合、 data を以下のようにマトリックスに変換すれば画像として表示できます

```elixir
image.data
|> Nx.from_binary(:u8)
|> Nx.reshape({image.height, image.width, 3})
|> Evision.Mat.from_nx_2d()
# Evision では BGR なので変換する
|> Evision.cvtColor(Evision.Constant.cv_COLOR_RGB2BGR())
```

![スクリーンショット 2022-12-09 17.15.00.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1485835/527f4988-d03e-dff7-1f20-d27876a71eaa.png)

Evision を使わず、テンソルのまま `Kino.Image.new` に入れる方法もあります

```elixir
image.data
|> Nx.from_binary(:u8)
|> Nx.reshape({image.height, image.width, 3})
|> Kino.Image.new()
```

![スクリーンショット 2022-12-09 17.16.47.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1485835/4c423149-8ae2-5418-9378-fb7d94770747.png)

## PNG指定

フォーマットを PNG に指定することもできます

```elixir
image_input = Kino.Input.image("IMAGE", format: :png)
```

その場合、データは以下のようになります

```elixir
image = Kino.Input.read(image_input)
```

![スクリーンショット 2022-12-09 17.18.56.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1485835/c371945c-bb7c-9957-9840-3950b464916c.png)

Evision で使う場合はこっちの方が扱いやすいです

```elixir
image.data
|> Evision.imdecode(Evision.Constant.cv_IMREAD_COLOR())
```

![スクリーンショット 2022-12-09 17.20.18.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1485835/ef4fc387-b11a-adde-284c-f8f0ca49ad79.png)

## JPEG指定

JPEG指定もできます

```elixir
image_input = Kino.Input.image("IMAGE", format: :jpeg)
```

この場合も Evision のマトリックスには変換しやすいですね

```elixir
image_input
|> Kino.Input.read()
|> then(&(&1.data))
|> Evision.imdecode(Evision.Constant.cv_IMREAD_COLOR())
```

## リサイズ

指定したサイズにリサイズできます

`fit: :contain` を指定した場合、指定したサイズに収まるように縦横比を保持したままリサイズします

```elixir
image_input = Kino.Input.image("IMAGE", format: :png, size: {200, 500}, fit: :contain)
```

![スクリーンショット 2022-12-09 17.26.37.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1485835/35d496d6-7eea-37c6-3316-fcf3d099772f.png)

`fit: :match` を指定した場合、指定したサイズになるよう、縦横比を変えてリサイズします

```elixir
image_input = Kino.Input.image("IMAGE", format: :png, size: {200, 500}, fit: :match)
```

![スクリーンショット 2022-12-09 17.28.14.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1485835/79039e20-745e-84e3-4957-5de710803f5b.png)

`fit: :pad` を指定した場合、指定したサイズになるよう、余白に黒を詰めます

```elixir
image_input = Kino.Input.image("IMAGE", format: :png, size: {200, 500}, fit: :pad)
```

![スクリーンショット 2022-12-09 17.29.44.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1485835/19fb3429-23a9-bdde-826f-883b1c15f6a4.png)

`fit: :crop` を指定した場合、指定したサイズになるよう、はみ出したところを切り捨てます

```elixir
image_input = Kino.Input.image("IMAGE", format: :png, size: {200, 500}, fit: :crop)
```

![スクリーンショット 2022-12-09 17.31.05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1485835/bbee6383-874b-2de3-a0a3-3d941639ea21.png)

## ダウンロード

`Kino.Download.new` を使えば、リサイズした画像をそのままダウンロードすることもできます

```elixir
Kino.Download.new(fn ->
  image_input
  |> Kino.Input.read()
  |> then(&(&1.data))
end,
filename: "resized.png")
```

![スクリーンショット 2022-12-09 17.43.18.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1485835/e4411ac8-8f15-a5eb-35f3-eb87df5fedd1.png)

## まとめ

これでローカルの好きなファイルを好きに加工できますね