mirror of
https://git.savannah.gnu.org/git/emacs.git
synced 2024-11-24 07:20:37 +00:00
Add CAM02 JCh and CAM02-UCS J'a'b' conversions
* src/lcms.c (rad2deg, parse_jch_list, parse_jab_list, xyz_to_jch): (jch_to_xyz, jch_to_jab, jab_to_jch): New functions. (lcms-jch->xyz, lcms-jch->xyz, lcms-jch->jab, lcms-jab->jch): New Lisp functions. (lcms-cam02-ucs): Refactor. (syms_of_lcms2): Declare new functions. * test/src/lcms-tests.el (lcms-roundtrip, lcms-ciecam02-gold): (lcms-jmh->cam02-ucs-silver): New tests. * etc/NEWS: Mention new functions.
This commit is contained in:
parent
157007b58e
commit
645ff6c702
3
etc/NEWS
3
etc/NEWS
@ -76,7 +76,8 @@ If the lcms2 library is installed, Emacs will enable features built on
|
|||||||
top of that library. The new configure option '--without-lcms2' can
|
top of that library. The new configure option '--without-lcms2' can
|
||||||
be used to build without lcms2 support even if it is installed. Emacs
|
be used to build without lcms2 support even if it is installed. Emacs
|
||||||
linked to Little CMS exposes color management functions in Lisp: the
|
linked to Little CMS exposes color management functions in Lisp: the
|
||||||
color metrics 'lcms-cie-de2000' and 'lcms-cam02-ucs'.
|
color metrics 'lcms-cie-de2000' and 'lcms-cam02-ucs', as well as
|
||||||
|
functions for conversion to and from CIE CAM02 and CAM02-UCS.
|
||||||
|
|
||||||
** The configure option '--with-gameuser' now defaults to 'no',
|
** The configure option '--with-gameuser' now defaults to 'no',
|
||||||
as this appears to be the most common configuration in practice.
|
as this appears to be the most common configuration in practice.
|
||||||
|
301
src/lcms.c
301
src/lcms.c
@ -25,6 +25,13 @@ along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
|
|||||||
|
|
||||||
#include "lisp.h"
|
#include "lisp.h"
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
double J;
|
||||||
|
double a;
|
||||||
|
double b;
|
||||||
|
} lcmsJab_t;
|
||||||
|
|
||||||
#ifdef WINDOWSNT
|
#ifdef WINDOWSNT
|
||||||
# include <windows.h>
|
# include <windows.h>
|
||||||
# include "w32.h"
|
# include "w32.h"
|
||||||
@ -36,6 +43,8 @@ DEF_DLL_FN (cmsHANDLE, cmsCIECAM02Init,
|
|||||||
(cmsContext ContextID, const cmsViewingConditions* pVC));
|
(cmsContext ContextID, const cmsViewingConditions* pVC));
|
||||||
DEF_DLL_FN (void, cmsCIECAM02Forward,
|
DEF_DLL_FN (void, cmsCIECAM02Forward,
|
||||||
(cmsHANDLE hModel, const cmsCIEXYZ* pIn, cmsJCh* pOut));
|
(cmsHANDLE hModel, const cmsCIEXYZ* pIn, cmsJCh* pOut));
|
||||||
|
DEF_DLL_FN (void, cmsCIECAM02Reverse,
|
||||||
|
(cmsHANDLE hModel, const cmsJCh* pIn, cmsCIEXYZ* pOut));
|
||||||
DEF_DLL_FN (void, cmsCIECAM02Done, (cmsHANDLE hModel));
|
DEF_DLL_FN (void, cmsCIECAM02Done, (cmsHANDLE hModel));
|
||||||
DEF_DLL_FN (cmsBool, cmsWhitePointFromTemp,
|
DEF_DLL_FN (cmsBool, cmsWhitePointFromTemp,
|
||||||
(cmsCIExyY* WhitePoint, cmsFloat64Number TempK));
|
(cmsCIExyY* WhitePoint, cmsFloat64Number TempK));
|
||||||
@ -54,6 +63,7 @@ init_lcms_functions (void)
|
|||||||
LOAD_DLL_FN (library, cmsCIE2000DeltaE);
|
LOAD_DLL_FN (library, cmsCIE2000DeltaE);
|
||||||
LOAD_DLL_FN (library, cmsCIECAM02Init);
|
LOAD_DLL_FN (library, cmsCIECAM02Init);
|
||||||
LOAD_DLL_FN (library, cmsCIECAM02Forward);
|
LOAD_DLL_FN (library, cmsCIECAM02Forward);
|
||||||
|
LOAD_DLL_FN (library, cmsCIECAM02Reverse);
|
||||||
LOAD_DLL_FN (library, cmsCIECAM02Done);
|
LOAD_DLL_FN (library, cmsCIECAM02Done);
|
||||||
LOAD_DLL_FN (library, cmsWhitePointFromTemp);
|
LOAD_DLL_FN (library, cmsWhitePointFromTemp);
|
||||||
LOAD_DLL_FN (library, cmsxyY2XYZ);
|
LOAD_DLL_FN (library, cmsxyY2XYZ);
|
||||||
@ -63,6 +73,7 @@ init_lcms_functions (void)
|
|||||||
# undef cmsCIE2000DeltaE
|
# undef cmsCIE2000DeltaE
|
||||||
# undef cmsCIECAM02Init
|
# undef cmsCIECAM02Init
|
||||||
# undef cmsCIECAM02Forward
|
# undef cmsCIECAM02Forward
|
||||||
|
# undef cmsCIECAM02Reverse
|
||||||
# undef cmsCIECAM02Done
|
# undef cmsCIECAM02Done
|
||||||
# undef cmsWhitePointFromTemp
|
# undef cmsWhitePointFromTemp
|
||||||
# undef cmsxyY2XYZ
|
# undef cmsxyY2XYZ
|
||||||
@ -70,6 +81,7 @@ init_lcms_functions (void)
|
|||||||
# define cmsCIE2000DeltaE fn_cmsCIE2000DeltaE
|
# define cmsCIE2000DeltaE fn_cmsCIE2000DeltaE
|
||||||
# define cmsCIECAM02Init fn_cmsCIECAM02Init
|
# define cmsCIECAM02Init fn_cmsCIECAM02Init
|
||||||
# define cmsCIECAM02Forward fn_cmsCIECAM02Forward
|
# define cmsCIECAM02Forward fn_cmsCIECAM02Forward
|
||||||
|
# define cmsCIECAM02Reverse fn_cmsCIECAM02Reverse
|
||||||
# define cmsCIECAM02Done fn_cmsCIECAM02Done
|
# define cmsCIECAM02Done fn_cmsCIECAM02Done
|
||||||
# define cmsWhitePointFromTemp fn_cmsWhitePointFromTemp
|
# define cmsWhitePointFromTemp fn_cmsWhitePointFromTemp
|
||||||
# define cmsxyY2XYZ fn_cmsxyY2XYZ
|
# define cmsxyY2XYZ fn_cmsxyY2XYZ
|
||||||
@ -145,6 +157,12 @@ deg2rad (double degrees)
|
|||||||
return M_PI * degrees / 180.0;
|
return M_PI * degrees / 180.0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static double
|
||||||
|
rad2deg (double radians)
|
||||||
|
{
|
||||||
|
return 180.0 * radians / M_PI;
|
||||||
|
}
|
||||||
|
|
||||||
static cmsCIEXYZ illuminant_d65 = { .X = 95.0455, .Y = 100.0, .Z = 108.8753 };
|
static cmsCIEXYZ illuminant_d65 = { .X = 95.0455, .Y = 100.0, .Z = 108.8753 };
|
||||||
|
|
||||||
static void
|
static void
|
||||||
@ -180,6 +198,46 @@ parse_xyz_list (Lisp_Object xyz_list, cmsCIEXYZ *color)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool
|
||||||
|
parse_jch_list (Lisp_Object jch_list, cmsJCh *color)
|
||||||
|
{
|
||||||
|
#define PARSE_JCH_LIST_FIELD(field) \
|
||||||
|
if (CONSP (jch_list) && NUMBERP (XCAR (jch_list))) \
|
||||||
|
{ \
|
||||||
|
color->field = XFLOATINT (XCAR (jch_list)); \
|
||||||
|
jch_list = XCDR (jch_list); \
|
||||||
|
} \
|
||||||
|
else \
|
||||||
|
return false;
|
||||||
|
|
||||||
|
PARSE_JCH_LIST_FIELD (J);
|
||||||
|
PARSE_JCH_LIST_FIELD (C);
|
||||||
|
PARSE_JCH_LIST_FIELD (h);
|
||||||
|
|
||||||
|
if (! NILP (jch_list))
|
||||||
|
return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool
|
||||||
|
parse_jab_list (Lisp_Object jab_list, lcmsJab_t *color)
|
||||||
|
{
|
||||||
|
#define PARSE_JAB_LIST_FIELD(field) \
|
||||||
|
if (CONSP (jab_list) && NUMBERP (XCAR (jab_list))) \
|
||||||
|
{ \
|
||||||
|
color->field = XFLOATINT (XCAR (jab_list)); \
|
||||||
|
jab_list = XCDR (jab_list); \
|
||||||
|
} \
|
||||||
|
else \
|
||||||
|
return false;
|
||||||
|
|
||||||
|
PARSE_JAB_LIST_FIELD (J);
|
||||||
|
PARSE_JAB_LIST_FIELD (a);
|
||||||
|
PARSE_JAB_LIST_FIELD (b);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
static bool
|
static bool
|
||||||
parse_viewing_conditions (Lisp_Object view, const cmsCIEXYZ *wp,
|
parse_viewing_conditions (Lisp_Object view, const cmsCIEXYZ *wp,
|
||||||
cmsViewingConditions *vc)
|
cmsViewingConditions *vc)
|
||||||
@ -216,6 +274,204 @@ parse_viewing_conditions (Lisp_Object view, const cmsCIEXYZ *wp,
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
xyz_to_jch (const cmsCIEXYZ *xyz, cmsJCh *jch, const cmsViewingConditions *vc)
|
||||||
|
{
|
||||||
|
cmsHANDLE h;
|
||||||
|
|
||||||
|
h = cmsCIECAM02Init (0, vc);
|
||||||
|
cmsCIECAM02Forward (h, xyz, jch);
|
||||||
|
cmsCIECAM02Done (h);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
jch_to_xyz (const cmsJCh *jch, cmsCIEXYZ *xyz, const cmsViewingConditions *vc)
|
||||||
|
{
|
||||||
|
cmsHANDLE h;
|
||||||
|
|
||||||
|
h = cmsCIECAM02Init (0, vc);
|
||||||
|
cmsCIECAM02Reverse (h, jch, xyz);
|
||||||
|
cmsCIECAM02Done (h);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
jch_to_jab (const cmsJCh *jch, lcmsJab_t *jab, double FL, double c1, double c2)
|
||||||
|
{
|
||||||
|
double Mp = 43.86 * log (1.0 + c2 * (jch->C * sqrt (sqrt (FL))));
|
||||||
|
jab->J = 1.7 * jch->J / (1.0 + (c1 * jch->J));
|
||||||
|
jab->a = Mp * cos (deg2rad (jch->h));
|
||||||
|
jab->b = Mp * sin (deg2rad (jch->h));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
jab_to_jch (const lcmsJab_t *jab, cmsJCh *jch, double FL, double c1, double c2)
|
||||||
|
{
|
||||||
|
jch->J = jab->J / (1.0 + c1 * (100.0 - jab->J));
|
||||||
|
jch->h = atan2 (jab->b, jab->a);
|
||||||
|
double Mp = hypot (jab->a, jab->b);
|
||||||
|
jch->h = rad2deg (jch->h);
|
||||||
|
if (jch->h < 0.0)
|
||||||
|
jch->h += 360.0;
|
||||||
|
jch->C = (exp (c2 * Mp) - 1.0) / (c2 * sqrt (sqrt (FL)));
|
||||||
|
}
|
||||||
|
|
||||||
|
DEFUN ("lcms-xyz->jch", Flcms_xyz_to_jch, Slcms_xyz_to_jch, 1, 3, 0,
|
||||||
|
doc: /* Convert CIE CAM02 JCh to CIE XYZ.
|
||||||
|
COLOR is a list (X Y Z), with Y scaled about unity.
|
||||||
|
Optional arguments WHITEPOINT and VIEW are the same as in `lcms-cam02-ucs',
|
||||||
|
which see. */)
|
||||||
|
(Lisp_Object color, Lisp_Object whitepoint, Lisp_Object view)
|
||||||
|
{
|
||||||
|
cmsViewingConditions vc;
|
||||||
|
cmsJCh jch;
|
||||||
|
cmsCIEXYZ xyz, xyzw;
|
||||||
|
|
||||||
|
#ifdef WINDOWSNT
|
||||||
|
if (!lcms_initialized)
|
||||||
|
lcms_initialized = init_lcms_functions ();
|
||||||
|
if (!lcms_initialized)
|
||||||
|
{
|
||||||
|
message1 ("lcms2 library not found");
|
||||||
|
return Qnil;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (!(CONSP (color) && parse_xyz_list (color, &xyz)))
|
||||||
|
signal_error ("Invalid color", color);
|
||||||
|
if (NILP (whitepoint))
|
||||||
|
xyzw = illuminant_d65;
|
||||||
|
else if (!(CONSP (whitepoint) && parse_xyz_list (whitepoint, &xyzw)))
|
||||||
|
signal_error ("Invalid white point", whitepoint);
|
||||||
|
if (NILP (view))
|
||||||
|
default_viewing_conditions (&xyzw, &vc);
|
||||||
|
else if (!(CONSP (view) && parse_viewing_conditions (view, &xyzw, &vc)))
|
||||||
|
signal_error ("Invalid viewing conditions", view);
|
||||||
|
|
||||||
|
xyz_to_jch(&xyz, &jch, &vc);
|
||||||
|
return list3 (make_float (jch.J), make_float (jch.C), make_float (jch.h));
|
||||||
|
}
|
||||||
|
|
||||||
|
DEFUN ("lcms-jch->xyz", Flcms_jch_to_xyz, Slcms_jch_to_xyz, 1, 3, 0,
|
||||||
|
doc: /* Convert CIE XYZ to CIE CAM02 JCh.
|
||||||
|
COLOR is a list (J C h), where lightness of white is equal to 100, and hue
|
||||||
|
is given in degrees.
|
||||||
|
Optional arguments WHITEPOINT and VIEW are the same as in `lcms-cam02-ucs',
|
||||||
|
which see. */)
|
||||||
|
(Lisp_Object color, Lisp_Object whitepoint, Lisp_Object view)
|
||||||
|
{
|
||||||
|
cmsViewingConditions vc;
|
||||||
|
cmsJCh jch;
|
||||||
|
cmsCIEXYZ xyz, xyzw;
|
||||||
|
|
||||||
|
#ifdef WINDOWSNT
|
||||||
|
if (!lcms_initialized)
|
||||||
|
lcms_initialized = init_lcms_functions ();
|
||||||
|
if (!lcms_initialized)
|
||||||
|
{
|
||||||
|
message1 ("lcms2 library not found");
|
||||||
|
return Qnil;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (!(CONSP (color) && parse_jch_list (color, &jch)))
|
||||||
|
signal_error ("Invalid color", color);
|
||||||
|
if (NILP (whitepoint))
|
||||||
|
xyzw = illuminant_d65;
|
||||||
|
else if (!(CONSP (whitepoint) && parse_xyz_list (whitepoint, &xyzw)))
|
||||||
|
signal_error ("Invalid white point", whitepoint);
|
||||||
|
if (NILP (view))
|
||||||
|
default_viewing_conditions (&xyzw, &vc);
|
||||||
|
else if (!(CONSP (view) && parse_viewing_conditions (view, &xyzw, &vc)))
|
||||||
|
signal_error ("Invalid viewing conditions", view);
|
||||||
|
|
||||||
|
jch_to_xyz(&jch, &xyz, &vc);
|
||||||
|
return list3 (make_float (xyz.X / 100.0),
|
||||||
|
make_float (xyz.Y / 100.0),
|
||||||
|
make_float (xyz.Z / 100.0));
|
||||||
|
}
|
||||||
|
|
||||||
|
DEFUN ("lcms-jch->jab", Flcms_jch_to_jab, Slcms_jch_to_jab, 1, 3, 0,
|
||||||
|
doc: /* Convert CIE CAM02 JCh to CAM02-UCS J'a'b'.
|
||||||
|
COLOR is a list (J C h) as described in `lcms-jch->xyz', which see.
|
||||||
|
Optional arguments WHITEPOINT and VIEW are the same as in `lcms-cam02-ucs',
|
||||||
|
which see. */)
|
||||||
|
(Lisp_Object color, Lisp_Object whitepoint, Lisp_Object view)
|
||||||
|
{
|
||||||
|
cmsViewingConditions vc;
|
||||||
|
lcmsJab_t jab;
|
||||||
|
cmsJCh jch;
|
||||||
|
cmsCIEXYZ xyzw;
|
||||||
|
double FL, k, k4;
|
||||||
|
|
||||||
|
#ifdef WINDOWSNT
|
||||||
|
if (!lcms_initialized)
|
||||||
|
lcms_initialized = init_lcms_functions ();
|
||||||
|
if (!lcms_initialized)
|
||||||
|
{
|
||||||
|
message1 ("lcms2 library not found");
|
||||||
|
return Qnil;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (!(CONSP (color) && parse_jch_list (color, &jch)))
|
||||||
|
signal_error ("Invalid color", color);
|
||||||
|
if (NILP (whitepoint))
|
||||||
|
xyzw = illuminant_d65;
|
||||||
|
else if (!(CONSP (whitepoint) && parse_xyz_list (whitepoint, &xyzw)))
|
||||||
|
signal_error ("Invalid white point", whitepoint);
|
||||||
|
if (NILP (view))
|
||||||
|
default_viewing_conditions (&xyzw, &vc);
|
||||||
|
else if (!(CONSP (view) && parse_viewing_conditions (view, &xyzw, &vc)))
|
||||||
|
signal_error ("Invalid viewing conditions", view);
|
||||||
|
|
||||||
|
k = 1.0 / (1.0 + (5.0 * vc.La));
|
||||||
|
k4 = k * k * k * k;
|
||||||
|
FL = vc.La * k4 + 0.1 * (1 - k4) * (1 - k4) * cbrt (5.0 * vc.La);
|
||||||
|
jch_to_jab (&jch, &jab, FL, 0.007, 0.0228);
|
||||||
|
return list3 (make_float (jab.J), make_float (jab.a), make_float (jab.b));
|
||||||
|
}
|
||||||
|
|
||||||
|
DEFUN ("lcms-jab->jch", Flcms_jab_to_jch, Slcms_jab_to_jch, 1, 3, 0,
|
||||||
|
doc: /* Convert CAM02-UCS J'a'b' to CIE CAM02 JCh.
|
||||||
|
COLOR is a list (J' a' b'), where white corresponds to lightness J equal to 100.
|
||||||
|
Optional arguments WHITEPOINT and VIEW are the same as in `lcms-cam02-ucs',
|
||||||
|
which see. */)
|
||||||
|
(Lisp_Object color, Lisp_Object whitepoint, Lisp_Object view)
|
||||||
|
{
|
||||||
|
cmsViewingConditions vc;
|
||||||
|
cmsJCh jch;
|
||||||
|
lcmsJab_t jab;
|
||||||
|
cmsCIEXYZ xyzw;
|
||||||
|
double FL, k, k4;
|
||||||
|
|
||||||
|
#ifdef WINDOWSNT
|
||||||
|
if (!lcms_initialized)
|
||||||
|
lcms_initialized = init_lcms_functions ();
|
||||||
|
if (!lcms_initialized)
|
||||||
|
{
|
||||||
|
message1 ("lcms2 library not found");
|
||||||
|
return Qnil;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (!(CONSP (color) && parse_jab_list (color, &jab)))
|
||||||
|
signal_error ("Invalid color", color);
|
||||||
|
if (NILP (whitepoint))
|
||||||
|
xyzw = illuminant_d65;
|
||||||
|
else if (!(CONSP (whitepoint) && parse_xyz_list (whitepoint, &xyzw)))
|
||||||
|
signal_error ("Invalid white point", whitepoint);
|
||||||
|
if (NILP (view))
|
||||||
|
default_viewing_conditions (&xyzw, &vc);
|
||||||
|
else if (!(CONSP (view) && parse_viewing_conditions (view, &xyzw, &vc)))
|
||||||
|
signal_error ("Invalid viewing conditions", view);
|
||||||
|
|
||||||
|
k = 1.0 / (1.0 + (5.0 * vc.La));
|
||||||
|
k4 = k * k * k * k;
|
||||||
|
FL = vc.La * k4 + 0.1 * (1 - k4) * (1 - k4) * cbrt (5.0 * vc.La);
|
||||||
|
jab_to_jch (&jab, &jch, FL, 0.007, 0.0228);
|
||||||
|
return list3 (make_float (jch.J), make_float (jch.C), make_float (jch.h));
|
||||||
|
}
|
||||||
|
|
||||||
/* References:
|
/* References:
|
||||||
Li, Luo et al. "The CRI-CAM02UCS colour rendering index." COLOR research
|
Li, Luo et al. "The CRI-CAM02UCS colour rendering index." COLOR research
|
||||||
and application, 37 No.3, 2012.
|
and application, 37 No.3, 2012.
|
||||||
@ -239,10 +495,9 @@ The default viewing conditions are (20 100 1 1). */)
|
|||||||
{
|
{
|
||||||
cmsViewingConditions vc;
|
cmsViewingConditions vc;
|
||||||
cmsJCh jch1, jch2;
|
cmsJCh jch1, jch2;
|
||||||
cmsHANDLE h1, h2;
|
|
||||||
cmsCIEXYZ xyz1, xyz2, xyzw;
|
cmsCIEXYZ xyz1, xyz2, xyzw;
|
||||||
double Jp1, ap1, bp1, Jp2, ap2, bp2;
|
lcmsJab_t jab1, jab2;
|
||||||
double Mp1, Mp2, FL, k, k4;
|
double FL, k, k4;
|
||||||
|
|
||||||
#ifdef WINDOWSNT
|
#ifdef WINDOWSNT
|
||||||
if (!lcms_initialized)
|
if (!lcms_initialized)
|
||||||
@ -267,41 +522,17 @@ The default viewing conditions are (20 100 1 1). */)
|
|||||||
else if (!(CONSP (view) && parse_viewing_conditions (view, &xyzw, &vc)))
|
else if (!(CONSP (view) && parse_viewing_conditions (view, &xyzw, &vc)))
|
||||||
signal_error ("Invalid view conditions", view);
|
signal_error ("Invalid view conditions", view);
|
||||||
|
|
||||||
h1 = cmsCIECAM02Init (0, &vc);
|
xyz_to_jch (&xyz1, &jch1, &vc);
|
||||||
h2 = cmsCIECAM02Init (0, &vc);
|
xyz_to_jch (&xyz2, &jch2, &vc);
|
||||||
cmsCIECAM02Forward (h1, &xyz1, &jch1);
|
|
||||||
cmsCIECAM02Forward (h2, &xyz2, &jch2);
|
|
||||||
cmsCIECAM02Done (h1);
|
|
||||||
cmsCIECAM02Done (h2);
|
|
||||||
|
|
||||||
/* Now have colors in JCh, need to calculate J'a'b'
|
|
||||||
|
|
||||||
M = C * F_L^0.25
|
|
||||||
J' = 1.7 J / (1 + 0.007 J)
|
|
||||||
M' = 43.86 ln(1 + 0.0228 M)
|
|
||||||
a' = M' cos(h)
|
|
||||||
b' = M' sin(h)
|
|
||||||
|
|
||||||
where
|
|
||||||
|
|
||||||
F_L = 0.2 k^4 (5 L_A) + 0.1 (1 - k^4)^2 (5 L_A)^(1/3),
|
|
||||||
k = 1/(5 L_A + 1)
|
|
||||||
*/
|
|
||||||
k = 1.0 / (1.0 + (5.0 * vc.La));
|
k = 1.0 / (1.0 + (5.0 * vc.La));
|
||||||
k4 = k * k * k * k;
|
k4 = k * k * k * k;
|
||||||
FL = vc.La * k4 + 0.1 * (1 - k4) * (1 - k4) * cbrt (5.0 * vc.La);
|
FL = vc.La * k4 + 0.1 * (1 - k4) * (1 - k4) * cbrt (5.0 * vc.La);
|
||||||
Mp1 = 43.86 * log (1.0 + 0.0228 * (jch1.C * sqrt (sqrt (FL))));
|
jch_to_jab (&jch1, &jab1, FL, 0.007, 0.0228);
|
||||||
Mp2 = 43.86 * log (1.0 + 0.0228 * (jch2.C * sqrt (sqrt (FL))));
|
jch_to_jab (&jch2, &jab2, FL, 0.007, 0.0228);
|
||||||
Jp1 = 1.7 * jch1.J / (1.0 + (0.007 * jch1.J));
|
|
||||||
Jp2 = 1.7 * jch2.J / (1.0 + (0.007 * jch2.J));
|
|
||||||
ap1 = Mp1 * cos (deg2rad (jch1.h));
|
|
||||||
ap2 = Mp2 * cos (deg2rad (jch2.h));
|
|
||||||
bp1 = Mp1 * sin (deg2rad (jch1.h));
|
|
||||||
bp2 = Mp2 * sin (deg2rad (jch2.h));
|
|
||||||
|
|
||||||
return make_float (sqrt ((Jp2 - Jp1) * (Jp2 - Jp1) +
|
return make_float (hypot (jab2.J - jab1.J,
|
||||||
(ap2 - ap1) * (ap2 - ap1) +
|
hypot (jab2.a - jab1.a, jab2.b - jab1.b)));
|
||||||
(bp2 - bp1) * (bp2 - bp1)));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
DEFUN ("lcms-temp->white-point", Flcms_temp_to_white_point, Slcms_temp_to_white_point, 1, 1, 0,
|
DEFUN ("lcms-temp->white-point", Flcms_temp_to_white_point, Slcms_temp_to_white_point, 1, 1, 0,
|
||||||
@ -359,6 +590,10 @@ void
|
|||||||
syms_of_lcms2 (void)
|
syms_of_lcms2 (void)
|
||||||
{
|
{
|
||||||
defsubr (&Slcms_cie_de2000);
|
defsubr (&Slcms_cie_de2000);
|
||||||
|
defsubr (&Slcms_xyz_to_jch);
|
||||||
|
defsubr (&Slcms_jch_to_xyz);
|
||||||
|
defsubr (&Slcms_jch_to_jab);
|
||||||
|
defsubr (&Slcms_jab_to_jch);
|
||||||
defsubr (&Slcms_cam02_ucs);
|
defsubr (&Slcms_cam02_ucs);
|
||||||
defsubr (&Slcms2_available_p);
|
defsubr (&Slcms2_available_p);
|
||||||
defsubr (&Slcms_temp_to_white_point);
|
defsubr (&Slcms_temp_to_white_point);
|
||||||
|
@ -94,6 +94,38 @@ B is considered the exact value."
|
|||||||
(apply #'color-xyz-to-xyy (lcms-temp->white-point 7504))
|
(apply #'color-xyz-to-xyy (lcms-temp->white-point 7504))
|
||||||
'(0.29902 0.31485 1.0))))
|
'(0.29902 0.31485 1.0))))
|
||||||
|
|
||||||
|
(ert-deftest lcms-roundtrip ()
|
||||||
|
"Test accuracy of converting to and from different color spaces"
|
||||||
|
(skip-unless (featurep 'lcms2))
|
||||||
|
(should
|
||||||
|
(let ((color '(.5 .3 .7)))
|
||||||
|
(lcms-triple-approx-p (lcms-jch->xyz (lcms-xyz->jch color))
|
||||||
|
color
|
||||||
|
0.0001)))
|
||||||
|
(should
|
||||||
|
(let ((color '(.8 -.2 .2)))
|
||||||
|
(lcms-triple-approx-p (lcms-jch->jab (lcms-jab->jch color))
|
||||||
|
color
|
||||||
|
0.0001))))
|
||||||
|
|
||||||
|
(ert-deftest lcms-ciecam02-gold ()
|
||||||
|
"Test CIE CAM02 JCh gold values"
|
||||||
|
(skip-unless (featurep 'lcms2))
|
||||||
|
(should
|
||||||
|
(lcms-triple-approx-p
|
||||||
|
(lcms-xyz->jch '(0.1931 0.2393 0.1014)
|
||||||
|
'(0.9888 0.900 0.3203)
|
||||||
|
'(18 200 1 1.0))
|
||||||
|
'(48.0314 38.7789 191.0452)
|
||||||
|
0.02))
|
||||||
|
(should
|
||||||
|
(lcms-triple-approx-p
|
||||||
|
(lcms-xyz->jch '(0.1931 0.2393 0.1014)
|
||||||
|
'(0.9888 0.90 0.3203)
|
||||||
|
'(18 20 1 1.0))
|
||||||
|
'(47.6856 36.0527 185.3445)
|
||||||
|
0.09)))
|
||||||
|
|
||||||
(ert-deftest lcms-dE-cam02-ucs-silver ()
|
(ert-deftest lcms-dE-cam02-ucs-silver ()
|
||||||
"Test CRI-CAM02-UCS deltaE metric values from colorspacious."
|
"Test CRI-CAM02-UCS deltaE metric values from colorspacious."
|
||||||
(skip-unless (featurep 'lcms2))
|
(skip-unless (featurep 'lcms2))
|
||||||
@ -114,4 +146,16 @@ B is considered the exact value."
|
|||||||
8.503323264883667
|
8.503323264883667
|
||||||
0.04)))
|
0.04)))
|
||||||
|
|
||||||
|
(ert-deftest lcms-jmh->cam02-ucs-silver ()
|
||||||
|
"Compare JCh conversion to CAM02-UCS to values from colorspacious."
|
||||||
|
(skip-unless (featurep 'lcms2))
|
||||||
|
(should
|
||||||
|
(lcms-triple-approx-p (lcms-jch->jab '(50 20 10))
|
||||||
|
'(62.96296296 16.22742674 2.86133316)
|
||||||
|
0.05))
|
||||||
|
(should
|
||||||
|
(lcms-triple-approx-p (lcms-jch->jab '(10 60 100))
|
||||||
|
'(15.88785047 -6.56546789 37.23461867)
|
||||||
|
0.04)))
|
||||||
|
|
||||||
;;; lcms-tests.el ends here
|
;;; lcms-tests.el ends here
|
||||||
|
Loading…
Reference in New Issue
Block a user