ptransform {colorSpec}R Documentation

make a linear transformation to a colorSpec responder

Description

apply a linear transformation to a colorSpec responder with M spectra, so that multiples of M given primary vectors are transformed to the standard basis of R^M. And a given white vector is transformed to the M-vector of all 1s.
The returned object is always multiply(x,A) where A is an internally calculated MxM matrix. The name ptransform is short for projective transformation.
In case of ERROR, a message is logged and NULL returned.

Usage

## S3 method for class 'colorSpec'
ptransform( x, primary, white, digits=Inf )

Arguments

x

a colorSpec responder with M spectra. type(x) must be 'responsivity.light' or 'responsivity.material'.

primary

an MxM matrix whose rows define the M primary vectors in the response space of x. It is OK for each row to have a single value that is NA. In this case the NA value is changed so that the sum of the row is 1. This is done because typically the rows represent chromaticities in the response space of x. After this change, the rows of primary must form a basis of R^M.
rownames(primary) must be defined; when M=3 they are typically c('R','G','B').

white

an M-vector in the response space of x, that is typically the ideal white point of a color display. When white is expressed in the basis defined by primary, the coordinates must all be non-zero.
white can also be a colorSpec object with a single spectrum suitable as stimulus for x; in this case the vector white is set to product( white, x, wavelength='auto' ).

digits

if a positive integer, then white is rounded to this number of decimal digits, but in a non-standard way, see Details. This is typically done so the internally calculated MxM matrix A agrees with that from a color standard, see Examples.

Details

The formal mathematical requirements for primary and white are:

Assuming both of these are true, then there is a unique matrix A so that

This statement is essentially the fundamental theorem of (analytic) projective geometry; see Bumcrot page 87, and Semple page 398. The rows of P plus white define a projective frame; the former are the fundamental points and the latter is the unit point.

If digits is a positive integer, the chromaticity of white is computed by dividing white by sum(white). The latter must be non-zero, or else it is an ERROR. This chromaticity is rounded to digits decimal digits, while preserving the sum of 1. This rounded chromaticity is non-zero, and defines a line through 0. The vector white is projected onto this line to get the new and rounded white, with the rounded chromaticity. See Examples.

Value

a colorSpec object equal to multiply(x,A) where A is an internally calculated MxM matrix. The quantity and wavelength are preserved. The specnames of the returned object are set to tolower( rownames(primary) ).
The user may want to change the quantity of the returned object; see Examples.

References

Bumcrot, Robert J. Modern Projective Geometry. Holt, Rinehart, and Winston. 1969.

IEC 61966-2-1:1999. Multimedia systems and equipment - Colour measurement and management. Part 2-1: Colour management - Default RGB colour space - sRGB. https://webstore.iec.ch/publication/6169

Semple, J. G. and G. T. Kneebone. Algebraic Projective Geometry. Oxford. 1952.

See Also

quantity, wavelength, colorSpec, multiply, product

Examples

############ Example for sRGB   ###########

# assign the standard sRGB primaries
P = matrix( c(0.64,0.33,NA,  0.3,0.6,NA, 0.15,0.06,NA ), 3, 3, byrow=TRUE )
rownames(P) = c('R','G','B')
P
#   [,1] [,2] [,3]
# R 0.64 0.33   NA
# G 0.30 0.60   NA
# B 0.15 0.06   NA

white = product( D65.1nm, xyz1931.1nm, wave='auto' )
white
#           X        Y        Z
# D65 100.437 105.6708 115.0574

white/sum(white)
#             X         Y         Z
# D65 0.3127269 0.3290232 0.3582499    

# But the sRGB standard D65 is xy=(0.3127,0.3290)
# so when the next line is executed,
# the calculated 3x3 matrix will *NOT* agree with the sRGB standard
y = ptransform( xyz1931.1nm, P, white, digits=Inf )

product( D65.1nm, y, wave='auto' )
#     R G B
# D65 1 1 1      # this is exactly what we want, but the internal 3x3 matrix is a little off

# now repeat, but this time round the white chromaticity to
# xy=(0.3127,0.3290) in order to get the matrix right
y = ptransform( xyz1931.1nm, P, white, digits=4 )

rgb = product( D65.1nm, y, wave='auto' )
rgb
#            R        G        B
# D65 1.000238 1.000053 0.999835   # off in the 4'th digit  (WARN: this is linear RGB)

255 * rgb
#            R        G        B
# D65 255.0607 255.0134 254.9579   # good enough for 8-bit RGB

65535 * rgb
#            R        G        B
# D65 65550.59 65538.44 65524.18   # NOT good enough for 16-bit RGB  

# So for 16-bit RGB, one can get the white RGB right, or the 3x3 matrix right, but not both !


############ Example for ProPhoto RGB   ###########

# assign the standard ProPhoto RGB primaries
P = matrix( c(0.7347,0.2653,NA,  0.1596,0.8404,NA, 0.0366,0.0001,NA ), 3, 3, byrow=TRUE )
rownames(P) = c('R','G','B')
P
#     [,1]   [,2] [,3]
# R 0.7347 0.2653   NA
# G 0.1596 0.8404   NA
# B 0.0366 0.0001   NA

white = product( D50.5nm, xyz1931.5nm, wave='auto' )
white
#            X       Y        Z
# D50 101.2815 105.042 86.67237

white / sum(white)
#             X         Y         Z
# D50 0.3456755 0.3585101 0.2958144  

# but the ProPhoto RGB standard is xy=(0.3457,0.3585);  proceed anyway
y = ptransform( xyz1931.5nm, P, white, digits=Inf )

product( D50.5nm, y, wave='auto' )
#     R G B
# D50 1 1 1     #  this is exactly what we want, but the internal 3x3 matrix is a little off

# the following line is an equivalent way to compute y.
# pass D50.5nm directly as the 'white' argument
y = ptransform( xyz1931.5nm, P, D50.5nm )

[Package colorSpec version 1.5-0 Index]