import PyOpenColorIO as OCIO
import process
+from util import *
+
+import generateLUT as genlut
+import createREDColorSpaces as red
+import createCanonColorSpaces as canon
+import createSonyColorSpaces as sony
+import createARRIColorSpaces as arri
#
# Utility functions
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 ):
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")
#
# Create the rest of the color spaces
#
- #sortedColorspaces = sorted(configData['colorSpaces'], key=lambda x: x.name)
- #print( sortedColorspaces )
- #for colorspace in sortedColorspaces:
for colorspace in sorted(configData['colorSpaces']):
print( "Creating new color space : %s" % colorspace.name)
#
# Functions to generate color space definitions and LUTs for transforms for a specific ACES release
#
-class ColorSpace:
- "A container for data needed to define an OCIO 'Color Space' "
-
- def __init__(self,
- name,
- description=None,
- bitDepth=OCIO.Constants.BIT_DEPTH_F32,
- equalityGroup=None,
- family=None,
- isData=False,
- toReferenceTransforms=[],
- fromReferenceTransforms=[],
- allocationType=OCIO.Constants.ALLOCATION_UNIFORM,
- allocationVars=[0.0, 1.0]):
- "Initialize the standard class variables"
- self.name = name
- self.bitDepth=bitDepth
- self.description = description
- self.equalityGroup=equalityGroup
- self.family=family
- self.isData=isData
- self.toReferenceTransforms=toReferenceTransforms
- self.fromReferenceTransforms=fromReferenceTransforms
- self.allocationType=allocationType
- self.allocationVars=allocationVars
-
-# Create a 4x4 matrix (list) based on a 3x3 matrix (list) input
-def mat44FromMat33(mat33):
- return [mat33[0], mat33[1], mat33[2], 0.0,
- mat33[3], mat33[4], mat33[5], 0.0,
- mat33[6], mat33[7], mat33[8], 0.0,
- 0,0,0,1.0]
-
# Output is a list of colorspaces and transforms that convert between those
# colorspaces and reference color space, ACES
'%s/ACEScg/ACEScsc.ACES_to_ACEScg.a1.0.0.ctl' % acesCTLReleaseDir
]
lut = "%s_to_ACES.spi1d" % name
- generate1dLUTFromCTL( lutDir + "/" + lut,
+
+ # Remove spaces and parentheses
+ lut = lut.replace(' ', '_').replace(')', '_').replace('(', '_')
+
+ genlut.generate1dLUTFromCTL( lutDir + "/" + lut,
ctls,
lutResolution1d,
'float',
'%s/ACEScg/ACEScsc.ACES_to_ACEScg.a1.0.0.ctl' % acesCTLReleaseDir
]
lut = "%s_to_aces.spi1d" % name
- generate1dLUTFromCTL( lutDir + "/" + lut,
+
+ # Remove spaces and parentheses
+ lut = lut.replace(' ', '_').replace(')', '_').replace('(', '_')
+
+ genlut.generate1dLUTFromCTL( lutDir + "/" + lut,
ctls,
lutResolution1d,
'uint16',
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
ADX16 = createADX(bitdepth=16)
configData['colorSpaces'].append(ADX16)
-
#
- # REDlogFilm to ACES
+ # Camera Input Transforms
#
- def createREDlogFilm(gamut, transferFunction, name='REDlogFilm'):
- name = "%s - %s" % (transferFunction, gamut)
- if transferFunction == "":
- name = "Linear - %s" % gamut
- if gamut == "":
- name = "%s" % transferFunction
-
- cs = ColorSpace(name)
- cs.description = name
- cs.equalityGroup = ''
- cs.family = 'RED'
- cs.isData=False
-
- def cineonToLinear(codeValue):
- nGamma = 0.6
- blackPoint = 95.0
- whitePoint = 685.0
- codeValueToDensity = 0.002
-
- blackLinear = pow(10.0, (blackPoint - whitePoint) * (codeValueToDensity / nGamma))
- codeLinear = pow(10.0, (codeValue - whitePoint) * (codeValueToDensity / nGamma))
-
- return (codeLinear - blackLinear)/(1.0 - blackLinear)
-
- cs.toReferenceTransforms = []
-
- if transferFunction == 'REDlogFilm':
- data = array.array('f', "\0" * lutResolution1d * 4)
- for c in range(lutResolution1d):
- data[c] = cineonToLinear(1023.0*c/(lutResolution1d-1))
-
- lut = "CineonLog_to_linear.spi1d"
- WriteSPI1D(lutDir + "/" + lut, 0.0, 1.0, data, lutResolution1d, 1)
-
- cs.toReferenceTransforms.append( {
- 'type':'lutFile',
- 'path':lut,
- 'interpolation':'linear',
- 'direction':'forward'
- } )
-
- if gamut == 'DRAGONcolor':
- cs.toReferenceTransforms.append( {
- 'type':'matrix',
- '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':
- 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 == 'REDcolor2':
- cs.toReferenceTransforms.append( {
- 'type':'matrix',
- 'matrix':mat44FromMat33([0.480997, 0.402289, 0.116714,
- -0.004938, 1.000154, 0.004781,
- -0.105257, 0.025320, 1.079907]),
- 'direction':'forward'
- })
-
- cs.fromReferenceTransforms = []
- return cs
-
- # Full conversion
- REDlogFilmDRAGON = createREDlogFilm("DRAGONcolor", "REDlogFilm", name="REDlogFilm")
- configData['colorSpaces'].append(REDlogFilmDRAGON)
-
- REDlogFilmREDcolor2 = createREDlogFilm("REDcolor2", "REDlogFilm", name="REDlogFilm")
- configData['colorSpaces'].append(REDlogFilmREDcolor2)
-
- REDlogFilmREDcolor3 = createREDlogFilm("REDcolor3", "REDlogFilm", name="REDlogFilm")
- configData['colorSpaces'].append(REDlogFilmREDcolor3)
-
- # Linearization only
- REDlogFilmDRAGON = createREDlogFilm("", "REDlogFilm", name="REDlogFilm")
- configData['colorSpaces'].append(REDlogFilmDRAGON)
- # Primaries only
- REDlogFilmDRAGON = createREDlogFilm("DRAGONcolor", "", name="REDlogFilm")
- configData['colorSpaces'].append(REDlogFilmDRAGON)
-
- REDlogFilmREDcolor2 = createREDlogFilm("REDcolor2", "", name="REDlogFilm")
- configData['colorSpaces'].append(REDlogFilmREDcolor2)
-
- REDlogFilmREDcolor3 = createREDlogFilm("REDcolor3", "", name="REDlogFilm")
- configData['colorSpaces'].append(REDlogFilmREDcolor3)
+ # RED color spaces to ACES
+ redColorSpaces = red.createColorSpaces(lutDir, lutResolution1d)
+ for cs in redColorSpaces:
+ configData['colorSpaces'].append(cs)
- #
# Canon-Log to ACES
- #
- def createCanonLog(gamut, transferFunction, name='Canon-Log'):
- name = "%s - %s" % (transferFunction, gamut)
- if transferFunction == "":
- name = "Linear - %s" % gamut
- if gamut == "":
- name = "%s" % transferFunction
-
- cs = ColorSpace(name)
- cs.description = name
- cs.equalityGroup = ''
- cs.family = 'Canon'
- cs.isData=False
-
- def legalToFull(codeValue):
- return (codeValue - 64.0)/(940.0 - 64.0)
-
- def canonLogToLinear(codeValue):
- # log = fullToLegal(c1 * log10(c2*linear + 1) + c3)
- # linear = (pow(10, (legalToFul(log) - c3)/c1) - 1)/c2
- c1 = 0.529136
- c2 = 10.1596
- c3 = 0.0730597
-
- linear = (pow(10.0, (legalToFull(codeValue) - c3)/c1) -1.0)/c2
- linear = 0.9 * linear
- #print( codeValue, linear )
- return linear
-
- cs.toReferenceTransforms = []
-
- if transferFunction == "Canon-Log":
- data = array.array('f', "\0" * lutResolution1d * 4)
- for c in range(lutResolution1d):
- data[c] = canonLogToLinear(1023.0*c/(lutResolution1d-1))
-
- lut = "%s_to_linear.spi1d" % transferFunction
- WriteSPI1D(lutDir + "/" + lut, 0.0, 1.0, data, lutResolution1d, 1)
-
- cs.toReferenceTransforms.append( {
- 'type':'lutFile',
- 'path':lut,
- 'interpolation':'linear',
- 'direction':'forward'
- } )
-
- if gamut == 'Rec. 709 Daylight':
- cs.toReferenceTransforms.append( {
- 'type':'matrix',
- 'matrix':[0.561538969, 0.402060105, 0.036400926, 0.0,
- 0.092739623, 0.924121198, -0.016860821, 0.0,
- 0.084812961, 0.006373835, 0.908813204, 0.0,
- 0,0,0,1.0],
- 'direction':'forward'
- })
- elif gamut == 'Rec. 709 Tungsten':
- cs.toReferenceTransforms.append( {
- 'type':'matrix',
- 'matrix':[0.566996399, 0.365079418, 0.067924183, 0.0,
- 0.070901044, 0.880331008, 0.048767948, 0.0,
- 0.073013542, -0.066540862, 0.99352732, 0.0,
- 0,0,0,1.0],
- 'direction':'forward'
- })
- elif gamut == 'DCI-P3 Daylight':
- cs.toReferenceTransforms.append( {
- 'type':'matrix',
- 'matrix':[0.607160575, 0.299507286, 0.093332140, 0.0,
- 0.004968120, 1.050982224, -0.055950343, 0.0,
- -0.007839939, 0.000809127, 1.007030813, 0.0,
- 0,0,0,1.0],
- 'direction':'forward'
- })
- elif gamut == 'DCI-P3 Tungsten':
- cs.toReferenceTransforms.append( {
- 'type':'matrix',
- 'matrix':[0.650279125, 0.253880169, 0.095840706, 0.0,
- -0.026137986, 1.017900530, 0.008237456, 0.0,
- 0.007757558, -0.063081669, 1.055324110, 0.0,
- 0,0,0,1.0],
- 'direction':'forward'
- })
- elif gamut == 'Cinema Gamut Daylight':
- cs.toReferenceTransforms.append( {
- 'type':'matrix',
- 'matrix':[0.763064455, 0.149021161, 0.087914384, 0.0,
- 0.003657457, 1.10696038, -0.110617837, 0.0,
- -0.009407794,-0.218383305, 1.227791099, 0.0,
- 0,0,0,1.0],
- 'direction':'forward'
- })
- elif gamut == 'Cinema Gamut Tungsten':
- cs.toReferenceTransforms.append( {
- 'type':'matrix',
- 'matrix':[0.817416293, 0.090755698, 0.091828009, 0.0,
- -0.035361374, 1.065690585, -0.030329211, 0.0,
- 0.010390366, -0.299271107, 1.288880741, 0.0,
- 0,0,0,1.0],
- 'direction':'forward'
- })
-
- cs.fromReferenceTransforms = []
- return cs
-
- # Full conversion
- CanonLog1 = createCanonLog("Rec. 709 Daylight", "Canon-Log", name="Canon-Log")
- configData['colorSpaces'].append(CanonLog1)
-
- CanonLog2 = createCanonLog("Rec. 709 Tungsten", "Canon-Log", name="Canon-Log")
- configData['colorSpaces'].append(CanonLog2)
-
- CanonLog3 = createCanonLog("DCI-P3 Daylight", "Canon-Log", name="Canon-Log")
- configData['colorSpaces'].append(CanonLog3)
-
- CanonLog4 = createCanonLog("DCI-P3 Tungsten", "Canon-Log", name="Canon-Log")
- configData['colorSpaces'].append(CanonLog4)
-
- CanonLog5 = createCanonLog("Cinema Gamut Daylight", "Canon-Log", name="Canon-Log")
- configData['colorSpaces'].append(CanonLog5)
-
- CanonLog6 = createCanonLog("Cinema Gamut Tungsten", "Canon-Log", name="Canon-Log")
- configData['colorSpaces'].append(CanonLog6)
-
- # Linearization only
- CanonLog7 = createCanonLog('', "Canon-Log", name="Canon-Log")
- configData['colorSpaces'].append(CanonLog7)
-
- # Primaries only
- CanonLog8 = createCanonLog("Rec. 709 Daylight", "", name="Canon-Log")
- configData['colorSpaces'].append(CanonLog8)
-
- CanonLog9 = createCanonLog("Rec. 709 Tungsten", "", name="Canon-Log")
- configData['colorSpaces'].append(CanonLog9)
-
- CanonLog10 = createCanonLog("DCI-P3 Daylight", "", name="Canon-Log")
- configData['colorSpaces'].append(CanonLog10)
-
- CanonLog11 = createCanonLog("DCI-P3 Tungsten", "", name="Canon-Log")
- configData['colorSpaces'].append(CanonLog11)
-
- CanonLog12 = createCanonLog("Cinema Gamut Daylight", "", name="Canon-Log")
- configData['colorSpaces'].append(CanonLog12)
-
- CanonLog13 = createCanonLog("Cinema Gamut Tungsten", "", name="Canon-Log")
- configData['colorSpaces'].append(CanonLog13)
+ canonColorSpaces = canon.createColorSpaces(lutDir, lutResolution1d)
+ for cs in canonColorSpaces:
+ configData['colorSpaces'].append(cs)
- #
# SLog to ACES
- #
- def createSlog(gamut, transferFunction, name='S-Log3'):
- name = "%s - %s" % (transferFunction, gamut)
- if transferFunction == "":
- name = "Linear - %s" % gamut
- if gamut == "":
- name = "%s" % transferFunction
-
- cs = ColorSpace(name)
- cs.description = name
- cs.equalityGroup = ''
- cs.family = 'Sony'
- cs.isData=False
-
- def sLog1ToLinear(SLog):
- b = 64.
- ab = 90.
- w = 940.
-
- if (SLog >= ab):
- lin = ( pow(10., ( ( ( SLog - b) / ( w - b) - 0.616596 - 0.03) / 0.432699)) - 0.037584) * 0.9
- else:
- lin = ( ( ( SLog - b) / ( w - b) - 0.030001222851889303) / 5.) * 0.9
- return lin
-
- def sLog2ToLinear(SLog):
- b = 64.
- ab = 90.
- w = 940.
-
- if (SLog >= ab):
- lin = ( 219. * ( pow(10., ( ( ( SLog - b) / ( w - b) - 0.616596 - 0.03) / 0.432699)) - 0.037584) / 155.) * 0.9
- else:
- lin = ( ( ( SLog - b) / ( w - b) - 0.030001222851889303) / 3.53881278538813) * 0.9
- return lin
-
- def sLog3ToLinear(codeValue):
- if codeValue >= (171.2102946929):
- linear = pow(10.0, ((codeValue - 420.0) / 261.5)) * (0.18 + 0.01) - 0.01
- else:
- linear = (codeValue - 95.0)*0.01125000/(171.2102946929 - 95.0)
- #print( codeValue, linear )
- return linear
-
- cs.toReferenceTransforms = []
-
- if transferFunction == "S-Log1":
- data = array.array('f', "\0" * lutResolution1d * 4)
- for c in range(lutResolution1d):
- data[c] = sLog1ToLinear(1023.0*c/(lutResolution1d-1))
-
- lut = "%s_to_linear.spi1d" % transferFunction
- WriteSPI1D(lutDir + "/" + lut, 0.0, 1.0, data, lutResolution1d, 1)
-
- #print( "Writing %s" % lut)
-
- cs.toReferenceTransforms.append( {
- 'type':'lutFile',
- 'path':lut,
- 'interpolation':'linear',
- 'direction':'forward'
- } )
- elif transferFunction == "S-Log2":
- data = array.array('f', "\0" * lutResolution1d * 4)
- for c in range(lutResolution1d):
- data[c] = sLog2ToLinear(1023.0*c/(lutResolution1d-1))
-
- lut = "%s_to_linear.spi1d" % transferFunction
- WriteSPI1D(lutDir + "/" + lut, 0.0, 1.0, data, lutResolution1d, 1)
-
- #print( "Writing %s" % lut)
-
- cs.toReferenceTransforms.append( {
- 'type':'lutFile',
- 'path':lut,
- 'interpolation':'linear',
- 'direction':'forward'
- } )
- elif transferFunction == "S-Log3":
- data = array.array('f', "\0" * lutResolution1d * 4)
- for c in range(lutResolution1d):
- data[c] = sLog3ToLinear(1023.0*c/(lutResolution1d-1))
-
- lut = "%s_to_linear.spi1d" % transferFunction
- WriteSPI1D(lutDir + "/" + lut, 0.0, 1.0, data, lutResolution1d, 1)
-
- #print( "Writing %s" % lut)
-
- cs.toReferenceTransforms.append( {
- 'type':'lutFile',
- 'path':lut,
- 'interpolation':'linear',
- 'direction':'forward'
- } )
-
- if gamut == 'S-Gamut':
- cs.toReferenceTransforms.append( {
- 'type':'matrix',
- 'matrix':mat44FromMat33([0.754338638, 0.133697046, 0.111968437,
- 0.021198141, 1.005410934, -0.026610548,
- -0.009756991, 0.004508563, 1.005253201]),
- 'direction':'forward'
- })
- elif gamut == 'S-Gamut Daylight':
- cs.toReferenceTransforms.append( {
- 'type':'matrix',
- 'matrix':mat44FromMat33([0.8764457030, 0.0145411681, 0.1090131290,
- 0.0774075345, 0.9529571767, -0.0303647111,
- 0.0573564351, -0.1151066335, 1.0577501984]),
- 'direction':'forward'
- })
- elif gamut == 'S-Gamut Tungsten':
- cs.toReferenceTransforms.append( {
- 'type':'matrix',
- 'matrix':mat44FromMat33([1.0110238740, -0.1362526051, 0.1252287310,
- 0.1011994504, 0.9562196265, -0.0574190769,
- 0.0600766530, -0.1010185315, 1.0409418785]),
- 'direction':'forward'
- })
- elif gamut == 'S-Gamut3.Cine':
- cs.toReferenceTransforms.append( {
- 'type':'matrix',
- 'matrix':mat44FromMat33([0.6387886672, 0.2723514337, 0.0888598992,
- -0.0039159061, 1.0880732308, -0.0841573249,
- -0.0299072021, -0.0264325799, 1.0563397820]),
- 'direction':'forward'
- })
- elif gamut == 'S-Gamut3':
- cs.toReferenceTransforms.append( {
- 'type':'matrix',
- 'matrix':mat44FromMat33([0.7529825954, 0.1433702162, 0.1036471884,
- 0.0217076974, 1.0153188355, -0.0370265329,
- -0.0094160528, 0.0033704179, 1.0060456349]),
- 'direction':'forward'
- })
-
- cs.fromReferenceTransforms = []
- return cs
-
- # SLog1
- SLog1SGamut = createSlog("S-Gamut", "S-Log1", name="S-Log")
- configData['colorSpaces'].append(SLog1SGamut)
-
- # SLog2
- SLog2SGamut = createSlog("S-Gamut", "S-Log2", name="S-Log2")
- configData['colorSpaces'].append(SLog2SGamut)
-
- SLog2SGamutDaylight = createSlog("S-Gamut Daylight", "S-Log2", name="S-Log2")
- configData['colorSpaces'].append(SLog2SGamutDaylight)
-
- SLog2SGamutTungsten = createSlog("S-Gamut Tungsten", "S-Log2", name="S-Log2")
- configData['colorSpaces'].append(SLog2SGamutTungsten)
-
- # SLog3
- SLog3SGamut3Cine = createSlog("S-Gamut3.Cine", "S-Log3", name="S-Log3")
- configData['colorSpaces'].append(SLog3SGamut3Cine)
-
- SLog3SGamut3 = createSlog("S-Gamut3", "S-Log3", name="S-Log3")
- configData['colorSpaces'].append(SLog3SGamut3)
-
- # Linearization only
- SLog1 = createSlog("", "S-Log1", name="S-Log")
- configData['colorSpaces'].append(SLog1)
-
- SLog2 = createSlog("", "S-Log2", name="S-Log2")
- configData['colorSpaces'].append(SLog2)
-
- SLog3 = createSlog("", "S-Log3", name="S-Log3")
- configData['colorSpaces'].append(SLog3)
-
- # Primaries only
- SGamut = createSlog("S-Gamut", "", name="S-Log")
- configData['colorSpaces'].append(SGamut)
-
- SGamutDaylight = createSlog("S-Gamut Daylight", "", name="S-Log2")
- configData['colorSpaces'].append(SGamutDaylight)
-
- SGamutTungsten = createSlog("S-Gamut Tungsten", "", name="S-Log2")
- configData['colorSpaces'].append(SGamutTungsten)
-
- SGamut3Cine = createSlog("S-Gamut3.Cine", "", name="S-Log3")
- configData['colorSpaces'].append(SGamut3Cine)
-
- SGamut3 = createSlog("S-Gamut3", "", name="S-Log3")
- configData['colorSpaces'].append(SGamut3)
+ sonyColorSpaces = sony.createColorSpaces(lutDir, lutResolution1d)
+ for cs in sonyColorSpaces:
+ configData['colorSpaces'].append(cs)
- #
# LogC to ACES
- #
- def createLogC(gamut, transferFunction, exposureIndex, name='LogC'):
- name = "%s (EI%s) - %s" % (transferFunction, exposureIndex, gamut)
- if transferFunction == "":
- name = "Linear - %s" % gamut
- if gamut == "":
- name = "%s (EI%s)" % (transferFunction, exposureIndex)
-
- cs = ColorSpace(name)
- cs.description = name
- cs.equalityGroup = ''
- cs.family = 'ARRI'
- cs.isData=False
-
- # Globals
- IDT_maker_version = "0.08"
-
- nominalEI = 400.0
- blackSignal = 0.003907
- midGraySignal = 0.01
- encodingGain = 0.256598
- encodingOffset = 0.391007
-
- def gainForEI(EI) :
- return (math.log(EI/nominalEI)/math.log(2) * (0.89 - 1) / 3 + 1) * encodingGain
-
- def LogCInverseParametersForEI(EI) :
- cut = 1.0 / 9.0
- slope = 1.0 / (cut * math.log(10))
- offset = math.log10(cut) - slope * cut
- gain = EI / nominalEI
- gray = midGraySignal / gain
- # The higher the EI, the lower the gamma
- encGain = gainForEI(EI)
- encOffset = encodingOffset
- for i in range(0,3) :
- nz = ((95.0 / 1023.0 - encOffset) / encGain - offset) / slope
- encOffset = encodingOffset - math.log10(1 + nz) * encGain
- # Calculate some intermediate values
- a = 1.0 / gray
- b = nz - blackSignal / gray
- e = slope * a * encGain
- f = encGain * (slope * b + offset) + encOffset
- # Manipulations so we can return relative exposure
- s = 4 / (0.18 * EI)
- t = blackSignal
- b = b + a * t
- a = a * s
- f = f + e * t
- e = e * s
- return { 'a' : a,
- 'b' : b,
- 'cut' : (cut - b) / a,
- 'c' : encGain,
- 'd' : encOffset,
- 'e' : e,
- 'f' : f }
-
- def logCtoLinear(codeValue, exposureIndex):
- p = LogCInverseParametersForEI(exposureIndex)
- breakpoint = p['e'] * p['cut'] + p['f']
- if (codeValue > breakpoint):
- linear = (pow(10,(codeValue/1023.0 - p['d']) / p['c']) - p['b']) / p['a']
- else:
- linear = (codeValue/1023.0 - p['f']) / p['e']
-
- #print( codeValue, linear )
- return linear
-
-
- cs.toReferenceTransforms = []
-
- if transferFunction == "V3 LogC":
- data = array.array('f', "\0" * lutResolution1d * 4)
- for c in range(lutResolution1d):
- 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)
-
- #print( "Writing %s" % lut)
- cs.toReferenceTransforms.append( {
- 'type':'lutFile',
- 'path':lut,
- 'interpolation':'linear',
- 'direction':'forward'
- } )
-
- if gamut == 'Wide Gamut':
- cs.toReferenceTransforms.append( {
- 'type':'matrix',
- 'matrix':mat44FromMat33([0.680206, 0.236137, 0.083658,
- 0.085415, 1.017471, -0.102886,
- 0.002057, -0.062563, 1.060506]),
- 'direction':'forward'
- })
-
- cs.fromReferenceTransforms = []
- return cs
-
- transferFunction = "V3 LogC"
- gamut = "Wide Gamut"
- #EIs = [160.0, 200.0, 250.0, 320.0, 400.0, 500.0, 640.0, 800.0, 1000.0, 1280.0, 1600.0, 2000.0, 2560.0, 3200.0]
- EIs = [160, 200, 250, 320, 400, 500, 640, 800, 1000, 1280, 1600, 2000, 2560, 3200]
- defaultEI = 800
-
- # Full conversion
- for EI in EIs:
- LogCEIfull = createLogC(gamut, transferFunction, EI, name="LogC")
- configData['colorSpaces'].append(LogCEIfull)
-
- # Linearization only
- for EI in [800]:
- LogCEIlinearization = createLogC("", transferFunction, EI, name="LogC")
- configData['colorSpaces'].append(LogCEIlinearization)
-
- # Primaries
- LogCEIprimaries = createLogC(gamut, "", defaultEI, name="LogC")
- configData['colorSpaces'].append(LogCEIprimaries)
+ arriColorSpaces = arri.createColorSpaces(lutDir, lutResolution1d)
+ for cs in arriColorSpaces:
+ configData['colorSpaces'].append(cs)
#
# Generic log transform
cs.isData=False
ctls = [
- #'%s/logShaper/logShaper16i_to_aces_param.ctl' % acesCTLReleaseDir
'%s/utilities/ACESlib.OCIO_shaper_log2_to_lin_param.a1.0.0.ctl' % acesCTLReleaseDir
]
lut = "%s_to_aces.spi1d" % name
- generate1dLUTFromCTL( lutDir + "/" + lut,
+ # Remove spaces and parentheses
+ lut = lut.replace(' ', '_').replace(')', '_').replace('(', '_')
+
+ genlut.generate1dLUTFromCTL( lutDir + "/" + lut,
ctls,
lutResolution1d,
'float',
ctls = [
shaperToACESCTL % acesCTLReleaseDir
]
- generate1dLUTFromCTL( lutDir + "/" + shaperLut,
+
+ # Remove spaces and parentheses
+ shaperLut = shaperLut.replace(' ', '_').replace(')', '_').replace('(', '_')
+
+ genlut.generate1dLUTFromCTL( lutDir + "/" + shaperLut,
ctls,
lutResolution1d,
'float',
]
lut = "%s.%s.spi3d" % (shaperName, lmtName)
- generate3dLUTFromCTL( lutDir + "/" + lut,
+ # Remove spaces and parentheses
+ lut = lut.replace(' ', '_').replace(')', '_').replace('(', '_')
+
+ genlut.generate3dLUTFromCTL( lutDir + "/" + lut,
ctls,
lutResolution3d,
'float',
]
lut = "Inverse.%s.%s.spi3d" % (odtName, shaperName)
- generate3dLUTFromCTL( lutDir + "/" + lut,
+ # Remove spaces and parentheses
+ lut = lut.replace(' ', '_').replace(')', '_').replace('(', '_')
+
+ genlut.generate3dLUTFromCTL( lutDir + "/" + lut,
ctls,
lutResolution3d,
'half',
lmtShaperName,
'%s/utilities/ACESlib.OCIO_shaper_log2_to_lin_param.a1.0.0.ctl',
'%s/utilities/ACESlib.OCIO_shaper_lin_to_log2_param.a1.0.0.ctl',
- #'%s/logShaper/logShaper16i_to_aces_param.ctl',
- #'%s/logShaper/aces_to_logShaper16i_param.ctl',
shaperInputScale_genericLog2,
lmtParams
]
ctls = [
shaperToACESCTL % acesCTLReleaseDir
]
- generate1dLUTFromCTL( lutDir + "/" + shaperLut,
+
+ # Remove spaces and parentheses
+ shaperLut = shaperLut.replace(' ', '_').replace(')', '_').replace('(', '_')
+
+ genlut.generate1dLUTFromCTL( lutDir + "/" + shaperLut,
ctls,
lutResolution1d,
'float',
]
lut = "%s.RRT.a1.0.0.%s.spi3d" % (shaperName, odtName)
- generate3dLUTFromCTL( lutDir + "/" + lut,
+ # Remove spaces and parentheses
+ lut = lut.replace(' ', '_').replace(')', '_').replace('(', '_')
+
+ genlut.generate3dLUTFromCTL( lutDir + "/" + lut,
#shaperLUT,
ctls,
lutResolution3d,
]
lut = "InvRRT.a1.0.0.%s.%s.spi3d" % (odtName, shaperName)
- generate3dLUTFromCTL( lutDir + "/" + lut,
+ # Remove spaces and parentheses
+ lut = lut.replace(' ', '_').replace(')', '_').replace('(', '_')
+
+ genlut.generate3dLUTFromCTL( lutDir + "/" + lut,
#None,
ctls,
lutResolution3d,
log2ShaperName,
'%s/utilities/ACESlib.OCIO_shaper_log2_to_lin_param.a1.0.0.ctl',
'%s/utilities/ACESlib.OCIO_shaper_lin_to_log2_param.a1.0.0.ctl',
- #'%s/logShaper/logShaper16i_to_aces_param.ctl',
- #'%s/logShaper/aces_to_logShaper16i_param.ctl',
shaperInputScale_genericLog2,
log2Params
]
#
# Choose your shaper
#
- # XXX
- # Shaper name. Should really be automated or made a user choice
- #
- # Options: aceslogShaper, aceslogScaledShaper, log2Shaper
- #shaperName = 'log2Shaper'
-
- #if shaperName in shaperData:
- # rrtShaperName = shaperName
- # rrtShaper = shaperData[shaperName]
- #else:
-
rrtShaperName = log2ShaperName
rrtShaper = log2ShaperData
#
# RRT + ODT Combinations
#
- #for odtName, odtValues in odtInfo.iteritems():
sortedOdts = sorted(odtInfo.iteritems(), key=lambda x: x[1])
print( sortedOdts )
for odt in sortedOdts:
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)
+
+ # 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)
- cs = createGenericMatrix('Linear - AP1', toReferenceValues=acesAP1toAP0)
+ # 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" )
return configData
def generateBakedLUTs(odtInfo, shaperName, bakedDir, configPath, lutResolution1d, lutResolution3d, lutResolutionShaper=1024):
-
# Add the legal and full variations into this list
odtInfoC = dict(odtInfo)
for odtCTLName, odtValues in odtInfo.iteritems():
print( "command line : \n%s\n" % " ".join(sys.argv) )
- if configDir == None:
+ if not acesCTLDir:
print( "process: No ACES CTL directory specified" )
return
-
+
+ if not configDir:
+ print( "process: No configuration directory specified" )
+ return
#
# Generate the configuration
#