Skip to content

Commit

Permalink
PR: Implement support for *HyAB* large color difference. (#1305)
Browse files Browse the repository at this point in the history
* add HyAB color difference

* fix failing tests

* use tsplit instead of np.moveaxis for consistency with other functions

* make metric difference consistent with original paper

* wrap result in as_float() consistency with other functions

* missing comma in BIBLIOGRAPHY.bib

* fix incorrect handing of input arrays introduced by 07b4e28

* minor BIBLIOGRAPHY.bib formatting
  • Loading branch information
mesvam authored Nov 1, 2024
1 parent e7a3ac0 commit 363145a
Show file tree
Hide file tree
Showing 5 changed files with 177 additions and 0 deletions.
16 changes: 16 additions & 0 deletions BIBLIOGRAPHY.bib
Original file line number Diff line number Diff line change
@@ -1,3 +1,19 @@
@article{Abasi2020,
author = {Abasi, Saeedeh and Amani Tehran, Mohammad and Fairchild, Mark D.},
title = {Distance metrics for very large color differences},
journal = {Color Research \& Application},
volume = {45},
number = {2},
pages = {208-223},
keywords = {color difference formula, distance metric, perceived color difference, very large color difference},
doi = {https://doi.org/10.1002/col.22451},
url = {https://onlinelibrary.wiley.com/doi/abs/10.1002/col.22451},
eprint = {https://onlinelibrary.wiley.com/doi/pdf/10.1002/col.22451},
abstract = {Abstract Small, supra-threshold color differences are typically described with Euclidean distance metrics, or dimension-weighted Euclidean metrics, in color appearance spaces such as CIELAB. This research examines the perception and modeling of very large color differences in the order of 10 CIELAB units or larger, with an aim of describing the salience of color differences between distinct objects in real-world scenes and images. A psychophysical experiment was completed to compare directly large color-difference pairs designed to probe various Euclidean and non-Euclidean distance metrics. The results indicate that very large color differences are best described by HyAB, a combination of a Euclidean metric in hue and chroma with a city-block metric to incorporate lightness differences.},
year = {2020},
urldate = {2024-10-23},
howpublished = {http://markfairchild.org/PDFs/PAP40.pdf},
}
@misc{ANSI2003a,
title = {Specification of {{ROMM RGB}}},
author = {{ANSI}},
Expand Down
4 changes: 4 additions & 0 deletions colour/difference/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@
delta_E_CIE2000,
delta_E_CMC,
delta_E_ITP,
delta_E_HyAB,
)
from .din99 import delta_E_DIN99
from .huang2015 import power_function_Huang2015
Expand All @@ -72,6 +73,7 @@
"delta_E_CIE2000",
"delta_E_CMC",
"delta_E_ITP",
"delta_E_HyAB",
]
__all__ += [
"delta_E_DIN99",
Expand Down Expand Up @@ -99,6 +101,7 @@
"CAM16-SCD": delta_E_CAM16SCD,
"CAM16-UCS": delta_E_CAM16UCS,
"DIN99": delta_E_DIN99,
"HyAB": delta_E_HyAB,
}
)
DELTA_E_METHODS.__doc__ = """
Expand Down Expand Up @@ -138,6 +141,7 @@ def delta_E(
"CAM16-SCD",
"CAM16-UCS",
"DIN99",
"HyAB",
]
| str
) = "CIE 2000",
Expand Down
59 changes: 59 additions & 0 deletions colour/difference/delta_e.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
- :func:`colour.difference.delta_E_CIE2000`
- :func:`colour.difference.delta_E_CMC`
- :func:`colour.difference.delta_E_ITP`
- :func:`colour.difference.delta_E_HyAB`
References
----------
Expand All @@ -37,6 +38,10 @@
30(1), 21-30. doi:10.1002/col.20070
- :cite:`Mokrzycki2011` : Mokrzycki, W., & Tatol, M. (2011). Color difference
Delta E - A survey. Machine Graphics and Vision, 20, 383-411.
- :cite:`Abasi2020` : Abasi S, Amani Tehran M, Fairchild MD.
Distance metrics for very large color differences.
Color Res Appl. 2020; 45: 208-223. https://doi.org/10.1002/col.22451
Retrieved October 23, 2024, from http://markfairchild.org/PDFs/PAP40.pdf
"""

from __future__ import annotations
Expand Down Expand Up @@ -65,6 +70,7 @@
"delta_E_CIE2000",
"delta_E_CMC",
"delta_E_ITP",
"delta_E_HyAB",
]

JND_CIE1976 = 2.3
Expand Down Expand Up @@ -560,3 +566,56 @@ def delta_E_ITP(ICtCp_1: ArrayLike, ICtCp_2: ArrayLike) -> NDArrayFloat:
)

return as_float(d_E_ITP)


def delta_E_HyAB(Lab_1: ArrayLike, Lab_2: ArrayLike) -> NDArrayFloat:
"""
Return the difference between two *CIE L\\*a\\*b\\** colourspace arrays
This metric is intended for large color differences,
on the order of 10 CIELAB units or greater
Parameters
----------
Lab_1
*CIE L\\*a\\*b\\** colourspace array 1.
Lab_2
*CIE L\\*a\\*b\\** colourspace array 2.
Returns
-------
:class:`numpy.ndarray`
Colour difference HyAB.
Notes
-----
+------------+-----------------------+-------------------+
| **Domain** | **Scale - Reference** | **Scale - 1** |
+============+=======================+===================+
| ``Lab_1`` | ``L_1`` : [0, 100] | ``L_1`` : [0, 1] |
| | | |
| | ``a_1`` : [-100, 100] | ``a_1`` : [-1, 1] |
| | | |
| | ``b_1`` : [-100, 100] | ``b_1`` : [-1, 1] |
+------------+-----------------------+-------------------+
| ``Lab_2`` | ``L_2`` : [0, 100] | ``L_2`` : [0, 1] |
| | | |
| | ``a_2`` : [-100, 100] | ``a_2`` : [-1, 1] |
| | | |
| | ``b_2`` : [-100, 100] | ``b_2`` : [-1, 1] |
+------------+-----------------------+-------------------+
References
----------
:cite:`Abasi2020`
Examples
--------
>>> Lab_1 = np.array([25.0, 8.0, -14.0])
>>> Lab_2 = np.array([75.0, -35.0, 16.0])
>>> delta_E_HyAB(Lab_1, Lab_2) # doctest: +ELLIPSIS
102.4309069...
"""
dLab = to_domain_100(Lab_1) - to_domain_100(Lab_2)
dL, da, db = tsplit(dLab)
HyAB = np.abs(dL) + np.sqrt(da**2 + db**2)
return as_float(HyAB)
86 changes: 86 additions & 0 deletions colour/difference/tests/test_delta_e.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
delta_E_CIE1994,
delta_E_CIE2000,
delta_E_CMC,
delta_E_HyAB,
delta_E_ITP,
)
from colour.utilities import domain_range_scale, ignore_numpy_errors
Expand All @@ -37,6 +38,7 @@
"TestDelta_E_CIE2000",
"TestDelta_E_CMC",
"TestDelta_E_ITP",
"TestDelta_E_HyAB",
]


Expand Down Expand Up @@ -745,3 +747,87 @@ def test_nan_delta_E_ITP(self):
cases = [-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan]
cases = np.array(list(set(product(cases, repeat=3))))
delta_E_ITP(cases, cases)


class TestDelta_E_HyAB:
"""
Define :func:`colour.difference.delta_e.delta_E_HyAB` definition unit
tests methods.
"""

def test_delta_E_HyAB(self):
"""Test :func:`colour.difference.delta_e.delta_E_HyAB` definition."""
np.testing.assert_allclose(
delta_E_HyAB(
np.array([62.9, 28.3, 95.1]),
np.array([15.3, 3.8, 29.3]),
),
117.81317540177199,
atol=TOLERANCE_ABSOLUTE_TESTS,
)

np.testing.assert_allclose(
delta_E_HyAB(
np.array([96.7, 43.5, 13.5]),
np.array([11.6, 63.5, 71.6]),
),
146.54599254630037,
atol=TOLERANCE_ABSOLUTE_TESTS,
)

def test_n_dimensional_delta_E_HyAB(self):
"""
Test :func:`colour.difference.delta_e.delta_E_HyAB` definition
n-dimensional arrays support.
"""
Lab_1 = np.array([62.9, 28.3, 95.1])
Lab_2 = np.array([15.3, 3.8, 29.3])
delta_E = delta_E_HyAB(Lab_1, Lab_2)

Lab_1 = np.tile(Lab_1, (6, 1))
Lab_2 = np.tile(Lab_2, (6, 1))
delta_E = np.tile(delta_E, 6)
np.testing.assert_allclose(
delta_E_HyAB(Lab_1, Lab_2),
delta_E,
atol=TOLERANCE_ABSOLUTE_TESTS,
)

Lab_1 = np.reshape(Lab_1, (2, 3, 3))
Lab_2 = np.reshape(Lab_2, (2, 3, 3))
delta_E = np.reshape(delta_E, (2, 3))
np.testing.assert_allclose(
delta_E_HyAB(Lab_1, Lab_2),
delta_E,
atol=TOLERANCE_ABSOLUTE_TESTS,
)

def test_domain_range_scale_delta_E_HyAB(self):
"""
Test :func:`colour.difference.delta_e.delta_E_HyAB` definition
domain and range scale support.
"""

Lab_1 = np.array([62.9, 28.3, 95.1])
Lab_2 = np.array([15.3, 3.8, 29.3])
delta_E = delta_E_HyAB(Lab_1, Lab_2)

d_r = (("reference", 1), ("1", 0.01), ("100", 1))
for scale, factor in d_r:
with domain_range_scale(scale):
np.testing.assert_allclose(
delta_E_HyAB(Lab_1 * factor, Lab_2 * factor),
delta_E,
atol=TOLERANCE_ABSOLUTE_TESTS,
)

@ignore_numpy_errors
def test_nan_delta_E_HyAB(self):
"""
Test :func:`colour.difference.delta_e.delta_E_HyAB` definition nan
support.
"""

cases = [-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan]
cases = np.array(list(set(product(cases, repeat=3))))
delta_E_HyAB(cases, cases)
12 changes: 12 additions & 0 deletions docs/colour.difference.rst
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,18 @@ DIN99

delta_E_DIN99

HyAB
-----

``colour.difference``

.. currentmodule:: colour.difference

.. autosummary::
:toctree: generated/

delta_E_HyAB

Standardized Residual Sum of Squares (STRESS) Index
---------------------------------------------------

Expand Down

0 comments on commit 363145a

Please sign in to comment.