mirror of
https://github.com/philomena-dev/philomena.git
synced 2024-12-05 00:57:59 +01:00
68 lines
2.1 KiB
Elixir
68 lines
2.1 KiB
Elixir
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
|