21
$\begingroup$

Render Result exists in bpy.data.images["Render Result"] but it contains no data.

Also bpy.data.images["Render Result"].size[0] is 0, even if the render shows as an image in the UV/Image Editor with a width of 1366.

Maybe the render result cannot be accessed as an image or I missing some update or refresh call somewhere but I can't find such a thing in the 2.68 API documentation.

Any tip in the correct direction appreciated.

$\endgroup$
1
  • 2
    $\begingroup$ What you could do is save the image to a RAM disk. That is a virtual disk on your computer that has a certain amount of memory allocated on the RAM of the computer. This way it still keeps everything in memory really. IO operations on RAM are way way faster than disk operations. This might keep things simple, with respect to scripting. I use ImDisk Toolkit. $\endgroup$ Apr 9, 2020 at 7:10

6 Answers 6

15
$\begingroup$

unfortunately accessing render results cannot be done via bpy.data.images['Render Result'].

There is a workaround by using Viewer node. basically you will need to attach a Viewer node to your render layer and then access pixels of the Viewer.

see this link: https://ammous88.wordpress.com/2015/01/16/blender-access-render-results-pixels-directly-from-python-2/

$\endgroup$
4
  • $\begingroup$ Give me some time to try it and see if it works better than my proposed solution. $\endgroup$ Jan 16, 2015 at 22:40
  • $\begingroup$ The viewer node trick worked and saves me from having to create a temp file on disk. In my case, I have customized nodes and cannot lose them so I'm not doing the node clear step. I created the Viewer Node in the editor not in code, but the important thing here is that sending the output to the Viewer Node works. $\endgroup$ Jan 18, 2015 at 1:47
  • $\begingroup$ indeed it does the job $\endgroup$
    – wsfax
    Jan 18, 2015 at 23:56
  • $\begingroup$ the only drawback is that pixels will always contain rgba even if you set your output to be rgb $\endgroup$
    – wsfax
    Jan 19, 2015 at 0:01
6
$\begingroup$

Answering myself, if somebody find a better solution please answer and I will change my accepted answer.

The only reliable way to access the render result pixels is this:

  1. Save the actual value of bpy.data.scenes["Scene"].render.filepath to a local variable

  2. Set bpy.data.scenes["Scene"].render.filepath to something to avoid overwriting any previous render, for example "//temp." + str(time.time()) + ".png". 1 and 2 can be ignored if you don't care of any previous render.

  3. Call bpy.ops.render.render(write_still=True). write_still set to true will cause the file to be immediately written to disk.

  4. Create a new image and load that file from disk, or use an existing image reserved for this purpose in your blend file and set its filepath and then reload.

  5. Do what you need to do with the loaded image pixels.

  6. Remove the loaded image, except you have a good reason to keep it in the blend file.

  7. Restore the original value of bpy.data.scenes["Scene"].render.filepath

Brief: the only reliable way to access pixel data of a render is to render it, save to disk immediately, recover from disk, read pixel data.

At the time of writing this, Blender version is 2.68.

$\endgroup$
5
$\begingroup$

Last time I experimented with this, it was necessary to save the content of Render Result as an image to disk and then load it as a new image. Then the pixel array is accessible. Maybe this API feature can be improved, or i'm doing it wrong :)

import bpy

render_result = next(image for image in bpy.data.images if image.type == "RENDER_RESULT")
if render_result is None:
    print("Render result not found!")
else:
    render_result.save_render("somefile.tga")
    r2 = bpy.data.images.load("somefile.tga")
    r2.pixels[0] = 0.2345
$\endgroup$
3
  • $\begingroup$ Thank you. Indeed, pixel data is accessible if the image has a file path and is saved first. But the render I want to read from is generated by the script (by calling bpy.ops.render()). If I render with F12, assign a file path to the render result and save, the next time I call bpy.ops.render() Blender generated a new image called "Render Result.001", It does not use the "Render Result" image any more. $\endgroup$ Aug 6, 2013 at 21:09
  • 1
    $\begingroup$ yeah, i'm going to avoid suggesting that you keep track of All images with string "Render Result" and sort them and access the last index -1, perhaps someone else can offer enlightenment. $\endgroup$
    – zeffii
    Aug 6, 2013 at 21:14
  • $\begingroup$ it can be solved by using render_result = next(image for image in bpy.data.images if image.type == "RENDER_RESULT") $\endgroup$
    – Iyad Ahmed
    Aug 27 at 23:56
4
$\begingroup$

A solution without heavy workarounds for 2.8+ in case someone is searching for this now

import bgl
import bpy
import gpu
import numpy as np


# Find render result
render_result = next(image for image in bpy.data.images if image.type == "RENDER_RESULT")

# Create a GPU texture that shares GPU memory with Blender
gpu_tex = gpu.texture.from_image(render_result)

# Read image from GPU
gpu_tex.read()

# OR read image into a NumPy array (might be more convenient for later operations)
fbo = gpu.types.GPUFrameBuffer(color_slots=(gpu_tex,))

buffer_np = np.empty(gpu_tex.width * gpu_tex.height * 4, dtype=np.float32)
buffer = bgl.Buffer(bgl.GL_FLOAT, buffer_np.shape, buffer_np)
with fbo.bind():
    bgl.glReadBuffer(bgl.GL_BACK)
    bgl.glReadPixels(0, 0, gpu_tex.width, gpu_tex.height, bgl.GL_RGBA, bgl.GL_FLOAT, buffer)

# Now the NumPy array has the pixel data, you can reshape it and/or export it as bytes if you wish
print(buffer_np)
$\endgroup$
2
  • $\begingroup$ prints [1. 0. 1. 1.] (2x2 array) in my case :( $\endgroup$
    – grabantot
    Aug 27 at 19:39
  • $\begingroup$ sorry it is like this, I have to update it to 3.6 I guess, will do asap :( $\endgroup$
    – Iyad Ahmed
    Aug 27 at 23:16
2
$\begingroup$

Yet another workaround is to bake to an Image type texture, as long as you are not after the rendered result from the camera but the rendered surface of an object.

Then you can access the image pixels from Python like any other.

$\endgroup$
1
  • 1
    $\begingroup$ If memory serves me, what I was trying to do is using a 3D model to render different camera angles to a sprite sheet, because I don't know a way to copy a portion of an image from one image to another using only the Blender Python API and standard python APIs without adding something else to the mix, I needed to read pixels from the render result to copy a part of the render to the sprite sheet png (pixel by pixel, performance was not important). Yes, I needed the camera transformations. But thank you for the info. $\endgroup$ Jul 5, 2017 at 22:56
1
$\begingroup$

This might help..

RENDERING :

bpy.data.scenes['Scene'].render.filepath='file1' # directory and name
bpy.data.scenes['Scene'].render.resolution_x=width
bpy.data.scenes['Scene'].render.resolution_y=height
bpy.data.scenes['Scene'].render.pixel_aspect_x=1.0
bpy.data.scenes['Scene'].render.pixel_aspect_y=1.0

IMAGES :

image=bpy.data.images['image02'] # image02 as seen in uv editor
imageR=bpy.data.images['Render Result'] # useless, so bad
width=image.size[0]
height=image.size[1]

PIXELS=[0.0 for i in range(len(image.pixels))]
# len(image.pixels) == width * height * 3 ( or 4 with the alpha channel )

# here, work with PIXELS
image.pixels=PIXELS
$\endgroup$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .