2
$\begingroup$

I'm working on Python script to apply textures to objects. I need to load an image that was created using PIL via img_from_array = Image.fromarray(ARRAY). Is it possible?

$\endgroup$
1
  • 1
    $\begingroup$ If you want to load from PNG/JPEG/etc data, see this. If you want to load from a pixel array, use img.pixels.foreach_set(data) (and do img.pack() afterwards). $\endgroup$
    – scurest
    Mar 23, 2022 at 8:08

1 Answer 1

4
$\begingroup$

EDIT: From a comment on the answer, it is clear that one more step is needed. EDIT 2: From another comment, there's apparently a better pil_to_image function.

Since you are starting from a PIL image, you need to convert the image to a Blender image. To do that, see this answer from which I take this function and modified it according to comments on this answer.

def pil_to_image(pil_image, name='NewImage', alpha=False):
    '''
    PIL image pixels is 2D array of byte tuple (when mode is 'RGB', 'RGBA') or byte (when mode is 'L')
    bpy image pixels is flat array of normalized values in RGBA order
    '''
    now = time.time()
    # setup PIL image conversion
    width = pil_image.width
    height = pil_image.height
    byte_to_normalized = 1.0 / 255.0
    # create new image
    bpy_image = bpy.data.images.new(name, width=width, height=height, alpha=alpha)

    # convert Image 'L' to 'RGBA', normalize then flatten 
    bpy_image.pixels.foreach_set((np.asarray(pil_image.convert('RGBA'),
                                             dtype=np.float32)
                                  * byte_to_normalized).ravel())
    bpy_image.pack()
    print("pil_to_image completed in",time.time() - now,"s")
    return bpy_image

Once you've done that, the key is that images are stored in bpy.data.images and that you use them in texture images by assigning the image to a field in the texture.

Given your comment and the above function you need something like

bpy_image = pil_to_image(img_from_array, name=WHATEVER_NAME_YOU_WANT)

to perform that conversion

Here's an example that adds the image created above to the active object. It's a bit of overkill, as it creates the material. It assumes the object has no materials yet, so the new material will go into slot 0 and be applied to the entire object by default. It also assumes you want to be able to map UV coordinates for the texture.

If you have an existing material you need to add an image texture and link it to the shader.

object = bpy.context.active_object

material = bpy.data.materials.new(name=MATERIAL_NAME)
object.data.materials.append(material)
material.use_nodes = True
tree = material.node_tree
nodes = tree.nodes

texcord = nodes.new('ShaderNodeTexCoord')
mapping = nodes.new('ShaderNodeMapping')
teximage = nodes.new("ShaderNodeTexImage")

bsdf = nodes["Principled BSDF"]

tree.links.new(texcord.outputs[2], mapping.inputs[0])
tree.links.new(mapping.outputs[0], teximage.inputs[0])
tree.links.new(teximage.outputs[0], bsdf.inputs[0])

teximage.image = bpy_image
$\endgroup$
15
  • $\begingroup$ Thanks for your explanation. Unfortunately I still don't get it. $\endgroup$
    – wooh
    Mar 23, 2022 at 18:36
  • $\begingroup$ In my scenario image is generated using Python PIL: img_from_array = Image.fromarray(ARRAY). And I want to assign this. Originally my script was: face = "FRONT" mat = bpy.data.materials.new(name=face) mat.use_nodes = True bsdf = mat.node_tree.nodes["Principled BSDF"] texImage = mat.node_tree.nodes.new('ShaderNodeTexImage') texImage.image = bpy.data.images.load(PNG_FILE) but now I need instead of loading PNG_FILE assign img_from_array $\endgroup$
    – wooh
    Mar 23, 2022 at 18:43
  • 1
    $\begingroup$ pixels.foreach_set(data) was added in 2.83 and is faster than the pixels[:] = data. You also need to pack the image with .pack() afterwards, otherwise the image is not "saved" (see what happens if you close and reopen Blender). $\endgroup$
    – scurest
    Mar 23, 2022 at 20:56
  • 1
    $\begingroup$ Also you need to pass the correct alpha= argument to images.new. This doesn't seem like it does anything, but it also matters for save/reload. $\endgroup$
    – scurest
    Mar 23, 2022 at 21:19
  • 1
    $\begingroup$ Solved. Thank you for help. Issue was not not related with conversion but with vertice definition in my script. $\endgroup$
    – wooh
    Mar 26, 2022 at 22:46

You must log in to answer this question.

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