# `Monty.Frameworks.Models.Salience.Strategies.Vocus2.Pyramids`
[🔗](https://github.com/d-led/ex.tbp.monty/blob/v0.1.0/packages/ex_tbp_monty_core/lib/monty/frameworks/models/salience/strategies/vocus2/pyramids.ex#L1)

Multi-scale images, and the comparisons VOCUS2 makes across them.

Mirrors `tbp/monty/frameworks/models/salience/strategies/vocus2/pyramids.py`.

An attention model has to find what stands out at *some* size and not at
others: a small bright patch, a long edge, a dark corner. It does that by
building the image at several resolutions (octaves), each blurred several times
(scales), and then comparing neighbouring scales — a centre against a
surround — which is what `center_surround/2` and `laplacian/1` are for.

The pyramid follows Lowe (2004) as VOCUS2 implements it: each octave starts
from the extra scale the previous one computed, resampled by `:area`, and each
following scale is blurred by whatever it takes to reach the next sigma from
the one before — `sqrt(next² - previous²)`, because blurs compose in
quadrature. That is the step that makes a pyramid a pyramid rather than a
handful of blurs.

# `plane`

```elixir
@type plane() :: [[float()]]
```

# `center_surround`

```elixir
@spec center_surround(plane(), keyword()) ::
  {Monty.Frameworks.Models.Salience.Strategies.Vocus2.Pyramids.Pyramid.t(),
   Monty.Frameworks.Models.Salience.Strategies.Vocus2.Pyramids.Pyramid.t()}
```

A centre pyramid and its surround, at the same scales.

The surround is the centre blurred further, by the sigma that takes a centre
of `:center_sigma` to a surround of `:surround_sigma` — in quadrature again,
and scaled up with each scale so a coarse level is not compared against a fine
one's surroundings.

# `collapse`

```elixir
@spec collapse(
  Monty.Frameworks.Models.Salience.Strategies.Vocus2.Pyramids.Pyramid.t(),
  ([plane()] -&gt;
     plane())
) ::
  plane()
```

Collapses a pyramid into one image, the shape of its finest scale.

Every plane is enlarged to that shape by `:cubic` and then reduced with
`reduce` — so a map that exists at several scales becomes one map again.

# `collapse_max`

```elixir
@spec collapse_max(
  Monty.Frameworks.Models.Salience.Strategies.Vocus2.Pyramids.Pyramid.t()
) :: plane()
```

Collapses a pyramid by taking the strongest plane at each pixel.

# `collapse_mean`

```elixir
@spec collapse_mean(
  Monty.Frameworks.Models.Salience.Strategies.Vocus2.Pyramids.Pyramid.t()
) :: plane()
```

Collapses a pyramid by averaging its planes at each pixel.

# `combine`

```elixir
@spec combine(
  [Monty.Frameworks.Models.Salience.Strategies.Vocus2.Pyramids.Pyramid.t()],
  ([plane()] -&gt;
     plane())
) ::
  Monty.Frameworks.Models.Salience.Strategies.Vocus2.Pyramids.Pyramid.t()
```

Combines pyramids of the same shape into one, plane by plane with `reduce`.

A single pyramid is already the answer, which is how a config that names one
map and not several still works.

# `combine_max`

```elixir
@spec combine_max([
  Monty.Frameworks.Models.Salience.Strategies.Vocus2.Pyramids.Pyramid.t()
]) ::
  Monty.Frameworks.Models.Salience.Strategies.Vocus2.Pyramids.Pyramid.t()
```

Combines pyramids by taking the strongest plane at each pixel.

# `combine_mean`

```elixir
@spec combine_mean([
  Monty.Frameworks.Models.Salience.Strategies.Vocus2.Pyramids.Pyramid.t()
]) ::
  Monty.Frameworks.Models.Salience.Strategies.Vocus2.Pyramids.Pyramid.t()
```

Combines pyramids by averaging their planes at each pixel.

# `gaussian_pyramid`

```elixir
@spec gaussian_pyramid(plane(), keyword()) ::
  Monty.Frameworks.Models.Salience.Strategies.Vocus2.Pyramids.Pyramid.t()
```

A Gaussian pyramid: `:n_scales` blurred versions of the image at each octave.

`:sigma` is the blur of the first scale of the first octave, and every octave
starts from the same sigma, so the scales of one octave and the next mean the
same thing.

# `laplacian`

```elixir
@spec laplacian(
  Monty.Frameworks.Models.Salience.Strategies.Vocus2.Pyramids.Pyramid.t()
) ::
  Monty.Frameworks.Models.Salience.Strategies.Vocus2.Pyramids.Pyramid.t()
```

The Laplacian pyramid: each octave minus the octave below it, enlarged back.

What survives the subtraction is what the coarser octave could not represent —
the detail this resolution adds — which is what makes an edge visible at the
scale it belongs to. Has one fewer octave than its input.

# `octave_shapes`

```elixir
@spec octave_shapes({pos_integer(), pos_integer()}, pos_integer() | nil) :: [
  {pos_integer(), pos_integer()}
]
```

The resolutions an image of `image_shape` is built at, finest first.

Each octave is half the previous, and there are as many as fit until a
dimension would fall below one pixel — or fewer, if `:max_octaves` says so.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
