Skip to content

Commit

Permalink
Complete Image.vibrance/3 and add Image.vibrance!/3
Browse files Browse the repository at this point in the history
  • Loading branch information
kipcole9 committed Jul 18, 2024
1 parent 619716d commit bcaac7b
Show file tree
Hide file tree
Showing 3 changed files with 81 additions and 16 deletions.
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ This is the changelog for Image version 0.54.0 released on ____, 2024. For olde

### Enhancements

* Adds `Image.vibrance/3` following the [libvips discussion](https://github.com/libvips/libvips/discussions/4039).
* Adds `Image.vibrance/3` and `Image.vibrance!/3` following the [libvips discussion](https://github.com/libvips/libvips/discussions/4039).

## Image 0.53.0

Expand Down
68 changes: 61 additions & 7 deletions lib/image.ex
Original file line number Diff line number Diff line change
Expand Up @@ -8647,6 +8647,9 @@ defmodule Image do
* `{:error, reason}`.
"""

# See https://github.com/libvips/libvips/discussions/4039

@doc since: "0.54.0"
@doc subject: "Operation"

Expand All @@ -8657,23 +8660,74 @@ defmodule Image do
use Image.Math

with {:ok, options} <- Options.Vibrance.validate_options(options) do
identity = Operation.identity!()
h2 = 255
h3 = 6 * identity / h2
h4 = 2 / (1 + exp!(-h3)) - 1
h5 = h3 / 6
h1 = Operation.identity!() * 1.0
threshold = options.threshold * 1.0

h2 = 255.0
h3 = 6.0 * h1 / h2
h4 = 2.0 / (1.0 + exp!(-h3)) - 1.0
h5 = h3 / 6.0
h6 = h4 - h5
h7 = vibrance
h8 = h5 + h7 * h6

with_colorspace(image, :lch, fn image ->
g5 = h2 * image[1] / options.threshold
g7 = options.threshold * Operation.maplut!(g5, h8)
g5 = h2 * image[1] / threshold
g7 = threshold * Operation.maplut!(g5, h8)
Image.join_bands([image[0], g7, image[2]])
end)
end
end

@doc """
Apply an curve adjustment to an image's saturation
(chroma) such that less saturated colors are more
affected than more saturated colors. Raises on
error.
This operation is similar to the vibrance function
in Adobe Lightroom. However this implementation does
not account for skin tones.
### Arguments
* `image` is any `t:Vix.Vips.Image.t/0`.
* `saturation` is any float greater than `0.0`. A number less
than `1.0` means reduce saturation. A number greater than `1.0`
means increase saturation.
* `options` is a keyword list of options.
### Options
* `:threshold` is the saturation level above which no
adjustment is made. The range is `1..100` with a default
of `70`.
### Returns
* `adjusted_image` or
* raises an exception.
"""

# See https://github.com/libvips/libvips/discussions/4039

@doc since: "0.54.0"
@doc subject: "Operation"

@spec vibrance!(image :: Vimage.t(), vibrance :: float(), options :: Options.Vibrance.vibrance_options()) ::
Vimage.t() | no_return()

def vibrance!(%Vimage{} = image, vibrance, options \\ []) when is_multiplier(vibrance) do
case vibrance(image, vibrance, options) do
{:ok, image} -> image
{:error, reason} -> raise Image.Error, reason
end
end

@doc """
Reduces noise in an image by applying a median
filter.
Expand Down
27 changes: 19 additions & 8 deletions lib/image/math.ex
Original file line number Diff line number Diff line change
Expand Up @@ -262,7 +262,7 @@ defmodule Image.Math do
end

@spec pow(number(), number()) :: {:ok, number()}
def pow(a, b) do
def pow(a, b) when is_number(a) and is_number(b) do
{:ok, Kernel.**(a, b)}
end

Expand Down Expand Up @@ -359,7 +359,7 @@ defmodule Image.Math do
end

@spec multiply(number(), number()) :: {:ok, number}
def multiply(a, b) do
def multiply(a, b) when is_number(a) and is_number(b) do
{:ok, Kernel.*(a, b)}
end

Expand All @@ -373,10 +373,13 @@ defmodule Image.Math do
divide(image, [value])
end

# @spec divide(number(), Vimage.t()) :: {:ok, Vimage.t()} | {:error, Image.error_message()}
# def divide(value, %Vimage{} = image) when is_number(value) do
#
# 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
Expand Down Expand Up @@ -621,8 +624,16 @@ defmodule Image.Math do
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) do
def divide!(a, b) when is_number(a) and is_number(b) do
Kernel./(a, b)
end

Expand All @@ -643,7 +654,7 @@ defmodule Image.Math do
end

@spec pow!(number(), number()) :: number() | no_return()
def pow!(a, b) do
def pow!(a, b) when is_number(a) and is_number(b) do
Kernel.**(a, b)
end

Expand Down

0 comments on commit bcaac7b

Please sign in to comment.