2
$\begingroup$

In Blender's Python, I can get the raw bytes that make up an image into a bytearray, like so:

    image = bpy.data.images[0]  # Pick any valid image you like.
    pixels = bytearray([int(p * 255) for p in image.pixels])

But, instead of raw pixel data, I would like to get a bytearray containing all the bytes of a PNG-format image. This can be compressed or uncompressed PNG, but I need the bytes to represent a valid PNG format as if for saving to a file, not raw pixel data. Ideally I'd like to get these bytes without actually saving them to disk, and without modifying the user's image if possible (for example they may think the image is a JPG or something). How can I do this? Does Blender have a PNG data format helper function that I could use without disturbing the user's image? If not, is there a code snippet that will convert Blender's internal image representation to PNG format?

$\endgroup$
  • $\begingroup$ It involves understanding of Blender's internal representation of images, right? I'm asking if Blender has a built-in convert-to-PNG-format helper, or if not then the answer will have to transform the data from Blender's own internal format. $\endgroup$ – emackey Sep 1 '16 at 15:10
  • $\begingroup$ I added these aspects of the question to the original via an edit. $\endgroup$ – emackey Sep 1 '16 at 15:16
3
$\begingroup$

I got this working based on Python code from Stack Overflow here.

Here's the version of that code adapted to work inside stock Blender 2.77a:

import base64
import zlib
import struct

def img_to_png(blender_image):
    width = blender_image.size[0]
    height = blender_image.size[1]
    buf = bytearray([int(p * 255) for p in blender_image.pixels])

    # reverse the vertical line order and add null bytes at the start
    width_byte_4 = width * 4
    raw_data = b''.join(b'\x00' + buf[span:span + width_byte_4]
                        for span in range((height - 1) * width_byte_4, -1, - width_byte_4))

    def png_pack(png_tag, data):
        chunk_head = png_tag + data
        return (struct.pack("!I", len(data)) +
                chunk_head +
                struct.pack("!I", 0xFFFFFFFF & zlib.crc32(chunk_head)))

    png_bytes = b''.join([
        b'\x89PNG\r\n\x1a\n',
        png_pack(b'IHDR', struct.pack("!2I5B", width, height, 8, 6, 0, 0, 0)),
        png_pack(b'IDAT', zlib.compress(raw_data, 9)),
        png_pack(b'IEND', b'')])

    return 'data:image/png;base64,' + base64.b64encode(png_bytes).decode()

Example usage:

print(img_to_png(bpy.data.images[0]))

This will print out a PNG-format data-URI of the image, regardless of what format the image is within Blender, and without altering the user's image. I suggest using a very small image to test with, so the test output is not overwhelming when printed to the console.

$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.