add fast quality mode

This commit is contained in:
Attila Afra
2024-04-29 00:08:41 +03:00
parent 91624ba3d1
commit c079970a0f
15 changed files with 146 additions and 77 deletions
+9 -4
View File
@@ -3,14 +3,19 @@ Version History
### Changes in v2.3.0:
- Significantly improved image quality of the `RT` filter in high quality
- Significantly improved image quality of the `RT` filter in *high* quality
mode for the following combinations of input features and parameters:
- HDR color + albedo + normal + `cleanAux`
- albedo
- normal
In these cases a much more complex filter is used, which results in lower
performance than before (about 2x). To revert to the previous performance
behavior, please switch to the balanced quality mode.
behavior, please switch to the *balanced* quality mode.
- Added *fast* quality mode for even higher performance (about 1.5-2x)
interactive/real-time previews at the cost of somewhat lower image quality.
Currently this is implemented for the `RT` filter except auxiliary feature
denoising (albedo, normal). In other cases denoising implicitly falls back
to *balanced* mode.
- Execute `Async` functions asynchronously on CPU devices as well
- Load/initialize device modules lazily (improves stability)
- Added the `oidnIsDeviceSupported` API function for checking whether the
@@ -103,8 +108,8 @@ Version History
for querying memory allocations supported by the device
- Added `externalMemoryTypes` device parameter for querying the supported
external memory handle types
- Added `quality` filter parameter for setting the filtering quality mode (high
or balanced quality)
- Added `quality` filter parameter for setting the filtering quality mode (*high*
or *balanced* quality)
- Minor API changes with backward compatibility:
- Added `oidn(Get|Set)(Device|Filter)(Bool|Int|Float)` functions and
deprecated `oidn(Get|Set)(Device|Filter)(1b|1i|1f)` functions
+8
View File
@@ -57,14 +57,22 @@ if(OIDN_FILTER_RT)
weights/rt_alb.tza
weights/rt_alb_large.tza
weights/rt_hdr.tza
weights/rt_hdr_small.tza
weights/rt_hdr_alb.tza
weights/rt_hdr_alb_small.tza
weights/rt_hdr_alb_nrm.tza
weights/rt_hdr_alb_nrm_small.tza
weights/rt_hdr_calb_cnrm.tza
weights/rt_hdr_calb_cnrm_small.tza
weights/rt_hdr_calb_cnrm_large.tza
weights/rt_ldr.tza
weights/rt_ldr_small.tza
weights/rt_ldr_alb.tza
weights/rt_ldr_alb_small.tza
weights/rt_ldr_alb_nrm.tza
weights/rt_ldr_alb_nrm_small.tza
weights/rt_ldr_calb_cnrm.tza
weights/rt_ldr_calb_cnrm_small.tza
weights/rt_nrm.tza
weights/rt_nrm_large.tza
)
+4 -1
View File
@@ -36,7 +36,7 @@ void printUsage()
<< " [-r/--run regex] [-n times_to_run]" << std::endl
<< " [-s/--size width height]" << std::endl
<< " [-t/--type float|half]" << std::endl
<< " [-q/--quality default|h|high|b|balanced]" << std::endl
<< " [-q/--quality default|h|high|b|balanced|f|fast]" << std::endl
<< " [--threads n] [--affinity 0|1] [--maxmem MB] [--inplace]" << std::endl
<< " [--buffer host(copy)|device(copy)|managed(copy)]" << std::endl
<< " [-v/--verbose 0-3]" << std::endl
@@ -254,6 +254,7 @@ void addAllBenchmarks()
addBenchmark("RT", {"hdr", "alb", "nrm"}, size);
addBenchmark("RT", {"ldr", "alb", "nrm"}, size);
addBenchmark("RT", {"hdr", "calb", "cnrm"}, size);
addBenchmark("RT", {"ldr", "calb", "cnrm"}, size);
}
#endif
@@ -326,6 +327,8 @@ int main(int argc, char* argv[])
quality = Quality::High;
else if (val == "b" || val == "balanced")
quality = Quality::Balanced;
else if (val == "f" || val == "fast")
quality = Quality::Fast;
else
throw std::runtime_error("invalid filter quality mode");
}
+3 -1
View File
@@ -30,7 +30,7 @@ void printUsage()
<< " [-o/--output output.pfm]" << std::endl
<< " [-r/--ref reference_output.pfm] [--maxerror e]" << std::endl
<< " [-t/--type float|half]" << std::endl
<< " [-q/--quality default|h|high|b|balanced]" << std::endl
<< " [-q/--quality default|h|high|b|balanced|f|fast]" << std::endl
<< " [-w/--weights weights.tza]" << std::endl
<< " [--threads n] [--affinity 0|1] [--maxmem MB] [--inplace]" << std::endl
<< " [--buffer host|device|managed]" << std::endl
@@ -171,6 +171,8 @@ int main(int argc, char* argv[])
quality = Quality::High;
else if (val == "b" || val == "balanced")
quality = Quality::Balanced;
else if (val == "f" || val == "fast")
quality = Quality::Fast;
else
throw std::runtime_error("invalid filter quality mode");
}
+1
View File
@@ -89,6 +89,7 @@ OIDN_NAMESPACE_BEGIN
case Quality::Default: sm << "default"; break;
case Quality::High: sm << "high"; break;
case Quality::Balanced: sm << "balanced"; break;
case Quality::Fast: sm << "fast"; break;
default:
throw std::invalid_argument("invalid quality mode");
}
+1
View File
@@ -14,6 +14,7 @@ OIDN_NAMESPACE_BEGIN
size_t size;
Data() : ptr(nullptr), size(0) {}
Data(std::nullptr_t) : ptr(nullptr), size(0) {}
template<typename T>
Data(T* ptr, size_t size)
+31 -13
View File
@@ -6,14 +6,22 @@
// Default weights
#if defined(OIDN_FILTER_RT)
#include "weights/rt_hdr.h"
#include "weights/rt_hdr_small.h"
#include "weights/rt_hdr_alb.h"
#include "weights/rt_hdr_alb_small.h"
#include "weights/rt_hdr_alb_nrm.h"
#include "weights/rt_hdr_alb_nrm_small.h"
#include "weights/rt_hdr_calb_cnrm.h"
#include "weights/rt_hdr_calb_cnrm_small.h"
#include "weights/rt_hdr_calb_cnrm_large.h"
#include "weights/rt_ldr.h"
#include "weights/rt_ldr_small.h"
#include "weights/rt_ldr_alb.h"
#include "weights/rt_ldr_alb_small.h"
#include "weights/rt_ldr_alb_nrm.h"
#include "weights/rt_ldr_alb_nrm_small.h"
#include "weights/rt_ldr_calb_cnrm.h"
#include "weights/rt_ldr_calb_cnrm_small.h"
#include "weights/rt_alb.h"
#include "weights/rt_alb_large.h"
#include "weights/rt_nrm.h"
@@ -26,19 +34,29 @@ OIDN_NAMESPACE_BEGIN
: UNetFilter(device)
{
#if defined(OIDN_FILTER_RT)
weightsBlobs.hdr = blobs::weights::rt_hdr;
weightsBlobs.hdr_alb = blobs::weights::rt_hdr_alb;
weightsBlobs.hdr_alb_nrm = blobs::weights::rt_hdr_alb_nrm;
weightsBlobs.hdr_calb_cnrm = blobs::weights::rt_hdr_calb_cnrm;
weightsBlobs.hdr_calb_cnrm_large = blobs::weights::rt_hdr_calb_cnrm_large;
weightsBlobs.ldr = blobs::weights::rt_ldr;
weightsBlobs.ldr_alb = blobs::weights::rt_ldr_alb;
weightsBlobs.ldr_alb_nrm = blobs::weights::rt_ldr_alb_nrm;
weightsBlobs.ldr_calb_cnrm = blobs::weights::rt_ldr_calb_cnrm;
weightsBlobs.alb = blobs::weights::rt_alb;
weightsBlobs.alb_large = blobs::weights::rt_alb_large;
weightsBlobs.nrm = blobs::weights::rt_nrm;
weightsBlobs.nrm_large = blobs::weights::rt_nrm_large;
models.hdr = {blobs::weights::rt_hdr,
blobs::weights::rt_hdr_small};
models.hdr_alb = {blobs::weights::rt_hdr_alb,
blobs::weights::rt_hdr_alb_small};
models.hdr_alb_nrm = {blobs::weights::rt_hdr_alb_nrm,
blobs::weights::rt_hdr_alb_nrm_small};
models.hdr_calb_cnrm = {blobs::weights::rt_hdr_calb_cnrm,
blobs::weights::rt_hdr_calb_cnrm_small,
blobs::weights::rt_hdr_calb_cnrm_large};
models.ldr = {blobs::weights::rt_ldr,
blobs::weights::rt_ldr_small};
models.ldr_alb = {blobs::weights::rt_ldr_alb,
blobs::weights::rt_ldr_alb_small};
models.ldr_alb_nrm = {blobs::weights::rt_ldr_alb_nrm,
blobs::weights::rt_ldr_alb_nrm_small};
models.ldr_calb_cnrm = {blobs::weights::rt_ldr_calb_cnrm,
blobs::weights::rt_ldr_calb_cnrm_small};
models.alb = {blobs::weights::rt_alb,
nullptr,
blobs::weights::rt_alb_large};
models.nrm = {blobs::weights::rt_nrm,
nullptr,
blobs::weights::rt_nrm_large};
#endif
}
+2 -2
View File
@@ -16,8 +16,8 @@ OIDN_NAMESPACE_BEGIN
hdr = true;
#if defined(OIDN_FILTER_RTLIGHTMAP)
weightsBlobs.hdr = blobs::weights::rtlightmap_hdr;
weightsBlobs.dir = blobs::weights::rtlightmap_dir;
models.hdr = {blobs::weights::rtlightmap_hdr};
models.dir = {blobs::weights::rtlightmap_dir};
#endif
}
+31 -20
View File
@@ -47,7 +47,8 @@ OIDN_NAMESPACE_BEGIN
Quality qualityValue = static_cast<Quality>(value);
if (qualityValue == Quality::Default)
qualityValue = defaultQuality;
else if (qualityValue != Quality::High && qualityValue != Quality::Balanced)
else if (qualityValue != Quality::High && qualityValue != Quality::Balanced &&
qualityValue != Quality::Fast)
throw Exception(Error::InvalidArgument, "unknown filter quality mode");
setParam(quality, qualityValue);
}
@@ -396,33 +397,25 @@ OIDN_NAMESPACE_BEGIN
Data UNetFilter::getWeights()
{
// Select the weights to use
const bool hq = quality == Quality::High;
Data weightsBlob;
// Select the model to use
Model* model = nullptr;
if (color)
{
if (!albedo && !normal)
{
weightsBlob = directional ? weightsBlobs.dir : (hdr ? weightsBlobs.hdr : weightsBlobs.ldr);
model = directional ? &models.dir : (hdr ? &models.hdr : &models.ldr);
}
else if (albedo && !normal)
{
weightsBlob = hdr ? weightsBlobs.hdr_alb : weightsBlobs.ldr_alb;
model = hdr ? &models.hdr_alb : &models.ldr_alb;
}
else if (albedo && normal)
{
if (cleanAux)
{
if (hdr)
weightsBlob = hq ? weightsBlobs.hdr_calb_cnrm_large : weightsBlobs.hdr_calb_cnrm;
else
weightsBlob = weightsBlobs.ldr_calb_cnrm;
}
model = hdr ? &models.hdr_calb_cnrm : &models.ldr_calb_cnrm;
else
{
weightsBlob = hdr ? weightsBlobs.hdr_alb_nrm : weightsBlobs.ldr_alb_nrm;
}
model = hdr ? &models.hdr_alb_nrm : &models.ldr_alb_nrm;
}
}
else
@@ -432,13 +425,13 @@ OIDN_NAMESPACE_BEGIN
{
if (hdr)
throw Exception(Error::InvalidOperation, "hdr mode is not supported for albedo filtering");
weightsBlob = hq ? weightsBlobs.alb_large : weightsBlobs.alb;
model = &models.alb;
}
else if (!albedo && normal)
{
if (hdr || srgb)
throw Exception(Error::InvalidOperation, "hdr and srgb modes are not supported for normal filtering");
weightsBlob = hq ? weightsBlobs.nrm_large : weightsBlobs.nrm;
model = &models.nrm;
}
else
{
@@ -446,8 +439,29 @@ OIDN_NAMESPACE_BEGIN
}
}
// Select the weights to use
Data weightsBlob = nullptr;
if (userWeightsBlob)
{
weightsBlob = userWeightsBlob;
}
else
{
switch (quality)
{
case Quality::Default:
case Quality::High:
weightsBlob = model->large ? model->large : model->base;
break;
case Quality::Balanced:
weightsBlob = model->base;
break;
case Quality::Fast:
weightsBlob = model->small ? model->small : model->base;
break;
}
}
if (!weightsBlob)
throw Exception(Error::InvalidOperation, "unsupported combination of input features");
@@ -637,10 +651,7 @@ OIDN_NAMESPACE_BEGIN
// Print statistics
if (device->isVerbose(2))
{
std::cout << "Model: " << (largeModel ? "large" : "base") << std::endl;
std::cout << "Memory usage: " << totalMemoryByteSize << std::endl;
}
return true;
}
+26 -14
View File
@@ -35,7 +35,7 @@ OIDN_NAMESPACE_BEGIN
static constexpr int receptiveFieldBase = 174; // receptive field in pixels for UNet
static constexpr int receptiveFieldLarge = 202; // receptive field in pixels for UNetLarge
static constexpr int minTileAlignment = 16; // required spatial alignment in pixels (padding may be necessary)
static constexpr int defaultMaxTileSize = 2160*2160; // default maximum number of pixels per tile
// Images
@@ -55,21 +55,33 @@ OIDN_NAMESPACE_BEGIN
int maxMemoryMB = -1; // maximum memory usage limit in MBs, disabled if < 0
int prevMaxMemoryMB = -1; // maximum memory usage limit in MBs from the previous commit
// Weights
struct Model
{
// Weights blobs
Data base;
Data small;
Data large;
Model() = default;
Model(const Data& base, const Data& small = nullptr, const Data& large = nullptr)
: base(base), small(small), large(large) {}
};
struct
{
Data hdr;
Data hdr_alb;
Data hdr_alb_nrm;
Data hdr_calb_cnrm, hdr_calb_cnrm_large;
Data ldr;
Data ldr_alb;
Data ldr_alb_nrm;
Data ldr_calb_cnrm;
Data dir;
Data alb, alb_large;
Data nrm, nrm_large;
} weightsBlobs;
Model hdr;
Model hdr_alb;
Model hdr_alb_nrm;
Model hdr_calb_cnrm;
Model ldr;
Model ldr_alb;
Model ldr_alb_nrm;
Model ldr_calb_cnrm;
Model dir;
Model alb;
Model nrm;
} models; // built-in weights blobs
Data userWeightsBlob;
private:
+14 -10
View File
@@ -288,10 +288,11 @@ Open Image Denoise still delivers the same high image quality on all device
types as before, including on GPUs. But often filtering performance is more
important than having the highest possible image quality, so it is now possible
to switch between multiple filter quality modes. Filters have a new
parameter called `quality`, which defaults to the existing high image quality
(`OIDN_QUALITY_HIGH`) but a balanced quality mode (`OIDN_QUALITY_BALANCED`)
has been added as well for even higher performance. We recommend using balanced
quality for interactive and real-time use cases.
parameter called `quality`, which defaults to the existing *high* image quality
(`OIDN_QUALITY_HIGH`) but *balanced* (`OIDN_QUALITY_BALANCED`) and *fast*
(`OIDN_QUALITY_FAST`) quality modes have been added as well for even higher
performance. We recommend using *balanced* or *fast* quality for interactive and
real-time use cases.
### Small API Changes
@@ -1224,21 +1225,24 @@ supported quality modes are listed in the following table.
Name Description
------------------------ ---------------------------------------------------------------------------
`OIDN_QUALITY_DEFAULT` default quality
`OIDN_QUALITY_FAST` high performance (for interactive/real-time preview rendering)
`OIDN_QUALITY_BALANCED` balanced quality/performance (for interactive/real-time rendering)
`OIDN_QUALITY_HIGH` high quality (for final-frame rendering); *default*
------------------------ ---------------------------------------------------------------------------
: Supported image quality modes, i.e., valid constants of type `OIDNQuality`.
By default, filtering is performed in high quality mode, which is recommended for
final-frame rendering. Using this setting the results have the same high quality
regardless of what kind of device (CPU or GPU) is used. However, due to
By default, filtering is performed in *high* quality mode, which is recommended
for final-frame rendering. Using this setting the results have the same high
quality regardless of what kind of device (CPU or GPU) is used. However, due to
significant hardware architecture differences between devices, there might be
small numerical differences between the produced outputs.
The balanced quality mode may provide somewhat lower image quality but higher
The *balanced* quality mode may provide somewhat lower image quality but higher
performance, and is thus recommended for interactive and real-time rendering.
Note that larger numerical differences should be expected across devices
compared to the high quality mode.
For even higher performance, the *fast* quality mode can be used but at the cost
of noticeably lower image quality, making it suitable for fast previews. Note
that in the *balanced* and *fast* quality modes larger numerical differences
should be expected across devices compared to the *high* quality mode.
The difference in quality and performance between quality modes depends on the
combination of input features, parameters (e.g. `cleanAux`), and the device
+1 -1
View File
@@ -358,7 +358,7 @@ typedef enum
{
OIDN_QUALITY_DEFAULT = 0, // default quality
//OIDN_QUALITY_FAST = 4
OIDN_QUALITY_FAST = 4, // high performance (for interactive/real-time preview rendering)
OIDN_QUALITY_BALANCED = 5, // balanced quality/performance (for interactive/real-time rendering)
OIDN_QUALITY_HIGH = 6, // high quality (for final-frame rendering)
} OIDNQuality;
+1
View File
@@ -323,6 +323,7 @@ OIDN_NAMESPACE_BEGIN
{
Default = OIDN_QUALITY_DEFAULT, // default quality
Fast = OIDN_QUALITY_FAST, // high performance (for interactive/real-time preview rendering)
Balanced = OIDN_QUALITY_BALANCED, // balanced quality/performance (for interactive/real-time rendering)
High = OIDN_QUALITY_HIGH, // high quality (for final-frame rendering)
};
+13 -10
View File
@@ -223,8 +223,11 @@ def test_regression(filter, feature_sets, dataset):
image_names = [os.path.relpath(filename, baseline_dir).rsplit('.', 3)[0] for filename in input_filenames]
# Iterate over quality
for quality in (['high', 'balanced'] if (filter == 'RT' and not cfg.minimal) or cfg.full else ['high']):
result = result_base + ('_large' if quality == 'high' and 'large' in model_sizes else '')
for quality in (['high', 'balanced', 'fast'] if (filter == 'RT' and not cfg.minimal) or cfg.full else ['high']):
model_size = {'high' : 'large', 'balanced' : 'base', 'fast' : 'small'}[quality]
result = result_base
if model_size != 'base' and model_size in model_sizes:
result += '_' + model_size
# Iterate over images
image_index = 0
@@ -315,14 +318,14 @@ if not cfg.filter or 'RT' in cfg.filter:
test_regression(
'RT',
[
(['hdr', 'alb', 'nrm'], True, ['base']),
(['hdr', 'alb'], False, ['base']),
(['hdr'], True, ['base']),
(['hdr', 'calb', 'cnrm'], True, ['base', 'large']),
(['ldr', 'alb', 'nrm'], False, ['base']),
(['ldr', 'alb'], False, ['base']),
(['ldr'], True, ['base']),
(['ldr', 'calb', 'cnrm'], False, ['base']),
(['hdr', 'alb', 'nrm'], True, ['base', 'small']),
(['hdr', 'alb'], False, ['base', 'small']),
(['hdr'], True, ['base', 'small']),
(['hdr', 'calb', 'cnrm'], True, ['base', 'small', 'large']),
(['ldr', 'alb', 'nrm'], False, ['base', 'small']),
(['ldr', 'alb'], False, ['base', 'small']),
(['ldr'], True, ['base', 'small']),
(['ldr', 'calb', 'cnrm'], False, ['base', 'small']),
(['alb'], True, ['base', 'large']),
(['nrm'], True, ['base', 'large'])
],
+1 -1
Submodule weights updated: a90e03e9e2...28883d1769