4
$\begingroup$

I need a Python script to copy a channel of one image to the alpha channel of another. I already created one, but it uses the approach of copying from one image pixel array to the other:

def copy_alpha(from_image, to_image):
    to_pixels = list(to_image.pixels)
    from_pixels = list(from_image.pixels)
    for i in range(0, len(to_pixels), 4):
        to_pixels[i+3] = from_pixels[i]
    to_image.pixels = to_pixels

It works, but gets slow using very high res images. My idea would be to do this operation via GPU instead. A while ago I created a GPU based channel mixer in Unity using a shader and I was wondering, if something like that could work in Blender as well.

I realized there is the standalone GPU Shader Module and the GPU Utilities module, but I couldn't find a proper function to handle this task.

I also want to avoid using the compositor, because my script must work in situations where there is no compositor used in the Blender file. And setting up a temporary compositor node tree via Python to handle this task seems like a very dirty solution to me.

What would be a good approach to send two images in Python to the GPU/to a shader so channels can be merged on the GPU?

$\endgroup$
1
  • 1
    $\begingroup$ To do that on GPU simply use Separate RGB node and then use one of its outputs as alpha. I don't think it makes any sense to send data to GPU in order to download it back from GPU, as you can just read the channel on CPU and save it on another channel. In both cases the CPU reads the channel once [in GPU solution in order to send it to GPU] and writes to a channel once [in GPU solution when reading from the GPU backbuffer] $\endgroup$ Commented Feb 28, 2022 at 15:56

1 Answer 1

4
+50
$\begingroup$

This is not a direct answer to the question since it doesn't leverage the GPU module. However you can get decent results with numpy which is the closest you'll get to using the efficient underlying C code base.

On a 10k*10k image (100Mpx) it took 1.5 seconds on my low-to-mid range computer. It would hang for several minutes with the code in the question.

import numpy as np

def copy_alpha(from_image, to_image):
    pixels_from = np.empty(shape=len(from_image.pixels), dtype=np.float32)
    from_image.pixels.foreach_get(pixels_from)
    pixels_to = np.empty(shape=len(from_image.pixels), dtype=np.float32)
    to_image.pixels.foreach_get(pixels_to)

    # Begin at index 3 (alpha value of 1st pixel), and iterate every 4 channels
    pixels_to[3::4] = pixels_from[3::4]

    to_image.pixels.foreach_set(pixels_to)

You may need to find a way to refresh the UI afterwards to update its display.

$\endgroup$
3
  • $\begingroup$ It seems amazing how fast it is $\endgroup$
    – Noob Cat
    Commented Mar 2, 2022 at 2:35
  • $\begingroup$ @NoobCat yeah, numpy and foreach_get / set are pretty awesome. :) $\endgroup$
    – Gorgious
    Commented Mar 2, 2022 at 6:30
  • $\begingroup$ Wow, awesome! While it's not a direct answer to the question it addresses exactly the problem that resulted in the question being asked, which is the poor performance of the copy process. So I'll accept it still! $\endgroup$
    – narranoid
    Commented Mar 4, 2022 at 17:49

You must log in to answer this question.

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