MagickWand/MagickCore:RGB -> YCC & YCC->RGB aren't inverses
Posted: 2008-07-26T15:57:52-07:00
First, wanted to thank the developers of ImageMagick for a great, well-documented, time-saving library.
I've been using the MagickWand API (ImageMagick 6.4.2) to extract pixel data from various image types, and do some colorspace conversion. The colorspaces of interest are RGB (RGBColorspace), CIELAB (LabColorspace), CMYK (CMYKColorspace), Grayscale (GrayscaleColorspace), and Kodak PhotoYCC (YCCColorspace, not to be confused with YCbCr or YPbPr).
I came across an interesting anomaly: the colorspace conversions defined in colorspace.c work great, except when involving YCC. The equations listed seem fine (the comments below about scaling and such are ambiguous, but that's a separate issue. Said equations are excerpted below:
However, the two transforms implemented are not inverses each other. Quite to the contrary, if one converts an image from RGB to YCC, then back to RGB, the values yielded in the floating-point pixel variable are off by a factor of exactly 81000, which yields stored pixel component quantized values between 0x0 and roughly 0xCF00 (instead of 0x0 - 0xFFFFFFFF for quantum_depth=32, which gives all 0's (black)in the resulting RGB image after bit depth reduction.
Perhaps I'm misunderstanding something, but it seems wrong that if I have a pixel value (quantum_depth = 32) of {0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF} in RGB, convert it to YCC via RGBTransformImage, then back to RGB via [TransformImageRGB[/i], that the resulting pixel value is {0x00000000,0x00000000,0x0000000}.
I've found the following two mathematically-equivalent fixes:
In my code
Patching colorspace.c
I can provide more info if needed, but both fixes have been demonstrated to work. Need to figure out where the translation from equations to code went bad, or what assumptions I'm missing, which is difficult because the code for this is (overly) optimized, combined arithmetic operations involving constants instead of being clear and letting the compiler do it for you. Not a big deal though..
Anyway, thanks in advance for looking at this!
-Matt
I've been using the MagickWand API (ImageMagick 6.4.2) to extract pixel data from various image types, and do some colorspace conversion. The colorspaces of interest are RGB (RGBColorspace), CIELAB (LabColorspace), CMYK (CMYKColorspace), Grayscale (GrayscaleColorspace), and Kodak PhotoYCC (YCCColorspace, not to be confused with YCbCr or YPbPr).
I came across an interesting anomaly: the colorspace conversions defined in colorspace.c work great, except when involving YCC. The equations listed seem fine (the comments below about scaling and such are ambiguous, but that's a separate issue. Said equations are excerpted below:
Code: Select all
/*
Initialize YCC tables:
Y = 0.29900*R+0.58700*G+0.11400*B
C1= -0.29900*R-0.58700*G+0.88600*B
C2= 0.70100*R-0.58700*G-0.11400*B
YCC is scaled by 1.3584. C1 zero is 156 and C2 is at 137.
*/
Code: Select all
/*
Initialize YCC tables:
R = Y +1.340762*C2
G = Y-0.317038*C1-0.682243*C2
B = Y+1.632639*C1
YCC is scaled by 1.3584. C1 zero is 156 and C2 is at 137.
*/
Perhaps I'm misunderstanding something, but it seems wrong that if I have a pixel value (quantum_depth = 32) of {0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF} in RGB, convert it to YCC via RGBTransformImage, then back to RGB via [TransformImageRGB[/i], that the resulting pixel value is {0x00000000,0x00000000,0x0000000}.
I've found the following two mathematically-equivalent fixes:
In my code
Code: Select all
//After the YCC->RGB MagickSetImageColorspace() call, but BEFORE we write out to file
//Scale by 81000 if we're doing YCC to RGB.
//No root cause found yet, but without this, if you convert RGB->YCC->RGB, the first conversion goes fine, but the second yields
//a black image (all values are non-zero, but way less than 1 LSB).
if(inputColorspace == YCCColorspace)
{
status=MagickEvaluateImage(magick_wand, MultiplyEvaluateOperator, 81000.0f);
if(status == MagickFalse)
ThrowWandException(magick_wand);
}
Code: Select all
case YCCColorspace:
{
pixel.red/=1.3584000f;
pixel.red*=81000.0f; //FIX
pixel.green/=1.3584000f;
pixel.green*=81000.0f; //FIX
pixel.blue/=1.3584000f;
pixel.blue*=81000.0f; //FIX
break;
}
Anyway, thanks in advance for looking at this!
-Matt