Skip to content

Commit 0af3c91

Browse files
authored
Color transform unpremult logic changes. (#1864)
After some deep thinking and discussion with people who know more color than me, I have become convinced that you *almost always* want to make sure to un-premultiply colors (i.e. divide by alpha) before doing color space transformations with OCIO, and then re-multiply by alpha (to get back to "associated alpha") immediately after the color transformation. To support this, * For the ImageBufAlgo color transformation functions, change the default value of the `unpremult` parameter from `false` to `true`. * `oiiotool --autocc` will do the unpremult/premult steps surrounding any automatic color transformations that it does upon input and output. * `oiiotool --colorconvert` and the various `--ocio` commands now take an optional modifier `:unpremult=1` which will cause the conversion to be internally bracked by a unpremult/premult step (when you do this, you no longer need an explicit --unpremult and --premult on the command line). * oiiotool now issues warnings for cases where it has a good reason to think you might be double-unpremulting (for example, if you do both --unpremult and follow it by a --colorconvert:unpremult=1). Later, we'll stage a second part of this where we change the default value for unpremult= to be 1, so that the modifier is not needed (at which point you REALLY don't want the explicit --unpremult/--premult, or it will be wrong). On the whole, I think this makes the situation less error prone for casual users. Most oiiotool command lines should just use --autocc and then not need to worry about any explicit use of `--colorconvert` or `--unpremult/--premult`. For the rare case when more control is needed, the individual commands and modifiers can be used. Note that for oiiotool, `--autocc` is not on by default. If you want images auto-converted to linear space and ouputs auto-converted from linear to the desired output space, you need to use `--autocc`.
1 parent 41bc41e commit 0af3c91

File tree

9 files changed

+135
-36
lines changed

9 files changed

+135
-36
lines changed

CHANGES.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,12 @@ Public API changes:
4343
only be used to pass into certain IBA functions). The color space names
4444
"rgb" and "default" are now understood to be synonyms for the default
4545
"linear" color space. #1788 (1.9.0)
46+
* `ImageBufAlgo::colorconvert` and various `ocio` transformations have
47+
changed the default value of their `unpremult` parameter from `false` to
48+
`true`, reflecting the fact that we believe this is almost always the more
49+
correct choice. Also, if their input image is clearly marked as having
50+
unasociated alpha already, they will not bracket the color conversion with
51+
the requested unpremult/premult. #1864 (1.9.2)
4652
* Remove long-deprecated API calls:
4753
* ImageBuf::get_pixels/get_pixel_channels varieties deprecated since 1.6.
4854
* ImageBuf::set_deep_value_uint, deprecated since 1.7.
@@ -68,6 +74,16 @@ Fixes, minor enhancements, and performance improvements:
6874
look good converted to greyscale, and usable by people with color
6975
blindness. #1820 (1.9.2)
7076
* oiiotool no longer enables autotile by default. #1856 (1.9.2)
77+
* `--colorconvert`, `--tocolorspace`, and all of the `--ocio` commands
78+
now take an optional modifier `:unpremult=1` which causes the color
79+
conversion to be internally bracketed by unpremult/premult steps (if
80+
the image has alpha and is not already marked as having unassociated
81+
alpha). You should therefore prefer `--colorconvert:unpremult=1 from to`
82+
rather than the more complex `--unpremult --colorconvert from to -premult`.
83+
#1864 (1.9.2)
84+
* `--autocc` will also cause unpremult/premult to bracket any color
85+
transformations it does automatically for read and write (if the image
86+
has alpha and does not appear to already be unassociated). #1864 (1.9.2)
7187
* ImageBufAlgo:
7288
* `color_map()` new maps "inferno", "magma", "plasma", "viridis".
7389
#1820 (1.9.2)

src/doc/oiiotool.tex

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1019,7 +1019,7 @@ \subsection*{Reading images}
10191019
\apiend
10201020

10211021
\apiitem{\ce --autocc}
1022-
Turns on automatic color conversion: Every input image file will be
1022+
Turns on automatic color space conversion: Every input image file will be
10231023
immediately converted to a scene-referred linear color space, and every file
10241024
written will be first transformed to an appropriate output color space based
10251025
on the filename or type. Additionally, if the name of an output file
@@ -2903,6 +2903,11 @@ \section{\oiiotool commands for color management}
29032903
& {\cf value=}\emph{str} & Adds a key/value pair to the ``context'' that
29042904
OpenColorIO will used when applying the look. Multiple key/value pairs
29052905
may be specified by making each one a comma-separated list. \\
2906+
& {\cf unpremult=}\emph{val} & If the numeric \emph{val} is nonzero, the
2907+
pixel values will be ``un-premultipled'' (divided by alpha) prior to
2908+
the actual color conversion, and then re-multipled by alpha afterwards.
2909+
The default is 0, meaning the color transformation not will be
2910+
automatically bracketed by divide-by-alpha / mult-by-alpha operations. \\
29062911
& {\cf strict=}\emph{val} & When nonzero (the default), an
29072912
inability to perform the color transform will be a hard error. If
29082913
strict is 0, inability to find the transformation will just print a
@@ -2936,6 +2941,11 @@ \section{\oiiotool commands for color management}
29362941
& {\cf value=}\emph{str} & Adds a key/value pair to the ``context'' that
29372942
OpenColorIO will used when applying the look. Multiple key/value pairs
29382943
may be specified by making each one a comma-separated list. \\
2944+
& {\cf unpremult=}\emph{val} & If the numeric \emph{val} is nonzero, the
2945+
pixel values will be ``un-premultipled'' (divided by alpha) prior to
2946+
the actual color conversion, and then re-multipled by alpha afterwards.
2947+
The default is 0, meaning the color transformation not will be
2948+
automatically bracketed by divide-by-alpha / mult-by-alpha operations. \\
29392949
\end{tabular}
29402950

29412951
This command is only meaningful if OIIO was compiled with OCIO support
@@ -2966,6 +2976,11 @@ \section{\oiiotool commands for color management}
29662976
& {\cf value=}\emph{str} & Adds a key/value pair to the ``context'' that
29672977
OpenColorIO will used when applying the display transform. Multiple key/value pairs
29682978
may be specified by making each one a comma-separated list. \\
2979+
& {\cf unpremult=}\emph{val} & If the numeric \emph{val} is nonzero, the
2980+
pixel values will be ``un-premultipled'' (divided by alpha) prior to
2981+
the actual color conversion, and then re-multipled by alpha afterwards.
2982+
The default is 0, meaning the color transformation not will be
2983+
automatically bracketed by divide-by-alpha / mult-by-alpha operations. \\
29692984
\end{tabular}
29702985

29712986
This command is only meaningful if OIIO was compiled with OCIO support
@@ -2989,6 +3004,11 @@ \section{\oiiotool commands for color management}
29893004
\begin{tabular}{p{10pt} p{1in} p{3.75in}}
29903005
& {\cf inverse=}\emph{val} & If \emph{val} is nonzero, inverts the
29913006
color transformation. \\
3007+
& {\cf unpremult=}\emph{val} & If the numeric \emph{val} is nonzero, the
3008+
pixel values will be ``un-premultipled'' (divided by alpha) prior to
3009+
the actual color conversion, and then re-multipled by alpha afterwards.
3010+
The default is 0, meaning the color transformation not will be
3011+
automatically bracketed by divide-by-alpha / mult-by-alpha operations. \\
29923012
\end{tabular}
29933013

29943014
This command is only meaningful if OIIO was compiled with OCIO support

src/doc/openimageio.tex

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -94,8 +94,8 @@
9494
\bigskip \\
9595
}
9696
\date{{\large
97-
Date: 5 Dec 2017
98-
\\ (with corrections, 4 Feb 2018)
97+
Date: 9 Feb 2018
98+
%\\ (with corrections, 4 Feb 2018)
9999
}}
100100

101101

src/include/OpenImageIO/imagebufalgo.h

Lines changed: 25 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -970,15 +970,17 @@ bool OIIO_API rangeexpand (ImageBuf &dst, const ImageBuf &src,
970970
/// size as specified by roi. If roi is not defined it will be all
971971
/// of dst, if dst is defined, or all of src, if dst is not yet defined.
972972
///
973-
/// If unpremult is true, unpremultiply before color conversion, then
974-
/// premultiply after the color conversion. You may want to use this
975-
/// flag if your image contains an alpha channel.
973+
/// If unpremult is true, divide the RGB channels by alpha (if it exists and
974+
/// is nonzero) before color conversion, then re-multiply by alpha after the
975+
/// after the color conversion. Passing unpremult=false skips this step,
976+
/// which may be desirable if you know that the image is "unassociated alpha"
977+
/// (a.k.a. "not pre-multiplied colors").
976978
///
977979
/// Return true on success, false on error (with an appropriate error
978980
/// message set in dst).
979981
bool OIIO_API colorconvert (ImageBuf &dst, const ImageBuf &src,
980982
string_view from, string_view to,
981-
bool unpremult=false,
983+
bool unpremult=true,
982984
string_view context_key="",
983985
string_view context_value="",
984986
ColorConfig *colorconfig=NULL,
@@ -991,9 +993,11 @@ bool OIIO_API colorconvert (ImageBuf &dst, const ImageBuf &src,
991993
/// size as specified by roi. If roi is not defined it will be all
992994
/// of dst, if dst is defined, or all of src, if dst is not yet defined.
993995
///
994-
/// If unpremult is true, unpremultiply before color conversion, then
995-
/// premultiply after the color conversion. You may want to use this
996-
/// flag if your image contains an alpha channel.
996+
/// If unpremult is true, divide the RGB channels by alpha (if it exists and
997+
/// is nonzero) before color conversion, then re-multiply by alpha after the
998+
/// after the color conversion. Passing unpremult=false skips this step,
999+
/// which may be desirable if you know that the image is "unassociated alpha"
1000+
/// (a.k.a. "not pre-multiplied colors").
9971001
///
9981002
/// Return true on success, false on error (with an appropriate error
9991003
/// message set in dst).
@@ -1029,7 +1033,7 @@ bool OIIO_API colorconvert (float *color, int nchannels,
10291033
/// message set in dst).
10301034
bool OIIO_API ociolook (ImageBuf &dst, const ImageBuf &src,
10311035
string_view looks, string_view from, string_view to,
1032-
bool unpremult=false, bool inverse=false,
1036+
bool unpremult=true, bool inverse=false,
10331037
string_view key="", string_view value="",
10341038
ColorConfig *colorconfig=NULL,
10351039
ROI roi=ROI::All(), int nthreads=0);
@@ -1038,23 +1042,25 @@ bool OIIO_API ociolook (ImageBuf &dst, const ImageBuf &src,
10381042
/// "display" transform. If from or looks are NULL, it will not
10391043
/// override the look or source color space (subtly different than
10401044
/// passing "", the empty string, which means to use no look or source
1041-
/// space).
1045+
/// space). If inverse is true, it will reverse the color transformation.
10421046
///
10431047
/// If dst is not yet initialized, it will be allocated to the same
10441048
/// size as specified by roi. If roi is not defined it will be all
10451049
/// of dst, if dst is defined, or all of src, if dst is not yet defined.
10461050
/// In-place operations (dst == src) are supported.
10471051
///
1048-
/// If unpremult is true, unpremultiply before color conversion, then
1049-
/// premultiply after the color conversion. You may want to use this
1050-
/// flag if your image contains an alpha channel.
1052+
/// If unpremult is true, divide the RGB channels by alpha (if it exists and
1053+
/// is nonzero) before color conversion, then re-multiply by alpha after the
1054+
/// after the color conversion. Passing unpremult=false skips this step,
1055+
/// which may be desirable if you know that the image is "unassociated alpha"
1056+
/// (a.k.a. "not pre-multiplied colors").
10511057
///
10521058
/// Return true on success, false on error (with an appropriate error
10531059
/// message set in dst).
10541060
bool OIIO_API ociodisplay (ImageBuf &dst, const ImageBuf &src,
10551061
string_view display, string_view view,
10561062
string_view from="", string_view looks="",
1057-
bool unpremult=false,
1063+
bool unpremult=true,
10581064
string_view key="", string_view value="",
10591065
ColorConfig *colorconfig=NULL,
10601066
ROI roi=ROI::All(), int nthreads=0);
@@ -1067,15 +1073,17 @@ bool OIIO_API ociodisplay (ImageBuf &dst, const ImageBuf &src,
10671073
/// size as specified by roi. If roi is not defined it will be all
10681074
/// of dst, if dst is defined, or all of src, if dst is not yet defined.
10691075
///
1070-
/// If unpremult is true, unpremultiply before color conversion, then
1071-
/// premultiply after the color conversion. You may want to use this
1072-
/// flag if your image contains an alpha channel.
1076+
/// If unpremult is true, divide the RGB channels by alpha (if it exists and
1077+
/// is nonzero) before color conversion, then re-multiply by alpha after the
1078+
/// after the color conversion. Passing unpremult=false skips this step,
1079+
/// which may be desirable if you know that the image is "unassociated alpha"
1080+
/// (a.k.a. "not pre-multiplied colors").
10731081
///
10741082
/// Return true on success, false on error (with an appropriate error
10751083
/// message set in dst).
10761084
bool OIIO_API ociofiletransform (ImageBuf &dst, const ImageBuf &src,
10771085
string_view name,
1078-
bool unpremult=false, bool inverse=false,
1086+
bool unpremult=true, bool inverse=false,
10791087
ColorConfig *colorconfig=NULL,
10801088
ROI roi=ROI::All(), int nthreads=0);
10811089

src/libOpenImageIO/color_ocio.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1236,6 +1236,13 @@ ImageBufAlgo::colorconvert (ImageBuf &dst, const ImageBuf &src,
12361236
roi.chbegin, src, roi, nthreads);
12371237
}
12381238

1239+
if (unpremult && src.spec().alpha_channel >= 0 &&
1240+
src.spec().get_int_attribute("oiio:UnassociatedAlpha") != 0) {
1241+
// If we appear to be operating on an image that already has
1242+
// unassociated alpha, don't do a redundant unpremult step.
1243+
unpremult = false;
1244+
}
1245+
12391246
bool ok = true;
12401247
OIIO_DISPATCH_COMMON_TYPES2 (ok, "colorconvert", colorconvert_impl,
12411248
dst.spec().format, src.spec().format,

src/libOpenImageIO/imagebufalgo_pixelmath.cpp

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -562,7 +562,11 @@ ImageBufAlgo::unpremult (ImageBuf &dst, const ImageBuf &src,
562562
{
563563
if (! IBAprep (roi, &dst, &src, IBAprep_CLAMP_MUTUAL_NCHANNELS))
564564
return false;
565-
if (src.spec().alpha_channel < 0) {
565+
if (src.spec().alpha_channel < 0
566+
// Wise? || src.spec().get_int_attribute("oiio:UnassociatedAlpha") != 0
567+
) {
568+
// If there is no alpha channel, just *copy* instead of dividing
569+
// by alpha.
566570
if (&dst != &src)
567571
return paste (dst, src.spec().x, src.spec().y, src.spec().z,
568572
roi.chbegin, src, roi, nthreads);
@@ -571,6 +575,8 @@ ImageBufAlgo::unpremult (ImageBuf &dst, const ImageBuf &src,
571575
bool ok;
572576
OIIO_DISPATCH_COMMON_TYPES2 (ok, "unpremult", unpremult_, dst.spec().format,
573577
src.spec().format, dst, src, roi, nthreads);
578+
// Mark the output as having unassociated alpha
579+
dst.specmod().attribute ("oiio:UnassociatedAlpha", 1);
574580
return ok;
575581
}
576582

@@ -624,6 +630,8 @@ ImageBufAlgo::premult (ImageBuf &dst, const ImageBuf &src,
624630
bool ok;
625631
OIIO_DISPATCH_COMMON_TYPES2 (ok, "premult", premult_, dst.spec().format,
626632
src.spec().format, dst, src, roi, nthreads);
633+
// Clear the output of any prior marking of associated alpha
634+
dst.specmod().erase_attribute ("oiio:UnassociatedAlpha");
627635
return ok;
628636
}
629637

0 commit comments

Comments
 (0)