Pickleable Image Object
Asked Answered
I

3

11

How do I create a pickleable file from a PIL Image object such that you could save those images as a single pickle file then maybe upload to another computer such as a server running PIL and unpickle it there?

Incurve answered 12/4, 2012 at 5:49 Comment(0)
B
18

You can convert the Image object into data then you can pickle it:

image = {
    'pixels': im.tostring(),
    'size': im.size,
    'mode': im.mode,
}

And back to an Image:

im = Image.fromstring(image['mode'], image['size'], image['pixels'])

NOTE: As astex mentioned, if you're using Pillow (which is recommended instead of PIL), the tostring() method is deprecated for tobytes(). Likewise with fromstring() for frombytes().

Bumgarner answered 12/4, 2012 at 5:51 Comment(1)
Image.tostring() is now deprecated in Pillow in favor of Image.tobytes(). For the sake of posterity, it may be better to change the above (or at least leave a note).Parasang
S
9

Slight variation of Gerald's answer using keyword args

create pickleable object

image = {'data': im.tostring(), 'size':im.size, 'mode':im.mode}

or

image = dict(data=im.tostring(), size=im.size, mode=im.mode)

unpickle back to image

im = Image.fromstring(**image)
Sogdian answered 12/4, 2012 at 6:25 Comment(2)
Does this mean that I don't need to store images in my application? I can just use the string that they serialize to and just hardcode it inside the application when I want to use the image?Camera
You technically could do that, but it's far better to keep it as an actual file... in either case it can be versioned along with your code, but having images kept as images means you can readily view them and know what media you have stored. If you're storing images as code, you're artificially inflating the size of your source files, making them more onerous to view and search, and making your media much harder to manage should you want to view and/or change that image.Vitriform
F
0

From related question Saving a dataset with ImagingCore objects pickle, but seems helpful to include here to expand on other answers.

The other answers are great for how to pull pickleable data out of PIL.Image.

Persistence of External Objects in the pickle docs shows how to write your own pickler that can handle custom types. Reworking the sample code for our image case it should be something like this (untested):

import pickle
from PIL import Image  # Assuming Pillow
from collections import namedtuple

# Proxy the PIL.Image by storing the bytes.
ImageProxy = namedtuple("ImageProxy", "pixels, size, mode")


class PilPickler(pickle.Pickler):
    def persistent_id(self, obj):
        if isinstance(obj, Image):
            # Create our proxy that (I think) will get pickled instead of a PIL object.
            return ImageProxy(
                pixels=obj.tobytes(),
                size=obj.size,
                mode=obj.mode,
            )
        else:
            # Fallback to default pickle.
            return None


class PilUnpickler(pickle.Unpickler):
    def persistent_load(self, pid):
        # pid is the object returned by PilPickler.
        if isinstance(pid, ImageProxy):
            return Image.frombytes(pid.mode, pid.size, pid.pixels)
        else:
            # Always raise an error if you cannot return the correct object.
            raise pickle.UnpicklingError("unsupported persistent object")


def main():
    import io
    import pprint

    images = []  # [... make images here ...]

    # Save the records using our custom PilPickler.
    file = io.BytesIO()
    PilPickler(file).dump(images)

    print("Pickled records:")
    pprint.pprint(images)

    # Load the records from the pickle data stream.
    file.seek(0)
    images = PilUnpickler(file).load()

    print("Unpickled records:")
    pprint.pprint(images)


if __name__ == "__main__":
    main()

This custom Pickler solution allows you to keep your existing setup and any PIL.Images inside your data structures will get pickled.

Foxhound answered 12/2, 2024 at 19:30 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.