Documentation

Color Analysis

Extract dominant colors and search images by color properties

Automatic on Upload

Color extraction runs automatically whenever you upload images — no extra call needed. Use client.colors.get() to retrieve colors after upload. The extract() method is only needed to re-run extraction with different settings (e.g., fewer colors or force refresh).

Get Image Colors

Retrieve colors from an uploaded image

async with AionVision(api_key="aion_...") as client:
# Upload an image (colors extracted automatically)
upload = await client.upload_one("furniture.jpg")
# Get the extracted colors
result = await client.colors.get(upload.image_id)
if result.is_completed:
for color in result.color_analysis.dominant_colors:
print(f"{color.name}: {color.hex} ({color.percentage:.1f}%)")
print(f" Family: {color.family}")
print(f" RGB: ({color.rgb.r}, {color.rgb.g}, {color.rgb.b})")

Re-extraction (Optional)

Colors are extracted automatically on upload with up to 16 colors. Use extract() only if you need to re-run with different settings.

# Re-extract with fewer colors (3-16 range)
result = await client.colors.extract(
image_id,
n_colors=6
)
# Force re-extraction even if colors already exist
result = await client.colors.extract(
image_id,
force=True,
n_colors=8
)

Color Analysis Structure

Understanding the color analysis response

result = await client.colors.get(image_id)
if result.is_completed:
analysis = result.color_analysis
# Dominant colors (ordered by percentage)
for color in analysis.dominant_colors:
print(f"{color.rank}. {color.name} ({color.hex})")
print(f" Coverage: {color.percentage:.1f}%")
print(f" Family: {color.family}")
# Multiple color spaces available
print(f" RGB: ({color.rgb.r}, {color.rgb.g}, {color.rgb.b})")
print(f" HSL: ({color.hsl.h:.0f}, {color.hsl.s:.0f}%, {color.hsl.l:.0f}%)")
print(f" LAB: ({color.lab.l:.1f}, {color.lab.a:.1f}, {color.lab.b:.1f})")
# Overall analytics
temp = analysis.analytics.temperature
print(f"Temperature: {temp.value} (score: {temp.score:.2f})")
brightness = analysis.analytics.brightness
print(f"Brightness: {brightness.category} (avg: {brightness.average:.0f})")
saturation = analysis.analytics.saturation
print(f"Saturation: {saturation.category} (avg: {saturation.average:.0f})")

Search by Color

Search by Hex Code

Find images matching a specific color with perceptual tolerance

# Search for walnut/brown colors
results = await client.colors.search(
hex_code="#8B4513",
delta_e_threshold=20.0, # Perceptual difference tolerance (0-100, default: 15.0)
min_percentage=10.0 # Minimum color coverage (default: 5.0)
)
print(f"Found {results.total_count} images")
for r in results.results:
print(f"Image {r.image_id}: score {r.match_score:.2f}")
if r.thumbnail_url:
print(f" Thumbnail: {r.thumbnail_url}")

Delta-E Color Matching

Understanding perceptual color difference

Delta-EPerceptionUse Case
0-5Imperceptible to slightExact color matching
5-15NoticeableSimilar color search
15-30Obvious differenceSame color family
30+Different colorsBroad category matching

Search by Color Family

Find images by semantic color category

# Find all earth-toned images
earth_tones = await client.colors.search(
color_family="earth_tone",
limit=100
)
# Find earth tones with significant presence
warm_earth = await client.colors.search(
color_family="earth_tone",
min_percentage=20.0 # Significant presence
)
# Find metallic colors
metallic = await client.colors.search(
color_family="metallic"
)

Search by Color Name

Search using semantic color names

# Find brass-colored materials
brass = await client.colors.search(color_name="brass")
# Find walnut-colored items
walnut = await client.colors.search(color_name="walnut")
# Find marble/white stone
marble = await client.colors.search(color_name="marble")

Paginated Search

Handle large result sets with pagination

# Paginate through all results
all_results = []
offset = 0
while True:
page = await client.colors.search(
color_family="neutral",
limit=50,
offset=offset
)
all_results.extend(page.results)
if not page.has_more:
break
offset += len(page.results)
print(f"Total: {len(all_results)} images")

Auto-Pagination

Use search_all() to automatically iterate through all pages

# Iterate through all matching results automatically
async for result in client.colors.search_all(color_family="earth_tone"):
print(f"Image {result.image_id}: {len(result.colors)} colors")

Color Families

List Available Families

Get all color families with descriptions

families = await client.colors.list_families()
for family in families:
print(f"{family.display_name}")
print(f" {family.description}")
print(f" Examples: {', '.join(family.example_colors[:3])}")

Available Color Families

FamilyDescription
neutralBlacks, whites, grays - versatile foundation colors
earth_toneWarm, natural colors inspired by earth and nature
warmReds, oranges, yellows - energizing and inviting
coolBlues, greens, purples - calming and sophisticated
metallicMetallic and shimmer colors — gold, silver, bronze, copper
pastelSoft, muted colors with white undertones
vibrantBold, highly saturated colors

Batch Operations

Batch Re-extraction (Optional)

Colors are extracted automatically on upload. Use batch re-extraction only to re-run with different settings across multiple images.

# Force re-extraction with different color count
result = await client.colors.batch_extract(
image_ids,
force=True, # Re-extract even if colors exist
n_colors=6 # Use fewer colors per image
)
print(f"Queued {result.queued_count} images")
# Check status of individual images later
for image_id in image_ids:
status = await client.colors.get(image_id)
print(f"{image_id}: {status.status}")

Checking Extraction Status

Status Properties

Check the state of color extraction

result = await client.colors.get(image_id)
# Check status using properties
if result.is_completed:
print("Colors ready!")
print(f"Extracted at: {result.extracted_at}")
elif result.is_pending:
print("Still processing...")
else:
print(f"Status: {result.status}")
# Access status as enum
from aion.types.colors import ColorExtractionStatus
status_enum = result.status_enum
if status_enum == ColorExtractionStatus.COMPLETED:
# Process colors
pass
elif status_enum == ColorExtractionStatus.FAILED:
# Handle failure
pass

Extraction Status Values

StatusMeaning
pendingExtraction not yet started
queuedQueued for processing
processingCurrently being processed
completedColors extracted successfully
failedExtraction failed

Type Reference

ColorExtractionResult

Returned by client.colors.get() and client.colors.extract()

@dataclass(frozen=True)
class ColorExtractionResult:
image_id: str
status: str # pending | queued | processing | completed | failed
color_analysis: Optional[ColorAnalysis]
extracted_at: Optional[datetime]
# Properties
@property
def status_enum(self) -> Optional[ColorExtractionStatus]
@property
def is_completed(self) -> bool
@property
def is_pending(self) -> bool

ColorAnalysis

Complete color analysis for an image

@dataclass(frozen=True)
class ColorAnalysis:
version: str # Analysis algorithm version
dominant_colors: list[DominantColor] # Ordered by percentage
analytics: ColorAnalytics # Overall analytics
extracted_at: Optional[datetime]

DominantColor

A single dominant color

@dataclass(frozen=True)
class DominantColor:
rank: int # 1 = most dominant
percentage: float # Coverage percentage
rgb: RGB # RGB values
hsl: HSL # HSL values
lab: LAB # LAB values (for Delta-E)
hex: str # Hex code (e.g., "#C4A87C")
name: str # Semantic name (e.g., "Walnut")
family: str # Color family
@property
def family_enum(self) -> Optional[ColorFamily]

Color Space Types

Color values in different color spaces

@dataclass(frozen=True)
class RGB:
r: int # 0-255
g: int # 0-255
b: int # 0-255
@dataclass(frozen=True)
class HSL:
h: float # Hue (0-360)
s: float # Saturation (0-100)
l: float # Lightness (0-100)
@dataclass(frozen=True)
class LAB:
l: float # Lightness (0-100)
a: float # Green-Red (-128 to 127)
b: float # Blue-Yellow (-128 to 127)

ColorAnalytics

Overall image color analytics

@dataclass(frozen=True)
class ColorAnalytics:
temperature: ColorTemperature # warm | cool | neutral
brightness: ColorBrightness # dark | medium | light
saturation: ColorSaturation # muted | medium | vivid
@dataclass(frozen=True)
class ColorTemperature:
value: str # "warm", "cool", or "neutral"
score: float # Confidence (0-1)
@dataclass(frozen=True)
class ColorBrightness:
average: float # Average brightness (0-100)
category: str # "dark", "medium", or "light"
@dataclass(frozen=True)
class ColorSaturation:
average: float # Average saturation (0-100)
category: str # "muted", "medium", or "vivid"

ColorSearchResponse

Returned by client.colors.search()

@dataclass(frozen=True)
class ColorSearchResponse:
results: list[ColorSearchResult]
total_count: int
limit: int
offset: int
@property
def has_more(self) -> bool # More results available
@dataclass(frozen=True)
class ColorSearchResult:
image_id: str
match_score: float # Lower = better match
thumbnail_url: Optional[str]
color_analysis: Optional[ColorAnalysis]

ColorFamilyInfo

Returned by client.colors.list_families()

@dataclass(frozen=True)
class ColorFamilyInfo:
name: str # Internal name (e.g., "earth_tone")
display_name: str # Display name (e.g., "Earth Tones")
description: str # Description
example_colors: list[str] # Example hex codes
@property
def family_enum(self) -> Optional[ColorFamily]

BatchColorExtractionResult

Returned by client.colors.batch_extract()

@dataclass(frozen=True)
class BatchColorExtractionResult:
queued_count: int # Number of images queued
message: str # Status message

Enums

class ColorFamily(str, Enum):
NEUTRAL = "neutral"
EARTH_TONE = "earth_tone"
WARM = "warm"
COOL = "cool"
METALLIC = "metallic"
PASTEL = "pastel"
VIBRANT = "vibrant"
class ColorExtractionStatus(str, Enum):
PENDING = "pending"
QUEUED = "queued"
PROCESSING = "processing"
COMPLETED = "completed"
FAILED = "failed"