Skip to main content

oximedia_calibrate/
lib.rs

1//! Professional color calibration and matching tools for `OxiMedia`.
2//!
3//! `oximedia-calibrate` provides comprehensive color calibration and matching
4//! capabilities for professional video and image processing workflows. This includes:
5//!
6//! - **Camera Calibration**: `ColorChecker`-based camera profiling and characterization
7//! - **Display Calibration**: Monitor calibration, gamma correction, and profiling
8//! - **Color Matching**: Match colors across multiple cameras and devices
9//! - **ICC Profile Generation**: Create ICC color profiles from measurements
10//! - **ICC Profile Application**: Apply ICC profiles to images and video
11//! - **LUT Generation**: Generate calibration LUTs from measurements
12//! - **White Balance**: Advanced white balance algorithms and presets
13//! - **Color Temperature**: Automatic color temperature detection and shifting
14//! - **Gamut Mapping**: Map device gamut to working color space
15//! - **Chromatic Adaptation**: Adapt colors to different illuminants
16//!
17//! # Example
18//!
19//! ```rust,ignore
20//! use oximedia_calibrate::{
21//!     camera::{ColorChecker, ColorCheckerType},
22//!     white::WhiteBalancePreset,
23//!     temp::estimate_color_temperature,
24//! };
25//!
26//! # fn example() -> Result<(), Box<dyn std::error::Error>> {
27//! // Detect ColorChecker in an image
28//! let checker = ColorChecker::detect_in_image(&image_data, ColorCheckerType::Classic24)?;
29//!
30//! // Generate camera profile
31//! let profile = checker.generate_camera_profile()?;
32//!
33//! // Apply white balance
34//! let balanced = WhiteBalancePreset::Daylight.apply_to_image(&image_data)?;
35//!
36//! // Estimate color temperature
37//! let temp = estimate_color_temperature(&image_data)?;
38//! # Ok(())
39//! # }
40//! ```
41//!
42//! # Features
43//!
44//! ## Camera Calibration
45//!
46//! - Automatic `ColorChecker` detection in images
47//! - Patch extraction with subpixel accuracy
48//! - Camera profile generation (ICC/LUT)
49//! - Multi-illuminant calibration support
50//! - Calibration verification and validation
51//!
52//! ## Display Calibration
53//!
54//! - Gamma curve measurement and calibration
55//! - Display uniformity testing
56//! - Monitor profiling for accurate color reproduction
57//! - Display characterization
58//!
59//! ## Color Matching
60//!
61//! - Multi-camera color matching workflows
62//! - Scene-to-scene color matching for continuity
63//! - Match to reference target capabilities
64//! - Color consistency verification
65//!
66//! ## ICC Profiles
67//!
68//! - ICC v2 and v4 profile generation
69//! - ICC profile parsing and validation
70//! - ICC profile application to images
71//! - Profile inspection and analysis
72//!
73//! ## LUT Generation
74//!
75//! - Measurement-based LUT creation
76//! - 1D and 3D calibration LUTs
77//! - LUT verification and validation
78//! - Interpolation quality assessment
79//!
80//! ## White Balance
81//!
82//! - Automatic white balance from scene analysis
83//! - Standard presets (Daylight, Tungsten, Fluorescent, etc.)
84//! - Custom white balance from reference patch
85//! - Gray world and white patch algorithms
86//!
87//! ## Color Temperature
88//!
89//! - Automatic color temperature estimation
90//! - Temperature shift application
91//! - Kelvin to RGB conversion
92//! - Illuminant D-series support
93//!
94//! ## Gamut Mapping
95//!
96//! - Device gamut to working space mapping
97//! - Perceptual gamut mapping strategies
98//! - Gamut compression algorithms
99//! - Out-of-gamut color handling
100//!
101//! ## Chromatic Adaptation
102//!
103//! - Bradford chromatic adaptation transform
104//! - Von Kries adaptation
105//! - CAT02 adaptation (CIECAM02)
106//! - Custom illuminant adaptation
107//!
108//! # `ColorChecker` Support
109//!
110//! - X-Rite `ColorChecker` Classic (24 patches)
111//! - X-Rite `ColorChecker` Passport
112//! - `Datacolor` `SpyderCheckr`
113//! - Custom target support
114//!
115//! # Calibration Workflows
116//!
117//! ## Camera Profiling Workflow
118//!
119//! 1. Shoot `ColorChecker` under target lighting
120//! 2. Detect `ColorChecker` in image
121//! 3. Extract patch colors
122//! 4. Generate camera ICC profile or LUT
123//! 5. Apply calibration to footage
124//! 6. Verify calibration accuracy
125//!
126//! ## Display Calibration Workflow
127//!
128//! 1. Measure display with colorimeter
129//! 2. Generate gamma and uniformity profiles
130//! 3. Create display ICC profile
131//! 4. Apply profile to output pipeline
132//! 5. Verify display accuracy
133//!
134//! ## Camera Matching Workflow
135//!
136//! 1. Calibrate primary camera (Camera A)
137//! 2. Shoot matching target with Camera B
138//! 3. Generate matching LUT/profile
139//! 4. Apply to Camera B footage
140//! 5. Verify color matching across cameras
141
142#![warn(missing_docs)]
143#![allow(clippy::module_name_repetitions)]
144#![allow(clippy::similar_names)]
145#![allow(clippy::cast_possible_truncation)]
146#![allow(clippy::cast_sign_loss)]
147#![allow(clippy::cast_precision_loss)]
148#![allow(clippy::cast_lossless)]
149#![allow(clippy::too_many_arguments)]
150#![allow(clippy::too_many_lines)]
151#![allow(clippy::missing_errors_doc)]
152#![allow(clippy::missing_panics_doc)]
153#![allow(dead_code)]
154
155pub mod aces_calibration;
156pub mod aging_model;
157pub mod ambient_compensation;
158pub mod batch_calibrate;
159pub mod calibrate_report;
160pub mod calibration_extras;
161pub mod calibration_schedule;
162pub mod camera;
163pub mod chart_detection;
164pub mod chromatic;
165pub mod color_checker;
166pub mod color_space;
167pub mod delta_e;
168pub mod display;
169pub mod display_verify;
170pub mod flare_correction;
171pub mod gamut;
172pub mod gamut_checker;
173pub mod geometry;
174pub mod hdr_calibration;
175pub mod icc;
176pub mod icc_profile;
177pub mod lens_profile;
178pub mod lut;
179pub mod r#match;
180pub mod metamerism;
181pub mod monitor_calibration;
182pub mod patch_extract;
183pub mod printer_calibration;
184pub mod spectral;
185pub mod spectral_reconstruction;
186pub mod temp;
187pub mod temporal_uniformity;
188pub mod test_chart;
189pub mod uniformity;
190pub mod white;
191pub mod white_balance;
192
193mod error;
194
195pub use error::{CalibrationError, CalibrationResult};
196
197/// RGB color value (normalized to 0.0-1.0 range).
198pub type Rgb = [f64; 3];
199
200/// RGBA color value (normalized to 0.0-1.0 range).
201pub type Rgba = [f64; 4];
202
203/// XYZ tristimulus value.
204pub type Xyz = [f64; 3];
205
206/// LAB color value (L*a*b* color space).
207pub type Lab = [f64; 3];
208
209/// 3x3 color matrix.
210pub type Matrix3x3 = [[f64; 3]; 3];
211
212/// 3x4 color matrix with offset.
213pub type Matrix3x4 = [[f64; 4]; 3];
214
215/// Standard illuminant types.
216#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
217pub enum Illuminant {
218    /// Standard Illuminant A (2856K, tungsten/incandescent).
219    A,
220    /// Standard Illuminant D50 (5000K, horizon daylight).
221    D50,
222    /// Standard Illuminant D55 (5500K, mid-morning daylight).
223    D55,
224    /// Standard Illuminant D65 (6500K, noon daylight).
225    D65,
226    /// Standard Illuminant D75 (7500K, north sky daylight).
227    D75,
228    /// Standard Illuminant E (equal energy).
229    E,
230    /// Fluorescent F2 (4200K, cool white fluorescent).
231    F2,
232    /// Fluorescent F7 (6500K, broad-band daylight fluorescent).
233    F7,
234    /// Fluorescent F11 (4000K, narrow-band white fluorescent).
235    F11,
236}
237
238impl Illuminant {
239    /// Get the XYZ tristimulus values for this illuminant (2° observer).
240    #[must_use]
241    pub const fn xyz(&self) -> Xyz {
242        match self {
243            Self::A => [1.098_51, 1.0, 0.355_85],
244            Self::D50 => [0.964_22, 1.0, 0.825_21],
245            Self::D55 => [0.956_85, 1.0, 0.921_69],
246            Self::D65 => [0.950_47, 1.0, 1.088_83],
247            Self::D75 => [0.949_72, 1.0, 1.226_38],
248            Self::E => [1.0, 1.0, 1.0],
249            Self::F2 => [0.991_44, 1.0, 0.678_09],
250            Self::F7 => [0.950_41, 1.0, 1.086_14],
251            Self::F11 => [1.009_62, 1.0, 0.643_65],
252        }
253    }
254
255    /// Get the color temperature in Kelvin.
256    #[must_use]
257    pub const fn color_temperature(&self) -> u32 {
258        match self {
259            Self::A => 2856,
260            Self::D50 => 5000,
261            Self::D55 => 5500,
262            Self::D65 => 6500,
263            Self::D75 => 7500,
264            Self::E => 5454,
265            Self::F2 => 4200,
266            Self::F7 => 6500,
267            Self::F11 => 4000,
268        }
269    }
270}
271
272/// Observer angle for colorimetric calculations.
273#[derive(Clone, Copy, Debug, PartialEq, Eq)]
274pub enum Observer {
275    /// CIE 1931 2° Standard Observer.
276    Degree2,
277    /// CIE 1964 10° Standard Observer.
278    Degree10,
279}
280
281#[cfg(test)]
282mod tests {
283    use super::*;
284
285    #[test]
286    fn test_illuminant_xyz() {
287        let d65 = Illuminant::D65.xyz();
288        assert!((d65[0] - 0.950_47).abs() < 1e-5);
289        assert!((d65[1] - 1.0).abs() < 1e-10);
290        assert!((d65[2] - 1.088_83).abs() < 1e-5);
291    }
292
293    #[test]
294    fn test_illuminant_temperature() {
295        assert_eq!(Illuminant::D65.color_temperature(), 6500);
296        assert_eq!(Illuminant::A.color_temperature(), 2856);
297        assert_eq!(Illuminant::D50.color_temperature(), 5000);
298    }
299}