|
1 | 1 | //! Algorithms for calculating the difference between colors.
|
| 2 | +//! |
| 3 | +//! ## Selecting an algorithm |
| 4 | +//! |
| 5 | +//! Different distance/difference algorithms and formulae are good for different |
| 6 | +//! situations. Some are faster but less accurate and some may only be suitable |
| 7 | +//! for certain color spaces. This table may help navigating the options a bit |
| 8 | +//! by summarizing the difference between the traits in this module. |
| 9 | +//! |
| 10 | +//! **Disclaimer:** _This is not an actual benchmark! It's always best to test and |
| 11 | +//! evaluate the differences in an actual application, when possible._ |
| 12 | +//! |
| 13 | +//! Property explanations: |
| 14 | +//! - **Complexity:** Low complexity options are generally faster than high |
| 15 | +//! complexity options. |
| 16 | +//! - **Accuracy:** How the numerical difference compares to the perceived |
| 17 | +//! difference. May differ with the color space. |
| 18 | +//! |
| 19 | +//! | Trait | Complexity | Accuracy | Notes | |
| 20 | +//! |-------|------------|----------|-------| |
| 21 | +//! | [`Ciede2000`] | High | High for small differences, lower for large differences | The de-facto standard, but requires complex calculations to compensate for increased errors in certain areas of the CIE L\*a\*b\* (CIELAB) space. |
| 22 | +//! | [`EuclideanDistance`] | Low | Medium to high for perceptually uniform spaces, otherwise low | Can be good enough for perceptually uniform spaces or as a "quick and dirty" check. |
| 23 | +//! | [`HyAb`] | Low | High accuracy for medium to large differences. Less accurate than CIEDE2000 for small differences, but still performs well and is much less computationally expensive. | Similar to Euclidean distance, but separates lightness and chroma more. Limited to Cartesian spaces with a lightness axis and a chroma plane. |
| 24 | +//! | [`Wcag21RelativeContrast`] | Low | Low and only compares lightness | Meant for checking contrasts in computer graphics (such as between text and background colors), assuming sRGB. Mostly useful as a hint or for checking WCAG 2.1 compliance, considering the criticism it has received. |
2 | 25 |
|
3 | 26 | use core::ops::{Add, BitAnd, BitOr, Div};
|
4 | 27 |
|
@@ -384,12 +407,39 @@ pub trait Wcag21RelativeContrast: Sized {
|
384 | 407 | }
|
385 | 408 | }
|
386 | 409 |
|
| 410 | +/// Calculate a combination of Euclidean and Manhattan/City-block distance |
| 411 | +/// between two colors. |
| 412 | +/// |
| 413 | +/// The HyAB distance was suggested as an alternative to CIEDE2000 for large |
| 414 | +/// color differences in [Distance metrics for very large color |
| 415 | +/// differences](http://markfairchild.org/PDFs/PAP40.pdf) (in [Color Res Appl. |
| 416 | +/// 2019;1–16](https://doi.org/10.1002/col.22451)) by Saeedeh Abasi, Mohammad |
| 417 | +/// Amani Tehran and Mark D. Fairchild. It's originally meant for [CIE L\*a\*b\* |
| 418 | +/// (CIELAB)][crate::Lab], but this trait is also implemented for other color |
| 419 | +/// spaces that have similar semantics, although **without the same quality |
| 420 | +/// guarantees**. |
| 421 | +/// |
| 422 | +/// The hybrid distance is the sum of the absolute lightness difference and the |
| 423 | +/// distance on the chroma plane. This makes the lightness and chroma |
| 424 | +/// differences more independent from each other, which is meant to correspond |
| 425 | +/// more to how humans perceive the two qualities. |
| 426 | +pub trait HyAb { |
| 427 | + /// The type for the distance value. |
| 428 | + type Scalar; |
| 429 | + |
| 430 | + /// Calculate the hybrid distance between `self` and `other`. |
| 431 | + /// |
| 432 | + /// This returns the sum of the absolute lightness difference and the |
| 433 | + /// distance on the chroma plane. |
| 434 | + fn hybrid_distance(self, other: Self) -> Self::Scalar; |
| 435 | +} |
| 436 | + |
387 | 437 | #[cfg(test)]
|
388 | 438 | mod test {
|
389 | 439 | use core::str::FromStr;
|
390 | 440 |
|
391 |
| - use super::Wcag21RelativeContrast; |
392 |
| - use crate::Srgb; |
| 441 | + use super::{HyAb, Wcag21RelativeContrast}; |
| 442 | + use crate::{FromColor, Lab, Srgb}; |
393 | 443 |
|
394 | 444 | #[test]
|
395 | 445 | fn relative_contrast() {
|
@@ -429,4 +479,16 @@ mod test {
|
429 | 479 | assert_relative_eq!(c1.relative_contrast(white), 1.22, epsilon = 0.01);
|
430 | 480 | assert_relative_eq!(c1.relative_contrast(black), 17.11, epsilon = 0.01);
|
431 | 481 | }
|
| 482 | + |
| 483 | + #[test] |
| 484 | + fn hyab() { |
| 485 | + // From https://github.com/Evercoder/culori/blob/cd1fe08a12fa9ddfcf6b2e82914733d23ac117d0/test/difference.test.js#L186 |
| 486 | + let red = Lab::<_, f64>::from_color(Srgb::from(0xff0000).into_linear()); |
| 487 | + let green = Lab::<_, f64>::from_color(Srgb::from(0x008000).into_linear()); |
| 488 | + assert_relative_eq!( |
| 489 | + red.hybrid_distance(green), |
| 490 | + 139.93576718451553, |
| 491 | + epsilon = 0.000001 |
| 492 | + ); |
| 493 | + } |
432 | 494 | }
|
0 commit comments