philomena/lib/philomena_media/intensities.ex

69 lines
2.1 KiB
Elixir
Raw Normal View History

defmodule PhilomenaMedia.Intensities do
@moduledoc """
Corner intensities are a simple mechanism for automatic image deduplication,
designed for a time when computer vision was an expensive technology and
resources were scarce.
Each image is divided into quadrants; image with odd numbers of pixels
on either dimension overlap quadrants by one pixel. The luma (brightness)
value corresponding each the pixel is computed according to BTU.709 primaries,
and its value is added to a sum for each quadrant. Finally, the value is divided
by the number of pixels in the quadrant to produce an average. The minimum luma
value of any pixel is 0, and the maximum is 255, so an average will be between
these values. Transparent pixels are composited on black before processing.
By using a range search in the database, this produces a reverse image search which
suffers no dimensionality issues, is exceptionally fast to evaluate, and is independent
of image dimensions, with poor precision and a poor-to-fair accuracy.
"""
@type t :: %__MODULE__{
nw: float(),
ne: float(),
sw: float(),
se: float()
}
defstruct nw: 0.0,
ne: 0.0,
sw: 0.0,
se: 0.0
@doc """
Gets the corner intensities of the given image file.
The image file must be in the PNG or JPEG format.
> #### Info {: .info}
>
> Clients should prefer to use `PhilomenaMedia.Processors.intensities/2`, as it handles
> media files of any type supported by this library, not just PNG or JPEG.
## Examples
iex> Intensities.file("image.png")
{:ok, %Intensities{nw: 111.689148, ne: 116.228048, sw: 93.268433, se: 104.630064}}
iex> Intensities.file("nonexistent.jpg")
:error
"""
@spec file(Path.t()) :: {:ok, t()} | :error
def file(input) do
System.cmd("image-intensities", [input])
|> case do
{output, 0} ->
[nw, ne, sw, se] =
output
|> String.trim()
|> String.split("\t")
|> Enum.map(&String.to_float/1)
{:ok, %__MODULE__{nw: nw, ne: ne, sw: sw, se: se}}
_error ->
:error
end
end
end