Page MenuHome

Contents of "Viewer Node" image block do not get updated
Confirmed, NormalPublicKNOWN ISSUE


This might not necessarily be a bug, but may be a feature request. I apologize in advance for being very unprofessional here. I don't think my problem is necessarily relevant to the way I build Blender of the system hardware I use. I've seen the same problem over and over on different systems. Anyways, I compile Blender from source as follow on a freshly-installed Ubuntu:

Here's how I set up Ubuntu after installing it:

apt-get -y update && apt-get -y install locales \
                                            make \
					    dpkg \
                                            wget \
                                            bzip2 \
                                            libglib2.0-0 \
                                            libxext6 \
                                            libsm6 \
                                            libxrender1 \
                                            g++ \
                                            gcc \
                                            xvfb \
                                            libyaml-cpp-dev \
					    git \
                                            cmake \
					    vim \
					    curl \
					    ca-certificates \
                                            software-properties-common \
					    python3 \
git clone
cd blender
git checkout 8ef39d5c882896bd75e0d4d17fb3e3d4710fc768 # Blender 2.79
git submodule update --init --recursive
git submodule foreach git checkout master
git submodule foreach git pull --rebase origin master

blender/build_files/build_environment/ --source=/blender-git/

PPATH="$(which python3)"
cmake blender \
    -DCMAKE_INSTALL_PREFIX=/usr/lib/python3/dist-packages \
    -DPYTHON_SITE_PACKAGES=/usr/lib/python3/dist-packages \
make -j 7
make install

I want to do a very simple thing but it seems that it's becoming very complicated to do it. I have described what my problem is here. I do not necessarily want to simulate clicking but it turns out that I cannot update the contents of 'Viewer Node' image block. Finally, I thought the best way would be to automatically have backdrop enabled. So I followed the instructions given here by Sebastian Koch thinking that I should be able to dynamically add/remove view nodes and update the contents of the image block automatically. Although I could access to the pixels, but But this does not happen!
I wrote the right code to do exactly what I show in this video but the pixels in "Viewer Node" image block do not get updated, as they do in the video. Not only that, I tried activating newly-added Viewer nodes manually (as described here) but I am still unsuccessful in updating the pixels in the image block. I also tried removing the image block in Python and then continue adding new Viewer nodes. But from the time I remove the image block its contents will always be zero even if I redo the rendering.

The only way I could successfully update the contents of the image block was after, first, I followed Sebastian Koch's solution and then wrote some code to remove/add all nodes and their links to get either depth map or surface Normals and did the rendering again. But the whole point of me trying to store pixels into Numpy arrays was to avoid doing re-rendering to get around the IO overhead. I could have instead stored two files (for both depth map and Normal maps) throught two Output nodes with rendering only once.

First, if this behavior does not look like a bug, I would appreciate the community provide me an easy solution to do what I want. Second, It would be great if you can improve Blender's Python API and add ways of easily accessing rendering results in memory so that people like me who want to do millions of rendering do not need to deal with the delays cause by IO to transfer rendering results from memory to hard drive and can easily store the results into Numpy arrays and store them on disk in chunks.

Event Timeline

Amir (Warrior) updated the task description. (Show Details)
Amir (Warrior) renamed this task from Unable to update the contents of "Viewer Node" image block to Contents of "Viewer Node" image block do not get updated.Mar 14 2018, 7:34 AM
Amir (Warrior) renamed this task from Contents of "Viewer Node" image block do not get updated to Contents of "Viewer Node" image block do not get updated even with backdrop on.
Amir (Warrior) renamed this task from Contents of "Viewer Node" image block do not get updated even with backdrop on to Contents of "Viewer Node" image block do not get updated .

When a composite node tree has two viewer nodes, clicking one of them will update the Viewer Node image shown in the image editor which is also used for the node editor backdrop. I don't see a way to do this update from python (with or without a UI).

I would expect that setting a node as selected and active and then tagging the node tree for an update and doing a scene.update, that the viewer node output would get updated. This is not the case and I don't see a way to make the viewer image update.

So the following lines will select and make a viewer node active but even using the GUI, you still need to manually click the node to get the viewer output to update. So there is an extra update step performed when selecting a node in the GUI that doesn't get run in a scene update.

v1 = bpy.context.scene.node_tree.nodes['Viewer'] = True = v1

If this is a bug, it would be that scene.update() doesn't refresh composite node tree data or specifically viewer node data. Otherwise it is a lack of access to update the viewer image data.

Philipp Oeser (lichtwerk) lowered the priority of this task from 90 to Normal.

Interesting problem.

Taking a step back, issues seem to be

  • lack of pixel access for Render Result [including Passes]. This is a known limitation (see also T53768)
  • lack of really updating the Viewer Node (without rerendering) from python

regarding the second issue:
I've had a quick look and there seem to be two node flags involved to get this going (NODE_ACTIVE and NODE_DO_OUTPUT)
The second one is only set from ED_node_set_active(), so only in the Editor (by selecting the node), not by python / RNA.

I've also tweaked the code here (copied over stuff from ED_node_set_active() to nodeSetActive(), so it gets done from python / RNA -- but thats probably a bad idea as well with consequences that I need to investigate further).
If you do this and update properly, then the compositor will actually really start recalculating and the "Viewer Node" image will update eventually.
That doesnt get you too far though, because as @Jeroen Bakker (jbakker) answered here the compositor is a background job, so reading pixels will only help when that is really finished...

But The answer at this moment is you can’t. The python interpreter needs to be finished in order to start the compositor recalculation. This is a background job and after it is finished you know for certain that the new image has been updated.
If we would have a possibility in the API to find out if the background job of the compositor is running we would be able to start a python thread or modal window to wait for this to happen and then continue the task you want to do.

Long story short: this is not a bug but a known limitation (but an interesting one that would be nice to get out of the way)

Amir (Warrior) added a comment.EditedApr 19 2018, 5:41 PM

@Philipp Oeser (lichtwerk) @Jeroen Bakker (jbakker) Thanks for investigating this more. I also remember at some point I was accessing the pixels directly through "Viewer Node" image block and stored them on disk. What I realized however was the pixels were sort of rotated. I remember the depth map rendering that I got was shown 90 degrees rotated. If possible, please take a look at this too.

I also have a request from you: I would appreciate if you can provide a nice Python API that allows people to easily access the rendering results without having to store the renderings on disk. Maybe bpy.ops.render.render() could take an argument like keepInMemory=True. Then people can access the rendering results through something like renderings = np.array(bpy.ops.render.results). The should also account for having a couple of output nodes. For instance, if in the node editor I have 5 Viewer nodes, renderings should be a 5 x 4 x resolution x resolution tensor.

@Jeroen Bakker (jbakker) Does this fix have anything to do with this issue?

@Amir (Warrior) No. Just tried with 741d7d60ed90

Using python we can change the active compositer viewer node, with = v2 and in python the active value will say that the desired node is active, but this is not visually reflected in the node editor. The viewer node has to be manually clicked to make it visually active which then updates the images['Viewer Node'] data that we want to access.

@Shane Ambler (sambler) I haven't tried this in 2.8x. Did you try it with both 2.79x and 2.8x?

@Amir (Warrior) I checked with a master build before my previous comment. I hadn't tried in 2.79 for a while but just checked and it hasn't changed, not that any fixes will be done to 2.79.

Since I do not intend to work on this soonish, will step down to not block others from working on this, sorry.

Campbell Barton (campbellbarton) changed the subtype of this task from "To Do" to "Known Issue".

Hopefully the issue can be resolved someday, as it slows down batch processing for no reason.

Here's my usual workflow when I need to custom-process Diffuse/Normals/Vector/Freestyle data for many frames of animation at once:

Instead of having several Viewer nodes in Compositor it's enough to have a single Viewer node plus several Switch nodes, and then in Python script:

# 1 - collect data into Lists[numFrames]
for frame in range(0, numFrames):
   # render Image
   bpy.context.scene.node_tree.nodes["Switch1"].check = False
   bpy.context.scene.node_tree.nodes["Switch2"].check = False
   bpy.context.scene.node_tree.nodes["Switch3"].check = False
   bpy.context.scene.node_tree.nodes["Switch4"].check = False
   bpy.ops.render.render(write_still=False)['Viewer Node'].pixels.foreach_get(images[frame])
   # get depth
   bpy.context.scene.node_tree.nodes["Switch1"].check = True
   bpy.ops.render.render(write_still=False)['Viewer Node'].pixels.foreach_get(depths[frame])
   # get normal
   bpy.context.scene.node_tree.nodes["Switch2"].check = True
   bpy.ops.render.render(write_still=False)['Viewer Node'].pixels.foreach_get(normals[frame])
   # get vector
   bpy.context.scene.node_tree.nodes["Switch3"].check = True
   bpy.ops.render.render(write_still=False)['Viewer Node'].pixels.foreach_get(vectors[frame])
   # get Freestyle overlay
   bpy.context.scene.node_tree.nodes["Switch4"].check = True
   bpy.ops.render.render(write_still=False)['Viewer Node'].pixels.foreach_get(freestyles[frame])
# 2 - process the data
result = processAllFramesAndLayers(images, depths, normals, vectors, freestyles, numFrames, imageWidth, imageHeight)
myOutputEXRImage.save_render(filename + ".exr"))

If it was possible to just call something like compositor.refresh() instead of bpy.ops.render.render(), this loop would finish 5 times faster.

Since python thread is already able to wait for bpy.ops.render.render() completion event, why cannot it similarly wait for compositor.refresh() completion?

oh man, what a show stopping problem. Can anyone recommend a good python package for controlling the mouse that works on windows, mac and linux without the user having to grant any permissions at OS level?

If this bug got fixed it would greatly benefit my VisionBlender add-on: