Skip to content
Open
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
113 changes: 88 additions & 25 deletions src/libOpenImageIO/color_ocio.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -201,8 +201,8 @@ struct CSInfo {
// Hidden implementation of ColorConfig
class ColorConfig::Impl {
public:
OCIO::ConstConfigRcPtr config_;
OCIO::ConstConfigRcPtr builtinconfig_;
OCIO::ConfigRcPtr config_;
OCIO::ConfigRcPtr builtinconfig_;

private:
std::vector<CSInfo> colorspaces;
Expand Down Expand Up @@ -815,6 +815,49 @@ ColorConfig::~ColorConfig() {}



// OIIO doctoring of OCIO configs for different default file rules. Currently,
// we only do this for built-in configs.
static void
fix_config_file_rules(OCIO::ConfigRcPtr& config)
{
OIIO_CONTRACT_ASSERT(config);
DBG("Fixing up rules:\n");
#if 1
// Just start with a clean slate
auto rules = OCIO::FileRules::Create();
#else
// Alternate universe: Start with the existing rules
auto rules = config->getFileRules()->createEditableCopy();
#endif
for (size_t i = 0, e = rules->getNumEntries(); i != e; ++i) {
DBG(" rule {}/{}: pat='{}' ext='{}' -> \"{}\"\n", i, rules->getName(i),
rules->getRegex(i), rules->getExtension(i),
rules->getColorSpace(i));
if (Strutil::iequals(rules->getExtension(i), "exr")) {
// Change the rule for exr extension, if it exists, to "unknown".
// Make no assumptions. OCIO's built-in configs think it should be
// ACES2065-1, which is almost never right.
rules->setColorSpace(i, "unknown");
DBG(" changed cs to \"{}\"\n", rules->getColorSpace(i));
} else if (!strcmp(rules->getName(i), "Default")) {
// Default rule or one that matches everything -- for OIIO, we
// just want to change this to unknown. We made decisions about
// default per-file-format color space decisions in the individual
// readers. We don't even consider file extension to be reliable
// evidence of the file type.
rules->setColorSpace(i, "unknown");
DBG(" changed cs to \"{}\"\n", rules->getColorSpace(i));
}
}

// But make the path search rule (look for the right-most color space name
// embedded in the path) have precedence over file naming rules.
rules->insertPathSearchRule(0);
config->setFileRules(rules);
}



bool
ColorConfig::Impl::init(string_view filename)
{
Expand All @@ -825,7 +868,10 @@ ColorConfig::Impl::init(string_view filename)
OCIO::SetLoggingLevel(OCIO::LOGGING_LEVEL_NONE);

try {
builtinconfig_ = OCIO::Config::CreateFromFile("ocio://default");
auto cfg = OCIO::Config::CreateFromFile("ocio://default");
OIIO_CONTRACT_ASSERT(cfg);
builtinconfig_ = cfg->createEditableCopy();
fix_config_file_rules(builtinconfig_);
} catch (OCIO::Exception& e) {
error("Error making OCIO built-in config: {}", e.what());
}
Expand All @@ -841,10 +887,13 @@ ColorConfig::Impl::init(string_view filename)
} else {
// Either filename passed, or taken from $OCIO, and it seems to exist
try {
config_ = OCIO::Config::CreateFromFile(
std::string(filename).c_str());
configname(filename);
m_config_is_built_in = Strutil::istarts_with(filename, "ocio://");
auto cfg = OCIO::Config::CreateFromFile(
std::string(filename).c_str());
if (cfg)
config_ = cfg->createEditableCopy();
if (config_ && Strutil::istarts_with(filename, "ocio://"))
fix_config_file_rules(config_);
} catch (OCIO::Exception& e) {
error("Error reading OCIO config \"{}\": {}", filename, e.what());
} catch (...) {
Expand Down Expand Up @@ -2344,27 +2393,41 @@ ImageBufAlgo::colorconvert(ImageBuf& dst, const ImageBuf& src, string_view from,
from = src.spec().get_string_attribute("oiio:Colorspace",
"scene_linear");
}
if (from.empty() || to.empty()) {
dst.errorfmt("Unknown color space name");
if (from.empty() || from == "unknown" || to.empty() || to == "unknown") {
dst.errorfmt("Unknown color space name (from=\"{}\", to=\"{}\")", from,
to);
return false;
}
ColorProcessorHandle processor;
{
if (!colorconfig)
colorconfig = &ColorConfig::default_colorconfig();
processor
= colorconfig->createColorProcessor(colorconfig->resolve(from),
colorconfig->resolve(to),
context_key, context_value);
if (!processor) {
if (colorconfig->has_error())
dst.errorfmt("{}", colorconfig->geterror());
else
dst.errorfmt(
"Could not construct the color transform {} -> {} (unknown error)",
from, to);
return false;
}

if (!colorconfig)
colorconfig = &ColorConfig::default_colorconfig();

if (colorconfig->isData(from) || colorconfig->equivalent(from, "raw")) {
// If the input color space is not color managed, the transformation
// is meaningless, just set 'to' to the same space so that the whole
// thing is a no-op, including continuing to think it's in the raw
// space.
to = from;
} else if (colorconfig->isData(to) || colorconfig->equivalent(to, "raw")) {
// If the output color space is not color managed, the transformation
// is meaningless, just make it look like it's already in the 'from'
// space. Note that this DOES change the apparent color space label
// to the requested raw output space.
from = to;
}

ColorProcessorHandle processor
= colorconfig->createColorProcessor(colorconfig->resolve(from),
colorconfig->resolve(to),
context_key, context_value);
if (!processor) {
if (colorconfig->has_error())
dst.errorfmt("{}", colorconfig->geterror());
else
dst.errorfmt(
"Could not construct the color transform {} -> {} (unknown error)",
from, to);
return false;
}

logtime.stop(-1); // transition to other colorconvert
Expand Down
2 changes: 2 additions & 0 deletions src/oiiotool/oiiotool.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5566,6 +5566,8 @@ input_file(Oiiotool& ot, cspan<const char*> argv)
// Try to deduce the color space it's in
std::string colorspace(
ot.colorconfig().getColorSpaceFromFilepath(filename, "", true));
if (colorspace == "unknown")
colorspace.clear();
if (colorspace.size() && ot.debug)
OIIO::print(" From {}, we deduce color space \"{}\"\n",
filename, colorspace);
Expand Down
65 changes: 65 additions & 0 deletions testsuite/oiiotool-color/ref/out-ocio2.3.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
oiiotool ERROR: --colorconfig : Requested non-existent OCIO config "missing.ocio"
Full command line was:
> oiiotool --nostderr --colorconfig missing.ocio -echo "Nonexistent config"
nope: 0.5,0.5,0.5 half
raw: 0.5,0.5,0.5 half
acescg: 0.5,0.5,0.5 half
srgb_tx: 0.5,0.5,0.5 half
nope: 0.5,0.5,0.5 half
raw: 0.5,0.5,0.5 half
acescg: 0.5,0.5,0.5 half
srgb_tx: 0.5,0.5,0.5 half
Comparing "colormap-inferno.tif" and "ref/colormap-inferno.tif"
PASS
Comparing "colormap-custom.tif" and "ref/colormap-custom.tif"
PASS
Comparing "unpremult.exr" and "ref/unpremult.exr"
PASS
Comparing "premult.exr" and "ref/premult.exr"
PASS
Comparing "contrast-stretch.tif" and "ref/contrast-stretch.tif"
PASS
Comparing "contrast-shrink.tif" and "ref/contrast-shrink.tif"
PASS
Comparing "contrast-inverse.tif" and "ref/contrast-inverse.tif"
PASS
Comparing "contrast-threshold.tif" and "ref/contrast-threshold.tif"
PASS
Comparing "contrast-sigmoid5.tif" and "ref/contrast-sigmoid5.tif"
PASS
Comparing "display-sRGB.tif" and "ref/display-sRGB.tif"
PASS
Comparing "rgbfromtga.png" and "ref/rgbfromtga.png"
PASS
Comparing "greyalpha_sRGB.tif" and "ref/greyalpha_sRGB.tif"
PASS
Comparing "greyalpha_sRGB_un.tif" and "ref/greyalpha_sRGB_un-ocio22.tif"
PASS
Comparing "grey_sRGB.tif" and "ref/grey_sRGB.tif"
PASS
Comparing "grey_sRGB_un.tif" and "ref/grey_sRGB_un.tif"
PASS
Comparing "tahoe-ccmatrix.tif" and "ref/tahoe-ccmatrix.tif"
PASS
Comparing "tahoe-sat0.tif" and "ref/tahoe-sat0.tif"
PASS
Comparing "tahoe-sat2.tif" and "ref/tahoe-sat2.tif"
PASS
Comparing "cmap-magma.tif" and "ref/cmap-magma.tif"
PASS
Comparing "cmap-inferno.tif" and "ref/cmap-inferno.tif"
PASS
Comparing "cmap-plasma.tif" and "ref/cmap-plasma.tif"
PASS
Comparing "cmap-viridis.tif" and "ref/cmap-viridis.tif"
PASS
Comparing "cmap-turbo.tif" and "ref/cmap-turbo.tif"
PASS
Comparing "cmap-blue-red.tif" and "ref/cmap-blue-red.tif"
PASS
Comparing "cmap-spectrum.tif" and "ref/cmap-spectrum.tif"
PASS
Comparing "cmap-heat.tif" and "ref/cmap-heat.tif"
PASS
Comparing "look-default.tif" and "ref/look-default.tif"
PASS
8 changes: 8 additions & 0 deletions testsuite/oiiotool-color/ref/out.txt
Original file line number Diff line number Diff line change
@@ -1,6 +1,14 @@
oiiotool ERROR: --colorconfig : Requested non-existent OCIO config "missing.ocio"
Full command line was:
> oiiotool --nostderr --colorconfig missing.ocio -echo "Nonexistent config"
nope: 0.5,0.5,0.5 half
raw: 0.5,0.5,0.5 half
acescg: 0.5,0.5,0.5 half
srgb_tx: 0.213989,0.213989,0.213989 half
nope: 0.5,0.5,0.5 half
raw: 0.5,0.5,0.5 half
acescg: 0.5,0.5,0.5 half
srgb_tx: 0.735352,0.735352,0.735352 half
Comparing "colormap-inferno.tif" and "ref/colormap-inferno.tif"
PASS
Comparing "colormap-custom.tif" and "ref/colormap-custom.tif"
Expand Down
31 changes: 31 additions & 0 deletions testsuite/oiiotool-color/run.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@

import os

redirect = " >> out.txt 2>&1 "


# print("ociover =", ociover)

# Make test pattern with increasing intensity left to right, decreasing
Expand Down Expand Up @@ -97,6 +100,34 @@
# test various behaviors and misbehaviors related to OCIO configs.
command += oiiotool ("--nostderr --colorconfig missing.ocio -echo \"Nonexistent config\"", failureok=True)

# Test what happens with autocc and input files with color space names in
# their filenames, for both color-managed and non-color-managed spaces.
#
# Input: We transform an exr with each name to acescg, with autocc.
# - "nope" is an unknown color space, and should warn.
# - "raw" means known to be not color managed, and should warn.
# - "acescg" is known and should end up with output (as acescg) that is
# unchanged, so the value should still be 0.5.
# - "srgb_tx" is known but should make a real transformation, giving a pixel
# value that is not 0.5.
for c in ("nope", "raw", "acescg", "srgb_tx") :
command += oiiotool (f"--pattern constant:color=.5 1x1 3 -d half -o in_{c}.exr")
command += oiiotool (f"--autocc in_{c}.exr -o out_acescg.exr")
command += oiiotool (f"out_acescg.exr -echo \"{c}: {{TOP.AVGCOLOR}} {{TOP.nativeformat}}\"")
# Output: We transform an acescg image to outputs with each name, with autocc.
# - "nope" is an unknown color space, and should warn. Or should it?
# - "raw" should retain the 0.5 value with no warning, since you're asking to
# output a non-color-manaaged image.
# - "acescg" is known and should end up with output (as acescg) that is
# unchanged, so the value should still be 0.5.
# - "srgb_tx" is known but should make a real transformation, giving a pixel
# value that is not 0.5.
for c in ("nope", "raw", "acescg", "srgb_tx") :
command += oiiotool (f"--pattern constant:color=.5 1x1 3 -d half -iscolorspace acescg "
+ f"-autocc -o out_{c}.exr")
command += oiiotool (f"out_{c}.exr -echo \"{c}: {{TOP.AVGCOLOR}} {{TOP.nativeformat}}\"")



# To add more tests, just append more lines like the above and also add
# the new 'feature.tif' (or whatever you call it) to the outputs list,
Expand Down
59 changes: 59 additions & 0 deletions testsuite/python-colorconfig/ref/out-ocio230.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
getNumColorSpaces = 15
getColorSpaceNames = ['ACES2065-1', 'ACEScc', 'ACEScct', 'ACEScg', 'Linear P3-D65', 'Linear Rec.2020', 'Linear Rec.709 (sRGB)', 'Gamma 1.8 Rec.709 - Texture', 'Gamma 2.2 AP1 - Texture', 'Gamma 2.2 Rec.709 - Texture', 'Gamma 2.4 Rec.709 - Texture', 'sRGB Encoded AP1 - Texture', 'sRGB Encoded P3-D65 - Texture', 'sRGB - Texture', 'Raw']
Index of 'lin_srgb' = 6
Index of 'unknown' = -1
Name of color space 2 = ACEScct
getNumLooks = 1
getLookNames = ['ACES 1.3 Reference Gamut Compression']
getNumDisplays = 6
getDisplayNames = ['sRGB - Display', 'Display P3 - Display', 'Rec.1886 Rec.709 - Display', 'Rec.2100-PQ - Display', 'ST2084-P3-D65 - Display', 'P3-D65 - Display']
getDefaultDisplayName = sRGB - Display
getNumViews = 3
getViewNames = ['ACES 1.0 - SDR Video', 'Un-tone-mapped', 'Raw']
getDefaultViewName = ACES 1.0 - SDR Video
getNumRoles = 9
getRoles = ['aces_interchange', 'cie_xyz_d65_interchange', 'color_picking', 'color_timing', 'compositing_log', 'data', 'matte_paint', 'scene_linear', 'texture_paint']
aliases of 'scene_linear' are ['ACES - ACEScg', 'lin_ap1']
resolve('foo'): foo
resolve('linear'): Linear Rec.709 (sRGB)
resolve('scene_linear'): ACEScg
resolve('lin_srgb'): Linear Rec.709 (sRGB)
resolve('srgb'): sRGB - Texture
resolve('ACEScg'): ACEScg
equivalent('lin_srgb', 'srgb'): False
equivalent('scene_linear', 'srgb'): False
equivalent('linear', 'lin_srgb'): False
equivalent('scene_linear', 'lin_srgb'): False
equivalent('ACEScg', 'scene_linear'): True
equivalent('lnf', 'scene_linear'): False
get_color_interop_id('ACEScg') = lin_ap1_scene
get_color_interop_id('lin_srgb') = lin_rec709_scene
get_color_interop_id([1, 13, 1, 1]) = srgb_rec709_scene
get_cicp('pq_rec2020_display') = [9, 16, 9, 1]
get_cicp('unknown_interop_id') = None
isColorSpaceLinear('scene_linear') = True
isColorSpaceLinear('srgb') = False
isData('scene_linear') = False
isData('Raw') = True
equivalent('ACEScg', 'Raw'): False
getColorSpaceFromFilepath('in_acescg.exr') = ACEScg
getColorSpaceFromFilepath('in_srgb_tx.exr') = sRGB - Texture
getColorSpaceFromFilepath('in_raw.exr') = Raw
getColorSpaceFromFilepath('none.exr') = unknown
getColorSpaceFromFilepath('noclue.exr') = unknown

Loaded test OCIO config: oiio_test_v0.9.2.ocio
Parsed color space for filepath 'foo_lin_ap1.exr': ACEScg
Default color space: lin_rec709
Default display: sRGB (~2.22) - Display
Default view for sRGB (~2.22) - Display (from lin_rec709): ACES 1.0 - SDR Video
Default view for sRGB (~2.22) - Display (from 'srgb_tx'): Colorimetry
Color space name from DisplayView transform referencing Shared View: sRGB (~2.22) - Display
Test buffer -- initial values: [[[0.1 0.5 0.9]]] (ACEScg)
ociodisplay #1 (apply default display/view): [[[-2.123 0.671 0.8037]]] (sRGB (~2.22) - Display)
ociodisplay #2 (apply default display/view again): [[[-2.783 0.766 0.9873]]] (sRGB (~2.22) - Display)
ociodisplay #3 (inverse look): [[[-18.22 0.876 2.121]]] (sRGB (~2.22) - Display)
ociodisplay #4 (forwards look): [[[-21330. 29.03 100.4 ]]] (sRGB (~2.22) - Display)
ociodisplay #5 (inverse look + forwards look): [[[93.44 93.44 93.44]]] (sRGB (~2.22) - Display)

Done.
Loading
Loading