spacesRGB User Guide

Glenn Davis

2024-01-23

RGB spaces are confusing because for each RGB space, up to three RGB vectors are involved:

Without qualification, the non-linear signal RGB is usually meant. This is the case for grDevices::rgb(), grDevices::hsv(), grDevices::convertColor(), etc. In this package, and in the functions RGBfromXYZ() and XYZfromRGB(), signal RGB is meant.

There are no strong package dependencies, but these packages are suggested:

Similar packages: Package colorspace [4] has similar functionality, and is much faster because it is written in C; but it only supports one RGB space - sRGB. Package farver [10] is also much faster because it is written in C++. But it only supports one RGB space, which is probably sRGB.

And speaking of speed, this package is not fast enough to transform millions of RGB pixels. But it is fast enough to transform a thousand RGB patches; the function plotPatchesRGB() is available for that purpose. For the complicated ACES Transforms, it is hoped that this R package can be a completely independent check on other implementations.



1 Desktop Graphics and Basic Broadcast Television

The 16 RGB working spaces for desktop graphics at Lindbloom [5], and basic broadcast television standards (such as BT.709 and BT.2020), are illustrated in this diagram:


Figure 1.1 - Desktop Graphics, and Broadcast Television

Scene XYZ is converted to scene RGB by a 3x3 matrix \(M^{-1}\). The matrix is uniquely determined by the xy chromaticities of the RGB primaries and the XYZ of the whitepoint (3*2 + 3 = 9 total numbers); this is a fundamental fact from projective geometry, see [9] page 398. Display RGB is converted to display XYZ through multiplication by \(M\). In this package \(M\) and \(M^{-1}\) are computed with full precision; the matrices that may be published in the standard are not used. All 4 vectors - scene RGB, scene XYZ, display RGB, and display XYZ - are called linear because they are all linear functions of the spectral energy distrubution of light.

The XYZ spaces are relative and normalized so that the maximum white has Y=1; there are no physical units for XYZ. Compare this with display XYZ in ACES Color.

The terminology below is mostly taken from BT.709 [13], BT.1886 [14], and BT.2100 [15].

In this package, both linear RGB vectors are normalized to the cube [0,1]\(^3\); they are usually thought of as optical in nature. Signal RGB can be thought of as electrical in nature. It is common to think of this signal as an 8-bit number stored in the frame buffer, so the cube [0,255]\(^3\) is very commmon for signal RGB. This package allows the user to specify any positive number as the upper limit of the interval. The relevant function argument is maxSignal.

In the above figure the thin arrows indicate that the transforms are univariate and operate independently on each channel and in the same way. These transforms - OETF, EOTF, and OOTF - are called transfer functions. The thick arrows indicate that the transform is multivariate; in this diagram they are the 3x3 matrix transforms.

The conversion function from optical scene RGB (linear) to electrical signal RGB (non-linear) is called the Opto-Electronic Transfer Function (OETF). Each primary component (R,G, and B) is transformed independently and has the same OETF. In this section, the OETF is always defined as a map of [0,1] to itself. The classical OETF is the “\(1/\gamma\) power law”. For Adobe RGB, the OETF is classical with \(\gamma = 563/256 \approx 2.2\). For the most popular RGB space - sRGB - the OETF is a continuous function defined in 2 pieces. In ICC profiles, the OETF is called a Tone Response Curve (TRC), or shaper curve.

All the transfer functions in this section are continuous and strictly increasing functions that take [0,1] to itself. All transfer functions in this section are either power laws, or well-approximated by power laws. The exponent giving the best match is called the effective gamma, the approximate gamma, or the best-fit exponent for the transfer function. The function summaryRGB() displays the best-fit exponent for the transfer functions of all these basic RGB spaces.

The conversion function from electrical signal RGB (non-linear) to optical display RGB (linear) is called the Electro-Optical Transfer Function (EOTF). Each primary component (R,G, and B) is transformed independently and has the same EOTF. The classical EOTF is the “\(\gamma\) power law”. That is \(R' = R^\gamma\), where \(R'\) is display Red and \(R\) is signal Red. In this section, the EOTF is always the inverse of the OETF; i.e. EOTF=OETF\(^{-1}\).

The Opto-optical Transfer Function (OOTF) is the OETF followed by the EOTF. This is called their composition and in this User Guide we will write: \(\text{OOTF} = \text{OETF} \otimes \text{EOTF}\), following BT.2100 [15]. In the R code we use the symbols * or %X%. Since EOTF=OETF\(^{-1}\), the OOTF is the identity, i.e. trivial. In some contexts one says that the system gamma is 1, or that the end-to-end exponent is 1. This implies that scene RGB and display RGB are the same, and that scene XYZ and display XYZ are the same. The OOTF is also called the system transfer function.

theSpace = getRGB('sRGB')
par( omi=c(0,0,0,0), mai=c(0.6,0.55,0.1,0.1) )
plot( theSpace$OETF, main='', ylab='', color='red' )
plot( theSpace$EOTF, add=TRUE, color='blue' ) ; plot( theSpace$OOTF, add=TRUE, color='black' )
legend( 'topleft', legend=c('OETF','EOTF','OOTF'), bty='n', lwd=2, col=c('red','blue','black') )
Figure 1.2  The 3 Transfer Functions of sRGB

Figure 1.2 The 3 Transfer Functions of sRGB

To summarize, the RGB spaces in this section all have these simplifying properties:
  1. The scene primaries and whitepoint are the same as the display primaries and whitepoint. These are expressed in CIE xy (or possibly XYZ for the whitepoint). This is equivalent to the fact that the 2 3x3 matrices in Figure 1.1 are inverses of each other.
  2. All transfer functions are univariate, with domain and range the interval [0,1], and operate in the same way on all 3 channels.
  3. The OOTF is the identity, or equivalently, scene RGB is equal to display RGB. By property 1., this implies that scene XYZ is equal to display XYZ.

These 3 properties are enjoyed by all 16 working RGB spaces at Lindbloom [5]. This package includes three of these working spaces: sRGB, Adobe RGB, and Apple RGB. The others can be easily added by the user using installRGB(), see Appendix A for how this works. The 3 properties are also enjoyed by these RGB spaces: ProPhoto RGB, BT.709, BT.2020, and 240M, which are also included.

There are some close similarities between some of these spaces. BT.709 (created 1990) and sRGB (created 1996) have the same primaries and whitepoint, but different transfer functions. sRGB copied its primaries and whitepoint from BT.709. BT.709 and BT.2020 have very close transfer functions, differing only in precision. HD+2.4 has the same primaries and OETF as BT.709, but a different EOTF.

Regarding property 3, it turns out that color reproduction for human vision is better when property 3 is not satisfied. In the next section we present an RGB space with a non-trivial OOTF, i.e. where the system gamma is not 1.



2 Advanced Broadcast Television

Due to the Hunt effect and the Stevens effect, color reproduction for human vision is better when the OOTF is not the identity. For an illustration see BT.2390 [16] Figure 8, and for further discussion see [7] and [12]. The recommendation in BT.1886 [14] is to use the OETF from BT.709 [13] and for the EOTF use a pure power law with \(\gamma=2.4\) (with optional black offset). The resulting RGB space “HD+2.4” is installed like this:

prim  = matrix( c(0.64,0.33,  0.30,0.60,  0.15,0.06,  0.3127,0.3290), 4, 2, byrow=TRUE )
installRGB( 'HD+2.4', scene=prim, OETF=BT.709.EOTF^-1, EOTF=BT.1886.EOTF(), overwrite=TRUE )  

And the resulting transfer functions look like this:

theSpace = getRGB('HD+2.4')
par( omi=c(0,0,0,0), mai=c(0.6,0.55,0.1,0.1) )
plot( theSpace$OETF, main='', ylab='', color='red' )
plot( theSpace$EOTF, add=TRUE, color='blue' ) ; plot( theSpace$OOTF, add=TRUE, color='black' )
legend( 'topleft', legend=c('OETF','EOTF','OOTF'), bty='n', lwd=2, col=c('red','blue','black') )
Figure 2.1  The 3 Transfer Functions of HD+2.4

Figure 2.1 The 3 Transfer Functions of HD+2.4

For the EOTF as given \(\gamma = 2.4\) exactly. For the OETF \(\gamma \approx 1/1.96\), and for the OOTF \(\gamma \approx 1.21\) (both are approximate).

To summarize, this advanced RGB space has a non-trivial OOTF, but it still has these 2 simplifying properties:
  1. The scene primaries and whitepoint are the same as the display primaries and whitepoint.
    These are expressed in CIE xy (or possibly XYZ for the whitepoint). This is equivalent to the fact that the 2 3x3 matrices in Figure 1.1 are inverses of each other.
  2. All transfer functions are univariate, with domain and range the interval [0,1], and operate in the same way on all 3 channels.

In section 4 we will discuss ACES Color, which satisfies neither of these. But first, it is useful to discuss the transfer function implementation in more detail.


3 The TransferFunction S3 Class

Like most modern languages, R treats functions as “first-class citizens”. This allows us to “package” a function and its inverse function in a single object (a list) together with the domain and range of the function. The function must be injective, though this is only checked if the dimension is 1. What we have just described is a so-called elementary transfer function which is not exported and not accessible to the user. The inverse function is actually optional, and if the dimension is 1 an approximate inverse is constructed automatically (using stats::splinefun()). The dimension N is determined by the domain and range, which are 2xN matrices. Each matrix defines a (finite) box in R\(^N\).

A TransferFunction is a list of elementary transfer functions. The constructor creates a list of length 1, and longer lists are created with composition() or equivalent infix operators: *, %X%, %O%, etc. In this User Guide we use only * and the symbol \(\otimes\) (from BT.2100 [15]).

The package provides a number of built-in TransferFunction objects that are common in desktop publishing, broadcast television, and cinema. Some of these are parameterized, such as the function power.EOTF() that takes an argument for the power-law exponent \(\gamma\). The functions composition() and inverse() allow building of more complex TransferFunctions from these simpler built-in ones.

Here is a simple example:

# create the squaring function on [0,1]
squaring  = TransferFunction( function(x) {x*x}, sqrt, domain=c(0,1), range=c(0,1) )
par( omi=c(0,0,0,0), mai=c(0.6,0.55,0.1,0.1) )
plot( squaring, main='', color='red' )
plot( inverse(squaring), add=TRUE, color='blue' )  # inverse is sqrt()
#   in the next line, power.EOTF(2) is also squaring, so comp should be identity
comp = squaring * inverse( power.EOTF(2.0) )
plot( comp, add=TRUE, color='black' )    
Figure 3.1  Building TransferFunctions from Simpler Parts

Figure 3.1 Building TransferFunctions from Simpler Parts

transfer( comp, seq(0,1,length.out=11) )  # verify that comp is the identity
##  [1] 0.0 0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 1.0

As in the above example, the transfer() method is used to actually transform numbers. Other useful methods are validate() to run a sequence of tests, print() to print basic properties (e.g. dimension, domain, and range) and also perform validation, and metadata() to store user-specific data with the object. There is inverse() which returns the inverse and equivalent postfix operator ^-1. See the man pages for more TransferFunction methods.

All the examples in this section are univariate, but multivariate functions are allowed. The rules for constructing univariate and multivariate TransferFunctions are a little different; again see the man pages. For multivariate examples, see the next section.

If one takes Figure 1.1 and removes the XYZ spaces, the remaining spaces form this commutative triangle:


Figure 3.2 - The Three Basic Transfer Functions

In the function installRGB(), the 3 transfer function arguments - OETF, EOTF, OOTF - can be ‘given’ or ‘not given’ (NULL). This yields 8 combinations, but only 6 are valid, as given in this table:

OETF EOTF OOTF installRGB() Action
given given given input is INVALID
given given - OOTF := OETF * EOTF
given - given EOTF := OETF^-1 * OOTF
- given given OETF := OOTF * EOTF^-1
given - - EOTF := OETF^-1 and OOTF := identity.TF
- given - OETF := EOTF^-1 and OOTF := identity.TF
- - given input is INVALID
- - - OETF = EOTF = OOTF := identity.TF

In the table NULL is replaced by -. If all 3 are given, it is INVALID because the triangle may not commute. If 2 functions are given, the 3rd is computed from those 2. If only 1 function is given, and it is EOTF or OETF, then it makes sense to make the other one the inverse of the given one, so that the OOTF is the identity. If only the OOTF is given, it is INVALID because there is no well-defined way to define the other two. If none are given, as in the last row, this might be useful for testing direct conversion between signal RGB and XYZ. The table contains a few cases where the inverse is used, but if the corresponding function is not invertible, installRGB() fails.




4 ACES Color - The Output Side

The Academy Color Encoding System is designed to seamlessly integrate digital video from any source, such as CGI, digital cinema cameras, and scanned celluloid film. See Academy Color Encoding System [11] and ACES Output Transform Details [17]. All the R code for this section is based on that in aces-dev [8]; this is ACES v1.1 and subject to revision in the future.

The output side of ACES Color processing, is illustrated in this diagram:


Figure 4.1 - ACES Color Output (display)

ACES RGB (technically ACES2065-1 color space with AP0 primaries) is linear and scene-referred, with imaginary primaries, see [11]. The complications of getting from camera signal to ACES RGB, often with a preferred camera “look”, is the input (capture) side of ACES (with IDT, LMT, CLF, …) and is currently outside the scope of this package. Unlike the RGB spaces in sections 1 and 2, these RGB values often exceed 1.0 for ordinary scenes. A perfect white diffuser in the scene is designed to be encoded with RGB=(1,1,1) and specular highlights can easily exceed this.

WARNING: What we call (non-linear) signal RGB is called Display RGB in ACES Output Transform Details [17]. And what we call (linear) display RGB does not appear in [17].

In the above figure the thin arrow for EOTF indicates that the transform is univariate and operates independently on each channel and in the same way. Exception: the EOTF in the last transform in Appendix B - the hybrid log-gamma - is multivariate. For the other transforms, he thick arrows indicate that they are multivariate and 3D.

OCES RGB (Output Color Encoding Specification RGB) is display-referred but the display is not real - it is theoretical. This theoretical display has a dynamic range of 100 million to 1 and can reproduce any color. It uses the same imaginary AP0 primaries as ACES RGB.

The RRT is the Reference Rendering Transform from ACES to OCES; it is fixed and
“… the RRT is intended to account for the perceptual factors required to convert the scene referred ACES images to an output referred encoding associated with an very high dynamic range, extremely wide color gamut device in a dark surround.” - Alex Forsythe [3]

The ODT is the Output Device Transform for a real device. It includes the display primaries and whitepoint and other factors. Real world displays will still need calibration to exactly match the ideal color space of each ODT.

The ODT may be split further using the concept of a Partial Output Device Transform or PODT, which is unique to this package. The split is \(\text{ODT} = \text{PODT} \otimes \text{EOTF}^{-1}\) for some standard \(\text{EOTF}\). The function general.PODT() returns a PODT which is used for all of the ODT examples in Section 7.1. In these examples \(\text{OETF} = \text{RRT} \otimes \text{ODT} = \text{RRT} \otimes \text{PODT} \otimes \text{EOTF}^{-1}\). This ODT split is not shown in the above figure.

In the examples in Section 7.2, the OOTF and EOTF are given and \(\text{OETF} := \text{OOTF} \otimes \text{EOTF}^{-1}\). The function general.OOTF() is used for these examples, and there is no explicit RRT or ODT.

Since the display primaries and whitepoint are different than those of ACES RGB, the matrices \(\text{XYZtoRGB}_\text{scene}\) and \(\text{RGBtoXYZ}_\text{display}\) are not inverses of each other, as they are in Figure 1.1. Another difference with Figure 1.1 is that ACES display XYZ has physical units; the Y coordinate has units of \(cd/m^2\) (aka \(nit\)).

Because of the complexity of ACES Color, calling a single instance of ACES an RGB “space” does not really do it justice. A more appropriate name might be an RGB “workflow” or “pipeline”.

4.1 Acknowledgements

I would like to thank Scott Dyer for supplying the high-precision data in the file test_values.txt in the folder tests, and Alex Forsythe for his encouragement to “dig into” ACES.





5 References

[1]
DANIEL ADLER, Duncan Murdoch and OTHERS. rgl: 3D visualization using OpenGL [online]. 2018. Available at: https://cran.r-project.org/package=rgl
[2]
DAVIS, Glenn. spacesXYZ: CIE XYZ and some of its derived color spaces [online]. 2018. Available at: https://cran.r-project.org/package=spacesXYZ
[3]
FORSYTHE, Alex. Color Appearance Models, Interpretation Options, and ODTs [online]. 2017. Available at: https://community.acescentral.com/t/color-appearance-models-interpretation-options-and-odts/1257/3. discussion forum
[4]
IHAKA, Ross, MURRELL, Paul, HORNIK, Kurt, FISHER, Jason C. and ZEILEIS, Achim. colorspace: Color space manipulation [online]. 2016. Available at: https://CRAN.R-project.org/package=colorspace
[5]
LINDBLOOM, Bruce. RGB working space information [online]. no date. Available at: http://brucelindbloom.com/index.html?WorkingSpaceInfo.html
[6]
OLAF MERSMANN, Rainer Hurling, Claudia Beleites. microbenchmark: Accurate timing functions [online]. 2018. Available at: https://cran.r-project.org/package=microbenchmark
[7]
POYNTON, Charles A. Digital Video and HD: Algorithms and Interfaces. B.m.: Morgan Kaufmann, 2012. Morgan kaufmann series in computer graphics and geometric modeling. ISBN 9780123919267.
[8]
SCOTT DYER. Academy color encoding system developer resources. B.m.: https://github.com/ampas/aces-dev; GitHub. 2018
[9]
SEMPLE, J. G. and KNEEBONE, G. T. Algebraic Projective Geometry. B.m.: Clarendon Press, 1952.
[10]
THOMAS LIN PEDERSEN, Berendea Nicolae and FRANÇOIS, Romain. farver: Vectorised colour conversion and comparison [online]. 2018. Available at: https://CRAN.R-project.org/package=farver
[11]
WIKIPEDIA CONTRIBUTORS. Academy color encoding system — Wikipedia, the free encyclopedia [online]. 2018. Available at: https://en.wikipedia.org/w/index.php?title=Academy_Color_Encoding_System. [Online; accessed 19-January-2019]
[12]
WIKIPEDIA CONTRIBUTORS. Color appearance model — Wikipedia, the free encyclopedia [online]. 2018. Available at: https://en.wikipedia.org/wiki/Color_appearance_model
[13]
BT.709. Parameter values for the HDTV standards for production and international programme exchange. Standard. 2015.
[14]
BT.1886. Reference electro-optical transfer function for flat panel displays used in HDTV studio production. Standard. 2011.
[15]
BT.2100. Image parameter values for high dynamic range television for use in production and international programme exchange. Standard. 2017.
[16]
BT.2390-4. High dynamic range television for production and international programme exchange. Standard. 2018.
[17]
ACES Output Transform Details - TB-2018-002 [online]. DRAFT Standard. 2018. Available at: https://community.acescentral.com/t/request-for-review-and-comments-on-v0-1-draft-of-aces-output-transform-details/1457





6 Appendix A - Built-in RGB Spaces

The package comes with a dictionary of 8 built-in RGB spaces; which are loaded from sysdata.rda. The code below shows how they are installed in that dictionary. Note that the argument scene can be a 4x2 matrix, or a list with a 6-vector and a 2-vector. Also note that the argument display is omitted, and the data is copied from scene. For details on this, see the man page for installRGB().

sRGB
installRGB( 'sRGB', scene=REC709_PRI, EOTF=sRGB.EOTF )

AdobeRGB
prim    = c(0.64,0.33,  0.21,0.71,  0.15,0.06)
white   = c( 0.3127, 0.3290 )      # D65
installRGB( 'AdobeRGB', scene=list(prim,white), EOTF=563/256 )

ProPhotoRGB
prim    = c(0.7347,0.2653,  0.1596,0.8404,  0.0366,0.0001)
white   = c( 0.3457,0.3585 )    # D50
installRGB( 'ProPhotoRGB', scene=list(prim,white), EOTF=ProPhotoRGB.EOTF )

AppleRGB
prim    = c(0.625,0.34,  0.28,0.595, 0.155,0.07)
white   = c( 0.3127, 0.3290 ) 
installRGB( 'AppleRGB', scene=list(prim,white), EOTF=1.8 )

BT.709
installRGB( 'BT.709', scene=REC709_PRI, EOTF=BT.709.EOTF )

BT.2020
installRGB( 'BT.2020', scene=REC2020_PRI, EOTF=BT.2020.EOTF )

240M
prim    = c(0.64,0.34,  0.31,0.595,  0.155,0.07 )
white   = c( 0.3127, 0.3290 )
installRGB( '240M', scene=list(prim,white), EOTF=SMPTE.240M.EOTF )

HD+2.4
installRGB( "HD+2.4", scene=REC709_PRI, OETF=BT.709.EOTF^-1, EOTF=2.4 )  



7 Appendix B - Sample ODTs and Output Transforms for ACES

This Appendix has spacesRGB expressions for the output transforms available as presets to the user in ACES v1.1, see ACES Output Transform Details [17].

For examples of these transforms “in action” see the files test-ACES.R and test_values.txt in the folder tests.

7.1 Output Transforms with given OETF and EOTF

In [17] Sections 5.1 to 7.2 are 16 Output Transforms whose transfer functions are defined by their OETF and EOTF, and then \(\text{OOTF} := \text{OETF} \otimes \text{EOTF}\). These tables present the OETF and EOTF using the S3 TransferFunction objects available in spacesRGB. The display primaries and whitepoints are easily extracted from the expressions for the OETF. Note that in the case of DCDM (Digital Cinema Distribution Master) in sections 5.7-9, which uses XYZ as its primaries, the argument display_pri=NULL.

Section 5.1 P3-D60
ACES Transform ID ODT.Academy.P3D60 48nits.a1.0.3
OETF RRT.TF * general.PODT( P3D60_PRI, Ymax=48 ) * power.OETF( 2.6 )
EOTF power.EOTF( 2.6 )
Section 5.2 P3-D65
ACES Transform ID ODT.Academy.P3D65 48nits.a1.1
OETF RRT.TF * general.PODT( P3D65_PRI, Ymax=48 ) * power.OETF( 2.6 )
EOTF power.EOTF( 2.6 )
Section 5.3 P3-D65 (D60 Simulation)
ACES Transform ID ODT.Academy.P3D65 D60sim 48nits.a1.1
OETF RRT.TF * general.PODT(P3D65_PRI,Ymax=48,observer=P3D60_PRI['W',]) * power.OETF(2.6)
EOTF power.EOTF( 2.6 )
Section 5.4 P3-D65 (Rec. 709 Limited)
ACES Transform ID ODT.Academy.P3D65 Rec709limited 48nits.a1.1
OETF RRT.TF * general.PODT(P3D65_PRI,Ymax=48,limiting_pri=REC709_PRI) * power.OETF(2.6)
EOTF power.EOTF( 2.6 )
Section 5.5 P3-DCI (D60 Simulation)
ACES Transform ID ODT.Academy.P3DCI D60sim 48nits.a1.1
OETF RRT.TF * general.PODT(P3DCI_PRI,Ymax=48,observer=P3D60_PRI['W',]) * power.OETF(2.6)
EOTF power.EOTF( 2.6 )
Section 5.6 P3-DCI (D65 Simulation)
ACES Transform ID ODT.Academy.P3DCI D65sim 48nits.a1.1
OETF RRT.TF * general.PODT(P3DCI_PRI,Ymax=48,observer=P3D65_PRI['W',]) * power.OETF(2.6)
EOTF power.EOTF( 2.6 )
Section 5.7 DCDM
ACES Transform ID ODT.Academy.DCDM.a1.0.3
OETF RRT.TF * general.PODT(NULL,Ymax=48) * DCDM.EOTF^-1
EOTF DCDM.EOTF
Section 5.8 DCDM (P3-D60 Limited)
ACES Transform ID ODT.Academy.DCDM P3D60limited.a1.1
OETF RRT.TF * general.PODT(NULL,Ymax=48,limiting_pri=P3D60_PRI) * DCDM.EOTF^-1
EOTF DCDM.EOTF
Section 5.9 DCDM (P3-D65 Limited)
ACES Transform ID ODT.Academy.DCDM P3D65limited.a1.1
OETF RRT.TF * general.PODT(NULL,Ymax=48,limiting_pri=P3D65_PRI) * DCDM.EOTF^-1
EOTF DCDM.EOTF
Section 6.1 Rec.709
ACES Transform ID ODT.Academy.Rec709 100nits dim.a1.0.3
OETF RRT.TF * general.PODT(REC709_PRI,Ymax=100,surround='dim') * BT.1886.EOTF()^-1
EOTF BT.1886.EOTF()
Section 6.2 Rec.709 (D60 Simulation)
ACES Transform ID ODT.Academy.Rec709 D60sim 100nits dim.a1.0.3
OETF RRT.TF * general.PODT(REC709_PRI,Ymax=100,observer=P3D60_PRI['W',],surround='dim') * BT.1886.EOTF()^-1
EOTF BT.1886.EOTF()
Section 6.3 Rec.2020
ACES Transform ID ODT.Academy.Rec2020 100nits dim.a1.0.3
OETF RRT.TF * general.PODT(REC2020_PRI,Ymax=100,surround='dim') * BT.1886.EOTF()^-1
EOTF BT.1886.EOTF()
Section 6.4 Rec.2020 (P3-D65 Limited)
ACES Transform ID ODT.Academy.Rec2020 P3D65limited 100nits dim.a1.1
OETF RRT.TF * general.PODT(REC2020_PRI,Ymax=100,limit=P3D65_PRI,surround='dim') * BT.1886.EOTF()^-1
EOTF BT.1886.EOTF()
Section 6.5 Rec.2020 (Rec.709 Limited)
ACES Transform ID ODT.Academy.Rec2020 P3D65limited 100nits dim.a1.1
OETF RRT.TF * general.PODT(REC2020_PRI,limit=REC709_PRI,surround='dim') * BT.1886.EOTF()^-1
EOTF BT.1886.EOTF()
Section 7.1 sRGB
ACES Transform ID ODT.Academy.RGBmonitor 100nits dim.a1.0.1
OETF RRT.TF * general.PODT(REC709_PRI,Ymax=100,surround='dim') * sRGB.EOTF^-1
EOTF sRGB.EOTF
Section 7.2 sRGB (D60 Simulation)
ACES Transform ID ODT.Academy.RGBmonitor D60sim 100nits dim.a1.0.1
OETF RRT.TF * general.PODT(REC709_PRI,Ymax=100,observer=P3D60_PRI['W',],surround='dim') * sRGB.EOTF^-1
EOTF sRGB.EOTF

None of these are pre-installed in the package. The resulting RGB space for last one - sRGB (D60 Simulation) - can be installed like this:

OETF = RRT.TF * general.PODT(REC709_PRI,observer=P3D60_PRI['W',],surround='dim') * sRGB.EOTF^-1 
installRGB( 'sRGB_D60sim', scene=AP0_PRI, EOTF=sRGB.EOTF, OETF=OETF )

And the other 15 spaces can be installed in a similar way. Note that the display primaries REC709_PRI do not have to be passed to installRGB(), since they are automatically picked from the metadata of the OETF. See the man page for installRGB().



7.2 Output Transforms with given OOTF and EOTF

In [17] Sections 8.1 to 9.4 are 5 Output Transforms whose transfer functions are defined by their OOTF and EOTF, and then \(\text{OETF} := \text{OOTF} \otimes \text{EOTF}^{-1}\). These tables present the OOTF and EOTF using the S3 TransferFunction objects available in spacesRGB. The display primaries and whitepoints are easily extracted from the expressions for the OOTF.

Section 8.1 P3-D65 ST2084 (108 nits)
ACES Transform ID RRTODT.Academy.P3D65 108nits 7.2nits ST2084.a1.1
OOTF general.OOTF( disp=P3D65_PRI, Ymid=7.2, Ymax=108 )
EOTF PQ.EOTF(10000/108)
Section 9.1 Rec.2020 ST2084 (1000 nits)
ACES Transform ID RRTODT.Academy.Rec2020 1000nits 15nits ST2084.a1.1
OOTF general.OOTF( disp=REC2020_PRI, Ymid=15, Ymax=1000 )
EOTF PQ.EOTF(10000/1000)
Section 9.2 Rec.2020 ST2084 (2000 nits)
ACES Transform ID RRTODT.Academy.Rec2020 2000nits 15nits ST2084.a1.1
OOTF general.OOTF( disp=REC2020_PRI, Ymid=15, Ymax=2000 )
EOTF PQ.EOTF(10000/2000)
Section 9.3 Rec.2020 ST2084 (4000 nits)
ACES Transform ID RRTODT.Academy.Rec2020 4000nits 15nits ST2084.a1.1
OOTF general.OOTF( disp=REC2020_PRI, Ymid=15, Ymax=4000 )
EOTF PQ.EOTF(10000/4000)
Section 9.4 Rec.2020 HLG (1000 nits)
ACES Transform ID RRTODT.Academy.Rec2020 1000nits 15nits HLG.a1.1
OOTF general.OOTF( disp=REC2020_PRI, Ymid=15, Ymax=1000 )
EOTF HLG.OETF()^-1 * HLG.OOTF(Lw=1000/1000)

In the final one, HLG.OETF() is univariate and HLG.OOTF(Lw=1) is multivariate with dimension 3. Thus the EOTF also has dimension 3. This is the only one of the 21 preset transforms that has a multivariate EOTF. The resulting RGB space can be installed like this:

installRGB( 'Rec.2020_HLG', scene=AP0_PRI, OOTF=general.OOTF(REC2020_PRI,Ymid=15,Ymax=1000),
                        EOTF=HLG.OETF()^-1 * HLG.OOTF(Lw=1) )

And the other 4 spaces can be installed in a similar way. Note that the display primaries REC2020_PRI do not have to be passed to installRGB(), since they are automatically picked from the metadata of the OOTF. See the man page for installRGB().



8 Session Information

R version 4.3.2 (2023-10-31 ucrt)
Platform: x86_64-w64-mingw32/x64 (64-bit)
Running under: Windows 10 x64 (build 19045)

Matrix products: default


locale:
[1] LC_COLLATE=C                           LC_CTYPE=English_United States.utf8   
[3] LC_MONETARY=English_United States.utf8 LC_NUMERIC=C                          
[5] LC_TIME=English_United States.utf8    

time zone: America/New_York
tzcode source: internal

attached base packages:
[1] stats     graphics  grDevices utils     datasets  methods   base     

other attached packages:
[1] spacesRGB_1.5-0

loaded via a namespace (and not attached):
 [1] digest_0.6.31         R6_2.5.1              microbenchmark_1.4.10 spacesXYZ_1.2-1      
 [5] fastmap_1.1.1         xfun_0.39             cachem_1.0.8          knitr_1.42           
 [9] htmltools_0.5.5       rmarkdown_2.21        cli_3.6.1             sass_0.4.6           
[13] jquerylib_0.1.4       compiler_4.3.2        highr_0.10            tools_4.3.2          
[17] evaluate_0.21         bslib_0.4.2           yaml_2.3.7            rlang_1.1.1          
[21] jsonlite_1.8.4