Skip to content

Commit

Permalink
feat(oiiotool): Allow decision making about fixnan (#4171)
Browse files Browse the repository at this point in the history
Added a new bit of pseudo-metadata that can be retrieved by oiiotool's
expression substitution: `{TOP.NONFINITE_COUNT}` will be replaced by the
count of inf and nan values present in the top image.

Also, the `-fixnan` command now has the side effect of setting a user
variable called `NONFINITE_COUNT` to the number of nonfinite values that
were detected or repaired.

Between the two of these, it's now easy to solve the following problem
that actually came up in production for us.

Sometimes, nans creep into images, and it's easier to repair them than
to fix the complicated process that produced them in the first place.
For some process in the studio, we had a script that was doing this
preemptively on every image it encountered:

    oiiotool image.exr -fixnan black -o image.exr

It was very expensive to repair every image whether it needed it or not,
when actually it's only needed once every
many-thousands-of-images. Most of the expense was in outputting the
repaired file overtop the original. Now we can do this

oiiotool image.exr -if "{TOP.NONFINITE_COUNT}" -fixnan black -o
image.exr -endif

It still has to read the image and look for NaN values, but since there
are almost never any actual NaN's, now for the usual case, it can skip
writing the output -- which was the vast majority of time.

Signed-off-by: Larry Gritz <[email protected]>
  • Loading branch information
lgritz committed Mar 5, 2024
1 parent c96dfd8 commit 7d2ef33
Show file tree
Hide file tree
Showing 2 changed files with 19 additions and 5 deletions.
2 changes: 2 additions & 0 deletions src/doc/oiiotool.rst
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,8 @@ contents of an expression may be any of:
(channels are comma-separated)
* `AVGCOLOR` : the average pixel value of the image (channels are
comma-separated)
* `NONFINITE_COUNT` : the number of pixel values in the image that are
either NaN or Inf values. (Added in OIIO 2.5.10.)
* `META` : a multi-line string containing the full metadata of the image,
similar to what would be printed with `oiiotool -info -v`.
* `METABRIEF` : a string containing the brief one-line description,
Expand Down
22 changes: 17 additions & 5 deletions src/oiiotool/oiiotool.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
#include <iostream>
#include <iterator>
#include <map>
#include <numeric>
#include <regex>
#include <sstream>
#include <string>
Expand Down Expand Up @@ -1941,6 +1942,13 @@ Oiiotool::express_parse_atom(const string_view expr, string_view& s,
for (size_t i = 0; i < pixstat.avg.size(); ++i)
out << (i ? "," : "") << pixstat.avg[i];
result = out.str();
} else if (metadata == "NONFINITE_COUNT") {
auto pixstat = ImageBufAlgo::computePixelStats((*img)(0, 0));
imagesize_t sum = std::accumulate(pixstat.nancount.begin(),
pixstat.nancount.end(), 0)
+ std::accumulate(pixstat.infcount.begin(),
pixstat.infcount.end(), 0);
result = Strutil::to_string(sum);
} else if (metadata == "META" || metadata == "METANATIVE") {
std::stringstream out;
print_info_options opt;
Expand Down Expand Up @@ -4731,19 +4739,23 @@ action_fixnan(Oiiotool& ot, cspan<const char*> argv)
ImageRecRef A = ot.pop();
ot.push(new ImageRec(*A, allsubimages ? -1 : 0, allsubimages ? -1 : 0, true,
false));
int subimages = allsubimages ? A->subimages() : 1;
imagesize_t total_nonfinite = 0;
int subimages = allsubimages ? A->subimages() : 1;
for (int s = 0; s < subimages; ++s) {
int miplevels = ot.curimg->miplevels(s);
for (int m = 0; m < miplevels; ++m) {
const ImageBuf& Aib((*A)(s, m));
ImageBuf& Rib((*ot.curimg)(s, m));
bool ok = ImageBufAlgo::fixNonFinite(Rib, Aib, mode);
if (!ok) {
int num_nonfinite = 0;
bool ok = ImageBufAlgo::fixNonFinite(Rib, Aib, mode,
&num_nonfinite);
if (!ok)
ot.error(command, Rib.geterror());
return;
}
total_nonfinite += num_nonfinite;
}
}
// Set user variable NONFINITE_COUNT to the number of pixels modified.
ot.uservars["NONFINITE_COUNT"] = int(total_nonfinite);
}


Expand Down

0 comments on commit 7d2ef33

Please sign in to comment.