Skip to content

Commit b82c5be

Browse files
Merge branch 'release/v0.2.0'
2 parents 47436ad + 8124d7f commit b82c5be

28 files changed

+71529
-171
lines changed

README.rst

Lines changed: 67 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -38,30 +38,58 @@ Features
3838
The following features are available:
3939

4040
- Reading *CLF* files to a Python representation.
41-
42-
The following features are planned and in development:
43-
4441
- Writing *CLF* files from the Python representation.
45-
- Validating *CLF* files according to the specification.
42+
- Executing *CLF* workflows and applying them to colours or images.
4643

47-
Features that will not be part of this library:
48-
49-
- Executing *CLF* workflows and applying them to colours or images. This feature will be implemented as part of `Colour
50-
<https://github.com/colour-science/colour/>`__.
5144

5245
Examples
5346
^^^^^^^^
5447

55-
The main entry point of the library is the ``read_clf`` function in the main namespace.
48+
The main entry point of the library is the ``read_clf`` function in the main namespace, which allows one to parse
49+
a CLF document:
5650

5751
.. code-block:: python
5852
5953
import colour_clf_io
6054
61-
example = """
62-
<?xml version="1.0" ?>
63-
<ProcessList xmlns="urn:AMPAS:CLF:v3.0" id="Example Wrapper" compCLFversion="2.0">
64-
<LUT3D id="lut-24" name="green look" interpolation="trilinear" inBitDepth="12i" outBitDepth="16f">
55+
example = """<?xml version="1.0" ?>
56+
<ProcessList xmlns="urn:AMPAS:CLF:v3.0" id="Example Wrapper" compCLFversion="2.0">
57+
<LUT3D id="lut-24" name="green look" interpolation="trilinear" inBitDepth="12i" outBitDepth="16f">
58+
<Description>3D LUT</Description>
59+
<Array dim="2 2 2 3">
60+
0.0 0.0 0.0
61+
0.0 0.0 1.0
62+
0.0 1.0 0.0
63+
0.0 1.0 1.0
64+
1.0 0.0 0.0
65+
1.0 0.0 1.0
66+
1.0 1.0 0.0
67+
1.0 1.0 1.0
68+
</Array>
69+
</LUT3D>
70+
</ProcessList>
71+
""" # noqa: E501
72+
clf_doc = colour_clf_io.read_clf(example)
73+
print(clf_doc)
74+
75+
.. code-block:: text
76+
77+
ProcessList(id='Example Wrapper', compatible_CLF_version='3.0', process_nodes=[LUT3D(id='lut-24', name='green look', in_bit_depth=<BitDepth.i12: '12i'>, out_bit_depth=<BitDepth.f16: '16f'>, description='3D LUT', array=Array(values=[0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 1.0, 0.0, 0.0, 1.0, 1.0, 1.0, 0.0, 0.0, 1.0, 0.0, 1.0, 1.0, 1.0, 0.0, 1.0, 1.0, 1.0], dim=(2, 2, 2, 3)), half_domain=False, raw_halfs=False, interpolation=<Interpolation3D.TRILINEAR: 'trilinear'>)], name=None, inverse_of=None, description=[], input_descriptor='', output_descriptor='', info=Info(app_release=None, copyright=None, revision=None, aces_transform_id=None, aces_user_name=None, calibration_info=None))
78+
79+
For writing a CLF file the ``write_clf`` function can be used to serialise a ``ProcessList`` back to XML:
80+
81+
82+
.. code-block:: python
83+
84+
xml = colour_clf_io.write_clf(clf_doc)
85+
print(xml)
86+
87+
.. code-block:: text
88+
89+
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
90+
<ProcessList xmlns="urn:AMPAS:CLF:v3.0" compCLFversion="3.0" id="Example Wrapper">
91+
<Info/>
92+
<LUT3D id="lut-24" inBitDepth="12i" interpolation="trilinear" name="green look" outBitDepth="16f">
6593
<Description>3D LUT</Description>
6694
<Array dim="2 2 2 3">
6795
0.0 0.0 0.0
@@ -73,15 +101,21 @@ The main entry point of the library is the ``read_clf`` function in the main nam
73101
1.0 1.0 0.0
74102
1.0 1.0 1.0
75103
</Array>
76-
</LUT3D>
104+
</LUT3D>
77105
</ProcessList>
78-
""" # noqa: E501
79-
clf_doc = colour_clf_io.read_clf(EXAMPLE_WRAPPER.format(example))
80-
print(clf_doc)
106+
107+
To execute a CLF workflow, you can create a *CLFProcessList* that can be applied to some input.
108+
109+
.. code-block:: python
110+
111+
lut = colour_clf_io.CLFProcessList(clf_doc)
112+
input_value = [0, 32768, 65535]
113+
result = lut.apply(input_value)
114+
print(result)
81115
82116
.. code-block:: text
83117
84-
ProcessList(id='Example Wrapper', compatible_CLF_version='3.0', process_nodes=[LUT3D(id='lut-24', name='green look', in_bit_depth=<BitDepth.i12: '12i'>, out_bit_depth=<BitDepth.f16: '16f'>, description='3D LUT', array=Array(values=[0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 1.0, 0.0, 0.0, 1.0, 1.0, 1.0, 0.0, 0.0, 1.0, 0.0, 1.0, 1.0, 1.0, 0.0, 1.0, 1.0, 1.0], dim=(2, 2, 2, 3)), half_domain=False, raw_halfs=False, interpolation=<Interpolation3D.TRILINEAR: 'trilinear'>)], name=None, inverse_of=None, description=[], input_descriptor='', output_descriptor='', info=Info(app_release=None, copyright=None, revision=None, aces_transform_id=None, aces_user_name=None, calibration_info=None))
118+
[ 0. 1. 1.]
85119
86120
User Guide
87121
----------
@@ -98,6 +132,10 @@ Primary Dependencies
98132
- `lxml >= 5.2.1 < 6 <https://pypi.org/project/lxml/>`__
99133
- `numpy >= 1.24, < 2 <https://pypi.org/project/numpy>`__
100134

135+
If you want to execute CLF workflows, you will also need
136+
137+
- `colour-science >= 0.4.6 <https://pypi.org/project/colour-science>`__
138+
101139
Pypi
102140
~~~~
103141

@@ -111,6 +149,17 @@ The overall development dependencies are installed as follows::
111149

112150
pip install --user 'colour-clf-io[development]'
113151

152+
UV
153+
~~~~
154+
155+
Using uv you can simply install **Colour - CLF IO** via::
156+
157+
uv add colour-clf-io
158+
159+
or, if you want to execute CLF workflows::
160+
161+
uv add colour-clf-io --optional processing
162+
114163

115164
Contributing
116165
^^^^^^^^^^^^

colour_clf_io/__init__.py

Lines changed: 60 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,16 @@
11
"""
2-
CLF Parsing
3-
===========
2+
Colour - CLF IO
3+
===============
44
55
Defines the functionality and data structures to parse *CLF* files.
66
77
The main functionality is exposed through the following two methods:
8-
- :func:`colour.io.clf.read_clf`: Read a file in the *CLF* format and return the
9-
corresponding :class: ProcessList.
10-
- :func:`colour.io.clf.parse_clf`: Read a string that contains a *CLF* file and
8+
- :func:`colour.io.clf.read_clf_from_file`: Read a file in the *CLF* format and
119
return the corresponding :class: ProcessList.
10+
- :func:`colour.io.clf.read_clf`: Read a string that contains a *CLF* file and
11+
return the corresponding :class: ProcessList.
12+
- :func:`colour.io.clf.write_clf`: Take a :class: ProcessList and output the
13+
corresponding CLF document.
1214
1315
References
1416
----------
@@ -20,6 +22,8 @@
2022

2123
import typing
2224

25+
from .parsing import Namespaces
26+
2327
if typing.TYPE_CHECKING:
2428
from pathlib import Path
2529

@@ -99,12 +103,19 @@
99103
__application_name__ = "Colour - CLF IO"
100104

101105
__major_version__ = "0"
102-
__minor_version__ = "1"
103-
__change_version__ = "1"
106+
__minor_version__ = "2"
107+
__change_version__ = "0"
104108
__version__ = f"{__major_version__}.{__minor_version__}.{__change_version__}"
105109

110+
try:
111+
from colour_clf_io.processing import CLFProcessList
112+
113+
__all__ += ["CLFProcessList"]
114+
except ImportError:
115+
pass
116+
106117

107-
def read_clf(path: str | Path) -> ProcessList | None:
118+
def read_clf_from_file(path: str | Path) -> ProcessList:
108119
"""
109120
Read given *CLF* file and return a *ProcessList*.
110121
@@ -124,13 +135,17 @@ def read_clf(path: str | Path) -> ProcessList | None:
124135
If the given file does not contain a valid *CLF* file.
125136
"""
126137

127-
xml = lxml.etree.parse(str(path)) # noqa: S320
138+
xml = lxml.etree.parse(str(path))
128139
xml_process_list = xml.getroot()
129140

130-
return ProcessList.from_xml(xml_process_list)
141+
process_list = ProcessList.from_xml(xml_process_list)
142+
if process_list is None:
143+
err = "Process list could not be parsed."
144+
raise ValueError(err)
145+
return process_list
131146

132147

133-
def parse_clf(text: str | bytes) -> ProcessList | None:
148+
def read_clf(text: str | bytes) -> ProcessList | None:
134149
"""
135150
Read given string as a *CLF* file and return a *ProcessList*.
136151
@@ -150,6 +165,39 @@ def parse_clf(text: str | bytes) -> ProcessList | None:
150165
If the given string does not contain a valid *CLF* file.
151166
"""
152167

153-
xml = lxml.etree.fromstring(text) # noqa: S320
168+
xml = lxml.etree.fromstring(text)
154169

155170
return ProcessList.from_xml(xml)
171+
172+
173+
def write_clf(
174+
process_list: ProcessList,
175+
path: str | Path | None = None,
176+
namespace: Namespaces = Namespaces.AMPAS,
177+
) -> None | str:
178+
"""
179+
Write the given *ProcessList* as a CLF file to the target
180+
location. If no *path* is given the CLF document will be returned as a string.
181+
182+
Parameters
183+
----------
184+
process_list
185+
*ProcessList* that should be written.
186+
path
187+
Location of the file, or *None* to return a string representation of the
188+
CLF document.
189+
namespace
190+
:class:`colour_clf_io.Namespaces` instance to be used for the namespace
191+
of the document.
192+
193+
Returns
194+
-------
195+
:class:`colour_clf_io.ProcessList`
196+
"""
197+
xml = process_list.to_xml(namespace)
198+
serialised = lxml.etree.tostring(xml)
199+
if path is None:
200+
return serialised.decode("utf-8")
201+
with open(path, "wb") as f:
202+
f.write(serialised)
203+
return None

0 commit comments

Comments
 (0)