Blender V4.5
denoising.cpp
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
2 *
3 * SPDX-License-Identifier: Apache-2.0 */
4
5#include "session/denoising.h"
6#include "device/cpu/device.h"
7
9#include "util/map.h"
10#include "util/task.h"
11
12#include <OpenImageIO/filesystem.h>
13
15
16/* Utility Functions */
17
18/* Splits in at its last dot, setting suffix to the part after the dot and in to the part before
19 * it. Returns whether a dot was found. */
20static bool split_last_dot(string &in, string &suffix)
21{
22 const size_t pos = in.rfind(".");
23 if (pos == string::npos) {
24 return false;
25 }
26 suffix = in.substr(pos + 1);
27 in = in.substr(0, pos);
28 return true;
29}
30
31/* Separate channel names as generated by Blender.
32 * If views is true:
33 * Inputs are expected in the form RenderLayer.Pass.View.Channel, sets renderlayer to
34 * "RenderLayer.View" Otherwise: Inputs are expected in the form RenderLayer.Pass.Channel */
36 string name, string &renderlayer, string &pass, string &channel, bool multiview_channels)
37{
38 if (!split_last_dot(name, channel)) {
39 return false;
40 }
41 string view;
42 if (multiview_channels && !split_last_dot(name, view)) {
43 return false;
44 }
45 if (!split_last_dot(name, pass)) {
46 return false;
47 }
48 renderlayer = name;
49
50 if (multiview_channels) {
51 renderlayer += "." + view;
52 }
53
54 return true;
55}
56
57/* Channel Mapping */
58
61 string name;
62};
63
64static void fill_mapping(vector<ChannelMapping> &map, int pos, string name, string channels)
65{
66 for (const char *chan = channels.c_str(); *chan; chan++) {
67 map.push_back({pos++, name + "." + *chan});
68 }
69}
70
71static const int INPUT_NUM_CHANNELS = 13;
72static const int INPUT_NOISY_IMAGE = 0;
73static const int INPUT_DENOISING_NORMAL = 3;
74static const int INPUT_DENOISING_ALBEDO = 6;
75static const int INPUT_MOTION = 9;
77{
79 fill_mapping(map, INPUT_NOISY_IMAGE, "Combined", "RGB");
80 fill_mapping(map, INPUT_DENOISING_NORMAL, "Denoising Normal", "XYZ");
81 fill_mapping(map, INPUT_DENOISING_ALBEDO, "Denoising Albedo", "RGB");
82 fill_mapping(map, INPUT_MOTION, "Vector", "XYZW");
83 return map;
84}
85
86static const int OUTPUT_NUM_CHANNELS = 3;
88{
90 fill_mapping(map, 0, "Combined", "RGB");
91 return map;
92}
93
94/* Render-layer Handling. */
95
97{
98 /* Map device input to image channels. */
101
102 for (const ChannelMapping &mapping : input_channels()) {
103 const vector<string>::iterator i = find(channels.begin(), channels.end(), mapping.name);
104 if (i == channels.end()) {
105 return false;
106 }
107
108 const size_t input_channel = mapping.channel;
109 const size_t layer_channel = i - channels.begin();
110 input_to_image_channel[input_channel] = layer_to_image_channel[layer_channel];
111 }
112
113 /* Map device output to image channels. */
116
117 for (const ChannelMapping &mapping : output_channels()) {
118 const vector<string>::iterator i = find(channels.begin(), channels.end(), mapping.name);
119 if (i == channels.end()) {
120 return false;
121 }
122
123 const size_t output_channel = mapping.channel;
124 const size_t layer_channel = i - channels.begin();
125 output_to_image_channel[output_channel] = layer_to_image_channel[layer_channel];
126 }
127
128 /* Check that all buffer channels are correctly set. */
129 for (int i = 0; i < INPUT_NUM_CHANNELS; i++) {
131 }
132 for (int i = 0; i < OUTPUT_NUM_CHANNELS; i++) {
134 }
135
136 return true;
137}
138
139bool DenoiseImageLayer::match_channels(const std::vector<string> &channelnames,
140 const std::vector<string> &neighbor_channelnames)
141{
143
144 assert(mapping.empty());
145 mapping.resize(output_to_image_channel.size(), -1);
146
147 for (int i = 0; i < output_to_image_channel.size(); i++) {
148 const string &channel = channelnames[output_to_image_channel[i]];
149 const std::vector<string>::const_iterator frame_channel = find(
150 neighbor_channelnames.begin(), neighbor_channelnames.end(), channel);
151
152 if (frame_channel == neighbor_channelnames.end()) {
153 return false;
154 }
155
156 mapping[i] = frame_channel - neighbor_channelnames.begin();
157 }
158
159 return true;
160}
161
162/* Denoise Task */
163
168
173
174/* Denoiser Operations */
175
177{
178 /* Load center image */
179 const DenoiseImageLayer &image_layer = image.layers[layer];
180
181 float *buffer_data = buffers.buffer.data();
182 image.read_pixels(image_layer, buffers.params, buffer_data);
183
184 /* Load previous image */
185 if (frame > 0 && !image.read_previous_pixels(image_layer, buffers.params, buffer_data)) {
186 error = "Failed to read neighbor frame pixels";
187 return false;
188 }
189
190 /* Copy to device */
191 buffers.buffer.copy_to_device();
192
193 return true;
194}
195
196/* Task stages */
197
199 PassType type,
201{
202 unique_ptr<Pass> pass = make_unique<Pass>();
203 pass->set_type(type);
204 pass->set_mode(mode);
205
206 passes.push_back(std::move(pass));
207}
208
210{
211 const string center_filepath = denoiser->input[frame];
212 if (!image.load(center_filepath, error)) {
213 return false;
214 }
215
216 /* Use previous frame output as input for subsequent frames. */
217 if (frame > 0 && !image.load_previous(denoiser->output[frame - 1], error)) {
218 return false;
219 }
220
221 if (image.layers.empty()) {
222 error = "No image layers found to denoise in " + center_filepath;
223 return false;
224 }
225
226 /* Enable temporal denoising for frames after the first (which will use the output from the
227 * previous frames). */
228 DenoiseParams params = denoiser->denoiser->get_params();
229 params.temporally_stable = frame > 0;
230 denoiser->denoiser->set_params(params);
231
232 /* Allocate device buffer. */
240
241 BufferParams buffer_params;
242 buffer_params.width = image.width;
243 buffer_params.height = image.height;
244 buffer_params.full_x = 0;
245 buffer_params.full_y = 0;
246 buffer_params.full_width = image.width;
247 buffer_params.full_height = image.height;
248 buffer_params.update_passes(passes);
249
250 passes.clear();
251
252 buffers.reset(buffer_params);
253
254 /* Read pixels for first layer. */
255 current_layer = 0;
257 return false;
258 }
259
260 return true;
261}
262
264{
265 for (current_layer = 0; current_layer < image.layers.size(); current_layer++) {
266 /* Read pixels for secondary layers, first was already loaded. */
267 if (current_layer > 0) {
269 return false;
270 }
271 }
272
273 /* Run task on device. */
274 denoiser->denoiser->denoise_buffer(buffers.params, &buffers, 1, true);
275
276 /* Copy denoised pixels from device. */
277 buffers.buffer.copy_from_device();
278
279 float *result = buffers.buffer.data();
280 float *out = image.pixels.data();
281
282 const DenoiseImageLayer &layer = image.layers[current_layer];
283 const int *output_to_image_channel = layer.output_to_image_channel.data();
284
285 for (int y = 0; y < image.height; y++) {
286 for (int x = 0; x < image.width; x++, result += buffers.params.pass_stride) {
287 for (int j = 0; j < OUTPUT_NUM_CHANNELS; j++) {
288 const int offset = buffers.params.get_pass_offset(PASS_COMBINED, PassMode::DENOISED);
289 const int image_channel = output_to_image_channel[j];
290 out[image.num_channels * x + image_channel] = result[offset + j];
291 }
292 }
293 out += image.num_channels * image.width;
294 }
295
296 printf("\n");
297 }
298
299 return true;
300}
301
303{
304 const bool ok = image.save_output(denoiser->output[frame], error);
305 free();
306 return ok;
307}
308
310{
311 image.free();
312 buffers.buffer.free();
313}
314
315/* Denoise Image Storage */
316
318{
319 width = 0;
320 height = 0;
321 num_channels = 0;
322 samples = 0;
323}
324
329
331{
332 in_previous.reset();
333}
334
336{
337 close_input();
338 pixels.clear();
339}
340
341bool DenoiseImage::parse_channels(const ImageSpec &in_spec, string &error)
342{
343 const std::vector<string> &channels = in_spec.channelnames;
344 const ParamValue *multiview = in_spec.find_attribute("multiView");
345 const bool multiview_channels = (multiview && multiview->type().basetype == TypeDesc::STRING &&
346 multiview->type().arraylen >= 2);
347
348 layers.clear();
349
350 /* Loop over all the channels in the file, parse their name and sort them
351 * by RenderLayer.
352 * Channels that can't be parsed are directly passed through to the output. */
353 map<string, DenoiseImageLayer> file_layers;
354 for (int i = 0; i < channels.size(); i++) {
355 string layer;
356 string pass;
357 string channel;
358 if (parse_channel_name(channels[i], layer, pass, channel, multiview_channels)) {
359 file_layers[layer].channels.push_back(pass + "." + channel);
360 file_layers[layer].layer_to_image_channel.push_back(i);
361 }
362 }
363
364 /* Loop over all detected RenderLayers, check whether they contain a full set of input channels.
365 * Any channels that won't be processed internally are also passed through. */
366 for (map<string, DenoiseImageLayer>::iterator i = file_layers.begin(); i != file_layers.end();
367 ++i)
368 {
369 const string &name = i->first;
370 DenoiseImageLayer &layer = i->second;
371
372 /* Check for full pass set. */
373 if (!layer.detect_denoising_channels()) {
374 continue;
375 }
376
377 layer.name = name;
378 layer.samples = samples;
379
380 /* If the sample value isn't set yet, check if there is a layer-specific one in the input file.
381 */
382 if (layer.samples < 1) {
383 const string sample_string = in_spec.get_string_attribute("cycles." + name + ".samples", "");
384 if (!sample_string.empty()) {
385 if (!sscanf(sample_string.c_str(), "%d", &layer.samples)) {
386 error = "Failed to parse samples metadata: " + sample_string;
387 return false;
388 }
389 }
390 }
391
392 if (layer.samples < 1) {
394 "No sample number specified in the file for layer %s or on the command line",
395 name.c_str());
396 return false;
397 }
398
399 layers.push_back(layer);
400 }
401
402 return true;
403}
404
406 const BufferParams &params,
407 float *input_pixels)
408{
409 /* Pixels from center file have already been loaded into pixels.
410 * We copy a subset into the device input buffer with channels reshuffled. */
411 const int *input_to_image_channel = layer.input_to_image_channel.data();
412
413 for (int i = 0; i < width * height; i++) {
414 for (int j = 0; j < 3; ++j) {
415 const int offset = params.get_pass_offset(PASS_COMBINED);
416 const int image_channel = input_to_image_channel[INPUT_NOISY_IMAGE + j];
417 input_pixels[i * params.pass_stride + offset + j] =
418 pixels[((size_t)i) * num_channels + image_channel];
419 }
420 for (int j = 0; j < 3; ++j) {
421 const int offset = params.get_pass_offset(PASS_DENOISING_NORMAL);
422 const int image_channel = input_to_image_channel[INPUT_DENOISING_NORMAL + j];
423 input_pixels[i * params.pass_stride + offset + j] =
424 pixels[((size_t)i) * num_channels + image_channel];
425 }
426 for (int j = 0; j < 3; ++j) {
427 const int offset = params.get_pass_offset(PASS_DENOISING_ALBEDO);
428 const int image_channel = input_to_image_channel[INPUT_DENOISING_ALBEDO + j];
429 input_pixels[i * params.pass_stride + offset + j] =
430 pixels[((size_t)i) * num_channels + image_channel];
431 }
432 for (int j = 0; j < 4; ++j) {
433 const int offset = params.get_pass_offset(PASS_MOTION);
434 const int image_channel = input_to_image_channel[INPUT_MOTION + j];
435 input_pixels[i * params.pass_stride + offset + j] =
436 pixels[((size_t)i) * num_channels + image_channel];
437 }
438 }
439}
440
442 const BufferParams &params,
443 float *input_pixels)
444{
445 /* Load pixels from neighboring frames, and copy them into device buffer
446 * with channels reshuffled. */
447 const size_t num_pixels = (size_t)width * (size_t)height;
448 const int num_channels = in_previous->spec().nchannels;
449
450 array<float> neighbor_pixels(num_pixels * num_channels);
451
452 if (!in_previous->read_image(0, 0, 0, num_channels, TypeDesc::FLOAT, neighbor_pixels.data())) {
453 return false;
454 }
455
456 const int *output_to_image_channel = layer.previous_output_to_image_channel.data();
457
458 for (int i = 0; i < width * height; i++) {
459 for (int j = 0; j < 3; ++j) {
460 const int offset = params.get_pass_offset(PASS_DENOISING_PREVIOUS);
461 const int image_channel = output_to_image_channel[j];
462 input_pixels[i * params.pass_stride + offset + j] =
463 neighbor_pixels[((size_t)i) * num_channels + image_channel];
464 }
465 }
466
467 return true;
468}
469
470bool DenoiseImage::load(const string &in_filepath, string &error)
471{
472 if (!Filesystem::is_regular(in_filepath)) {
473 error = "Couldn't find file: " + in_filepath;
474 return false;
475 }
476
477 unique_ptr<ImageInput> in(ImageInput::open(in_filepath));
478 if (!in) {
479 error = "Couldn't open file: " + in_filepath;
480 return false;
481 }
482
483 in_spec = in->spec();
484 width = in_spec.width;
485 height = in_spec.height;
486 num_channels = in_spec.nchannels;
487
489 return false;
490 }
491
492 if (layers.empty()) {
493 error = "Could not find a render layer containing denoising data and motion vector passes";
494 return false;
495 }
496
497 const size_t num_pixels = (size_t)width * (size_t)height;
498 pixels.resize(num_pixels * num_channels);
499
500 /* Read all channels into buffer. Reading all channels at once is faster
501 * than individually due to interleaved EXR channel storage. */
502 if (!in->read_image(0, 0, 0, num_channels, TypeDesc::FLOAT, pixels.data())) {
503 error = "Failed to read image: " + in_filepath;
504 return false;
505 }
506
507 return true;
508}
509
510bool DenoiseImage::load_previous(const string &filepath, string &error)
511{
512 if (!Filesystem::is_regular(filepath)) {
513 error = "Couldn't find neighbor frame: " + filepath;
514 return false;
515 }
516
517 unique_ptr<ImageInput> in_neighbor(ImageInput::open(filepath));
518 if (!in_neighbor) {
519 error = "Couldn't open neighbor frame: " + filepath;
520 return false;
521 }
522
523 const ImageSpec &neighbor_spec = in_neighbor->spec();
524 if (neighbor_spec.width != width || neighbor_spec.height != height) {
525 error = "Neighbor frame has different dimensions: " + filepath;
526 return false;
527 }
528
529 for (DenoiseImageLayer &layer : layers) {
530 if (!layer.match_channels(in_spec.channelnames, neighbor_spec.channelnames)) {
531 error = "Neighbor frame misses denoising data passes: " + filepath;
532 return false;
533 }
534 }
535
536 in_previous = std::move(in_neighbor);
537
538 return true;
539}
540
541bool DenoiseImage::save_output(const string &out_filepath, string &error)
542{
543 /* Save image with identical dimensions, channels and metadata. */
544 ImageSpec out_spec = in_spec;
545
546 /* Ensure that the output frame contains sample information even if the input didn't. */
547 for (int i = 0; i < layers.size(); i++) {
548 const string name = "cycles." + layers[i].name + ".samples";
549 if (!out_spec.find_attribute(name, TypeDesc::STRING)) {
550 out_spec.attribute(name, TypeDesc::STRING, string_printf("%d", layers[i].samples));
551 }
552 }
553
554 /* We don't need input anymore at this point, and will possibly
555 * overwrite the same file. */
556 close_input();
557
558 /* Write to temporary file path, so we denoise images in place and don't
559 * risk destroying files when something goes wrong in file saving. */
560 const string extension = OIIO::Filesystem::extension(out_filepath);
561 const string unique_name = ".denoise-tmp-" + OIIO::Filesystem::unique_path();
562 const string tmp_filepath = out_filepath + unique_name + extension;
563 unique_ptr<ImageOutput> out(ImageOutput::create(tmp_filepath));
564
565 if (!out) {
566 error = "Failed to open temporary file " + tmp_filepath + " for writing";
567 return false;
568 }
569
570 /* Open temporary file and write image buffers. */
571 if (!out->open(tmp_filepath, out_spec)) {
572 error = "Failed to open file " + tmp_filepath + " for writing: " + out->geterror();
573 return false;
574 }
575
576 bool ok = true;
577 if (!out->write_image(TypeDesc::FLOAT, pixels.data())) {
578 error = "Failed to write to file " + tmp_filepath + ": " + out->geterror();
579 ok = false;
580 }
581
582 if (!out->close()) {
583 error = "Failed to save to file " + tmp_filepath + ": " + out->geterror();
584 ok = false;
585 }
586
587 out.reset();
588
589 /* Copy temporary file to output filepath. */
590 string rename_error;
591 if (ok && !OIIO::Filesystem::rename(tmp_filepath, out_filepath, rename_error)) {
592 error = "Failed to move denoised image to " + out_filepath + ": " + rename_error;
593 ok = false;
594 }
595
596 if (!ok) {
597 OIIO::Filesystem::remove(tmp_filepath);
598 }
599
600 return ok;
601}
602
603/* File pattern handling and outer loop over frames */
604
606{
607 /* Initialize task scheduler. */
609
610 /* Initialize device. */
611 device = Device::create(denoiser_device_info, stats, profiler, true);
612 device->load_kernels(KERNEL_FEATURE_DENOISING);
613
614 vector<DeviceInfo> cpu_devices;
615 device_cpu_info(cpu_devices);
616 cpu_device = device_cpu_create(cpu_devices[0], device->stats, device->profiler, true);
617
619 if (denoiser) {
620 denoiser->load_kernels(nullptr);
621 }
622}
623
625{
626 denoiser.reset();
627 device.reset();
629}
630
632{
633 assert(input.size() == output.size());
634
635 const int num_frames = output.size();
636
637 if (!denoiser) {
638 error = "Failed to create denoiser";
639 return false;
640 }
641
642 for (int frame = 0; frame < num_frames; frame++) {
643 /* Skip empty output paths. */
644 if (output[frame].empty()) {
645 continue;
646 }
647
648 /* Execute task. */
649 DenoiseTask task(device.get(), this, frame);
650 if (!task.load()) {
651 error = task.error;
652 return false;
653 }
654
655 if (!task.exec()) {
656 error = task.error;
657 return false;
658 }
659
660 if (!task.save()) {
661 error = task.error;
662 return false;
663 }
664
665 task.free();
666 }
667
668 return true;
669}
670
static AppView * view
int full_width
Definition buffers.h:85
int full_height
Definition buffers.h:86
NODE_DECLARE int width
Definition buffers.h:70
void update_passes()
Definition buffers.cpp:114
void read_pixels(const DenoiseImageLayer &layer, const BufferParams &params, float *input_pixels)
bool parse_channels(const ImageSpec &in_spec, string &error)
void close_input()
array< float > pixels
Definition denoising.h:102
vector< DenoiseImageLayer > layers
Definition denoising.h:109
bool read_previous_pixels(const DenoiseImageLayer &layer, const BufferParams &params, float *input_pixels)
int num_channels
Definition denoising.h:96
bool load(const string &in_filepath, string &error)
unique_ptr< ImageInput > in_previous
Definition denoising.h:106
bool load_previous(const string &in_filepath, string &error)
bool save_output(const string &out_filepath, string &error)
ImageSpec in_spec
Definition denoising.h:105
DenoiserPipeline * denoiser
Definition denoising.h:157
RenderBuffers buffers
Definition denoising.h:167
bool load_input_pixels(const int layer)
DenoiseTask(Device *device, DenoiserPipeline *denoiser, const int frame)
string error
Definition denoising.h:153
Device * device
Definition denoising.h:158
int current_layer
Definition denoising.h:165
DenoiseImage image
Definition denoising.h:164
std::unique_ptr< Denoiser > denoiser
Definition denoising.h:52
friend class DenoiseTask
Definition denoising.h:46
DenoiserPipeline(DeviceInfo &denoiser_device_info, const DenoiseParams &params)
unique_ptr< Device > cpu_device
Definition denoising.h:51
unique_ptr< Device > device
Definition denoising.h:50
vector< string > input
Definition denoising.h:39
Profiler profiler
Definition denoising.h:49
vector< string > output
Definition denoising.h:43
static unique_ptr< Denoiser > create(Device *denoiser_device, Device *cpu_fallback_device, const DenoiseParams &params, const GraphicsInteropDevice &interop_device)
Definition denoiser.cpp:148
static unique_ptr< Device > create(const DeviceInfo &info, Stats &stats, Profiler &profiler, bool headless)
static void exit()
Definition task.cpp:81
static void init(const int num_threads=0)
Definition task.cpp:60
void push_back(unique_ptr< T > &&value)
static const int INPUT_NOISY_IMAGE
Definition denoising.cpp:72
static const int OUTPUT_NUM_CHANNELS
Definition denoising.cpp:86
static void fill_mapping(vector< ChannelMapping > &map, int pos, string name, string channels)
Definition denoising.cpp:64
static bool parse_channel_name(string name, string &renderlayer, string &pass, string &channel, bool multiview_channels)
Definition denoising.cpp:35
static CCL_NAMESPACE_BEGIN bool split_last_dot(string &in, string &suffix)
Definition denoising.cpp:20
static const int INPUT_DENOISING_ALBEDO
Definition denoising.cpp:74
static vector< ChannelMapping > output_channels()
Definition denoising.cpp:87
static const int INPUT_DENOISING_NORMAL
Definition denoising.cpp:73
static const int INPUT_NUM_CHANNELS
Definition denoising.cpp:71
static const int INPUT_MOTION
Definition denoising.cpp:75
static void add_pass(unique_ptr_vector< Pass > &passes, PassType type, PassMode mode=PassMode::NOISY)
static vector< ChannelMapping > input_channels()
Definition denoising.cpp:76
void device_cpu_info(vector< DeviceInfo > &devices)
CCL_NAMESPACE_BEGIN unique_ptr< Device > device_cpu_create(const DeviceInfo &info, Stats &stats, Profiler &profiler, bool headless)
#define KERNEL_FEATURE_DENOISING
#define CCL_NAMESPACE_END
uint pos
#define assert(assertion)
#define in
#define out
#define printf(...)
uiWidgetBaseParameters params[MAX_WIDGET_BASE_BATCH]
PassType
@ PASS_DENOISING_NORMAL
@ PASS_MOTION
@ PASS_COMBINED
@ PASS_DENOISING_ALBEDO
@ PASS_DENOISING_PREVIOUS
static void error(const char *str)
static void unique_name(bNode *node)
PassMode
Definition pass.h:20
@ DENOISED
Definition pass.h:22
@ NOISY
Definition pass.h:21
CCL_NAMESPACE_BEGIN string string_printf(const char *format,...)
Definition string.cpp:23
vector< int > previous_output_to_image_channel
Definition denoising.h:75
vector< int > input_to_image_channel
Definition denoising.h:68
bool match_channels(const std::vector< string > &channelnames, const std::vector< string > &neighbor_channelnames)
bool detect_denoising_channels()
Definition denoising.cpp:96
vector< int > output_to_image_channel
Definition denoising.h:72
vector< string > channels
Definition denoising.h:60
vector< int > layer_to_image_channel
Definition denoising.h:62
i
Definition text_draw.cc:230