Skip to main content

Crate munsellspace

Crate munsellspace 

Source
Expand description

§MunsellSpace 🎨

High-precision sRGB to Munsell color space conversion with 99.98% reference accuracy.

This library provides the most accurate open-source implementation for converting RGB colors to Munsell notation, validated against the complete 4,007-color reference dataset.

§Quick Start

use munsellspace::MunsellConverter;

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let converter = MunsellConverter::new()?;
     
    // Convert RGB to Munsell
    let munsell = converter.srgb_to_munsell([255, 0, 0])?;
    println!("Pure red: {}", munsell); // Output: 7.9R 5.2/20.5
     
    Ok(())
}

§Features

  • 99.98% Accuracy: Validated against complete reference dataset (4,006/4,007 exact matches)
  • High Performance: 4,000+ colors/second batch processing
  • Scientific Precision: Reference data lookup with intelligent interpolation
  • Thread Safety: Full support for concurrent usage with Send + Sync implementations
  • Semantic Color Names: 30 color name overlays from Centore (2020) research
  • Comprehensive Testing: Full test suite with accuracy validation

§About Munsell Color Space

The Munsell color system describes colors using three perceptually uniform dimensions:

  • Hue: Color family (R, YR, Y, GY, G, BG, B, PB, P, RP)
  • Value: Lightness from 0 (black) to 10 (white)
  • Chroma: Saturation from 0 (neutral) to 15+ (vivid)

Example: 5R 4.0/14.0 = medium red (5R) with medium lightness (4.0) and high saturation (14.0).

§Thread Safety

All public types in MunsellSpace are thread-safe and implement Send + Sync. You can safely share converters across multiple threads using Arc<T>:

use munsellspace::{MunsellConverter, IsccNbsClassifier};
use std::sync::Arc;
use std::thread;

fn main() -> Result<(), Box<dyn std::error::Error>> {
    // Create shared instances
    let converter = Arc::new(MunsellConverter::new()?);
    let classifier = Arc::new(IsccNbsClassifier::new()?);
     
    let mut handles = vec![];
     
    // Spawn multiple threads for concurrent processing
    for thread_id in 0..4 {
        let converter_clone = Arc::clone(&converter);
        let classifier_clone = Arc::clone(&classifier);
         
        let handle = thread::spawn(move || {
            // Each thread can safely use the converters concurrently
            let munsell = converter_clone.srgb_to_munsell([255, 0, 0]).unwrap();
             
            if let (Some(hue), Some(chroma)) = (&munsell.hue, munsell.chroma) {
                if let Ok(Some(iscc_color)) = classifier_clone.classify_munsell(hue, munsell.value, chroma) {
                    println!("Thread {}: {} -> {:?}", thread_id, munsell, iscc_color);
                }
            }
        });
         
        handles.push(handle);
    }
     
    // Wait for all threads to complete
    for handle in handles {
        handle.join().unwrap();
    }
     
    Ok(())
}

Internal caches use Arc<RwLock<T>> for safe concurrent access, allowing multiple readers or exclusive writers without data races.

§Semantic Color Names (v1.2.0+)

MunsellSpace includes 30 semantic color name overlays derived from Paul Centore’s 2020 research paper “Beige, aqua, fuchsia, etc.: Definitions for some non-basic surface colour names” (JAIC, 25, 24-54). These overlays define convex polyhedra in Munsell space for each color name, allowing you to determine which color names apply to any given Munsell color.

use munsellspace::{MunsellSpec, semantic_overlay, matching_overlays, get_registry};

fn main() {
    // Parse a Munsell color and find matching color names
    let color = MunsellSpec::new(7.4, 6.2, 3.4); // Near aqua centroid

    // Get the best-matching color name
    if let Some(name) = semantic_overlay(&color) {
        println!("Best match: {}", name);  // "aqua"
    }

    // Get all matching color names (colors can match multiple names)
    let matches = matching_overlays(&color);
    println!("All matches: {:?}", matches);

    // Access the complete registry for advanced use
    let registry = get_registry();
    println!("Registry has {} overlays", registry.len()); // 30
}

Available color names (30 total):

  • Non-basic (20): aqua, beige, coral, fuchsia, gold, lavender, lilac, magenta, mauve, navy, peach, rose, rust, sand, tan, taupe, teal, turquoise, violet, wine
  • Basic (10): blue, brown, gray, green, orange, pink, purple, red, white, yellow

§Unified Color Naming API (v1.2.0+)

The ColorClassifier provides a unified interface for all color naming systems. From any color input, get complete naming information with consistent modifiers across ISCC-NBS standard, extended, and semantic names.

use munsellspace::{ColorClassifier, ColorModifier};

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let classifier = ColorClassifier::new()?;

    // Classify any color format
    let desc = classifier.classify_srgb([180, 80, 60])?;

    // Get descriptors from all naming systems
    println!("Standard: {}", desc.standard_descriptor());   // "moderate reddish brown"
    println!("Extended: {}", desc.extended_descriptor());   // "moderate rust"
    if let Some(semantic) = desc.semantic_descriptor() {
        println!("Semantic: {}", semantic);                 // "moderate rust"
    }

    // The same modifier applies across all systems
    println!("Modifier: {:?}", desc.modifier);              // Moderate

    // Format any modifier + color combination
    let formatted = ColorModifier::Vivid.format("coral");
    println!("{}", formatted);                              // "vivid coral"

    Ok(())
}

§Flexible Color Characterization (v1.2.1+)

The new characterization API separates objective color facts from formatting preferences, giving you complete control over how colors are described.

use munsellspace::{ColorClassifier, ColorCharacterization, FormatOptions, BaseColorSet, OverlayMode};

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let classifier = ColorClassifier::new()?;

    // Get objective characterization
    let char = classifier.characterize_srgb([0, 0, 128])?;

    // Access raw data
    println!("ISCC-NBS #{}: {}", char.iscc_nbs_number, char.iscc_base_color);
    println!("Semantic matches: {:?}", char.semantic_matches);

    // Format with different preferences
    let standard = FormatOptions::new(BaseColorSet::Standard, OverlayMode::Ignore);
    let with_overlay = FormatOptions::new(BaseColorSet::Extended, OverlayMode::Include);

    println!("Standard: {}", char.describe(&standard));       // "dark blue"
    println!("With overlay: {}", char.describe(&with_overlay)); // "dark navy"

    // Preset options for common cases
    println!("{}", char.describe(&FormatOptions::standard()));           // "dark blue"
    println!("{}", char.describe(&FormatOptions::extended()));           // "dark blue"
    println!("{}", char.describe(&FormatOptions::standard_with_overlays())); // "dark navy"
    println!("{}", char.describe(&FormatOptions::extended_with_overlays())); // "dark navy"

    Ok(())
}

BaseColorSet: Controls which ISCC-NBS names to use

  • Standard: 29 official ISCC-NBS base names (“vivid yellow green”, “dark greenish blue”)
  • Extended: Uses lime/teal/turquoise for compound names (“vivid lime”, “dark teal”)

OverlayMode: Controls semantic overlay behavior

  • Ignore: Always use ISCC-NBS base colors
  • Include: Use nearest semantic overlay when available

§Edge Cases and Limitations

§Near-black colors (Value < 0.2)

The Munsell Renotation Dataset contains no entries below Value 0.2. Colors that compute to a Munsell Value below this threshold (roughly sRGB [0,0,0] through [12,12,12]) are returned as neutral (N) with chroma 0, because there is no renotation data to resolve their hue or chroma, and human color discrimination is negligible at such low luminance levels.

let converter = MunsellConverter::new()?;
let near_black = converter.srgb_to_munsell([1, 1, 1])?;
assert_eq!(near_black.notation, "N 0.0"); // Treated as neutral black

§Pure black

RGB [0,0,0] returns N 0.0 (neutral black with Value 0 and chroma 0).

Re-exports§

pub use converter::MunsellConverter;
pub use types::MunsellColor;
pub use types::RgbColor;
pub use types::IsccNbsName;
pub use types::IsccNbsPolygon;
pub use types::MunsellPoint;
pub use error::MunsellError;
pub use error::Result;
pub use illuminants::Illuminant;
pub use illuminants::ChromaticAdaptation;
pub use illuminants::ChromaticAdaptationMethod;
pub use iscc::IsccNbsClassifier;
pub use iscc::ColorMetadata;
pub use mechanical_wedges::MechanicalWedgeSystem;
pub use mathematical::MathematicalMunsellConverter;
pub use mathematical::MunsellSpecification;
pub use mathematical::CieXyY;
pub use mathematical::ChromaticAdaptation as MathematicalChromaticAdaptation;
pub use reverse_conversion::ReverseConverter;
pub use reverse_conversion::ColorFormats;
pub use reverse_conversion::CieLab;
pub use reverse_conversion::HslColor;
pub use reverse_conversion::HsvColor;
pub use reverse_conversion::munsell_to_hex_string;
pub use unified_cache::UnifiedColorCache;
pub use unified_cache::CachedColorResult;
pub use semantic_overlay::MunsellSpec;
pub use semantic_overlay::MunsellCartesian;
pub use semantic_overlay::SemanticOverlay;
pub use semantic_overlay::SemanticOverlayRegistry;
pub use semantic_overlay::parse_hue_to_number;
pub use semantic_overlay::hue_number_to_string;
pub use semantic_overlay::parse_munsell_notation;
pub use semantic_overlay::semantic_overlay;Deprecated
pub use semantic_overlay::matching_overlays;Deprecated
pub use semantic_overlay::matching_overlays_ranked;Deprecated
pub use semantic_overlay::matches_overlay;Deprecated
pub use semantic_overlay::closest_overlay;Deprecated
pub use semantic_overlay_data::create_overlay_registry;
pub use semantic_overlay_data::get_registry;
pub use color_names::ColorClassifier;
pub use color_names::ColorDescriptor;
pub use color_names::ColorModifier;
pub use color_names::known_color_names;
pub use color_names::is_known_color;
pub use color_names::color_name_count;
pub use color_names::ColorCharacterization;
pub use color_names::FormatOptions;
pub use color_names::BaseColorSet;
pub use color_names::OverlayMode;

Modules§

color_interpolation
Interpolation and extrapolation classes - exact 1:1 port from Python colour-science Line-by-line port with exact behavior matching
color_math_utils
Utility and validation functions - exact 1:1 port from Python colour-science Line-by-line port with exact behavior matching
color_names
Unified color naming API for MunsellSpace
color_notation_parser
String parsing and formatting functions - exact 1:1 port from Python colour-science Line-by-line port with exact behavior matching
constants
Mathematical constants and datasets for Munsell color space conversion
converter
High-precision sRGB to Munsell color space converter.
error
Error types for MunsellSpace conversion operations.
illuminants
Standard illuminants and chromatic adaptation
iscc
ISCC-NBS Color Name System Implementation
lab_color_space
Lab color space functions - exact 1:1 port from Python colour-science Line-by-line port with exact behavior matching
mathematical
Mathematical Munsell color space conversion implementation.
mechanical_wedges
Mechanical Wedge System for Deterministic ISCC-NBS Color Classification
munsell_color_science
Exact 1:1 port of Python colour-science munsell functions.
munsell_converter_core
Python-compatible Munsell converter This module integrates the exact 1:1 Python ports for accurate conversion
reverse_conversion
Reverse conversion pipeline: Munsell -> Lab -> sRGB/hex/HSL/HSV
semantic_overlay
Semantic overlay functionality for non-basic color names.
semantic_overlay_data
Semantic overlay registry using Centore polyhedron data.
types
Core types for Munsell color space representation.
unified_cache

Constants§

VERSION
Library version