colors
sleap_io.rendering.colors
¶
Color palette utilities for pose rendering.
This module provides built-in color palettes and utilities for converting colors to Skia format for rendering pose data.
Functions:
| Name | Description |
|---|---|
build_color_map |
Build color mapping based on scheme. |
determine_color_scheme |
Determine the color scheme to use based on context. |
get_palette |
Get n colors from a named palette as RGB tuples. |
resolve_color |
Resolve a flexible color specification to an RGB tuple. |
rgb_to_skia_color |
Convert RGB tuple to Skia Color4f. |
Attributes:
| Name | Type | Description |
|---|---|---|
ColorSpec |
Represent a PEP 604 union type |
|
NAMED_COLORS |
dict() -> new empty dictionary |
|
PALETTES |
dict() -> new empty dictionary |
|
TYPE_CHECKING |
Returns True when the argument is true, False otherwise. |
|
__annotations__ |
dict() -> new empty dictionary |
|
__cached__ |
str(object='') -> str |
|
__doc__ |
str(object='') -> str |
|
__file__ |
str(object='') -> str |
|
__name__ |
str(object='') -> str |
|
__package__ |
str(object='') -> str |
ColorSpec = tuple[int, int, int] | tuple[float, float, float] | int | float | str
module-attribute
¶
Represent a PEP 604 union type
E.g. for int | str
NAMED_COLORS = {'black': (0, 0, 0), 'white': (255, 255, 255), 'red': (255, 0, 0), 'green': (0, 255, 0), 'blue': (0, 0, 255), 'yellow': (255, 255, 0), 'cyan': (0, 255, 255), 'magenta': (255, 0, 255), 'gray': (128, 128, 128), 'grey': (128, 128, 128), 'orange': (255, 165, 0), 'purple': (128, 0, 128), 'pink': (255, 192, 203), 'brown': (139, 69, 19)}
module-attribute
¶
dict() -> new empty dictionary dict(mapping) -> new dictionary initialized from a mapping object's (key, value) pairs dict(iterable) -> new dictionary initialized as if via: d = {} for k, v in iterable: d[k] = v dict(**kwargs) -> new dictionary initialized with the name=value pairs in the keyword argument list. For example: dict(one=1, two=2)
PALETTES = {'standard': [(0, 114, 189), (217, 83, 25), (237, 177, 32), (126, 47, 142), (119, 172, 48), (77, 190, 238), (162, 20, 47)], 'distinct': [(255, 100, 100), (100, 100, 255), (100, 255, 100), (255, 255, 100), (255, 100, 255), (100, 255, 255), (255, 180, 100), (180, 100, 255), (255, 150, 150), (150, 255, 200)], 'rainbow': [(255, 0, 0), (255, 127, 0), (255, 255, 0), (127, 255, 0), (0, 255, 0), (0, 255, 127), (0, 255, 255), (0, 127, 255), (0, 0, 255), (127, 0, 255), (255, 0, 255), (255, 0, 127)], 'warm': [(255, 87, 51), (255, 140, 0), (255, 195, 0), (255, 215, 0), (255, 69, 0), (220, 20, 60)], 'cool': [(0, 150, 255), (0, 191, 255), (30, 144, 255), (65, 105, 225), (138, 43, 226), (75, 0, 130)], 'pastel': [(255, 179, 186), (255, 223, 186), (255, 255, 186), (186, 255, 201), (186, 225, 255), (218, 186, 255)], 'seaborn': [(76, 114, 176), (221, 132, 82), (85, 168, 104), (196, 78, 82), (129, 114, 179), (147, 120, 96), (218, 139, 195), (140, 140, 140), (204, 185, 116), (100, 181, 205)], 'tableau10': [(31, 119, 180), (255, 127, 14), (44, 160, 44), (214, 39, 40), (148, 103, 189), (140, 86, 75), (227, 119, 194), (127, 127, 127), (188, 189, 34), (23, 190, 207)], 'viridis': [(68, 1, 84), (72, 40, 120), (62, 73, 137), (49, 104, 142), (38, 130, 142), (31, 158, 137), (53, 183, 121), (109, 205, 89), (180, 222, 44), (253, 231, 37)]}
module-attribute
¶
dict() -> new empty dictionary dict(mapping) -> new dictionary initialized from a mapping object's (key, value) pairs dict(iterable) -> new dictionary initialized as if via: d = {} for k, v in iterable: d[k] = v dict(**kwargs) -> new dictionary initialized with the name=value pairs in the keyword argument list. For example: dict(one=1, two=2)
TYPE_CHECKING = False
module-attribute
¶
Returns True when the argument is true, False otherwise. The builtins True and False are the only two instances of the class bool. The class bool is a subclass of the class int, and cannot be subclassed.
__annotations__ = {'NAMED_COLORS': 'dict[str, tuple[int, int, int]]', 'PALETTES': 'dict[str, list[tuple[int, int, int]]]'}
module-attribute
¶
dict() -> new empty dictionary dict(mapping) -> new dictionary initialized from a mapping object's (key, value) pairs dict(iterable) -> new dictionary initialized as if via: d = {} for k, v in iterable: d[k] = v dict(**kwargs) -> new dictionary initialized with the name=value pairs in the keyword argument list. For example: dict(one=1, two=2)
__cached__ = '/home/runner/work/sleap-io/sleap-io/sleap_io/rendering/__pycache__/colors.cpython-313.pyc'
module-attribute
¶
str(object='') -> str str(bytes_or_buffer[, encoding[, errors]]) -> str
Create a new string object from the given object. If encoding or errors is specified, then the object must expose a data buffer that will be decoded using the given encoding and error handler. Otherwise, returns the result of object.str() (if defined) or repr(object). encoding defaults to 'utf-8'. errors defaults to 'strict'.
__doc__ = 'Color palette utilities for pose rendering.\n\nThis module provides built-in color palettes and utilities for converting colors\nto Skia format for rendering pose data.\n'
module-attribute
¶
str(object='') -> str str(bytes_or_buffer[, encoding[, errors]]) -> str
Create a new string object from the given object. If encoding or errors is specified, then the object must expose a data buffer that will be decoded using the given encoding and error handler. Otherwise, returns the result of object.str() (if defined) or repr(object). encoding defaults to 'utf-8'. errors defaults to 'strict'.
__file__ = '/home/runner/work/sleap-io/sleap-io/sleap_io/rendering/colors.py'
module-attribute
¶
str(object='') -> str str(bytes_or_buffer[, encoding[, errors]]) -> str
Create a new string object from the given object. If encoding or errors is specified, then the object must expose a data buffer that will be decoded using the given encoding and error handler. Otherwise, returns the result of object.str() (if defined) or repr(object). encoding defaults to 'utf-8'. errors defaults to 'strict'.
__name__ = 'sleap_io.rendering.colors'
module-attribute
¶
str(object='') -> str str(bytes_or_buffer[, encoding[, errors]]) -> str
Create a new string object from the given object. If encoding or errors is specified, then the object must expose a data buffer that will be decoded using the given encoding and error handler. Otherwise, returns the result of object.str() (if defined) or repr(object). encoding defaults to 'utf-8'. errors defaults to 'strict'.
__package__ = 'sleap_io.rendering'
module-attribute
¶
str(object='') -> str str(bytes_or_buffer[, encoding[, errors]]) -> str
Create a new string object from the given object. If encoding or errors is specified, then the object must expose a data buffer that will be decoded using the given encoding and error handler. Otherwise, returns the result of object.str() (if defined) or repr(object). encoding defaults to 'utf-8'. errors defaults to 'strict'.
build_color_map(scheme, n_instances, n_nodes, n_tracks, track_indices=None, palette='standard', identity_indices=None, n_identities=0, category_indices=None, n_categories=0)
¶
Build color mapping based on scheme.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
scheme
|
Literal
|
Color scheme to use. |
required |
n_instances
|
int
|
Number of instances in frame. |
required |
n_nodes
|
int
|
Number of nodes in skeleton. |
required |
n_tracks
|
int
|
Total number of tracks (for track coloring). |
required |
track_indices
|
list[int] | None
|
Track index for each instance (for track coloring). |
None
|
palette
|
Literal | str
|
Color palette name. |
'standard'
|
identity_indices
|
list[int] | None
|
Global identity index for each instance (for identity
coloring; a palette index into |
None
|
n_identities
|
int
|
Total number of identities (for identity coloring). |
0
|
category_indices
|
list[int] | None
|
Global category index for each instance (for category
coloring; a palette index into |
None
|
n_categories
|
int
|
Total number of categories (for category coloring). |
0
|
Returns:
| Type | Description |
|---|---|
dict[str, list[tuple[int, int, int]]]
|
Dictionary with 'instance_colors' and/or 'node_colors' lists. |
Source code in sleap_io/rendering/colors.py
def build_color_map(
scheme: ColorScheme,
n_instances: int,
n_nodes: int,
n_tracks: int,
track_indices: list[int] | None = None,
palette: PaletteName | str = "standard",
identity_indices: list[int] | None = None,
n_identities: int = 0,
category_indices: list[int] | None = None,
n_categories: int = 0,
) -> dict[str, list[tuple[int, int, int]]]:
"""Build color mapping based on scheme.
Args:
scheme: Color scheme to use.
n_instances: Number of instances in frame.
n_nodes: Number of nodes in skeleton.
n_tracks: Total number of tracks (for track coloring).
track_indices: Track index for each instance (for track coloring).
palette: Color palette name.
identity_indices: Global identity index for each instance (for identity
coloring; a palette index into ``Labels.identities`` order).
n_identities: Total number of identities (for identity coloring).
category_indices: Global category index for each instance (for category
coloring; a palette index into ``Labels.categories`` order).
n_categories: Total number of categories (for category coloring).
Returns:
Dictionary with 'instance_colors' and/or 'node_colors' lists.
"""
colors = {}
if scheme == "track":
# Colors based on track identity
n = max(n_tracks, n_instances) if n_tracks > 0 else n_instances
palette_colors = get_palette(palette, n)
if track_indices is not None:
instance_colors = [
palette_colors[idx % len(palette_colors)] for idx in track_indices
]
else:
instance_colors = palette_colors[:n_instances]
colors["instance_colors"] = instance_colors
elif scheme == "identity":
# Colors based on global identity. Mirrors the track scheme: one palette
# color per identity index (in ``Labels.identities`` order).
n = max(n_identities, n_instances) if n_identities > 0 else n_instances
palette_colors = get_palette(palette, max(n, 1))
if identity_indices is not None:
instance_colors = [
palette_colors[idx % len(palette_colors)] for idx in identity_indices
]
else:
instance_colors = palette_colors[:n_instances]
colors["instance_colors"] = instance_colors
elif scheme == "category":
# Colors based on global category. Mirrors the identity scheme: one palette
# color per category index (in ``Labels.categories`` order).
n = max(n_categories, n_instances) if n_categories > 0 else n_instances
palette_colors = get_palette(palette, max(n, 1))
if category_indices is not None:
instance_colors = [
palette_colors[idx % len(palette_colors)] for idx in category_indices
]
else:
instance_colors = palette_colors[:n_instances]
colors["instance_colors"] = instance_colors
elif scheme == "instance":
# Colors based on instance index
palette_colors = get_palette(palette, n_instances)
colors["instance_colors"] = palette_colors
elif scheme == "node":
# Colors based on node type
palette_colors = get_palette(palette, n_nodes)
colors["node_colors"] = palette_colors
return colors
determine_color_scheme(has_tracks, is_single_image, scheme='auto')
¶
Determine the color scheme to use based on context.
When scheme is "auto", uses smart defaults: - If tracks available: color by track (consistent across frames) - Else if single image: color by instance (distinguishes animals) - Else if multi-image: color by node type (prevents flicker)
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
has_tracks
|
bool
|
Whether the labels have track assignments. |
required |
is_single_image
|
bool
|
Whether rendering a single image (vs video). |
required |
scheme
|
Literal
|
Requested color scheme. "auto" uses smart defaults. |
'auto'
|
Returns:
| Type | Description |
|---|---|
Literal
|
Resolved color scheme to use. |
Source code in sleap_io/rendering/colors.py
def determine_color_scheme(
has_tracks: bool,
is_single_image: bool,
scheme: ColorScheme = "auto",
) -> ColorScheme:
"""Determine the color scheme to use based on context.
When scheme is "auto", uses smart defaults:
- If tracks available: color by track (consistent across frames)
- Else if single image: color by instance (distinguishes animals)
- Else if multi-image: color by node type (prevents flicker)
Args:
has_tracks: Whether the labels have track assignments.
is_single_image: Whether rendering a single image (vs video).
scheme: Requested color scheme. "auto" uses smart defaults.
Returns:
Resolved color scheme to use.
"""
if scheme != "auto":
return scheme
# Smart defaults
if has_tracks:
return "track"
elif is_single_image:
return "instance"
else:
return "node"
get_palette(name, n_colors)
¶
Get n colors from a named palette as RGB tuples.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
name
|
Literal | str
|
Palette name. Built-in options: 'standard', 'distinct', 'rainbow', 'warm', 'cool', 'pastel', 'seaborn', 'tableau10', 'viridis'. With colorcet installed: 'glasbey', 'glasbey_hv', 'glasbey_cool', 'glasbey_warm'. |
required |
n_colors
|
int
|
Number of colors needed. |
required |
Returns:
| Type | Description |
|---|---|
list[tuple[int, int, int]]
|
List of (R, G, B) tuples. |
Raises:
| Type | Description |
|---|---|
ValueError
|
If palette name is not recognized. |
Source code in sleap_io/rendering/colors.py
def get_palette(name: PaletteName | str, n_colors: int) -> list[tuple[int, int, int]]:
"""Get n colors from a named palette as RGB tuples.
Args:
name: Palette name. Built-in options: 'standard', 'distinct', 'rainbow',
'warm', 'cool', 'pastel', 'seaborn', 'tableau10', 'viridis'.
With colorcet installed: 'glasbey', 'glasbey_hv', 'glasbey_cool',
'glasbey_warm'.
n_colors: Number of colors needed.
Returns:
List of (R, G, B) tuples.
Raises:
ValueError: If palette name is not recognized.
"""
# Try built-in palettes first
if name in PALETTES:
palette = PALETTES[name]
return _extend_palette(palette, n_colors)
# Try colorcet palettes
import colorcet as cc
if name in cc.palette:
hex_colors = cc.palette[name]
rgb_colors = [_hex_to_rgb(c) for c in hex_colors]
return _extend_palette(rgb_colors, n_colors)
# Unknown palette - raise error with available options
raise ValueError(
f"Unknown palette: {name}. "
f"Available: {list(PALETTES.keys())} (built-in), "
"or any colorcet palette (e.g., glasbey, glasbey_hv, fire, rainbow4)"
)
resolve_color(color)
¶
Resolve a flexible color specification to an RGB tuple.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
color
|
ColorSpec
|
Color specification in various formats:
- RGB int tuple: |
required |
Returns:
| Type | Description |
|---|---|
tuple[int, int, int]
|
RGB tuple of integers in 0-255 range. |
Raises:
| Type | Description |
|---|---|
ValueError
|
If color specification is invalid. |
Examples:
Source code in sleap_io/rendering/colors.py
def resolve_color(color: ColorSpec) -> tuple[int, int, int]:
"""Resolve a flexible color specification to an RGB tuple.
Args:
color: Color specification in various formats:
- RGB int tuple: ``(255, 128, 0)``
- RGB float tuple: ``(1.0, 0.5, 0.0)`` - values in 0.0-1.0 range
- Grayscale int: ``128`` → ``(128, 128, 128)``
- Grayscale float: ``0.5`` → ``(127, 127, 127)``
- Named color: ``"black"``, ``"white"``, ``"red"``, etc.
- Hex color: ``"#ff8000"`` or ``"#f80"``
- Palette index: ``"tableau10[2]"``, ``"glasbey[5]"``
Returns:
RGB tuple of integers in 0-255 range.
Raises:
ValueError: If color specification is invalid.
Examples:
>>> resolve_color((255, 128, 0))
(255, 128, 0)
>>> resolve_color((1.0, 0.5, 0.0))
(255, 127, 0)
>>> resolve_color("red")
(255, 0, 0)
>>> resolve_color("#ff8000")
(255, 128, 0)
>>> resolve_color("#f80")
(255, 136, 0)
>>> resolve_color("tableau10[2]")
(44, 160, 44)
>>> resolve_color(128)
(128, 128, 128)
>>> resolve_color(0.5)
(127, 127, 127)
"""
# Grayscale int
if isinstance(color, int):
value = max(0, min(255, color))
return (value, value, value)
# Grayscale float (0.0-1.0 range)
if isinstance(color, float):
value = int(max(0.0, min(1.0, color)) * 255)
return (value, value, value)
# Tuple (RGB)
if isinstance(color, tuple):
if len(color) != 3:
raise ValueError(f"RGB tuple must have 3 elements, got {len(color)}")
r, g, b = color
# Detect float vs int by Python type
if isinstance(r, float) or isinstance(g, float) or isinstance(b, float):
# Float tuple: 0.0-1.0 range
r_int = int(max(0.0, min(1.0, float(r))) * 255)
g_int = int(max(0.0, min(1.0, float(g))) * 255)
b_int = int(max(0.0, min(1.0, float(b))) * 255)
return (r_int, g_int, b_int)
else:
# Int tuple: 0-255 range
r_int = max(0, min(255, int(r)))
g_int = max(0, min(255, int(g)))
b_int = max(0, min(255, int(b)))
return (r_int, g_int, b_int)
# String
if isinstance(color, str):
color_lower = color.lower().strip()
# Named color
if color_lower in NAMED_COLORS:
return NAMED_COLORS[color_lower]
# Hex color
if color.startswith("#"):
hex_part = color[1:]
if len(hex_part) == 3 or len(hex_part) == 6:
return _hex_to_rgb(color)
raise ValueError(f"Invalid hex color: {color}")
# Palette index: "palette_name[index]"
palette_match = re.match(r"^(\w+)\[(\d+)\]$", color)
if palette_match:
palette_name = palette_match.group(1)
index = int(palette_match.group(2))
# Get palette colors
try:
palette_colors = get_palette(palette_name, index + 1)
if index < len(palette_colors):
return palette_colors[index]
except ValueError:
pass
raise ValueError(f"Invalid palette index: {color}")
raise ValueError(
f"Unknown color: {color!r}. Valid named colors: {list(NAMED_COLORS.keys())}"
)
raise TypeError(f"Invalid color type: {type(color).__name__}")
rgb_to_skia_color(rgb, alpha=255)
¶
Convert RGB tuple to Skia Color4f.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
rgb
|
tuple[int, int, int]
|
(R, G, B) tuple with values 0-255. |
required |
alpha
|
int
|
Alpha value 0-255. Defaults to 255 (opaque). |
255
|
Returns:
| Type | Description |
|---|---|
Color4f
|
Skia Color4f object. |
Source code in sleap_io/rendering/colors.py
def rgb_to_skia_color(rgb: tuple[int, int, int], alpha: int = 255) -> "skia.Color4f":
"""Convert RGB tuple to Skia Color4f.
Args:
rgb: (R, G, B) tuple with values 0-255.
alpha: Alpha value 0-255. Defaults to 255 (opaque).
Returns:
Skia Color4f object.
"""
import skia
return skia.Color(rgb[0], rgb[1], rgb[2], alpha)