From 8d895790e162a3cf61998223531280ef9ae56da8 Mon Sep 17 00:00:00 2001 From: Guangyang Wen Date: Tue, 14 Nov 2017 16:44:30 +0100 Subject: [PATCH] conversion --- CMakeLists.txt | 6 ++-- include/color.h | 63 ++++++------------------------------------ include/constant.h | 22 +++++++++++++++ src/CIEDE2000.cpp | 11 +------- src/color.cpp | 13 +++++++++ src/conversion.cpp | 47 +++++++++++++++++++++++++++++++ test/testCIEDE2000.cpp | 32 +++++++++++++++++++-- 7 files changed, 124 insertions(+), 70 deletions(-) create mode 100644 include/constant.h create mode 100644 src/color.cpp create mode 100644 src/conversion.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index d16dfc9..3ee6606 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -6,8 +6,8 @@ project(color) set(CMAKE_CXX_STANDARD 11) set(CMAKE_EXPORT_COMPILE_COMMANDS ON) -add_library(CIEDE2000 SHARED src/CIEDE2000.cpp) -target_include_directories(CIEDE2000 PUBLIC include) +add_library(color SHARED src/color.cpp src/conversion.cpp src/CIEDE2000.cpp) +target_include_directories(color PUBLIC include) add_executable(testCIEDE2000 test/testCIEDE2000.cpp) -target_link_libraries(testCIEDE2000 CIEDE2000) +target_link_libraries(testCIEDE2000 color) diff --git a/include/color.h b/include/color.h index 24b3e3d..55ebc55 100644 --- a/include/color.h +++ b/include/color.h @@ -13,22 +13,13 @@ #define M_PI 3.14159265358979323846264338327950288 /* pi */ #endif -/** Namespace containing all necessary objects and methods for color */ namespace color { -/*********************************************************************** - * 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; /** A color in CIEXYZ colorspace */ @@ -37,7 +28,6 @@ struct XYZ { double y; double z; }; -/** Convenience definition for struct XYZ */ using XYZ = struct XYZ; /** A color in CIERGB colorspace */ @@ -46,13 +36,8 @@ struct RGB { double g; double b; }; -/** Convenience definition for struct RGB */ using RGB = struct RGB; -/*********************************************************************** - * Operations. - **********************************************************************/ - /** * @brief * Obtain Delta-E 2000 value. @@ -72,51 +57,19 @@ using RGB = struct RGB; */ 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. - ******************************************************************************/ +RGB XYZtoRGB(const XYZ &xyz); +XYZ LABtoXYZ(const LAB &lab); +RGB LABtoRGB(const LAB &lab); + +bool offRGB(const LAB &lab); } // namespace color -/** - * @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 color::LAB &labColor); +std::ostream &operator<<(std::ostream &s, const color::XYZ &labColor); +std::ostream &operator<<(std::ostream &s, const color::RGB &labColor); #endif /* GPF_COLOR_H_ */ diff --git a/include/constant.h b/include/constant.h new file mode 100644 index 0000000..f1ba761 --- /dev/null +++ b/include/constant.h @@ -0,0 +1,22 @@ +#ifndef ICC_CONSTANT_H_ +#define ICC_CONSTANT_H_ + +namespace color { +/* ICC's PCS illuminant */ +const double PCS_X = 31595. / 32768.; +const double PCS_Y = 1; +const double PCS_Z = 27030. / 32768.; + +/* Macbook's LCD color profile for XYZ -> RGB */ +const double A11 = 3.98875596; +const double A12 = -2.41598659; +const double A13 = -0.52021109; +const double A21 = -1.40052984; +const double A22 = 2.37728799; +const double A23 = -0.03294208; +const double A31 = -0.03269638; +const double A32 = -0.18259425; +const double A33 = 1.4716528; +} // namespace color + +#endif /* ICC_CONSTANT_H_ */ \ No newline at end of file diff --git a/src/CIEDE2000.cpp b/src/CIEDE2000.cpp index b5123f1..0ac1d57 100644 --- a/src/CIEDE2000.cpp +++ b/src/CIEDE2000.cpp @@ -147,13 +147,4 @@ double color::CIEDE2000(const LAB &lab1, const LAB &lab2) { (R_T * (deltaCPrime / (k_C * S_C)) * (deltaHPrime / (k_H * S_H)))); return (deltaE); -} - -/******************************************************************************* - * Operators. - ******************************************************************************/ - -std::ostream &operator<<(std::ostream &s, const color::LAB &labColor) { - return (s << "CIELAB(" << labColor.l << "," << labColor.a << "," << labColor.b - << ")"); -} +} \ No newline at end of file diff --git a/src/color.cpp b/src/color.cpp new file mode 100644 index 0000000..b066371 --- /dev/null +++ b/src/color.cpp @@ -0,0 +1,13 @@ +#include + +std::ostream &operator<<(std::ostream &s, const color::LAB &lab) { + return (s << "LAB(" << lab.l << "," << lab.a << "," << lab.b << ")"); +} + +std::ostream &operator<<(std::ostream &s, const color::XYZ &xyz) { + return (s << "XYZ(" << xyz.x << "," << xyz.y << "," << xyz.z << ")"); +} + +std::ostream &operator<<(std::ostream &s, const color::RGB &rgb) { + return (s << "RGB(" << rgb.r << "," << rgb.g << "," << rgb.b << ")"); +} \ No newline at end of file diff --git a/src/conversion.cpp b/src/conversion.cpp new file mode 100644 index 0000000..a493f2c --- /dev/null +++ b/src/conversion.cpp @@ -0,0 +1,47 @@ +#define _USE_MATH_DEFINES +#include +#include +#include + +namespace color { +double sgn(double val) { return (0. < val) - (val < 0.); } +constexpr inline double pow3(double x) { return x * x * x; } + +RGB XYZtoRGB(const XYZ &xyz) { + double r = xyz.x * A11 + xyz.y * A12 + xyz.z * A13; + double g = xyz.x * A21 + xyz.y * A22 + xyz.z * A23; + double b = xyz.x * A31 + xyz.y * A32 + xyz.z * A33; + + r = ((abs(r) > 0.0031308) ? sgn(r) * (1.055 * pow(abs(r), 1 / 2.4) - 0.055) : (12.92 * r)); + g = ((abs(g) > 0.0031308) ? sgn(g) * (1.055 * pow(abs(g), 1 / 2.4) - 0.055) : (12.92 * g)); + b = ((abs(b) > 0.0031308) ? sgn(b) * (1.055 * pow(abs(b), 1 / 2.4) - 0.055) : (12.92 * b)); + + return RGB{r, g, b}; +} + +XYZ LABtoXYZ(const LAB &lab) { + double y = (lab.l + 16.0) / 116.0; + double x = lab.a / 500.0 + y; + double z = y - lab.b / 200.0; + + double x3 = pow3(x); + double y3 = pow3(y); + double z3 = pow3(z); + + x = ((x3 > 0.008856) ? x3 : ((x - 16.0 / 116.0) / 7.787)) * PCS_X; + y = ((y3 > 0.008856) ? y3 : ((y - 16.0 / 116.0) / 7.787)) * PCS_Y; + z = ((z3 > 0.008856) ? z3 : ((z - 16.0 / 116.0) / 7.787)) * PCS_Z; + + return XYZ{x, y, z}; +} + +RGB LABtoRGB(const LAB &lab) { return XYZtoRGB(LABtoXYZ(lab)); } + +bool offRGB(const LAB &lab) { + const RGB rgb{LABtoRGB(lab)}; + + return rgb.r >= 0 && rgb.r <= 1 && rgb.g >= 0 && rgb.g <= 1 && rgb.b >= 0 && + rgb.b <= 1; +} + +} // namespace color diff --git a/test/testCIEDE2000.cpp b/test/testCIEDE2000.cpp index a0fe306..ac36c91 100644 --- a/test/testCIEDE2000.cpp +++ b/test/testCIEDE2000.cpp @@ -4,6 +4,7 @@ * See LICENSE for details. */ +#include #include #include #include @@ -23,11 +24,38 @@ int main(int argc, char *argv[]) { return (testcolor()); } int testcolor() { color::LAB lab1, lab2; - double expectedResult, myResult; + color::RGB rgb1, rgb_expected; unsigned int line = 1; + std::vector passFail; + + lab1 = {100, 0, 0}; + rgb1 = color::LABtoRGB(lab1); + rgb_expected = {1, 1, 1}; + + passFail.push_back(std::abs(rgb1.r - rgb_expected.r) < 1e-3); + std::cout << line << ": " << rgb1.r << " vs " << rgb_expected.r << '\t' + << (passFail[line - 1] ? "PASS" : "FAIL") << std::endl; + ++line; + + passFail.push_back(std::abs(rgb1.g - rgb_expected.g) < 1e-3); + std::cout << line << ": " << rgb1.g << " vs " << rgb_expected.g << '\t' + << (passFail[line - 1] ? "PASS" : "FAIL") << std::endl; + ++line; + + passFail.push_back(std::abs(rgb1.b - rgb_expected.b) < 1e-3); + std::cout << line << ": " << rgb1.b << " vs " << rgb_expected.b << '\t' + << (passFail[line - 1] ? "PASS" : "FAIL") << std::endl; + ++line; + + std::cout << color::LABtoRGB(color::LAB{50, 0, 0}) << std::endl; + std::cout << color::LABtoRGB(color::LAB{50, 1000, 0}) << std::endl; + std::cout << color::LABtoRGB(color::LAB{50, -1000, 0}) << std::endl; + std::cout << color::LABtoRGB(color::LAB{50, 0, 1000}) << std::endl; + std::cout << color::LABtoRGB(color::LAB{50, 0, -1000}) << std::endl; + + double expectedResult, myResult; 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};