Skip to content
This repository was archived by the owner on Nov 24, 2024. It is now read-only.

Commit f7c2138

Browse files
committed
Implement support for decoding CCTFs.
1 parent 733728a commit f7c2138

File tree

9 files changed

+282
-91
lines changed

9 files changed

+282
-91
lines changed

app.py

Lines changed: 29 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,10 @@
1616
from colour.utilities import domain_range_scale
1717

1818
from colour_analysis import (
19-
COLOURSPACE_MODEL, IMAGE_COLOURSPACE, PRIMARY_COLOURSPACE,
19+
COLOURSPACE_MODEL, IMAGE_COLOURSPACE, IMAGE_DECODING_CCTF, PRIMARY_COLOURSPACE,
2020
RGB_colourspaces, RGB_colourspace_volume_visual, SECONDARY_COLOURSPACE,
21-
colourspace_models, spectral_locus_visual, RGB_image_scatter_visual,
22-
image_data)
21+
colourspace_models, decoding_cctfs, spectral_locus_visual,
22+
RGB_image_scatter_visual, image_data)
2323

2424
__author__ = 'Colour Developers'
2525
__copyright__ = 'Copyright (C) 2018 - Colour Developers'
@@ -41,7 +41,7 @@
4141

4242
__all__ = [
4343
'APP', 'CACHE', 'CACHE_DEFAULT_TIMEOUT', 'IMAGES_DIRECTORY',
44-
'images_response', 'colourspace_models_response',
44+
'images_response', 'decoding_cctfs_response','colourspace_models_response',
4545
'RGB_colourspaces_response', 'RGB_colourspace_volume_visual_response',
4646
'spectral_locus_visual_response', 'RGB_image_scatter_visual_response',
4747
'image_data_response', 'index', 'after_request'
@@ -153,6 +153,26 @@ def images_response():
153153
return response
154154

155155

156+
@APP.route('/decoding-cctfs')
157+
@CACHE.cached(timeout=CACHE_DEFAULT_TIMEOUT, query_string=True)
158+
def decoding_cctfs_response():
159+
"""
160+
Returns the decoding colour component transfer functions response.
161+
162+
Returns
163+
-------
164+
Response
165+
Decoding colour component transfer functions response.
166+
"""
167+
168+
json_data = decoding_cctfs()
169+
170+
response = Response(json_data, status=200, mimetype='application/json')
171+
response.headers['X-Content-Length'] = len(json_data)
172+
173+
return response
174+
175+
156176
@APP.route('/colourspace-models')
157177
@CACHE.cached(timeout=CACHE_DEFAULT_TIMEOUT, query_string=True)
158178
def colourspace_models_response():
@@ -265,6 +285,7 @@ def RGB_image_scatter_visual_response(image):
265285
secondary_colourspace=args.get('secondaryColourspace',
266286
SECONDARY_COLOURSPACE),
267287
image_colourspace=args.get('imageColourspace', IMAGE_COLOURSPACE),
288+
image_decoding_cctf=args.get('imageDecodingCctf', IMAGE_DECODING_CCTF),
268289
colourspace_model=args.get('colourspaceModel', COLOURSPACE_MODEL),
269290
out_of_primary_colourspace_gamut=_bool_to_bool(
270291
args.get('outOfPrimaryColourspaceGamut', False)),
@@ -302,6 +323,7 @@ def image_data_response(image):
302323
secondary_colourspace=args.get('secondaryColourspace',
303324
SECONDARY_COLOURSPACE),
304325
image_colourspace=args.get('imageColourspace', IMAGE_COLOURSPACE),
326+
image_decoding_cctf=args.get('imageDecodingCctf', IMAGE_DECODING_CCTF),
305327
out_of_primary_colourspace_gamut=_bool_to_bool(
306328
args.get('outOfPrimaryColourspaceGamut', False)),
307329
out_of_secondary_colourspace_gamut=_bool_to_bool(
@@ -332,7 +354,8 @@ def index():
332354
image=os.listdir(IMAGES_DIRECTORY)[0],
333355
primary_colourspace=PRIMARY_COLOURSPACE,
334356
secondary_colourspace=SECONDARY_COLOURSPACE,
335-
image_colourspace=IMAGE_COLOURSPACE)
357+
image_colourspace=IMAGE_COLOURSPACE,
358+
image_decoding_cctf=IMAGE_DECODING_CCTF)
336359

337360

338361
@APP.after_request
@@ -342,8 +365,7 @@ def after_request(response):
342365
'Content-Type,Authorization')
343366
response.headers.add('Access-Control-Allow-Methods',
344367
'GET,PUT,POST,DELETE,OPTIONS')
345-
response.headers.add('Access-Control-Expose-Headers',
346-
'X-Content-Length')
368+
response.headers.add('Access-Control-Expose-Headers', 'X-Content-Length')
347369

348370
return response
349371

colour_analysis.py

Lines changed: 72 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,13 @@
1313
import numpy as np
1414
import os
1515
import re
16+
from collections import OrderedDict
1617
from werkzeug.contrib.cache import SimpleCache
1718

18-
from colour import RGB_COLOURSPACES, RGB_to_XYZ, read_image
19-
from colour.models import (XYZ_to_colourspace_model, XYZ_to_RGB, RGB_to_RGB,
20-
oetf_reverse_sRGB)
19+
from colour import (LOG_DECODING_CURVES, OETFS_REVERSE,
20+
RGB_COLOURSPACES, RGB_to_RGB, RGB_to_XYZ, XYZ_to_RGB,
21+
read_image)
22+
from colour.models import XYZ_to_colourspace_model, function_gamma
2123
from colour.plotting import filter_cmfs, filter_RGB_colourspaces
2224
from colour.utilities import first_item, normalise_maximum, tsplit, tstack
2325

@@ -30,11 +32,12 @@
3032

3133
__all__ = [
3234
'LINEAR_FILE_FORMATS', 'DEFAULT_FLOAT_DTYPE', 'COLOURSPACE_MODELS',
33-
'COLOURSPACE_MODELS_LABELS', 'PRIMARY_COLOURSPACE',
34-
'SECONDARY_COLOURSPACE', 'IMAGE_COLOURSPACE', 'COLOURSPACE_MODEL',
35-
'IMAGE_CACHE', 'load_image', 'colourspace_model_axis_reorder',
36-
'colourspace_model_faces_reorder', 'colourspace_models',
37-
'RGB_colourspaces', 'buffer_geometry', 'create_plane', 'create_box',
35+
'COLOURSPACE_MODELS_LABELS', 'DECODING_CCTFS', 'PRIMARY_COLOURSPACE',
36+
'SECONDARY_COLOURSPACE', 'IMAGE_COLOURSPACE', 'IMAGE_DECODING_CCTF',
37+
'COLOURSPACE_MODEL', 'IMAGE_CACHE', 'load_image',
38+
'colourspace_model_axis_reorder', 'colourspace_model_faces_reorder',
39+
'decoding_cctfs', 'colourspace_models', 'RGB_colourspaces',
40+
'buffer_geometry', 'create_plane', 'create_box',
3841
'RGB_colourspace_volume_visual', 'spectral_locus_visual',
3942
'RGB_image_scatter_visual', 'image_data'
4043
]
@@ -93,6 +96,21 @@
9396
'hdr-CIELAB', 'hdr-IPT'}**
9497
"""
9598

99+
DECODING_CCTFS = OrderedDict()
100+
DECODING_CCTFS.update(
101+
sorted({
102+
'Gamma 2.2': lambda x: function_gamma(x, 2.2),
103+
'Gamma 2.4': lambda x: function_gamma(x, 2.4),
104+
'Gamma 2.6': lambda x: function_gamma(x, 2.6)
105+
}.items()))
106+
DECODING_CCTFS.update(sorted(OETFS_REVERSE.items()))
107+
DECODING_CCTFS.update(sorted(LOG_DECODING_CURVES.items()))
108+
"""
109+
Decoding colour component transfer functions.
110+
111+
DECODING_CCTFS : OrderedDict
112+
"""
113+
96114
PRIMARY_COLOURSPACE = 'sRGB'
97115
"""
98116
Primary analysis RGB colourspace.
@@ -114,6 +132,13 @@
114132
IMAGE_COLOURSPACE : unicode
115133
"""
116134

135+
IMAGE_DECODING_CCTF = 'sRGB'
136+
"""
137+
Analysed image RGB colourspace decoding colour component transfer function.
138+
139+
IMAGE_DECODING_CCTF : unicode
140+
"""
141+
117142
COLOURSPACE_MODEL = 'CIE xyY'
118143
"""
119144
Analysis colour model.
@@ -132,7 +157,7 @@
132157
"""
133158

134159

135-
def load_image(path):
160+
def load_image(path, decoding_cctf='sRGB'):
136161
"""
137162
Loads the image at given path and caches it in `IMAGE_CACHE` cache. If the
138163
image is already cached, it is returned directly.
@@ -141,21 +166,28 @@ def load_image(path):
141166
----------
142167
path : unicode
143168
Image path.
169+
decoding_cctf : unicode, optional
170+
Decoding colour component transfer function (Decoding CCTF) /
171+
electro-optical transfer function (EOTF / EOCF) that maps an
172+
:math:`R'G'B'` video component signal value to tristimulus values at
173+
the display.
144174
145175
Returns
146176
-------
147177
ndarray
148178
Image as a ndarray.
149179
"""
150180

151-
RGB = IMAGE_CACHE.get(path)
181+
key = '{0}-{1}'.format(path, decoding_cctf)
182+
183+
RGB = IMAGE_CACHE.get(key)
152184
if RGB is None:
153185
RGB = read_image(path)
154186

155187
if os.path.splitext(path)[-1].lower() not in LINEAR_FILE_FORMATS:
156-
RGB = oetf_reverse_sRGB(RGB)
188+
RGB = DECODING_CCTFS[decoding_cctf](RGB)
157189

158-
IMAGE_CACHE.set(path, RGB)
190+
IMAGE_CACHE.set(key, RGB)
159191

160192
return RGB
161193

@@ -220,6 +252,20 @@ def colourspace_model_faces_reorder(a, model=None):
220252
return a
221253

222254

255+
def decoding_cctfs():
256+
"""
257+
Returns the decoding colour component transfer functions formatted as
258+
*JSON*.
259+
260+
Returns
261+
-------
262+
unicode
263+
Decoding colour component transfer functions formatted as *JSON*.
264+
"""
265+
266+
return json.dumps(DECODING_CCTFS.keys())
267+
268+
223269
def colourspace_models():
224270
"""
225271
Returns the colourspace models formatted as *JSON*.
@@ -608,6 +654,7 @@ def RGB_image_scatter_visual(path,
608654
primary_colourspace=PRIMARY_COLOURSPACE,
609655
secondary_colourspace=SECONDARY_COLOURSPACE,
610656
image_colourspace=IMAGE_COLOURSPACE,
657+
image_decoding_cctf=IMAGE_DECODING_CCTF,
611658
colourspace_model=COLOURSPACE_MODEL,
612659
out_of_primary_colourspace_gamut=False,
613660
out_of_secondary_colourspace_gamut=False,
@@ -628,6 +675,11 @@ def RGB_image_scatter_visual(path,
628675
image_colourspace: unicode, optional
629676
**{'Primary', 'Secondary'}**,
630677
Analysed image RGB colourspace.
678+
image_decoding_cctf : unicode, optional
679+
Analysed image decoding colour component transfer function
680+
(Decoding CCTF) / electro-optical transfer function (EOTF / EOCF) that
681+
maps an :math:`R'G'B'` video component signal value to tristimulus
682+
values at the display.
631683
colourspace_model : unicode, optional
632684
Colourspace model used to generate the visual geometry.
633685
out_of_primary_colourspace_gamut : bool, optional
@@ -659,7 +711,7 @@ def RGB_image_scatter_visual(path,
659711
colourspace = (primary_colourspace if image_colourspace == 'Primary' else
660712
secondary_colourspace)
661713

662-
RGB = load_image(path)
714+
RGB = load_image(path, image_decoding_cctf)
663715

664716
if saturate:
665717
RGB = np.clip(RGB, 0, 1)
@@ -698,6 +750,7 @@ def image_data(path,
698750
primary_colourspace=PRIMARY_COLOURSPACE,
699751
secondary_colourspace=SECONDARY_COLOURSPACE,
700752
image_colourspace=IMAGE_COLOURSPACE,
753+
image_decoding_cctf=IMAGE_DECODING_CCTF,
701754
out_of_primary_colourspace_gamut=False,
702755
out_of_secondary_colourspace_gamut=False,
703756
saturate=False):
@@ -716,6 +769,11 @@ def image_data(path,
716769
image_colourspace: unicode, optional
717770
**{'Primary', 'Secondary'}**,
718771
Analysed image RGB colourspace.
772+
image_decoding_cctf : unicode, optional
773+
Analysed image decoding colour component transfer function
774+
(Decoding CCTF) / electro-optical transfer function (EOTF / EOCF) that
775+
maps an :math:`R'G'B'` video component signal value to tristimulus
776+
values at the display.
719777
out_of_primary_colourspace_gamut : bool, optional
720778
Whether to only generate the out of primary RGB colourspace gamut
721779
values.
@@ -738,7 +796,7 @@ def image_data(path,
738796
filter_RGB_colourspaces('^{0}$'.format(
739797
re.escape(secondary_colourspace))))
740798

741-
RGB = load_image(path)
799+
RGB = load_image(path, image_decoding_cctf)
742800

743801
if saturate:
744802
RGB = np.clip(RGB, 0, 1)

dist/colour-analysis.js

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/views/gamut-view.js

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,8 @@ class GamutView extends PerspectiveView {
3232
colourspaceModel: 'CIE xyY',
3333
primaryColourspace: 'sRGB',
3434
secondaryColourspace: 'DCI-P3',
35-
imageColourspace: 'Primary'
35+
imageColourspace: 'Primary',
36+
imageDecodingCctf: 'sRGB'
3637
},
3738
...settings
3839
};
@@ -78,6 +79,7 @@ class GamutView extends PerspectiveView {
7879
this._primaryColourspace = settings.primaryColourspace;
7980
this._secondaryColourspace = settings.secondaryColourspace;
8081
this._imageColourspace = settings.imageColourspace;
82+
this._imageDecodingCctf = settings.imageDecodingCctf;
8183

8284
this._viewAxesVisual = undefined;
8385

@@ -222,6 +224,22 @@ class GamutView extends PerspectiveView {
222224
}
223225
}
224226

227+
get imageDecodingCctf() {
228+
return this._imageDecodingCctf;
229+
}
230+
231+
set imageDecodingCctf(value) {
232+
this._imageDecodingCctf = value;
233+
234+
if (this._imageScatterVisual != undefined) {
235+
this._imageScatterVisual.imageDecodingCctf = value;
236+
}
237+
238+
if (this._imageScatterOverlayVisual != undefined) {
239+
this._imageScatterOverlayVisual.imageDecodingCctf = value;
240+
}
241+
}
242+
225243
get viewAxesVisual() {
226244
return this._viewAxesVisual;
227245
}
@@ -335,6 +353,7 @@ class GamutView extends PerspectiveView {
335353
primaryColourspace: this._primaryColourspace,
336354
secondaryColourspace: this._secondaryColourspace,
337355
imageColourspace: this._imageColourspace,
356+
imageDecodingCctf: this._imageDecodingCctf,
338357
colourspaceModel: this._colourspaceModel
339358
},
340359
...settings
@@ -353,6 +372,7 @@ class GamutView extends PerspectiveView {
353372
primaryColourspace: this._primaryColourspace,
354373
secondaryColourspace: this._secondaryColourspace,
355374
imageColourspace: this._imageColourspace,
375+
imageDecodingCctf: this._imageDecodingCctf,
356376
colourspaceModel: this._colourspaceModel,
357377
uniformOpacity: 0.5
358378
},

src/views/image-view.js

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,8 @@ class ImageView extends OrthographicView {
1919
colourspaceModel: 'CIE xyY',
2020
primaryColourspace: 'sRGB',
2121
secondaryColourspace: 'DCI-P3',
22-
imageColourspace: 'Primary'
22+
imageColourspace: 'Primary',
23+
imageDecodingCctf: 'sRGB'
2324
},
2425
...settings
2526
};
@@ -36,6 +37,7 @@ class ImageView extends OrthographicView {
3637
this._primaryColourspace = settings.primaryColourspace;
3738
this._secondaryColourspace = settings.secondaryColourspace;
3839
this._imageColourspace = settings.imageColourspace;
40+
this._imageDecodingCctf = settings.imageDecodingCctf;
3941

4042
// The following groups are defining the rendering order.
4143
this._imageVisualGroup = new THREE.Group();
@@ -116,6 +118,22 @@ class ImageView extends OrthographicView {
116118
}
117119
}
118120

121+
get imageDecodingCctf() {
122+
return this._imageDecodingCctf;
123+
}
124+
125+
set imageDecodingCctf(value) {
126+
this._imageDecodingCctf = value;
127+
128+
if (this._imageVisual != undefined) {
129+
this._imageVisual.imageDecodingCctf = value;
130+
}
131+
132+
if (this._imageOverlayVisual != undefined) {
133+
this._imageOverlayVisual.imageDecodingCctf = value;
134+
}
135+
}
136+
119137
get imageVisual() {
120138
return this._imageVisual;
121139
}
@@ -139,7 +157,9 @@ class ImageView extends OrthographicView {
139157
image: this._image,
140158
primaryColourspace: this._primaryColourspace,
141159
secondaryColourspace: this._secondaryColourspace,
142-
imageColourspace: this._imageColourspace
160+
imageColourspace: this._imageColourspace,
161+
imageDecodingCctf: this._imageDecodingCctf
162+
143163
},
144164
...settings
145165
});
@@ -156,6 +176,7 @@ class ImageView extends OrthographicView {
156176
primaryColourspace: this._primaryColourspace,
157177
secondaryColourspace: this._secondaryColourspace,
158178
imageColourspace: this._imageColourspace,
179+
imageDecodingCctf: this._imageDecodingCctf,
159180
uniformOpacity: 0.5,
160181
depth: 0.5
161182
},

0 commit comments

Comments
 (0)