mirror of
https://github.com/python-pillow/Pillow.git
synced 2025-01-27 17:54:32 +03:00
Merge pull request #673 from wiredfool/pickling
Support for pickling Image objects
This commit is contained in:
commit
a91387e9dd
68
PIL/Image.py
68
PIL/Image.py
|
@ -54,7 +54,7 @@ try:
|
||||||
from PIL import _imaging as core
|
from PIL import _imaging as core
|
||||||
if PILLOW_VERSION != getattr(core, 'PILLOW_VERSION', None):
|
if PILLOW_VERSION != getattr(core, 'PILLOW_VERSION', None):
|
||||||
raise ImportError("The _imaging extension was built for another "
|
raise ImportError("The _imaging extension was built for another "
|
||||||
" version of Pillow or PIL")
|
" version of Pillow or PIL")
|
||||||
|
|
||||||
except ImportError as v:
|
except ImportError as v:
|
||||||
core = _imaging_not_installed()
|
core = _imaging_not_installed()
|
||||||
|
@ -455,7 +455,7 @@ def _getscaleoffset(expr):
|
||||||
try:
|
try:
|
||||||
((a, b, c), d, e) = data # full syntax
|
((a, b, c), d, e) = data # full syntax
|
||||||
if (a is stub and b == "__mul__" and isinstance(c, numbers.Number) and
|
if (a is stub and b == "__mul__" and isinstance(c, numbers.Number) and
|
||||||
d == "__add__" and isinstance(e, numbers.Number)):
|
d == "__add__" and isinstance(e, numbers.Number)):
|
||||||
return c, e
|
return c, e
|
||||||
except TypeError:
|
except TypeError:
|
||||||
pass
|
pass
|
||||||
|
@ -565,6 +565,20 @@ class Image:
|
||||||
self.save(file, format)
|
self.save(file, format)
|
||||||
return file
|
return file
|
||||||
|
|
||||||
|
def __eq__(self, other):
|
||||||
|
a = (self.mode == other.mode)
|
||||||
|
b = (self.size == other.size)
|
||||||
|
c = (self.getpalette() == other.getpalette())
|
||||||
|
d = (self.info == other.info)
|
||||||
|
e = (self.category == other.category)
|
||||||
|
f = (self.readonly == other.readonly)
|
||||||
|
g = (self.tobytes() == other.tobytes())
|
||||||
|
return a and b and c and d and e and f and g
|
||||||
|
|
||||||
|
def __ne__(self, other):
|
||||||
|
eq = (self == other)
|
||||||
|
return not eq
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return "<%s.%s image mode=%s size=%dx%d at 0x%X>" % (
|
return "<%s.%s image mode=%s size=%dx%d at 0x%X>" % (
|
||||||
self.__class__.__module__, self.__class__.__name__,
|
self.__class__.__module__, self.__class__.__name__,
|
||||||
|
@ -583,6 +597,26 @@ class Image:
|
||||||
return new
|
return new
|
||||||
raise AttributeError(name)
|
raise AttributeError(name)
|
||||||
|
|
||||||
|
def __getstate__(self):
|
||||||
|
return [
|
||||||
|
self.info,
|
||||||
|
self.mode,
|
||||||
|
self.size,
|
||||||
|
self.getpalette(),
|
||||||
|
self.tobytes()]
|
||||||
|
|
||||||
|
def __setstate__(self, state):
|
||||||
|
Image.__init__(self)
|
||||||
|
self.tile = []
|
||||||
|
info, mode, size, palette, data = state
|
||||||
|
self.info = info
|
||||||
|
self.mode = mode
|
||||||
|
self.size = size
|
||||||
|
self.im = core.new(mode, size)
|
||||||
|
if mode in ("L", "P"):
|
||||||
|
self.putpalette(palette)
|
||||||
|
self.frombytes(data)
|
||||||
|
|
||||||
def tobytes(self, encoder_name="raw", *args):
|
def tobytes(self, encoder_name="raw", *args):
|
||||||
"""
|
"""
|
||||||
Return image as a bytes object
|
Return image as a bytes object
|
||||||
|
@ -643,9 +677,11 @@ class Image:
|
||||||
if self.mode != "1":
|
if self.mode != "1":
|
||||||
raise ValueError("not a bitmap")
|
raise ValueError("not a bitmap")
|
||||||
data = self.tobytes("xbm")
|
data = self.tobytes("xbm")
|
||||||
return b"".join([("#define %s_width %d\n" % (name, self.size[0])).encode('ascii'),
|
return b"".join([
|
||||||
("#define %s_height %d\n"% (name, self.size[1])).encode('ascii'),
|
("#define %s_width %d\n" % (name, self.size[0])).encode('ascii'),
|
||||||
("static char %s_bits[] = {\n" % name).encode('ascii'), data, b"};"])
|
("#define %s_height %d\n" % (name, self.size[1])).encode('ascii'),
|
||||||
|
("static char %s_bits[] = {\n" % name).encode('ascii'), data, b"};"
|
||||||
|
])
|
||||||
|
|
||||||
def frombytes(self, data, decoder_name="raw", *args):
|
def frombytes(self, data, decoder_name="raw", *args):
|
||||||
"""
|
"""
|
||||||
|
@ -791,7 +827,8 @@ class Image:
|
||||||
trns = None
|
trns = None
|
||||||
delete_trns = False
|
delete_trns = False
|
||||||
# transparency handling
|
# transparency handling
|
||||||
if "transparency" in self.info and self.info['transparency'] is not None:
|
if "transparency" in self.info and \
|
||||||
|
self.info['transparency'] is not None:
|
||||||
if self.mode in ('L', 'RGB') and mode == 'RGBA':
|
if self.mode in ('L', 'RGB') and mode == 'RGBA':
|
||||||
# Use transparent conversion to promote from transparent
|
# Use transparent conversion to promote from transparent
|
||||||
# color to an alpha channel.
|
# color to an alpha channel.
|
||||||
|
@ -839,7 +876,8 @@ class Image:
|
||||||
# if we can't make a transparent color, don't leave the old
|
# if we can't make a transparent color, don't leave the old
|
||||||
# transparency hanging around to mess us up.
|
# transparency hanging around to mess us up.
|
||||||
del(new.info['transparency'])
|
del(new.info['transparency'])
|
||||||
warnings.warn("Couldn't allocate palette entry for transparency")
|
warnings.warn("Couldn't allocate palette entry " +
|
||||||
|
"for transparency")
|
||||||
return new
|
return new
|
||||||
|
|
||||||
# colorspace conversion
|
# colorspace conversion
|
||||||
|
@ -866,7 +904,8 @@ class Image:
|
||||||
new_im.info['transparency'] = new_im.palette.getcolor(trns)
|
new_im.info['transparency'] = new_im.palette.getcolor(trns)
|
||||||
except:
|
except:
|
||||||
del(new_im.info['transparency'])
|
del(new_im.info['transparency'])
|
||||||
warnings.warn("Couldn't allocate palette entry for transparency")
|
warnings.warn("Couldn't allocate palette entry " +
|
||||||
|
"for transparency")
|
||||||
else:
|
else:
|
||||||
new_im.info['transparency'] = trns
|
new_im.info['transparency'] = trns
|
||||||
return new_im
|
return new_im
|
||||||
|
@ -982,7 +1021,8 @@ class Image:
|
||||||
if isinstance(filter, collections.Callable):
|
if isinstance(filter, collections.Callable):
|
||||||
filter = filter()
|
filter = filter()
|
||||||
if not hasattr(filter, "filter"):
|
if not hasattr(filter, "filter"):
|
||||||
raise TypeError("filter argument should be ImageFilter.Filter instance or class")
|
raise TypeError("filter argument should be ImageFilter.Filter " +
|
||||||
|
"instance or class")
|
||||||
|
|
||||||
if self.im.bands == 1:
|
if self.im.bands == 1:
|
||||||
return self._new(filter.filter(self.im))
|
return self._new(filter.filter(self.im))
|
||||||
|
@ -1507,9 +1547,10 @@ class Image:
|
||||||
import math
|
import math
|
||||||
angle = -angle * math.pi / 180
|
angle = -angle * math.pi / 180
|
||||||
matrix = [
|
matrix = [
|
||||||
math.cos(angle), math.sin(angle), 0.0,
|
math.cos(angle), math.sin(angle), 0.0,
|
||||||
-math.sin(angle), math.cos(angle), 0.0
|
-math.sin(angle), math.cos(angle), 0.0
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
def transform(x, y, matrix=matrix):
|
def transform(x, y, matrix=matrix):
|
||||||
(a, b, c, d, e, f) = matrix
|
(a, b, c, d, e, f) = matrix
|
||||||
|
@ -1773,9 +1814,8 @@ class Image:
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if self.mode == 'RGBA':
|
if self.mode == 'RGBA':
|
||||||
return self.convert('RGBa') \
|
return self.convert('RGBa').transform(
|
||||||
.transform(size, method, data, resample, fill) \
|
size, method, data, resample, fill).convert('RGBA')
|
||||||
.convert('RGBA')
|
|
||||||
|
|
||||||
if isinstance(method, ImageTransformHandler):
|
if isinstance(method, ImageTransformHandler):
|
||||||
return method.transform(size, self, resample=resample, fill=fill)
|
return method.transform(size, self, resample=resample, fill=fill)
|
||||||
|
|
70
Tests/test_pickle.py
Normal file
70
Tests/test_pickle.py
Normal file
|
@ -0,0 +1,70 @@
|
||||||
|
from tester import *
|
||||||
|
|
||||||
|
from PIL import Image
|
||||||
|
|
||||||
|
|
||||||
|
def helper_test_pickle_file(pickle, protocol=0):
|
||||||
|
im = Image.open('Images/lena.jpg')
|
||||||
|
filename = tempfile('temp.pkl')
|
||||||
|
|
||||||
|
# Act
|
||||||
|
with open(filename, 'wb') as f:
|
||||||
|
pickle.dump(im, f, protocol)
|
||||||
|
with open(filename, 'rb') as f:
|
||||||
|
loaded_im = pickle.load(f)
|
||||||
|
|
||||||
|
# Assert
|
||||||
|
assert_image_completely_equal(im, loaded_im)
|
||||||
|
|
||||||
|
|
||||||
|
def helper_test_pickle_string(pickle, protocol=0, file='Images/lena.jpg'):
|
||||||
|
im = Image.open(file)
|
||||||
|
|
||||||
|
# Act
|
||||||
|
dumped_string = pickle.dumps(im, protocol)
|
||||||
|
loaded_im = pickle.loads(dumped_string)
|
||||||
|
|
||||||
|
# Assert
|
||||||
|
assert_image_completely_equal(im, loaded_im)
|
||||||
|
|
||||||
|
|
||||||
|
def test_pickle_image():
|
||||||
|
# Arrange
|
||||||
|
import pickle
|
||||||
|
|
||||||
|
# Act / Assert
|
||||||
|
for protocol in range(0, pickle.HIGHEST_PROTOCOL + 1):
|
||||||
|
helper_test_pickle_string(pickle, protocol)
|
||||||
|
helper_test_pickle_file(pickle, protocol)
|
||||||
|
|
||||||
|
|
||||||
|
def test_cpickle_image():
|
||||||
|
# Arrange
|
||||||
|
try:
|
||||||
|
import cPickle
|
||||||
|
except ImportError:
|
||||||
|
return
|
||||||
|
|
||||||
|
# Act / Assert
|
||||||
|
for protocol in range(0, cPickle.HIGHEST_PROTOCOL + 1):
|
||||||
|
helper_test_pickle_string(cPickle, protocol)
|
||||||
|
helper_test_pickle_file(cPickle, protocol)
|
||||||
|
|
||||||
|
|
||||||
|
def test_pickle_p_mode():
|
||||||
|
# Arrange
|
||||||
|
import pickle
|
||||||
|
|
||||||
|
# Act / Assert
|
||||||
|
for file in [
|
||||||
|
"Tests/images/test-card.png",
|
||||||
|
"Tests/images/zero_bb.png",
|
||||||
|
"Tests/images/zero_bb_scale2.png",
|
||||||
|
"Tests/images/non_zero_bb.png",
|
||||||
|
"Tests/images/non_zero_bb_scale2.png",
|
||||||
|
"Tests/images/p_trns_single.png",
|
||||||
|
"Tests/images/pil123p.png"
|
||||||
|
]:
|
||||||
|
helper_test_pickle_string(pickle, file=file)
|
||||||
|
|
||||||
|
# End of file
|
|
@ -242,7 +242,13 @@ def assert_image_equal(a, b, msg=None):
|
||||||
failure(msg or "got size %r, expected %r" % (a.size, b.size))
|
failure(msg or "got size %r, expected %r" % (a.size, b.size))
|
||||||
elif a.tobytes() != b.tobytes():
|
elif a.tobytes() != b.tobytes():
|
||||||
failure(msg or "got different content")
|
failure(msg or "got different content")
|
||||||
# generate better diff?
|
else:
|
||||||
|
success()
|
||||||
|
|
||||||
|
|
||||||
|
def assert_image_completely_equal(a, b, msg=None):
|
||||||
|
if a != b:
|
||||||
|
failure(msg or "images different")
|
||||||
else:
|
else:
|
||||||
success()
|
success()
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user