product: Compute the product of colorSpec objects

View source: R/colorSpec.product.R

productR Documentation

Compute the product of colorSpec objects

Description

Take a sequence of colorSpec objects and compute their product. Only certain types of sequences are allowed. The return value can be a new colorSpec object or a matrix; see Details.

Usage

## S3 method for class 'colorSpec'
product( ... )

Arguments

...

unnamed arguments are colorSpec objects, and possibly a single character string, see Details. Possible named arguments are:

wavelength

The default wavelength='identical' means that all the colorSpec objects must have the same wavelength sequence; if they do not it is an ERROR. wavelength can be a new wavelength sequence, and all the objects are then resampled at these new wavelengths. wavelength can also be 'auto' or NULL which means to compute a suitable wavelength sequence from those of the objects, see Details. It is OK to abbreviate the string wavelength (e.g. to wave); see Examples. It is OK for the wavelength sequence to be irregular; when the return value is a matrix the integration weights the spectrum values appropriately.

method, span, extrapolation, clamp

passed to resample() with no checking or changes

integration

only applies when the return type is matrix. The default option is 'rectangular', which means to weight the spectrum value equally at all wavelengths; this is the ASTM E308-01 recommendation. The other option is 'trapezoidal', which means to give the 2 endpoint wavelength values 1/2 the weight of the others. Trapezoidal integration is provided mostly for compatibility with other software.

Details

To explain the allowable product sequences it is helpful to introduce some simple notation for the objects:

notation colorSpec type description of the object
L light a light source
M material a material
R_L responsivity.light a light responder (aka detector)
R_M responsivity.material a material responder (e.g. a scanner)

It is also helpful to define a sequence of positive integers to be conformable iff it has at most one value greater than 1. For example, a sequence of all 1s is conformable. A sequence of all q's is conformable. The sequences c(1,3) and c(1,1,4,1,1,4,1) are conformable, but c(1,1,4,1,3,4,1) is not.

There are 6 types of sequences for which the product is defined:

1.    M_1 * M_2 * ... * M_m M'
The product of m materials is another material. Think of a stack of m transmitting filters effectively forming a new filter. If we think of each object as a matrix (with the spectra in the columns), then the product is element-by-element using R's * - the Hadamard product. The numbers of spectra in the terms must be conformable. If some objects have 1 spectrum and all the others have q, then the column-vector spectrums are repeated q times to form a matrix with q columns. If the numbers of spectra are not conformable, it is an ERROR and the function returns NULL.
As an example, suppose M_1 has 1 spectrum and M_2 has q spectra, and m=2. Then the product is a material with q spectra. Think of an IR-blocking filter followed by the RGB filters in a 3-CCD camera.

2.    L * M_1 * M_2 * ... * M_m L'
The product of a light source followed by m materials is a light source. Think of a light source followed by a stack of m transmitting filters, effectively forming a new light source. The numbers of spectra in the terms must be conformable as in sequence 1, and the matrices are multiplied element by element.
As an example, suppose L has 1 spectrum and M_1 has q spectra, and m=1. Then the product is a light source with q spectra. Think of a light source followed by a filter wheel with q filters.

3.    M_1 * M_2 * ... * M_m * R_L R_L'
The product of m materials followed by a light responder, is a light responder. Think of a stack of m transmitting filters in front of a camera, effectively forming a new camera. The numbers of spectra in the terms must be conformable as in sequence 1, and the matrices are multiplied element by element.
As an example, suppose R_L has 1 spectrum and M_1 has q spectra, and m=1. Then the product is a light responder with q spectra. Think of a 3-CCD camera in which all 3 CCDs have exactly the same responsivity and so can be modeled with a single object R_L.

4.    L * M_1 * ... ** ... * M_m * R_L R_M'
This is the strangest product. The bullet symbol • means that a variable material is inserted at that slot in the sequence (or light path). For each material spectrum inserted there is a response from R_L. Therefore the product of this sequence is a material responder R_M. Think of a light source L going through a transparent object • on a flatbed scanner and into a camera R_L. For more about the mathematics of this product, see the colorSpec-guide.pdf in the doc directory. These material responder spectra are the same as the effective spectral responsivities in Digital Color Management. The numbers of spectra in the terms must be conformable as in sequence 1, and the product is a material responder with q spectra.
In the function product() the location of the • is marked by any character string whatsoever - it's up to the user who might choose something that describes the typical material (between the light source and camera). For example one might choose:
scanner = product( A.1nm, 'photo', Flea2.RGB, wave='auto')
to model a scanner that is most commonly used to scan photographs. Other possible strings could be 'artwork', 'crystal', 'varmaterial', or even 'slot'. See the vignette Viewing Object Colors in a Gallery for a worked-out example.

5.    L * M_1 * M_2 * ... * M_m * R_L matrix
The product of a light source, followed by m materials, followed by a light responder, is a matrix! The numbers of spectra in the terms must splittable into a conformable left part (L' from sequence 2.) and a conformable right part (R_L' from sequence 3.). There is a row for each spectrum in L', and a column for each spectrum in R_L'. Suppose the element-by-element product of the left part is n×p and the element-by-element product of the right part is and n×q, where n is the number of wavelengths. Then the output matrix is the usual matrix product %*% of the transpose of the left part times and right part, which is p×q.
As an example, think of a light source followed by a reflective color target with 24 patches followed by an RGB camera. The sequence of spectra counts is c(1,24,3) which is splittable into c(1,24) and c(3). The product matrix is 24×3. See the vignette Viewing Object Colors in a Gallery for a worked-out example.
Note that is OK for there to be no materials in this product; it is OK if m=0. See the vignette Blue Flame and Green Comet for a worked-out example.

6.    M_1 * M_2 * ... * M_m * R_M matrix
The product of m materials followed by a material responder, is a matrix ! The sequence of numbers of spectra must be splittable into left and right parts as in sequence 4, and the product matrix is formed the same way. One reason for computing this matrix in 2 steps is that one can calibrate the material responder separately in a customizable way. See the vignette Viewing Object Colors in a Gallery for a worked-out example with a flatbed scanner.

Note that sequences 5. and 6. are the only ones that use the usual matrix product %*%. They may also use the Hadamard matrix product *, as in sequences 1 to 4.

The argument wavelength can also be 'auto' or NULL. In this case the intersection of all the wavelength ranges of the objects is computed. If the intersection is empty, it is an ERROR and the function returns NULL. The wavelength step step.wl is taken to be the smallest over all the object wavelength sequences. If the minimum step.wl is less than 1 nanometer, it is rounded off to the nearest power of 2 (e.g 1, 0.5, 0.25, ...).

Value

product() returns either a colorSpec object or a matrix, see Details.

If product() returns a colorSpec object, the organization of the object is 'matrix' or 'vector'; any extradata is lost. However, all terms in the product are saved in attr(*,'sequence'). One can use str() to inspect this attribute.

If product() returns a matrix, this matrix can sometimes be ambiguous, see Note.

All actinometric terms are converted to radiometric on-the-fly and the returned colorSpec object is also radiometric.

In case of ERROR it returns NULL.

Note

The product for sequences 1, 2, and 3 is associative. After all matrices are filled out to have q columns, the result is essentially a Hadamard product of matrices, which is associative. Also note that a subsequence of sequences 2 and 3 might be sequence 1.

The product for sequence 4 is never associative, since subproducts that contain the variable • are undefined. However the result is essentially a Hadamard product of matrices, and is unambiguous.

The product for sequence 5 is associative in special cases, but not in general. The problem is that the left and right splitting point is not unique. If all objects have only a single spectrum, then it *is* associative, and therefore unambiguous. If the left part has a different number of multiple spectra than the right part, then it is not associative in general since some ways of grouping the product may be undefined.
Moreover, in some cases the product can be ambiguous. Suppose that the vector of spectrum counts is c(1,3,1); this could come from a single light source, followed by 3 filters (e.g. RGB), followed by a graylevel camera. There are 2 ways to split this: "1|3,1" and "1,3|1". The first split is interpreted as the light source into a camera with 3 channels. The second split is interpreted as 3 colored light sources into a graylevel camera. In the first split the returned matrix is a 1x3 row vector. In the second split the returned matrix is a 3x1 column vector. For the vector "1,3,1", one can show that the computed components in the 2 vectors are equal, so the ambiguity is benign. But consider the longer sequence "1,3,3,1". There are 3 ways to split this, and the returned matrices are 1x3, 3x3, and 3x1. So this ambiguity is obviously a problem. Whenever there is an ambiguity, the function chooses a splitting in which the left part is as long as possible, and issues a warning message. The user should inspect the result carefully. To avoid the ambiguity, the user should break the product into smaller pieces and call product() multiple times.

The product for sequence 6 is essentially the same as sequence 5, and the function issues a warning message when appropriate. Note that a subproduct is defined only if it avoids the final multiplication with R_M.

References

Edward J. Giorgianni and Thomas E. Madden. Digital Color Management: Encoding Solutions. 2nd Edition John Wiley. 2009. Figure 10.11a. page 141.

Wikipedia. Hadamard product (matrices). https://en.wikipedia.org/wiki/Hadamard_product_(matrices)

ASTM E308-01. Standard Practice for Computing the Colors of Objects by Using the CIE System. (2001).

See Also

wavelength, type, resample, calibrate, radiometric, step.wl

Examples

#  sequence 1.
path = system.file( "extdata/objects/Midwest-SP700-2014.txt", package='colorSpec' )
blocker.IR = readSpectra( path )
product( blocker.IR, Hoya, wave='auto' )


#  sequence 2.
product( subset(solar.irradiance,1), atmosphere2003, blocker.IR, Hoya, wave='auto' )


#  sequence 3.
plumbicon = readSpectra( system.file( "extdata/cameras/plumbicon30mm.txt", package='colorSpec' ) )
product( blocker.IR, subset(Hoya,1:3), plumbicon, wave='auto' )


#   sequence 4.
#   make an RGB scanner
bluebalancer = subset(Hoya,'LB')
# combine tungsten light source A.1nm with blue light-balance filter
# use the string 'artwork' to mark the variable material slot
scanner = product( A.1nm, bluebalancer, 'artwork', Flea2.RGB, wave='auto' )


#  sequence 5.
product( D65.1nm, Flea2.RGB, wave='auto' )  # a 1x3 matrix, no materials
product( D65.1nm, neutralMaterial(0.01), Flea2.RGB, wave='auto' ) # a 1x3 matrix, 1 material
path = system.file( "extdata/sources/Lumencor-SpectraX.txt", package='colorSpec' )
lumencor = readSpectra( path, wave=340:660 )
product( lumencor, Flea2.RGB, wave='auto' )   # a 7x3 matrix, no materials


#  sequence 6.
scanner = calibrate( scanner )
target = readSpectra( system.file( "extdata/targets/N130501.txt", package='colorSpec') )
product( target, scanner, wave='auto' )  #  a 288x3 matrix


colorSpec documentation built on May 29, 2024, 6 a.m.