1d0c95b76c5edd4bc057620fdd657cf4033410c6
[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                      variant=''):
225     """
226     Creates colorspace covering the conversion from GH4 picture styles 
227     to ACES based on empirical data
228
229     Parameters
230     ----------
231     gamut : str
232         The name of the encoding gamut to use.
233     transfer_function : str
234         The name of the transfer function to use
235     lut_directory : str or unicode 
236         The directory to use when generating LUTs
237     lut_resolution_1d : int
238         The resolution of generated 1D LUTs
239     aliases : list of str
240         Aliases for this colorspace
241     style : str
242         Associated Photo Style
243     variant : str
244         Variant of processing
245
246     Returns
247     -------
248     ColorSpace
249     A ColorSpace container class referencing the LUTs, matrices 
250     and identifying information for the requested colorspace.
251     """
252
253     name = ('GH4-%s - GH4-%s-Gamut %s' % (transfer_function, gamut, variant)).strip()
254     if transfer_function == '':
255         name = ('Linear - GH4-%s-Gamut %s' % (gamut, variant)).strip()
256     if gamut == '':
257         name = 'Curve - GH4-%s' % transfer_function
258
259     cs = ColorSpace(name)
260     cs.description = name
261     cs.aliases = aliases
262     cs.equality_group = ''
263     cs.family = 'Input/Panasonic/GH4'
264     cs.is_data = False
265
266     # A linear space needs allocation variables
267     if transfer_function == '':
268         cs.allocation_type = ocio.Constants.ALLOCATION_LG2
269         cs.allocation_vars = [-8, 6, 0.00390625]
270
271     cs.to_reference_transforms = []
272
273     if transfer_function != '':
274         f = prepare_photostyle_to_linear(transfer_function)
275         
276         #data = array.array('f', '\0' * lut_resolution_1d * 4)
277         #for c in range(lut_resolution_1d):
278         #    data[c] = v_log_to_linear(float(c) / (lut_resolution_1d - 1))
279         dom = np.linspace(0.0, 1.0, lut_resolution_1d)
280         data = f(dom)
281
282         # constant extrapolation
283         data_min = np.nanmin(data)
284         data_max = np.nanmax(data)
285         inds = np.where(np.isnan(data))
286         for i in inds[0]:
287             if i < lut_resolution_1d/2:
288                 data[i] = data_min
289                 #data[i] = 0.0
290             else:
291                 data[i] = data_max
292         
293         lut = 'gh4_%s_to_linear.spi1d' % transfer_function
294         genlut.write_SPI_1d(
295             os.path.join(lut_directory, lut),
296             0.0,
297             1.0,
298             data,
299             lut_resolution_1d,
300             1)
301
302         cs.to_reference_transforms.append({
303             'type': 'lutFile',
304             'path': lut,
305             'interpolation': 'linear',
306             'direction': 'forward'})
307
308     #
309     # matrix calculations based on:
310     # http://www.colour-science.org
311     #
312     
313     if gamut != '':
314
315         # Lin -> XYZ 
316         
317         if gamut == 'V-Log':
318             cs.to_reference_transforms.append({
319                 'type': 'matrix',
320                 # colour.V_GAMUT_COLOURSPACE.RGB_to_XYZ_matrix
321                 'matrix': [0.679644,  0.152211,  0.1186 , 0.,
322                            0.260686,  0.774894, -0.03558, 0.,
323                            -0.00931 , -0.004612,  1.10298, 0.,
324                            0, 0, 0,  1.],
325                  'direction': 'forward'})
326         else:
327             cs.to_reference_transforms.append({
328                 'type': 'matrix',
329                 # colour.sRGB_COLOURSPACE.RGB_to_XYZ_matrix
330                 'matrix': [0.41238656,  0.35759149,  0.18045049, 0.0,
331                            0.21263682,  0.71518298,  0.0721802 , 0.0,
332                            0.01933062,  0.11919716,  0.95037259, 0.0,
333                            0, 0, 0, 1.0],
334                 'direction': 'forward'})
335             
336         # D65 -> D50 adaption
337         #
338         # colour.chromatic_adaptation_matrix_VonKries(
339         #    colour.xy_to_XYZ(c.sRGB_COLOURSPACE.whitepoint),
340         #    colour.xy_to_XYZ(colour.ILLUMINANTS[
341         #        'CIE 1931 2 Degree Standard Observer']['D50']), 'CAT02')
342         #
343         
344         cs.to_reference_transforms.append({
345             'type': 'matrix',
346             'matrix': [1.04251352,  0.03083262, -0.05276445, 0.0,
347                        0.0221519 ,  1.00187227, -0.02105432, 0.0,
348                        -0.00116356, -0.00341802,  0.76197255, 0.0,
349                        0, 0, 0, 1.0],
350             'direction': 'forward'})
351
352         if variant == ('XYZ'):
353             cs.from_reference_transforms = []
354             return cs
355
356         if variant == ('Calibrated'):
357             # wb = [0.]*16
358             # wb[0] = calibration_wb[style][0]
359             # wb[5] = calibration_wb[style][1]
360             # wb[10] = calibration_wb[style][2]
361             # wb[15] = 1.0
362             # cs.to_reference_transforms.append({
363             #     'type': 'matrix',
364             #     'matrix': wb,
365             #     'direction': 'forward'})
366             cs.to_reference_transforms.append({
367                 'type': 'matrix',
368                 'matrix': (calibration_matrices[style][0] + [0.0] +
369                            calibration_matrices[style][1] + [0.0] +
370                            calibration_matrices[style][2] + [0.0] +
371                            [0, 0, 0, 1.0]),
372                 'direction': 'forward'})
373
374         # D50 -> ACES WP (~D60)
375
376         # colour.chromatic_adaptation_matrix_VonKries(
377         #   colour.xy_to_XYZ(colour.ILLUMINANTS[
378         #     'CIE 1931 2 Degree Standard Observer']['D50']),
379         #   colour.xy_to_XYZ(colour.V_GAMUT_COLOURSPACE.whitepoint),
380         #   'CAT02')
381         
382         cs.to_reference_transforms.append({
383             'type': 'matrix',
384             'matrix': [ 0.95991641, -0.02931741,  0.06566143, 0.0,
385                         -0.02119542,  0.99887268,  0.02613247, 0.0,
386                         0.00137075,  0.00443593,  1.31260073, 0.0,
387                         0, 0, 0, 1.0],
388             'direction': 'forward'})
389
390         
391         # XYZ -> ACES
392
393         # colour.ACES_2065_1_COLOURSPACE.XYZ_to_RGB_matrix
394         cs.to_reference_transforms.append({
395             'type': 'matrix',
396             'matrix': [1.04981102e+00,   0.00000000e+00,  -9.74845410e-05, 0.,
397                        -4.95903023e-01,  1.37331305e+00,   9.82400365e-02, 0.,
398                        0.00000000e+00,   0.00000000e+00,   9.91252022e-01, 0.,
399                        0, 0, 0,  1.],
400             'direction': 'forward'})
401
402         #
403         # matrix given py panasonic / calculated by nuke
404         # for one step transformation:
405         #
406         
407         # if gamut == 'V-Log':
408         #         cs.to_reference_transforms.append({
409         #             'type': 'matrix',
410         #             'matrix': [0.724383  ,  0.166748 ,  0.108497, 0.,
411         #                        0.021354  ,  0.985138 , -0.006319, 0.,
412         #                        -0.009234 , -0.001043 ,  1.010273, 0.,
413         #                        0, 0, 0,  1.],
414         #              'direction': 'forward'})
415         # elif gamut != '':
416         #         cs.to_reference_transforms.append({
417         #             'type': 'matrix',
418         #             'matrix': [0.43964687, 0.38298151, 0.17737158, 0.0,
419         #                        0.08978026, 0.81343973, 0.09677973, 0.0,
420         #                        0.01754464, 0.11155707, 0.87089837, 0.0,
421         #                        0, 0, 0, 1.0],
422         #             'direction': 'forward'})
423
424     cs.from_reference_transforms = []
425     return cs
426
427
428 def create_colorspaces(lut_directory, lut_resolution_1d):
429     """
430     Generates the colorspace conversions.
431
432     Parameters
433     ----------
434     lut_directory : str or unicode 
435         The directory to use when generating LUTs
436     lut_resolution_1d : int
437         The resolution of generated 1D LUTs
438
439     Returns
440     -------
441     list
442          A list of colorspaces for Panasonic cameras and encodings 
443     """
444     
445     colorspaces = []
446     
447     styles_sort = styles.keys()
448     styles_sort.sort()
449     
450     for style in styles_sort: 
451
452         # Full conversion
453         style_1 = create_gh4_style(
454             style,
455             style, 
456             lut_directory,
457             lut_resolution_1d,
458             ['gh4_%s_gh4_gamut' % style.lower().replace('-','_')])
459         colorspaces.append(style_1)
460
461         # Linearization Only
462         style_2 = create_gh4_style(
463             '',
464             style,
465             lut_directory,
466             lut_resolution_1d,
467             ['crv_gh4_%s' % style.lower().replace('-','_')])
468         colorspaces.append(style_2)
469
470         # Linearization and XYZ conversion
471         style_3 = create_gh4_style(
472             '%s' % style,
473             style,
474             lut_directory,
475             lut_resolution_1d,
476             ['gh4_%s_xyz' % style.lower().replace('-','_')],
477             variant='XYZ')
478         colorspaces.append(style_3)
479
480         # Primaries Only
481         style_4 = create_gh4_style(
482             style,
483             '',
484             lut_directory,
485             lut_resolution_1d,
486             ['lin_gh4_%s_gamut' % style.lower().replace('-','_')])
487         colorspaces.append(style_4)
488
489         if style != 'BW':
490             
491             # Full conversion calibrated
492             style_5 = create_gh4_style(
493                 '%s' % style,
494                 style, 
495                 lut_directory,
496                 lut_resolution_1d,
497                 ['gh4_%s_gh4_calibrated_gamut'% style.lower().replace('-','_')],
498                 style,
499                 variant='Calibrated')
500             colorspaces.append(style_5)
501
502             # Primaries Only calibrated
503             style_6 = create_gh4_style(
504                 '%s-Calibrated' % style,
505                 '',
506                 lut_directory,
507                 lut_resolution_1d,
508                 ['lin_gh4_%s_calibrated_gamut'%style.lower().replace('-','_')],
509                 style,
510                 variant='Calibrated')
511             colorspaces.append(style_6)
512
513             
514     return colorspaces
515
516 calibration_matrices = {
517     
518 'Cinelike-D': [[1.165504, 0.251869, -0.118313],
519                 [-0.369525, 1.80097, -0.096786],
520                 [-0.05245, 0.097669, 1.326889]],
521  'Cinelike-V': [[1.060891, 0.353566, -0.102382],
522                 [-0.460172, 1.927755, -0.122295],
523                 [-0.129601, 0.137134, 1.373328]],
524  'Custom': [[0.946141, 0.485274, -0.087787],
525             [-0.555203, 2.05073, -0.100206],
526             [-0.128126, 0.12821, 1.426733]],
527  'Natural': [[0.874551, 0.439286, -0.085863],
528              [-0.516138, 1.916301, -0.11907],
529              [-0.071172, 0.056567, 1.347203]],
530  'Portrait': [[0.660386, 0.650877, -0.060211],
531               [-0.600758, 1.998228, -0.085769],
532               [-0.110666, 0.124363, 1.339439]],
533  'Scenery': [[0.812315, 0.534553, -0.098869],
534              [-0.542253, 1.934419, -0.071867],
535              [-0.123626, 0.142352, 1.292352]],
536  'Standard': [[0.886056, 0.415669, -0.073317],
537               [-0.482919, 1.84707, -0.088924],
538               [-0.117718, 0.126855, 1.321097]],
539  'V-Log': [[1.127527, 0.284008, -0.033005],
540            [-0.192653, 1.619262, -0.030388],
541            [-0.080181, 0.057527, 1.39992]],
542  'Vivid': [[0.754924, 0.526196, -0.061668],
543            [-0.503233, 1.851748, -0.065254],
544            [-0.197153, 0.267533, 1.212053]]
545
546 }