Skip to content

Commit b611970

Browse files
authored
IBA::contrast_remap() and oiiotool --contrast (#2043)
Allows flexible linear or sigmoidal contrast remapping.
1 parent 07dcbc9 commit b611970

22 files changed

+467
-106
lines changed

src/doc/imagebufalgo.tex

Lines changed: 75 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1197,6 +1197,80 @@ \section{Image arithmetic}
11971197
\apiend
11981198

11991199

1200+
\apiitem{ImageBuf {\ce contrast_remap} (const ImageBuf \&src, \\
1201+
\bigspc\spc cspan<float> black=0.0f, cspan<float> white=1.0f, \\
1202+
\bigspc\spc cspan<float> min=0.0f, cspan<float> max=1.0f, \\
1203+
\bigspc\spc cspan<float> scontrast=1.0f, cspan<float> sthresh=0.5f, \\
1204+
\bigspc\spc ROI roi=\{\}, int nthreads=0) \\
1205+
bool {\ce contrast_remap} (ImageBuf \&dst, const ImageBuf \&src,\\
1206+
\bigspc\spc cspan<float> black=0.0f, cspan<float> white=1.0f, \\
1207+
\bigspc\spc cspan<float> min=0.0f, cspan<float> max=1.0f, \\
1208+
\bigspc\spc cspan<float> scontrast=1.0f, cspan<float> sthresh=0.5f, \\
1209+
\bigspc\spc ROI roi=\{\}, int nthreads=0)}
1210+
\index{ImageBufAlgo!contrast_remap} \indexapi{contrast_remap}
1211+
\NEW % 1.9
1212+
1213+
Return (or copy into {\cf dst}) pixel values that are a contrast-remap
1214+
of the corresponding values of the {\cf src} image, transforming pixel
1215+
value domain [black, white] to range [min, max], either linearly or with
1216+
optional application of a smooth sigmoidal remapping (if scontrast != 1.0).
1217+
1218+
\noindent The following steps are performed, in order:
1219+
1220+
\begin{enumerate}
1221+
\item Linearly rescale {\cf black} to 0.0 and {\cf white} to 1.0.
1222+
\item Optionally raise to power 1/gamma (only if {\cf gamma} is not 1.0).
1223+
\item Apply a sigmoidal remapping (only if {\cf scontrast} $> 1$) where a larger
1224+
{\cf scontrast} value makes a steeper slope, and the steepest part is at
1225+
value {\cf sthresh} (relative to the new remapped value after steps 1 \& 2;
1226+
the default is 0.5).
1227+
\item Rescale the range of that result: 0.0 to min and 1.0 to max.
1228+
\end{enumerate}
1229+
1230+
Values outside of the [black,white] range will be extrapolated to
1231+
outside [min,max], so it may be prudent to apply a clamp() to the
1232+
results.
1233+
1234+
The black, white, min, max, scontrast, sthresh parameters may each
1235+
either be a single float value for all channels, or a span giving
1236+
per-channel values.
1237+
1238+
You can use this function for a simple linear contrast remapping of
1239+
[black, white] to [min, max] if you use the default values for sthresh.
1240+
Or just a simple sigmoidal contrast stretch within the [0,1] range if
1241+
you leave all other parameters at their defaults, or a combination of
1242+
these effects. Note that if black == white, the result will be a simple
1243+
binary thresholding where values < black map to min and values >= bkack
1244+
map to max.
1245+
1246+
\smallskip
1247+
\noindent Examples:
1248+
\begin{code}
1249+
ImageBuf A ("tahoe.tif");
1250+
1251+
// Simple linear remap that stretches input 0.1 to black, and input
1252+
// 0.75 to white.
1253+
ImageBuf linstretch = ImageBufAlgo::contrast_remap (A, 0.1f, 0.75f);
1254+
1255+
// Remapping 0->1 and 1->0 inverts the colors of the image,
1256+
// equivalent to ImageBufAlgo::invert().
1257+
ImageBuf inverse = ImageBufAlgo::contrast_remap (A, 1.0f, 0.0f);
1258+
1259+
// Use a sigmoid curve to add contrast but without any hard cutoffs.
1260+
// Use a contrast parameter of 5.0.
1261+
ImageBuf sigmoid = ImageBufAlgo::contrast_remap (a, 0.0f, 1.0f,
1262+
0.0f, 1.0f, 5.0f);
1263+
\end{code}
1264+
1265+
\noindent \begin{tabular}{cccc}
1266+
\includegraphics[width=1.1in]{figures/tahoe-small.jpg} &
1267+
\includegraphics[width=1.1in]{figures/tahoe-lincontrast.jpg} &
1268+
\includegraphics[width=1.1in]{figures/tahoe-inverse.jpg} &
1269+
\includegraphics[width=1.1in]{figures/tahoe-sigmoid.jpg} \\
1270+
original & linstretch & inverse & sigmoid \\
1271+
\end{tabular}
1272+
\apiend
1273+
12001274

12011275
\apiitem{ImageBuf {\ce color_map} (const ImageBuf \&src, int srcchannel, \\
12021276
\bigspc\spc int nknots, int channels, cspan<float> knots, \\
@@ -1237,7 +1311,7 @@ \section{Image arithmetic}
12371311
// Use luminance of a.exr (assuming Rec709 primaries and a linear
12381312
// scale) and map to a color spectrum:
12391313
ImageBuf A ("a.exr");
1240-
ImageBuf B = ImageBufAlgo::color_map (B, A, -1, "inferno");
1314+
ImageBuf B = ImageBufAlgo::color_map (A, -1, "inferno");
12411315

12421316
float mymap[] = { 0.25, 0.25, 0.25, 0, 0.5, 0, 1, 0, 0 };
12431317
B = ImageBufAlgo::color_map (A, -1 /* use luminance */,

src/doc/makefigures.bash

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,10 @@ ${OIIOTOOL} --autocc dilate.jpg erode.jpg -sub -d uint8 -o morphgradient.jpg
7272
${OIIOTOOL} --autocc morphsource.jpg morphopen.jpg -sub -d uint8 -o tophat.jpg
7373
${OIIOTOOL} --autocc morphclose.jpg morphsource.jpg -sub -d uint8 -o bottomhat.jpg
7474

75+
${OIIOTOOL} -autocc tahoe-small.jpg --contrast:black=0.1:white=0.75 -o tahoe-lincontrast.jpg
76+
${OIIOTOOL} -autocc tahoe-small.jpg --contrast:black=1:white=0 -o tahoe-inverse.jpg
77+
${OIIOTOOL} -autocc tahoe-small.jpg --contrast:scontrast=5 -o tahoe-sigmoid.jpg
78+
7579
${OIIOTOOL} -autocc tahoe-small.jpg --colormap inferno -o colormap-inferno.jpg
7680
${OIIOTOOL} -autocc tahoe-small.jpg --colormap viridis -o colormap-viridis.jpg
7781
${OIIOTOOL} -autocc tahoe-small.jpg --colormap spectrum -o colormap-spectrum.jpg

src/doc/oiiotool.tex

Lines changed: 39 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -940,27 +940,6 @@ \section{\oiiotool commands: general and image information}
940940
expansion for a subset of the command line.
941941
\apiend
942942

943-
\begin{comment}
944-
\apiitem{\ce --inplace}
945-
Causes the output to \emph{replace} the input file, rather than create a
946-
new file with a different name.
947-
948-
Without this flag, \oiiotool expects two file names, which will
949-
be used to specify the input and output files, respectively.
950-
951-
But when {\cf --inplace} option is used, any number of file names $\ge 1$ may
952-
be specified, and the image conversion commands are applied to each file
953-
in turn, with the output being saved under the original file name. This
954-
is useful for applying the same conversion to many files.
955-
956-
For example, the following example will add the caption ``Hawaii
957-
vacation'' to all JPEG files in the current directory:
958-
959-
\begin{code}
960-
oiiotool --inplace --adjust-time --caption "Hawaii vacation" *.jpg
961-
\end{code}
962-
\apiend
963-
\end{comment}
964943

965944

966945
\section{\oiiotool commands: reading and writing images}
@@ -1982,6 +1961,45 @@ \section{\oiiotool commands that do image processing}
19821961
\includegraphics[width=1.5in]{figures/luma.jpg} \\
19831962
\apiend
19841963

1964+
\apiitem{\ce --contrast}
1965+
\NEW % 1.9
1966+
Remap pixel values from [black, white] to [min, max], with an optional
1967+
smooth sigmoidal contrast stretch as well.
1968+
1969+
Optional appended arguments include:
1970+
1971+
\begin{tabular}{p{10pt} p{1in} p{3.75in}}
1972+
& {\cf black=}\emph{vals} & Specify black value(s), default 0.0. \\
1973+
& {\cf white=}\emph{vals} & Specify white value(s), default 1.0. \\
1974+
& {\cf min=}\emph{vals} & Specify the minimum range value(s), default 0.0. \\
1975+
& {\cf max=}\emph{vals} & Specify the maximum range value(s), default 1.0. \\
1976+
& {\cf scontrast=}\emph{vals} & Specify sigmoidal contrast slope value(s), default 1.0. \\
1977+
& {\cf sthresh=}\emph{vals} & Specify sigmoidal threshold value(s) giving
1978+
the position of maximum slope, default 0.5. \\
1979+
& {\cf clamp=}\emph{on} & If \emph{on} is nonzero, will optionally clamp
1980+
all result channels to [min,max].
1981+
\end{tabular}
1982+
1983+
\noindent Each \emph{vals} may be either a single floating point value
1984+
for all channels, or a comma-separated list of per-channel values.
1985+
1986+
\noindent Examples:
1987+
\begin{code}
1988+
oiiotool tahoe.tif --contrast:black=0.1:white=0.75 -o linstretch.tif
1989+
oiiotool tahoe.tif --contrast:black=1.0:white=0.0:clamp=0 -o inverse.tif
1990+
oiiotool tahoe.tif --contrast:scontrast=5 -o sigmoid.tif
1991+
\end{code}
1992+
1993+
\noindent \begin{tabular}{cccc}
1994+
\includegraphics[width=1.1in]{figures/tahoe-small.jpg} &
1995+
\includegraphics[width=1.1in]{figures/tahoe-lincontrast.jpg} &
1996+
\includegraphics[width=1.1in]{figures/tahoe-inverse.jpg} &
1997+
\includegraphics[width=1.1in]{figures/tahoe-sigmoid.jpg} \\
1998+
original & linstretch & inverse & sigmoid \\
1999+
\end{tabular}
2000+
\apiend
2001+
2002+
19852003
\apiitem{\ce --colormap {\rm \emph{mapname}}}
19862004
Creates an RGB color map based on the luminance of the input image. The
19872005
{\cf mapname} may be one of: \qkw{magma}, \qkw{inferno}, \qkw{plasma},

src/doc/openimageio.tex

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@
9696
\bigskip \\
9797
}
9898
\date{{\large
99-
Date: 8 Oct 2018
99+
Date: 21 Oct 2018
100100
%\\ (with corrections, 19 Mar 2018)
101101
}}
102102

src/doc/pythonbindings.tex

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2579,6 +2579,41 @@ \subsection{Image arithmetic}
25792579
\apiend
25802580

25812581

2582+
\apiitem{ImageBuf ImageBufAlgo.{\ce contrast_remap} (src, \\
2583+
\bigspc\spc black=0.0, white=1.0, min=0.0, max=1.0, \\
2584+
\bigspc\spc sthresh=0.0, scontrast=1.0, \\
2585+
\bigspc\spc ROI roi=\{\}, int nthreads=0) \\
2586+
bool ImageBufAlgo.{\ce contrast_remap} (ImageBuf \&dst, src,\\
2587+
\bigspc\spc black=0.0, white=1.0, min=0.0, max=1.0, \\
2588+
\bigspc\spc sthresh=0.0, scontrast=1.0, \\
2589+
\bigspc\spc ROI roi=\{\}, int nthreads=0)}
2590+
\index{ImageBufAlgo!contrast_remap} \indexapi{contrast_remap}
2591+
\NEW % 1.9
2592+
2593+
Return (or copy into {\cf dst}) pixel values that are a contrast-remap
2594+
of the corresponding values of the {\cf src} image, transforming pixel
2595+
value domain [black, white] to range [min, max], either linearly or with
2596+
optional application of a smooth sigmoidal remapping (if scontrast != 1.0).
2597+
2598+
\smallskip
2599+
\noindent Examples:
2600+
\begin{code}
2601+
A = ImageBuf('tahoe.tif');
2602+
2603+
# Simple linear remap that stretches input 0.1 to black, and input
2604+
# 0.75 to white.
2605+
linstretch = ImageBufAlgo.contrast_remap (A, black=0.1, white=0.75)
2606+
2607+
// Remapping 0->1 and 1->0 inverts the colors of the image,
2608+
// equivalent to ImageBufAlgo.invert().
2609+
inverse = ImageBufAlgo.contrast_remap (A, black=1.0, white=0.0)
2610+
2611+
// Use a sigmoid curve to add contrast but without any hard cutoffs.
2612+
// Use a contrast parameter of 5.0.
2613+
sigmoid = ImageBufAlgo.contrast_remap (a, contrast=5.0)
2614+
\end{code}
2615+
\apiend
2616+
25822617
\apiitem{ImageBuf ImageBufAlgo.{\ce color_map} (src, srcchannel, \\
25832618
\bigspc\bigspc nknots, channels, knots, roi=ROI.All, nthreads=0) \\
25842619
ImageBuf ImageBufAlgo.{\ce color_map} (src, srcchannel, \\

src/include/OpenImageIO/imagebufalgo.h

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -795,6 +795,46 @@ bool OIIO_API color_map (ImageBuf &dst, const ImageBuf &src, int srcchannel,
795795

796796

797797

798+
/// Remap contrast by transforming values [black, white] to new range
799+
/// [min, max], either linearly or with optional application of a smooth
800+
/// sigmoidal remapping (if scontrast != 1.0).
801+
///
802+
/// The following steps are performed, in order:
803+
/// 1. Linearly rescale values [black, white] to [0, 1].
804+
/// 2. If scontrast != 1, apply a sigmoidal remapping where a larger
805+
/// scontrast value makes a steeper slope, and the steepest part is at
806+
/// value sthresh (relative to the new remapped value after steps 1 & 2;
807+
/// the default is 0.5).
808+
/// 3. Rescale the range of that result: 0.0 -> min and 1.0 -> max.
809+
///
810+
/// Values outside of the [black,white] range will be extrapolated to
811+
/// outside [min,max], so it may be prudent to apply a clamp() to the
812+
/// results.
813+
///
814+
/// The black, white, min, max, scontrast, sthresh parameters may each
815+
/// either be a single float value for all channels, or a span giving
816+
/// per-channel values.
817+
///
818+
/// You can use this function for a simple linear contrast remapping of
819+
/// [black, white] to [min, max] if you use the default values for sthresh.
820+
/// Or just a simple sigmoidal contrast stretch within the [0,1] range if
821+
/// you leave all other parameters at their defaults, or a combination of
822+
/// these effects. Note that if black == white, the result will be a simple
823+
/// binary thresholding where values < black map to min and values >= bkack
824+
/// map to max.
825+
OIIO_API ImageBuf contrast_remap (const ImageBuf &src,
826+
cspan<float> black=0.0f, cspan<float> white=1.0f,
827+
cspan<float> min=0.0f, cspan<float> max=1.0f,
828+
cspan<float> scontrast=1.0f, cspan<float> sthresh=0.5f,
829+
ROI={}, int nthreads=0);
830+
OIIO_API bool contrast_remap (ImageBuf &dst, const ImageBuf &src,
831+
cspan<float> black=0.0f, cspan<float> white=1.0f,
832+
cspan<float> min=0.0f, cspan<float> max=1.0f,
833+
cspan<float> scontrast=1.0f, cspan<float> sthresh=0.5f,
834+
ROI={}, int nthreads=0);
835+
836+
837+
798838

799839
struct OIIO_API PixelStats {
800840
std::vector<float> min;

0 commit comments

Comments
 (0)