Colorspace round trips

Questions and postings pertaining to the usage of ImageMagick regardless of the interface. This includes the command-line utilities, as well as the C and C++ APIs. Usage questions are like "How do I use ImageMagick to create drop shadows?".
Post Reply
snibgo
Posts: 11683
Joined: 2010-01-23T23:01:33-07:00
Authentication code: 1151
Location: England, UK

Colorspace round trips

Post by snibgo » 2013-06-16T19:28:51-07:00

I'm not convinced about the round-trip capabilities of some of the colorspaces.

In Windows 7, IM v6.8.6-0, I did the following:

Code: Select all

convert hald:8 h8.png
convert h8.png -colorspace %%a -colorspace sRGB x.png
compare -metric RMSE h8.png x.png NULL: 2>>csrt.lis
The results, sorted with worst RMSE at the top, are:

Code: Select all

18181.7 (0.277435) Gray
18181.7 (0.277435) Rec709Luma
16972 (0.258977) Rec601Luma
7613.53 (0.116175) LCHab
7613.53 (0.116175) LCH
7191.13 (0.10973) YCC
6176.26 (0.0942437) LCHuv
5912.42 (0.0902177) YDbDr
265.687 (0.00405413) YUV
156.837 (0.00239318) YIQ
152.622 (0.00232887) XYZ
71.7158 (0.00109431) LMS
8.19112 (0.000124989) Rec601YCbCr
8.15981 (0.000124511) YCbCr
8.15981 (0.000124511) YPbPr
1.69933 (2.59302e-005) Luv
1.64528 (2.51054e-005) Rec709YCbCr
1.43594 (2.1911e-005) Lab
1.43594 (2.1911e-005) CIELab
1.40868 (2.14951e-005) Log
1.11803 (1.70601e-005) scRGB
1.11803 (1.70601e-005) RGB
0.995072 (1.51838e-005) OHTA
0.745473 (1.13752e-005) HSL
0.675326 (1.03048e-005) HSI
0.644634 (9.83648e-006) HCLp
0.612231 (9.34205e-006) HCL
0.583683 (8.90643e-006) HSB
0.583683 (8.90643e-006) HSV
0.578735 (8.83093e-006) HWB
0 (0) Transparent
0 (0) sRGB
0 (0) CMYK
0 (0) CMY
The worst three are monochrome colorspaces, so the round-trip should fail. The results from YUV onwards are less than 1%, so we might put this down to rounding errors.

But can anyone explain the 11% difference for LCHab, etc?
snibgo's IM pages: im.snibgo.com

User avatar
GreenKoopa
Posts: 457
Joined: 2010-11-04T17:24:08-07:00
Authentication code: 8675308

Re: Colorspace round trips

Post by GreenKoopa » 2013-06-17T00:13:04-07:00

Those colorspaces should be capable of a round trip without clipping. And that does seem like a lot to be rounding error. Have you tried using HDRI ? I don't know where to get a build, or how to create one for Windows. It may give insight. On the other hand, HDRI working wouldn't necessarily mean that this is caused by rounding error alone.

Looking at the math for these, I see nothing exciting. And the ones with larger error don't have anything in common. I do know that with integer math, these functions can be tricky to implement. Intermediate steps can easily cause clipping. For example, multiplying a value by 8 then dividing by 10 is fine in math or HDRI, but severely clips with computer integers. Reversing to dividing then multiplying introduces noise, but at least not the clipping. Other methods yield better results. I'm sure the programmers know all of this well, but it can be a complex area for bugs to creep in.

Or maybe it's rounding error after all. Not tonight, but I'll try to experiment later.

snibgo
Posts: 11683
Joined: 2010-01-23T23:01:33-07:00
Authentication code: 1151
Location: England, UK

Re: Colorspace round trips

Post by snibgo » 2013-06-17T05:56:59-07:00

Thanks, GK.

Floating-point IM is available from http://www.imagemagick.org/download/alpha/ . This is actually IM v7, but seems compatible with v6 usage. For Windows, I install ImageMagick-7.0.0-0-Q16-x64-dll.exe to a directory of my choice, and "set IMG7={that_directory}".

Sadly, IM7 "-colorspace" seems to be currently broken.

I repeated the test (on v6) with an ordinary photograph. Apart from YCC, the results are far better.

Code: Select all

4000.05 (0.0610369) YCC
2313.23 (0.0352976) Rec601Luma
2295.13 (0.0350215) Rec709Luma
2295.13 (0.0350215) Gray
184.861 (0.0028208) LCHab
184.861 (0.0028208) LCH
144.18 (0.00220005) LCHuv
5.26625 (8.03579e-005) XYZ
4.46201 (6.8086e-005) LMS
2.10566 (3.21303e-005) scRGB
2.10566 (3.21303e-005) RGB
1.88689 (2.87921e-005) Rec601YCbCr
1.53452 (2.34153e-005) YCbCr
1.53452 (2.34153e-005) YPbPr
1.53389 (2.34056e-005) YUV
1.53298 (2.33917e-005) YIQ
1.47786 (2.25507e-005) YDbDr
0.986905 (1.50592e-005) OHTA
0.901452 (1.37553e-005) Luv
0.889155 (1.35676e-005) Rec709YCbCr
0.775572 (1.18345e-005) Log
0.650785 (9.93034e-006) Lab
0.650785 (9.93034e-006) CIELab
0.507796 (7.74847e-006) HSL
0.228193 (3.48201e-006) HCLp
0.22819 (3.48196e-006) HCL
0.215116 (3.28246e-006) HSI
0.00485913 (7.41455e-008) HSB
0.00485913 (7.41455e-008) HSV
0 (0) Transparent
0 (0) sRGB
0 (0) HWB
0 (0) CMYK
0 (0) CMY
One hypothesis: some RGB colours cannot be represented in IM's implementation of LCHab, etc. Or, as you suggest, clipping.
snibgo's IM pages: im.snibgo.com

User avatar
GreenKoopa
Posts: 457
Joined: 2010-11-04T17:24:08-07:00
Authentication code: 8675308

Re: Colorspace round trips

Post by GreenKoopa » 2013-06-17T10:37:27-07:00

LCHab and LCHuv are simply Lab and Luv, except cylindrical like HSL and HSV are to RGB. Since Lab, Luv, HSL, and HSV all made the round trip correctly, I believe there is no reason that LCHab and LCHuv couldn't. Maybe cylindrical plus noise causes the problem? Or LCHab and LCHuv are more truly cylindrical, trig and all. Internally it is possible that a round trip looks like sRGB->Lab->LCHab->Lab->sRGB. I don't think this helps explain YCC or YDbDr.

It still feels like something is wrong, or at least could be done better. My next step would be to stop thinking and actually look at what color ranges don't survive the round trip.

User avatar
fmw42
Posts: 25016
Joined: 2007-07-02T17:14:51-07:00
Authentication code: 1152
Location: Sunnyvale, California, USA

Re: Colorspace round trips

Post by fmw42 » 2013-06-17T11:10:34-07:00

This may be related to the gamut ranges. Some colorspace are outside the range of sRGB or vice-versa. But since LCHab and LCHuv are relatively new in IM, there could still be bugs.

User avatar
GreenKoopa
Posts: 457
Joined: 2010-11-04T17:24:08-07:00
Authentication code: 8675308

Re: Colorspace round trips

Post by GreenKoopa » 2013-06-17T13:40:43-07:00

gamut range, good point to keep in mind.

As the worst offender, I'm going to focus on LHCab. This is my understanding:
The sRGB gamut fits easily within Lab (even implementation-clipped Lab).
LHCab has the same gamut as Lab (mathematically, but implementation?).
L's range is 0..1 (scaled to 0..QuantumRange)
a & b's range is -128..127 in Q8, which is a small clip of the full space. Q16 implementations vary slightly, as we can see by converting with -colorspace vs -profile.
C's range is 0..181 (when staying within the clip of a & b)
H's range is a full circle. (scaled to 0..QuantumRange)

Conclusions:
IM appears to limit C to 0..127. Even for the small gamut of sRGB this probably results in some clipping.
For being a circle, H has far too many QuantumRange values. This must be a wrapping problem in the code.

User avatar
magick
Site Admin
Posts: 10979
Joined: 2003-05-31T11:32:55-07:00

Re: Colorspace round trips

Post by magick » 2013-06-17T17:30:57-07:00

LCHab includes a 0.5 offset to shift -127 to 0 and 0 to 127 to cover the full range [0..255]. Here is the actual code. If you spot an error, let us know:

Code: Select all

static inline void ConvertXYZToLCHab(const double X,const double Y,const double Z,
  double *luma,double *chroma,double *hue)
{
  double
    a,
    b;

  ConvertXYZToLab(X,Y,Z,luma,&a,&b);
  *chroma=hypot(255.0*(a-0.5),255.0*(b-0.5));
  *hue=180.0*atan2(255.0*(b-0.5),255.0*(a-0.5))/MagickPI;
  *chroma=(*chroma)/255.0+0.5;
  *hue=(*hue)/255.0+0.5;
  if (*hue < 0.0)
    *hue+=1.0;
}

MagickPrivate void ConvertRGBToLCHab(const double red,const double green,const double blue,
  double *luma,double *chroma,double *hue)
{
  double
    X,
    Y,
    Z;

  ConvertRGBToXYZ(red,green,blue,&X,&Y,&Z);
  ConvertXYZToLCHab(X,Y,Z,luma,chroma,hue);
}

static inline void ConvertLCHabToXYZ(const double luma,const double chroma,const double hue,
  double *X,double *Y,double *Z)
{
  ConvertLabToXYZ(luma,chroma*cos(hue*MagickPI/180.0),chroma*
    sin(hue*MagickPI/180.0),X,Y,Z);
}

MagickPrivate void ConvertLCHabToRGB(const double luma,const double chroma,const double hue,
  double *red,double *green,double *blue)
{
  double
    X,
    Y,
    Z;

  ConvertLCHabToXYZ(100.0*luma,255.0*(chroma-0.5),255.0*(hue-0.5),&X,&Y,&Z);
  ConvertXYZToRGB(X,Y,Z,red,green,blue);
}

User avatar
GreenKoopa
Posts: 457
Joined: 2010-11-04T17:24:08-07:00
Authentication code: 8675308

Re: Colorspace round trips

Post by GreenKoopa » 2013-06-17T18:46:09-07:00

I see both my C and H channel concerns.

Code: Select all

// From ConvertXYZToLCHab()

// a in [0.0,1.0]
// b in [0.0,1.0]
// 255.0*(a-0.5) in [-127.5,127.5]
// 255.0*(b-0.5) in [-127.5,127.5]

// ----- C -----
*chroma=hypot(255.0*(a-0.5),255.0*(b-0.5));
// chroma in [0.0,181)
*chroma=(*chroma)/255.0+0.5;
// chroma in [0.5,1.21)


// ----- H -----
*hue=180.0*atan2(255.0*(b-0.5),255.0*(a-0.5))/MagickPI;
// atan2() in (-PI,PI], 0.0 when undefined?
// hue in (-180.0,180.0]
*hue=(*hue)/255.0+0.5;
// OOps?

User avatar
GreenKoopa
Posts: 457
Joined: 2010-11-04T17:24:08-07:00
Authentication code: 8675308

Re: Colorspace round trips

Post by GreenKoopa » 2013-06-17T20:52:40-07:00

Thank you for the code magick!
magick wrote:LCHab includes a 0.5 offset to shift -127 to 0 and 0 to 127 to cover the full range [0..255].
Lab's a and b need offset, but LCHab's C is a non-negative number roughly in the range [0,181].

For LCHuv, you will find the same hue problem. C is non-negative.

Why offset hue? The very next line adds 360 degrees to negative angles.

Code: Select all

  *hue=(*hue)/255.0+0.5;
  if (*hue < 0.0)
    *hue+=1.0;

User avatar
GreenKoopa
Posts: 457
Joined: 2010-11-04T17:24:08-07:00
Authentication code: 8675308

Re: Colorspace round trips

Post by GreenKoopa » 2013-06-19T01:05:37-07:00

Moving on to YDbDr. Clipping is occurring on the Db and Dr channels. The clipping is heavy off both ends. I don't know anything about this colorspace but Wikipedia says their range is [-1.333,1.333]. Wikipedia says this is an encoding of RGB, meaning that there is no gamut conversion to cause the clipping. There is likely a bug to be found here too.

User avatar
magick
Site Admin
Posts: 10979
Joined: 2003-05-31T11:32:55-07:00

Re: Colorspace round trips

Post by magick » 2013-06-19T05:17:51-07:00

Why offset hue? The very next line adds 360 degrees to negative angles.
We have a 16-million color image covering every RGB color. A round-trip from LCHab to sRGB returns this distortion:
  • convert reference.tif -colorspace LCHab -colorspace sRGB colorspace.png
    compare -metric rmse colorspace.png 16MillionColors.tif null:
    7538.72 (0.115033)
Now, let's remove the 0.5 offset from hue:
  • compare -metric rmse colorspace.png 16MillionColors.tif null:
    15661.3 (0.238977)
Increased distortion suggests the offset is needed.

The same with YDbDr. If we scale by 1.333, the distortion increases.

We'll review the algorithms in case there are bugs we overlooked.

User avatar
GreenKoopa
Posts: 457
Joined: 2010-11-04T17:24:08-07:00
Authentication code: 8675308

Re: Colorspace round trips

Post by GreenKoopa » 2013-06-19T08:05:00-07:00

magick wrote:Increased distortion suggests the offset is needed.
You are dividing by 255.0 when it should be 360.0 for hue. See my code walk-through above. Offsetting helps because it centers the clipping, but it will be unnecessary when you fix the scaling problem.

User avatar
magick
Site Admin
Posts: 10979
Joined: 2003-05-31T11:32:55-07:00

Re: Colorspace round trips

Post by magick » 2013-06-19T08:39:49-07:00

We can reproduce the problem you posted and have a patch in ImageMagick 6.8.6-1 Beta available by sometime tomorrow. Thanks.

Post Reply