Packages
vix
0.31.0
0.40.0
0.39.0
0.38.0
0.37.0
0.36.0
0.35.0
0.34.0
0.33.1
0.33.0
0.32.0
0.31.1
0.31.0
0.30.0
0.29.0
0.28.0
0.27.0
0.26.0
0.25.0
0.24.0
0.23.1
0.22.0
0.21.0
0.20.0
0.19.0
0.18.0
0.17.0
0.16.4
0.16.3
0.16.2
0.16.1
0.16.0
0.15.1
0.15.0
0.14.0
0.13.0
0.12.0
0.11.0
0.10.1
0.10.0
0.9.0
0.8.0
0.7.0
0.6.1
0.6.0
0.5.0
0.4.0
0.3.0
0.2.1
0.2.0
retired
0.1.0
NIF based bindings for libvips
Current section
Files
Jump to
Current section
Files
lib/vix/operator.ex
defmodule Vix.Operator do
@moduledoc """
Provides implementation of basic math operators for Vix Image.
Useful to improve the readability of complex Image processing
operation pipelines.
```elixir
def foo do
use Vix.Operator
black = Operation.black!(100, 100, bands: 3)
white = Operation.invert!(black)
white == white + black
grey = black + 125 # same as [125]
grey = black + [125] # same as [125, 125, 125], since the image contains 3 bands
grey = black + [125, 125, 125]
end
```
"""
alias Vix.Vips.Image
alias Vix.Vips.Operation
import Kernel, except: [+: 2, -: 2, *: 2, /: 2]
@doc false
defmacro __using__(_opts) do
quote do
import Kernel, except: [+: 2, -: 2, *: 2, /: 2]
import Vix.Operator
end
end
@doc """
Perform addition operation of an image and a number, an array or
an another image.
* if argument is a number or a list of only one number, then the same number is used
for all image bands for the operation. Example `img + 255`, `img + [255]`
* if array size matches image bands, then each array
element is added to respective band. Example `red = Operation.black!(100, 100, bands: 3) + [125, 0, 0]`
* if array size is more than image bands and image only contains one band
then the output will be a multi band image with each array element mapping
to one band
* if argument is an image, then `Vix.Vips.Operation.add!/2` operation will
be performed
When none of the argument is an image then delegates to `Kernel.+/2`
"""
@spec Image.t() + Image.t() :: Image.t()
@spec Image.t() + number() :: Image.t()
@spec number() + Image.t() :: Image.t()
@spec Image.t() + [number()] :: Image.t()
@spec [number()] + Image.t() :: Image.t()
@spec number() + number() :: number()
def a + b do
add(a, b)
end
@doc """
Perform multiplication operation of an image and a number, an array or
an another image.
* if argument is a number or a list of only one number, then the same number is used
for all image bands for the operation. Example `img * 2`, `img + [2]`
* if array size matches image bands, then each array
element is added to respective band
* if array size is more than image bands and image only contains one band
then the output will be a multi band image with each array element mapping
to one band
* if argument is an image, then `Vix.Vips.Operation.multiply!/2` operation will
be performed
When none of the argument is an image then delegates to `Kernel.*/2`
"""
@spec Image.t() * Image.t() :: Image.t()
@spec Image.t() * number() :: Image.t()
@spec number() * Image.t() :: Image.t()
@spec Image.t() * [number()] :: Image.t()
@spec [number()] * Image.t() :: Image.t()
@spec number() * number() :: number()
def a * b do
mul(a, b)
end
@doc """
Perform subtraction operation of an image and a number, an array or
an another image.
* if argument is a number or a list of only one number, then the same number is used
for all image bands for the operation. Example `img - 125`, `img - [125]`
* if array size matches image bands, then each array
element is added to respective band
* if array size is more than image bands and image only contains one band
then the output will be a multi band image with each array element mapping
to one band
* if argument is an image, then `Vix.Vips.Operation.subtract!/2` operation will
be performed
When none of the argument is an image then delegates to `Kernel.-/2`
"""
@spec Image.t() - Image.t() :: Image.t()
@spec Image.t() - number() :: Image.t()
@spec number() - Image.t() :: Image.t()
@spec Image.t() - [number()] :: Image.t()
@spec [number()] - Image.t() :: Image.t()
@spec number() - number() :: number()
def a - b do
sub(a, b)
end
@doc """
Perform division operation of an image and a number, an array or
an another image.
* if argument is a number or a list of only one number, then the same number is used
for all image bands for the operation. Example `img / 2`, `img / [2]`
* if array size matches image bands, then each array
element is added to respective band
* if array size is more than image bands and image only contains one band
then the output will be a multi band image with each array element mapping
to one band
* if argument is an image, then `Vix.Vips.Operation.divide!/2` operation will
be performed
When none of the argument is an image then delegates to `Kernel.//2`
"""
@spec Image.t() / Image.t() :: Image.t()
@spec Image.t() / number() :: Image.t()
@spec number() / Image.t() :: Image.t()
@spec Image.t() / [number()] :: Image.t()
@spec [number()] / Image.t() :: Image.t()
@spec number() / number() :: number()
def a / b do
divide(a, b)
end
defmacrop when_number_list(arg, do: block) do
quote do
if Enum.all?(unquote(arg), &is_number/1) do
unquote(block)
else
raise ArgumentError, "list elements must be a number, got: #{inspect(unquote(arg))}"
end
end
end
# credo:disable-for-next-line Credo.Check.Refactor.CyclomaticComplexity
defp add(a, b) do
cond do
image?(a) && image?(b) ->
Operation.add!(a, b)
image?(a) && is_number(b) ->
add(a, [b])
is_number(a) && image?(b) ->
add(b, [a])
is_list(a) && image?(b) ->
add(b, a)
image?(a) && is_list(b) ->
when_number_list b do
Operation.linear!(a, [1.0], b)
end
true ->
Kernel.+(a, b)
end
end
# credo:disable-for-next-line Credo.Check.Refactor.CyclomaticComplexity
defp mul(a, b) do
cond do
image?(a) && image?(b) ->
Operation.multiply!(a, b)
image?(a) && is_number(b) ->
mul(a, [b])
is_number(a) && image?(b) ->
mul(b, [a])
is_list(a) && image?(b) ->
mul(b, a)
image?(a) && is_list(b) ->
when_number_list b do
Operation.linear!(a, b, [+0.0])
end
true ->
Kernel.*(a, b)
end
end
# credo:disable-for-next-line Credo.Check.Refactor.CyclomaticComplexity
defp sub(a, b) do
cond do
image?(a) && image?(b) ->
Operation.subtract!(a, b)
image?(a) && is_number(b) ->
sub(a, [b])
is_number(a) && image?(b) ->
sub([a], b)
is_list(a) && image?(b) ->
when_number_list a do
# a - b = (b * -1) + a
Operation.linear!(b, [-1.0], a)
end
image?(a) && is_list(b) ->
when_number_list b do
Operation.linear!(a, [1.0], Enum.map(b, &(-&1)))
end
true ->
Kernel.-(a, b)
end
end
# credo:disable-for-next-line Credo.Check.Refactor.CyclomaticComplexity
defp divide(a, b) do
cond do
image?(a) && image?(b) ->
Operation.divide!(a, b)
image?(a) && is_number(b) ->
divide(a, [b])
is_number(a) && image?(b) ->
divide([a], b)
is_list(a) && image?(b) ->
when_number_list a do
# a / b = (b^-1) * a = (1 / b) * a
b
|> Operation.math2_const!(:VIPS_OPERATION_MATH2_POW, [-1.0])
|> Operation.linear!(a, [+0.0])
end
image?(a) && is_list(b) ->
when_number_list b do
Operation.linear!(a, Enum.map(b, &(1 / &1)), [+0.0])
end
true ->
Kernel./(a, b)
end
end
defp image?(%Image{}), do: true
defp image?(_), do: false
end