Powered by AppSignal & Oban Pro
Would you like to see your link here? Contact us

画像分割

image_division.livemd

画像分割

Mix.install([
  {:nx, "~> 0.9"},
  {:evision, "~> 0.2"},
  {:req, "~> 0.5"},
  {:kino, "~> 0.14"}
])

画像の取得

# 指定したパスにダウンロードする
img_path = "rwakabay.jpg"

Req.get!(
  "https://www.elixirconf.eu/assets/images/ryo-wakabayashi.jpg",
  output: File.stream!(img_path)
)

img_mat = Evision.imread(img_path)
# ファイル名、拡張子、画像サイズの取得
file_ext = Path.extname(img_path)
file_basename = Path.basename(img_path, file_ext)
img_shape = img_mat.shape

{file_basename, file_ext, img_shape}
# 画像リスト保存用関数
save_img_tensor_list = fn img_tensor_list, kind ->
  img_tensor_list
  |> Enum.with_index()
  |> Enum.map(fn {img_tensor, index} ->
    dst_filename = "#{file_basename}_#{kind}_#{index}#{file_ext}"

    img_mat = Evision.Mat.from_nx_2d(img_tensor)

    Evision.imwrite(dst_filename, img_mat)

    dst_filename
  end)
end

水平に分割

# 分割サイズ(分割した画像一つの幅、高さのピクセル数)
chunk_size = 60
# 分割した画像を並べて表示
img_mat
|> Evision.Mat.to_nx(Nx.BinaryBackend)
|> Nx.to_batched(chunk_size)
# BGR to RGB
|> Enum.map(&Nx.reverse(&1, axes: [2]))
|> Enum.map(&Kino.Image.new(&1))
|> Kino.Layout.grid(columns: 1)
|> dbg()
# 分割した画像をファイルに保存
img_mat
|> Evision.Mat.to_nx(Nx.BinaryBackend)
|> Nx.to_batched(chunk_size)
|> save_img_tensor_list.("h")
# 分割したファイルを結合
Stream.unfold(0, fn counter -> {counter, counter + 1} end)
|> Stream.map(&{&1, "#{file_basename}_h_#{&1}#{file_ext}"})
|> Stream.take_while(fn {_, f} -> File.exists?(f) end)
|> Enum.map(fn {index, filename} ->
  new_tensor =
    filename
    |> Evision.imread()
    |> Evision.Mat.to_nx(Nx.BinaryBackend)

  # 偶数の場合は色を反転
  case rem(index, 2) do
    0 ->
      Nx.reverse(new_tensor, axes: [2])

    _ ->
      new_tensor
  end
end)
# 結合
|> Nx.concatenate()
# トリミング
|> Nx.slice([0, 0, 0], Tuple.to_list(img_shape))
|> Kino.Image.new()

垂直に分割

# 分割した画像を並べて表示
img_mat
|> Evision.Mat.to_nx(Nx.BinaryBackend)
# 縦横入れ替え
|> Nx.transpose(axes: [1, 0, 2])
# 分割
|> Nx.to_batched(chunk_size)
# 縦横入れ替え
|> Enum.map(&Nx.transpose(&1, axes: [1, 0, 2]))
# BGR to RGB
|> Enum.map(&Nx.reverse(&1, axes: [2]))
|> Enum.map(&Kino.Image.new(&1))
|> Kino.Layout.grid(columns: 10)
|> dbg()
# 分割した画像をファイルに保存
img_mat
|> Evision.Mat.to_nx(Nx.BinaryBackend)
# 縦横入れ替え
|> Nx.transpose(axes: [1, 0, 2])
# 分割
|> Nx.to_batched(chunk_size)
|> Enum.map(&Nx.transpose(&1, axes: [1, 0, 2]))
|> save_img_tensor_list.("v")
# 分割したファイルを結合
Stream.unfold(0, fn counter -> {counter, counter + 1} end)
|> Stream.map(&{&1, "#{file_basename}_v_#{&1}#{file_ext}"})
|> Stream.take_while(fn {_, f} -> File.exists?(f) end)
|> Enum.map(fn {index, filename} ->
  new_tensor =
    filename
    |> Evision.imread()
    |> Evision.Mat.to_nx(Nx.BinaryBackend)

  # 偶数の場合は色を反転
  case rem(index, 2) do
    0 ->
      Nx.reverse(new_tensor, axes: [2])

    _ ->
      new_tensor
  end
end)
# 結合
|> Nx.concatenate(axis: 1)
# トリミング
|> Nx.slice([0, 0, 0], Tuple.to_list(img_shape))
|> Kino.Image.new()

タイル状に分割

# 分割した画像を並べて表示
img_mat
|> Evision.Mat.to_nx(Nx.BinaryBackend)
# 水平に分割
|> Nx.to_batched(chunk_size)
# 垂直に分割
|> Enum.map(&Nx.transpose(&1, axes: [1, 0, 2]))
|> Enum.flat_map(&Nx.to_batched(&1, chunk_size))
|> Enum.map(&Nx.transpose(&1, axes: [1, 0, 2]))
# BGR to RGB
|> Enum.map(&Nx.reverse(&1, axes: [2]))
|> Enum.map(&Kino.Image.new(&1))
|> Kino.Layout.grid(columns: 10)
|> dbg()
# 分割した画像をファイルに保存
img_mat
|> Evision.Mat.to_nx(Nx.BinaryBackend)
# 水平に分割
|> Nx.to_batched(chunk_size)
# 垂直に分割
|> Enum.map(&Nx.transpose(&1, axes: [1, 0, 2]))
|> Enum.flat_map(&Nx.to_batched(&1, chunk_size))
|> Enum.map(&Nx.transpose(&1, axes: [1, 0, 2]))
|> save_img_tensor_list.("t")
{width, _, _} = img_shape
h_size = div(width, chunk_size)

# 分割したファイルを結合
Stream.unfold(0, fn counter -> {counter, counter + 1} end)
|> Stream.map(&{&1, "#{file_basename}_t_#{&1}#{file_ext}"})
|> Stream.take_while(fn {_, f} -> File.exists?(f) end)
|> Enum.map(fn {t_index, filename} ->
  new_tensor =
    filename
    |> Evision.imread()
    |> Evision.Mat.to_nx(Nx.BinaryBackend)

  {new_tensor, t_index}
end)
|> Enum.chunk_every(h_size)
|> Enum.map(fn new_tensor_list ->
  new_tensor_list
  |> Enum.with_index()
  |> Enum.map(fn {{new_tensor, t_index}, v_index} ->
    cond do
      rem(v_index, 2) == rem(div(t_index, h_size), 2) ->
        Nx.reverse(new_tensor, axes: [2])

      true ->
        new_tensor
    end
  end)
  # 横方向に結合
  |> Nx.concatenate(axis: 1)
end)
# 縦方向に結合
|> Nx.concatenate()
# トリミング
|> Nx.slice([0, 0, 0], Tuple.to_list(img_shape))
|> Kino.Image.new()