はじめに
Elixir の Image モジュールで減色処理(画像に使う色の種類を減らす処理)を実行します
印刷コストの削減などを目的として使われる処理です
どんな処理なのかは実行結果を見るのが分かりやすいので、読み進めてください
実装したノートブックはこちら
セットアップ
必要なモジュールをインストールします
Mix.install(
[
{:image, "~> 0.52"},
{:kino, "~> 0.13"},
{:scholar, "~> 0.3"},
{:exla, "~> 0.7"}
],
config: [
nx: [
default_backend: EXLA.Backend,
default_defn_options: [compiler: EXLA]
]
]
)
減色処理には Scholar モジュールが必須です(コード上出てきませんが、内部的に使用しています)
また、そのままでは時間がかかりすぎるため、 EXLA をインストールして設定を追加しています
画像の準備
任意の画像を読み込みます
ただし、透過画像だと上手く処理できないため、不透明度 = アルファチャネル(RGBA の A)を分離します
{puppies_img, _} =
"/home/livebook/vix/puppies.png"
|> Image.open!()
|> Image.split_alpha()
puppies_img
減色処理
Image.k_means! で K 平均法によって色をクラスタリングします
画像に含まれる色をグループ分けして、いくつかの色だけに絞り込むことになります
Image.k_means!(puppies_img)
実行結果は以下のように RGB の配列(16色)になります
[
[23, 25, 25],
~c"14=",
[57, 82, 22],
~c"LQZ",
[92, 129, 34],
[96, 107, 131],
[106, 127, 87],
[129, 142, 168],
[135, 144, 123],
[138, 180, 59],
[163, 172, 185],
[191, 177, 138],
[193, 197, 208],
[200, 222, 71],
[219, 228, 159],
[229, 229, 220]
]
たまたま数値が文字コードに合致したもの(~c"14=" など)は文字リストとして表示されています
:num_clusters で色数を指定できます
どんな色なのか画像にして見てましょう
Image.k_means!(puppies_img, num_clusters: 8)
|> Enum.map(fn color ->
Image.new!(100, 100, color: color)
end)
|> Kino.Layout.grid(columns: 8)
黒い犬と白い犬の毛色や芝生の色が抽出されていることが分かります
Image.reduce_colors! によって画像を減色できます
Image.reduce_colors!(puppies_img)
デフォルトでは 16 色に減色され、以下のような画像になります
色数を減らしていくとどうなるか見てみましょう
[
puppies_img,
Image.reduce_colors!(puppies_img, colors: 10),
Image.reduce_colors!(puppies_img, colors: 8),
Image.reduce_colors!(puppies_img, colors: 6),
Image.reduce_colors!(puppies_img, colors: 4),
Image.reduce_colors!(puppies_img, colors: 2)
]
|> Kino.Layout.grid(columns: 3)
色数が少ないと、はっきり減色されたことが分かりますね
4 色の画像は初代ゲームボーイを思い出します
まとめ
Image モジュールで減色処理が実行できました
レトロな感じに画像を加工したい場合や、画像の情報量を少なくしたい場合などに使えそうです



