Files
filament/third_party/OpenImageDenoise/examples/denoise.cpp
2019-05-28 11:43:50 -07:00

291 lines
8.7 KiB
C++

// ======================================================================== //
// Copyright 2009-2019 Intel Corporation //
// //
// Licensed under the Apache License, Version 2.0 (the "License"); //
// you may not use this file except in compliance with the License. //
// You may obtain a copy of the License at //
// //
// http://www.apache.org/licenses/LICENSE-2.0 //
// //
// Unless required by applicable law or agreed to in writing, software //
// distributed under the License is distributed on an "AS IS" BASIS, //
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. //
// See the License for the specific language governing permissions and //
// limitations under the License. //
// ======================================================================== //
#include <iostream>
#include <cassert>
#include <cmath>
#include <signal.h>
#ifdef VTUNE
#include <ittnotify.h>
#endif
#include <OpenImageDenoise/oidn.hpp>
#include "common/timer.h"
#include "image_io.h"
#include "cli.h"
using namespace oidn;
void printUsage()
{
std::cout << "Open Image Denoise Example" << std::endl;
std::cout << "Usage: denoise [-ldr ldr_color.pfm] [-srgb] [-hdr hdr_color.pfm]" << std::endl
<< " [-alb albedo.pfm] [-nrm normal.pfm]" << std::endl
<< " [-o output.pfm] [-ref reference_output.pfm]" << std::endl
<< " [-bench ntimes] [-threads n] [-affinity 0|1]" << std::endl;
}
void errorCallback(void* userPtr, oidn::Error error, const char* message)
{
throw std::runtime_error(message);
}
volatile bool isCancelled = false;
void signalHandler(int signal)
{
isCancelled = true;
}
bool progressCallback(void* userPtr, double n)
{
if (isCancelled)
return false;
std::cout << "\rDenoising " << int(n * 100.) << "%" << std::flush;
return true;
}
int main(int argc, char* argv[])
{
std::string colorFilename, albedoFilename, normalFilename;
std::string outputFilename, refFilename;
bool hdr = false;
bool srgb = false;
int numBenchmarkRuns = 0;
int numThreads = -1;
int setAffinity = -1;
// Parse the arguments
if (argc == 1)
{
printUsage();
return 1;
}
try
{
ArgParser args(argc, argv);
while (args.hasNext())
{
std::string opt = args.getNextOpt();
if (opt == "ldr")
{
colorFilename = args.getNextValue();
hdr = false;
}
else if (opt == "hdr")
{
colorFilename = args.getNextValue();
hdr = true;
}
else if (opt == "srgb")
srgb = true;
else if (opt == "alb" || opt == "albedo")
albedoFilename = args.getNextValue();
else if (opt == "nrm" || opt == "normal")
normalFilename = args.getNextValue();
else if (opt == "o" || opt == "out" || opt == "output")
outputFilename = args.getNextValue();
else if (opt == "ref" || opt == "reference")
refFilename = args.getNextValue();
else if (opt == "bench" || opt == "benchmark")
numBenchmarkRuns = std::max(args.getNextValueInt(), 0);
else if (opt == "threads")
numThreads = args.getNextValueInt();
else if (opt == "affinity")
setAffinity = args.getNextValueInt();
else if (opt == "h" || opt == "help")
{
printUsage();
return 1;
}
else
throw std::invalid_argument("invalid argument");
}
if (colorFilename.empty())
throw std::runtime_error("no color image specified");
// Load the input image
ImageBuffer color, albedo, normal;
ImageBuffer ref;
std::cout << "Loading input" << std::flush;
color = loadImage(colorFilename);
if (color.getChannels() != 3)
throw std::runtime_error("invalid color image");
if (!albedoFilename.empty())
{
albedo = loadImage(albedoFilename);
if (albedo.getChannels() != 3 || albedo.getSize() != color.getSize())
throw std::runtime_error("invalid albedo image");
}
if (!normalFilename.empty())
{
normal = loadImage(normalFilename);
if (normal.getChannels() != 3 || normal.getSize() != color.getSize())
throw std::runtime_error("invalid normal image");
}
if (!refFilename.empty())
{
ref = loadImage(refFilename);
if (ref.getChannels() != 3 || ref.getSize() != color.getSize())
throw std::runtime_error("invalid reference output image");
}
const int width = color.getWidth();
const int height = color.getHeight();
std::cout << std::endl << "Resolution: " << width << "x" << height << std::endl;
// Initialize the output image
ImageBuffer output(width, height, 3);
// Initialize the denoising filter
std::cout << "Initializing" << std::flush;
Timer timer;
oidn::DeviceRef device = oidn::newDevice();
const char* errorMessage;
if (device.getError(errorMessage) != oidn::Error::None)
throw std::runtime_error(errorMessage);
device.setErrorFunction(errorCallback);
if (numThreads > 0)
device.set("numThreads", numThreads);
if (setAffinity >= 0)
device.set("setAffinity", bool(setAffinity));
device.commit();
oidn::FilterRef filter = device.newFilter("RT");
filter.setImage("color", color.getData(), oidn::Format::Float3, width, height);
if (albedo)
filter.setImage("albedo", albedo.getData(), oidn::Format::Float3, width, height);
if (normal)
filter.setImage("normal", normal.getData(), oidn::Format::Float3, width, height);
filter.setImage("output", output.getData(), oidn::Format::Float3, width, height);
if (hdr)
filter.set("hdr", true);
if (srgb)
filter.set("srgb", true);
filter.setProgressMonitorFunction(progressCallback);
signal(SIGINT, signalHandler);
filter.commit();
const double initTime = timer.query();
const int versionMajor = device.get<int>("versionMajor");
const int versionMinor = device.get<int>("versionMinor");
const int versionPatch = device.get<int>("versionPatch");
std::cout << ": version=" << versionMajor << "." << versionMinor << "." << versionPatch
<< ", msec=" << (1000. * initTime) << std::endl;
// Denoise the image
//std::cout << "Denoising" << std::flush;
timer.reset();
filter.execute();
const double denoiseTime = timer.query();
std::cout << ": msec=" << (1000. * denoiseTime) << std::endl;
filter.setProgressMonitorFunction(nullptr);
signal(SIGINT, SIG_DFL);
if (ref)
{
// Verify the output values
int nerr = 0;
float maxre = 0;
for (size_t i = 0; i < output.getDataSize(); ++i)
{
float expect = std::max(ref[i], 0.f);
if (!hdr)
expect = std::min(expect, 1.f);
const float actual = output[i];
float re;
if (std::abs(expect) < 1e-5 && std::abs(actual) < 1e-5)
re = 0;
else if (expect != 0)
re = std::abs((expect - actual) / expect);
else
re = std::abs(expect - actual);
if (maxre < re) maxre = re;
if (re > 1e-3)
{
//std::cout << "i=" << i << " expect=" << expect << " actual=" << actual << std::endl;
++nerr;
}
}
std::cout << "Verified output: nfloats=" << output.getDataSize() << ", nerr=" << nerr << ", maxre=" << maxre << std::endl;
// Save debug images
std::cout << "Saving debug images" << std::flush;
saveImage("denoise_in.ppm", color);
saveImage("denoise_out.ppm", output);
saveImage("denoise_ref.ppm", ref);
std::cout << std::endl;
}
if (!outputFilename.empty())
{
// Save output image
std::cout << "Saving output" << std::flush;
saveImage(outputFilename, output);
std::cout << std::endl;
}
if (numBenchmarkRuns > 0)
{
// Benchmark loop
#ifdef VTUNE
__itt_resume();
#endif
std::cout << "Benchmarking: " << "ntimes=" << numBenchmarkRuns << std::flush;
timer.reset();
for (int i = 0; i < numBenchmarkRuns; ++i)
filter.execute();
const double totalTime = timer.query();
std::cout << ", sec=" << totalTime << ", msec/image=" << (1000.*totalTime / numBenchmarkRuns) << std::endl;
#ifdef VTUNE
__itt_pause();
#endif
}
}
catch (std::exception& e)
{
std::cout << std::endl << "Error: " << e.what() << std::endl;
return 1;
}
return 0;
}