renoir.color.analysis¶
Color analysis functions for statistical and color space analysis.
This module provides tools for analyzing color distributions, converting between color spaces, and computing color statistics from artworks.
- class renoir.color.analysis.ColorAnalyzer[source]¶
Bases:
objectAnalyze color distributions and relationships in artworks.
This class provides methods for statistical analysis of colors, color space conversions, and comparative analysis across artworks. Designed for teaching color theory and computational analysis to art and design students.
- rgb_to_hsv(rgb)[source]¶
Convert RGB color to HSV (Hue, Saturation, Value) color space.
HSV is often more intuitive for artists and designers as it separates color into hue (color type), saturation (intensity), and value (brightness).
- Parameters:
rgb (
Tuple[int,int,int]) – Tuple of (R, G, B) values (0-255)- Returns:
H: Hue in degrees (0-360) S: Saturation as percentage (0-100) V: Value as percentage (0-100)
- Return type:
Tuple of (H, S, V) where
Example
>>> analyzer = ColorAnalyzer() >>> hsv = analyzer.rgb_to_hsv((255, 87, 51)) >>> print(f"Hue: {hsv[0]}°, Saturation: {hsv[1]}%, Value: {hsv[2]}%")
- hsv_to_rgb(hsv)[source]¶
Convert HSV color to RGB color space.
- Parameters:
hsv (
Tuple[float,float,float]) – Tuple of (H, S, V) where: H: Hue in degrees (0-360) S: Saturation as percentage (0-100) V: Value as percentage (0-100)- Return type:
Tuple[int,int,int]- Returns:
Tuple of (R, G, B) values (0-255)
Example
>>> analyzer = ColorAnalyzer() >>> rgb = analyzer.hsv_to_rgb((10, 80, 100)) >>> print(f"RGB: {rgb}")
- rgb_to_hsl(rgb)[source]¶
Convert RGB color to HSL (Hue, Saturation, Lightness) color space.
- Parameters:
rgb (
Tuple[int,int,int]) – Tuple of (R, G, B) values (0-255)- Returns:
H: Hue in degrees (0-360) S: Saturation as percentage (0-100) L: Lightness as percentage (0-100)
- Return type:
Tuple of (H, S, L) where
- hsl_to_rgb(hsl)[source]¶
Convert HSL color to RGB.
- Parameters:
hsl (
Tuple[float,float,float]) – Tuple of (H, S, L) where: H: Hue in degrees (0-360) S: Saturation as percentage (0-100) L: Lightness as percentage (0-100)- Return type:
Tuple[int,int,int]- Returns:
Tuple of (R, G, B) values (0-255)
- analyze_palette_statistics(colors)[source]¶
Compute statistical measures for a color palette.
Educational method for teaching students about color data analysis.
- Parameters:
colors (
List[Tuple[int,int,int]]) – List of RGB tuples- Returns:
mean_rgb: Average RGB values
std_rgb: Standard deviation of RGB values
hsv_values: HSV representation of each color
mean_hue: Average hue
mean_saturation: Average saturation
mean_value: Average brightness/value
- Return type:
Dictionary containing
Example
>>> analyzer = ColorAnalyzer() >>> colors = [(255, 87, 51), (100, 200, 150), (50, 100, 200)] >>> stats = analyzer.analyze_palette_statistics(colors) >>> print(f"Average hue: {stats['mean_hue']:.1f}°")
- calculate_color_diversity(colors)[source]¶
Calculate color diversity using hue distribution entropy.
Higher values indicate more diverse color usage. Useful for comparing artistic styles quantitatively.
- Parameters:
colors (
List[Tuple[int,int,int]]) – List of RGB tuples- Return type:
float- Returns:
Diversity score (0-1, higher = more diverse)
Example
>>> analyzer = ColorAnalyzer() >>> monochrome = [(100, 100, 100), (110, 110, 110), (120, 120, 120)] >>> diverse = [(255, 0, 0), (0, 255, 0), (0, 0, 255)] >>> print(analyzer.calculate_color_diversity(monochrome)) # Low score >>> print(analyzer.calculate_color_diversity(diverse)) # High score
- calculate_saturation_score(colors)[source]¶
Calculate average saturation score for a palette.
Useful for characterizing artistic styles: - High saturation: Bold, vibrant (Fauvism, Pop Art) - Low saturation: Muted, subtle (Impressionism, Realism)
- Parameters:
colors (
List[Tuple[int,int,int]]) – List of RGB tuples- Return type:
float- Returns:
Average saturation (0-100)
Example
>>> analyzer = ColorAnalyzer() >>> vibrant = [(255, 0, 0), (0, 255, 0), (0, 0, 255)] >>> muted = [(200, 180, 170), (150, 140, 130)] >>> print(analyzer.calculate_saturation_score(vibrant)) # ~100 >>> print(analyzer.calculate_saturation_score(muted)) # ~20
- calculate_brightness_score(colors)[source]¶
Calculate average brightness/value score for a palette.
- Parameters:
colors (
List[Tuple[int,int,int]]) – List of RGB tuples- Return type:
float- Returns:
Average brightness (0-100)
- compare_palettes(palette1, palette2)[source]¶
Compare two color palettes statistically.
Educational method for teaching comparative color analysis.
- Parameters:
palette1 (
List[Tuple[int,int,int]]) – First list of RGB tuplespalette2 (
List[Tuple[int,int,int]]) – Second list of RGB tuples
- Return type:
Dict- Returns:
Dictionary with comparative statistics
Example
>>> analyzer = ColorAnalyzer() >>> monet_colors = [(120, 150, 180), (200, 220, 230)] >>> picasso_colors = [(255, 50, 50), (50, 50, 200)] >>> comparison = analyzer.compare_palettes(monet_colors, picasso_colors) >>> print(f"Saturation difference: {comparison['saturation_diff']:.1f}%")
- classify_color_temperature(rgb)[source]¶
Classify a color as warm or cool based on hue.
Educational method for teaching color theory concepts.
- Parameters:
rgb (
Tuple[int,int,int]) – RGB tuple- Return type:
str- Returns:
‘warm’, ‘cool’, or ‘neutral’
Example
>>> analyzer = ColorAnalyzer() >>> print(analyzer.classify_color_temperature((255, 0, 0))) # 'warm' >>> print(analyzer.classify_color_temperature((0, 0, 255))) # 'cool' >>> print(analyzer.classify_color_temperature((128, 128, 128))) # 'neutral'
- analyze_color_temperature_distribution(colors)[source]¶
Analyze the distribution of warm vs. cool colors in a palette.
- Parameters:
colors (
List[Tuple[int,int,int]]) – List of RGB tuples- Return type:
Dict- Returns:
Dictionary with temperature distribution statistics
Example
>>> analyzer = ColorAnalyzer() >>> colors = [(255, 0, 0), (0, 0, 255), (0, 255, 0)] >>> temp_dist = analyzer.analyze_color_temperature_distribution(colors) >>> print(temp_dist)
- detect_complementary_colors(colors, tolerance=30)[source]¶
Detect complementary color pairs in a palette.
Complementary colors are opposite on the color wheel (180° apart). Educational method for teaching color harmony.
- Parameters:
colors (
List[Tuple[int,int,int]]) – List of RGB tuplestolerance (
float) – Hue difference tolerance in degrees (default: 30)
- Return type:
List[Tuple[Tuple[int,int,int],Tuple[int,int,int]]]- Returns:
List of complementary color pairs
Example
>>> analyzer = ColorAnalyzer() >>> colors = [(255, 0, 0), (0, 255, 255), (128, 0, 128)] >>> pairs = analyzer.detect_complementary_colors(colors)
- calculate_contrast_ratio(color1, color2)[source]¶
Calculate WCAG contrast ratio between two colors.
Useful for teaching accessibility in design. Ratio of 4.5:1 is minimum for normal text (WCAG AA).
- Parameters:
color1 (
Tuple[int,int,int]) – First RGB tuplecolor2 (
Tuple[int,int,int]) – Second RGB tuple
- Return type:
float- Returns:
Contrast ratio (1-21)
Example
>>> analyzer = ColorAnalyzer() >>> ratio = analyzer.calculate_contrast_ratio((0, 0, 0), (255, 255, 255)) >>> print(f"Contrast ratio: {ratio:.2f}:1") # 21.00:1
- detect_triadic_harmony(colors, tolerance=30)[source]¶
Detect triadic color harmonies in a palette.
Triadic harmonies are three colors equally spaced on the color wheel (120° apart). Used by masters like Mondrian and in vibrant designs.
- Parameters:
colors (
List[Tuple[int,int,int]]) – List of RGB tuplestolerance (
float) – Hue difference tolerance in degrees (default: 30)
- Return type:
List[Tuple[Tuple[int,int,int],Tuple[int,int,int],Tuple[int,int,int]]]- Returns:
List of triadic color triplets
Example
>>> analyzer = ColorAnalyzer() >>> colors = [(255, 0, 0), (0, 255, 0), (0, 0, 255)] # R, G, B >>> triads = analyzer.detect_triadic_harmony(colors) >>> print(f"Found {len(triads)} triadic harmonies")
- detect_analogous_harmony(colors, max_hue_range=60)[source]¶
Detect analogous color schemes in a palette.
Analogous colors are adjacent on the color wheel (within 60° typically). Creates harmonious, serene color schemes. Common in nature and landscapes.
- Parameters:
colors (
List[Tuple[int,int,int]]) – List of RGB tuplesmax_hue_range (
float) – Maximum hue range in degrees (default: 60)
- Return type:
List[List[Tuple[int,int,int]]]- Returns:
List of analogous color groups (groups of 2+ colors)
Example
>>> analyzer = ColorAnalyzer() >>> # Blues and greens (analogous) >>> colors = [(0, 100, 255), (0, 200, 200), (0, 255, 100)] >>> groups = analyzer.detect_analogous_harmony(colors)
- detect_split_complementary(colors, tolerance=30)[source]¶
Detect split-complementary color schemes.
Split-complementary uses a base color and two colors adjacent to its complement (instead of the direct complement). Provides high contrast while being more subtle than complementary. Popular in Renaissance art.
- Parameters:
colors (
List[Tuple[int,int,int]]) – List of RGB tuplestolerance (
float) – Hue difference tolerance in degrees (default: 30)
- Return type:
List[Tuple[Tuple[int,int,int],Tuple[int,int,int],Tuple[int,int,int]]]- Returns:
List of split-complementary triplets (base, complement1, complement2)
Example
>>> analyzer = ColorAnalyzer() >>> # Red with blue-green and yellow-green (instead of pure green) >>> colors = [(255, 0, 0), (0, 200, 100), (100, 200, 0)] >>> splits = analyzer.detect_split_complementary(colors)
- detect_tetradic_harmony(colors, tolerance=30)[source]¶
Detect tetradic (double complementary) color harmonies.
Tetradic uses two complementary pairs, forming a rectangle on the color wheel. Creates rich, diverse palettes. Used in complex compositions and modern art.
- Parameters:
colors (
List[Tuple[int,int,int]]) – List of RGB tuplestolerance (
float) – Hue difference tolerance in degrees (default: 30)
- Return type:
List[Tuple[Tuple[int,int,int],Tuple[int,int,int],Tuple[int,int,int],Tuple[int,int,int]]]- Returns:
List of tetradic color quartets
Example
>>> analyzer = ColorAnalyzer() >>> # Two complementary pairs >>> colors = [(255, 0, 0), (0, 255, 0), (0, 0, 255), (255, 255, 0)] >>> tetrads = analyzer.detect_tetradic_harmony(colors)
- analyze_color_harmony(colors)[source]¶
Comprehensive analysis of color harmonies present in a palette.
Analyzes all major harmony types and provides statistics. Educational method for teaching color theory in practice.
- Parameters:
colors (
List[Tuple[int,int,int]]) – List of RGB tuples- Returns:
complementary_pairs: List of complementary color pairs
triadic_sets: List of triadic harmonies
analogous_groups: List of analogous color groups
split_complementary_sets: List of split-complementary schemes
tetradic_sets: List of tetradic harmonies
harmony_score: Overall harmony score (0-1)
dominant_harmony: Most prevalent harmony type
- Return type:
Dictionary containing
Example
>>> analyzer = ColorAnalyzer() >>> colors = [(255, 0, 0), (0, 255, 0), (0, 0, 255)] >>> analysis = analyzer.analyze_color_harmony(colors) >>> print(f"Dominant harmony: {analysis['dominant_harmony']}")
- palette_earth_movers_distance(palette1, palette2)[source]¶
Calculate Palette Earth Mover’s Distance (PEMD) between two palettes.
Uses CIEDE2000 as the perceptual ground distance and colour proportions as weights, solved via optimal transport. This provides a structurally aware comparison that accounts for both colour similarity and proportion differences.
- Parameters:
palette1 (
List[Tuple[Tuple[int,int,int],float]]) – List of (RGB tuple, proportion) pairs. Proportions should sum to 1.0.palette2 (
List[Tuple[Tuple[int,int,int],float]]) – List of (RGB tuple, proportion) pairs. Proportions should sum to 1.0.
- Return type:
float- Returns:
PEMD distance (lower = more similar). Scale depends on CIEDE2000 units (typically 0–100+, where <2 is imperceptible).
- Raises:
ImportError – If scipy is not installed.
ValueError – If palettes are empty.
Example
>>> analyzer = ColorAnalyzer() >>> p1 = [((255, 0, 0), 0.6), ((0, 0, 255), 0.4)] >>> p2 = [((250, 10, 5), 0.5), ((10, 0, 250), 0.5)] >>> dist = analyzer.palette_earth_movers_distance(p1, p2) >>> print(f"PEMD: {dist:.2f}")
- calculate_color_complexity(colors, proportions=None, weights=None)[source]¶
Calculate the Colour Complexity Index (CCI) for a palette.
A multi-dimensional information-theoretic measure combining: - Hue entropy (spread across the colour wheel) - Perceptual spread (mean pairwise CIEDE2000 distance) - Proportion evenness (1 - Gini coefficient) - Harmony penalty (lower complexity if colours follow harmony rules)
- Parameters:
colors (
List[Tuple[int,int,int]]) – List of RGB tuplesproportions (
Optional[List[float]]) – Optional list of colour proportions (should sum to 1). If None, equal proportions are assumed.weights (
Optional[Dict[str,float]]) – Optional dict of component weights with keys: ‘hue_entropy’, ‘perceptual_spread’, ‘proportion_evenness’, ‘harmony_penalty’. Defaults to equal weighting.
- Returns:
cci: Composite Colour Complexity Index (0-1)
hue_entropy: Normalised hue entropy (0-1)
perceptual_spread: Normalised mean pairwise CIEDE2000 (0-1)
proportion_evenness: 1 - Gini coefficient (0-1)
harmony_penalty: Harmony score (0-1, subtracted)
components: Dict of weighted sub-scores
- Return type:
Dictionary containing
Example
>>> analyzer = ColorAnalyzer() >>> mondrian = [(255, 0, 0), (0, 0, 255), (255, 255, 0), ... (255, 255, 255), (0, 0, 0)] >>> result = analyzer.calculate_color_complexity(mondrian) >>> print(f"CCI: {result['cci']:.3f}")
- colour_provenance_score(colors, year, proportions=None)[source]¶
Calculate Colour Provenance Score (CPS) for a palette and attributed date.
Estimates how consistent a palette is with historically available pigments at the given date. Low scores may indicate anachronistic colour usage.
Requires the artist_pigments vocabulary with historical date fields.
- Parameters:
colors (
List[Tuple[int,int,int]]) – List of RGB tuples from the artworkyear (
int) – Attributed year of the artworkproportions (
Optional[List[float]]) – Optional colour proportions. If None, equal weights used.
- Returns:
score: Overall provenance score (0–1, higher = more consistent)
per_color: List of per-colour assessments
flagged: Colours flagged as potentially anachronistic
- Return type:
Dictionary containing
Example
>>> analyzer = ColorAnalyzer() >>> colors = [(0, 50, 200), (255, 0, 0), (255, 255, 0)] >>> result = analyzer.colour_provenance_score(colors, year=1780) >>> print(f"Provenance: {result['score']:.2f}") >>> for flag in result['flagged']: ... print(f" ⚠ {flag['color']}: {flag['reason']}")