first public test version of the GH4 branch
[OpenColorIO-Configs.git] / aces_1.0.1 / python / aces_ocio / colorspaces / panasonic_gh4.py
1 #!/usr/bin/env python
2 # -*- coding: utf-8 -*-
3
4 """
5 Implements support for *Panasonic GH4* colorspaces conversions and transfer
6 functions based on empirical data.
7 """
8 from panasonic_gh4_data import *
9
10 import numpy as np
11 from scipy import interpolate
12 import matplotlib.pyplot as plt
13 import math, sys
14
15 import array
16 import os
17
18 import PyOpenColorIO as ocio
19
20 import aces_ocio.generate_lut as genlut
21 from aces_ocio.utilities import ColorSpace
22
23 __author__ = 'Martin Schitter'
24 __copyright__ = 'Copyright (C) 2015 - Martin Schitter'
25 __license__ = 'GNU General Public License'
26 __maintainer__ = 'Martin Schitter'
27 __email__ = 'ms+aces@mur.at'
28 __status__ = 'Experimental'
29
30 __all__ = ['create_gh4_style',
31            'create_colorspaces']
32 styles = {
33     'Scenery': [0.0079310344827586213, 0.017241379310344827,
34     0.053448275862068968, 0.27517241379310342, 0.7501724137931034,
35     1.3010344827586207, 2.1315517241379309, 3.0501724137931037,
36     4.3113793103448277, 5.8546551724137927, 7.9153448275862068,
37     10.296551724137931, 13.276896551724137, 16.769310344827588,
38     21.641896551724138, 27.386034482758621, 34.62258620689655,
39     43.353448275862071, 53.682931034482756, 65.670000000000002,
40     80.268275862068961, 102.43344827586206, 116.53896551724138,
41     134.62586206896552, 156.84931034482759, 170.11293103448276,
42     184.20775862068965, 196.58258620689656, 207.89155172413794,
43     217.53241379310344, 227.18224137931034, 236.08120689655172,
44     244.85396551724139, 252.55293103448275, 254.95758620689656,
45     255.0, 255.0, 255.0, 255.0, 255.0],
46     
47     'Natural': [0.032758620689655175, 0.048275862068965517,
48     0.16224137931034482, 0.4613793103448276, 1.0034482758620689,
49     1.7708620689655172, 2.6732758620689654, 3.9575862068965519,
50     5.5139655172413793, 7.4403448275862072, 9.6510344827586199,
51     12.639310344827587, 16.452758620689654, 20.901551724137931,
52     26.867241379310343, 34.115862068965519, 43.008448275862072,
53     52.998448275862067, 64.548103448275867, 76.758448275862065,
54     90.370862068965522, 109.64431034482759, 121.71120689655173,
55     137.56103448275863, 157.60396551724139, 169.77672413793104,
56     182.39965517241379, 194.02293103448275, 205.00258620689655,
57     214.89431034482757, 224.41034482758621, 233.6396551724138,
58     242.20258620689654, 250.67862068965516, 254.0, 254.0, 254.0,
59     254.0, 254.0, 254.0],
60     
61     'Vivid': [0.0013793103448275861, 0.0025862068965517241,
62     0.024482758620689656, 0.11120689655172414, 0.16810344827586207,
63     0.79741379310344829, 1.2001724137931034, 1.9437931034482758,
64     2.9256896551724139, 4.0244827586206897, 5.4912068965517244,
65     7.490344827586207, 10.088448275862069, 13.246379310344828,
66     17.292586206896551, 22.015862068965518, 28.217068965517242,
67     35.784310344827588, 44.731379310344828, 56.00931034482759,
68     70.820689655172416, 93.825344827586207, 108.79879310344828,
69     128.2251724137931, 152.32620689655172, 166.44137931034481,
70     181.00758620689655, 193.65810344827585, 205.37741379310344,
71     215.1153448275862, 224.87586206896552, 234.14637931034483,
72     242.44327586206896, 250.86465517241379, 254.98724137931035,
73     255.0, 255.0, 255.0, 255.0, 255.0],
74     
75     'Cinelike-D': [0.21293103448275863, 0.27379310344827584,
76     0.55500000000000005, 1.0448275862068965, 1.5686206896551724,
77     2.7260344827586205, 3.9756896551724137, 5.5051724137931037,
78     7.6129310344827585, 10.310344827586206, 13.517931034482759,
79     17.027586206896551, 21.260862068965519, 25.73741379310345,
80     30.967241379310344, 37.214310344827588, 44.142931034482757,
81     51.230689655172412, 58.622068965517244, 66.37155172413793,
82     75.241034482758621, 88.354310344827582, 96.904655172413797,
83     108.34051724137932, 125.1453448275862, 136.35637931034483,
84     150.76827586206898, 168.86913793103449, 185.40758620689655,
85     200.0305172413793, 213.47120689655173, 227.06482758620689,
86     242.42689655172413, 253.55879310344827, 254.97620689655173,
87     255.0, 255.0, 255.0, 255.0, 255.0],
88     
89     'V-Log': [31.429137931034482, 31.752758620689654,
90     31.977413793103448, 32.531551724137934, 32.976551724137934,
91     33.822586206896553, 34.706206896551727, 36.100344827586206,
92     37.966551724137929, 40.231724137931032, 42.8301724137931,
93     46.389482758620687, 50.373103448275863, 54.91965517241379,
94     60.101724137931036, 65.1351724137931, 70.557586206896545,
95     76.224827586206899, 81.544655172413798, 87.275517241379305,
96     93.204999999999998, 100.89396551724138, 105.2244827586207,
97     111.05396551724138, 118.80051724137931, 123.33396551724138,
98     129.31517241379311, 135.62620689655174, 141.68793103448274,
99     148.09086206896552, 154.73741379310346, 160.67603448275861,
100     167.00999999999999, 173.17275862068965, 179.17706896551724,
101     185.62896551724137, 190.93706896551726, 191.0, 191.0, 191.0],
102     
103     'Standard': [0.019827586206896553, 0.030344827586206897,
104     0.1296551724137931, 0.39862068965517239, 0.84431034482758616,
105     1.5265517241379309, 2.3403448275862071, 3.3762068965517242,
106     4.9127586206896554, 6.6224137931034486, 8.8127586206896549,
107     11.51948275862069, 14.727241379310344, 18.599482758620688,
108     23.895862068965517, 30.157413793103448, 38.333275862068966,
109     47.146724137931038, 57.659999999999997, 68.3953448275862,
110     81.231034482758616, 99.765517241379314, 111.80913793103448,
111     127.22586206896551, 147.36137931034483, 160.73448275862069,
112     174.77620689655171, 188.09258620689656, 200.56137931034482,
113     212.24275862068964, 222.05310344827586, 231.96775862068966,
114     240.85534482758621, 250.1894827586207, 255.0, 255.0, 255.0,
115     255.0, 255.0, 255.0],
116     
117     'BW': [0.012068965517241379, 0.03017241379310345,
118     0.15879310344827585, 0.40379310344827585, 1.039655172413793,
119     1.5532758620689655, 2.5448275862068965, 3.6275862068965519,
120     5.1298275862068969, 7.1593103448275865, 9.1227586206896554,
121     12.219482758620689, 15.602413793103448, 19.667068965517242,
122     25.558793103448277, 32.138103448275864, 40.762413793103448,
123     50.295862068965519, 61.059655172413791, 72.762758620689652,
124     86.235172413793109, 105.61879310344827, 118.42362068965517,
125     134.05362068965516, 155.23034482758621, 168.7403448275862,
126     182.65913793103448, 195.72913793103447, 208.67896551724138,
127     220.05086206896553, 229.83293103448275, 238.34137931034482,
128     246.78810344827585, 254.53086206896552, 255.0, 255.0, 255.0,
129     255.0, 255.0, 255.0],
130     
131     'Cinelike-V': [0.0, 0.0, 0.00034482758620689653,
132     0.017586206896551725, 0.16534482758620689, 0.71965517241379307,
133     1.0924137931034483, 1.8994827586206897, 2.7312068965517242,
134     3.7174137931034483, 5.0798275862068962, 7.0439655172413795,
135     9.4998275862068962, 12.756379310344828, 17.174827586206895,
136     22.375, 29.295000000000002, 37.235344827586204,
137     47.138448275862068, 58.181034482758619, 71.008103448275861,
138     89.295000000000002, 101.09810344827586, 117.49913793103448,
139     138.71793103448275, 152.10293103448276, 166.64051724137931,
140     181.39448275862068, 194.29568965517242, 206.1453448275862,
141     218.05224137931035, 229.84310344827585, 243.2646551724138,
142     254.25103448275863, 254.99310344827586, 255.0, 255.0, 255.0,
143     255.0, 255.0],
144     
145     'Portrait': [0.044999999999999998, 0.10706896551724138,
146     0.21482758620689654, 0.61827586206896556, 1.258448275862069,
147     1.9755172413793103, 2.8977586206896553, 4.243620689655172,
148     5.7982758620689658, 7.8572413793103451, 10.573620689655172,
149     13.566896551724138, 17.62396551724138, 22.091724137931035,
150     28.537068965517243, 36.138103448275864, 45.346551724137932,
151     55.862241379310348, 67.451379310344834, 79.880689655172418,
152     94.139827586206891, 113.69862068965517, 126.13396551724138,
153     141.94724137931036, 161.84068965517241, 173.89344827586206,
154     186.02206896551724, 196.99051724137931, 207.93103448275863,
155     217.54758620689654, 227.05655172413793, 235.96586206896552,
156     245.08810344827586, 253.00948275862069, 253.97603448275862,
157     254.0, 254.0, 254.0, 254.0, 254.0],
158     
159     'Custom': [0.039310344827586205, 0.1, 0.31086206896551727,
160     0.73482758620689659, 1.3484482758620691, 2.046551724137931,
161     3.1565517241379308, 4.5710344827586207, 6.1872413793103451,
162     8.3953448275862073, 10.947068965517241, 14.126724137931035,
163     18.054827586206898, 22.778965517241378, 29.185689655172414,
164     36.938448275862072, 45.952931034482759, 56.380000000000003,
165     67.094310344827591, 79.601724137931029, 93.560000000000002,
166     113.16017241379311, 125.54551724137932, 141.44189655172414,
167     162.21155172413793, 174.12517241379311, 186.3755172413793,
168     199.36862068965516, 211.13103448275862, 221.01068965517243,
169     230.70120689655172, 239.67758620689656, 249.01879310344827,
170     254.96413793103449, 255.0, 255.0, 255.0, 255.0, 255.0, 255.0]
171 }
172
173 def prepare_photostyle_to_linear(style, corr=None, debug_plot=False):
174
175     # 18% gray card correction
176     corr = 16.5
177     
178     #if not corr:
179     #    if style == 'V-Log':
180     #        corr = 16.5
181     #    else:
182     #        #corr = 17.3
183     #        corr = 16.5
184
185     exp = np.linspace( (corr-39)/3.0, corr/3.0, 40)
186     l = np.power(2, exp) * 0.18
187     vals = np.array(styles[style])
188     vals = vals/255
189
190     # remove visible edges at some exposure values
191     print 'DIR:', dir()
192     for n in [24, 21, 4]:
193         if debug_plot:
194             plt.semilogy(vals[n], l[n], 'bx' )
195         vals = np.delete(vals, n)
196         l = np.delete(l, n)
197
198     # modify equal values to get an invertable monotone function
199     force_monotone = np.linspace(0,0.00001,len(vals))
200     vals = vals+force_monotone
201
202     #interp = interpolate.Akima1DInterpolator(vals, l)
203     interp = interpolate.PchipInterpolator(vals, l, extrapolate=False)
204
205     if debug_plot:
206         plt.semilogy(vals, l, 'b+' )
207         x = np.linspace(0., 1.0, 4096)
208         y = interp(x)
209         plt.semilogy(x, y, 'r.', ms=1 )
210         x = np.linspace(0., 1.0, 256)
211         y = interp(x)
212         plt.semilogy(x, y, 'bo', ms=2 )
213         plt.show()
214
215     return interp
216
217
218 def create_gh4_style(gamut,
219                      transfer_function,
220                      lut_directory,
221                      lut_resolution_1d,
222                      aliases,
223                      style=None):
224     """
225     Creates colorspace covering the conversion from GH4 picture styles 
226     to ACES based on empirical data
227
228     Parameters
229     ----------
230     gamut : str
231         The name of the encoding gamut to use.
232     transfer_function : str
233         The name of the transfer function to use
234     lut_directory : str or unicode 
235         The directory to use when generating LUTs
236     lut_resolution_1d : int
237         The resolution of generated 1D LUTs
238     aliases : list of str
239         Aliases for this colorspace
240
241     Returns
242     -------
243     ColorSpace
244     A ColorSpace container class referencing the LUTs, matrices 
245     and identifying information for the requested colorspace.
246     """
247
248     name = 'GH4-%s - GH4-%s-Gamut' % (transfer_function, gamut)
249     if transfer_function == '':
250         name = 'Linear - GH4-%s-Gamut' % gamut
251     if gamut == '':
252         name = 'Curve - GH4-%s' % transfer_function
253
254     cs = ColorSpace(name)
255     cs.description = name
256     cs.aliases = aliases
257     cs.equality_group = ''
258     cs.family = 'Input/Panasonic/GH4'
259     cs.is_data = False
260
261     # A linear space needs allocation variables
262     if transfer_function == '':
263         cs.allocation_type = ocio.Constants.ALLOCATION_LG2
264         cs.allocation_vars = [-8, 6, 0.00390625]
265
266     cs.to_reference_transforms = []
267
268     if transfer_function != '':
269         f = prepare_photostyle_to_linear(transfer_function)
270         
271         #data = array.array('f', '\0' * lut_resolution_1d * 4)
272         #for c in range(lut_resolution_1d):
273         #    data[c] = v_log_to_linear(float(c) / (lut_resolution_1d - 1))
274         dom = np.linspace(0.0, 1.0, lut_resolution_1d)
275         data = f(dom)
276
277         # constant extrapolation
278         data_min = np.nanmin(data)
279         data_max = np.nanmax(data)
280         inds = np.where(np.isnan(data))
281         for i in inds[0]:
282             if i < lut_resolution_1d/2:
283                 data[i] = data_min
284                 #data[i] = 0.0
285             else:
286                 data[i] = data_max
287         
288         lut = 'gh4_%s_to_linear.spi1d' % transfer_function
289         genlut.write_SPI_1d(
290             os.path.join(lut_directory, lut),
291             0.0,
292             1.0,
293             data,
294             lut_resolution_1d,
295             1)
296
297         cs.to_reference_transforms.append({
298             'type': 'lutFile',
299             'path': lut,
300             'interpolation': 'linear',
301             'direction': 'forward'})
302
303     #
304     # matrix calculations based on:
305     # http://www.colour-science.org
306     #
307     
308     if gamut != '':
309
310         # Lin -> XYZ 
311         
312         if gamut == 'V-Log':
313             cs.to_reference_transforms.append({
314                 'type': 'matrix',
315                 # colour.V_GAMUT_COLOURSPACE.RGB_to_XYZ_matrix
316                 'matrix': [0.679644,  0.152211,  0.1186 , 0.,
317                            0.260686,  0.774894, -0.03558, 0.,
318                            -0.00931 , -0.004612,  1.10298, 0.,
319                            0, 0, 0,  1.],
320                  'direction': 'forward'})
321         else:
322             cs.to_reference_transforms.append({
323                 'type': 'matrix',
324                 # colour.sRGB_COLOURSPACE.RGB_to_XYZ_matrix
325                 'matrix': [0.41238656,  0.35759149,  0.18045049, 0.0,
326                            0.21263682,  0.71518298,  0.0721802 , 0.0,
327                            0.01933062,  0.11919716,  0.95037259, 0.0,
328                            0, 0, 0, 1.0],
329                 'direction': 'forward'})
330             
331         # D65 -> D50 adaption
332         #
333         # colour.chromatic_adaptation_matrix_VonKries(
334         #    colour.xy_to_XYZ(c.sRGB_COLOURSPACE.whitepoint),
335         #    colour.xy_to_XYZ(colour.ILLUMINANTS[
336         #        'CIE 1931 2 Degree Standard Observer']['D50']), 'CAT02')
337         #
338         
339         cs.to_reference_transforms.append({
340             'type': 'matrix',
341             'matrix': [1.04251352,  0.03083262, -0.05276445, 0.0,
342                        0.0221519 ,  1.00187227, -0.02105432, 0.0,
343                        -0.00116356, -0.00341802,  0.76197255, 0.0,
344                        0, 0, 0, 1.0],
345             'direction': 'forward'})
346
347         if gamut.endswith('XYZ'):
348             cs.from_reference_transforms = []
349             return cs
350
351         if gamut.endswith('Calibrated'):
352             wb = [0.]*16
353             wb[0] = calibration_wb[style][0]
354             wb[5] = calibration_wb[style][1]
355             wb[10] = calibration_wb[style][2]
356             wb[15] = 1.0
357             cs.to_reference_transforms.append({
358                 'type': 'matrix',
359                 'matrix': wb,
360                 'direction': 'forward'})
361             cs.to_reference_transforms.append({
362                 'type': 'matrix',
363                 'matrix': (calibration_matrices[style][0] + [0.0] +
364                            calibration_matrices[style][1] + [0.0] +
365                            calibration_matrices[style][2] + [0.0] +
366                            [0, 0, 0, 1.0]),
367                 'direction': 'forward'})
368
369         # D50 -> ACES WP (~D60)
370
371         # colour.chromatic_adaptation_matrix_VonKries(
372         #   colour.xy_to_XYZ(colour.ILLUMINANTS[
373         #     'CIE 1931 2 Degree Standard Observer']['D50']),
374         #   colour.xy_to_XYZ(colour.V_GAMUT_COLOURSPACE.whitepoint),
375         #   'CAT02')
376         
377         cs.to_reference_transforms.append({
378             'type': 'matrix',
379             'matrix': [ 0.95991641, -0.02931741,  0.06566143, 0.0,
380                         -0.02119542,  0.99887268,  0.02613247, 0.0,
381                         0.00137075,  0.00443593,  1.31260073, 0.0,
382                         0, 0, 0, 1.0],
383             'direction': 'forward'})
384
385         
386         # XYZ -> ACES
387
388         # colour.ACES_2065_1_COLOURSPACE.XYZ_to_RGB_matrix
389         cs.to_reference_transforms.append({
390             'type': 'matrix',
391             'matrix': [1.04981102e+00,   0.00000000e+00,  -9.74845410e-05, 0.,
392                        -4.95903023e-01,  1.37331305e+00,   9.82400365e-02, 0.,
393                        0.00000000e+00,   0.00000000e+00,   9.91252022e-01, 0.,
394                        0, 0, 0,  1.],
395             'direction': 'forward'})
396
397         #
398         # matrix given py panasonic / calculated by nuke
399         # for one step transformation:
400         #
401         
402         # if gamut == 'V-Log':
403         #         cs.to_reference_transforms.append({
404         #             'type': 'matrix',
405         #             'matrix': [0.724383  ,  0.166748 ,  0.108497, 0.,
406         #                        0.021354  ,  0.985138 , -0.006319, 0.,
407         #                        -0.009234 , -0.001043 ,  1.010273, 0.,
408         #                        0, 0, 0,  1.],
409         #              'direction': 'forward'})
410         # elif gamut != '':
411         #         cs.to_reference_transforms.append({
412         #             'type': 'matrix',
413         #             'matrix': [0.43964687, 0.38298151, 0.17737158, 0.0,
414         #                        0.08978026, 0.81343973, 0.09677973, 0.0,
415         #                        0.01754464, 0.11155707, 0.87089837, 0.0,
416         #                        0, 0, 0, 1.0],
417         #             'direction': 'forward'})
418
419     cs.from_reference_transforms = []
420     return cs
421
422
423 def create_colorspaces(lut_directory, lut_resolution_1d):
424     """
425     Generates the colorspace conversions.
426
427     Parameters
428     ----------
429     lut_directory : str or unicode 
430         The directory to use when generating LUTs
431     lut_resolution_1d : int
432         The resolution of generated 1D LUTs
433
434     Returns
435     -------
436     list
437          A list of colorspaces for Panasonic cameras and encodings 
438     """
439     
440     colorspaces = []
441     
442     styles_sort = styles.keys()
443     styles_sort.sort()
444     
445     for style in styles_sort: 
446
447         # Full conversion
448         style_1 = create_gh4_style(
449             style,
450             style, 
451             lut_directory,
452             lut_resolution_1d,
453             ['gh4_%s_gh4_gamut' % style.lower().replace('-','_')])
454         colorspaces.append(style_1)
455
456         # Linearization Only
457         style_2 = create_gh4_style(
458             '',
459             style,
460             lut_directory,
461             lut_resolution_1d,
462             ['crv_gh4_%s' % style.lower().replace('-','_')])
463         colorspaces.append(style_2)
464
465         # Linearization and XYZ conversion
466         style_3 = create_gh4_style(
467             '%s-XYZ' % style,
468             style,
469             lut_directory,
470             lut_resolution_1d,
471             ['gh4_%s_xyz' % style.lower().replace('-','_')])
472         colorspaces.append(style_3)
473
474         # Primaries Only
475         style_4 = create_gh4_style(
476             style,
477             '',
478             lut_directory,
479             lut_resolution_1d,
480             ['lin_gh4_%s_gamut' % style.lower().replace('-','_')])
481         colorspaces.append(style_4)
482
483         if style != 'BW':
484             
485             # Full conversion calibrated
486             style_5 = create_gh4_style(
487                 '%s-Calibrated' % style,
488                 style, 
489                 lut_directory,
490                 lut_resolution_1d,
491                 ['gh4_%s_gh4_calibrated_gamut'% style.lower().replace('-','_')],
492                 style)
493             colorspaces.append(style_5)
494
495             # Primaries Only calibrated
496             style_6 = create_gh4_style(
497                 '%s-Calibrated' % style,
498                 '',
499                 lut_directory,
500                 lut_resolution_1d,
501                 ['lin_gh4_%s_calibrated_gamut'%style.lower().replace('-','_')],
502                 style)
503             colorspaces.append(style_6)
504
505             
506     return colorspaces
507