diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..fa4369e --- /dev/null +++ b/.gitignore @@ -0,0 +1,7 @@ +*.dll +*.so +*.dylib +*.o +*.dSYM +testCIEDE2000 + diff --git a/CIEDE2000.cpp b/CIEDE2000.cpp new file mode 100644 index 0000000..6a43952 --- /dev/null +++ b/CIEDE2000.cpp @@ -0,0 +1,172 @@ +/* + * CIEDE2000.cpp + * Part of http://github.com/gfiumara/CIEDE2000 by Gregory Fiumara. + * See LICENSE for details. + */ + +#include + +#include + +/******************************************************************************* + * Conversions. + ******************************************************************************/ + +constexpr double +CIEDE2000::deg2Rad( + const double deg) +{ + return (deg * (M_PI / 180.0)); +} + +constexpr double +CIEDE2000::rad2Deg( + const double rad) +{ + return ((180.0 / M_PI) * rad); +} + +double +CIEDE2000::CIEDE2000( + const LAB &lab1, + const LAB &lab2) +{ + /* + * "For these and all other numerical/graphical 􏰀delta E00 values + * reported in this article, we set the parametric weighting factors + * to unity(i.e., k_L = k_C = k_H = 1.0)." (Page 27). + */ + const double k_L = 1.0, k_C = 1.0, k_H = 1.0; + const double deg360InRad = CIEDE2000::deg2Rad(360.0); + const double deg180InRad = CIEDE2000::deg2Rad(180.0); + const double pow25To7 = 6103515625.0; /* pow(25, 7) */ + + /* + * Step 1 + */ + /* Equation 2 */ + double C1 = sqrt((lab1.a * lab1.a) + (lab1.b * lab1.b)); + double C2 = sqrt((lab2.a * lab2.a) + (lab2.b * lab2.b)); + /* Equation 3 */ + double barC = (C1 + C2) / 2.0; + /* Equation 4 */ + double G = 0.5 * (1 - sqrt(pow(barC, 7) / (pow(barC, 7) + pow25To7))); + /* Equation 5 */ + double a1Prime = (1.0 + G) * lab1.a; + double a2Prime = (1.0 + G) * lab2.a; + /* Equation 6 */ + double CPrime1 = sqrt((a1Prime * a1Prime) + (lab1.b * lab1.b)); + double CPrime2 = sqrt((a2Prime * a2Prime) + (lab2.b * lab2.b)); + /* Equation 7 */ + double hPrime1; + if (lab1.b == 0 && a1Prime == 0) + hPrime1 = 0.0; + else + hPrime1 = atan2(lab1.b, a1Prime); + /* + * This must be converted to a hue angle in degrees between 0 + * and 360 by addition of 2􏰏 to negative hue angles. + */ + if (hPrime1 < 0) + hPrime1 += deg360InRad; + double hPrime2; + if (lab2.b == 0 && a2Prime == 0) + hPrime2 = 0.0; + else + hPrime2 = atan2(lab2.b, a2Prime); + /* + * This must be converted to a hue angle in degrees between 0 + * and 360 by addition of 2􏰏 to negative hue angles. + */ + if (hPrime2 < 0) + hPrime2 += deg360InRad; + + /* + * Step 2 + */ + /* Equation 8 */ + double deltaLPrime = lab2.l - lab1.l; + /* Equation 9 */ + double deltaCPrime = CPrime2 - CPrime1; + /* Equation 10 */ + double deltahPrime; + double CPrimeProduct = CPrime1 * CPrime2; + if (CPrimeProduct == 0) + deltahPrime = 0; + else { + /* Avoid the fabs() call */ + deltahPrime = hPrime2 - hPrime1; + if (deltahPrime < -deg180InRad) + deltahPrime += deg360InRad; + else if (deltahPrime > deg180InRad) + deltahPrime -= deg360InRad; + } + /* Equation 11 */ + double deltaHPrime = 2.0 * sqrt(CPrimeProduct) * + sin(deltahPrime / 2.0); + + /* + * Step 3 + */ + /* Equation 12 */ + double barLPrime = (lab1.l + lab2.l) / 2.0; + /* Equation 13 */ + double barCPrime = (CPrime1 + CPrime2) / 2.0; + /* Equation 14 */ + double barhPrime, hPrimeSum = hPrime1 + hPrime2; + if (CPrime1 * CPrime2 == 0) { + barhPrime = hPrimeSum; + } else { + if (fabs(hPrime1 - hPrime2) <= deg180InRad) + barhPrime = hPrimeSum / 2.0; + else { + if (hPrimeSum < deg360InRad) + barhPrime = (hPrimeSum + deg360InRad) / 2.0; + else + barhPrime = (hPrimeSum - deg360InRad) / 2.0; + } + } + /* Equation 15 */ + double T = 1.0 - (0.17 * cos(barhPrime - CIEDE2000::deg2Rad(30.0))) + + (0.24 * cos(2.0 * barhPrime)) + + (0.32 * cos((3.0 * barhPrime) + CIEDE2000::deg2Rad(6.0))) - + (0.20 * cos((4.0 * barhPrime) - CIEDE2000::deg2Rad(63.0))); + /* Equation 16 */ + double deltaTheta = CIEDE2000::deg2Rad(30.0) * + exp(-pow((barhPrime - deg2Rad(275.0)) / deg2Rad(25.0), 2.0)); + /* Equation 17 */ + double R_C = 2.0 * sqrt(pow(barCPrime, 7.0) / + (pow(barCPrime, 7.0) + pow25To7)); + /* Equation 18 */ + double S_L = 1 + ((0.015 * pow(barLPrime - 50.0, 2.0)) / + sqrt(20 + pow(barLPrime - 50.0, 2.0))); + /* Equation 19 */ + double S_C = 1 + (0.045 * barCPrime); + /* Equation 20 */ + double S_H = 1 + (0.015 * barCPrime * T); + /* Equation 21 */ + double R_T = (-sin(2.0 * deltaTheta)) * R_C; + + /* Equation 22 */ + double deltaE = sqrt( + pow(deltaLPrime / (k_L * S_L), 2.0) + + pow(deltaCPrime / (k_C * S_C), 2.0) + + pow(deltaHPrime / (k_H * S_H), 2.0) + + (R_T * (deltaCPrime / (k_C * S_C)) * (deltaHPrime / (k_H * S_H)))); + + return (deltaE); +} + +/******************************************************************************* + * Operators. + ******************************************************************************/ + +std::ostream& +operator<<( + std::ostream &s, + const CIEDE2000::LAB &labColor) +{ + return (s << "CIELAB(" << labColor.l << "," << labColor.a << "," << + labColor.b << ")"); +} + diff --git a/CIEDE2000.h b/CIEDE2000.h new file mode 100644 index 0000000..2a43bde --- /dev/null +++ b/CIEDE2000.h @@ -0,0 +1,117 @@ +/* + * CIEDE2000.h + * Part of http://github.com/gfiumara/CIEDE2000 by Gregory Fiumara. + * See LICENSE for details. + */ + +#ifndef GPF_CIEDE2000_H_ +#define GPF_CIEDE2000_H_ + +#include + +#ifndef M_PI +#define M_PI 3.14159265358979323846264338327950288 /* pi */ +#endif + +/** Namespace containing all necessary objects and methods for CIEDE2000 */ +namespace CIEDE2000 +{ + /*********************************************************************** + * Types. + **********************************************************************/ + + /** A color in CIELAB colorspace */ + struct LAB + { + /** Lightness */ + double l; + /** Color-opponent a dimension */ + double a; + /** Color-opponent b dimension */ + double b; + }; + /** Convenience definition for struct LAB */ + using LAB = struct LAB; + + /*********************************************************************** + * Operations. + **********************************************************************/ + + /** + * @brief + * Obtain Delta-E 2000 value. + * @details + * Based on the paper "The CIEDE2000 Color-Difference Formula: + * Implementation Notes, Supplementary Test Data, and Mathematical + * Observations" by Gaurav Sharma, Wencheng Wu, and Edul N. Dalal, + * from http://www.ece.rochester.edu/~gsharma/ciede2000/. + * + * @param lab1 + * First color in LAB colorspace. + * @param lab2 + * Second color in LAB colorspace. + * + * @return + * Delta-E difference between lab1 and lab2. + */ + double + CIEDE2000( + const LAB &lab1, + const LAB &lab2); + + /*********************************************************************** + * Conversions. + **********************************************************************/ + + /** + * @brief + * Convert degrees to radians. + * + * @param deg + * Angle in degrees. + * + * @return + * deg in radians. + */ + constexpr double + deg2Rad( + const double deg); + + /** + * @brief + * Convert radians to degrees. + * + * @param rad + * Angle in radians. + * + * @return + * rad in degrees. + */ + constexpr double + rad2Deg( + const double rad); +} + +/******************************************************************************* + * Conversions. + ******************************************************************************/ + +/** + * @brief + * LAB output stream operator. + * + * @param s + * Output stream. + * @param labColor + * Color to output. + * + * @return + * s with labColor appended. + */ +std::ostream& +operator<<( + std::ostream &s, + const CIEDE2000::LAB &labColor); + +#endif /* GPF_CIEDE2000_H_ */ + diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..74c1ab7 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2015 Greg Fiumara + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..fcc5325 --- /dev/null +++ b/Makefile @@ -0,0 +1,53 @@ +# +# Makefile +# Part of http://github.com/gfiumara/CIEDE2000 by Gregory Fiumara. +# See LICENSE for details. +# + +.PHONY: all clean debug library test + +LIBRARY = CIEDE2000 +SOURCE = $(LIBRARY).cpp +OBJECT = $(SOURCE:%.cpp=%.o) +DISPOSABLE_FILES = lib$(LIBRARY).so lib$(LIBRARY).dylib $(LIBRARY).dll \ + $(OBJECT) test$(OBJECT) test$(LIBRARY) +DISPOSABLE_DIRS = *dSYM +OS = $(shell uname) + +ifneq ($(findstring MINGW,$(OS)),MINGW) +WARNING_FLAGS = -Wall -Wextra -pedantic +PRODUCTION_FLAGS = -O3 +endif +DEBUG_FLAGS = -g +TEST_FLAGS = -Wno-unused-parameter + +CXXFLAGS += -I. -std=c++11 $(WARNING_FLAGS) $(PRODUCTION_FLAGS) + +all: library + +debug: CXXFLAGS += $(DEBUG_FLAGS) +debug: CXXFLAGS := $(filter-out $(PRODUCTION_FLAGS),$(CXXFLAGS)) +debug: all + +test$(LIBRARY): LDFLAGS += -L. -lCIEDE2000 +test$(LIBRARY): CXXFLAGS += $(TEST_FLAGS) +test$(LIBRARY): test$(OBJECT) + $(CXX) $^ -o $@ $(LDFLAGS) + +test: library test$(LIBRARY) + +library: $(LIBRARY).cpp +ifeq ($(findstring Darwin,$(OS)),Darwin) + $(CXX) -dynamiclib $(CXXFLAGS) $^ -o lib$(LIBRARY).dylib +else +ifeq ($(findstring MINGW,$(OS)),MINGW) + $(CXX) -shared $(CXXFLAGS) $^ -o lib$(LIBRARY).dll +else + $(CXX) -shared $(CXXFLAGS) $^ -o lib$(LIBRARY).so +endif +endif + +clean: + $(RM) $(DISPOSABLE_FILES) + $(RM) -r $(DISPOSABLE_DIRS) + diff --git a/README.md b/README.md new file mode 100644 index 0000000..b809ebf --- /dev/null +++ b/README.md @@ -0,0 +1,71 @@ +CIE Delta E 2000 (CIEDE2000) +---------------------------- + +A C++ library containing a function to determine how similar two colors are. +The code here is original, based on the psuedocode written in the paper +[The CIEDE2000 Color-Difference Formula: Implementation Notes, Supplementary +Test Data, and Mathematical Observations](http://www.ece.rochester.edu/ +~gsharma/ciede2000/), by Gaurav Sharma, Wencheng Wu, and Edul N. Dalal. While +this is written in C++ so that I could place it in a namespace, there's nothing +C++-specific about the implementation, and so it would be trivial to convert +to C if required. The conversion is left as an exercise to the reader. + +**To Build** +`make` + +**To Test** +`make test && ./testCIEDE2000` (Expected output below.) + +As I implemented the method from the paper, I commented the code to line up +code fragements with lines from the paper for tracability. There are ways to +speed up and optimize this algorithm, which have not been attempted. + +A test program (`testCIEDE2000`) is built by default to run the +[tests](http://www.ece.rochester.edu/~gsharma/ciede2000/dataNprograms/ +CIEDE2000.xls) perscribed by the algorithm authors. Tested on OS X 10.10.2, +and Ubuntu 14.04 LTS. Should work on Cygwin/MINGW, though there will likely +need to be edits to the [Makefile](Makefile). + +**NOTE** +The `CIEDE2000` algorithm uses colors from the +[LAB colorspace](http://en.wikipedia.org/wiki/Lab_color_space), not RGB. In +practice, I've used [ImageMagick](http://www.imagemagick.org) to convert +color values for use with CIEDE2000, though that not may be entirely correct. + +**Expected Testing Output** + + $ ./testCIEDE2000 + 1: 2.0425 vs 2.0425 PASS + 2: 2.8615 vs 2.8615 PASS + 3: 3.4412 vs 3.4412 PASS + 4: 1.0000 vs 1.0000 PASS + 5: 1.0000 vs 1.0000 PASS + 6: 1.0000 vs 1.0000 PASS + 7: 2.3669 vs 2.3669 PASS + 8: 2.3669 vs 2.3669 PASS + 9: 7.1792 vs 7.1792 PASS + 10: 7.1792 vs 7.1792 PASS + 11: 7.2195 vs 7.2195 PASS + 12: 7.2195 vs 7.2195 PASS + 13: 4.8045 vs 4.8045 PASS + 14: 4.8045 vs 4.8045 PASS + 15: 4.7461 vs 4.7461 PASS + 16: 4.3065 vs 4.3065 PASS + 17: 27.1492 vs 27.1492 PASS + 18: 22.8977 vs 22.8977 PASS + 19: 31.9030 vs 31.9030 PASS + 20: 19.4535 vs 19.4535 PASS + 21: 1.0000 vs 1.0000 PASS + 22: 1.0000 vs 1.0000 PASS + 23: 1.0000 vs 1.0000 PASS + 24: 1.0000 vs 1.0000 PASS + 25: 1.2644 vs 1.2644 PASS + 26: 1.2630 vs 1.2630 PASS + 27: 1.8731 vs 1.8731 PASS + 28: 1.8645 vs 1.8645 PASS + 29: 2.0373 vs 2.0373 PASS + 30: 1.4146 vs 1.4146 PASS + 31: 1.4441 vs 1.4441 PASS + 32: 1.5381 vs 1.5381 PASS + 33: 0.6377 vs 0.6377 PASS + 34: 0.9082 vs 0.9082 PASS diff --git a/testCIEDE2000.cpp b/testCIEDE2000.cpp new file mode 100644 index 0000000..6c6e673 --- /dev/null +++ b/testCIEDE2000.cpp @@ -0,0 +1,563 @@ +/* + * testCIEDE2000.cpp + * Part of http://github.com/gfiumara/CIEDE2000 by Gregory Fiumara. + * See LICENSE for details. + */ + +#include +#include +#include + +#include + +/** + * @brief + * Run the test dataset run from the CIEDE2000 paper. + * + * @return + * EXIT_SUCCESS if all tests pass, EXIT_FAILURE otherwise. + */ +int +testCIEDE2000(); + +int +main( + int argc, + char *argv[]) +{ + return (testCIEDE2000()); +} + +int +testCIEDE2000() +{ + CIEDE2000::LAB lab1, lab2; + double expectedResult, myResult; + unsigned int line = 1; + char myBuffer[64], expectedBuffer[64]; + std::string myStr, expectedStr; + std::vector passFail; + + lab1 = {50.0000, 2.6772, -79.7751}; + lab2 = {50.0000, 0.0000, -82.7485}; + expectedResult = 2.0425; + sprintf(expectedBuffer, "%.4f", expectedResult); + expectedStr = expectedBuffer; + myResult = CIEDE2000::CIEDE2000(lab1, lab2); + sprintf(myBuffer, "%.4f", myResult); + myStr = myBuffer; + std::cout << line << ": "; + std::cout << myStr << " vs " << expectedStr << '\t'; + passFail.push_back(myStr == expectedStr); + std::cout << (passFail[line - 1] ? "PASS" : "FAIL") << ' '; + std::cout << std::endl; + line++; + + lab1 = {50.0000, 3.1571, -77.2803}; + lab2 = {50.0000, 0.0000, -82.7485}; + expectedResult = 2.8615; + sprintf(expectedBuffer, "%.4f", expectedResult); + expectedStr = expectedBuffer; + myResult = CIEDE2000::CIEDE2000(lab1, lab2); + sprintf(myBuffer, "%.4f", myResult); + myStr = myBuffer; + std::cout << line << ": "; + std::cout << myStr << " vs " << expectedStr << '\t'; + passFail.push_back(myStr == expectedStr); + std::cout << (passFail[line - 1] ? "PASS" : "FAIL") << ' '; + std::cout << std::endl; + line++; + + lab1 = {50.0000, 2.8361, -74.0200}; + lab2 = {50.0000, 0.0000, -82.7485}; + expectedResult = 3.4412; + sprintf(expectedBuffer, "%.4f", expectedResult); + expectedStr = expectedBuffer; + myResult = CIEDE2000::CIEDE2000(lab1, lab2); + sprintf(myBuffer, "%.4f", myResult); + myStr = myBuffer; + std::cout << line << ": "; + std::cout << myStr << " vs " << expectedStr << '\t'; + passFail.push_back(myStr == expectedStr); + std::cout << (passFail[line - 1] ? "PASS" : "FAIL") << ' '; + std::cout << std::endl; + line++; + + lab1 = {50.0000, -1.3802, -84.2814}; + lab2 = {50.0000, 0.0000, -82.7485}; + expectedResult = 1.0000; + sprintf(expectedBuffer, "%.4f", expectedResult); + expectedStr = expectedBuffer; + myResult = CIEDE2000::CIEDE2000(lab1, lab2); + sprintf(myBuffer, "%.4f", myResult); + myStr = myBuffer; + std::cout << line << ": "; + std::cout << myStr << " vs " << expectedStr << '\t'; + passFail.push_back(myStr == expectedStr); + std::cout << (passFail[line - 1] ? "PASS" : "FAIL") << ' '; + std::cout << std::endl; + line++; + + lab1 = {50.0000, -1.1848, -84.8006}; + lab2 = {50.0000, 0.0000, -82.7485}; + expectedResult = 1.0000; + sprintf(expectedBuffer, "%.4f", expectedResult); + expectedStr = expectedBuffer; + myResult = CIEDE2000::CIEDE2000(lab1, lab2); + sprintf(myBuffer, "%.4f", myResult); + myStr = myBuffer; + std::cout << line << ": "; + std::cout << myStr << " vs " << expectedStr << '\t'; + passFail.push_back(myStr == expectedStr); + std::cout << (passFail[line - 1] ? "PASS" : "FAIL") << ' '; + std::cout << std::endl; + line++; + + lab1 = {50.0000, -0.9009, -85.5211}; + lab2 = {50.0000, 0.0000, -82.7485}; + expectedResult = 1.0000; + sprintf(expectedBuffer, "%.4f", expectedResult); + expectedStr = expectedBuffer; + myResult = CIEDE2000::CIEDE2000(lab1, lab2); + sprintf(myBuffer, "%.4f", myResult); + myStr = myBuffer; + std::cout << line << ": "; + std::cout << myStr << " vs " << expectedStr << '\t'; + passFail.push_back(myStr == expectedStr); + std::cout << (passFail[line - 1] ? "PASS" : "FAIL") << ' '; + std::cout << std::endl; + line++; + + lab1 = {50.0000, 0.0000, 0.0000}; + lab2 = {50.0000, -1.0000, 2.0000}; + expectedResult = 2.3669; + sprintf(expectedBuffer, "%.4f", expectedResult); + expectedStr = expectedBuffer; + myResult = CIEDE2000::CIEDE2000(lab1, lab2); + sprintf(myBuffer, "%.4f", myResult); + myStr = myBuffer; + std::cout << line << ": "; + std::cout << myStr << " vs " << expectedStr << '\t'; + passFail.push_back(myStr == expectedStr); + std::cout << (passFail[line - 1] ? "PASS" : "FAIL") << ' '; + std::cout << std::endl; + line++; + + lab1 = {50.0000, -1.0000, 2.0000}; + lab2 = {50.0000, 0.0000, 0.0000}; + expectedResult = 2.3669; + sprintf(expectedBuffer, "%.4f", expectedResult); + expectedStr = expectedBuffer; + myResult = CIEDE2000::CIEDE2000(lab1, lab2); + sprintf(myBuffer, "%.4f", myResult); + myStr = myBuffer; + std::cout << line << ": "; + std::cout << myStr << " vs " << expectedStr << '\t'; + passFail.push_back(myStr == expectedStr); + std::cout << (passFail[line - 1] ? "PASS" : "FAIL") << ' '; + std::cout << std::endl; + line++; + + lab1 = {50.0000, 2.4900, -0.0010}; + lab2 = {50.0000, -2.4900, 0.0009}; + expectedResult = 7.1792; + sprintf(expectedBuffer, "%.4f", expectedResult); + expectedStr = expectedBuffer; + myResult = CIEDE2000::CIEDE2000(lab1, lab2); + sprintf(myBuffer, "%.4f", myResult); + myStr = myBuffer; + std::cout << line << ": "; + std::cout << myStr << " vs " << expectedStr << '\t'; + passFail.push_back(myStr == expectedStr); + std::cout << (passFail[line - 1] ? "PASS" : "FAIL") << ' '; + std::cout << std::endl; + line++; + + lab1 = {50.0000, 2.4900, -0.0010}; + lab2 = {50.0000, -2.4900, 0.0010}; + expectedResult = 7.1792; + sprintf(expectedBuffer, "%.4f", expectedResult); + expectedStr = expectedBuffer; + myResult = CIEDE2000::CIEDE2000(lab1, lab2); + sprintf(myBuffer, "%.4f", myResult); + myStr = myBuffer; + std::cout << line << ": "; + std::cout << myStr << " vs " << expectedStr << '\t'; + passFail.push_back(myStr == expectedStr); + std::cout << (passFail[line - 1] ? "PASS" : "FAIL") << ' '; + std::cout << std::endl; + line++; + + lab1 = {50.0000, 2.4900, -0.0010}; + lab2 = {50.0000, -2.4900, 0.0011}; + expectedResult = 7.2195; + sprintf(expectedBuffer, "%.4f", expectedResult); + expectedStr = expectedBuffer; + myResult = CIEDE2000::CIEDE2000(lab1, lab2); + sprintf(myBuffer, "%.4f", myResult); + myStr = myBuffer; + std::cout << line << ": "; + std::cout << myStr << " vs " << expectedStr << '\t'; + passFail.push_back(myStr == expectedStr); + std::cout << (passFail[line - 1] ? "PASS" : "FAIL") << ' '; + std::cout << std::endl; + line++; + + lab1 = {50.0000, 2.4900, -0.0010}; + lab2 = {50.0000, -2.4900, 0.0012}; + expectedResult = 7.2195; + sprintf(expectedBuffer, "%.4f", expectedResult); + expectedStr = expectedBuffer; + myResult = CIEDE2000::CIEDE2000(lab1, lab2); + sprintf(myBuffer, "%.4f", myResult); + myStr = myBuffer; + std::cout << line << ": "; + std::cout << myStr << " vs " << expectedStr << '\t'; + passFail.push_back(myStr == expectedStr); + std::cout << (passFail[line - 1] ? "PASS" : "FAIL") << ' '; + std::cout << std::endl; + line++; + + lab1 = {50.0000, -0.0010, 2.4900}; + lab2 = {50.0000, 0.0009, -2.4900}; + expectedResult = 4.8045; + sprintf(expectedBuffer, "%.4f", expectedResult); + expectedStr = expectedBuffer; + myResult = CIEDE2000::CIEDE2000(lab1, lab2); + sprintf(myBuffer, "%.4f", myResult); + myStr = myBuffer; + std::cout << line << ": "; + std::cout << myStr << " vs " << expectedStr << '\t'; + passFail.push_back(myStr == expectedStr); + std::cout << (passFail[line - 1] ? "PASS" : "FAIL") << ' '; + std::cout << std::endl; + line++; + + lab1 = {50.0000, -0.0010, 2.4900}; + lab2 = {50.0000, 0.0010, -2.4900}; + expectedResult = 4.8045; + sprintf(expectedBuffer, "%.4f", expectedResult); + expectedStr = expectedBuffer; + myResult = CIEDE2000::CIEDE2000(lab1, lab2); + sprintf(myBuffer, "%.4f", myResult); + myStr = myBuffer; + std::cout << line << ": "; + std::cout << myStr << " vs " << expectedStr << '\t'; + passFail.push_back(myStr == expectedStr); + std::cout << (passFail[line - 1] ? "PASS" : "FAIL") << ' '; + std::cout << std::endl; + line++; + + lab1 = {50.0000, -0.0010, 2.4900}; + lab2 = {50.0000, 0.0011, -2.4900}; + expectedResult = 4.7461; + sprintf(expectedBuffer, "%.4f", expectedResult); + expectedStr = expectedBuffer; + myResult = CIEDE2000::CIEDE2000(lab1, lab2); + sprintf(myBuffer, "%.4f", myResult); + myStr = myBuffer; + std::cout << line << ": "; + std::cout << myStr << " vs " << expectedStr << '\t'; + passFail.push_back(myStr == expectedStr); + std::cout << (passFail[line - 1] ? "PASS" : "FAIL") << ' '; + std::cout << std::endl; + line++; + + lab1 = {50.0000, 2.5000, 0.0000}; + lab2 = {50.0000, 0.0000, -2.5000}; + expectedResult = 4.3065; + sprintf(expectedBuffer, "%.4f", expectedResult); + expectedStr = expectedBuffer; + myResult = CIEDE2000::CIEDE2000(lab1, lab2); + sprintf(myBuffer, "%.4f", myResult); + myStr = myBuffer; + std::cout << line << ": "; + std::cout << myStr << " vs " << expectedStr << '\t'; + passFail.push_back(myStr == expectedStr); + std::cout << (passFail[line - 1] ? "PASS" : "FAIL") << ' '; + std::cout << std::endl; + line++; + + lab1 = {50.0000, 2.5000, 0.0000}; + lab2 = {73.0000, 25.0000, -18.0000}; + expectedResult = 27.1492; + sprintf(expectedBuffer, "%.4f", expectedResult); + expectedStr = expectedBuffer; + myResult = CIEDE2000::CIEDE2000(lab1, lab2); + sprintf(myBuffer, "%.4f", myResult); + myStr = myBuffer; + std::cout << line << ": "; + std::cout << myStr << " vs " << expectedStr << '\t'; + passFail.push_back(myStr == expectedStr); + std::cout << (passFail[line - 1] ? "PASS" : "FAIL") << ' '; + std::cout << std::endl; + line++; + + lab1 = {50.0000, 2.5000, 0.0000}; + lab2 = {61.0000, -5.0000, 29.0000}; + expectedResult = 22.8977; + sprintf(expectedBuffer, "%.4f", expectedResult); + expectedStr = expectedBuffer; + myResult = CIEDE2000::CIEDE2000(lab1, lab2); + sprintf(myBuffer, "%.4f", myResult); + myStr = myBuffer; + std::cout << line << ": "; + std::cout << myStr << " vs " << expectedStr << '\t'; + passFail.push_back(myStr == expectedStr); + std::cout << (passFail[line - 1] ? "PASS" : "FAIL") << ' '; + std::cout << std::endl; + line++; + + lab1 = {50.0000, 2.5000, 0.0000}; + lab2 = {56.0000, -27.0000, -3.0000}; + expectedResult = 31.9030; + sprintf(expectedBuffer, "%.4f", expectedResult); + expectedStr = expectedBuffer; + myResult = CIEDE2000::CIEDE2000(lab1, lab2); + sprintf(myBuffer, "%.4f", myResult); + myStr = myBuffer; + std::cout << line << ": "; + std::cout << myStr << " vs " << expectedStr << '\t'; + passFail.push_back(myStr == expectedStr); + std::cout << (passFail[line - 1] ? "PASS" : "FAIL") << ' '; + std::cout << std::endl; + line++; + + lab1 = {50.0000, 2.5000, 0.0000}; + lab2 = {58.0000, 24.0000, 15.0000}; + expectedResult = 19.4535; + sprintf(expectedBuffer, "%.4f", expectedResult); + expectedStr = expectedBuffer; + myResult = CIEDE2000::CIEDE2000(lab1, lab2); + sprintf(myBuffer, "%.4f", myResult); + myStr = myBuffer; + std::cout << line << ": "; + std::cout << myStr << " vs " << expectedStr << '\t'; + passFail.push_back(myStr == expectedStr); + std::cout << (passFail[line - 1] ? "PASS" : "FAIL") << ' '; + std::cout << std::endl; + line++; + + lab1 = {50.0000, 2.5000, 0.0000}; + lab2 = {50.0000, 3.1736, 0.5854}; + expectedResult = 1.0000; + sprintf(expectedBuffer, "%.4f", expectedResult); + expectedStr = expectedBuffer; + myResult = CIEDE2000::CIEDE2000(lab1, lab2); + sprintf(myBuffer, "%.4f", myResult); + myStr = myBuffer; + std::cout << line << ": "; + std::cout << myStr << " vs " << expectedStr << '\t'; + passFail.push_back(myStr == expectedStr); + std::cout << (passFail[line - 1] ? "PASS" : "FAIL") << ' '; + std::cout << std::endl; + line++; + + lab1 = {50.0000, 2.5000, 0.0000}; + lab2 = {50.0000, 3.2972, 0.0000}; + expectedResult = 1.0000; + sprintf(expectedBuffer, "%.4f", expectedResult); + expectedStr = expectedBuffer; + myResult = CIEDE2000::CIEDE2000(lab1, lab2); + sprintf(myBuffer, "%.4f", myResult); + myStr = myBuffer; + std::cout << line << ": "; + std::cout << myStr << " vs " << expectedStr << '\t'; + passFail.push_back(myStr == expectedStr); + std::cout << (passFail[line - 1] ? "PASS" : "FAIL") << ' '; + std::cout << std::endl; + line++; + + lab1 = {50.0000, 2.5000, 0.0000}; + lab2 = {50.0000, 1.8634, 0.5757}; + expectedResult = 1.0000; + sprintf(expectedBuffer, "%.4f", expectedResult); + expectedStr = expectedBuffer; + myResult = CIEDE2000::CIEDE2000(lab1, lab2); + sprintf(myBuffer, "%.4f", myResult); + myStr = myBuffer; + std::cout << line << ": "; + std::cout << myStr << " vs " << expectedStr << '\t'; + passFail.push_back(myStr == expectedStr); + std::cout << (passFail[line - 1] ? "PASS" : "FAIL") << ' '; + std::cout << std::endl; + line++; + + lab1 = {50.0000, 2.5000, 0.0000}; + lab2 = {50.0000, 3.2592, 0.3350}; + expectedResult = 1.0000; + sprintf(expectedBuffer, "%.4f", expectedResult); + expectedStr = expectedBuffer; + myResult = CIEDE2000::CIEDE2000(lab1, lab2); + sprintf(myBuffer, "%.4f", myResult); + myStr = myBuffer; + std::cout << line << ": "; + std::cout << myStr << " vs " << expectedStr << '\t'; + passFail.push_back(myStr == expectedStr); + std::cout << (passFail[line - 1] ? "PASS" : "FAIL") << ' '; + std::cout << std::endl; + line++; + + lab1 = {60.2574, -34.0099, 36.2677}; + lab2 = {60.4626, -34.1751, 39.4387}; + expectedResult = 1.2644; + sprintf(expectedBuffer, "%.4f", expectedResult); + expectedStr = expectedBuffer; + myResult = CIEDE2000::CIEDE2000(lab1, lab2); + sprintf(myBuffer, "%.4f", myResult); + myStr = myBuffer; + std::cout << line << ": "; + std::cout << myStr << " vs " << expectedStr << '\t'; + passFail.push_back(myStr == expectedStr); + std::cout << (passFail[line - 1] ? "PASS" : "FAIL") << ' '; + std::cout << std::endl; + line++; + + lab1 = {63.0109, -31.0961, -5.8663}; + lab2 = {62.8187, -29.7946, -4.0864}; + expectedResult = 1.2630; + sprintf(expectedBuffer, "%.4f", expectedResult); + expectedStr = expectedBuffer; + myResult = CIEDE2000::CIEDE2000(lab1, lab2); + sprintf(myBuffer, "%.4f", myResult); + myStr = myBuffer; + std::cout << line << ": "; + std::cout << myStr << " vs " << expectedStr << '\t'; + passFail.push_back(myStr == expectedStr); + std::cout << (passFail[line - 1] ? "PASS" : "FAIL") << ' '; + std::cout << std::endl; + line++; + + lab1 = {61.2901, 3.7196, -5.3901}; + lab2 = {61.4292, 2.2480, -4.9620}; + expectedResult = 1.8731; + sprintf(expectedBuffer, "%.4f", expectedResult); + expectedStr = expectedBuffer; + myResult = CIEDE2000::CIEDE2000(lab1, lab2); + sprintf(myBuffer, "%.4f", myResult); + myStr = myBuffer; + std::cout << line << ": "; + std::cout << myStr << " vs " << expectedStr << '\t'; + passFail.push_back(myStr == expectedStr); + std::cout << (passFail[line - 1] ? "PASS" : "FAIL") << ' '; + std::cout << std::endl; + line++; + + lab1 = {35.0831, -44.1164, 3.7933}; + lab2 = {35.0232, -40.0716, 1.5901}; + expectedResult = 1.8645; + sprintf(expectedBuffer, "%.4f", expectedResult); + expectedStr = expectedBuffer; + myResult = CIEDE2000::CIEDE2000(lab1, lab2); + sprintf(myBuffer, "%.4f", myResult); + myStr = myBuffer; + std::cout << line << ": "; + std::cout << myStr << " vs " << expectedStr << '\t'; + passFail.push_back(myStr == expectedStr); + std::cout << (passFail[line - 1] ? "PASS" : "FAIL") << ' '; + std::cout << std::endl; + line++; + + lab1 = {22.7233, 20.0904, -46.6940}; + lab2 = {23.0331, 14.9730, -42.5619}; + expectedResult = 2.0373; + sprintf(expectedBuffer, "%.4f", expectedResult); + expectedStr = expectedBuffer; + myResult = CIEDE2000::CIEDE2000(lab1, lab2); + sprintf(myBuffer, "%.4f", myResult); + myStr = myBuffer; + std::cout << line << ": "; + std::cout << myStr << " vs " << expectedStr << '\t'; + passFail.push_back(myStr == expectedStr); + std::cout << (passFail[line - 1] ? "PASS" : "FAIL") << ' '; + std::cout << std::endl; + line++; + + lab1 = {36.4612, 47.8580, 18.3852}; + lab2 = {36.2715, 50.5065, 21.2231}; + expectedResult = 1.4146; + sprintf(expectedBuffer, "%.4f", expectedResult); + expectedStr = expectedBuffer; + myResult = CIEDE2000::CIEDE2000(lab1, lab2); + sprintf(myBuffer, "%.4f", myResult); + myStr = myBuffer; + std::cout << line << ": "; + std::cout << myStr << " vs " << expectedStr << '\t'; + passFail.push_back(myStr == expectedStr); + std::cout << (passFail[line - 1] ? "PASS" : "FAIL") << ' '; + std::cout << std::endl; + line++; + + lab1 = {90.8027, -2.0831, 1.4410}; + lab2 = {91.1528, -1.6435, 0.0447}; + expectedResult = 1.4441; + sprintf(expectedBuffer, "%.4f", expectedResult); + expectedStr = expectedBuffer; + myResult = CIEDE2000::CIEDE2000(lab1, lab2); + sprintf(myBuffer, "%.4f", myResult); + myStr = myBuffer; + std::cout << line << ": "; + std::cout << myStr << " vs " << expectedStr << '\t'; + passFail.push_back(myStr == expectedStr); + std::cout << (passFail[line - 1] ? "PASS" : "FAIL") << ' '; + std::cout << std::endl; + line++; + + lab1 = {90.9257, -0.5406, -0.9208}; + lab2 = {88.6381, -0.8985, -0.7239}; + expectedResult = 1.5381; + sprintf(expectedBuffer, "%.4f", expectedResult); + expectedStr = expectedBuffer; + myResult = CIEDE2000::CIEDE2000(lab1, lab2); + sprintf(myBuffer, "%.4f", myResult); + myStr = myBuffer; + std::cout << line << ": "; + std::cout << myStr << " vs " << expectedStr << '\t'; + passFail.push_back(myStr == expectedStr); + std::cout << (passFail[line - 1] ? "PASS" : "FAIL") << ' '; + std::cout << std::endl; + line++; + + lab1 = {6.7747, -0.2908, -2.4247}; + lab2 = {5.8714, -0.0985, -2.2286}; + expectedResult = 0.6377; + sprintf(expectedBuffer, "%.4f", expectedResult); + expectedStr = expectedBuffer; + myResult = CIEDE2000::CIEDE2000(lab1, lab2); + sprintf(myBuffer, "%.4f", myResult); + myStr = myBuffer; + std::cout << line << ": "; + std::cout << myStr << " vs " << expectedStr << '\t'; + passFail.push_back(myStr == expectedStr); + std::cout << (passFail[line - 1] ? "PASS" : "FAIL") << ' '; + std::cout << std::endl; + line++; + + lab1 = {2.0776, 0.0795, -1.1350}; + lab2 = {0.9033, -0.0636, -0.5514}; + expectedResult = 0.9082; + sprintf(expectedBuffer, "%.4f", expectedResult); + expectedStr = expectedBuffer; + myResult = CIEDE2000::CIEDE2000(lab1, lab2); + sprintf(myBuffer, "%.4f", myResult); + myStr = myBuffer; + std::cout << line << ": "; + std::cout << myStr << " vs " << expectedStr << '\t'; + passFail.push_back(myStr == expectedStr); + std::cout << (passFail[line - 1] ? "PASS" : "FAIL") << ' '; + std::cout << std::endl; + line++; + + std::cout << std::endl; + int ret = EXIT_SUCCESS; + for (unsigned int i = 0; i < passFail.size(); i++) { + if (!passFail[i]) { + std::cout << "Test failed on line " << + std::to_string(i + 1) << std::endl; + ret = EXIT_FAILURE; + } + } + + return (ret); +} +