vijaym123
vijaym123

Reputation: 343

Pickleable Image Object

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?

Upvotes: 11

Views: 6311

Answers (3)

idbrii
idbrii

Reputation: 11946

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.

Upvotes: 0

gak
gak

Reputation: 32783

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().

Upvotes: 18

John La Rooy
John La Rooy

Reputation: 304403

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)

Upvotes: 9

Related Questions