11611faf32f6cb9b97b9fd8cae9e3ea86e983f94
[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] = 1/calibration_wb[style][0]
359             wb[5] = 1/calibration_wb[style][1]
360             wb[10] = 1/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
367             # calibrat_matrices = clibration_matrices_argyll
368             
369             cs.to_reference_transforms.append({
370                 'type': 'matrix',
371                 'matrix': (calibration_matrices[style][0] + [0.0] +
372                            calibration_matrices[style][1] + [0.0] +
373                            calibration_matrices[style][2] + [0.0] +
374                            [0, 0, 0, 1.0]),
375                 'direction': 'forward'})
376
377         # D50 -> ACES WP (~D60)
378
379         # colour.chromatic_adaptation_matrix_VonKries(
380         #   colour.xy_to_XYZ(colour.ILLUMINANTS[
381         #     'CIE 1931 2 Degree Standard Observer']['D50']),
382         #   colour.xy_to_XYZ(colour.ACES_2065_1_COLOURSPACE.whitepoint),
383         #   'CAT02')
384         
385         cs.to_reference_transforms.append({
386             'type': 'matrix',
387             'matrix': [ 9.71197151e-01,  -2.17328334e-02,  4.59761326e-02, 0.0,
388                         -1.56890302e-02,  1.00002082e+00,  1.83070138e-02, 0.0,
389                         9.70076767e-04,   3.08175255e-03,  1.21767128e+00, 0.0,
390                         0, 0, 0, 1.0],
391             'direction': 'forward'})
392
393         
394         # XYZ -> ACES
395
396         # colour.ACES_2065_1_COLOURSPACE.XYZ_to_RGB_matrix
397         cs.to_reference_transforms.append({
398             'type': 'matrix',
399             'matrix': [1.04981102e+00,   0.00000000e+00,  -9.74845410e-05, 0.,
400                        -4.95903023e-01,  1.37331305e+00,   9.82400365e-02, 0.,
401                        0.00000000e+00,   0.00000000e+00,   9.91252022e-01, 0.,
402                        0, 0, 0,  1.],
403             'direction': 'forward'})
404
405         #
406         # matrix given py panasonic / calculated by nuke
407         # for one step transformation:
408         #
409         
410         # if gamut == 'V-Log':
411         #         cs.to_reference_transforms.append({
412         #             'type': 'matrix',
413         #             'matrix': [0.724383  ,  0.166748 ,  0.108497, 0.,
414         #                        0.021354  ,  0.985138 , -0.006319, 0.,
415         #                        -0.009234 , -0.001043 ,  1.010273, 0.,
416         #                        0, 0, 0,  1.],
417         #              'direction': 'forward'})
418         # elif gamut != '':
419         #         cs.to_reference_transforms.append({
420         #             'type': 'matrix',
421         #             'matrix': [0.43964687, 0.38298151, 0.17737158, 0.0,
422         #                        0.08978026, 0.81343973, 0.09677973, 0.0,
423         #                        0.01754464, 0.11155707, 0.87089837, 0.0,
424         #                        0, 0, 0, 1.0],
425         #             'direction': 'forward'})
426
427     cs.from_reference_transforms = []
428     return cs
429
430
431 def create_colorspaces(lut_directory, lut_resolution_1d):
432     """
433     Generates the colorspace conversions.
434
435     Parameters
436     ----------
437     lut_directory : str or unicode 
438         The directory to use when generating LUTs
439     lut_resolution_1d : int
440         The resolution of generated 1D LUTs
441
442     Returns
443     -------
444     list
445          A list of colorspaces for Panasonic cameras and encodings 
446     """
447     
448     colorspaces = []
449     
450     styles_sort = styles.keys()
451     styles_sort.sort()
452     
453     for style in styles_sort: 
454
455         # Full conversion
456         style_1 = create_gh4_style(
457             style,
458             style, 
459             lut_directory,
460             lut_resolution_1d,
461             ['gh4_%s_gh4_gamut' % style.lower().replace('-','_')])
462         colorspaces.append(style_1)
463
464         # Linearization Only
465         style_2 = create_gh4_style(
466             '',
467             style,
468             lut_directory,
469             lut_resolution_1d,
470             ['crv_gh4_%s' % style.lower().replace('-','_')])
471         colorspaces.append(style_2)
472
473         # Linearization and XYZ conversion
474         style_3 = create_gh4_style(
475             '%s' % style,
476             style,
477             lut_directory,
478             lut_resolution_1d,
479             ['gh4_%s_xyz' % style.lower().replace('-','_')],
480             variant='XYZ')
481         colorspaces.append(style_3)
482
483         # Primaries Only
484         style_4 = create_gh4_style(
485             style,
486             '',
487             lut_directory,
488             lut_resolution_1d,
489             ['lin_gh4_%s_gamut' % style.lower().replace('-','_')])
490         colorspaces.append(style_4)
491
492         if style != 'BW':
493             
494             # Full conversion calibrated
495             style_5 = create_gh4_style(
496                 '%s' % style,
497                 style, 
498                 lut_directory,
499                 lut_resolution_1d,
500                 ['gh4_%s_gh4_calibrated_gamut'% style.lower().replace('-','_')],
501                 style,
502                 variant='Calibrated')
503             colorspaces.append(style_5)
504
505             # Primaries Only calibrated
506             style_6 = create_gh4_style(
507                 '%s-Calibrated' % style,
508                 '',
509                 lut_directory,
510                 lut_resolution_1d,
511                 ['lin_gh4_%s_calibrated_gamut'%style.lower().replace('-','_')],
512                 style,
513                 variant='Calibrated')
514             colorspaces.append(style_6)
515
516             
517     return colorspaces