From a5ba03b4052116fb3c9ccdeb779cf6aa990815e2 Mon Sep 17 00:00:00 2001 From: Sam Hocevar Date: Sun, 6 Jan 2013 21:27:08 +0000 Subject: [PATCH] color: unit test data for the CIEDE2000 color-difference formula. --- src/image/color/cie1931.cpp | 40 +++++++++-------- test/Makefile.am | 3 +- test/testsuite.vcxproj | 1 + test/unit/color.cpp | 87 +++++++++++++++++++++++++++++++++++++ 4 files changed, 111 insertions(+), 20 deletions(-) create mode 100644 test/unit/color.cpp diff --git a/src/image/color/cie1931.cpp b/src/image/color/cie1931.cpp index 1abecfaa..73beb0f4 100644 --- a/src/image/color/cie1931.cpp +++ b/src/image/color/cie1931.cpp @@ -26,6 +26,8 @@ namespace lol float Color::DistanceCIEDE2000(vec3 lab1, vec3 lab2) { + float const pi = 3.141592653589793f; + float const deg2rad = 6.28318530718f / 360.f; float const rad2deg = 360.f / 6.28318530718f; @@ -47,36 +49,36 @@ float Color::DistanceCIEDE2000(vec3 lab1, vec3 lab2) float dCp = Cp2 - Cp1; float Cp_ = 0.5f * (Cp1 + Cp2); - float hp1 = fmod(atan2(lab1.z, ap1) * rad2deg, 360.f); - if (hp1 < 0.f) - hp1 += 360.f; - float hp2 = fmod(atan2(lab2.z, ap2) * rad2deg, 360.f); - if (hp2 < 0.f) - hp2 += 360.f; - float dhp; - if (abs(hp1 - hp2) <= 180.f) + float hp1 = fmod(atan2(lab1.z, ap1) + 2.f * pi, 2.f * pi); + float hp2 = fmod(atan2(lab2.z, ap2) + 2.f * pi, 2.f * pi); + float dhp; /* -pi .. pi */ + if (abs(hp1 - hp2) <= pi) dhp = hp2 - hp1; else if (hp2 <= hp1) - dhp = hp2 - hp1 + 360.f; + dhp = hp2 - hp1 + 2.f * pi; else - dhp = hp2 - hp1 - 360.f; - float dHp = 2.f * sqrt(Cp1 * Cp2) * sin(dhp / 2.f * deg2rad); - float Hp_; - if (abs(hp1 - hp2) > 180.f) - Hp_ = 0.5f * (hp1 + hp2 + 360.f); + dhp = hp2 - hp1 - 2.f * pi; + float dHp = 2.f * sqrt(Cp1 * Cp2) * sin(dhp / 2.f); + float Hp_; /* half-angle 0 .. 360 */ + if (!(Cp1 * Cp2)) + Hp_ = hp1 + hp2; + else if (abs(hp1 - hp2) > pi && hp1 + hp2 < 2.f * pi) + Hp_ = 0.5f * (hp1 + hp2) + pi; + else if (abs(hp1 - hp2) > 180.f) + Hp_ = 0.5f * (hp1 + hp2) - pi; else Hp_ = 0.5f * (hp1 + hp2); - float T = 1.f - 0.17f * cos((Hp_ - 30.f) * deg2rad) - + 0.24f * cos(2 * Hp_ * deg2rad) - + 0.32f * cos((3.f * Hp_ + 6.f) * deg2rad) - - 0.20f * cos((4.f * Hp_ - 63.f) * deg2rad); + float T = 1.f - 0.17f * cos(Hp_ - pi / 6.f) + + 0.24f * cos(2.f * Hp_) + + 0.32f * cos(3.f * Hp_ + pi / 30.f) + - 0.20f * cos(4.f * Hp_ - 0.35f * pi); float SL = 1.f + 0.015f * (L_ - 50) * (L_ - 50) / sqrt(20.f + (L_ - 50) * (L_ - 50)); float SC = 1.f + 0.045f * Cp_; float SH = 1.f + 0.015f * Cp_ * T; float RT = -2.f * sqrt(pow(Cp_, 7.f) / (pow(Cp_, 7.f) + pow(25.f, 7.f))) - * sin(60.f * deg2rad * exp(-pow((Hp_ - 275.f) / 25.f, 2.f))); + * sin(60.f * deg2rad * exp(-pow((Hp_ * rad2deg - 275.f) / 25.f, 2.f))); dLp /= SL; dCp /= SC; diff --git a/test/Makefile.am b/test/Makefile.am index 458d75be..7dcd6fd2 100644 --- a/test/Makefile.am +++ b/test/Makefile.am @@ -22,7 +22,8 @@ endif testsuite_SOURCES = testsuite.cpp \ unit/vector.cpp unit/matrix.cpp unit/half.cpp unit/trig.cpp \ unit/build.cpp unit/real.cpp unit/image.cpp unit/quat.cpp unit/cmplx.cpp \ - unit/array.cpp unit/rotation.cpp unit/string.cpp unit/map.cpp + unit/array.cpp unit/rotation.cpp unit/string.cpp unit/map.cpp \ + unit/color.cpp testsuite_CPPFLAGS = @LOL_CFLAGS@ testsuite_LDFLAGS = $(top_builddir)/src/liblol.a @LOL_LIBS@ testsuite_DEPENDENCIES = $(top_builddir)/src/liblol.a diff --git a/test/testsuite.vcxproj b/test/testsuite.vcxproj index dc93dd80..b00ed9fd 100644 --- a/test/testsuite.vcxproj +++ b/test/testsuite.vcxproj @@ -39,6 +39,7 @@ + diff --git a/test/unit/color.cpp b/test/unit/color.cpp new file mode 100644 index 00000000..86c9d2e5 --- /dev/null +++ b/test/unit/color.cpp @@ -0,0 +1,87 @@ +// +// Lol Engine +// +// Copyright: (c) 2010-2013 Sam Hocevar +// This program is free software; you can redistribute it and/or +// modify it under the terms of the Do What The Fuck You Want To +// Public License, Version 2, as published by Sam Hocevar. See +// http://www.wtfpl.net/ for more details. +// + +#if defined HAVE_CONFIG_H +# include "config.h" +#endif + +#include "core.h" +#include "lol/unit.h" + +namespace lol +{ + +/* Data taken from “The CIEDE2000 Color-Difference Formula: Implementation + * Notes, Supplementary Test Data, and Mathematical Observations” (2004) + * by Gaurav Sharma, Wencheng Wu, Edul N. Dalal. Some data is disabled + * because it is at the discontinuity limit. */ +static float const ciede2k[] = +{ + 50.0000f, 2.6772f, -79.7751f, 50.0000f, 0.0000f, -82.7485f, 2.0425f, + 50.0000f, 3.1571f, -77.2803f, 50.0000f, 0.0000f, -82.7485f, 2.8615f, + 50.0000f, 2.8361f, -74.0200f, 50.0000f, 0.0000f, -82.7485f, 3.4412f, + 50.0000f, -1.3802f, -84.2814f, 50.0000f, 0.0000f, -82.7485f, 1.0000f, + 50.0000f, -1.1848f, -84.8006f, 50.0000f, 0.0000f, -82.7485f, 1.0000f, + 50.0000f, -0.9009f, -85.5211f, 50.0000f, 0.0000f, -82.7485f, 1.0000f, + 50.0000f, 0.0000f, 0.0000f, 50.0000f, -1.0000f, 2.0000f, 2.3669f, + 50.0000f, -1.0000f, 2.0000f, 50.0000f, 0.0000f, 0.0000f, 2.3669f, + 50.0000f, 2.4900f, -0.0010f, 50.0000f, -2.4900f, 0.0009f, 7.1792f, + 50.0000f, 2.4900f, -0.0010f, 50.0000f, -2.4900f, 0.0010f, 7.1792f, + //50.0000f, 2.4900f, -0.0010f, 50.0000f, -2.4900f, 0.0011f, 7.2195f, + //50.0000f, 2.4900f, -0.0010f, 50.0000f, -2.4900f, 0.0012f, 7.2195f, + 50.0000f, -0.0010f, 2.4900f, 50.0000f, 0.0009f, -2.4900f, 4.8045f, + 50.0000f, -0.0010f, 2.4900f, 50.0000f, 0.0010f, -2.4900f, 4.8045f, + //50.0000f, -0.0010f, 2.4900f, 50.0000f, 0.0011f, -2.4900f, 4.7461f, + 50.0000f, 2.5000f, 0.0000f, 50.0000f, 0.0000f, -2.5000f, 4.3065f, + 50.0000f, 2.5000f, 0.0000f, 73.0000f, 25.0000f, -18.0000f, 27.1492f, + 50.0000f, 2.5000f, 0.0000f, 61.0000f, -5.0000f, 29.0000f, 22.8977f, + 50.0000f, 2.5000f, 0.0000f, 56.0000f, -27.0000f, -3.0000f, 31.9030f, + 50.0000f, 2.5000f, 0.0000f, 58.0000f, 24.0000f, 15.0000f, 19.4535f, + 50.0000f, 2.5000f, 0.0000f, 50.0000f, 3.1736f, 0.5854f, 1.0000f, + 50.0000f, 2.5000f, 0.0000f, 50.0000f, 3.2972f, 0.0000f, 1.0000f, + 50.0000f, 2.5000f, 0.0000f, 50.0000f, 1.8634f, 0.5757f, 1.0000f, + 50.0000f, 2.5000f, 0.0000f, 50.0000f, 3.2592f, 0.3350f, 1.0000f, + 60.2574f, -34.0099f, 36.2677f, 60.4626f, -34.1751f, 39.4387f, 1.2644f, + 63.0109f, -31.0961f, -5.8663f, 62.8187f, -29.7946f, -4.0864f, 1.2630f, + 61.2901f, 3.7196f, -5.3901f, 61.4292f, 2.2480f, -4.9620f, 1.8731f, + 35.0831f, -44.1164f, 3.7933f, 35.0232f, -40.0716f, 1.5901f, 1.8645f, + 22.7233f, 20.0904f, -46.6940f, 23.0331f, 14.9730f, -42.5619f, 2.0373f, + 36.4612f, 47.8580f, 18.3852f, 36.2715f, 50.5065f, 21.2231f, 1.4146f, + 90.8027f, -2.0831f, 1.4410f, 91.1528f, -1.6435f, 0.0447f, 1.4441f, + 90.9257f, -0.5406f, -0.9208f, 88.6381f, -0.8985f, -0.7239f, 1.5381f, + 6.7747f, -0.2908f, -2.4247f, 5.8714f, -0.0985f, -2.2286f, 0.6377f, + 2.0776f, 0.0795f, -1.1350f, 0.9033f, -0.0636f, -0.5514f, 0.9082f, +}; + +LOLUNIT_FIXTURE(ColorTest) +{ + void SetUp() {} + + void TearDown() {} + + LOLUNIT_TEST(CIEDE2000) + { + size_t count = sizeof(ciede2k) / sizeof(*ciede2k); + + for (size_t n = 0; n < count; n += 7) + { + vec3 a(ciede2k[n + 0], ciede2k[n + 1], ciede2k[n + 2]); + vec3 b(ciede2k[n + 3], ciede2k[n + 4], ciede2k[n + 5]); + float d1 = ciede2k[n + 6]; + + float d2 = Color::DistanceCIEDE2000(a, b); + + LOLUNIT_ASSERT_DOUBLES_EQUAL(d1, d2, 0.0001); + } + } +}; + +} /* namespace lol */ +