Skip to content

Commit

Permalink
Initial commit.
Browse files Browse the repository at this point in the history
  • Loading branch information
gfiumara committed Mar 1, 2015
0 parents commit 16fe987
Show file tree
Hide file tree
Showing 7 changed files with 1,004 additions and 0 deletions.
7 changes: 7 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
*.dll
*.so
*.dylib
*.o
*.dSYM
testCIEDE2000

172 changes: 172 additions & 0 deletions CIEDE2000.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
/*
* CIEDE2000.cpp
* Part of http://github.com/gfiumara/CIEDE2000 by Gregory Fiumara.
* See LICENSE for details.
*/

#include <cmath>

#include <CIEDE2000.h>

/*******************************************************************************
* 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 << ")");
}

117 changes: 117 additions & 0 deletions CIEDE2000.h
Original file line number Diff line number Diff line change
@@ -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 <ostream>

#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_ */

21 changes: 21 additions & 0 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -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.
53 changes: 53 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -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)

Loading

0 comments on commit 16fe987

Please sign in to comment.