Module Convert.Perceptual

Perceptual color converter using LAB color space for nearest-neighbor matching.

This converter uses the CIE LAB color space to find the nearest terminal color, which provides better perceptual accuracy than Euclidean RGB distance. Colors that appear similar to humans will be numerically close in LAB space.

The implementation delegates nearest-color search to the palette modules (Spectrum_palettes.Terminal.Basic and Spectrum_palettes.Terminal.Xterm256), which use octree-based spatial indexing in LAB space for efficient lookup.

For ANSI-16 quantization, searches the full 16-color palette. For ANSI-256 quantization, searches only xterm codes 16-255 (color cube + greys), excluding basic codes 0-15.

val rgb_to_ansi256 : ?grey_threshold:int -> Gg.v4 -> int

Convert RGB color to ANSI-256 color code (0-255).

Uses perceptual color matching in LAB space to find the nearest xterm-256 palette color (codes 16-255, excluding basic 16 colors).

  (* Convert orange to nearest xterm-256 color *)
  let orange = Gg.Color.v_srgb 1.0 0.5 0.0 in
  let code = Perceptual.rgb_to_ansi256 orange in
  Printf.printf "\027[38;5;%dm■\027[0m Orange is code %d\n" code code
  (* Find nearest color for a purple shade *)
  let purple = Gg.Color.v_srgb 0.6 0.2 0.8 in
  let code = Perceptual.rgb_to_ansi256 purple in
  Printf.printf "Purple maps to xterm-256 code: %d\n" code
  (* Convert multiple colors *)
  let colors = [
    ("red", Gg.Color.v_srgb 0.9 0.1 0.1);
    ("green", Gg.Color.v_srgb 0.1 0.9 0.1);
    ("blue", Gg.Color.v_srgb 0.1 0.1 0.9);
  ] in
  List.iter (fun (name, color) ->
      let code = Perceptual.rgb_to_ansi256 color in
      Printf.printf "%s -> %d\n" name code
    ) colors
  • parameter grey_threshold

    Optional threshold for grey detection (currently unused)

  • parameter color

    RGB color to convert

  • returns

    ANSI-256 color code (16-255)

val rgb_to_ansi16 : Gg.v4 -> int

Convert RGB color to ANSI-16 basic color code (30-37, 90-97).

Uses perceptual color matching in LAB space to find the nearest of the 16 basic ANSI colors.

  (* Convert red to nearest basic color *)
  let red = Gg.Color.v_srgb 0.9 0.1 0.1 in
  let code = Perceptual.rgb_to_ansi16 red in
  Printf.printf "\027[%dm■\027[0m Red is code %d\n" code code
  (* Likely maps to bright red: code 91 *)
  (* Test basic vs bright color mapping *)
  let dark_green = Gg.Color.v_srgb 0.0 0.5 0.0 in
  let bright_green = Gg.Color.v_srgb 0.0 1.0 0.0 in

  let dark_code = Perceptual.rgb_to_ansi16 dark_green in
  let bright_code = Perceptual.rgb_to_ansi16 bright_green in

  Printf.printf "Dark green: %d, Bright green: %d\n"
    dark_code bright_code
  (* Dark: 32, Bright: 92 *)
  (* Quantize a gradient to basic colors *)
  for i = 0 to 10 do
    let intensity = float_of_int i /. 10. in
    let color = Gg.Color.v_srgb intensity 0.0 0.0 in
    let code = Perceptual.rgb_to_ansi16 color in
    Printf.printf "\027[%dm■\027[0m " code
  done;
  print_newline ()
  • parameter color

    RGB color to convert

  • returns

    ANSI-16 color code (30-37, 90-97)