Packages
image
0.66.0
0.72.0
0.71.0
0.70.0
0.69.0
0.68.0
0.67.0
0.67.0-dev
retired
0.66.0
0.65.0
0.64.0
0.63.0
0.62.1
0.62.0
0.61.1
0.61.0
0.60.0
0.59.3
0.59.2
0.59.1
0.59.0
0.58.0
0.57.0
0.56.1
0.56.0
0.55.2
0.55.1
retired
0.55.0
0.54.4
0.54.3
0.54.2
0.54.1
0.54.0
0.53.0
0.52.3
0.52.2
0.52.1
0.52.0
retired
0.51.0
0.50.0
0.49.0
0.48.1
0.48.0
0.47.0
0.46.0
0.45.0
0.44.0
0.43.2
0.43.1
0.43.0
0.42.0
0.41.0
0.40.0
0.39.3
0.39.2
0.39.1
0.39.0
0.38.4
0.38.3
0.38.2
0.38.1
0.38.0
0.37.0
0.36.2
0.36.1
0.36.0
0.35.0
0.34.0
0.33.0
0.32.0
0.31.1
0.31.0
0.30.0
0.29.0
0.28.2
0.28.1
0.28.0
0.27.0
0.26.0
0.25.1
0.25.0
0.24.1
0.24.0
0.23.2
0.23.1
0.23.0
0.22.1
0.22.0
0.21.0
0.20.0
retired
0.19.0
0.18.1
0.18.0
0.17.0
0.16.0
0.15.0
0.14.4
0.14.2
0.14.1
0.14.0
retired
0.13.1
0.13.0
0.12.0
0.11.0
0.10.0
0.10.0-rc.0
retired
0.9.0
retired
0.8.0
0.7.0
0.6.0
0.5.0
0.4.0
0.3.0
0.2.0
0.1.0
An approachable image processing library primarily based upon Vix and libvips that is NIF-based, fast, multi-threaded, pipelined and has a low memory footprint.
Current section
Files
Jump to
Current section
Files
lib/image/math.ex
defmodule Image.Math do
@moduledoc """
Implements math operators for images,
delegating to the `Kernel` functions in the
cases where the parameters do not include
`t:Vix.Vips.Image.t/0`.
To override the standard operations in a
function or module, add `use Image.Math`.
To maximise readability and clarity it
is recommended that `use Image.Math` be added
to only those functions that require it.
### Example
defmodule MyModule do
# Not recommended
use Image.Math
def my_function(%Vix.Vips.Image{} = image) do
# Recommended
use Image.Math
# Increase the all bands by 20%
brigher = image * 1.2
# Or adjust only green by 20%
bright_green = image * [1, 1.2, 1]
end
end
"""
alias Vix.Vips.Operation
alias Vix.Vips.Image, as: Vimage
import Kernel,
except: [+: 2, -: 1, -: 2, *: 2, /: 2, **: 2, <: 2, >: 2, ==: 2, >=: 2, <=: 2, abs: 1]
@doc """
Guards if a given value might be reasonably interpreted
as a pixel.
"""
defguard is_pixel(value) when is_number(value) or is_list(value)
@doc false
defmacro __using__(_opts) do
quote do
import Kernel,
except: [+: 2, -: 1, -: 2, *: 2, /: 2, **: 2, <: 2, >: 2, ==: 2, >=: 2, <=: 2, abs: 2]
import Image.Math
end
end
@doc """
Matrix unary minues of an
image or a number.
"""
def -a when is_struct(a, Vimage) do
multiply!(a, -1)
end
def -a when is_number(a) do
Kernel.-(a)
end
@doc """
Matrix addition of two images or
one image and a constant or vector.
Delegates to `Kernel.+/2` if none of
the parameters is a `t:Vix.Vips.Image.t/0`.
"""
def a + b do
add!(a, b)
end
@doc """
Matrix subtraction of two images or
one image and a constant or vector.
Delegates to `Kernel.-/2` if none of
the parameters is a `t:Vix.Vips.Image.t/0`.
"""
def a - b do
subtract!(a, b)
end
@doc """
Matrix multiplation of two images or
one image and a constant or vector.
Delegates to `Kernel.*/2` if none of
the parameters is a `t:Vix.Vips.Image.t/0`.
"""
def a * b do
multiply!(a, b)
end
@doc """
Matrix division of two images or
one image and a constant or vector.
Delegates to `Kernel.//2` if none of
the parameters is a `t:Vix.Vips.Image.t/0`.
"""
def a / b do
divide!(a, b)
end
@doc """
Matrix exponent of two images or
one image and a constant or vector.
Delegates to `Kernel.**/2` if none of
the parameters is a `t:Vix.Vips.Image.t/0`.
"""
def a ** b do
pow!(a, b)
end
@doc """
Matrix less than of two images or
one image and a constant or vector.
Delegates to `Kernel.</2` if none of
the parameters is a `t:Vix.Vips.Image.t/0`.
"""
def a < b do
less_than!(a, b)
end
@doc """
Matrix less than or equal of two images or
one image and a constant or vector.
Delegates to `Kernel.<=/2` if none of
the parameters is a `t:Vix.Vips.Image.t/0`.
"""
def a <= b do
less_than_or_equal!(a, b)
end
@doc """
Matrix greater than of two images or
one image and a constant or vector.
Delegates to `Kernel.>/2` if none of
the parameters is a `t:Vix.Vips.Image.t/0`.
"""
def a > b do
greater_than!(a, b)
end
@doc """
Matrix greater than or equal of two images or
one image and a constant or vector.
Delegates to `Kernel.>=/2` if none of
the parameters is a `t:Vix.Vips.Image.t/0`.
"""
def a >= b do
greater_than_or_equal!(a, b)
end
@doc """
Matrix equality of two images or
one image and a constant or vector.
Delegates to `Kernel.==/2` if none of
the parameters is a `t:Vix.Vips.Image.t/0`.
"""
def a == b do
equal!(a, b)
end
@doc """
Matrix inequality of two images or
one image and a constant or vector.
Delegates to `Kernel.!=/2` if none of
the parameters is a `t:Vix.Vips.Image.t/0`.
"""
def a != b do
not_equal!(a, b)
end
@doc """
Matrix bitwise 'and' of
two images.
"""
def a &&& b do
boolean_and!(a, b)
end
@doc """
Matrix bitwise 'or' of
two images.
"""
def a ||| b do
boolean_or!(a, b)
end
# @doc """
# Matrix bitwise 'xor' of
# two images.
#
# """
# def a ^^^ b do
# boolean_xor!(a, b)
# end
@doc """
Matrix bitwise 'left shift' of
two images.
"""
def a <<< b do
boolean_lshift!(a, b)
end
@doc """
Matrix bitwise 'right shift' of
two images.
"""
def a >>> b do
boolean_rshift!(a, b)
end
@spec exp(Vimage.t()) :: {:ok, Vimage.t()} | {:error, Image.error()}
def exp(image) do
Operation.math(image, :VIPS_OPERATION_MATH_EXP)
end
@spec abs(Vimage.t()) :: {:ok, Vimage.t()} | {:error, Image.error()}
def abs(%Vimage{} = image) do
Operation.abs(image)
end
@spec abs(number) :: {:ok, number}
def abs(number) do
{:ok, Kernel.abs(number)}
end
@spec max(Vimage.t()) :: {:ok, float()} | {:error, Image.error()}
def max(%Vimage{} = image) do
case Operation.max(image) do
{:ok, {max, _}} -> {:ok, max}
other -> other
end
end
@spec min(Vimage.t()) :: {:ok, float()} | {:error, Image.error()}
def min(%Vimage{} = image) do
case Operation.min(image) do
{:ok, {min, _}} -> {:ok, min}
other -> other
end
end
@spec pow(Vimage.t(), Vimage.t()) ::
{:ok, Vimage.t()} | {:error, Image.error()}
def pow(%Vimage{} = image, %Vimage{} = image2) do
Operation.math2(image, image2, :VIPS_OPERATION_MATH2_POW)
end
@spec pow(Vimage.t(), number()) ::
{:ok, Vimage.t()} | {:error, Image.error()}
def pow(%Vimage{} = image, value) when is_number(value) do
Operation.math2_const(image, :VIPS_OPERATION_MATH2_POW, [value])
end
@spec pow(number(), number()) :: {:ok, number()}
def pow(a, b) when is_number(a) and is_number(b) do
{:ok, Kernel.**(a, b)}
end
@spec cos(Vimage.t()) :: {:ok, Vimage.t()}
def cos(%Vimage{} = image) do
Operation.math(image, :VIPS_OPERATION_MATH_COS)
end
@spec cos(number()) :: {:ok, number()}
def cos(other) do
{:ok, :math.cos(other)}
end
@spec sin(Vimage.t()) :: {:ok, Vimage.t()}
def sin(%Vimage{} = image) do
Operation.math(image, :VIPS_OPERATION_MATH_SIN)
end
@spec sin(number()) :: {:ok, number()}
def sin(other) do
{:ok, :math.sin(other)}
end
@spec add(Vimage.t(), Vimage.t()) :: {:ok, Vimage.t()} | {:error, Image.error()}
def add(%Vimage{} = image, %Vimage{} = image2) do
Operation.add(image, image2)
end
@spec add(Vimage.t(), number()) :: {:ok, Vimage.t()} | {:error, Image.error()}
def add(%Vimage{} = image, value) when is_number(value) do
add(image, [value])
end
@spec add(Vimage.t(), [number(), ...]) :: {:ok, Vimage.t()} | {:error, Image.error()}
def add(%Vimage{} = image, value) when is_list(value) do
Operation.linear(image, [1.0], value)
end
def add(value, %Vimage{} = image) when is_number(value) do
add(image, value)
end
@spec add(number(), number()) :: {:ok, number}
def add(a, b) do
{:ok, Kernel.+(a, b)}
end
@spec subtract(Vimage.t(), Vimage.t()) :: {:ok, Vimage.t()} | {:error, Image.error()}
def subtract(%Vimage{} = image, %Vimage{} = image2) do
Operation.subtract(image, image2)
end
@spec subtract(Vimage.t(), number()) :: {:ok, Vimage.t()} | {:error, Image.error()}
def subtract(%Vimage{} = image, value) when is_number(value) do
subtract(image, [value])
end
@spec subtract(Vimage.t(), [number()]) :: {:ok, Vimage.t()} | {:error, Image.error()}
def subtract(%Vimage{} = image, value) when is_list(value) do
Operation.linear(image, [1.0], Enum.map(value, &(-&1)))
end
@spec subtract(number(), Vimage.t()) :: {:ok, Vimage.t()} | {:error, Image.error()}
def subtract(value, %Vimage{} = image) when is_number(value) do
image
|> multiply!(-1)
|> add!(value)
|> wrap(:ok)
end
@spec subtract(number(), number()) :: {:ok, number}
def subtract(a, b) do
{:ok, Kernel.-(a, b)}
end
@spec multiply(Vimage.t(), Vimage.t()) :: {:ok, Vimage.t()} | {:error, Image.error()}
def multiply(%Vimage{} = image, %Vimage{} = image2) do
Operation.multiply(image, image2)
end
@spec multiply(Vimage.t(), number()) :: {:ok, Vimage.t()} | {:error, Image.error()}
def multiply(%Vimage{} = image, value) when is_number(value) do
multiply(image, [value])
end
@spec multiply(number(), Vimage.t()) :: {:ok, Vimage.t()} | {:error, Image.error()}
def multiply(value, %Vimage{} = image) when is_number(value) do
multiply(image, [value])
end
@spec multiply(Vimage.t(), list()) :: {:ok, Vimage.t()} | {:error, Image.error()}
def multiply(%Vimage{} = image, value) when is_list(value) do
Operation.linear(image, value, [0.0])
end
@spec multiply(number(), number()) :: {:ok, number}
def multiply(a, b) when is_number(a) and is_number(b) do
{:ok, Kernel.*(a, b)}
end
@spec divide(Vimage.t(), Vimage.t()) :: {:ok, Vimage.t()} | {:error, Image.error()}
def divide(%Vimage{} = image, %Vimage{} = image2) do
Operation.divide(image, image2)
end
@spec divide(Vimage.t(), number()) :: {:ok, Vimage.t()} | {:error, Image.error()}
def divide(%Vimage{} = image, value) when is_number(value) do
divide(image, [value])
end
# See https://github.com/libvips/libvips/blob/master/cplusplus/VImage.cpp#L1062-L1066
@spec divide(number(), Vimage.t()) :: {:ok, Vimage.t()} | {:error, Image.error()}
def divide(value, %Vimage{} = image) when is_number(value) do
image
|> pow!(-1.0)
|> multiply(value)
end
@spec divide(Vimage.t(), [number()]) :: {:ok, Vimage.t()} | {:error, Image.error()}
def divide(%Vimage{} = image, value) when is_list(value) do
Operation.linear(image, Enum.map(value, &(1.0 / &1)), [0.0])
end
@spec divide(number(), number()) :: {:ok, number}
def divide(a, b) do
{:ok, Kernel.-(a, b)}
end
@spec less_than(Vimage.t(), Vimage.t()) ::
{:ok, Vimage.t()} | {:error, Image.error()}
def less_than(%Vimage{} = image, %Vimage{} = other) do
Vix.Vips.Operation.relational(image, other, :VIPS_OPERATION_RELATIONAL_LESS)
end
@spec less_than(Vimage.t(), Image.pixel()) ::
{:ok, Vimage.t()} | {:error, Image.error()}
def less_than(%Vimage{} = image, other) when is_pixel(other) do
Vix.Vips.Operation.relational_const(image, :VIPS_OPERATION_RELATIONAL_LESS, List.wrap(other))
end
@spec less_than_or_equal(Vimage.t(), Vimage.t()) ::
{:ok, Vimage.t()} | {:error, Image.error()}
def less_than_or_equal(%Vimage{} = image, %Vimage{} = other) do
Vix.Vips.Operation.relational(image, other, :VIPS_OPERATION_RELATIONAL_LESSEQ)
end
@spec less_than_or_equal(Vimage.t(), Image.pixel()) ::
{:ok, Vimage.t()} | {:error, Image.error()}
def less_than_or_equal(%Vimage{} = image, other) when is_pixel(other) do
Vix.Vips.Operation.relational_const(image, :VIPS_OPERATION_RELATIONAL_LESSEQ, List.wrap(other))
end
@spec greater_than(Vimage.t(), Vimage.t()) ::
{:ok, Vimage.t()} | {:error, Image.error()}
def greater_than(%Vimage{} = image, %Vimage{} = other) do
Vix.Vips.Operation.relational(image, other, :VIPS_OPERATION_RELATIONAL_MORE)
end
@spec greater_than(Vimage.t(), Image.pixel()) ::
{:ok, Vimage.t()} | {:error, Image.error()}
def greater_than(%Vimage{} = image, other) when is_pixel(other) do
Vix.Vips.Operation.relational_const(image, :VIPS_OPERATION_RELATIONAL_MORE, List.wrap(other))
end
@spec greater_than_or_equal(Vimage.t(), Vimage.t()) ::
{:ok, Vimage.t()} | {:error, Image.error()}
def greater_than_or_equal(%Vimage{} = image, %Vimage{} = other) do
Vix.Vips.Operation.relational(image, other, :VIPS_OPERATION_RELATIONAL_MOREEQ)
end
@spec greater_than_or_equal(Vimage.t(), Image.pixel()) ::
{:ok, Vimage.t()} | {:error, Image.error()}
def greater_than_or_equal(%Vimage{} = image, other) when is_pixel(other) do
Vix.Vips.Operation.relational_const(image, :VIPS_OPERATION_RELATIONAL_MOREEQ, List.wrap(other))
end
@spec equal(Vimage.t(), Vimage.t()) ::
{:ok, Vimage.t()} | {:error, Image.error()}
def equal(%Vimage{} = image, %Vimage{} = other) do
Vix.Vips.Operation.relational(image, other, :VIPS_OPERATION_RELATIONAL_EQUAL)
end
@spec equal(Vimage.t(), Image.pixel()) ::
{:ok, Vimage.t()} | {:error, Image.error()}
def equal(%Vimage{} = image, other) when is_pixel(other) do
Vix.Vips.Operation.relational_const(image, :VIPS_OPERATION_RELATIONAL_EQUAL, List.wrap(other))
end
@spec not_equal(Vimage.t(), Vimage.t()) ::
{:ok, Vimage.t()} | {:error, Image.error()}
def not_equal(%Vimage{} = image, %Vimage{} = other) do
Vix.Vips.Operation.relational(image, other, :VIPS_OPERATION_RELATIONAL_NOTEQ)
end
@spec not_equal(Vimage.t(), Image.pixel()) ::
{:ok, Vimage.t()} | {:error, Image.error()}
def not_equal(%Vimage{} = image, other) when is_pixel(other) do
Vix.Vips.Operation.relational_const(image, :VIPS_OPERATION_RELATIONAL_NOTEQ, List.wrap(other))
end
@spec less_than!(Vimage.t(), Vimage.t() | Image.pixel()) :: Vimage.t() | no_return()
def less_than!(%Vimage{} = image, value) do
case less_than(image, value) do
{:ok, image} -> image
{:error, reason} -> raise Image.Error, reason
end
end
@spec less_than!(number(), number()) :: number() | no_return()
def less_than!(a, b) do
Kernel.<(a, b)
end
@spec less_than_or_equal!(Vimage.t(), Vimage.t() | Image.pixel()) :: Vimage.t() | no_return()
def less_than_or_equal!(%Vimage{} = image, value) do
case less_than_or_equal(image, value) do
{:ok, image} -> image
{:error, reason} -> raise Image.Error, reason
end
end
@spec less_than_or_equal!(number(), number()) :: number() | no_return()
def less_than_or_equal!(a, b) do
Kernel.<=(a, b)
end
@spec greater_than!(Vimage.t(), Vimage.t() | Image.pixel()) :: Vimage.t() | no_return()
def greater_than!(%Vimage{} = image, value) do
case greater_than(image, value) do
{:ok, image} -> image
{:error, reason} -> raise Image.Error, reason
end
end
@spec greater_than!(number(), number()) :: number() | no_return()
def greater_than!(a, b) do
Kernel.>(a, b)
end
@spec greater_than_or_equal!(Vimage.t(), Image.pixel()) :: Vimage.t() | no_return()
def greater_than_or_equal!(%Vimage{} = image, value) do
case greater_than_or_equal(image, value) do
{:ok, image} -> image
{:error, reason} -> raise Image.Error, reason
end
end
@spec greater_than_or_equal!(number(), number()) :: number() | no_return()
def greater_than_or_equal!(a, b) do
Kernel.>=(a, b)
end
@spec equal!(Vimage.t(), Image.pixel()) :: Vimage.t() | no_return()
def equal!(%Vimage{} = image, value) do
case equal(image, value) do
{:ok, image} -> image
{:error, reason} -> raise Image.Error, reason
end
end
@spec equal!(number(), number()) :: number() | no_return()
def equal!(a, b) do
Kernel.==(a, b)
end
@spec not_equal!(Vimage.t(), Image.pixel()) :: Vimage.t() | no_return()
def not_equal!(%Vimage{} = image, value) do
case not_equal(image, value) do
{:ok, image} -> image
{:error, reason} -> raise Image.Error, reason
end
end
@spec not_equal!(number(), number()) :: number() | no_return()
def not_equal!(a, b) do
Kernel.!=(a, b)
end
@spec add!(Vimage.t(), Image.pixel() | number()) :: Vimage.t() | no_return()
def add!(%Vimage{} = image, value) do
case add(image, value) do
{:ok, image} -> image
{:error, reason} -> raise Image.Error, reason
end
end
@spec add!(Image.pixel() | number(), Vimage.t()) :: Vimage.t() | no_return()
def add!(value, %Vimage{} = image) do
case add(image, value) do
{:ok, image} -> image
{:error, reason} -> raise Image.Error, reason
end
end
@spec add!(number(), number()) :: number() | no_return()
def add!(a, b) do
Kernel.+(a, b)
end
@spec subtract!(Vimage.t(), Image.pixel()) :: Vimage.t() | no_return()
def subtract!(%Vimage{} = image, value) do
case subtract(image, value) do
{:ok, image} -> image
{:error, reason} -> raise Image.Error, reason
end
end
@spec subtract!(Image.pixel(), Vimage.t()) :: Vimage.t() | no_return()
def subtract!(value, %Vimage{} = image) when is_number(value) do
case subtract(value, image) do
{:ok, image} -> image
{:error, reason} -> raise Image.Error, reason
end
end
@spec subtract!(number(), number()) :: number() | no_return()
def subtract!(a, b) do
Kernel.-(a, b)
end
@spec multiply!(Vimage.t(), Image.pixel() | number()) :: Vimage.t() | no_return()
def multiply!(%Vimage{} = image, value) do
case multiply(image, value) do
{:ok, image} -> image
{:error, reason} -> raise Image.Error, reason
end
end
@spec multiply!(Image.pixel() | number(), Vimage.t()) :: Vimage.t() | no_return()
def multiply!(value, %Vimage{} = image) do
case multiply(image, value) do
{:ok, image} -> image
{:error, reason} -> raise Image.Error, reason
end
end
@spec multiply!(number(), number()) :: number() | no_return()
def multiply!(a, b) do
Kernel.*(a, b)
end
@spec divide!(Vimage.t(), Image.pixel()) :: Vimage.t() | no_return()
def divide!(%Vimage{} = image, value) do
case divide(image, value) do
{:ok, image} -> image
{:error, reason} -> raise Image.Error, reason
end
end
@spec divide!(Image.pixel(), Vimage.t()) :: Vimage.t() | no_return()
def divide!(value, %Vimage{} = image) do
case divide(value, image) do
{:ok, image} -> image
{:error, reason} -> raise Image.Error, reason
end
end
@spec divide!(number(), number()) :: number() | no_return()
def divide!(a, b) when is_number(a) and is_number(b) do
Kernel./(a, b)
end
@spec exp!(Vimage.t()) :: Vimage.t() | no_return()
def exp!(image) do
case exp(image) do
{:ok, image} -> image
{:error, reason} -> raise Image.Error, reason
end
end
@spec abs!(Vimage.t()) :: Vimage.t() | no_return()
def abs!(image) do
case abs(image) do
{:ok, image} -> image
{:error, reason} -> raise Image.Error, reason
end
end
@spec max!(Vimage.t()) :: float() | no_return()
def max!(image) do
case max(image) do
{:ok, float} -> float
{:error, reason} -> raise Image.Error, reason
end
end
@spec min!(Vimage.t()) :: float() | no_return()
def min!(image) do
case min(image) do
{:ok, float} -> float
{:error, reason} -> raise Image.Error, reason
end
end
@spec pow!(Vimage.t(), number()) :: Vimage.t() | no_return()
def pow!(%Vimage{} = image, value) do
case pow(image, value) do
{:ok, image} -> image
{:error, reason} -> raise Image.Error, reason
end
end
@spec pow!(number(), number()) :: number() | no_return()
def pow!(a, b) when is_number(a) and is_number(b) do
Kernel.**(a, b)
end
@dialyzer {:nowarn_function, {:cos!, 1}}
@spec cos!(Vimage.t()) :: Vimage.t() | no_return()
def cos!(%Vimage{} = image) do
case cos(image) do
{:ok, image} -> image
{:error, reason} -> raise Image.Error, reason
end
end
@dialyzer {:nowarn_function, {:sin!, 1}}
@spec sin!(Vimage.t()) :: Vimage.t() | no_return()
def sin!(%Vimage{} = image) do
case sin(image) do
{:ok, image} -> image
{:error, reason} -> raise Image.Error, reason
end
end
### Logical operations
@spec boolean_and(Vimage.t(), Vimage.t()) ::
{:ok, Vimage.t()} | {:error, Image.error()}
def boolean_and(%Vimage{} = image_1, %Vimage{} = image_2) do
Operation.boolean(image_1, image_2, :VIPS_OPERATION_BOOLEAN_AND)
end
@spec boolean_and!(Vimage.t(), Vimage.t()) :: Vimage.t() | no_return()
def boolean_and!(%Vimage{} = image_1, %Vimage{} = image_2) do
case boolean_and(image_1, image_2) do
{:ok, image} -> image
{:error, reason} -> raise Image.Error, reason
end
end
@spec boolean_or(Vimage.t(), Vimage.t()) ::
{:ok, Vimage.t()} | {:error, Image.error()}
def boolean_or(%Vimage{} = image_1, %Vimage{} = image_2) do
Operation.boolean(image_1, image_2, :VIPS_OPERATION_BOOLEAN_OR)
end
@spec boolean_or!(Vimage.t(), Vimage.t()) :: Vimage.t() | no_return()
def boolean_or!(%Vimage{} = image_1, %Vimage{} = image_2) do
case boolean_or(image_1, image_2) do
{:ok, image} -> image
{:error, reason} -> raise Image.Error, reason
end
end
@spec boolean_xor(Vimage.t(), Vimage.t()) ::
{:ok, Vimage.t()} | {:error, Image.error()}
def boolean_xor(%Vimage{} = image_1, %Vimage{} = image_2) do
Operation.boolean(image_1, image_2, :VIPS_OPERATION_BOOLEAN_EOR)
end
@spec boolean_xor!(Vimage.t(), Vimage.t()) :: Vimage.t() | no_return()
def boolean_xor!(%Vimage{} = image_1, %Vimage{} = image_2) do
case boolean_xor(image_1, image_2) do
{:ok, image} -> image
{:error, reason} -> raise Image.Error, reason
end
end
@spec boolean_lshift(Vimage.t(), Vimage.t()) ::
{:ok, Vimage.t()} | {:error, Image.error()}
def boolean_lshift(%Vimage{} = image_1, %Vimage{} = image_2) do
Operation.boolean(image_1, image_2, :VIPS_OPERATION_BOOLEAN_LSHIFT)
end
@spec boolean_lshift!(Vimage.t(), Vimage.t()) :: Vimage.t() | no_return()
def boolean_lshift!(%Vimage{} = image_1, %Vimage{} = image_2) do
case boolean_lshift(image_1, image_2) do
{:ok, image} -> image
{:error, reason} -> raise Image.Error, reason
end
end
@spec boolean_rshift(Vimage.t(), Vimage.t()) ::
{:ok, Vimage.t()} | {:error, Image.error()}
def boolean_rshift(%Vimage{} = image_1, %Vimage{} = image_2) do
Operation.boolean(image_1, image_2, :VIPS_OPERATION_BOOLEAN_RSHIFT)
end
@spec boolean_rshift!(Vimage.t(), Vimage.t()) :: Vimage.t() | no_return()
def boolean_rshift!(%Vimage{} = image_1, %Vimage{} = image_2) do
case boolean_rshift(image_1, image_2) do
{:ok, image} -> image
{:error, reason} -> raise Image.Error, reason
end
end
@doc """
Return the top `n` image maxima.
The function returns the coordinates of`:n`
largest values of the image.
### Arguments
* `image` is any `t:Vix.Vips.Image.t/0`.
* `n` is the number of maxima to find. The
default is `10`. Maxima in this case means the
largest `n` values; They may not be equal to the
maximum.
### Returns
* `{maximum, x_max, y_max, [{x, y}, ...])`
"""
@spec top_n(image :: Vimage.t(), n :: non_neg_integer()) ::
{
maximum :: float(),
x_max :: non_neg_integer(),
y_max :: non_neg_integer(),
max_coordinates :: [Image.point(), ...]
}
def top_n(%Vimage{} = image, n \\ 10) when is_integer(n) do
{:ok, {v, opts}} = Operation.max(image, size: n)
{v, opts[:x], opts[:y], Enum.zip(opts[:"x-array"], opts[:"y-array"])}
end
@doc """
Return the bottom `n` image minima.
The function returns the coordinates of`:n`
smallest values of the image.
### Arguments
* `image` is any `t:Vix.Vips.Image.t/0`.
* `n` is the number of minima to find. The
default is `10`. Minima in this case means the
smallest `n` values; They may not be equal to the
minimum.
### Returns
* `{minimum, x_min, y_min, [{x, y}, ...])`
"""
@spec bottom_n(image :: Vimage.t(), n :: non_neg_integer()) ::
{
minimim :: float(),
x_max :: non_neg_integer(),
y_max :: non_neg_integer(),
max_coordinates :: [Image.point(), ...]
}
def bottom_n(%Vimage{} = image, n \\ 10) when is_integer(n) do
{:ok, {v, opts}} = Operation.min(image, size: n)
{v, opts[:x], opts[:y], Enum.zip(opts[:"x-array"], opts[:"y-array"])}
end
@doc """
Return the image maxima.
This function retrieves the coordinates of the `n`
largest values then then filters them to return only
those coordinates that have the maximum value.
### Arguments
* `image` is any `t:Vix.Vips.Image.t/0`.
* `n` is the number of maxima to find. The
default is `10`. Maxima in this case means
those values that exactly match the maximum
value.
### Returns
* `{maximum, [{x, y}, ...], maybe_overflow)`. If
`maybe_overflow` is set to `:maybe_overflow` its an indication
that the number of coordinates is the same as the requested `n`.
Therefore it is possible - maybe even likely - that there are other
coordinates that have the maximum value but have not been returned.
### Example
This example draws a red image with a single green pixel. We then
look for all the coordinates that have a green pixel.
iex> {:ok, image} =
iex> Image.new!(5, 5, color: :red)
iex> |> Image.mutate(fn i -> Image.Draw.point!(i, 2, 2, color: [0,255,0]) end)
iex> image
iex> |> Image.Math.==([0, 255, 0])
iex> |> Image.band_and!()
iex> |> Image.Math.maxpos()
{255, [{2, 2}], nil}
# Since all pixels are red and we want
# the coordinates of all the red pixels
# we have an overlow: We retrieve only 3
# maxima and they are all red. Perhaps
# the red of the image pixels are also red?
# Yes - they are!
iex> Image.new!(2, 2, color: :red)
iex> |> Image.Math.==([255, 0, 0])
iex> |> Image.band_and!()
iex> |> Image.Math.maxpos(3)
{255, [{0, 1}, {1, 0}, {0, 0}], :maybe_overflow}
"""
@spec maxpos(image :: Vimage.t(), n :: non_neg_integer()) ::
{
maximum :: number(),
max_coordinates :: [Image.point(), ...],
maybe_overflow :: :maybe_overflow | nil
}
@dialyzer {:nowarn_function, maxpos: 2}
def maxpos(%Vimage{} = image, n \\ 10) when is_integer(n) do
band_format = Image.band_format(image)
{:ok, {max, opts}} = Operation.max(image, size: n)
coordinates =
Enum.zip_reduce([opts[:"out-array"], opts[:"x-array"], opts[:"y-array"]], [], fn
[^max, x, y], acc -> [{x, y} | acc]
_other, acc -> acc
end)
max = if match?({:u, _}, band_format), do: trunc(max), else: max
if length(coordinates) == n do
{max, coordinates, :maybe_overflow}
else
{max, coordinates, nil}
end
end
@doc """
Return the image minima.
This function retrieves the coordinates of the `n`
smallest values then then filters them to return only
those coordinates that have the minimum value.
### Arguments
* `image` is any `t:Vix.Vips.Image.t/0`.
* `n` is the number of minima to find. The
default is `10`. Minima in this case means
those values that exactly match the minimum
value.
### Returns
* `{minimum, [{x, y}, ...], maybe_overflow)`. If
`maybe_overflow` is set to `:maybe_overflow` its an indication
that the number of coordinates is the same as the requested `n`.
Therefore it is possible - maybe even likely - that there are other
coordinates that have the minimum value but have not been returned.
"""
@dialyzer {:nowarn_function, minpos: 2}
@spec minpos(image :: Vimage.t(), n :: non_neg_integer()) ::
{
maximum :: number(),
max_coordinates :: [Image.point(), ...],
maybe_overflow :: :maybe_overflow | nil
}
def minpos(%Vimage{} = image, n \\ 10) when is_integer(n) do
band_format = Image.band_format(image)
{:ok, {min, opts}} = Operation.min(image, size: n)
coordinates =
Enum.zip_reduce([opts[:"out-array"], opts[:"x-array"], opts[:"y-array"]], [], fn
[^min, x, y], acc -> [{x, y} | acc]
_other, acc -> acc
end)
min = if match?({:u, _}, band_format), do: trunc(min), else: min
if length(coordinates) == n do
{min, coordinates, :maybe_overflow}
else
{min, coordinates, nil}
end
end
@doc false
def list_height([first | _rest] = list) when is_list(first), do: length(list)
def list_height(_other), do: 0
@doc false
def list_width([first | _rest]) when is_list(first), do: length(first)
def list_width(list) when is_list(list), do: length(list)
defp wrap(elem, atom) do
{atom, elem}
end
end