Proposed xyY colorspace

Questions and postings pertaining to the development of ImageMagick, feature enhancements, and ImageMagick internals. ImageMagick source code and algorithms are discussed here. Usage questions which are too arcane for the normal user list should also be posted here.
Post Reply
snibgo
Posts: 12159
Joined: 2010-01-23T23:01:33-07:00
Authentication code: 1151
Location: England, UK

Proposed xyY colorspace

Post by snibgo »

I propose a new colorspace named "xyY" or perhaps "CIExyY". See http://en.wikipedia.org/wiki/CIE_1931_color_space for the transformations between XYZ and xyY.

This will help us build CIE 1931 chromaticity diagrams:
Image
The following diff files, from v6.8.9-6, give changes that implement xyY:

colorspace.h:

Code: Select all

--- /home/Alan/imdevsrc/../ImageMagick-6.8.9-6/magick/colorspace.h	2013-11-18 13:03:16.000000000 +0000
+++ /home/Alan/imdevsrc/magick/colorspace.h	2014-09-29 02:53:40.504147300 +0100
@@ -30,6 +30,7 @@
   TransparentColorspace,
   OHTAColorspace,
   LabColorspace,
+  xyYColorspace,
   XYZColorspace,
   YCbCrColorspace,
   YCCColorspace,
colorspace.c:

Code: Select all

--- /home/Alan/imdevsrc/../ImageMagick-6.8.9-6/magick/colorspace.c	2014-03-29 15:42:48.000000000 +0000
+++ /home/Alan/imdevsrc/magick/colorspace.c	2014-09-29 03:01:07.675182900 +0100
@@ -195,6 +195,21 @@
   *Q=QuantumScale*(0.211456*red-0.522591*green+0.311135*blue)+0.5;
 }
 
+static void ConvertRGBToxyY(const Quantum red,const Quantum green,
+  const Quantum blue,double *lowx,double *lowy,double *capY)
+{
+  double
+    X,
+    Y,
+    Z;
+
+  ConvertRGBToXYZ(red,green,blue,&X,&Y,&Z);
+
+  *lowx = X / (X+Y+Z);
+  *lowy = Y / (X+Y+Z);
+  *capY = Y;
+}
+
 
 MagickExport MagickBooleanType RGBTransformImage(Image *image,
   const ColorspaceType colorspace)
@@ -377,6 +392,7 @@
     case LCHuvColorspace:
     case LMSColorspace:
     case LuvColorspace:
+    case xyYColorspace:
     case XYZColorspace:
     case YCbCrColorspace:
     case YDbDrColorspace:
@@ -502,6 +518,11 @@
               ConvertRGBToLuv(red,green,blue,&X,&Y,&Z);
               break;
             }
+            case xyYColorspace:
+            {
+              ConvertRGBToxyY(red,green,blue,&X,&Y,&Z);
+              break;
+            }
             case XYZColorspace:
             {
               ConvertRGBToXYZ(red,green,blue,&X,&Y,&Z);
@@ -1131,7 +1152,9 @@
     }
   else
     if ((IsRGBColorspace(colorspace) != MagickFalse) ||
-        (colorspace == XYZColorspace))
+        (colorspace == XYZColorspace) ||
+        (colorspace == xyYColorspace)
+       )
       image->gamma=1.0;
     else
       {
@@ -1369,6 +1392,21 @@
     4.813762626262513e-04*(V-0.5)));
 }
 
+static inline void ConvertxyYToRGB(const double lowx,const double lowy,
+  const double capY,Quantum *red,Quantum *green,Quantum *blue)
+{
+  double
+    X,
+    Y,
+    Z;
+
+  X = capY / lowy * lowx;
+  Y = capY;
+  Z = capY / lowy * (1.0 - lowx - lowy);
+
+  ConvertXYZToRGB(X,Y,Z,red,green,blue);
+}
+
 MagickExport MagickBooleanType TransformRGBImage(Image *image,
   const ColorspaceType colorspace)
 {
@@ -1786,6 +1824,7 @@
     case LCHuvColorspace:
     case LMSColorspace:
     case LuvColorspace:
+    case xyYColorspace:
     case XYZColorspace:
     case YCbCrColorspace:
     case YDbDrColorspace:
@@ -1911,6 +1950,11 @@
               ConvertLuvToRGB(X,Y,Z,&red,&green,&blue);
               break;
             }
+            case xyYColorspace:
+            {
+              ConvertxyYToRGB(X,Y,Z,&red,&green,&blue);
+              break;
+            }
             case XYZColorspace:
             {
               ConvertXYZToRGB(X,Y,Z,&red,&green,&blue);
option.c:

Code: Select all

--- /home/Alan/imdevsrc/../ImageMagick-6.8.9-6/magick/option.c	2014-06-30 19:05:31.000000000 +0100
+++ /home/Alan/imdevsrc/magick/option.c	2014-09-29 01:40:46.998158100 +0100
@@ -909,6 +909,7 @@
     { "sRGB", sRGBColorspace, UndefinedOptionFlag, MagickFalse },
     { "Transparent", TransparentColorspace, UndefinedOptionFlag, MagickFalse },
     { "XYZ", XYZColorspace, UndefinedOptionFlag, MagickFalse },
+    { "xyY", xyYColorspace, UndefinedOptionFlag, MagickFalse },
     { "YCbCr", YCbCrColorspace, UndefinedOptionFlag, MagickFalse },
     { "YDbDr", YDbDrColorspace, UndefinedOptionFlag, MagickFalse },
     { "YCC", YCCColorspace, UndefinedOptionFlag, MagickFalse },

snibgo's IM pages: im.snibgo.com
User avatar
magick
Site Admin
Posts: 11064
Joined: 2003-05-31T11:32:55-07:00

Re: Proposed xyY colorspace

Post by magick »

We'll get your patch into the next point release of ImageMagick. Thanks for your contribution.
snibgo
Posts: 12159
Joined: 2010-01-23T23:01:33-07:00
Authentication code: 1151
Location: England, UK

Re: Proposed xyY colorspace

Post by snibgo »

Thanks.

It seems that IM's conversions between any two colorspaces is always done via sRGB. But not all images can be encoded in sRGB. Modern cameras capture colours outside sRGB's gamut, and printers can print them. So a recent trend is to use colorspaces with a wider gamut. The code in colorspace.c clips values to be within sRGB, with ClampToQuantum().

(For a background on colorspaces, profiles and gamuts, I recommend Andrew Rodney's 37-minute video "Everything you thought you wanted to know about color gamut" http://www.youtube.com/watch?v=n0bxSD-Xx-Q . The 3-D graphs have axes x, y and Y.)

A kludged solution is not to clamp, and use HDRI. Then colours outside the sRGB gamut will have values <0 or >Quantum, but the arithmetic still works. In my colorspace.c, I have replaced many occurrences of "ClampToQuantum" with "CLAMPQUANT". New code defines this:

Code: Select all

#if defined(MAGICKCORE_HDRI_SUPPORT)
#define CLAMPQUANT
#else
#define CLAMPQUANT ClampToQuantum
#endif
I do not propose this kludge.

A non-kludge solution would work with integer IM, and yield correct XYZ or xyY values for images that are encoded in profiles wider than sRGB, such as AdobeRGB1998 or ProPhoto. This needs more work.

Does anyone have any thoughts?
snibgo's IM pages: im.snibgo.com
User avatar
magick
Site Admin
Posts: 11064
Joined: 2003-05-31T11:32:55-07:00

Re: Proposed xyY colorspace

Post by magick »

Use HDRI. ClampToQuantum() is a no-op for HDRI so the full range of the colorspace is preserved. This is among the reasons we choose to default to HDRI for ImageMagick version 7.
snibgo
Posts: 12159
Joined: 2010-01-23T23:01:33-07:00
Authentication code: 1151
Location: England, UK

Re: Proposed xyY colorspace

Post by snibgo »

magick wrote:ClampToQuantum() is a no-op for HDRI so the full range of the colorspace is preserved.
Oh, gosh, so it is (in quantum.h). I confirm my kludge has no effect in HDRI. Sorry about that.

This is good news. However, I don't see how an image encoded in a wide-profile would get an accurate XYZ or xyY encoding. For example, the purest green (0,100%,0) in AdobeRGB1998 is out of gamut in sRGB. In sRGB it would be something like (0,125%,0). I don't know how to do that conversion in IM.
snibgo's IM pages: im.snibgo.com
snibgo
Posts: 12159
Joined: 2010-01-23T23:01:33-07:00
Authentication code: 1151
Location: England, UK

Re: Proposed xyY colorspace

Post by snibgo »

snibgo wrote:I don't know how to do that conversion in IM.
Cracked it. The first line creates a sample image in AdobeRGB space; the last line gives the sRGB values.

Code: Select all

convert ^
  xc:#0f0 -set profile AdobeRGB1998.icc ^
  -precision 15 -profile D65_XYZ.icc +strip -set colorspace XYZ -colorspace sRGB txt:
D65_XYZ is available from http://www.color.org/XYZprofiles.xalter.

Please forgive my ramblings. My queries are solved. Thanks.
snibgo's IM pages: im.snibgo.com
Post Reply