13#include <OpenImageIO/filesystem.h>
23 size_t pos = in.rfind(
".");
24 if (
pos == string::npos) {
27 suffix = in.substr(
pos + 1);
28 in = in.substr(0,
pos);
37 string name,
string &renderlayer,
string &pass,
string &channel,
bool multiview_channels)
51 if (multiview_channels) {
52 renderlayer +=
"." +
view;
67 for (
const char *chan = channels.c_str(); *chan; chan++) {
68 map.push_back({
pos++, name +
"." + *chan});
105 if (i == channels.end()) {
109 size_t input_channel = mapping.channel;
110 size_t layer_channel = i - channels.begin();
120 if (i == channels.end()) {
124 size_t output_channel = mapping.channel;
125 size_t layer_channel = i - channels.begin();
141 const std::vector<string> &neighbor_channelnames)
145 assert(mapping.size() == 0);
150 std::vector<string>::const_iterator frame_channel = find(
151 neighbor_channelnames.begin(), neighbor_channelnames.end(), channel);
153 if (frame_channel == neighbor_channelnames.end()) {
157 mapping[i] = frame_channel - neighbor_channelnames.begin();
166 : denoiser(denoiser), device(device), frame(frame), current_layer(0),
buffers(device)
186 if (
frame > 0 && !image.read_previous_pixels(image_layer,
buffers.
params, buffer_data)) {
187 error =
"Failed to read neighbor frame pixels";
202 pass->set_type(type);
203 pass->set_mode(mode);
205 passes.push_back(pass);
211 if (!image.load(center_filepath,
error)) {
220 if (image.layers.empty()) {
221 error =
"No image layers found to denoise in " + center_filepath;
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);
241 buffer_params.
width = image.width;
242 buffer_params.
height = image.height;
249 for (
Pass *pass : passes) {
285 for (
int y = 0; y < image.height; y++) {
289 int image_channel = output_to_image_channel[j];
290 out[image.num_channels * x + image_channel] = result[offset + j];
293 out += image.num_channels * image.width;
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);
353 map<string, DenoiseImageLayer> file_layers;
354 for (
int i = 0; i < channels.size(); i++) {
355 string layer, pass, channel;
357 file_layers[layer].channels.push_back(pass +
"." + channel);
358 file_layers[layer].layer_to_image_channel.push_back(i);
364 for (map<string, DenoiseImageLayer>::iterator i = file_layers.begin(); i != file_layers.end();
367 const string &name = i->first;
371 if (!layer.detect_denoising_channels()) {
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;
390 if (layer.samples < 1) {
392 "No sample number specified in the file for layer %s or on the command line",
397 layers.push_back(layer);
409 const int *input_to_image_channel = layer.input_to_image_channel.data();
412 for (
int j = 0; j < 3; ++j) {
415 input_pixels[i *
params.pass_stride + offset + j] =
418 for (
int j = 0; j < 3; ++j) {
421 input_pixels[i *
params.pass_stride + offset + j] =
424 for (
int j = 0; j < 3; ++j) {
427 input_pixels[i *
params.pass_stride + offset + j] =
430 for (
int j = 0; j < 4; ++j) {
432 int image_channel = input_to_image_channel[
INPUT_MOTION + j];
433 input_pixels[i *
params.pass_stride + offset + j] =
445 const size_t num_pixels = (size_t)
width * (
size_t)
height;
454 const int *output_to_image_channel = layer.previous_output_to_image_channel.data();
457 for (
int j = 0; j < 3; ++j) {
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];
470 if (!Filesystem::is_regular(in_filepath)) {
471 error =
"Couldn't find file: " + in_filepath;
475 unique_ptr<ImageInput> in(ImageInput::open(in_filepath));
477 error =
"Couldn't open file: " + in_filepath;
490 if (layers.empty()) {
491 error =
"Could not find a render layer containing denoising data and motion vector passes";
495 size_t num_pixels = (size_t)
width * (
size_t)
height;
500 if (!in->read_image(0, 0, 0,
num_channels, TypeDesc::FLOAT, pixels.data())) {
501 error =
"Failed to read image: " + in_filepath;
510 if (!Filesystem::is_regular(filepath)) {
511 error =
"Couldn't find neighbor frame: " + filepath;
515 unique_ptr<ImageInput> in_neighbor(ImageInput::open(filepath));
517 error =
"Couldn't open neighbor frame: " + filepath;
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;
528 if (!layer.match_channels(
in_spec.channelnames, neighbor_spec.channelnames)) {
529 error =
"Neighbor frame misses denoising data passes: " + filepath;
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)) {
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));
564 error =
"Failed to open temporary file " + tmp_filepath +
" for writing";
569 if (!out->open(tmp_filepath, out_spec)) {
570 error =
"Failed to open file " + tmp_filepath +
" for writing: " + out->geterror();
575 if (!out->write_image(TypeDesc::FLOAT, pixels.data())) {
576 error =
"Failed to write to file " + tmp_filepath +
": " + out->geterror();
581 error =
"Failed to save to file " + tmp_filepath +
": " + out->geterror();
589 if (ok && !OIIO::Filesystem::rename(tmp_filepath, out_filepath, rename_error)) {
590 error =
"Failed to move denoised image to " + out_filepath +
": " + rename_error;
595 OIIO::Filesystem::remove(tmp_filepath);
629 assert(input.size() == output.size());
631 int num_frames = output.size();
633 for (
int frame = 0; frame < num_frames; frame++) {
635 if (output[frame].empty()) {
int get_pass_offset(PassType type, PassMode mode=PassMode::NOISY) const
void read_pixels(const DenoiseImageLayer &layer, const BufferParams ¶ms, float *input_pixels)
bool parse_channels(const ImageSpec &in_spec, string &error)
vector< DenoiseImageLayer > layers
bool read_previous_pixels(const DenoiseImageLayer &layer, const BufferParams ¶ms, float *input_pixels)
bool load(const string &in_filepath, string &error)
unique_ptr< ImageInput > in_previous
bool load_previous(const string &in_filepath, string &error)
bool save_output(const string &out_filepath, string &error)
DenoiserPipeline * denoiser
DenoiseTask(Device *device, DenoiserPipeline *denoiser, int frame)
bool load_input_pixels(int layer)
std::unique_ptr< Denoiser > denoiser
DenoiserPipeline(DeviceInfo &denoiser_device_info, const DenoiseParams ¶ms)
static unique_ptr< Denoiser > create(Device *denoise_device, Device *cpu_fallback_device, const DenoiseParams ¶ms)
virtual bool load_kernels(uint)
static Device * create(const DeviceInfo &info, Stats &stats, Profiler &profiler, bool headless)
device_vector< float > buffer
void reset(const BufferParams ¶ms)
static void init(int num_threads=0)
static const int INPUT_NOISY_IMAGE
static const int OUTPUT_NUM_CHANNELS
static void fill_mapping(vector< ChannelMapping > &map, int pos, string name, string channels)
static bool parse_channel_name(string name, string &renderlayer, string &pass, string &channel, bool multiview_channels)
static CCL_NAMESPACE_BEGIN bool split_last_dot(string &in, string &suffix)
static const int INPUT_DENOISING_ALBEDO
static vector< ChannelMapping > output_channels()
static const int INPUT_DENOISING_NORMAL
static const int INPUT_NUM_CHANNELS
static const int INPUT_MOTION
static void add_pass(vector< Pass * > &passes, PassType type, PassMode mode=PassMode::NOISY)
static vector< ChannelMapping > input_channels()
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
@ PASS_DENOISING_PREVIOUS
#define KERNEL_FEATURE_DENOISING
static void error(const char *str)
static void unique_name(bNode *node)
CCL_NAMESPACE_BEGIN string string_printf(const char *format,...)
vector< int > previous_output_to_image_channel
vector< int > input_to_image_channel
bool match_channels(const std::vector< string > &channelnames, const std::vector< string > &neighbor_channelnames)
bool detect_denoising_channels()
vector< int > output_to_image_channel
vector< int > layer_to_image_channel