2

I'm trying to save image buffers as .pngs with blender's python API (but this is mainly just a python question) I'm trying to speed things up by making a thread to save the image, and the function that creates the thread is being called from a callback-handler that is activated whenever the 3D screen is refreshed, here is the full code (its a little messy):

import base64, io, os, bgl, gpu, bpy, threading, time, sys
import numpy as np
from gpu_extras.presets import draw_texture_2d
from PIL import Image
import multiprocessing.pool as mpool

finalPath = bpy.context.scene.render.filepath + "hithere.png"


WIDTH = 1920
HEIGHT = 1080



offscreen = gpu.types.GPUOffScreen(WIDTH, HEIGHT)

def draw2():
    global finalPath
    global array
    global WIDTH
    global HEIGHT
    global needsSaving
    context = bpy.context
    scene = context.scene

    view_matrix = scene.camera.matrix_world.inverted()

    projection_matrix = scene.camera.calc_matrix_camera(
        context.depsgraph, x=WIDTH, y=HEIGHT)

    offscreen.draw_view3d(
        scene,
        context.view_layer,
        context.space_data,
        context.region,
        view_matrix,
        projection_matrix)

    bgl.glDisable(bgl.GL_DEPTH_TEST)
    draw_texture_2d(offscreen.color_texture, (0, -125), WIDTH, HEIGHT)

    buffer = bgl.Buffer(bgl.GL_BYTE, WIDTH * HEIGHT * 4)
    bgl.glReadBuffer(bgl.GL_BACK)
    bgl.glReadPixels(0, -125, WIDTH, HEIGHT, bgl.GL_RGBA, bgl.GL_UNSIGNED_BYTE, buffer)


    needle = threading.Thread(target=saveIt,args=[buffer, finalPath, WIDTH, HEIGHT])
    needle.daemon = True
    needle.start()
  ####  thread.start_new_thread(saveIt,(buffer, finalPath, WIDTH, HEIGHT))



def coby(scene):
    frame = scene.frame_current
    folder = scene.render.filepath
    myFormat = "png"#scene.render.image_settings.renderformat.lower()
    outputPath = os.path.join(folder, "%05d.%s" % (frame, myFormat))
    global finalPath
    finalPath = outputPath



h = bpy.types.SpaceView3D.draw_handler_add(draw2, (), 'WINDOW', 'POST_PIXEL')
bpy.app.handlers.frame_change_pre.clear()
bpy.app.handlers.frame_change_pre.append(coby)


def saveIt(buffer, path, width, height):
    array = np.asarray(buffer, dtype=np.uint8)
    myBytes = array.tobytes()
    im = Image.frombytes("RGBA",(width, height), myBytes)
    rawBytes = io.BytesIO()
    im.save(rawBytes, "PNG")
    rawBytes.seek(0)
    base64Encoded = base64.b64encode(rawBytes.read())
    txt =  "data:image/png;base64," + base64Encoded.decode()
    f = open(finalPath, "wb")
    f.write(base64.decodebytes(base64Encoded))
    f.close()

it actually does work, except when I play the timeline in blender (which calls the frame_pre callback and also the 3D view-refresh callback (not sure in which order though), most of my images are replaced, except some are not, as can be seen in this screenshot: [![enter image description here][1]][1]

I originally had all blue-planed images, then I ran the script through the thread, and it replaced almost all of them, except some of the blue-planed images still remain (seemingly at a random interval). This worked fine if I call .join() right after I make the thread, or don't use the thread at all, but seemingly the thread is the only way to make it work a lot faster.

I've been looking around for how to use threads with queues and pooling (How to use python multiprocessing Pool.map within loop, What happened to thread.start_new_thread in python 3, How can I make a background, non-blocking input loop in python?, Creating Threads in python),

SO: Why aren't all of the threads finishing? [1]: https://i.sstatic.net/nJCwH.png .................

0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.