Skip to content

Commit a946288

Browse files
authored
Color management nomenclature improvements (AcademySoftwareFoundation#4479)
A round of color management improvements (I hope). We were pretty squirrelly about what "linear" meant. Get more exact. Now we will treat "linear" as a legacy synonym for "lin_rec709" (sRGB primaries, but linear response). Change file format readers to say "lin_rec709" when they mean it, and not use the more generic alias "linear". "scene_linear", on the other hand, is the internal working color space for math. Which concrete color space it is an alias for is determined by the OCIO config in use. Use "g18_rec709" and "g22_rec709" for rec709 + gamma 1.8 or 2.2, respectvely. A few new API calls help out: equivalent_colorspace(), set_colorspace(spec,name), set_colorspace_rec709_gamma(). OpenEXR always tagged files as linear by default. It was too aggressive about this, so with this PR, now it defaults to lin_rec709 only for files that appear to be RGB and not have any other clues about the color space. Similarly, the raw reader was too eager to set the Exif:ColorSpace to look like sRGB even when it wasn't. Lots of reference outputs needed to be updated. This is a potentially big behavior change. But I think it's for the better. Signed-off-by: Larry Gritz <[email protected]>
1 parent ff20241 commit a946288

File tree

75 files changed

+542
-445
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

75 files changed

+542
-445
lines changed

src/cineon.imageio/cineoninput.cpp

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -169,27 +169,26 @@ CineonInput::open(const std::string& name, ImageSpec& newspec)
169169
// This is not very smart, but it seems that as a practical matter,
170170
// all Cineon files are log. So ignore the gamma field and just set
171171
// the color space to KodakLog.
172-
m_spec.attribute("oiio:ColorSpace", "KodakLog");
172+
m_spec.set_colorspace("KodakLog");
173173
#else
174174
// image linearity
175175
// FIXME: making this more robust would require the per-channel transfer
176176
// function functionality which isn't yet in OIIO
177177
switch (m_cin.header.ImageDescriptor(0)) {
178178
case cineon::kRec709Red:
179179
case cineon::kRec709Green:
180-
case cineon::kRec709Blue: m_spec.attribute("oiio:ColorSpace", "Rec709");
180+
case cineon::kRec709Blue: m_spec.set_colorspace("Rec709");
181181
default:
182182
// either grayscale or printing density
183183
if (!std::isinf(m_cin.header.Gamma()) && m_cin.header.Gamma() != 0.0f)
184184
// actual gamma value is read later on
185-
m_spec.attribute("oiio:ColorSpace",
186-
Strutil::fmt::format("Gamma{:.2g}", g));
185+
set_colorspace_rec709_gamma(m_spec, float(m_cin.header.Gamma()));
187186
break;
188187
}
189188
190189
// gamma exponent
191190
if (!std::isinf(m_cin.header.Gamma()) && m_cin.header.Gamma() != 0.0f)
192-
m_spec.attribute("oiio:Gamma", (float)m_cin.header.Gamma());
191+
set_colorspace_rec709_gamma(m_spec, float(m_cin.header.Gamma()));
193192
#endif
194193

195194
// general metadata

src/dds.imageio/ddsinput.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -845,7 +845,7 @@ DDSInput::seek_subimage(int subimage, int miplevel)
845845
// linear color space for HDR-ish images
846846
if (colorspace == nullptr
847847
&& (basetype == TypeDesc::HALF || basetype == TypeDesc::FLOAT))
848-
colorspace = "linear";
848+
colorspace = "lin_rec709";
849849

850850
m_spec.set_colorspace(colorspace);
851851

src/doc/imageioapi.rst

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -285,6 +285,12 @@ just exist in the OIIO namespace as general utilities. (See
285285

286286
.. doxygenfunction:: get_extension_map
287287

288+
.. doxygenfunction:: OIIO::set_colorspace
289+
290+
.. doxygenfunction:: OIIO::set_colorspace_rec709_gamma
291+
292+
.. doxygenfunction:: OIIO::equivalent_colorspace
293+
288294
|
289295
290296
.. _sec-startupshutdown:

src/doc/imageoutput.rst

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -888,12 +888,12 @@ color space:
888888
.. code-tab:: c++
889889

890890
ImageSpec spec (width, length, channels, format);
891-
spec.attribute ("oiio:ColorSpace", "scene_linear");
891+
spec.set_colorspace("scene_linear");
892892

893893
.. code-tab:: py
894894

895895
spec = ImageSpec(width, length, channels, format)
896-
spec.attribute ("oiio:ColorSpace", "scene_linear")
896+
spec.set_colorspace("scene_linear")
897897

898898
If a particular ``ImageOutput`` implementation is required (by the rules of
899899
the file format it writes) to have pixels in a fixed color space,

src/doc/pythonbindings.rst

Lines changed: 47 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -732,7 +732,6 @@ Section :ref:`sec-ImageSpec`, is replicated for Python.
732732
spec.set_colorspace ("sRGB")
733733
734734
735-
736735
.. py:method:: ImageSpec.undefined ()
737736
738737
Returns `True` for a newly initialized (undefined) ImageSpec.
@@ -3885,6 +3884,53 @@ details.
38853884
formats = oiio.get_string_attribute ("format_list")
38863885
38873886
3887+
.. py:method:: set_colorspace (spec, name)
3888+
3889+
Set the metadata of the `spec` to presume that color space is `name` (or
3890+
to assume nothing about the color space if `name` is empty).
3891+
3892+
Example:
3893+
3894+
.. code-block:: python
3895+
3896+
spec = oiio.ImageSpec()
3897+
oiio.set_colorspace (spec, "lin_rec709")
3898+
3899+
This function was added in OpenImageIO 3.0.
3900+
3901+
3902+
.. py:method:: set_colorspace_rec709_gamma (spec, name)
3903+
3904+
Set the metadata of the `spec` to reflect Rec709 color primaries and the
3905+
given gamma.
3906+
3907+
Example:
3908+
3909+
.. code-block:: python
3910+
3911+
spec = oiio.ImageSpec()
3912+
oiio.set_colorspace_rec709_gamma (spec, 2.2)
3913+
3914+
This function was added in OpenImageIO 3.0.
3915+
3916+
3917+
.. py:method:: equivalent_colorspace (a, b)
3918+
3919+
Return `True` if the color spaces `a` and `b` are equivalent in the
3920+
default active color config.
3921+
3922+
Example:
3923+
3924+
.. code-block:: python
3925+
3926+
# ib is an ImageBuf
3927+
cs = ib.spec().get_string_attribute("oiio:ColorSpace")
3928+
if oiio.equivalent_colorspace(cs, "sRGB") :
3929+
print ("The image is sRGB")
3930+
3931+
This function was added in OpenImageIO 3.0.
3932+
3933+
38883934
.. py:method:: is_imageio_format_name (name)
38893935
38903936
Returns True if `name` is the name of a known and supported file format,

src/doc/stdmetadata.rst

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -143,18 +143,19 @@ Color information
143143

144144
- `"scene_linear"` : Color pixel values are known to be scene-linear and
145145
using facility-default color primaries as defined by the OpenColorIO
146-
configuration. Note that `"linear"` is treated as a synonym. (Note: when
147-
no color config is found, this are presumed to use sRGB/Rec709 color
148-
primaries when built against OpenColorIO 2.1 or earlier, or when no OCIO
149-
support is available, but is presumed to be ACEScg when built against
150-
OCIO 2.2 or higher and using its built-in config.)
151-
- `"lin_srgb"` : Color pixel values are known to be linear and
152-
using sRGB/Rec709 color primaries.
146+
configuration.
147+
- `"lin_srgb"`, `"lin_rec709"` : Color pixel values are known to be
148+
linear and using sRGB/Rec709 color primaries. Note that `"linear"` is
149+
treated as a synonym.
153150
- `"sRGB"` : Using standard sRGB response and primaries.
154151
- `"Rec709"` : Using standard Rec709 response and primaries.
155152
- `"ACEScg"` : ACEScg color space encoding.
156153
- `"AdobeRGB"` : Adobe RGB color space.
157154
- `"KodakLog"` : Kodak logarithmic color space.
155+
- `"g22_rec709"` : Rec709/sRGB primaries, but using a response curve
156+
corresponding to gamma 2.2.
157+
- `"g18_rec709"` : Rec709/sRGB primaries, but using a response curve
158+
corresponding to gamma 1.8.
158159
- `"GammaX.Y"` : Color values have been gamma corrected
159160
(raised to the power :math:`1/\gamma`). The `X.Y` is the numeric value
160161
of the gamma exponent.
@@ -230,7 +231,7 @@ Disk file format info/hints
230231
`piz`, `pxr24`, `b44`, `b44a`, `dwaa`, or `dwab`.
231232

232233
he compression name is permitted to have a quality value to be appended
233-
fter a colon, for example `dwaa:60`. The exact meaning and range of
234+
after a colon, for example `dwaa:60`. The exact meaning and range of
234235
he quality value can vary between different file formats and compression
235236
odes, and some don't support quality values at all (it will be ignored if
236237
ot supported, or if out of range).

src/dpx.imageio/dpxinput.cpp

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -312,17 +312,12 @@ DPXInput::seek_subimage(int subimage, int miplevel)
312312

313313
// image linearity
314314
switch (m_dpx.header.Transfer(subimage)) {
315-
case dpx::kLinear: m_spec.attribute("oiio:ColorSpace", "Linear"); break;
316-
case dpx::kLogarithmic:
317-
m_spec.attribute("oiio:ColorSpace", "KodakLog");
318-
break;
319-
case dpx::kITUR709: m_spec.attribute("oiio:ColorSpace", "Rec709"); break;
315+
case dpx::kLinear: m_spec.set_colorspace("Linear"); break;
316+
case dpx::kLogarithmic: m_spec.set_colorspace("KodakLog"); break;
317+
case dpx::kITUR709: m_spec.set_colorspace("Rec709"); break;
320318
case dpx::kUserDefined:
321319
if (!std::isnan(m_dpx.header.Gamma()) && m_dpx.header.Gamma() != 0) {
322-
float g = float(m_dpx.header.Gamma());
323-
m_spec.attribute("oiio:ColorSpace",
324-
Strutil::fmt::format("Gamma{:.2}", g));
325-
m_spec.attribute("oiio:Gamma", g);
320+
set_colorspace_rec709_gamma(m_spec, float(m_dpx.header.Gamma()));
326321
break;
327322
}
328323
// intentional fall-through

src/gif.imageio/gifinput.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -259,7 +259,7 @@ GIFInput::read_subimage_metadata(ImageSpec& newspec)
259259
newspec.nchannels = 4;
260260
newspec.default_channel_names();
261261
newspec.alpha_channel = 4;
262-
newspec.attribute("oiio:ColorSpace", "sRGB");
262+
newspec.set_colorspace("sRGB");
263263

264264
m_previous_disposal_method = m_disposal_method;
265265
m_disposal_method = DISPOSAL_UNSPECIFIED;

src/hdr.imageio/hdrinput.cpp

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -291,7 +291,7 @@ HdrInput::RGBE_ReadHeader()
291291
if (!line.size())
292292
return false;
293293

294-
m_spec.attribute("oiio:ColorSpace", "lin_srgb");
294+
m_spec.set_colorspace("lin_rec709");
295295
// presume linear w/ srgb primaries -- seems like the safest assumption
296296
// for this old file format.
297297

@@ -310,13 +310,7 @@ HdrInput::RGBE_ReadHeader()
310310
// 2.2, not 2.19998.
311311
float g = float(1.0 / tempf);
312312
g = roundf(100.0 * g) / 100.0f;
313-
m_spec.attribute("oiio:Gamma", g);
314-
if (g == 1.0f)
315-
m_spec.attribute("oiio:ColorSpace", "linear");
316-
else
317-
m_spec.attribute("oiio:ColorSpace",
318-
Strutil::fmt::format("Gamma{:.2g}", g));
319-
313+
set_colorspace_rec709_gamma(m_spec, g);
320314
} else if (Strutil::parse_values(line,
321315
"EXPOSURE=", span<float>(tempf))) {
322316
m_spec.attribute("hdr:exposure", tempf);

src/heif.imageio/heifinput.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -243,7 +243,7 @@ HeifInput::seek_subimage(int subimage, int miplevel)
243243
m_himage.get_height(heif_channel_interleaved), bits / 8,
244244
TypeUInt8);
245245

246-
m_spec.attribute("oiio:ColorSpace", "sRGB");
246+
m_spec.set_colorspace("sRGB");
247247

248248
#if LIBHEIF_HAVE_VERSION(1, 12, 0)
249249
// Libheif >= 1.12 added API call to find out if the image is associated

0 commit comments

Comments
 (0)