Moved LUT generation into a separate module
[OpenColorIO-Configs.git] / aces_1.0.0 / python / create_aces_config.py
index 29e3f8d..f61002e 100755 (executable)
@@ -49,6 +49,7 @@ import OpenImageIO as oiio
 import PyOpenColorIO as OCIO
 
 import process
+import generateLUT as genlut
 
 #
 # Utility functions
@@ -74,8 +75,6 @@ def setConfigDefaultRoles( config,
     if reference: config.setRole( OCIO.Constants.ROLE_REFERENCE, reference )
     if scene_linear: config.setRole( OCIO.Constants.ROLE_SCENE_LINEAR, scene_linear )
     if texture_paint: config.setRole( OCIO.Constants.ROLE_TEXTURE_PAINT, texture_paint )
-    
-    
 
 # Write config to disk
 def writeConfig( config, configPath, sanityCheck=True ):
@@ -92,254 +91,6 @@ def writeConfig( config, configPath, sanityCheck=True ):
     fileHandle.write( config.serialize() )
     fileHandle.close()
 
-#
-# Functions used to generate LUTs using CTL transforms
-#
-def generate1dLUTImage(ramp1dPath, resolution=1024, minValue=0.0, maxValue=1.0):
-    #print( "Generate 1d LUT image - %s" % ramp1dPath)
-
-    # open image
-    format = os.path.splitext(ramp1dPath)[1]
-    ramp = oiio.ImageOutput.create(ramp1dPath)
-
-    # set image specs
-    spec = oiio.ImageSpec()
-    spec.set_format( oiio.FLOAT )
-    #spec.format.basetype = oiio.FLOAT
-    spec.width = resolution
-    spec.height = 1
-    spec.nchannels = 3
-
-    ramp.open (ramp1dPath, spec, oiio.Create)
-
-    data = array.array("f", "\0" * spec.width * spec.height * spec.nchannels * 4)
-    for i in range(resolution):
-        value = float(i)/(resolution-1) * (maxValue - minValue) + minValue
-        data[i*spec.nchannels +0] = value
-        data[i*spec.nchannels +1] = value
-        data[i*spec.nchannels +2] = value
-
-    ramp.write_image(spec.format, data)
-    ramp.close()
-
-# Credit to Alex Fry for the original single channel version of the spi1d writer
-def WriteSPI1D(filename, fromMin, fromMax, data, entries, channels):
-    f = file(filename,'w')
-    f.write("Version 1\n")
-    f.write("From %f %f\n" % (fromMin, fromMax))
-    f.write("Length %d\n" % entries)
-    f.write("Components %d\n" % (min(3, channels)) )
-    f.write("{\n")
-    for i in range(0, entries):
-        entry = ""
-        for j in range(0, min(3, channels)):
-            entry = "%s %s" % (entry, data[i*channels + j])
-        f.write("        %s\n" % entry)
-    f.write("}\n")
-    f.close()
-
-def generate1dLUTFromImage(ramp1dPath, outputPath=None, minValue=0.0, maxValue=1.0):
-    if outputPath == None:
-        outputPath = ramp1dPath + ".spi1d"
-
-    # open image
-    ramp = oiio.ImageInput.open( ramp1dPath )
-
-    # get image specs
-    spec = ramp.spec()
-    type = spec.format.basetype
-    width = spec.width
-    height = spec.height
-    channels = spec.nchannels
-
-    # get data
-    # Force data to be read as float. The Python API doesn't handle half-floats well yet.
-    type = oiio.FLOAT
-    data = ramp.read_image(type)
-
-    WriteSPI1D(outputPath, minValue, maxValue, data, width, channels)
-
-def generate3dLUTImage(ramp3dPath, resolution=32):
-    args = ["--generate", "--cubesize", str(resolution), "--maxwidth", str(resolution*resolution), "--output", ramp3dPath]
-    lutExtract = process.Process(description="generate a 3d LUT image", cmd="ociolutimage", args=args)
-    lutExtract.execute()    
-
-def generate3dLUTFromImage(ramp3dPath, outputPath=None, resolution=32):
-    if outputPath == None:
-        outputPath = ramp3dPath + ".spi3d"
-
-    args = ["--extract", "--cubesize", str(resolution), "--maxwidth", str(resolution*resolution), "--input", ramp3dPath, "--output", outputPath]
-    lutExtract = process.Process(description="extract a 3d LUT", cmd="ociolutimage", args=args)
-    lutExtract.execute()    
-
-def applyCTLToImage(inputImage, 
-    outputImage, 
-    ctlPaths=[], 
-    inputScale=1.0, 
-    outputScale=1.0, 
-    globalParams={},
-    acesCTLReleaseDir=None):
-    if len(ctlPaths) > 0:
-        ctlenv = os.environ
-        if acesCTLReleaseDir != None:
-            ctlModulePath = "%s/utilities" % acesCTLReleaseDir
-            ctlenv['CTL_MODULE_PATH'] = ctlModulePath
-
-        args = []
-        for ctl in ctlPaths:
-            args += ['-ctl', ctl]
-        args += ["-force"]
-        #args += ["-verbose"]
-        args += ["-input_scale", str(inputScale)]
-        args += ["-output_scale", str(outputScale)]
-        args += ["-global_param1", "aIn", "1.0"]
-        for key, value in globalParams.iteritems():
-            args += ["-global_param1", key, str(value)]
-        args += [inputImage]
-        args += [outputImage]
-
-        #print( "args : %s" % args )
-
-        ctlp = process.Process(description="a ctlrender process", cmd="ctlrender", args=args, env=ctlenv )
-
-        ctlp.execute()
-
-def convertBitDepth(inputImage, outputImage, depth):
-    args = [inputImage, "-d", depth, "-o", outputImage]
-    convert = process.Process(description="convert image bit depth", cmd="oiiotool", args=args)
-    convert.execute()    
-
-def generate1dLUTFromCTL(lutPath, 
-    ctlPaths, 
-    lutResolution=1024, 
-    identityLutBitDepth='half', 
-    inputScale=1.0, 
-    outputScale=1.0,
-    globalParams={},
-    cleanup=True,
-    acesCTLReleaseDir=None,
-    minValue=0.0,
-    maxValue=1.0):
-    #print( lutPath )
-    #print( ctlPaths )
-
-    lutPathBase = os.path.splitext(lutPath)[0]
-
-    identityLUTImageFloat = lutPathBase + ".float.tiff"
-    generate1dLUTImage(identityLUTImageFloat, lutResolution, minValue, maxValue)
-
-    if identityLutBitDepth != 'half':
-        identityLUTImage = lutPathBase + ".uint16.tiff"
-        convertBitDepth(identityLUTImageFloat, identityLUTImage, identityLutBitDepth)
-    else:
-        identityLUTImage = identityLUTImageFloat
-
-    transformedLUTImage = lutPathBase + ".transformed.exr"
-    applyCTLToImage(identityLUTImage, transformedLUTImage, ctlPaths, inputScale, outputScale, globalParams, acesCTLReleaseDir)
-
-    generate1dLUTFromImage(transformedLUTImage, lutPath, minValue, maxValue)
-
-    if cleanup:
-        os.remove(identityLUTImage)
-        if identityLUTImage != identityLUTImageFloat:
-            os.remove(identityLUTImageFloat)
-        os.remove(transformedLUTImage)
-
-def correctLUTImage(transformedLUTImage, correctedLUTImage, lutResolution):
-    # open image
-    transformed = oiio.ImageInput.open( transformedLUTImage )
-
-    # get image specs
-    transformedSpec = transformed.spec()
-    type = transformedSpec.format.basetype
-    width = transformedSpec.width
-    height = transformedSpec.height
-    channels = transformedSpec.nchannels
-
-    # rotate or not
-    if width != lutResolution * lutResolution or height != lutResolution:
-        print( "Correcting image as resolution is off. Found %d x %d. Expected %d x %d" % (width, height, lutResolution * lutResolution, lutResolution) )
-        print( "Generating %s" % correctedLUTImage)
-
-        #
-        # We're going to generate a new correct image
-        #
-
-        # Get the source data
-        # Force data to be read as float. The Python API doesn't handle half-floats well yet.
-        type = oiio.FLOAT
-        sourceData = transformed.read_image(type)
-
-        format = os.path.splitext(correctedLUTImage)[1]
-        correct = oiio.ImageOutput.create(correctedLUTImage)
-
-        # set image specs
-        correctSpec = oiio.ImageSpec()
-        correctSpec.set_format( oiio.FLOAT )
-        correctSpec.width = height
-        correctSpec.height = width
-        correctSpec.nchannels = channels
-
-        correct.open (correctedLUTImage, correctSpec, oiio.Create)
-
-        destData = array.array("f", "\0" * correctSpec.width * correctSpec.height * correctSpec.nchannels * 4)
-        for j in range(0, correctSpec.height):
-            for i in range(0, correctSpec.width):
-                for c in range(0, correctSpec.nchannels):
-                    #print( i, j, c )
-                    destData[correctSpec.nchannels*correctSpec.width*j + correctSpec.nchannels*i + c] = sourceData[correctSpec.nchannels*correctSpec.width*j + correctSpec.nchannels*i + c]
-
-        correct.write_image(correctSpec.format, destData)
-        correct.close()
-    else:
-        #shutil.copy(transformedLUTImage, correctedLUTImage)
-        correctedLUTImage = transformedLUTImage
-
-    transformed.close()
-
-    return correctedLUTImage
-
-def generate3dLUTFromCTL(lutPath, 
-    ctlPaths, 
-    lutResolution=64, 
-    identityLutBitDepth='half', 
-    inputScale=1.0,
-    outputScale=1.0, 
-    globalParams={},
-    cleanup=True,
-    acesCTLReleaseDir=None):
-    #print( lutPath )
-    #print( ctlPaths )
-
-    lutPathBase = os.path.splitext(lutPath)[0]
-
-    identityLUTImageFloat = lutPathBase + ".float.tiff"
-    generate3dLUTImage(identityLUTImageFloat, lutResolution)
-
-
-    if identityLutBitDepth != 'half':
-        identityLUTImage = lutPathBase + "." + identityLutBitDepth + ".tiff"
-        convertBitDepth(identityLUTImageFloat, identityLUTImage, identityLutBitDepth)
-    else:
-        identityLUTImage = identityLUTImageFloat
-
-    transformedLUTImage = lutPathBase + ".transformed.exr"
-    applyCTLToImage(identityLUTImage, transformedLUTImage, ctlPaths, inputScale, outputScale, globalParams, acesCTLReleaseDir)
-
-    correctedLUTImage = lutPathBase + ".correct.exr"
-    correctedLUTImage = correctLUTImage(transformedLUTImage, correctedLUTImage, lutResolution)    
-
-    generate3dLUTFromImage(correctedLUTImage, lutPath, lutResolution)
-
-    if cleanup:
-        os.remove(identityLUTImage)
-        if identityLUTImage != identityLUTImageFloat:
-            os.remove(identityLUTImageFloat)
-        os.remove(transformedLUTImage)
-        if correctedLUTImage != transformedLUTImage:
-            os.remove(correctedLUTImage)
-        #os.remove(correctedLUTImage)
-
 def generateOCIOTransform(transforms):
     #print( "Generating transforms")
 
@@ -614,7 +365,7 @@ def generateLUTs(odtInfo, lmtInfo, shaperName, acesCTLReleaseDir, lutDir, lutRes
             '%s/ACEScg/ACEScsc.ACES_to_ACEScg.a1.0.0.ctl' % acesCTLReleaseDir
         ]
         lut = "%s_to_ACES.spi1d" % name
-        generate1dLUTFromCTL( lutDir + "/" + lut, 
+        genlut.generate1dLUTFromCTL( lutDir + "/" + lut, 
             ctls, 
             lutResolution1d, 
             'float', 
@@ -665,7 +416,7 @@ def generateLUTs(odtInfo, lmtInfo, shaperName, acesCTLReleaseDir, lutDir, lutRes
             '%s/ACEScg/ACEScsc.ACES_to_ACEScg.a1.0.0.ctl' % acesCTLReleaseDir
         ]
         lut = "%s_to_aces.spi1d" % name
-        generate1dLUTFromCTL( lutDir + "/" + lut, 
+        genlut.generate1dLUTFromCTL( lutDir + "/" + lut, 
             ctls, 
             lutResolution1d, 
             'uint16', 
@@ -818,7 +569,7 @@ def generateLUTs(odtInfo, lmtInfo, shaperName, acesCTLReleaseDir, lutDir, lutRes
                 data.append(cid_to_rle(x))
 
             lut = 'ADX_CID_to_RLE.spi1d'
-            WriteSPI1D(lutDir + "/" + lut, RANGE[0], RANGE[1], data, NUM_SAMPLES, 1)
+            genlut.writeSPI1D(lutDir + "/" + lut, RANGE[0], RANGE[1], data, NUM_SAMPLES, 1)
 
             return lut
 
@@ -893,7 +644,7 @@ def generateLUTs(odtInfo, lmtInfo, shaperName, acesCTLReleaseDir, lutDir, lutRes
                 data[c] = cineonToLinear(1023.0*c/(lutResolution1d-1))
 
             lut = "CineonLog_to_linear.spi1d"
-            WriteSPI1D(lutDir + "/" + lut, 0.0, 1.0, data, lutResolution1d, 1)
+            genlut.writeSPI1D(lutDir + "/" + lut, 0.0, 1.0, data, lutResolution1d, 1)
 
             cs.toReferenceTransforms.append( {
                 'type':'lutFile', 
@@ -905,17 +656,17 @@ def generateLUTs(odtInfo, lmtInfo, shaperName, acesCTLReleaseDir, lutDir, lutRes
         if gamut == 'DRAGONcolor':
             cs.toReferenceTransforms.append( {
                 'type':'matrix',
-                'matrix':mat44FromMat33([0.532279, 0.376648, 0.091073, 
-                                        0.046344, 0.974513, -0.020860, 
+                'matrix':mat44FromMat33([0.532279,  0.376648,  0.091073, 
+                                         0.046344,  0.974513, -0.020860, 
                                         -0.053976, -0.000320, 1.054267]),
                 'direction':'forward'
             })
-        elif gamut == 'REDcolor3':
+        elif gamut == 'DRAGONcolor2':
             cs.toReferenceTransforms.append( {
                 'type':'matrix',
-                'matrix':mat44FromMat33([0.512136, 0.360370, 0.127494, 
-                                        0.070377, 0.903884, 0.025737
-                                        -0.020824, 0.017671, 1.003123]),
+                'matrix':mat44FromMat33([0.468452,  0.331484,  0.200064, 
+                                         0.040787,  0.857658,  0.101553
+                                        -0.047504, -0.000282, 1.047756]),
                 'direction':'forward'
             })
         elif gamut == 'REDcolor2':
@@ -926,6 +677,22 @@ def generateLUTs(odtInfo, lmtInfo, shaperName, acesCTLReleaseDir, lutDir, lutRes
                                         -0.105257, 0.025320, 1.079907]),
                 'direction':'forward'
             })
+        elif gamut == 'REDcolor3':
+            cs.toReferenceTransforms.append( {
+                'type':'matrix',
+                'matrix':mat44FromMat33([0.512136, 0.360370, 0.127494, 
+                                         0.070377, 0.903884, 0.025737, 
+                                        -0.020824, 0.017671, 1.003123]),
+                'direction':'forward'
+            })
+        elif gamut == 'REDcolor4':
+            cs.toReferenceTransforms.append( {
+                'type':'matrix',
+                'matrix':mat44FromMat33([0.474202, 0.333677, 0.192121, 
+                                         0.065164, 0.836932, 0.097901, 
+                                        -0.019281, 0.016362, 1.002889]),
+                'direction':'forward'
+            })
 
         cs.fromReferenceTransforms = []
         return cs
@@ -934,12 +701,18 @@ def generateLUTs(odtInfo, lmtInfo, shaperName, acesCTLReleaseDir, lutDir, lutRes
     REDlogFilmDRAGON = createREDlogFilm("DRAGONcolor", "REDlogFilm", name="REDlogFilm")
     configData['colorSpaces'].append(REDlogFilmDRAGON)
 
+    REDlogFilmDRAGON2 = createREDlogFilm("DRAGONcolor2", "REDlogFilm", name="REDlogFilm")
+    configData['colorSpaces'].append(REDlogFilmDRAGON2)
+
     REDlogFilmREDcolor2 = createREDlogFilm("REDcolor2", "REDlogFilm", name="REDlogFilm")
     configData['colorSpaces'].append(REDlogFilmREDcolor2)
 
     REDlogFilmREDcolor3 = createREDlogFilm("REDcolor3", "REDlogFilm", name="REDlogFilm")
     configData['colorSpaces'].append(REDlogFilmREDcolor3)
 
+    REDlogFilmREDcolor4 = createREDlogFilm("REDcolor4", "REDlogFilm", name="REDlogFilm")
+    configData['colorSpaces'].append(REDlogFilmREDcolor4)
+
     # Linearization only
     REDlogFilmDRAGON = createREDlogFilm("", "REDlogFilm", name="REDlogFilm")
     configData['colorSpaces'].append(REDlogFilmDRAGON)
@@ -948,12 +721,18 @@ def generateLUTs(odtInfo, lmtInfo, shaperName, acesCTLReleaseDir, lutDir, lutRes
     REDlogFilmDRAGON = createREDlogFilm("DRAGONcolor", "", name="REDlogFilm")
     configData['colorSpaces'].append(REDlogFilmDRAGON)
 
+    REDlogFilmDRAGON2 = createREDlogFilm("DRAGONcolor2", "", name="REDlogFilm")
+    configData['colorSpaces'].append(REDlogFilmDRAGON2)
+
     REDlogFilmREDcolor2 = createREDlogFilm("REDcolor2", "", name="REDlogFilm")
     configData['colorSpaces'].append(REDlogFilmREDcolor2)
 
     REDlogFilmREDcolor3 = createREDlogFilm("REDcolor3", "", name="REDlogFilm")
     configData['colorSpaces'].append(REDlogFilmREDcolor3)
 
+    REDlogFilmREDcolor4 = createREDlogFilm("REDcolor4", "", name="REDlogFilm")
+    configData['colorSpaces'].append(REDlogFilmREDcolor4)
+
     #
     # Canon-Log to ACES
     #
@@ -993,7 +772,7 @@ def generateLUTs(odtInfo, lmtInfo, shaperName, acesCTLReleaseDir, lutDir, lutRes
                 data[c] = canonLogToLinear(1023.0*c/(lutResolution1d-1))
 
             lut = "%s_to_linear.spi1d" % transferFunction
-            WriteSPI1D(lutDir + "/" + lut, 0.0, 1.0, data, lutResolution1d, 1)
+            genlut.writeSPI1D(lutDir + "/" + lut, 0.0, 1.0, data, lutResolution1d, 1)
 
             cs.toReferenceTransforms.append( {
                 'type':'lutFile', 
@@ -1156,7 +935,7 @@ def generateLUTs(odtInfo, lmtInfo, shaperName, acesCTLReleaseDir, lutDir, lutRes
                 data[c] = sLog1ToLinear(1023.0*c/(lutResolution1d-1))
 
             lut = "%s_to_linear.spi1d" % transferFunction
-            WriteSPI1D(lutDir + "/" + lut, 0.0, 1.0, data, lutResolution1d, 1)
+            genlut.writeSPI1D(lutDir + "/" + lut, 0.0, 1.0, data, lutResolution1d, 1)
 
             #print( "Writing %s" % lut)
 
@@ -1172,7 +951,7 @@ def generateLUTs(odtInfo, lmtInfo, shaperName, acesCTLReleaseDir, lutDir, lutRes
                 data[c] = sLog2ToLinear(1023.0*c/(lutResolution1d-1))
 
             lut = "%s_to_linear.spi1d" % transferFunction
-            WriteSPI1D(lutDir + "/" + lut, 0.0, 1.0, data, lutResolution1d, 1)
+            genlut.writeSPI1D(lutDir + "/" + lut, 0.0, 1.0, data, lutResolution1d, 1)
 
             #print( "Writing %s" % lut)
 
@@ -1188,7 +967,7 @@ def generateLUTs(odtInfo, lmtInfo, shaperName, acesCTLReleaseDir, lutDir, lutRes
                 data[c] = sLog3ToLinear(1023.0*c/(lutResolution1d-1))
 
             lut = "%s_to_linear.spi1d" % transferFunction
-            WriteSPI1D(lutDir + "/" + lut, 0.0, 1.0, data, lutResolution1d, 1)
+            genlut.writeSPI1D(lutDir + "/" + lut, 0.0, 1.0, data, lutResolution1d, 1)
 
             #print( "Writing %s" % lut)
 
@@ -1370,7 +1149,7 @@ def generateLUTs(odtInfo, lmtInfo, shaperName, acesCTLReleaseDir, lutDir, lutRes
                 data[c] = logCtoLinear(1023.0*c/(lutResolution1d-1), int(exposureIndex))
 
             lut = "%s_to_linear.spi1d" % ("%s_%s" % (transferFunction, exposureIndex))
-            WriteSPI1D(lutDir + "/" + lut, 0.0, 1.0, data, lutResolution1d, 1)
+            genlut.writeSPI1D(lutDir + "/" + lut, 0.0, 1.0, data, lutResolution1d, 1)
 
             #print( "Writing %s" % lut)
             cs.toReferenceTransforms.append( {
@@ -1435,7 +1214,7 @@ def generateLUTs(odtInfo, lmtInfo, shaperName, acesCTLReleaseDir, lutDir, lutRes
         ]
         lut = "%s_to_aces.spi1d" % name
 
-        generate1dLUTFromCTL( lutDir + "/" + lut, 
+        genlut.generate1dLUTFromCTL( lutDir + "/" + lut, 
             ctls, 
             lutResolution1d, 
             'float', 
@@ -1490,7 +1269,7 @@ def generateLUTs(odtInfo, lmtInfo, shaperName, acesCTLReleaseDir, lutDir, lutRes
             ctls = [
                 shaperToACESCTL % acesCTLReleaseDir
             ]
-            generate1dLUTFromCTL( lutDir + "/" + shaperLut, 
+            genlut.generate1dLUTFromCTL( lutDir + "/" + shaperLut, 
                 ctls, 
                 lutResolution1d, 
                 'float', 
@@ -1519,7 +1298,7 @@ def generateLUTs(odtInfo, lmtInfo, shaperName, acesCTLReleaseDir, lutDir, lutRes
             ]
             lut = "%s.%s.spi3d" % (shaperName, lmtName)
 
-            generate3dLUTFromCTL( lutDir + "/" + lut, 
+            genlut.generate3dLUTFromCTL( lutDir + "/" + lut, 
                 ctls, 
                 lutResolution3d, 
                 'float', 
@@ -1549,7 +1328,7 @@ def generateLUTs(odtInfo, lmtInfo, shaperName, acesCTLReleaseDir, lutDir, lutRes
             ]
             lut = "Inverse.%s.%s.spi3d" % (odtName, shaperName)
 
-            generate3dLUTFromCTL( lutDir + "/" + lut, 
+            genlut.generate3dLUTFromCTL( lutDir + "/" + lut, 
                 ctls, 
                 lutResolution3d, 
                 'half', 
@@ -1653,7 +1432,7 @@ def generateLUTs(odtInfo, lmtInfo, shaperName, acesCTLReleaseDir, lutDir, lutRes
             ctls = [
                 shaperToACESCTL % acesCTLReleaseDir
             ]
-            generate1dLUTFromCTL( lutDir + "/" + shaperLut, 
+            genlut.generate1dLUTFromCTL( lutDir + "/" + shaperLut, 
                 ctls, 
                 lutResolution1d, 
                 'float', 
@@ -1698,7 +1477,7 @@ def generateLUTs(odtInfo, lmtInfo, shaperName, acesCTLReleaseDir, lutDir, lutRes
             ]
             lut = "%s.RRT.a1.0.0.%s.spi3d" % (shaperName, odtName)
 
-            generate3dLUTFromCTL( lutDir + "/" + lut, 
+            genlut.generate3dLUTFromCTL( lutDir + "/" + lut, 
                 #shaperLUT,
                 ctls, 
                 lutResolution3d, 
@@ -1746,7 +1525,7 @@ def generateLUTs(odtInfo, lmtInfo, shaperName, acesCTLReleaseDir, lutDir, lutRes
             ]
             lut = "InvRRT.a1.0.0.%s.%s.spi3d" % (odtName, shaperName)
 
-            generate3dLUTFromCTL( lutDir + "/" + lut, 
+            genlut.generate3dLUTFromCTL( lutDir + "/" + lut, 
                 #None,
                 ctls, 
                 lutResolution3d, 
@@ -1911,26 +1690,60 @@ def generateLUTs(odtInfo, lmtInfo, shaperName, acesCTLReleaseDir, lutDir, lutRes
 
         cs.toReferenceTransforms = []
         if toReferenceValues != []:
-            cs.toReferenceTransforms.append( {
-                'type':'matrix',
-                'matrix':mat44FromMat33(toReferenceValues),
-                'direction':'forward'
-            })
+            for matrix in toReferenceValues:
+                cs.toReferenceTransforms.append( {
+                    'type':'matrix',
+                    'matrix':mat44FromMat33(matrix),
+                    'direction':'forward'
+                })
 
         cs.fromReferenceTransforms = []
         if fromReferenceValues != []:
-            cs.fromReferenceTransforms.append( {
-                'type':'matrix',
-                'matrix':mat44FromMat33(fromReferenceValues),
-                'direction':'forward'
-            })
+            for matrix in fromReferenceValues:
+                cs.fromReferenceTransforms.append( {
+                    'type':'matrix',
+                    'matrix':mat44FromMat33(matrix),
+                    'direction':'forward'
+                })
 
         return cs
 
-    cs = createGenericMatrix('XYZ', fromReferenceValues=acesAP0toXYZ)
+    cs = createGenericMatrix('XYZ', fromReferenceValues=[acesAP0toXYZ])
+    configData['colorSpaces'].append(cs)   
+
+    cs = createGenericMatrix('Linear - AP1', toReferenceValues=[acesAP1toAP0])
+    configData['colorSpaces'].append(cs)   
+
+    # ACES to Linear, P3D60 primaries
+    xyzToP3D60 = [ 2.4027414142, -0.8974841639, -0.3880533700,
+                  -0.8325796487,  1.7692317536,  0.0237127115,
+                   0.0388233815, -0.0824996856,  1.0363685997]
+
+    cs = createGenericMatrix('Linear - P3-D60', fromReferenceValues=[acesAP0toXYZ, xyzToP3D60])
+    configData['colorSpaces'].append(cs)   
+
+    # ACES to Linear, P3D60 primaries
+    xyzToP3DCI = [ 2.7253940305, -1.0180030062, -0.4401631952,
+                  -0.7951680258,  1.6897320548,  0.0226471906,
+                   0.0412418914, -0.0876390192,  1.1009293786]
+
+    cs = createGenericMatrix('Linear - P3-DCI', fromReferenceValues=[acesAP0toXYZ, xyzToP3DCI])
     configData['colorSpaces'].append(cs)   
 
-    cs = createGenericMatrix('Linear - AP1', toReferenceValues=acesAP1toAP0)
+    # ACES to Linear, Rec 709 primaries
+    xyzToRec709 = [ 3.2409699419, -1.5373831776, -0.4986107603,
+                   -0.9692436363,  1.8759675015,  0.0415550574,
+                    0.0556300797, -0.2039769589,  1.0569715142]
+
+    cs = createGenericMatrix('Linear - Rec.709', fromReferenceValues=[acesAP0toXYZ, xyzToRec709])
+    configData['colorSpaces'].append(cs)   
+
+    # ACES to Linear, Rec 2020 primaries
+    xyzToRec2020 = [ 1.7166511880, -0.3556707838, -0.2533662814,
+                    -0.6666843518,  1.6164812366,  0.0157685458,
+                     0.0176398574, -0.0427706133,  0.9421031212]
+
+    cs = createGenericMatrix('Linear - Rec.2020', fromReferenceValues=[acesAP0toXYZ, xyzToRec2020])
     configData['colorSpaces'].append(cs)   
 
     print( "generateLUTs - end" )