Bug 465921

Summary: CIE color rendering errors
Product: Ghostscript Reporter: Jack Moffitt <jack>
Component: PS InterpreterAssignee: L. Peter Deutsch <ghost>
Status: NOTIFIED FIXED    
Severity: normal    
Priority: P2    
Version: master   
Hardware: All   
OS: All   
Customer: Word Size: ---

Description Jack Moffitt 2001-09-27 22:36:00 UTC
Originally reported by: loewenstein@users.sourceforge.net
------------------------------------------------------------------------

Symptoms:

Some colors don't render correctly with some rendering and
color space dictionaries, including the srgb dictionaries from srgb.com.

------------------------------------------------------------------------

Ghostscript version (or include output from "gs -h"):
  7.00 and 6.50
------------------------------------------------------------------------

Where you got Ghostscript:
 mirror.cs.wisc.edu
------------------------------------------------------------------------

Hardware system you are using (including printer model if the problem
involves printing):
AMD Athlon, Tyan motherboard.
Problem also reproduced on Sun Microsystems workstation on
Ghostscript 6.50, compiled using Sun's compilers.
------------------------------------------------------------------------

Operating system you are using:
Windows ME (and Solaris 7.? )
------------------------------------------------------------------------

If you are using X Windows, and your problem involved output to the
screen, the output from running xdpyinfo and xwininfo:
I didn't do this at work, but it appears independent of window system.
------------------------------------------------------------------------

C compiler you are using, including its version, if you compiled
Ghostscript yourself:
Using pre-compiled version.
------------------------------------------------------------------------

If you compiled Ghostscript yourself, changes you made to the makefiles:

------------------------------------------------------------------------

Environment variables:

        GS_DEVICE

        GS_FONTPATH

        GS_LIB

        GS_OPTIONS

None set
------------------------------------------------------------------------

Command line:
(Using bash shell)
c:/gs/gs7.00/bin/gswin32c.exe colortest.ps

------------------------------------------------------------------------

URL or FTP location of test files (include the data at the end of this
form if 500K or less):

------------------------------------------------------------------------

Suggested fix, if any:
I took a brief look at gscie.c, where I assume the problem resides.
I didn't try debugging the code.

------------------------------------------------------------------------

Other comments:

The test code generates 3 rows of 4 squares.  Each row has the "same"
color painted in /DeviceRGB, 1 version of srgb, another version of srgb
and again in /DeviceRGB.  Because both the color space and the rendering
are srgb (supposedly), each row should be of a constant color.  They are
not.

These colors were not chosen at random; they were extracted from an
image where color rendering was clearly not working properly.

The two attached test files were also run at work using Sun's imagetool,
which rendered them apparently correctly, with no discernible difference
between the squares in any 1 row.

Modifying the rendering dictionary changes the colors in quite
unexpected ways.  For example the colors are very sensitive to RangePQR,
but insensistive to MatrixPQR.

Comment 1 Jack Moffitt 2001-09-27 22:39:55 UTC
Comment originally by loewenstein@users.sourceforge.net
Logged In: YES 
user_id=335045

This is a duplicate of two attempts at submitting the bug by e-mail, with no response or acknowledgment.
Comment 2 L. Peter Deutsch 2001-12-02 20:37:08 UTC
Comment originally by lpd@users.sourceforge.net
Logged In: YES 
user_id=8861

I've verified, using our PostScript emulation of the CIE
rendering algorithms (lib/docie.ps), that the combined
transforms are indeed the identity transform. I've also
verified, using -ZC, that the C code (presumably some
combination of gscie.c, gscrd.c, and gsciemap.c) is not
producing the correct result. -ZC shows that the first
intermediate result ,after the PQR stage, is already fairly
far off the correct values. I will investigate further.
Comment 3 L. Peter Deutsch 2001-12-03 16:00:47 UTC
Comment originally by lpd@users.sourceforge.net
Logged In: YES 
user_id=8861

The problem is a numerical sensitivity that arises when any
intermediate value in the CIE pipeline is close to zero (as
it is in this case for the XYZ values: all 3 values of the
first color triple, and the Z value of the 2nd and 3rd color
triple, are less than .01). Each cache for sampled function
values has 512 entries, so TransformPQR, which has a range
of [-0.5 .. 2.0], is only sampled at intervals of
approximately 0.005. The code doesn't interpolate between
cached values at intermediate steps, since it's
computationally expensive and almost never improves the
quality of the result. Consequently, if an intermediate
value is less than 0.01, the result of looking it up in the
cache may be off by a large factor from the correct value.

The code does provide a compile-time option to interpolate
intermediate cached values (CIE_INTERPOLATE_INTERMEDIATE in
gscie.h). When I enabled this option, the output RGB values
were correct -- they matched the exact (docie.ps) values to
within 2%.

I believe a reasonable approach to this problem is to
conditionally use interpolation when it would make a
difference -- namely when the values in the adjoining cache
entries differ by more than, say, 5%. I believe this test is
still worth making, since interpolation adds an additional
18 floating multiplies, 27 floating point add/subtracts, and
9 floating point floor operations to each stage of the
pipeline that is actually used (2 such stages in the sRGB
case). In fact, the sensitive regions can be identified when
the caches are built, so the check for whether the lookup
value is in a sensitive region can be a simple range test.

Since this is a non-trivial change, I'll ask the GS
developer forum for opinions before proceeding further.
Comment 4 Jack Moffitt 2001-12-03 21:50:38 UTC
Comment originally by loewenstein@users.sourceforge.net
Logged In: YES 
user_id=335045

I must  say that the problem is pretty much  what I guessed from the symptoms and reading the comments 
in the C code.

I would recommend some additional changes.

Firstly, check for equality of the colorspace and rendering blackpoints and whitepoints.  If they are both 
equal,  eliminate the PQR calculation altogether  as allowed by the PostScript specification.  Given that you 
are allowing some error in the color rendering, this  could be an approximate equality check.   (I have 
already communicated this privately, with some more crazy suggestions!).

Secondly, refine RangePQR using the knowledge of The range of X, Y and Z.
You are basing the interval in the cache on the range specified in the PostScript rendering dictionary (in this 
case RangePQR).  This range is intended to indicate a valid range for the TransformPQR function arguments 
(for the function to produce valid results); it is not an indication of the range within which the arguments 
will actually fall.

It is straightforward to calculate from the range of X, Y and Z (in this color space each component ranges 
between the whitepoint and the blackpoint).  This range, in the format of RangePQR is:

   [-0.1757646 1.11719255 -0.7130651 1.7534663 -0.0685 1.15820885]

You will notice that the only limit that can be exceeded is the lower limit of Q, which is specified as -0.5 (I 
am virtually certain this is a bug in the rendering specification).  Ghostscript should use its knowledge of the 
range of X, Y, and Z to calculate the possible range of P, Q and R, so that the caching interval can be 
minimized.  The user should be free to code arbitrarily large ranges should no clipping to a range be desired.

Ghostscript should do this for all ranges and functions in the rendering and colorspace dictionaries.  Doing a 
perfect job is a potentially non-linear programming problem, but it should be sufficient for this application to 
propagate the range through just one matrix multiplication or function application at a time (this gives a 
somewhat pessimistic, but safe result).
Comment 5 L. Peter Deutsch 2001-12-03 23:05:38 UTC
Comment originally by lpd@users.sourceforge.net
Logged In: YES 
user_id=8861

Your suggestions are good, but they wouldn't help this
particular problem.

1. Check for equality of white/black points: yes,
Ghostscript should definitely do that. However, for this
particular color space and rendering dictionary, the code
already figures out that TransformPQR is the identity,
simply by looking at the sampled values, and optimizes it
out.

2. Refine RangePQR based on possible ranges of XYZ:
actually, Ghostscript already does exactly what you describe
in some places in the CIE code, but it should do it in more.

In this situation, however, neither of these things (would)
help. With TransformPQR optimized out, the values after the
MatrixLMN(Encode) step are very close to the correct values
(within 1% or so), but the N value is so close to 0 that the
lack of interpolation results in wildly incorrect values
when looking up this values in the sampled cache for the
EncodeLMN procedure: specifically, the N value is
approximately 0.00212, 0.00242, and 0.00698 in the 3 test
cases.

I think selective interpolation is still going to be needed.
Comment 6 L. Peter Deutsch 2001-12-06 21:48:32 UTC
Comment originally by lpd@users.sourceforge.net
Logged In: YES 
user_id=8861

Code for doing selective interpolation has been tested and
is being reviewed.
Comment 7 L. Peter Deutsch 2001-12-08 16:04:54 UTC
Comment originally by lpd@users.sourceforge.net
Logged In: YES 
user_id=8861

The selective interpolation fix has been checked in.