1#!/usr/bin/env python 2 3""" 4env 5PYTHONPATH=/net/soft_scratch/users/jeremys/git/ocio.js/build/src/pyglue/ 6LD_LIBRARY_PATH=/net/soft_scratch/users/jeremys/git/ocio.js/build/src/core/ 7""" 8 9import math, os, sys 10import PyOpenColorIO as OCIO 11 12print "OCIO",OCIO.version 13 14outputfilename = "config.ocio" 15 16def WriteSPI1D(filename, fromMin, fromMax, data): 17 f = file(filename,'w') 18 f.write("Version 1\n") 19 f.write("From %s %s\n" % (fromMin, fromMax)) 20 f.write("Length %d\n" % len(data)) 21 f.write("Components 1\n") 22 f.write("{\n") 23 for value in data: 24 f.write(" %s\n" % value) 25 f.write("}\n") 26 f.close() 27 28def Fit(value, fromMin, fromMax, toMin, toMax): 29 if fromMin == fromMax: 30 raise ValueError("fromMin == fromMax") 31 return (value - fromMin) / (fromMax - fromMin) * (toMax - toMin) + toMin 32 33 34############################################################################### 35 36 37config = OCIO.Config() 38config.setSearchPath('luts') 39 40config.setRole(OCIO.Constants.ROLE_SCENE_LINEAR, "linear") 41config.setRole(OCIO.Constants.ROLE_REFERENCE, "linear") 42config.setRole(OCIO.Constants.ROLE_COLOR_TIMING, "Cineon") 43config.setRole(OCIO.Constants.ROLE_COMPOSITING_LOG, "Cineon") 44config.setRole(OCIO.Constants.ROLE_DATA,"raw") 45config.setRole(OCIO.Constants.ROLE_DEFAULT,"raw") 46config.setRole(OCIO.Constants.ROLE_COLOR_PICKING,"sRGB") 47config.setRole(OCIO.Constants.ROLE_MATTE_PAINT,"sRGB") 48config.setRole(OCIO.Constants.ROLE_TEXTURE_PAINT,"sRGB") 49 50 51############################################################################### 52 53cs = OCIO.ColorSpace(name='linear') 54cs.setDescription("Scene-linear, high dynamic range. Used for rendering and compositing.") 55cs.setBitDepth(OCIO.Constants.BIT_DEPTH_F32) 56cs.setAllocation(OCIO.Constants.ALLOCATION_LG2) 57cs.setAllocationVars([-15.0, 6.0]) 58config.addColorSpace(cs) 59 60 61############################################################################### 62 63def toSRGB(v): 64 if v<0.04045/12.92: 65 return v*12.92 66 return 1.055 * v**(1.0/2.4) - 0.055 67 68def fromSRGB(v): 69 if v<0.04045: 70 return v/12.92 71 return ((v + .055) / 1.055) ** 2.4 72 73# These samples and range have been chosen to write out this colorspace with 74# a limited over/undershoot range, which also exactly samples the 0.0,1.0 75# crossings 76 77NUM_SAMPLES = 2**12+5 78RANGE = (-0.125, 1.125) 79data = [] 80for i in xrange(NUM_SAMPLES): 81 x = i/(NUM_SAMPLES-1.0) 82 x = Fit(x, 0.0, 1.0, RANGE[0], RANGE[1]) 83 data.append(fromSRGB(x)) 84 85# Data is srgb->linear 86WriteSPI1D('luts/srgb.spi1d', RANGE[0], RANGE[1], data) 87 88cs = OCIO.ColorSpace(name='sRGB') 89cs.setDescription("Standard RGB Display Space") 90cs.setBitDepth(OCIO.Constants.BIT_DEPTH_F32) 91cs.setAllocation(OCIO.Constants.ALLOCATION_UNIFORM) 92cs.setAllocationVars([RANGE[0], RANGE[1]]) 93 94t = OCIO.FileTransform('srgb.spi1d', interpolation=OCIO.Constants.INTERP_LINEAR) 95cs.setTransform(t, OCIO.Constants.COLORSPACE_DIR_TO_REFERENCE) 96config.addColorSpace(cs) 97 98 99NUM_SAMPLES = 2**16+25 100RANGE = (-0.125, 4.875) 101data = [] 102for i in xrange(NUM_SAMPLES): 103 x = i/(NUM_SAMPLES-1.0) 104 x = Fit(x, 0.0, 1.0, RANGE[0], RANGE[1]) 105 data.append(fromSRGB(x)) 106 107# Data is srgb->linear 108WriteSPI1D('luts/srgbf.spi1d', RANGE[0], RANGE[1], data) 109 110cs = OCIO.ColorSpace(name='sRGBf') 111cs.setDescription("Standard RGB Display Space, but with additional range to preserve float highlights.") 112cs.setBitDepth(OCIO.Constants.BIT_DEPTH_F32) 113cs.setAllocation(OCIO.Constants.ALLOCATION_UNIFORM) 114cs.setAllocationVars([RANGE[0], RANGE[1]]) 115 116t = OCIO.FileTransform('srgbf.spi1d', interpolation=OCIO.Constants.INTERP_LINEAR) 117cs.setTransform(t, OCIO.Constants.COLORSPACE_DIR_TO_REFERENCE) 118config.addColorSpace(cs) 119 120 121############################################################################### 122 123def toRec709(v): 124 if v<0.018: 125 return v*4.5 126 return 1.099 * v**0.45 - 0.099 127 128def fromRec709(v): 129 if v<0.018*4.5: 130 return v/4.5 131 return ((v + .099) / 1.099) ** (1.0/0.45) 132 133# These samples and range have been chosen to write out this colorspace with 134# a limited over/undershoot range, which also exactly samples the 0.0,1.0 135# crossings 136 137NUM_SAMPLES = 2**12+5 138RANGE = (-0.125, 1.125) 139data = [] 140for i in xrange(NUM_SAMPLES): 141 x = i/(NUM_SAMPLES-1.0) 142 x = Fit(x, 0.0, 1.0, RANGE[0], RANGE[1]) 143 data.append(fromRec709(x)) 144 145# Data is srgb->linear 146WriteSPI1D('luts/rec709.spi1d', RANGE[0], RANGE[1], data) 147 148cs = OCIO.ColorSpace(name='rec709') 149cs.setDescription("Rec. 709 (Full Range) Display Space") 150cs.setBitDepth(OCIO.Constants.BIT_DEPTH_F32) 151cs.setAllocation(OCIO.Constants.ALLOCATION_UNIFORM) 152cs.setAllocationVars([RANGE[0], RANGE[1]]) 153 154t = OCIO.FileTransform('rec709.spi1d', interpolation=OCIO.Constants.INTERP_LINEAR) 155cs.setTransform(t, OCIO.Constants.COLORSPACE_DIR_TO_REFERENCE) 156config.addColorSpace(cs) 157 158 159############################################################################### 160 161cineonBlackOffset = 10.0 ** ((95.0 - 685.0)/300.0) 162 163def fromCineon(x): 164 return (10.0**((1023.0 * x - 685.0) / 300.0) - cineonBlackOffset) / (1.0 - cineonBlackOffset) 165 166# These samples and range have been chosen to write out this colorspace with 167# a limited over/undershoot range, which also exactly samples the 0.0,1.0 168# crossings 169 170NUM_SAMPLES = 2**12+5 171RANGE = (-0.125, 1.125) 172data = [] 173for i in xrange(NUM_SAMPLES): 174 x = i/(NUM_SAMPLES-1.0) 175 x = Fit(x, 0.0, 1.0, RANGE[0], RANGE[1]) 176 data.append(fromCineon(x)) 177 178# Data is srgb->linear 179WriteSPI1D('luts/cineon.spi1d', RANGE[0], RANGE[1], data) 180 181cs = OCIO.ColorSpace(name='Cineon') 182cs.setDescription("Cineon (Log Film Scan)") 183cs.setBitDepth(OCIO.Constants.BIT_DEPTH_F32) 184cs.setAllocation(OCIO.Constants.ALLOCATION_UNIFORM) 185cs.setAllocationVars([RANGE[0], RANGE[1]]) 186 187t = OCIO.FileTransform('cineon.spi1d', interpolation=OCIO.Constants.INTERP_LINEAR) 188cs.setTransform(t, OCIO.Constants.COLORSPACE_DIR_TO_REFERENCE) 189config.addColorSpace(cs) 190 191 192 193############################################################################### 194 195cs = OCIO.ColorSpace(name='Gamma1.8') 196cs.setDescription("Emulates a idealized Gamma 1.8 display device.") 197cs.setBitDepth(OCIO.Constants.BIT_DEPTH_F32) 198cs.setAllocation(OCIO.Constants.ALLOCATION_UNIFORM) 199cs.setAllocationVars([0.0, 1.0]) 200 201t = OCIO.ExponentTransform(value=(1.8,1.8,1.8,1.0)) 202cs.setTransform(t, OCIO.Constants.COLORSPACE_DIR_TO_REFERENCE) 203config.addColorSpace(cs) 204 205cs = OCIO.ColorSpace(name='Gamma2.2') 206cs.setDescription("Emulates a idealized Gamma 2.2 display device.") 207cs.setBitDepth(OCIO.Constants.BIT_DEPTH_F32) 208cs.setAllocation(OCIO.Constants.ALLOCATION_UNIFORM) 209cs.setAllocationVars([0.0, 1.0]) 210 211t = OCIO.ExponentTransform(value=(2.2,2.2,2.2,1.0)) 212cs.setTransform(t, OCIO.Constants.COLORSPACE_DIR_TO_REFERENCE) 213config.addColorSpace(cs) 214 215 216############################################################################### 217 218 219# Log to Linear light conversions for Panalog 220# WARNING: these are estimations known to be close enough. 221# The actual transfer functions are not published 222 223panalogBlackOffset = 10.0 ** ((64.0 - 681.0) / 444.0) 224 225def fromPanalog(x): 226 return (10.0**((1023 * x - 681.0) / 444.0) - panalogBlackOffset) / (1.0 - panalogBlackOffset) 227 228# These samples and range have been chosen to write out this colorspace with 229# a limited over/undershoot range, which also exactly samples the 0.0,1.0 230# crossings 231 232NUM_SAMPLES = 2**12+5 233RANGE = (-0.125, 1.125) 234data = [] 235for i in xrange(NUM_SAMPLES): 236 x = i/(NUM_SAMPLES-1.0) 237 x = Fit(x, 0.0, 1.0, RANGE[0], RANGE[1]) 238 data.append(fromPanalog(x)) 239 240# Data is srgb->linear 241WriteSPI1D('luts/panalog.spi1d', RANGE[0], RANGE[1], data) 242 243cs = OCIO.ColorSpace(name='Panalog') 244cs.setDescription("Sony/Panavision Genesis Log Space") 245cs.setBitDepth(OCIO.Constants.BIT_DEPTH_F32) 246cs.setAllocation(OCIO.Constants.ALLOCATION_UNIFORM) 247cs.setAllocationVars([RANGE[0], RANGE[1]]) 248 249t = OCIO.FileTransform('panalog.spi1d', interpolation=OCIO.Constants.INTERP_LINEAR) 250cs.setTransform(t, OCIO.Constants.COLORSPACE_DIR_TO_REFERENCE) 251config.addColorSpace(cs) 252 253 254 255############################################################################### 256 257 258 259redBlackOffset = 10.0 ** ((0.0 - 1023.0) / 511.0) 260 261def fromREDLog(x): 262 return ((10.0 ** ((1023.0 * x - 1023.0) / 511.0)) - redBlackOffset) / (1.0 - redBlackOffset) 263 264# These samples and range have been chosen to write out this colorspace with 265# a limited over/undershoot range, which also exactly samples the 0.0,1.0 266# crossings 267 268 269NUM_SAMPLES = 2**12+5 270RANGE = (-0.125, 1.125) 271data = [] 272for i in xrange(NUM_SAMPLES): 273 x = i/(NUM_SAMPLES-1.0) 274 x = Fit(x, 0.0, 1.0, RANGE[0], RANGE[1]) 275 data.append(fromREDLog(x)) 276 277# Data is srgb->linear 278WriteSPI1D('luts/redlog.spi1d', RANGE[0], RANGE[1], data) 279 280cs = OCIO.ColorSpace(name='REDLog') 281cs.setDescription("RED Log Space") 282cs.setBitDepth(OCIO.Constants.BIT_DEPTH_F32) 283cs.setAllocation(OCIO.Constants.ALLOCATION_UNIFORM) 284cs.setAllocationVars([RANGE[0], RANGE[1]]) 285 286t = OCIO.FileTransform('redlog.spi1d', interpolation=OCIO.Constants.INTERP_LINEAR) 287cs.setTransform(t, OCIO.Constants.COLORSPACE_DIR_TO_REFERENCE) 288config.addColorSpace(cs) 289 290 291 292############################################################################### 293 294def fromViperLog(x): 295 return 10.0**((1023.0 * x - 1023.0) / 500.0) 296 297# These samples and range have been chosen to write out this colorspace with 298# a limited over/undershoot range, which also exactly samples the 0.0,1.0 299# crossings 300 301 302NUM_SAMPLES = 2**12+5 303RANGE = (-0.125, 1.125) 304data = [] 305for i in xrange(NUM_SAMPLES): 306 x = i/(NUM_SAMPLES-1.0) 307 x = Fit(x, 0.0, 1.0, RANGE[0], RANGE[1]) 308 data.append(fromViperLog(x)) 309 310# Data is srgb->linear 311WriteSPI1D('luts/viperlog.spi1d', RANGE[0], RANGE[1], data) 312 313cs = OCIO.ColorSpace(name='ViperLog') 314cs.setDescription("Viper Log Space") 315cs.setBitDepth(OCIO.Constants.BIT_DEPTH_F32) 316cs.setAllocation(OCIO.Constants.ALLOCATION_UNIFORM) 317cs.setAllocationVars([RANGE[0], RANGE[1]]) 318 319t = OCIO.FileTransform('viperlog.spi1d', interpolation=OCIO.Constants.INTERP_LINEAR) 320cs.setTransform(t, OCIO.Constants.COLORSPACE_DIR_TO_REFERENCE) 321config.addColorSpace(cs) 322 323 324 325############################################################################### 326 327 328alexav3logc_a = 5.555556 329alexav3logc_b = 0.052272 330alexav3logc_c = 0.247190 331alexav3logc_d = 0.385537 332alexav3logc_e = 5.367655 333alexav3logc_f = 0.092809 334alexav3logc_cut = 0.010591 335alexav3logc_eCutF = alexav3logc_e*alexav3logc_cut + alexav3logc_f 336 337# This corresponds to EI800 per Arri Doc 338# http://www.arridigital.com/forum/index.php?topic=6372.0 339# http://www.arri.com/?eID=registration&file_uid=7775 340def fromAlexaV3LogC(x): 341 if x > alexav3logc_eCutF: 342 return (10.0 **((x - alexav3logc_d) / alexav3logc_c) - alexav3logc_b) / alexav3logc_a 343 else: 344 return (x - alexav3logc_f) / alexav3logc_e 345 346 347# These samples and range have been chosen to write out this colorspace with 348# a limited over/undershoot range, which also exactly samples the 0.0,1.0 349# crossings 350 351 352NUM_SAMPLES = 2**12+5 353RANGE = (-0.125, 1.125) 354data = [] 355for i in xrange(NUM_SAMPLES): 356 x = i/(NUM_SAMPLES-1.0) 357 x = Fit(x, 0.0, 1.0, RANGE[0], RANGE[1]) 358 data.append(fromAlexaV3LogC(x)) 359 360# Data is srgb->linear 361WriteSPI1D('luts/alexalogc.spi1d', RANGE[0], RANGE[1], data) 362 363cs = OCIO.ColorSpace(name='AlexaV3LogC') 364cs.setDescription("Alexa Log C") 365cs.setBitDepth(OCIO.Constants.BIT_DEPTH_F32) 366cs.setAllocation(OCIO.Constants.ALLOCATION_UNIFORM) 367cs.setAllocationVars([RANGE[0], RANGE[1]]) 368 369t = OCIO.FileTransform('alexalogc.spi1d', interpolation=OCIO.Constants.INTERP_LINEAR) 370cs.setTransform(t, OCIO.Constants.COLORSPACE_DIR_TO_REFERENCE) 371config.addColorSpace(cs) 372 373 374############################################################################### 375 376'PLogLin' 377 378# Josh Pines style pivoted log/lin conversion 379minLinValue = 1e-10 380linReference = 0.18 381logReference = 445.0 382negativeGamma = 0.6 383densityPerCodeValue = 0.002 384ngOverDpcv = negativeGamma/densityPerCodeValue 385dpcvOverNg = densityPerCodeValue/negativeGamma 386 387def fromPLogLin(x): 388 return (10.0**((x*1023.0 - logReference)*dpcvOverNg ) * linReference) 389 390 391# These samples and range have been chosen to write out this colorspace with 392# a limited over/undershoot range, which also exactly samples the 0.0,1.0 393# crossings 394 395NUM_SAMPLES = 2**12+5 396RANGE = (-0.125, 1.125) 397data = [] 398for i in xrange(NUM_SAMPLES): 399 x = i/(NUM_SAMPLES-1.0) 400 x = Fit(x, 0.0, 1.0, RANGE[0], RANGE[1]) 401 data.append(fromPLogLin(x)) 402 403# Data is srgb->linear 404WriteSPI1D('luts/ploglin.spi1d', RANGE[0], RANGE[1], data) 405 406cs = OCIO.ColorSpace(name='PLogLin') 407cs.setDescription("Josh Pines style pivoted log/lin conversion. 445->0.18") 408cs.setBitDepth(OCIO.Constants.BIT_DEPTH_F32) 409cs.setAllocation(OCIO.Constants.ALLOCATION_UNIFORM) 410cs.setAllocationVars([RANGE[0], RANGE[1]]) 411 412t = OCIO.FileTransform('ploglin.spi1d', interpolation=OCIO.Constants.INTERP_LINEAR) 413cs.setTransform(t, OCIO.Constants.COLORSPACE_DIR_TO_REFERENCE) 414config.addColorSpace(cs) 415 416 417############################################################################### 418 419def fromSLog(x): 420 return (10.0 ** (((x - 0.616596 - 0.03) / 0.432699)) - 0.037584) 421 422# These samples and range have been chosen to write out this colorspace with 423# a limited over/undershoot range, which also exactly samples the 0.0,1.0 424# crossings 425 426NUM_SAMPLES = 2**12+5 427RANGE = (-0.125, 1.125) 428data = [] 429for i in xrange(NUM_SAMPLES): 430 x = i/(NUM_SAMPLES-1.0) 431 x = Fit(x, 0.0, 1.0, RANGE[0], RANGE[1]) 432 data.append(fromSLog(x)) 433 434# Data is srgb->linear 435WriteSPI1D('luts/slog.spi1d', RANGE[0], RANGE[1], data) 436 437cs = OCIO.ColorSpace(name='SLog') 438cs.setDescription("Sony SLog") 439cs.setBitDepth(OCIO.Constants.BIT_DEPTH_F32) 440cs.setAllocation(OCIO.Constants.ALLOCATION_UNIFORM) 441cs.setAllocationVars([RANGE[0], RANGE[1]]) 442 443t = OCIO.FileTransform('slog.spi1d', interpolation=OCIO.Constants.INTERP_LINEAR) 444cs.setTransform(t, OCIO.Constants.COLORSPACE_DIR_TO_REFERENCE) 445config.addColorSpace(cs) 446 447 448############################################################################### 449 450'REDSpace' 451 452 453 454############################################################################### 455 456cs = OCIO.ColorSpace(name='raw') 457cs.setDescription("Raw Data. Used for normals, points, etc.") 458cs.setBitDepth(OCIO.Constants.BIT_DEPTH_F32) 459cs.setIsData(True) 460config.addColorSpace(cs) 461 462 463############################################################################### 464 465display = 'default' 466config.addDisplay(display, 'None', 'raw') 467config.addDisplay(display, 'sRGB', 'sRGB') 468config.addDisplay(display, 'rec709', 'rec709') 469 470config.setActiveDisplays('default') 471config.setActiveViews('sRGB') 472 473 474############################################################################### 475 476 477 478try: 479 config.sanityCheck() 480except Exception,e: 481 print e 482 483f = file(outputfilename,"w") 484f.write(config.serialize()) 485f.close() 486print "Wrote",outputfilename 487 488# Core/LUT/include/LUT/fnLUTConversions.h 489 490