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