Color.Palette.Summarize (Color v0.13.0)

Copy Markdown

Reduce an arbitrary list of colours to k representative colours by perceptually-uniform clustering.

This module is a thin wrapper over Color.Palette.Cluster. It takes a list of colour inputs, builds initial singleton clusters, runs the agglomerative merge to k survivors, and picks one member from each survivor as the swatch.

Both the merge step and the rep-selection step are exposed publicly on Color.Palette.Cluster so the :image library's pixel extraction pipeline can call exactly the same primitives (after a Scholar K-means pass) without algorithmic drift.

Algorithm

Each input colour starts as its own cluster; the closest pair (by mass-weighted Oklab distance, chromatic axes weighted twice as much as lightness) is merged repeatedly until k clusters remain. Each cluster's representative is then chosen by Color.Palette.Cluster.representative/2:

  • chromatic centroids → highest chroma × mass member (the most vivid example, weighted by how often it appears);

  • achromatic centroids → member nearest the centroid in weighted Oklab, with mass as tiebreaker.

See Color.Palette.Cluster for the full algorithmic discussion.

When to use this vs Color.Palette.Sort

  • Sort orders an existing list of colours; the output has the same number of colours as the input.

  • Summarize reduces the list to a smaller representative set. Run it before sorting when you have a large bag of near-duplicate swatches.

Complexity

Pairwise distance is recomputed each merge, giving an O(n³) worst case. This is fine for the typical palette sizes (tens of colours) the rest of Color.Palette works with; for thousands of inputs (e.g. raw image pixels), use a dedicated K-means pass upstream and call summarize/3 (or Color.Palette.Cluster.merge_until/3) only on the resulting cluster centroids.

Summary

Functions

Reduces a list of colours to at most k representative colours.

Functions

summarize(colors, k, options \\ [])

@spec summarize([Color.input()], pos_integer(), keyword()) :: [Color.SRGB.t()]

Reduces a list of colours to at most k representative colours.

Arguments

  • colors is a list of values accepted by Color.new/1 — hex strings, CSS named colours, %Color.SRGB{} structs, Oklch structs, etc.

  • k is the maximum number of colours in the output. If the input has fewer than k distinct entries, the output may be shorter.

Options

  • :weights is an optional list of non-negative numbers, the same length as colors. Each weight is the relative mass of its input colour during clustering. Default: every colour weighted 1.0.

  • :ab_weight is the multiplier on the chromatic axes (a, b) in the Oklab distance metric, relative to lightness L. Default 2.0.

  • :rep_chroma_threshold is the Oklch chroma above which the representative member is the highest-chroma original member of its cluster. Below this threshold, the representative is the closest to the centroid in weighted Oklab. Default 0.03.

Returns

  • A list of %Color.SRGB{} structs (one per surviving cluster), in the order the merging algorithm settled on. Pipe through Color.Palette.sort/2 for a perceptually ordered strip.

Examples

iex> hexes = ["#ff0000", "#fe0202", "#0000ff", "#0202fe", "#00ff00"]
iex> result = Color.Palette.Summarize.summarize(hexes, 3)
iex> length(result)
3

iex> hexes = ["#ff0000", "#0000ff"]
iex> Color.Palette.Summarize.summarize(hexes, 5) |> length()
2