Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
67 changes: 67 additions & 0 deletions src/doc/pythonbindings.rst
Original file line number Diff line number Diff line change
Expand Up @@ -3888,6 +3888,73 @@ sections) work with deep inputs::

|

.. _sec-pythoncolorconfig:


ColorConfig
===========

The `ColorConfig` class that represents the set of color transformations that
are allowed.

If OpenColorIO is enabled at build time, this configuration is loaded at
runtime, allowing the user to have complete control of all color transformation
math. See the
`OpenColorIO documentation <https://opencolorio.readthedocs.io>`_ for details.

If OpenColorIO is not enabled at build time, a generic color configuration
is provided for minimal color support.

..
TODO: The documentation for this class is incomplete.

.. py:method:: get_cicp (colorspace)

Find CICP code corresponding to the colorspace.
Return a sequence of 4 ints, or None if not found.

Example:

.. code-block:: python

colorconfig = oiio.ColorConfig()
cicp = colorconfig.get_cicp("pq_rec2020_display")
if cicp:
primaries, transfer, matrix, color_range = cicp

This function was added in OpenImageIO 3.1.


.. py:method:: get_color_interop_id (colorspace)

Find color interop ID for the given colorspace.
Returns empty string if not found.

Example:

.. code-block:: python

colorconfig = oiio.ColorConfig()
interop_id = colorconfig.get_color_interop_id("Rec.2100-PQ - Display")

This function was added in OpenImageIO 3.1.


.. py:method:: get_color_interop_id (cicp)

Find color interop ID corresponding to the CICP code.
Returns empty string if not found.

Example:

.. code-block:: python

colorconfig = oiio.ColorConfig()
interop_id = colorconfig.get_color_interop_id([9, 16, 9, 1])

This function was added in OpenImageIO 3.1.


.. _sec-pythonmiscapi:

Miscellaneous Utilities
Expand Down
6 changes: 6 additions & 0 deletions src/ffmpeg.imageio/ffmpeginput.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ receive_frame(AVCodecContext* avctx, AVFrame* picture, AVPacket* avpkt)



#include <OpenImageIO/color.h>
#include <OpenImageIO/imageio.h>
#include <iostream>
#include <mutex>
Expand Down Expand Up @@ -549,6 +550,11 @@ FFmpegInput::open(const std::string& name, ImageSpec& spec)
m_codec_context->colorspace,
m_codec_context->color_range == AVCOL_RANGE_MPEG ? 0 : 1 };
m_spec.attribute("CICP", TypeDesc(TypeDesc::INT, 4), cicp);
const ColorConfig& colorconfig(ColorConfig::default_colorconfig());
string_view interop_id = colorconfig.get_color_interop_id(cicp);
if (!interop_id.empty())
m_spec.attribute("oiio:ColorSpace", interop_id);

m_nsubimages = m_frames;
spec = m_spec;
m_filename = name;
Expand Down
6 changes: 6 additions & 0 deletions src/heif.imageio/heifinput.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// SPDX-License-Identifier: Apache-2.0
// https://github.com/AcademySoftwareFoundation/OpenImageIO

#include <OpenImageIO/color.h>
#include <OpenImageIO/filesystem.h>
#include <OpenImageIO/fmath.h>
#include <OpenImageIO/imageio.h>
Expand Down Expand Up @@ -292,6 +293,11 @@ HeifInput::seek_subimage(int subimage, int miplevel)
int(nclx->matrix_coefficients),
int(nclx->full_range_flag ? 1 : 0) };
m_spec.attribute("CICP", TypeDesc(TypeDesc::INT, 4), cicp);
const ColorConfig& colorconfig(
ColorConfig::default_colorconfig());
string_view interop_id = colorconfig.get_color_interop_id(cicp);
if (!interop_id.empty())
m_spec.attribute("oiio:ColorSpace", interop_id);
}
heif_nclx_color_profile_free(nclx);
}
Expand Down
12 changes: 8 additions & 4 deletions src/heif.imageio/heifoutput.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
// https://github.com/AcademySoftwareFoundation/OpenImageIO


#include <OpenImageIO/color.h>
#include <OpenImageIO/filesystem.h>
#include <OpenImageIO/fmath.h>
#include <OpenImageIO/imageio.h>
Expand Down Expand Up @@ -249,10 +250,13 @@ HeifOutput::close()
std::unique_ptr<heif_color_profile_nclx,
void (*)(heif_color_profile_nclx*)>
nclx(heif_nclx_color_profile_alloc(), heif_nclx_color_profile_free);
const ParamValue* p = m_spec.find_attribute("CICP",
TypeDesc(TypeDesc::INT, 4));
if (p) {
const int* cicp = static_cast<const int*>(p->data());
const ColorConfig& colorconfig(ColorConfig::default_colorconfig());
const ParamValue* p = m_spec.find_attribute("CICP",
TypeDesc(TypeDesc::INT, 4));
string_view colorspace = m_spec.get_string_attribute("oiio:ColorSpace");
cspan<int> cicp = (p) ? p->as_cspan<int>()
: colorconfig.get_cicp(colorspace);
if (!cicp.empty()) {
nclx->color_primaries = heif_color_primaries(cicp[0]);
nclx->transfer_characteristics = heif_transfer_characteristics(
cicp[1]);
Expand Down
18 changes: 18 additions & 0 deletions src/include/OpenImageIO/color.h
Original file line number Diff line number Diff line change
Expand Up @@ -402,6 +402,24 @@ class OIIO_API ColorConfig {
bool equivalent(string_view color_space,
string_view other_color_space) const;

/// Find CICP code corresponding to the colorspace.
/// Return a cspan of 4 ints, or an empty span if not found.
///
/// @version 3.1
cspan<int> get_cicp(string_view colorspace) const;

/// Find color interop ID for the given colorspace.
/// Returns empty string if not found.
///
/// @version 3.1
string_view get_color_interop_id(string_view colorspace) const;

/// Find color interop ID corresponding to the CICP code.
/// Returns empty string if not found.
///
/// @version 3.1
string_view get_color_interop_id(const int cicp[4]) const;

/// Return a filename or other identifier for the config we're using.
std::string configname() const;

Expand Down
173 changes: 173 additions & 0 deletions src/libOpenImageIO/color_ocio.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1997,6 +1997,179 @@ ColorConfig::parseColorSpaceFromString(string_view str) const
}


//////////////////////////////////////////////////////////////////////////
//
// Color Interop ID

namespace {
enum class CICPPrimaries : int {
Rec709 = 1,
Rec2020 = 9,
XYZD65 = 10,
P3D65 = 12,
};

enum class CICPTransfer : int {
BT709 = 1,
Gamma22 = 4,
Linear = 8,
sRGB = 13,
PQ = 16,
Gamma26 = 17,
HLG = 18,
};

enum class CICPMatrix : int {
RGB = 0,
BT709 = 1,
Unspecified = 2,
Rec2020_NCL = 9,
Rec2020_CL = 10,
};

enum class CICPRange : int {
Narrow = 0,
Full = 1,
};

struct ColorInteropID {
constexpr ColorInteropID(const char* interop_id, const char* legacy_alias)
: interop_id(interop_id)
, legacy_alias(legacy_alias)
, cicp({ 0, 0, 0, 0 })
, has_cicp(false)
{
}

constexpr ColorInteropID(const char* interop_id, const char* legacy_alias,
CICPPrimaries primaries, CICPTransfer transfer,
CICPMatrix matrix)
: interop_id(interop_id)
, legacy_alias(legacy_alias)
, cicp({ int(primaries), int(transfer), int(matrix),
int(CICPRange::Full) })
, has_cicp(true)
{
}

const char* interop_id;
const char* legacy_alias;
std::array<int, 4> cicp;
bool has_cicp;
};

// Mapping between color interop ID and CICP, based on Color Interop Forum
// recommendations. The legacy aliases are for older ACES configs.
constexpr ColorInteropID color_interop_ids[] = {
// Scene referred interop IDs first so they are the default in automatic
// conversion from CICP to interop ID. Some are not display color spaces
// at all, but can be represented by CICP anyway.
{ "lin_ap1_scene", "lin_ap1" },
{ "lin_ap0_scene", "lin_ap0" },
{ "lin_rec709_scene", "lin_rec709", CICPPrimaries::Rec709,
CICPTransfer::Linear, CICPMatrix::BT709 },
{ "lin_p3d65_scene", "lin_p3d65", CICPPrimaries::P3D65,
CICPTransfer::Linear, CICPMatrix::BT709 },
{ "lin_rec2020_scene", "lin_rec2020", CICPPrimaries::Rec2020,
CICPTransfer::Linear, CICPMatrix::Rec2020_CL },
{ "lin_adobergb_scene", "lin_adobergb" },
{ "lin_ciexyzd65_scene", "cie_xyz_d65", CICPPrimaries::XYZD65,
CICPTransfer::Linear, CICPMatrix::Unspecified },
{ "srgb_rec709_scene", "srgb_texture", CICPPrimaries::Rec709,
CICPTransfer::sRGB, CICPMatrix::BT709 },
{ "g22_rec709_scene", "g22_rec709", CICPPrimaries::Rec709,
CICPTransfer::Gamma22, CICPMatrix::BT709 },
{ "g18_rec709_scene", "g18_rec709" },
{ "srgb_ap1_scene", "srgb_ap1" },
{ "g22_ap1_scene", "g22_ap1" },
{ "srgb_p3d65_scene", "srgb_p3d65", CICPPrimaries::P3D65,
CICPTransfer::sRGB, CICPMatrix::BT709 },
{ "g22_adobergb_scene", nullptr },
{ "data", nullptr },
{ "unknown", nullptr },

// Display referred interop IDs.
{ "srgb_rec709_display", "srgb_display", CICPPrimaries::Rec709,
CICPTransfer::sRGB, CICPMatrix::BT709 },
{ "g24_rec709_display", "rec1886_rec709_display", CICPPrimaries::Rec709,
CICPTransfer::BT709, CICPMatrix::BT709 },
{ "srgb_p3d65_display", "p3d65_display", CICPPrimaries::P3D65,
CICPTransfer::sRGB, CICPMatrix::BT709 },
{ "srgbe_p3d65_display", nullptr, CICPPrimaries::P3D65, CICPTransfer::sRGB,
CICPMatrix::BT709 },
{ "pq_p3d65_display", "st2084_p3d65_display", CICPPrimaries::P3D65,
CICPTransfer::PQ, CICPMatrix::Rec2020_NCL },
{ "pq_rec2020_display", "rec2100_pq_display", CICPPrimaries::Rec2020,
CICPTransfer::PQ, CICPMatrix::Rec2020_NCL },
{ "hlg_rec2020_display", "rec2100_hlg_display", CICPPrimaries::Rec2020,
CICPTransfer::HLG, CICPMatrix::Rec2020_NCL },
// No CICP mapping to keep previous behavior unchanged, as Gamma 2.2
// display is more likely meant to be written as sRGB. On read the
// scene referred interop ID will be used.
{ "g22_rec709_display", nullptr
/* CICPPrimaries::Rec709, CICPTransfer::Gamma22, CICPMatrix::BT709 */ },
// No CICP code for Adobe RGB primaries.
{ "g22_adobergb_display", nullptr },
{ "g26_p3d65_display", "p3_dci_display", CICPPrimaries::P3D65,
CICPTransfer::Gamma26, CICPMatrix::BT709 },
{ "g26_xyzd65_display", nullptr, CICPPrimaries::XYZD65,
CICPTransfer::Gamma26, CICPMatrix::Unspecified },
{ "pq_xyzd65_display", nullptr, CICPPrimaries::XYZD65, CICPTransfer::PQ,
CICPMatrix::Unspecified },
};
} // namespace

string_view
ColorConfig::get_color_interop_id(string_view colorspace) const
{
if (colorspace.empty())
return "";
#if OCIO_VERSION_HEX >= MAKE_OCIO_VERSION_HEX(2, 5, 0)
if (getImpl()->config_ && !disable_ocio) {
OCIO::ConstColorSpaceRcPtr c = getImpl()->config_->getColorSpace(
std::string(resolve(colorspace)).c_str());
const char* interop_id = (c) ? c->getInteropID() : nullptr;
if (interop_id) {
return interop_id;
}
}
#endif
for (const ColorInteropID& interop : color_interop_ids) {
if (equivalent(colorspace, interop.interop_id)
|| (interop.legacy_alias
&& equivalent(colorspace, interop.legacy_alias))) {
return interop.interop_id;
}
}
return "";
}

string_view
ColorConfig::get_color_interop_id(const int cicp[4]) const
{
for (const ColorInteropID& interop : color_interop_ids) {
if (interop.has_cicp && interop.cicp[0] == cicp[0]
&& interop.cicp[1] == cicp[1]) {
return interop.interop_id;
}
}
return "";
}

cspan<int>
ColorConfig::get_cicp(string_view colorspace) const
{
string_view interop_id = get_color_interop_id(colorspace);
if (!interop_id.empty()) {
for (const ColorInteropID& interop : color_interop_ids) {
if (interop.has_cicp && interop_id == interop.interop_id) {
return interop.cicp;
}
}
}
return cspan<int>();
}


//////////////////////////////////////////////////////////////////////////
//
Expand Down
Loading
Loading