Current section

Files

Jump to
image lib image math.ex
Raw

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_message()}
def exp(image) do
Operation.math(image, :VIPS_OPERATION_MATH_EXP)
end
@spec abs(Vimage.t()) :: {:ok, Vimage.t()} | {:error, Image.error_message()}
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_message()}
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_message()}
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_message()}
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_message()}
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_message()}
def add(%Vimage{} = image, %Vimage{} = image2) do
Operation.add(image, image2)
end
@spec add(Vimage.t(), number()) :: {:ok, Vimage.t()} | {:error, Image.error_message()}
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_message()}
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_message()}
def subtract(%Vimage{} = image, %Vimage{} = image2) do
Operation.subtract(image, image2)
end
@spec subtract(Vimage.t(), number()) :: {:ok, Vimage.t()} | {:error, Image.error_message()}
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_message()}
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_message()}
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_message()}
def multiply(%Vimage{} = image, %Vimage{} = image2) do
Operation.multiply(image, image2)
end
@spec multiply(Vimage.t(), number()) :: {:ok, Vimage.t()} | {:error, Image.error_message()}
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_message()}
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_message()}
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_message()}
def divide(%Vimage{} = image, %Vimage{} = image2) do
Operation.divide(image, image2)
end
@spec divide(Vimage.t(), number()) :: {:ok, Vimage.t()} | {:error, Image.error_message()}
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_message()}
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_message()}
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_message()}
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_message()}
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_message()}
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_message()}
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_message()}
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_message()}
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_message()}
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_message()}
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_message()}
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_message()}
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_message()}
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_message()}
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 ArgumentError, 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 ArgumentError, 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 ArgumentError, 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 ArgumentError, 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 ArgumentError, 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 ArgumentError, 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 ArgumentError, 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 ArgumentError, 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 ArgumentError, 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 ArgumentError, 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 ArgumentError, 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 ArgumentError, 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 ArgumentError, 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 ArgumentError, 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 ArgumentError, 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 ArgumentError, reason
end
end
@spec max!(Vimage.t()) :: float() | no_return()
def max!(image) do
case max(image) do
{:ok, float} -> float
{:error, reason} -> raise ArgumentError, reason
end
end
@spec min!(Vimage.t()) :: float() | no_return()
def min!(image) do
case min(image) do
{:ok, float} -> float
{:error, reason} -> raise ArgumentError, 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 ArgumentError, 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 ArgumentError, 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 ArgumentError, reason
end
end
### Logical operations
@spec boolean_and(Vimage.t(), Vimage.t()) ::
{:ok, Vimage.t()} | {:error, Image.error_message()}
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 ArgumentError, reason
end
end
@spec boolean_or(Vimage.t(), Vimage.t()) ::
{:ok, Vimage.t()} | {:error, Image.error_message()}
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 ArgumentError, reason
end
end
@spec boolean_xor(Vimage.t(), Vimage.t()) ::
{:ok, Vimage.t()} | {:error, Image.error_message()}
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 ArgumentError, reason
end
end
@spec boolean_lshift(Vimage.t(), Vimage.t()) ::
{:ok, Vimage.t()} | {:error, Image.error_message()}
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 ArgumentError, reason
end
end
@spec boolean_rshift(Vimage.t(), Vimage.t()) ::
{:ok, Vimage.t()} | {:error, Image.error_message()}
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 ArgumentError, 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_positions, y_positions}])`
"""
@spec top_n(image :: Vimage.t(), n :: non_neg_integer()) ::
{
maximum :: float(),
x_max :: non_neg_integer(),
y_max :: non_neg_integer(),
max_coordinates :: [{x_positions :: integer(), y_positions :: integer()}, ...]
}
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_positions, y_positions}])`
"""
@spec bottom_n(image :: Vimage.t(), n :: non_neg_integer()) ::
{
minimim :: float(),
x_max :: non_neg_integer(),
y_max :: non_neg_integer(),
max_coordinates :: [{x_positions :: integer(), y_positions :: integer()}, ...]
}
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_positions, y_positions}], 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, [{2, 2}], :maybe_overflow}
"""
@spec maxpos(image :: Vimage.t(), n :: non_neg_integer()) ::
{
maximum :: number(),
max_coordinates :: [{x_positions :: integer(), y_positions :: integer()}, ...],
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_positions, y_positions}], 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 :: [{x_positions :: integer(), y_positions :: integer()}, ...],
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