Fix #155982: Cycles denoise animation operator fails for multi-part EXR

Support reading multi-part EXR in this code, and add a test. Also fixes
the denoise_animation test being disabled.

Regression from be1619f4ad.

Pull Request: https://projects.blender.org/blender/blender/pulls/156040
This commit is contained in:
Brecht Van Lommel 2026-03-23 11:43:42 +01:00 • committed by Brecht Van Lommel
parent ec8c605537
commit a78585b00d
11 changed files with 73 additions and 22 deletions

View file

@ -465,6 +465,50 @@ bool DenoiseImage::read_previous_pixels(const DenoiseImageLayer &layer,
return true;
}
bool DenoiseImage::read_pixels(ImageInput *in)
{
/* For multi-part EXR each subimage contains a pass, so we'll read them all. */
const int num_subimages = in_spec.get_int_attribute("oiio:subimages", 1);
vector<string> channelnames;
vector<int> num_channels_subimage;
for (int s = 0; s < num_subimages; s++) {
const ImageSpec spec = in->spec(s);
num_channels_subimage.push_back(spec.nchannels);
for (const string &name : spec.channelnames) {
channelnames.push_back(name);
}
}
num_channels = int(channelnames.size());
pixels.resize(size_t(width) * size_t(height) * num_channels);
/* Read all channels from each subimage. */
const int64_t xstride = int64_t(num_channels) * sizeof(float);
size_t channel_offset = 0;
for (size_t s = 0; s < num_channels_subimage.size(); s++) {
if (!in->read_image(s,
0,
0,
num_channels_subimage[s],
TypeDesc::FLOAT,
pixels.data() + channel_offset,
xstride))
{
return false;
}
channel_offset += num_channels_subimage[s];
}
/* Update in_spec to reflect the flattened channel list for use in parse_channels. */
in->seek_subimage(0, 0);
in_spec.channelnames = channelnames;
in_spec.nchannels = num_channels;
in_spec.channelformats.clear();
in_spec.format = TypeDesc::FLOAT;
return true;
}
bool DenoiseImage::load(const string &in_filepath, string &error)
{
if (!Filesystem::is_regular(in_filepath)) {
@ -481,7 +525,11 @@ bool DenoiseImage::load(const string &in_filepath, string &error)
in_spec = in->spec();
width = in_spec.width;
height = in_spec.height;
num_channels = in_spec.nchannels;
if (!read_pixels(in.get())) {
error = "Failed to read image: " + in_filepath;
return false;
}
if (!parse_channels(in_spec, error)) {
return false;
@ -492,16 +540,6 @@ bool DenoiseImage::load(const string &in_filepath, string &error)
return false;
}
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 (!in->read_image(0, 0, 0, num_channels, TypeDesc::FLOAT, pixels.data())) {
error = "Failed to read image: " + in_filepath;
return false;
}
return true;
}

View file

@ -134,6 +134,10 @@ class DenoiseImage {
* fill layers and set up the output channels and passthrough map. */
bool parse_channels(const ImageSpec &in_spec, string &error);
/* Read pixels from an open ImageInput into the pixels buffer.
* Updates in_spec, num_channels, and pixels. */
bool read_pixels(ImageInput *in);
void close_input();
};

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:85112b0a7f123fa09809e0b6164331e40ff788cf78f91260809a20799501c1a6
size 24531
oid sha256:e5d6c222c00115583b7e893c3525bdbda837311db39561c3e506d200d77c7b13
size 22554

View file

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:e5d6c222c00115583b7e893c3525bdbda837311db39561c3e506d200d77c7b13
size 22554

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:79f308618c8a72ccf25f3650564372fb4e1007b1ed3b554d800545304298fbdc
size 30455
oid sha256:154920bfd7e39096fd49bc75fd9a2e092f756c13fb19d425cff9465bd02563f4
size 22481

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:0155d0a2a9150c01e4aee55877783ae2b95cc06052b5b2f38325e3377ea3dfef
size 412358
oid sha256:b8161abd02d8a464c030d2b37a470fda2f97f920d287f129c49adf95780c6ed2
size 157999

View file

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:bfe1afee3a6601bb8d855a0493f849f372ba8652d4972bd2749b9ff51212f68b
size 143992

View file

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:eb3a0acdfb0a8f5d40f10da685214c57a382d29697fcc1f6765e95eb3a5994a2
size 163475

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:733c1365516e032bae340d310dc3d8a39b584ddd6de172dbbf93506defbd4073
size 412358
oid sha256:1312b3ebe11763287b25bad11ca219000ffddb78111417e150f15e5532c3cf02
size 157999

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:e89d6367d1f2ac244b15f75425d0b5fc1c27af34acaf40483b80bd3bc32c26e8
size 412559
oid sha256:0959a36f703726f505c672cedac97d73256f3e39f97d584a6fd4f25191779087
size 157977

View file

@ -851,7 +851,7 @@ if((WITH_CYCLES OR WITH_GPU_RENDER_TESTS) AND TEST_SRC_DIR_EXISTS)
endif()
if(WITH_OPENIMAGEDENOISE)
list(APPEND render_tests denoise)
list(APPEND render_tests denoise denoise_animation)
endif()
# Disabled until new OpenPGL version with deterministic results.