Current section

Files

Jump to
image lib image matrix.ex
Raw

lib/image/matrix.ex

defmodule Image.Matrix do
@moduledoc false
# Adapted from [Nx.tensor/2](https://hexdocs.pm/nx/Nx.html#tensor/2)
def image_from_matrix(list) when is_list(list) do
type = infer_type(list)
[width, height] = dimensions(list)
with {:ok, format} <- Image.BandFormat.image_format_from_nx(type),
{:ok, image} = Vix.Vips.Image.new_matrix_from_array(width, height, list) do
Vix.Vips.Operation.cast(image, format)
end
end
### Helpers
defp dimensions(list) do
dimensions(list, [])
end
defp dimensions([], dimensions) do
[0 | dimensions]
end
defp dimensions([head | rest], parent_dimensions) when is_list(head) do
child_dimensions = dimensions(head, [])
n =
Enum.reduce(rest, 1, fn list, count ->
case dimensions(list, []) do
^child_dimensions ->
count + 1
other_dimensions ->
raise ArgumentError,
"cannot build image because lists have different shapes, got " <>
inspect(List.to_tuple(child_dimensions)) <>
" at position 0 and " <>
inspect(List.to_tuple(other_dimensions)) <> " at position #{count + 1}"
end
end)
child_dimensions ++ [n | parent_dimensions]
end
defp dimensions(list, dimensions) do
[length(list) | dimensions]
end
defp infer_type([head | tail]) when is_list(tail) do
Enum.reduce(tail, infer_type(head), &merge(infer_type(&1), &2))
end
defp infer_type(number) when is_number(number) do
infer(number)
end
defp infer_type(value) do
raise ArgumentError,
"invalid value given to Image.Matrix.binary_from_list/1, got: #{inspect(value)}"
end
defp infer(value) when is_integer(value), do: {:s, 64}
defp infer(value) when is_float(value), do: {:f, 32}
defp merge({type, left_size}, {type, right_size}) do
{type, max(left_size, right_size)}
end
defp merge(left, right) do
case sort(left, right) do
{{:u, size1}, {:s, size2}} -> {:s, max(min(size1 * 2, 64), size2)}
{_, type2} -> type2
end
end
defp sort({left_type, _} = left, {right_type, _} = right) do
if type_to_int(left_type) < type_to_int(right_type) do
{left, right}
else
{right, left}
end
end
defp type_to_int(:c), do: 4
defp type_to_int(:f), do: 3
defp type_to_int(:bf), do: 2
defp type_to_int(:s), do: 1
defp type_to_int(:u), do: 0
end