[Bf-blender-cvs] [3f94371] soc-2016-cycles_denoising: Cycles: Add denoising option to the Cycles Standalone executable

Lukas Stockner noreply at git.blender.org
Wed Aug 10 03:22:11 CEST 2016


Commit: 3f94371a36291843510676040de6ec1c23ac2b49
Author: Lukas Stockner
Date:   Mon Aug 8 21:09:28 2016 +0200
Branches: soc-2016-cycles_denoising
https://developer.blender.org/rB3f94371a36291843510676040de6ec1c23ac2b49

Cycles: Add denoising option to the Cycles Standalone executable

To use it, call it with "./cycles --denoise --samples <sample number> --output <denoised_file.png> <rendered_image.exr>".
You need to enter the sample number that the image was rendered with - others will work as well, but might produce artifacts.
The input image can be generated by rendering with "Keep denoising data" enabled (denoising itself isn't needed) and saving the result as Multilayer EXR.

For now, this is mainly useful for quicker testing without re-rendering and profiling, not so much for regular users.
However, the next step will be to implement inter-frame denoising for animations, which will provide a significant quality boost.

===================================================================

M	intern/cycles/app/CMakeLists.txt
A	intern/cycles/app/cycles_denoising.cpp
A	intern/cycles/app/cycles_denoising.h
M	intern/cycles/app/cycles_standalone.cpp
A	intern/cycles/app/cycles_standalone.h
M	intern/cycles/render/session.cpp

===================================================================

diff --git a/intern/cycles/app/CMakeLists.txt b/intern/cycles/app/CMakeLists.txt
index 73dbf16..249af9d 100644
--- a/intern/cycles/app/CMakeLists.txt
+++ b/intern/cycles/app/CMakeLists.txt
@@ -104,8 +104,11 @@ endmacro()
 if(WITH_CYCLES_STANDALONE)
 	set(SRC
 		cycles_standalone.cpp
+		cycles_standalone.h
 		cycles_xml.cpp
 		cycles_xml.h
+		cycles_denoising.cpp
+		cycles_denoising.h
 	)
 	add_executable(cycles ${SRC})
 	cycles_target_link_libraries(cycles)
diff --git a/intern/cycles/app/cycles_denoising.cpp b/intern/cycles/app/cycles_denoising.cpp
new file mode 100644
index 0000000..de54b2c
--- /dev/null
+++ b/intern/cycles/app/cycles_denoising.cpp
@@ -0,0 +1,228 @@
+/*
+ * Copyright 2016 Blender Foundation
+ *
+ * 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 "cycles_denoising.h"
+
+#include "util_image.h"
+
+CCL_NAMESPACE_BEGIN
+
+typedef class PassTypeInfo
+{
+public:
+	PassTypeInfo(DenoiseExtendedTypes type, int num_channels, string channels)
+	 : type(type), num_channels(num_channels), channels(channels) {}
+	PassTypeInfo() : type(EX_TYPE_NONE), num_channels(0), channels("") {}
+
+	DenoiseExtendedTypes type;
+	int num_channels;
+	string channels;
+
+	bool operator<(const PassTypeInfo &other) const {
+		return type < other.type;
+	}
+} PassTypeInfo;
+
+static map<string, PassTypeInfo> denoise_passes_init()
+{
+	map<string, PassTypeInfo> passes;
+
+	passes["DenoiseNormal"]    = PassTypeInfo(EX_TYPE_DENOISE_NORMAL,     3, "XYZ");
+	passes["DenoiseNormalVar"] = PassTypeInfo(EX_TYPE_DENOISE_NORMAL_VAR, 3, "XYZ");
+	passes["DenoiseAlbedo"]    = PassTypeInfo(EX_TYPE_DENOISE_ALBEDO,     3, "RGB");
+	passes["DenoiseAlbedoVar"] = PassTypeInfo(EX_TYPE_DENOISE_ALBEDO_VAR, 3, "RGB");
+	passes["DenoiseDepth"]     = PassTypeInfo(EX_TYPE_DENOISE_DEPTH,      1, "Z");
+	passes["DenoiseDepthVar"]  = PassTypeInfo(EX_TYPE_DENOISE_DEPTH_VAR,  1, "Z");
+	passes["DenoiseShadowA"]   = PassTypeInfo(EX_TYPE_DENOISE_SHADOW_A,   3, "RGB");
+	passes["DenoiseShadowB"]   = PassTypeInfo(EX_TYPE_DENOISE_SHADOW_B,   3, "RGB");
+	passes["DenoiseNoisy"]     = PassTypeInfo(EX_TYPE_DENOISE_NOISY,      3, "RGB");
+	passes["DenoiseNoisyVar"]  = PassTypeInfo(EX_TYPE_DENOISE_NOISY_VAR,  3, "RGB");
+	passes["DenoiseClean"]     = PassTypeInfo(EX_TYPE_DENOISE_CLEAN,      3, "RGB");
+
+	return passes;
+}
+
+static map<string, PassTypeInfo> denoise_passes_map = denoise_passes_init();
+
+static bool split_channel(string full_channel, string &layer, string &pass, string &channel)
+{
+	/* Splits channel name into <layer>.<pass>.<channel> */
+	if(std::count(full_channel.begin(), full_channel.end(), '.') != 2) {
+		return false;
+	}
+
+	int first_dot = full_channel.find(".");
+	int second_dot = full_channel.rfind(".");
+	layer = full_channel.substr(0, first_dot);
+	pass = full_channel.substr(first_dot + 1, second_dot - first_dot - 1);
+	channel = full_channel.substr(second_dot + 1);
+
+	return true;
+}
+
+static int find_channel(string channels, string channel)
+{
+	if(channel.length() != 1) return -1;
+	size_t pos = channels.find(channel);
+	if(pos == string::npos) return -1;
+	return pos;
+}
+
+static RenderBuffers* load_frame(string file, Device *device)
+{
+	RenderBuffers *buffers = NULL;
+
+	ImageInput *frame = ImageInput::open(file);
+	if(!frame) {
+		printf("Couldn't open frame %s!\n", file.c_str());
+		return NULL;
+	}
+
+	const ImageSpec &spec = frame->spec();
+
+	/* Find a single RenderLayer to load. */
+	string renderlayer = "";
+	string layer, pass, channel;
+	for(int i = 0; i < spec.nchannels; i++) {
+		if(!split_channel(spec.channelnames[i], layer, pass, channel)) continue;
+		if(pass == "DenoiseNoisy") {
+			renderlayer = layer;
+			break;
+		}
+	}
+
+	if(renderlayer != "") {
+		/* Find all passes that the frame contains. */
+		int passes = EX_TYPE_NONE;
+		map<DenoiseExtendedTypes, int> num_channels;
+		map<PassTypeInfo, int3> channel_ids;
+		for(int i = 0; i < spec.nchannels; i++) {
+			if(!split_channel(spec.channelnames[i], layer, pass, channel)) continue;
+			if(layer != renderlayer) {
+				/* The channel belongs to another RenderLayer. */
+				continue;
+			}
+			if(denoise_passes_map.count(pass)) {
+				PassTypeInfo type = denoise_passes_map[pass];
+				assert(type.num_channels <= 3);
+				/* Pass was found, count the channels. */
+				size_t channel_id = find_channel(type.channels, channel);
+				if(channel_id != -1) {
+					/* This channel is part of the pass, so count it. */
+					num_channels[type.type]++;
+					/* Remember which OIIO channel belongs to which pass. */
+					channel_ids[type][channel_id] = i;
+					if(num_channels[type.type] == type.num_channels) {
+						/* We found all the channels of the pass! */
+						passes |= type.type;
+					}
+				}
+			}
+		}
+
+		if((~passes & EX_TYPE_DENOISE_REQUIRED) == 0) {
+			printf("Found all needed passes in the frame!\n");
+
+			BufferParams params;
+			params.width  = params.full_width  = params.final_width  = spec.width;
+			params.height = params.full_height = params.final_height = spec.height;
+			params.full_x = params.full_y = 0;
+			params.denoising_passes = true;
+			params.selective_denoising = (passes & EX_TYPE_DENOISE_CLEAN);
+
+			buffers = new RenderBuffers(device);
+			buffers->reset(device, params);
+
+			int4 rect = make_int4(0, 0, params.width, params.height);
+
+			float *pass_data = new float[4*params.width*params.height];
+			/* Read all the passes from the file. */
+			for(map<PassTypeInfo, int3>::iterator i = channel_ids.begin(); i != channel_ids.end(); i++)
+			{
+				for(int c = 0; c < i->first.num_channels; c++) {
+					int xstride = i->first.num_channels*sizeof(float);
+					int ystride = params.width * xstride;
+					printf("Reading pass %s!            \r", spec.channelnames[i->second[c]].c_str());
+					fflush(stdout);
+					frame->read_image(i->second[c], i->second[c]+1, TypeDesc::FLOAT, pass_data + c, xstride, ystride);
+				}
+				buffers->get_denoising_rect(i->first.type, 1.0f, options.session_params.samples, i->first.num_channels, rect, pass_data, true);
+			}
+
+			/* Read combined channel. */
+			for(int i = 0; i < spec.nchannels; i++) {
+				if(!split_channel(spec.channelnames[i], layer, pass, channel)) continue;
+				if(layer != renderlayer || pass != "Combined") continue;
+
+				size_t channel_id = find_channel("RGBA", channel);
+				if(channel_id != 1) {
+					int xstride = 4*sizeof(float);
+					int ystride = params.width * xstride;
+					printf("Reading pass %s!            \r", spec.channelnames[i].c_str());
+					fflush(stdout);
+					frame->read_image(i, i+1, TypeDesc::FLOAT, pass_data + channel_id, xstride, ystride);
+				}
+			}
+			buffers->get_pass_rect(PASS_COMBINED, 1.0f, options.session_params.samples, 4, rect, pass_data, true);
+
+			delete[] pass_data;
+
+			buffers->copy_to_device();
+		}
+		else {
+			printf("The frame is missing some pass!\n");
+		}
+	}
+	else {
+		printf("Didn't fine a suitable RenderLayer!\n");
+	}
+
+	frame->close();
+	ImageInput::destroy(frame);
+
+	return buffers;
+}
+
+bool cycles_denoising_session()
+{
+	options.session_params.only_denoise = true;
+	options.session_params.progressive_refine = false;
+	options.session_params.progressive = false;
+	options.session_params.background = true;
+	options.session_params.tile_order = TILE_BOTTOM_TO_TOP;
+
+	options.session = new Session(options.session_params);
+	options.session->progress.set_update_callback(function_bind(&session_print_status));
+	options.session->set_pause(false);
+
+	RenderBuffers *buffers = load_frame(options.filepaths[0], options.session->device);
+	if(!buffers) {
+		return false;
+	}
+	options.session->buffers = buffers;
+
+	options.session->start_denoise();
+	options.session->wait();
+
+	/* Required for correct scaling of the output. */
+	options.session->params.samples--;
+
+	delete options.session;
+
+	return true;
+}
+
+CCL_NAMESPACE_END
\ No newline at end of file
diff --git a/intern/cycles/app/cycles_denoising.h b/intern/cycles/app/cycles_denoising.h
new file mode 100644
index 0000000..ef18e17
--- /dev/null
+++ b/intern/cycles/app/cycles_denoising.h
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2016 Blender Foundation
+ *
+ * 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 "cycles_standalone.h"
+
+#ifndef __CYCLES_DENOISING_H__
+#define __CYCLES_DENOISING_H__
+
+CCL_NAMESPACE_BEGIN
+
+bool cycles_denoising_session();
+
+CCL_NAMESPACE_END
+
+#endif /* __CYCLES_DENOISING_H__ */
diff --git a/intern/cycles/app/cycles_standalone.cpp b/intern/cycles/app/cycles_standalone.cpp
index e477cb9..be57a20 100644
--- a/intern/cycles/app/cycles_standalone.cpp
+++ b/intern/cycles/app/cycles_standalone.cpp
@@ -38,20 +38,13 @@
 #include "util_view.h"
 #endif
 
+#include "cycles_standalone.h"
 #include "cycles_xml.h"
+#include "cycles_denoising.h"
 
 CCL_NAMESPACE_BEGIN
 
-struct Options {
-	Session *session;
-	Scene *scene;
-	string filepath;
-	int width, height;
-	SceneParams scene_params;
-	SessionParams session_params;
-	bool quiet;
-	bool show_help, interactive, pause;
-} options;
+Options options;
 
 static void session_print(const string& str)
 {
@@ -70,7 +63,7 @@ static void session_print(const string& str)
 	fflush(stdout);
 }
 
-static void session_pr

@@ Diff output truncated at 10240 characters. @@




More information about the Bf-blender-cvs mailing list