Learn R Programming

colorSpec (version 1.5-0)

ptransform: 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 colorSpec
ptransform( x, primary, white, digits=Inf )

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.

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:

  • The rows of primary must form a basis of \(R^M\). Equivalently, if \(P\) denotes the matrix primary, then \(P\) is invertible.

  • The coordinates of white in this basis are all non-zero. Equivalently, if \(x\) is the solution of \( x P = white\), then every component of x is non-zero.

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

  • \(A\) transforms a multiple of the \(i\)'th row of \(P\) to the \(i\)'th standard basis vector of \(R^M\).

  • \(A\) transforms white to the M-vector of all 1s.

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.

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

Run this code
############ 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 )

Run the code above in your browser using DataLab