[Bf-blender-cvs] [b06d32e4be6] blender2.7: Cycles: make cycles.merge_images work with incomplete layers and passes.

Brecht Van Lommel noreply at git.blender.org
Wed Mar 20 17:33:31 CET 2019


Commit: b06d32e4be677a2d3a2083fe433d1e76e4496ab7
Author: Brecht Van Lommel
Date:   Wed Mar 20 16:02:38 2019 +0100
Branches: blender2.7
https://developer.blender.org/rBb06d32e4be677a2d3a2083fe433d1e76e4496ab7

Cycles: make cycles.merge_images work with incomplete layers and passes.

If layers and passes are not exactly the same in all files, we make a best
effort to merge them instead of failing.

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

M	intern/cycles/render/merge.cpp

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

diff --git a/intern/cycles/render/merge.cpp b/intern/cycles/render/merge.cpp
index af655417046..3a85ae18f06 100644
--- a/intern/cycles/render/merge.cpp
+++ b/intern/cycles/render/merge.cpp
@@ -32,36 +32,41 @@ CCL_NAMESPACE_BEGIN
 /* Merge Image Layer */
 
 enum MergeChannelOp {
+	MERGE_CHANNEL_NOP,
 	MERGE_CHANNEL_COPY,
 	MERGE_CHANNEL_SUM,
 	MERGE_CHANNEL_AVERAGE
 };
 
+struct MergeImagePass {
+	/* Full channel name. */
+    string channel_name;
+	/* Channel format in the file. */
+    TypeDesc format;
+	/* Type of operation to perform when merging. */
+	MergeChannelOp op;
+	/* Offset of layer channels in input image. */
+	int offset;
+	/* Offset of layer channels in merged image. */
+	int merge_offset;
+};
+
 struct MergeImageLayer {
 	/* Layer name. */
 	string name;
-
-	/* All channels belonging to this MergeImageLayer. */
-	vector<string> channel_names;
-	/* Offsets of layer channels in image. */
-	vector<int> channel_offsets;
-	/* Type of operation to perform when merging. */
-	vector<MergeChannelOp> channel_ops;
-
+	/* Passes. */
+	vector<MergeImagePass> passes;
 	/* Sample amount that was used for rendering this layer. */
 	int samples;
 };
 
 /* Merge Image */
 
-class MergeImage {
-public:
+struct MergeImage {
 	/* OIIO file handle. */
 	unique_ptr<ImageInput> in;
-
 	/* Image file path. */
 	string filepath;
-
 	/* Render layers. */
 	vector<MergeImageLayer> layers;
 };
@@ -133,7 +138,6 @@ static bool parse_channels(const ImageSpec &in_spec,
                            vector<MergeImageLayer>& layers,
                            string& error)
 {
-	const std::vector<string> &channels = in_spec.channelnames;
 	const ParamValue *multiview = in_spec.find_attribute("multiView");
 	const bool multiview_channels = (multiview &&
 	                                 multiview->type().basetype == TypeDesc::STRING &&
@@ -145,29 +149,41 @@ static bool parse_channels(const ImageSpec &in_spec,
 	 * by RenderLayer.
 	 * Channels that can't be parsed are directly passed through to the output. */
 	map<string, MergeImageLayer> file_layers;
-	for(int i = 0; i < channels.size(); i++) {
-		string layer, pass, channel;
-
-		if(parse_channel_name(channels[i], layer, pass, channel, multiview_channels)) {
-			file_layers[layer].channel_names.push_back(pass + "." + channel);
-			file_layers[layer].channel_offsets.push_back(i);
-			file_layers[layer].channel_ops.push_back(parse_channel_operation(pass));
+	for(int i = 0; i < in_spec.nchannels; i++) {
+		MergeImagePass pass;
+		pass.channel_name = in_spec.channelnames[i];
+		pass.format = (in_spec.channelformats.size() > 0) ? in_spec.channelformats[i] : in_spec.format;
+		pass.offset = i;
+		pass.merge_offset = i;
+
+		string layername, passname, channelname;
+		if(parse_channel_name(pass.channel_name, layername, passname, channelname, multiview_channels)) {
+			/* Channer part of a render layer. */
+			pass.op = parse_channel_operation(passname);
+		}
+		else {
+			/* Other channels are added in unnamed layer. */
+			layername = "";
+			pass.op = parse_channel_operation(pass.channel_name);
 		}
 
-		/* Any unparsed channels are copied from the first image. */
+		file_layers[layername].passes.push_back(pass);
 	}
 
 	/* Loop over all detected RenderLayers, check whether they contain a full set of input channels.
 	 * Any channels that won't be processed internally are also passed through. */
-	for(map<string, MergeImageLayer>::iterator i = file_layers.begin(); i != file_layers.end(); ++i) {
-		const string& name = i->first;
-		MergeImageLayer& layer = i->second;
+	for(auto& i: file_layers) {
+		const string& name = i.first;
+		MergeImageLayer& layer = i.second;
 
 		layer.name = name;
 		layer.samples = 0;
 
-		/* If the sample value isn't set yet, check if there is a layer-specific one in the input file. */
-		if(layer.samples < 1) {
+		/* Determine number of samples from metadata. */
+		if(layer.name == "") {
+			layer.samples = 1;
+		}
+		else if(layer.samples < 1) {
 			string sample_string = in_spec.get_string_attribute("cycles." + name + ".samples", "");
 			if(sample_string != "") {
 				if(!sscanf(sample_string.c_str(), "%d", &layer.samples)) {
@@ -223,13 +239,10 @@ static bool open_images(const vector<string>& filepaths,
 			if(base_spec.width != spec.width ||
 			   base_spec.height != spec.height ||
 			   base_spec.depth != spec.depth ||
-			   base_spec.nchannels != spec.nchannels ||
 			   base_spec.format != spec.format ||
-			   base_spec.channelformats != spec.channelformats ||
-			   base_spec.channelnames != spec.channelnames ||
 			   base_spec.deep != spec.deep)
 			{
-				error = "Images do not have exact matching data and channel layout.";
+				error = "Images do not have matching size and data layout.";
 				return false;
 			}
 		}
@@ -240,26 +253,6 @@ static bool open_images(const vector<string>& filepaths,
 	return true;
 }
 
-static bool load_pixels(const MergeImage& image, array<float>& pixels, string& error)
-{
-	const ImageSpec& in_spec = image.in->spec();
-	const size_t width = in_spec.width;
-	const size_t height = in_spec.height;
-	const size_t num_channels = in_spec.nchannels;
-
-	const size_t num_pixels = (size_t)width * (size_t)height;
-	pixels.resize(num_pixels * num_channels);
-
-	/* Read all channels into buffer. Reading all channels at once is faster
-	 * than individually due to interleaved EXR channel storage. */
-	if(!image.in->read_image(TypeDesc::FLOAT, pixels.data())) {
-		error = "Failed to read image: " + image.filepath;
-		return false;
-	}
-
-	return true;
-}
-
 static void merge_render_time(ImageSpec& spec,
                               const vector<MergeImage>& images,
                               const string& name,
@@ -281,24 +274,160 @@ static void merge_render_time(ImageSpec& spec,
 
 static void merge_layer_render_time(ImageSpec& spec,
                                     const vector<MergeImage>& images,
+                                    const string& layer_name,
                                     const string& time_name,
                                     const bool average)
 {
-	for(size_t i = 0; i < images[0].layers.size(); i++) {
-		string name = "cycles." + images[0].layers[i].name + "." + time_name;
-		double time = 0.0;
+	string name = "cycles." + layer_name + "." + time_name;
+	double time = 0.0;
+
+	for(const MergeImage& image: images) {
+		string time_str = image.in->spec().get_string_attribute(name, "");
+		time += time_human_readable_to_seconds(time_str);
+	}
+
+	if(average) {
+		time /= images.size();
+	}
+
+	spec.attribute(name, TypeDesc::STRING, time_human_readable_from_seconds(time));
+}
+
+static void merge_channels_metadata(vector<MergeImage>& images,
+                                    ImageSpec& out_spec,
+                                    vector<int>& channel_total_samples)
+{
+	/* Based on first image. */
+	out_spec  = images[0].in->spec();
+
+	/* Merge channels and compute offsets. */
+	out_spec.nchannels = 0;
+	out_spec.channelformats.clear();
+	out_spec.channelnames.clear();
+
+	for(MergeImage& image: images) {
+		for(MergeImageLayer& layer: image.layers) {
+			for(MergeImagePass& pass: layer.passes) {
+				/* Test if matching channel already exists in merged image. */
+				bool found = false;
+
+				for(size_t i = 0; i < out_spec.nchannels; i++) {
+					if(pass.channel_name == out_spec.channelnames[i]) {
+						pass.merge_offset = i;
+						channel_total_samples[i] += layer.samples;
+						/* First image wins for channels that can't be averaged or summed. */
+						if (pass.op == MERGE_CHANNEL_COPY) {
+							pass.op = MERGE_CHANNEL_NOP;
+						}
+						found = true;
+						break;
+					}
+				}
+
+				if(!found) {
+					/* Add new channel. */
+					pass.merge_offset = out_spec.nchannels;
+					channel_total_samples.push_back(layer.samples);
 
-		for(const MergeImage& image: images) {
-			string time_str = image.in->spec().get_string_attribute(name, "");
-			time += time_human_readable_to_seconds(time_str);
+					out_spec.channelnames.push_back(pass.channel_name);
+					out_spec.channelformats.push_back(pass.format);
+					out_spec.nchannels++;
+				}
+			}
 		}
+	}
+
+	/* Merge metadata. */
+	merge_render_time(out_spec, images, "RenderTime", false);
+
+	map<string, int> layer_num_samples;
+	for(MergeImage& image: images) {
+		for(MergeImageLayer& layer: image.layers) {
+			if(layer.name != "") {
+				layer_num_samples[layer.name] += layer.samples;
+			}
+		}
+	}
+
+	for(const auto& i: layer_num_samples) {
+		string name = "cycles." + i.first + ".samples";
+		out_spec.attribute(name, TypeDesc::STRING, string_printf("%d", i.second));
 
-		if(average) {
-			time /= images.size();
+		merge_layer_render_time(out_spec, images, i.first, "total_time", false);
+		merge_layer_render_time(out_spec, images, i.first, "render_time", false);
+		merge_layer_render_time(out_spec, images, i.first, "synchronization_time", true);
+	}
+}
+
+static void alloc_pixels(const ImageSpec& spec, array<float>& pixels)
+{
+	const size_t width = spec.width;
+	const size_t height = spec.height;
+	const size_t num_channels = spec.nchannels;
+
+	const size_t num_pixels = (size_t)width * (size_t)height;
+	pixels.resize(num_pixels * num_channels);
+}
+
+static bool merge_pixels(const vector<MergeImage>& images,
+                         const ImageSpec& out_spec,
+                         const vector<int>& channel_total_samples,
+                         array<float>& out_pixels,
+                         string& error)
+{
+	alloc_pixels(out_spec, out_pixels);
+	memset(out_pixels.data(), 0, out_pixels.size() * sizeof(float));
+
+	for(const MergeImage& image: images) {
+		/* Read all channels into buffer. Reading all channels at once is
+		 * faster than individually due to interleaved EXR channel storage. */
+		array<float> pixels;
+		alloc_pixels(image.in->spec(), pixels);
+
+		if(!image.in->read_image(TypeDesc::FLOAT, pixels.data())) {
+			error = "Failed to read image: " + image.filepath;
+			return false;
 		}
 
-		spec.attribute(name, TypeDesc::STRING, time_human_readable_from_seconds(time));
+		for(size_t li = 0; li < image.layers.size(); li++) {
+			const MergeImageLayer& layer = image.layers[li];
+
+			const size_t stride = out_spec.nchannels

@@ Diff output truncated at 10240 characters. @@



More information about the Bf-blender-cvs mailing list