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