Skip to content

Commit

Permalink
make_texture: Clamp half values to prevent Inf values popping up (#2892)
Browse files Browse the repository at this point in the history
When making a texture out of float values, but writing ultimately to a
`half` file, it's not too hard to end up with valid but large float
values that are outside the range representable by a `half` (fp16)
value and therefore will get converted to an `Inf`, which nobody wants
in their texture.

This patch prevents Inf from showing up in the output of make_texture
by clamping output buffers to the range of `half` when the source data
are float and the destination file is half.
  • Loading branch information
lgritz committed Mar 26, 2021
1 parent 05661f8 commit 32d4c25
Show file tree
Hide file tree
Showing 4 changed files with 105 additions and 0 deletions.
14 changes: 14 additions & 0 deletions src/libOpenImageIO/maketexture.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -606,6 +606,13 @@ write_mipmap(ImageBufAlgo::MakeTextureMode mode, std::shared_ptr<ImageBuf>& img,
ImageSpec outspec = outspec_template;
outspec.set_format(outputdatatype);

// Going from float to half is prone to generating Inf values if we had
// any floats that were out of the range that half can represent. Nobody
// wants Inf in textures; better to clamp.
bool clamp_half = (outspec.format == TypeHalf
&& (img->spec().format == TypeFloat
|| img->spec().format == TypeHalf));

if (mipmap && !out->supports("multiimage") && !out->supports("mipmap")) {
outstream << "maketx ERROR: \"" << outputfilename
<< "\" format does not support multires images\n";
Expand Down Expand Up @@ -673,6 +680,11 @@ write_mipmap(ImageBufAlgo::MakeTextureMode mode, std::shared_ptr<ImageBuf>& img,
outstream << " Top level is " << formatres(outspec) << std::endl;
}

if (clamp_half) {
std::shared_ptr<ImageBuf> tmp(new ImageBuf);
ImageBufAlgo::clamp(*tmp, *img, -HALF_MAX, HALF_MAX, true);
std::swap(tmp, img);
}
if (!img->write(out)) {
// ImageBuf::write transfers any errors from the ImageOutput to
// the ImageBuf.
Expand Down Expand Up @@ -810,6 +822,8 @@ write_mipmap(ImageBufAlgo::MakeTextureMode mode, std::shared_ptr<ImageBuf>& img,
Filter2D::destroy(filter);
}
}
if (clamp_half)
ImageBufAlgo::clamp(*small, *small, -HALF_MAX, HALF_MAX, true);

stat_miptime += miptimer();
outspec = smallspec;
Expand Down
42 changes: 42 additions & 0 deletions testsuite/oiiotool-maketx/ref/out-win.txt
Original file line number Diff line number Diff line change
Expand Up @@ -289,6 +289,48 @@ nan.exr : 64 x 64, 3 channel, half openexr
Stats FiniteCount: 4096 4096 4096
Constant: No
Monochrome: Yes
Reading bigval.exr
bigval.exr : 2 x 2, 3 channel, half openexr
MIP-map levels: 2x2 1x1
SHA-1: 9DBE885D25443B9CDDC4BAC5997162861B5B3F15
channel list: R, G, B
tile size: 64 x 64
compression: "zip"
fovcot: 1
Orientation: 1 (normal)
PixelAspectRatio: 1
screenWindowCenter: 0, 0
screenWindowWidth: 1
textureformat: "Plain Texture"
wrapmodes: "black,black"
oiio:AverageColor: "1e+06,1e+06,1e+06"
oiio:ColorSpace: "Linear"
oiio:ConstantColor: "1e+06,1e+06,1e+06"
oiio:SHA-1: "E3D97EED7EE68F1885685F312DDD7D8773C29862"
oiio:subimages: 1
openexr:roundingmode: 0
MIP 0 of 2 (2 x 2):
Stats Min: 65504.000000 65504.000000 65504.000000 (float)
Stats Max: 65504.000000 65504.000000 65504.000000 (float)
Stats Avg: 65504.000000 65504.000000 65504.000000 (float)
Stats StdDev: 0.000000 0.000000 0.000000 (float)
Stats NanCount: 0 0 0
Stats InfCount: 0 0 0
Stats FiniteCount: 4 4 4
Constant: Yes
Constant Color: 65504.000000 65504.000000 65504.000000 (float)
Monochrome: Yes
MIP 1 of 2 (1 x 1):
Stats Min: 65504.000000 65504.000000 65504.000000 (float)
Stats Max: 65504.000000 65504.000000 65504.000000 (float)
Stats Avg: 65504.000000 65504.000000 65504.000000 (float)
Stats StdDev: 0.000000 0.000000 0.000000 (float)
Stats NanCount: 0 0 0
Stats InfCount: 0 0 0
Stats FiniteCount: 1 1 1
Constant: Yes
Constant Color: 65504.000000 65504.000000 65504.000000 (float)
Monochrome: Yes
Reading checker-exr.pdq
checker-exr.pdq : 128 x 128, 4 channel, half openexr
MIP-map levels: 128x128 64x64 32x32 16x16 8x8 4x4 2x2 1x1
Expand Down
42 changes: 42 additions & 0 deletions testsuite/oiiotool-maketx/ref/out.txt
Original file line number Diff line number Diff line change
Expand Up @@ -289,6 +289,48 @@ nan.exr : 64 x 64, 3 channel, half openexr
Stats FiniteCount: 4096 4096 4096
Constant: No
Monochrome: Yes
Reading bigval.exr
bigval.exr : 2 x 2, 3 channel, half openexr
MIP-map levels: 2x2 1x1
SHA-1: 9DBE885D25443B9CDDC4BAC5997162861B5B3F15
channel list: R, G, B
tile size: 64 x 64
compression: "zip"
fovcot: 1
Orientation: 1 (normal)
PixelAspectRatio: 1
screenWindowCenter: 0, 0
screenWindowWidth: 1
textureformat: "Plain Texture"
wrapmodes: "black,black"
oiio:AverageColor: "1e+06,1e+06,1e+06"
oiio:ColorSpace: "Linear"
oiio:ConstantColor: "1e+06,1e+06,1e+06"
oiio:SHA-1: "E3D97EED7EE68F1885685F312DDD7D8773C29862"
oiio:subimages: 1
openexr:roundingmode: 0
MIP 0 of 2 (2 x 2):
Stats Min: 65504.000000 65504.000000 65504.000000 (float)
Stats Max: 65504.000000 65504.000000 65504.000000 (float)
Stats Avg: 65504.000000 65504.000000 65504.000000 (float)
Stats StdDev: 0.000000 0.000000 0.000000 (float)
Stats NanCount: 0 0 0
Stats InfCount: 0 0 0
Stats FiniteCount: 4 4 4
Constant: Yes
Constant Color: 65504.000000 65504.000000 65504.000000 (float)
Monochrome: Yes
MIP 1 of 2 (1 x 1):
Stats Min: 65504.000000 65504.000000 65504.000000 (float)
Stats Max: 65504.000000 65504.000000 65504.000000 (float)
Stats Avg: 65504.000000 65504.000000 65504.000000 (float)
Stats StdDev: 0.000000 0.000000 0.000000 (float)
Stats NanCount: 0 0 0
Stats InfCount: 0 0 0
Stats FiniteCount: 1 1 1
Constant: Yes
Constant Color: 65504.000000 65504.000000 65504.000000 (float)
Monochrome: Yes
Reading checker-exr.pdq
checker-exr.pdq : 128 x 128, 4 channel, half openexr
MIP-map levels: 128x128 64x64 32x32 16x16 8x8 4x4 2x2 1x1
Expand Down
7 changes: 7 additions & 0 deletions testsuite/oiiotool-maketx/run.py
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,13 @@ def omaketx_command (infile, outfile, extraargs="",
"--fixnan box3", options=":nomipmap=1",
showinfo=True, showinfo_extra="--stats")

# Test that when outputting half textures, we clamp large float values
# rather than inadvertetly turning into Inf in the process of output to
# half.
command += oiiotool (" --pattern constant:color=1.0e6,1.0e6,1.0e6 2x2 3 -d float -o million.tif")
command += omaketx_command ("million.tif", "bigval.exr",
"-d half", showinfo_extra="--stats")

# Test --format to force exr even though it can't be deduced from the name.
command += omaketx_command ("checker.tif", "checker-exr.pdq",
options=":fileformatname=exr")
Expand Down

0 comments on commit 32d4c25

Please sign in to comment.