12 #include <OpenImageIO/filesystem.h>
13 #include <OpenImageIO/imageio.h>
68 unique_ptr<ImageInput>
in;
79 if (pass_name ==
"Depth" || pass_name ==
"IndexMA" || pass_name ==
"IndexOB" ||
100 size_t pos = in.rfind(
".");
101 if (
pos == string::npos) {
104 suffix = in.substr(
pos + 1);
105 in = in.substr(0,
pos);
113 string name,
string &renderlayer,
string &pass,
string &channel,
bool multiview_channels)
127 if (multiview_channels) {
128 renderlayer +=
"." +
view;
138 const ParamValue *multiview = in_spec.find_attribute(
"multiView");
139 const bool multiview_channels = (multiview && multiview->type().basetype == TypeDesc::STRING &&
140 multiview->type().arraylen >= 2);
147 map<string, MergeImageLayer> file_layers;
148 for (
int i = 0; i < in_spec.nchannels; i++) {
151 pass.
format = (in_spec.channelformats.size() > 0) ? in_spec.channelformats[i] : in_spec.format;
155 string layername, channelname;
157 pass.
channel_name, layername, pass.
name, channelname, multiview_channels)) {
167 file_layers[layername].passes.push_back(pass);
171 if (file_layers.size() == 1 && file_layers.find(
"") != file_layers.end()) {
172 for (
const ParamValue &attrib : in_spec.extra_attribs) {
173 const string attrib_name = attrib.name().string();
176 const size_t start = strlen(
"cycles.");
177 const size_t end = attrib_name.size() - strlen(
".samples");
178 const string layername = attrib_name.substr(start, end - start);
183 file_layers[layername] = layer;
190 for (
auto &[name, layer] : file_layers) {
195 if (layer.name ==
"") {
198 else if (layer.samples < 1) {
199 string sample_string = in_spec.get_string_attribute(
"cycles." + name +
".samples",
"");
200 if (sample_string !=
"") {
201 if (!sscanf(sample_string.c_str(),
"%d", &layer.samples)) {
202 error =
"Failed to parse samples metadata: " + sample_string;
208 if (layer.samples < 1) {
210 "No sample number specified in the file for layer %s or on the command line",
216 auto sample_pass_it = find_if(
217 layer.passes.begin(), layer.passes.end(), [](
const MergeImagePass &pass) {
218 return pass.name ==
"Debug Sample Count";
220 if (sample_pass_it != layer.passes.end()) {
221 layer.has_sample_pass =
true;
222 layer.sample_pass_offset =
distance(layer.passes.begin(), sample_pass_it);
225 layer.has_sample_pass =
false;
228 layers.push_back(layer);
236 for (
const string &filepath : filepaths) {
237 unique_ptr<ImageInput> in(ImageInput::open(filepath));
239 error =
"Couldn't open file: " + filepath;
244 image.in = std::move(in);
245 image.filepath = filepath;
250 if (
image.layers.size() == 0) {
251 error =
"Could not find a render layer for merging";
255 if (
image.in->spec().deep) {
256 error =
"Merging deep images not supported.";
260 if (images.size() > 0) {
261 const ImageSpec &base_spec = images[0].in->spec();
262 const ImageSpec &spec =
image.in->spec();
264 if (base_spec.width != spec.width || base_spec.height != spec.height ||
265 base_spec.depth != spec.depth || base_spec.format != spec.format ||
266 base_spec.deep != spec.deep) {
267 error =
"Images do not have matching size and data layout.";
272 images.push_back(std::move(
image));
286 string time_str =
image.in->spec().get_string_attribute(name,
"");
291 time /= images.size();
299 const string &layer_name,
300 const string &time_name,
303 string name =
"cycles." + layer_name +
"." + time_name;
307 string time_str =
image.in->spec().get_string_attribute(name,
"");
312 time /= images.size();
321 out_spec = images[0].in->spec();
324 out_spec.nchannels = 0;
325 out_spec.channelformats.clear();
326 out_spec.channelnames.clear();
332 auto channel = find_if(
333 out_spec.channelnames.begin(),
334 out_spec.channelnames.end(),
335 [&pass](
const auto &channel_name) { return pass.channel_name == channel_name; });
337 if (channel != out_spec.channelnames.end()) {
338 int index =
distance(out_spec.channelnames.begin(), channel);
351 out_spec.channelformats.push_back(pass.
format);
352 out_spec.nchannels++;
361 map<string, int> layer_num_samples;
364 if (layer.
name !=
"") {
370 for (
const auto &[layer_name, layer_samples] : layer_num_samples) {
371 string name =
"cycles." + layer_name +
".samples";
372 out_spec.attribute(name, TypeDesc::STRING,
to_string(layer_samples));
382 const size_t width = spec.width;
383 const size_t height = spec.height;
384 const size_t num_channels = spec.nchannels;
391 const ImageSpec &out_spec,
392 const unordered_map<string, SampleCount> &layer_samples,
397 memset(out_pixels.
data(), 0, out_pixels.
size() *
sizeof(
float));
406 error =
"Failed to read image: " +
image.filepath;
412 const size_t out_stride = out_spec.nchannels;
424 out_pixels[out_offset] = pixels[
offset];
429 out_pixels[out_offset] += pixels[
offset];
436 const auto &samples = layer_samples.at(layer.
name);
440 const float total_samples = samples.per_pixel[i];
444 layer_samples = pixels[sample_pass_offset] * layer.
samples;
450 out_pixels[out_offset] += pixels[
offset] * (1.0f * layer_samples / total_samples);
455 const auto &samples = layer_samples.at(layer.
name);
458 out_pixels[out_offset] = 1.0f * samples.per_pixel[i] / samples.total;
471 const ImageSpec &spec,
477 string extension = OIIO::Filesystem::extension(filepath);
478 string unique_name =
".merge-tmp-" + OIIO::Filesystem::unique_path();
479 string tmp_filepath = filepath +
unique_name + extension;
483 error =
"Failed to open temporary file " + tmp_filepath +
" for writing";
488 if (!
out->open(tmp_filepath, spec)) {
489 error =
"Failed to open file " + tmp_filepath +
" for writing: " +
out->geterror();
495 error =
"Failed to write to file " + tmp_filepath +
": " +
out->geterror();
500 error =
"Failed to save to file " + tmp_filepath +
": " +
out->geterror();
508 if (ok && !OIIO::Filesystem::rename(tmp_filepath, filepath, rename_error)) {
509 error =
"Failed to move merged image to " + filepath +
": " + rename_error;
521 unordered_map<string, SampleCount> &layer_samples)
523 for (
auto &
image : images) {
524 const ImageSpec &in_spec =
image.in->spec();
526 for (
auto &layer :
image.layers) {
527 bool initialize = (layer_samples.count(layer.name) == 0);
528 auto ¤t_layer_samples = layer_samples[layer.name];
531 current_layer_samples.total = 0;
532 current_layer_samples.per_pixel.resize(in_spec.width * in_spec.height);
534 current_layer_samples.per_pixel.begin(), current_layer_samples.per_pixel.end(), 0.0f);
537 if (layer.has_sample_pass) {
540 sample_count_buffer.
resize(in_spec.width * in_spec.height);
541 image.in->read_image(0,
543 layer.sample_pass_offset,
544 layer.sample_pass_offset,
546 (
void *)sample_count_buffer.
data());
548 for (
size_t i = 0; i < current_layer_samples.per_pixel.size(); i++) {
549 current_layer_samples.per_pixel[i] += sample_count_buffer[i] * layer.samples;
554 for (
size_t i = 0; i < current_layer_samples.per_pixel.size(); i++) {
555 current_layer_samples.per_pixel[i] += layer.samples;
559 current_layer_samples.total += layer.samples;
572 error =
"No input file paths specified.";
576 error =
"No output file path specified.";
587 unordered_map<string, SampleCount> layer_samples;
_GL_VOID GLfloat value _GL_VOID_RET _GL_VOID const GLuint GLboolean *residences _GL_BOOL_RET _GL_VOID GLsizei height
_GL_VOID GLfloat value _GL_VOID_RET _GL_VOID const GLuint GLboolean *residences _GL_BOOL_RET _GL_VOID GLsizei GLfloat GLfloat GLfloat GLfloat const GLubyte *bitmap _GL_VOID_RET _GL_VOID GLenum const void *lists _GL_VOID_RET _GL_VOID const GLdouble *equation _GL_VOID_RET _GL_VOID GLdouble GLdouble blue _GL_VOID_RET _GL_VOID GLfloat GLfloat blue _GL_VOID_RET _GL_VOID GLint GLint blue _GL_VOID_RET _GL_VOID GLshort GLshort blue _GL_VOID_RET _GL_VOID GLubyte GLubyte blue _GL_VOID_RET _GL_VOID GLuint GLuint blue _GL_VOID_RET _GL_VOID GLushort GLushort blue _GL_VOID_RET _GL_VOID GLbyte GLbyte GLbyte alpha _GL_VOID_RET _GL_VOID GLdouble GLdouble GLdouble alpha _GL_VOID_RET _GL_VOID GLfloat GLfloat GLfloat alpha _GL_VOID_RET _GL_VOID GLint GLint GLint alpha _GL_VOID_RET _GL_VOID GLshort GLshort GLshort alpha _GL_VOID_RET _GL_VOID GLubyte GLubyte GLubyte alpha _GL_VOID_RET _GL_VOID GLuint GLuint GLuint alpha _GL_VOID_RET _GL_VOID GLushort GLushort GLushort alpha _GL_VOID_RET _GL_VOID GLenum mode _GL_VOID_RET _GL_VOID GLint GLsizei width
_GL_VOID GLfloat value _GL_VOID_RET _GL_VOID const GLuint GLboolean *residences _GL_BOOL_RET _GL_VOID GLsizei GLfloat GLfloat GLfloat GLfloat const GLubyte *bitmap _GL_VOID_RET _GL_VOID GLenum const void *lists _GL_VOID_RET _GL_VOID const GLdouble *equation _GL_VOID_RET _GL_VOID GLdouble GLdouble blue _GL_VOID_RET _GL_VOID GLfloat GLfloat blue _GL_VOID_RET _GL_VOID GLint GLint blue _GL_VOID_RET _GL_VOID GLshort GLshort blue _GL_VOID_RET _GL_VOID GLubyte GLubyte blue _GL_VOID_RET _GL_VOID GLuint GLuint blue _GL_VOID_RET _GL_VOID GLushort GLushort blue _GL_VOID_RET _GL_VOID GLbyte GLbyte GLbyte alpha _GL_VOID_RET _GL_VOID GLdouble GLdouble GLdouble alpha _GL_VOID_RET _GL_VOID GLfloat GLfloat GLfloat alpha _GL_VOID_RET _GL_VOID GLint GLint GLint alpha _GL_VOID_RET _GL_VOID GLshort GLshort GLshort alpha _GL_VOID_RET _GL_VOID GLubyte GLubyte GLubyte alpha _GL_VOID_RET _GL_VOID GLuint GLuint GLuint alpha _GL_VOID_RET _GL_VOID GLushort GLushort GLushort alpha _GL_VOID_RET _GL_VOID GLenum mode _GL_VOID_RET _GL_VOID GLint GLsizei GLsizei GLenum type _GL_VOID_RET _GL_VOID GLsizei GLenum GLenum const void *pixels _GL_VOID_RET _GL_VOID const void *pointer _GL_VOID_RET _GL_VOID GLdouble v _GL_VOID_RET _GL_VOID GLfloat v _GL_VOID_RET _GL_VOID GLint GLint i2 _GL_VOID_RET _GL_VOID GLint j _GL_VOID_RET _GL_VOID GLfloat param _GL_VOID_RET _GL_VOID GLint param _GL_VOID_RET _GL_VOID GLdouble GLdouble GLdouble GLdouble GLdouble zFar _GL_VOID_RET _GL_UINT GLdouble *equation _GL_VOID_RET _GL_VOID GLenum GLint *params _GL_VOID_RET _GL_VOID GLenum GLfloat *v _GL_VOID_RET _GL_VOID GLenum GLfloat *params _GL_VOID_RET _GL_VOID GLfloat *values _GL_VOID_RET _GL_VOID GLushort *values _GL_VOID_RET _GL_VOID GLenum GLfloat *params _GL_VOID_RET _GL_VOID GLenum GLdouble *params _GL_VOID_RET _GL_VOID GLenum GLint *params _GL_VOID_RET _GL_VOID GLsizei stride
T * resize(size_t newsize)
#define CCL_NAMESPACE_END
depth_tx normal_tx diffuse_light_tx specular_light_tx volume_light_tx environment_tx ambient_occlusion_tx aov_value_tx in_weight_img image(1, GPU_R32F, Qualifier::WRITE, ImageType::FLOAT_2D_ARRAY, "out_weight_img") .image(3
static const char * to_string(const Interpolation &interp)
ccl_gpu_kernel_postfix ccl_global float int num_pixels
ccl_gpu_kernel_postfix ccl_global float int int int int float bool int offset
ccl_device_inline float average(const float2 &a)
static bool parse_channels(const ImageSpec &in_spec, vector< MergeImageLayer > &layers, string &error)
static bool parse_channel_name(string name, string &renderlayer, string &pass, string &channel, bool multiview_channels)
static bool split_last_dot(string &in, string &suffix)
static bool merge_pixels(const vector< MergeImage > &images, const ImageSpec &out_spec, const unordered_map< string, SampleCount > &layer_samples, array< float > &out_pixels, string &error)
static bool open_images(const vector< string > &filepaths, vector< MergeImage > &images, string &error)
static void merge_layer_render_time(ImageSpec &spec, const vector< MergeImage > &images, const string &layer_name, const string &time_name, const bool average)
static void alloc_pixels(const ImageSpec &spec, array< float > &pixels)
static void merge_channels_metadata(vector< MergeImage > &images, ImageSpec &out_spec)
static void merge_render_time(ImageSpec &spec, const vector< MergeImage > &images, const string &name, const bool average)
static void read_layer_samples(vector< MergeImage > &images, unordered_map< string, SampleCount > &layer_samples)
static MergeChannelOp parse_channel_operation(const string &pass_name)
static bool save_output(const string &filepath, const ImageSpec &spec, const array< float > &pixels, string &error)
static void error(const char *str)
bool remove(void *owner, const AttributeIDRef &attribute_id)
std::unique_ptr< IDProperty, IDPropertyDeleter > create(StringRefNull prop_name, int32_t value)
Allocate a new IDProperty of type IDP_INT, set its name and value.
T distance(const T &a, const T &b)
static const pxr::TfToken out("out", pxr::TfToken::Immortal)
static void unique_name(bNode *node)
bool string_startswith(const string_view s, const string_view start)
CCL_NAMESPACE_BEGIN string string_printf(const char *format,...)
bool string_endswith(const string_view s, const string_view end)
vector< MergeImagePass > passes
vector< MergeImageLayer > layers
unique_ptr< ImageInput > in
static void initialize(SubdivDisplacement *displacement)
double time_human_readable_to_seconds(const string &time_string)
string time_human_readable_from_seconds(const double seconds)