Merge branch 'master' into jpeg_qtables

This commit is contained in:
wiredfool 2014-06-20 00:35:06 -07:00
commit 9935c40d6c
76 changed files with 1307 additions and 435 deletions

View File

@ -4,6 +4,21 @@ Changelog (Pillow)
2.5.0 (unreleased)
------------------
- Add binary morphology addon
[dov, wiredfool]
- Decompression bomb protection
[hugovk]
- Put images in a single directory
[hugovk]
- Support OpenJpeg 2.1
[al45tair]
- Remove unistd.h #include for all platforms
[wiredfool]
- Use unittest for tests
[hugovk]

View File

@ -31,11 +31,18 @@ from PIL import VERSION, PILLOW_VERSION, _plugins
import warnings
class DecompressionBombWarning(RuntimeWarning):
pass
class _imaging_not_installed:
# module placeholder
def __getattr__(self, id):
raise ImportError("The _imaging C module is not installed")
# Limit to around a quarter gigabyte for a 24 bit (3 bpp) image
MAX_IMAGE_PIXELS = int(1024 * 1024 * 1024 / 4 / 3)
try:
# give Tk a chance to set up the environment, in case we're
# using an _imaging module linked against libtcl/libtk (use
@ -2172,6 +2179,20 @@ _fromarray_typemap[((1, 1), _ENDIAN + "i4")] = ("I", "I")
_fromarray_typemap[((1, 1), _ENDIAN + "f4")] = ("F", "F")
def _decompression_bomb_check(size):
if MAX_IMAGE_PIXELS is None:
return
pixels = size[0] * size[1]
if pixels > MAX_IMAGE_PIXELS:
warnings.warn(
"Image size (%d pixels) exceeds limit of %d pixels, "
"could be decompression bomb DOS attack." %
(pixels, MAX_IMAGE_PIXELS),
DecompressionBombWarning)
def open(fp, mode="r"):
"""
Opens and identifies the given image file.
@ -2209,7 +2230,9 @@ def open(fp, mode="r"):
factory, accept = OPEN[i]
if not accept or accept(prefix):
fp.seek(0)
return factory(fp, filename)
im = factory(fp, filename)
_decompression_bomb_check(im.size)
return im
except (SyntaxError, IndexError, TypeError):
# import traceback
# traceback.print_exc()
@ -2222,7 +2245,9 @@ def open(fp, mode="r"):
factory, accept = OPEN[i]
if not accept or accept(prefix):
fp.seek(0)
return factory(fp, filename)
im = factory(fp, filename)
_decompression_bomb_check(im.size)
return im
except (SyntaxError, IndexError, TypeError):
# import traceback
# traceback.print_exc()

View File

@ -6,7 +6,7 @@
#
# For a background, see "Image Processing By Interpolation and
# Extrapolation", Paul Haeberli and Douglas Voorhies. Available
# at http://www.sgi.com/grafica/interp/index.html
# at http://www.graficaobscura.com/interp/index.html
#
# History:
# 1996-03-23 fl Created

View File

@ -396,7 +396,7 @@ w7IkEbzhVQAAAABJRU5ErkJggg==
if __name__ == "__main__":
# create font data chunk for embedding
import base64, os, sys
font = "../Images/courB08"
font = "../Tests/images/courB08"
print(" f._load_pilfont_data(")
print(" # %s" % os.path.basename(font))
print(" BytesIO(base64.decodestring(b'''")

236
PIL/ImageMorph.py Normal file
View File

@ -0,0 +1,236 @@
# A binary morphology add-on for the Python Imaging Library
#
# History:
# 2014-06-04 Initial version.
#
# Copyright (c) 2014 Dov Grobgeld <dov.grobgeld@gmail.com>
from PIL import Image
from PIL import _imagingmorph
import re
LUT_SIZE = 1<<9
class LutBuilder:
"""A class for building MorphLut's from a descriptive language
The input patterns is a list of a strings sequences like these:
4:(...
.1.
111)->1
(whitespaces including linebreaks are ignored). The option 4
descibes a series of symmetry operations (in this case a
4-rotation), the pattern is decribed by:
. or X - Ignore
1 - Pixel is on
0 - Pixel is off
The result of the operation is described after "->" string.
The default is to return the current pixel value, which is
returned if no other match is found.
Operations:
4 - 4 way rotation
N - Negate
1 - Dummy op for no other operation (an op must always be given)
M - Mirroring
Example:
lb = LutBuilder(patterns = ["4:(... .1. 111)->1"])
lut = lb.build_lut()
"""
def __init__(self,patterns = None,op_name=None):
if patterns is not None:
self.patterns = patterns
else:
self.patterns = []
self.lut = None
if op_name is not None:
known_patterns = {
'corner' : ['1:(... ... ...)->0',
'4:(00. 01. ...)->1'],
'dilation4' : ['4:(... .0. .1.)->1'],
'dilation8' : ['4:(... .0. .1.)->1',
'4:(... .0. ..1)->1'],
'erosion4' : ['4:(... .1. .0.)->0'],
'erosion8' : ['4:(... .1. .0.)->0',
'4:(... .1. ..0)->0'],
'edge' : ['1:(... ... ...)->0',
'4:(.0. .1. ...)->1',
'4:(01. .1. ...)->1']
}
if not op_name in known_patterns:
raise Exception('Unknown pattern '+op_name+'!')
self.patterns = known_patterns[op_name]
def add_patterns(self, patterns):
self.patterns += patterns
def build_default_lut(self):
symbols = [0, 1]
m = 1 << 4 # pos of current pixel
self.lut = bytearray([symbols[(i & m)>0] for i in range(LUT_SIZE)])
def get_lut(self):
return self.lut
def _string_permute(self, pattern, permutation):
"""string_permute takes a pattern and a permutation and returns the
string permuted accordinging to the permutation list.
"""
assert(len(permutation)==9)
return ''.join([pattern[p] for p in permutation])
def _pattern_permute(self, basic_pattern, options, basic_result):
"""pattern_permute takes a basic pattern and its result and clones
the mattern according to the modifications described in the $options
parameter. It returns a list of all cloned patterns."""
patterns = [(basic_pattern, basic_result)]
# rotations
if '4' in options:
res = patterns[-1][1]
for i in range(4):
patterns.append(
(self._string_permute(patterns[-1][0],
[6,3,0,
7,4,1,
8,5,2]), res))
# mirror
if 'M' in options:
n = len(patterns)
for pattern,res in patterns[0:n]:
patterns.append(
(self._string_permute(pattern, [2,1,0,
5,4,3,
8,7,6]), res))
# negate
if 'N' in options:
n = len(patterns)
for pattern,res in patterns[0:n]:
# Swap 0 and 1
pattern = (pattern
.replace('0','Z')
.replace('1','0')
.replace('Z','1'))
res = '%d'%(1-int(res))
patterns.append((pattern, res))
return patterns
def build_lut(self):
"""Compile all patterns into a morphology lut.
TBD :Build based on (file) morphlut:modify_lut
"""
self.build_default_lut()
patterns = []
# Parse and create symmetries of the patterns strings
for p in self.patterns:
m = re.search(r'(\w*):?\s*\((.+?)\)\s*->\s*(\d)', p.replace('\n',''))
if not m:
raise Exception('Syntax error in pattern "'+p+'"')
options = m.group(1)
pattern = m.group(2)
result = int(m.group(3))
# Get rid of spaces
pattern= pattern.replace(' ','').replace('\n','')
patterns += self._pattern_permute(pattern, options, result)
# # Debugging
# for p,r in patterns:
# print p,r
# print '--'
# compile the patterns into regular expressions for speed
for i in range(len(patterns)):
p = patterns[i][0].replace('.','X').replace('X','[01]')
p = re.compile(p)
patterns[i] = (p, patterns[i][1])
# Step through table and find patterns that match.
# Note that all the patterns are searched. The last one
# caught overrides
for i in range(LUT_SIZE):
# Build the bit pattern
bitpattern = bin(i)[2:]
bitpattern = ('0'*(9-len(bitpattern)) + bitpattern)[::-1]
for p,r in patterns:
if p.match(bitpattern):
self.lut[i] = [0, 1][r]
return self.lut
class MorphOp:
"""A class for binary morphological operators"""
def __init__(self,
lut=None,
op_name = None,
patterns = None):
"""Create a binary morphological operator"""
self.lut = lut
if op_name is not None:
self.lut = LutBuilder(op_name = op_name).build_lut()
elif patterns is not None:
self.lut = LutBuilder(patterns = patterns).build_lut()
def apply(self, image):
"""Run a single morphological operation on an image
Returns a tuple of the number of changed pixels and the
morphed image"""
if self.lut is None:
raise Exception('No operator loaded')
outimage = Image.new(image.mode, image.size, None)
count = _imagingmorph.apply(bytes(self.lut), image.im.id, outimage.im.id)
return count, outimage
def match(self, image):
"""Get a list of coordinates matching the morphological operation on an image
Returns a list of tuples of (x,y) coordinates of all matching pixels."""
if self.lut is None:
raise Exception('No operator loaded')
return _imagingmorph.match(bytes(self.lut), image.im.id)
def get_on_pixels(self, image):
"""Get a list of all turned on pixels in a binary image
Returns a list of tuples of (x,y) coordinates of all matching pixels."""
return _imagingmorph.get_on_pixels(image.im.id)
def load_lut(self, filename):
"""Load an operator from an mrl file"""
with open(filename,'rb') as f:
self.lut = bytearray(f.read())
if len(self.lut)!= 8192:
self.lut = None
raise Exception('Wrong size operator file!')
def save_lut(self, filename):
"""Load an operator save mrl file"""
if self.lut is None:
raise Exception('No operator loaded')
with open(filename,'wb') as f:
f.write(self.lut)
def set_lut(self, lut):
"""Set the lut from an external source"""
self.lut = lut

View File

@ -158,15 +158,25 @@ class Jpeg2KImageFile(ImageFile.ImageFile):
self.layers = 0
fd = -1
length = -1
if hasattr(self.fp, "fileno"):
try:
fd = self.fp.fileno()
length = os.fstat(fd).st_size
except:
fd = -1
elif hasattr(self.fp, "seek"):
try:
pos = f.tell()
f.seek(0, 2)
length = f.tell()
f.seek(pos, 0)
except:
length = -1
self.tile = [('jpeg2k', (0, 0) + self.size, 0,
(self.codec, self.reduce, self.layers, fd))]
(self.codec, self.reduce, self.layers, fd, length))]
def load(self):
if self.reduce:
@ -178,7 +188,7 @@ class Jpeg2KImageFile(ImageFile.ImageFile):
if self.tile:
# Update the reduce and layers settings
t = self.tile[0]
t3 = (t[3][0], self.reduce, self.layers, t[3][3])
t3 = (t[3][0], self.reduce, self.layers, t[3][3], t[3][4])
self.tile = [(t[0], (0, 0) + self.size, t[2], t3)]
ImageFile.ImageFile.load(self)

View File

@ -14,11 +14,11 @@
#
# NOTE: This format cannot be automatically recognized, so the reader
# is not registered for use with Image.open(). To open a WEL file, use
# is not registered for use with Image.open(). To open a WAL file, use
# the WalImageFile.open() function instead.
# This reader is based on the specification available from:
# http://www.flipcode.com/tutorials/tut_q2levels.shtml
# http://www.flipcode.com/archives/Quake_2_BSP_File_Format.shtml
# and has been tested with a few sample files found using google.
from __future__ import print_function

View File

@ -1,5 +1,5 @@
Python SANE module V1.1 (30 Sep. 2004)
================================================================================
The SANE module provides an interface to the SANE scanner and frame
grabber interface for Linux. This module was contributed by Andrew
@ -9,11 +9,11 @@ word 'SANE' or 'sane' in the subject of your mail, otherwise it might
be classified as spam in the future.
To build this module, type (in the Sane directory):
To build this module, type (in the Sane directory)::
python setup.py build
In order to install the module type:
In order to install the module type::
python setup.py install

View File

@ -1,6 +1,5 @@
-------
Scripts
-------
=======
This directory contains a number of more or less trivial utilities
and demo programs.
@ -9,50 +8,50 @@ Comments and contributions are welcome.
</F>
--------------------------------------------------------------------
pildriver.py (by Eric S. Raymond)
--------------------------------------------------------------------
A class implementing an image-processing calculator for scripts.
Parses lists of commnds (or, called interactively, command-line
arguments) into image loads, transformations, and saves.
--------------------------------------------------------------------
viewer.py
--------------------------------------------------------------------
A simple image viewer. Can display all file formats handled by
PIL. Transparent images are properly handled.
--------------------------------------------------------------------
thresholder.py
--------------------------------------------------------------------
A simple utility that demonstrates how a transparent 1-bit overlay
can be used to show the current thresholding of an 8-bit image.
--------------------------------------------------------------------
enhancer.py
--------------------------------------------------------------------
Illustrates the ImageEnhance module. Drag the sliders to modify the
images. This might be very slow on some platforms, depending on the
Tk version.
--------------------------------------------------------------------
painter.py
--------------------------------------------------------------------
Illustrates how a painting program could be based on PIL and Tk.
Press the left mouse button and drag over the image to remove the
colour. Some clever tricks have been used to get decent performance
when updating the screen; see the sources for details.
--------------------------------------------------------------------
player.py
--------------------------------------------------------------------
A simple image sequence player. You can use either a sequence format
like FLI/FLC, GIF, or ARG, or give a number of images which are
interpreted as frames in a sequence. All frames must have the same
size.
--------------------------------------------------------------------
gifmaker.py
--------------------------------------------------------------------
Convert a sequence file to a GIF animation.
@ -60,20 +59,20 @@ Note that the GIF encoder provided with this release of PIL writes
uncompressed GIF files only, so the resulting animations are rather
large compared with these created by other tools.
--------------------------------------------------------------------
explode.py
--------------------------------------------------------------------
Split a sequence file into individual frames.
--------------------------------------------------------------------
image2py.py
--------------------------------------------------------------------
Convert an image to a Python module containing an IMAGE variable.
Note that the module using the module must include JPEG and ZIP
decoders, unless the -u option is used.
--------------------------------------------------------------------
olesummary.py
--------------------------------------------------------------------
Uses the OleFileIO module to dump the summary information from an OLE
structured storage file. This works with most OLE files, including

View File

@ -233,7 +233,7 @@ def lena(mode="RGB", cache={}):
# im = cache.get(mode)
if im is None:
if mode == "RGB":
im = Image.open("Images/lena.ppm")
im = Image.open("Tests/images/lena.ppm")
elif mode == "F":
im = lena("L").convert(mode)
elif mode[:4] == "I;16":

BIN
Tests/images/corner.lut Normal file

Binary file not shown.

View File

Before

Width:  |  Height:  |  Size: 1.2 KiB

After

Width:  |  Height:  |  Size: 1.2 KiB

BIN
Tests/images/dilation4.lut Normal file

Binary file not shown.

BIN
Tests/images/dilation8.lut Normal file

Binary file not shown.

BIN
Tests/images/edge.lut Normal file

Binary file not shown.

BIN
Tests/images/erosion4.lut Normal file

Binary file not shown.

BIN
Tests/images/erosion8.lut Normal file

Binary file not shown.

View File

Before

Width:  |  Height:  |  Size: 29 KiB

After

Width:  |  Height:  |  Size: 29 KiB

View File

Before

Width:  |  Height:  |  Size: 21 KiB

After

Width:  |  Height:  |  Size: 21 KiB

View File

Before

Width:  |  Height:  |  Size: 16 KiB

After

Width:  |  Height:  |  Size: 16 KiB

View File

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

View File

Before

Width:  |  Height:  |  Size: 4.7 KiB

After

Width:  |  Height:  |  Size: 4.7 KiB

View File

Before

Width:  |  Height:  |  Size: 37 KiB

After

Width:  |  Height:  |  Size: 37 KiB

View File

Before

Width:  |  Height:  |  Size: 3.8 KiB

After

Width:  |  Height:  |  Size: 3.8 KiB

BIN
Tests/images/morph_a.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 83 B

View File

Before

Width:  |  Height:  |  Size: 100 KiB

After

Width:  |  Height:  |  Size: 100 KiB

View File

Before

Width:  |  Height:  |  Size: 26 KiB

After

Width:  |  Height:  |  Size: 26 KiB

View File

Before

Width:  |  Height:  |  Size: 7.9 KiB

After

Width:  |  Height:  |  Size: 7.9 KiB

View File

@ -0,0 +1,45 @@
from helper import unittest, PillowTestCase, tearDownModule
from PIL import Image
test_file = "Tests/images/lena.ppm"
ORIGINAL_LIMIT = Image.MAX_IMAGE_PIXELS
class TestDecompressionBomb(PillowTestCase):
def tearDown(self):
Image.MAX_IMAGE_PIXELS = ORIGINAL_LIMIT
def test_no_warning_small_file(self):
# Implicit assert: no warning.
# A warning would cause a failure.
Image.open(test_file)
def test_no_warning_no_limit(self):
# Arrange
# Turn limit off
Image.MAX_IMAGE_PIXELS = None
self.assertEqual(Image.MAX_IMAGE_PIXELS, None)
# Act / Assert
# Implicit assert: no warning.
# A warning would cause a failure.
Image.open(test_file)
def test_warning(self):
# Arrange
# Set limit to a low, easily testable value
Image.MAX_IMAGE_PIXELS = 10
self.assertEqual(Image.MAX_IMAGE_PIXELS, 10)
# Act / Assert
self.assert_warning(
Image.DecompressionBombWarning,
lambda: Image.open(test_file))
if __name__ == '__main__':
unittest.main()
# End of file

View File

@ -3,7 +3,7 @@ from helper import unittest, PillowTestCase, tearDownModule
from PIL import Image
# sample ppm stream
file = "Images/lena.fli"
file = "Tests/images/lena.fli"
data = open(file, "rb").read()

View File

@ -5,7 +5,7 @@ from PIL import Image
codecs = dir(Image.core)
# sample gif stream
file = "Images/lena.gif"
file = "Tests/images/lena.gif"
with open(file, "rb") as f:
data = f.read()
@ -45,7 +45,7 @@ class TestFileGif(PillowTestCase):
def test_roundtrip2(self):
# see https://github.com/python-pillow/Pillow/issues/403
out = self.tempfile('temp.gif')
im = Image.open('Images/lena.gif')
im = Image.open('Tests/images/lena.gif')
im2 = im.copy()
im2.save(out)
reread = Image.open(out)
@ -55,7 +55,7 @@ class TestFileGif(PillowTestCase):
def test_palette_handling(self):
# see https://github.com/python-pillow/Pillow/issues/513
im = Image.open('Images/lena.gif')
im = Image.open('Tests/images/lena.gif')
im = im.convert('RGB')
im = im.resize((100, 100), Image.ANTIALIAS)

View File

@ -3,7 +3,7 @@ from helper import unittest, PillowTestCase, tearDownModule
from PIL import Image
# sample icon file
file = "Images/pillow.icns"
file = "Tests/images/pillow.icns"
data = open(file, "rb").read()
enable_jpeg2k = hasattr(Image.core, 'jp2klib_version')

View File

@ -3,7 +3,7 @@ from helper import unittest, PillowTestCase, tearDownModule
from PIL import Image
# sample ppm stream
file = "Images/lena.ico"
file = "Tests/images/lena.ico"
data = open(file, "rb").read()

View File

@ -10,7 +10,7 @@ codecs = dir(Image.core)
# sample png stream
file = "Images/lena.png"
file = "Tests/images/lena.png"
data = open(file, "rb").read()
# stuff to create inline PNG images
@ -204,10 +204,10 @@ class TestFilePng(PillowTestCase):
def test_load_verify(self):
# Check open/load/verify exception (@PIL150)
im = Image.open("Images/lena.png")
im = Image.open("Tests/images/lena.png")
im.verify()
im = Image.open("Images/lena.png")
im = Image.open("Tests/images/lena.png")
im.load()
self.assertRaises(RuntimeError, lambda: im.verify())

View File

@ -3,7 +3,7 @@ from helper import unittest, PillowTestCase, tearDownModule
from PIL import Image
# sample ppm stream
file = "Images/lena.ppm"
file = "Tests/images/lena.ppm"
data = open(file, "rb").read()

View File

@ -3,7 +3,7 @@ from helper import unittest, PillowTestCase, tearDownModule
from PIL import Image
# sample ppm stream
file = "Images/lena.psd"
file = "Tests/images/lena.psd"
data = open(file, "rb").read()

View File

@ -5,7 +5,7 @@ from PIL import Image, TarIO
codecs = dir(Image.core)
# sample ppm stream
tarfile = "Images/lena.tar"
tarfile = "Tests/images/lena.tar"
class TestFileTar(PillowTestCase):

View File

@ -23,7 +23,7 @@ class TestFileWebp(PillowTestCase):
def test_read_rgb(self):
file_path = "Images/lena.webp"
file_path = "Tests/images/lena.webp"
image = Image.open(file_path)
self.assertEqual(image.mode, "RGB")
@ -33,7 +33,7 @@ class TestFileWebp(PillowTestCase):
image.getdata()
# generated with:
# dwebp -ppm ../../Images/lena.webp -o lena_webp_bits.ppm
# dwebp -ppm ../../Tests/images/lena.webp -o lena_webp_bits.ppm
target = Image.open('Tests/images/lena_webp_bits.ppm')
self.assert_image_similar(image, target, 20.0)

View File

@ -22,7 +22,7 @@ class TestFileWebpAlpha(PillowTestCase):
def test_read_rgba(self):
# Generated with `cwebp transparent.png -o transparent.webp`
file_path = "Images/transparent.webp"
file_path = "Tests/images/transparent.webp"
image = Image.open(file_path)
self.assertEqual(image.mode, "RGBA")
@ -33,7 +33,7 @@ class TestFileWebpAlpha(PillowTestCase):
image.tobytes()
target = Image.open('Images/transparent.png')
target = Image.open('Tests/images/transparent.png')
self.assert_image_similar(image, target, 20.0)
def test_write_lossless_rgb(self):

View File

@ -15,7 +15,7 @@ class TestFileWebpMetadata(PillowTestCase):
def test_read_exif_metadata(self):
file_path = "Images/flower.webp"
file_path = "Tests/images/flower.webp"
image = Image.open(file_path)
self.assertEqual(image.format, "WEBP")
@ -54,7 +54,7 @@ class TestFileWebpMetadata(PillowTestCase):
def test_read_icc_profile(self):
file_path = "Images/flower2.webp"
file_path = "Tests/images/flower2.webp"
image = Image.open(file_path)
self.assertEqual(image.format, "WEBP")

View File

@ -3,7 +3,7 @@ from helper import unittest, PillowTestCase, tearDownModule
from PIL import Image
# sample ppm stream
file = "Images/lena.xpm"
file = "Tests/images/lena.xpm"
data = open(file, "rb").read()

View File

@ -2,7 +2,7 @@ from helper import unittest, PillowTestCase, tearDownModule
from PIL import FontFile, BdfFontFile
filename = "Images/courB08.bdf"
filename = "Tests/images/courB08.bdf"
class TestFontBdf(PillowTestCase):

View File

@ -36,7 +36,7 @@ class TestImageConvert(PillowTestCase):
self.assertEqual(orig, converted)
def test_8bit(self):
im = Image.open('Images/lena.jpg')
im = Image.open('Tests/images/lena.jpg')
self._test_float_conversion(im.convert('L'))
def test_16bit(self):

View File

@ -3,7 +3,7 @@ from helper import unittest, PillowTestCase, tearDownModule, fromstring, tostrin
from PIL import Image
codecs = dir(Image.core)
filename = "Images/lena.jpg"
filename = "Tests/images/lena.jpg"
data = tostring(Image.open(filename).resize((512, 512)), "JPEG")

View File

@ -16,14 +16,14 @@ class TestImageLoad(PillowTestCase):
self.assertEqual(pix[0, 0], (223, 162, 133))
def test_close(self):
im = Image.open("Images/lena.gif")
im = Image.open("Tests/images/lena.gif")
im.close()
self.assertRaises(ValueError, lambda: im.load())
self.assertRaises(ValueError, lambda: im.getpixel((0, 0)))
def test_contextmanager(self):
fn = None
with Image.open("Images/lena.gif") as im:
with Image.open("Tests/images/lena.gif") as im:
fn = im.fp.fileno()
os.fstat(fn)

View File

@ -18,6 +18,8 @@ class TestImageCms(PillowTestCase):
def setUp(self):
try:
from PIL import ImageCms
# need to hit getattr to trigger the delayed import error
ImageCms.core.profile_open
except ImportError as v:
self.skipTest(v)

View File

@ -7,21 +7,21 @@ from PIL import ImageDraw
import sys
# Image size
w, h = 100, 100
W, H = 100, 100
# Bounding box points
x0 = int(w / 4)
x1 = int(x0 * 3)
y0 = int(h / 4)
y1 = int(x0 * 3)
X0 = int(W / 4)
X1 = int(X0 * 3)
Y0 = int(H / 4)
Y1 = int(X0 * 3)
# Two kinds of bounding box
bbox1 = [(x0, y0), (x1, y1)]
bbox2 = [x0, y0, x1, y1]
BBOX1 = [(X0, Y0), (X1, Y1)]
BBOX2 = [X0, Y0, X1, Y1]
# Two kinds of coordinate sequences
points1 = [(10, 10), (20, 40), (30, 30)]
points2 = [10, 10, 20, 40, 30, 30]
POINTS1 = [(10, 10), (20, 40), (30, 30)]
POINTS2 = [10, 10, 20, 40, 30, 30]
class TestImageDraw(PillowTestCase):
@ -47,7 +47,7 @@ class TestImageDraw(PillowTestCase):
def helper_arc(self, bbox):
# Arrange
im = Image.new("RGB", (w, h))
im = Image.new("RGB", (W, H))
draw = ImageDraw.Draw(im)
# Act
@ -60,15 +60,15 @@ class TestImageDraw(PillowTestCase):
im, Image.open("Tests/images/imagedraw_arc.png"))
def test_arc1(self):
self.helper_arc(bbox1)
self.helper_arc(BBOX1)
def test_arc2(self):
self.helper_arc(bbox2)
self.helper_arc(BBOX2)
def test_bitmap(self):
# Arrange
small = Image.open("Tests/images/pil123rgba.png").resize((50, 50))
im = Image.new("RGB", (w, h))
im = Image.new("RGB", (W, H))
draw = ImageDraw.Draw(im)
# Act
@ -81,7 +81,7 @@ class TestImageDraw(PillowTestCase):
def helper_chord(self, bbox):
# Arrange
im = Image.new("RGB", (w, h))
im = Image.new("RGB", (W, H))
draw = ImageDraw.Draw(im)
# Act
@ -93,14 +93,14 @@ class TestImageDraw(PillowTestCase):
im, Image.open("Tests/images/imagedraw_chord.png"))
def test_chord1(self):
self.helper_chord(bbox1)
self.helper_chord(BBOX1)
def test_chord2(self):
self.helper_chord(bbox2)
self.helper_chord(BBOX2)
def helper_ellipse(self, bbox):
# Arrange
im = Image.new("RGB", (w, h))
im = Image.new("RGB", (W, H))
draw = ImageDraw.Draw(im)
# Act
@ -112,18 +112,18 @@ class TestImageDraw(PillowTestCase):
im, Image.open("Tests/images/imagedraw_ellipse.png"))
def test_ellipse1(self):
self.helper_ellipse(bbox1)
self.helper_ellipse(BBOX1)
def test_ellipse2(self):
self.helper_ellipse(bbox2)
self.helper_ellipse(BBOX2)
def helper_line(self, points):
# Arrange
im = Image.new("RGB", (w, h))
im = Image.new("RGB", (W, H))
draw = ImageDraw.Draw(im)
# Act
draw.line(points1, fill="yellow", width=2)
draw.line(points, fill="yellow", width=2)
del draw
# Assert
@ -131,14 +131,14 @@ class TestImageDraw(PillowTestCase):
im, Image.open("Tests/images/imagedraw_line.png"))
def test_line1(self):
self.helper_line(points1)
self.helper_line(POINTS1)
def test_line2(self):
self.helper_line(points2)
self.helper_line(POINTS2)
def helper_pieslice(self, bbox):
# Arrange
im = Image.new("RGB", (w, h))
im = Image.new("RGB", (W, H))
draw = ImageDraw.Draw(im)
# Act
@ -150,18 +150,18 @@ class TestImageDraw(PillowTestCase):
im, Image.open("Tests/images/imagedraw_pieslice.png"))
def test_pieslice1(self):
self.helper_pieslice(bbox1)
self.helper_pieslice(BBOX1)
def test_pieslice2(self):
self.helper_pieslice(bbox2)
self.helper_pieslice(BBOX2)
def helper_point(self, points):
# Arrange
im = Image.new("RGB", (w, h))
im = Image.new("RGB", (W, H))
draw = ImageDraw.Draw(im)
# Act
draw.point(points1, fill="yellow")
draw.point(points, fill="yellow")
del draw
# Assert
@ -169,18 +169,18 @@ class TestImageDraw(PillowTestCase):
im, Image.open("Tests/images/imagedraw_point.png"))
def test_point1(self):
self.helper_point(points1)
self.helper_point(POINTS1)
def test_point2(self):
self.helper_point(points2)
self.helper_point(POINTS2)
def helper_polygon(self, points):
# Arrange
im = Image.new("RGB", (w, h))
im = Image.new("RGB", (W, H))
draw = ImageDraw.Draw(im)
# Act
draw.polygon(points1, fill="red", outline="blue")
draw.polygon(points, fill="red", outline="blue")
del draw
# Assert
@ -188,14 +188,14 @@ class TestImageDraw(PillowTestCase):
im, Image.open("Tests/images/imagedraw_polygon.png"))
def test_polygon1(self):
self.helper_polygon(points1)
self.helper_polygon(POINTS1)
def test_polygon2(self):
self.helper_polygon(points2)
self.helper_polygon(POINTS2)
def helper_rectangle(self, bbox):
# Arrange
im = Image.new("RGB", (w, h))
im = Image.new("RGB", (W, H))
draw = ImageDraw.Draw(im)
# Act
@ -207,17 +207,17 @@ class TestImageDraw(PillowTestCase):
im, Image.open("Tests/images/imagedraw_rectangle.png"))
def test_rectangle1(self):
self.helper_rectangle(bbox1)
self.helper_rectangle(BBOX1)
def test_rectangle2(self):
self.helper_rectangle(bbox2)
self.helper_rectangle(BBOX2)
def test_floodfill(self):
# Arrange
im = Image.new("RGB", (w, h))
im = Image.new("RGB", (W, H))
draw = ImageDraw.Draw(im)
draw.rectangle(bbox2, outline="yellow", fill="green")
centre_point = (int(w/2), int(h/2))
draw.rectangle(BBOX2, outline="yellow", fill="green")
centre_point = (int(W/2), int(H/2))
# Act
ImageDraw.floodfill(im, centre_point, ImageColor.getrgb("red"))
@ -233,10 +233,10 @@ class TestImageDraw(PillowTestCase):
# floodfill() is experimental
# Arrange
im = Image.new("RGB", (w, h))
im = Image.new("RGB", (W, H))
draw = ImageDraw.Draw(im)
draw.rectangle(bbox2, outline="yellow", fill="green")
centre_point = (int(w/2), int(h/2))
draw.rectangle(BBOX2, outline="yellow", fill="green")
centre_point = (int(W/2), int(H/2))
# Act
ImageDraw.floodfill(

169
Tests/test_imagemorph.py Normal file
View File

@ -0,0 +1,169 @@
# Test the ImageMorphology functionality
from helper import *
from PIL import Image
from PIL import ImageMorph
class MorphTests(PillowTestCase):
def setUp(self):
self.A = self.string_to_img(
"""
.......
.......
..111..
..111..
..111..
.......
.......
"""
)
def img_to_string(self, im):
"""Turn a (small) binary image into a string representation"""
chars = '.1'
width, height = im.size
return '\n'.join(
[''.join([chars[im.getpixel((c,r))>0] for c in range(width)])
for r in range(height)])
def string_to_img(self, image_string):
"""Turn a string image representation into a binary image"""
rows = [s for s in image_string.replace(' ','').split('\n')
if len(s)]
height = len(rows)
width = len(rows[0])
im = Image.new('L',(width,height))
for i in range(width):
for j in range(height):
c = rows[j][i]
v = c in 'X1'
im.putpixel((i,j),v)
return im
def img_string_normalize(self, im):
return self.img_to_string(self.string_to_img(im))
def assert_img_equal(self, A, B):
self.assertEqual(self.img_to_string(A), self.img_to_string(B))
def assert_img_equal_img_string(self, A, Bstring):
self.assertEqual(self.img_to_string(A), self.img_string_normalize(Bstring))
def test_str_to_img(self):
im = Image.open('Tests/images/morph_a.png')
self.assert_image_equal(self.A, im)
def create_lut(self):
for op in ('corner', 'dilation4', 'dilation8', 'erosion4', 'erosion8', 'edge'):
lb = ImageMorph.LutBuilder(op_name=op)
lut = lb.build_lut(self)
with open('Tests/images/%s.lut' % op, 'wb') as f:
f.write(lut)
#create_lut()
def test_lut(self):
for op in ('corner', 'dilation4', 'dilation8', 'erosion4', 'erosion8', 'edge'):
lb = ImageMorph.LutBuilder(op_name=op)
lut = lb.build_lut()
with open('Tests/images/%s.lut' % op , 'rb') as f:
self.assertEqual(lut, bytearray(f.read()))
# Test the named patterns
def test_erosion8(self):
# erosion8
mop = ImageMorph.MorphOp(op_name='erosion8')
count,Aout = mop.apply(self.A)
self.assertEqual(count,8)
self.assert_img_equal_img_string(Aout,
"""
.......
.......
.......
...1...
.......
.......
.......
""")
def test_dialation8(self):
# dialation8
mop = ImageMorph.MorphOp(op_name='dilation8')
count,Aout = mop.apply(self.A)
self.assertEqual(count,16)
self.assert_img_equal_img_string(Aout,
"""
.......
.11111.
.11111.
.11111.
.11111.
.11111.
.......
""")
def test_erosion4(self):
# erosion4
mop = ImageMorph.MorphOp(op_name='dilation4')
count,Aout = mop.apply(self.A)
self.assertEqual(count,12)
self.assert_img_equal_img_string(Aout,
"""
.......
..111..
.11111.
.11111.
.11111.
..111..
.......
""")
def test_edge(self):
# edge
mop = ImageMorph.MorphOp(op_name='edge')
count,Aout = mop.apply(self.A)
self.assertEqual(count,1)
self.assert_img_equal_img_string(Aout,
"""
.......
.......
..111..
..1.1..
..111..
.......
.......
""")
def test_corner(self):
# Create a corner detector pattern
mop = ImageMorph.MorphOp(patterns = ['1:(... ... ...)->0',
'4:(00. 01. ...)->1'])
count,Aout = mop.apply(self.A)
self.assertEqual(count,5)
self.assert_img_equal_img_string(Aout,
"""
.......
.......
..1.1..
.......
..1.1..
.......
.......
""")
# Test the coordinate counting with the same operator
coords = mop.match(self.A)
self.assertEqual(len(coords), 4)
self.assertEqual(tuple(coords),
((2,2),(4,2),(2,4),(4,4)))
coords = mop.get_on_pixels(Aout)
self.assertEqual(len(coords), 4)
self.assertEqual(tuple(coords),
((2,2),(4,2),(2,4),(4,4)))

View File

@ -4,7 +4,7 @@ from PIL import Image
from PIL import ImageOps
from PIL import ImageFilter
im = Image.open("Images/lena.ppm")
im = Image.open("Tests/images/lena.ppm")
class TestImageOpsUsm(PillowTestCase):

View File

@ -19,7 +19,7 @@ import locale
# one of string.whitespace is not freely convertable into ascii.
path = "Images/lena.jpg"
path = "Tests/images/lena.jpg"
class TestLocale(PillowTestCase):

View File

@ -6,7 +6,7 @@ from PIL import Image
class TestPickle(PillowTestCase):
def helper_pickle_file(self, pickle, protocol=0):
im = Image.open('Images/lena.jpg')
im = Image.open('Tests/images/lena.jpg')
filename = self.tempfile('temp.pkl')
# Act
@ -19,7 +19,7 @@ class TestPickle(PillowTestCase):
self.assertEqual(im, loaded_im)
def helper_pickle_string(
self, pickle, protocol=0, file='Images/lena.jpg'):
self, pickle, protocol=0, file='Tests/images/lena.jpg'):
im = Image.open(file)
# Act

View File

@ -215,7 +215,7 @@ def lena(mode="RGB", cache={}):
im = cache.get(mode)
if im is None:
if mode == "RGB":
im = Image.open("Images/lena.ppm")
im = Image.open("Tests/images/lena.ppm")
elif mode == "F":
im = lena("L").convert(mode)
elif mode[:4] == "I;16":

View File

@ -9,7 +9,7 @@ try:
except:
format = "PNG"
im = Image.open("Images/lena.ppm")
im = Image.open("Tests/images/lena.ppm")
im.load()
queue = queue.Queue()

292
Tk/README.rst Normal file
View File

@ -0,0 +1,292 @@
Using PIL With Tkinter
====================================================================
Starting with 1.0 final (release candidate 2 and later, to be
precise), PIL can attach itself to Tkinter in flight. As a result,
you no longer need to rebuild the Tkinter extension to be able to
use PIL.
However, if you cannot get the this to work on your platform, you
can do it in the old way:
Adding Tkinter support
----------------------
1. Compile Python's _tkinter.c with the WITH_APPINIT and WITH_PIL
flags set, and link it with tkImaging.c and tkappinit.c. To
do this, copy the former to the Modules directory, and edit
the _tkinter line in Setup (or Setup.in) according to the
instructions in that file.
NOTE: if you have an old Python version, the tkappinit.c
file is not included by default. If this is the case, you
will have to add the following lines to tkappinit.c, after
the MOREBUTTONS stuff::
{
extern void TkImaging_Init(Tcl_Interp* interp);
TkImaging_Init(interp);
}
This registers a Tcl command called "PyImagingPhoto", which is
use to communicate between PIL and Tk's PhotoImage handler.
You must also change the _tkinter line in Setup (or Setup.in)
to something like::
_tkinter _tkinter.c tkImaging.c tkappinit.c -DWITH_APPINIT
-I/usr/local/include -L/usr/local/lib -ltk8.0 -ltcl8.0 -lX11
The Photoimage Booster Patch (for Windows 95/NT)
====================================================================
This patch kit boosts performance for 16/24-bit displays. The
first patch is required on Tk 4.2 (where it fixes the problems for
16-bit displays) and later versions, with the exception for Tk 8.0b1
where Sun added something similar themselves, only to remove it in
8.0b2. By installing both patches, Tk's PhotoImage handling becomes
much faster on both 16-bit and 24-bit displays. The patch has been
tested with Tk 4.2 and 8.0.
Here's a benchmark, made with a sample program which loads two
512x512 greyscale PGM's, and two 512x512 colour PPM's, and displays
each of them in a separate toplevel windows. Tcl/Tk was compiled
with Visual C 4.0, and run on a P100 under Win95. Image load times
are not included in the timings:
+----------------------+------------+-------------+----------------+
| | **8-bit** | **16-bit** | **24-bit** |
+----------------------+------------+-------------+----------------+
| 1. original 4.2 code | 5.52 s | 8.57 s | 3.79 s |
+----------------------+------------+-------------+----------------+
| 2. booster patch | 5.49 s | 1.87 s | 1.82 s |
+----------------------+------------+-------------+----------------+
| speedup | None | 4.6x | 2.1x |
+----------------------+------------+-------------+----------------+
Here's the patches:
1. For portability and speed, the best thing under Windows is to
treat 16-bit displays as if they were 24-bit. The Windows device
drivers take care of the rest.
.. Note::
If you have Tk 4.1 or Tk 8.0b1, you don't have to apply this
patch! It only applies to Tk 4.2, Tk 8.0a[12] and Tk 8.0b2.
In ``win/tkWinImage.c``, change the following line in ``XCreateImage``::
imagePtr->bits_per_pixel = depth;
to::
/* ==================================================================== */
/* The tk photo image booster patch -- patch section 1 */
/* ==================================================================== */
if (visual->class == TrueColor)
/* true colour is stored as 3 bytes: (blue, green, red) */
imagePtr->bits_per_pixel = 24;
else
imagePtr->bits_per_pixel = depth;
/* ==================================================================== */
2. The DitherInstance implementation is not good. It's especially
bad on highend truecolour displays. IMO, it should be rewritten from
scratch (some other day...).
Anyway, the following band-aid makes the situation a little bit
better under Windows. This hack trades some marginal quality (no
dithering on 16-bit displays) for a dramatic performance boost.
Requires patch 1, unless you're using Tk 4.1 or Tk 8.0b1.
In generic/tkImgPhoto.c, add the #ifdef section to the DitherInstance
function::
/* ==================================================================== */
for (; height > 0; height -= nLines) {
if (nLines > height) {
nLines = height;
}
dstLinePtr = (unsigned char *) imagePtr->data;
yEnd = yStart + nLines;
/* ==================================================================== */
/* The tk photo image booster patch -- patch section 2 */
/* ==================================================================== */
#ifdef __WIN32__
if (colorPtr->visualInfo.class == TrueColor
&& instancePtr->gamma == 1.0) {
/* Windows hicolor/truecolor booster */
for (y = yStart; y < yEnd; ++y) {
destBytePtr = dstLinePtr;
srcPtr = srcLinePtr;
for (x = xStart; x < xEnd; ++x) {
destBytePtr[0] = srcPtr[2];
destBytePtr[1] = srcPtr[1];
destBytePtr[2] = srcPtr[0];
destBytePtr += 3; srcPtr += 3;
}
srcLinePtr += lineLength;
dstLinePtr += bytesPerLine;
}
} else
#endif
/* ==================================================================== */
for (y = yStart; y < yEnd; ++y) {
srcPtr = srcLinePtr;
errPtr = errLinePtr;
destBytePtr = dstLinePtr;
last updated: 97-07-03/fl
The PIL Bitmap Booster Patch
====================================================================
The pilbitmap booster patch greatly improves performance of the
ImageTk.BitmapImage constructor. Unfortunately, the design of Tk
doesn't allow us to do this from the tkImaging interface module, so
you have to patch the Tk sources.
Once installed, the ImageTk module will automatically detect this
patch.
(Note: this patch has been tested with Tk 8.0 on Win32 only, but it
should work just fine on other platforms as well).
1. To the beginning of TkGetBitmapData (in generic/tkImgBmap.c), add
the following stuff::
/* ==================================================================== */
int width, height, numBytes, hotX, hotY;
char *p, *end, *expandedFileName;
ParseInfo pi;
char *data = NULL;
Tcl_DString buffer;
/* ==================================================================== */
/* The pilbitmap booster patch -- patch section */
/* ==================================================================== */
char *PILGetBitmapData();
if (string) {
/* Is this a PIL bitmap reference? */
data = PILGetBitmapData(string, widthPtr, heightPtr, hotXPtr, hotYPtr);
if (data)
return data;
}
/* ==================================================================== */
pi.string = string;
if (string == NULL) {
if (Tcl_IsSafe(interp)) {
2. Append the following to the same file (you may wish to include
Imaging.h instead of copying the struct declaration...)::
/* ==================================================================== */
/* The pilbitmap booster patch -- code section */
/* ==================================================================== */
/* Imaging declaration boldly copied from Imaging.h (!) */
typedef struct ImagingInstance *Imaging; /* a.k.a. ImagingImage :-) */
typedef unsigned char UINT8;
typedef int INT32;
struct ImagingInstance {
/* Format */
char mode[4+1]; /* Band names ("1", "L", "P", "RGB", "RGBA", "CMYK") */
int type; /* Always 0 in this version */
int depth; /* Always 8 in this version */
int bands; /* Number of bands (1, 3, or 4) */
int xsize; /* Image dimension. */
int ysize;
/* Colour palette (for "P" images only) */
void* palette;
/* Data pointers */
UINT8 **image8; /* Set for 8-bit image (pixelsize=1). */
INT32 **image32; /* Set for 32-bit image (pixelsize=4). */
/* Internals */
char **image; /* Actual raster data. */
char *block; /* Set if data is allocated in a single block. */
int pixelsize; /* Size of a pixel, in bytes (1 or 4) */
int linesize; /* Size of a line, in bytes (xsize * pixelsize) */
/* Virtual methods */
void (*im_delete)(Imaging *);
};
/* The pilbitmap booster patch allows you to pass PIL images to the
Tk bitmap decoder. Passing images this way is much more efficient
than using the "tobitmap" method. */
char *
PILGetBitmapData(string, widthPtr, heightPtr, hotXPtr, hotYPtr)
char *string;
int *widthPtr, *heightPtr;
int *hotXPtr, *hotYPtr;
{
char* data;
char* p;
int y;
Imaging im;
if (strncmp(string, "PIL:", 4) != 0)
return NULL;
im = (Imaging) atol(string + 4);
if (strcmp(im->mode, "1") != 0 && strcmp(im->mode, "L") != 0)
return NULL;
data = p = (char *) ckalloc((unsigned) ((im->xsize+7)/8) * im->ysize);
for (y = 0; y < im->ysize; y++) {
char* in = im->image8[y];
int i, m, b;
b = 0; m = 1;
for (i = 0; i < im->xsize; i++) {
if (in[i] != 0)
b |= m;
m <<= 1;
if (m == 256){
*p++ = b;
b = 0; m = 1;
}
}
if (m != 1)
*p++ = b;
}
*widthPtr = im->xsize;
*heightPtr = im->ysize;
*hotXPtr = -1;
*hotYPtr = -1;
return data;
}
/* ==================================================================== */
3. Recompile Tk and relink the _tkinter module (where necessary).
Last updated: 97-05-17/fl

View File

@ -1,108 +0,0 @@
====================================================================
The Photoimage Booster Patch (for Windows 95/NT)
====================================================================
This patch kit boosts performance for 16/24-bit displays. The
first patch is required on Tk 4.2 (where it fixes the problems for
16-bit displays) and later versions, with the exception for Tk 8.0b1
where Sun added something similar themselves, only to remove it in
8.0b2. By installing both patches, Tk's PhotoImage handling becomes
much faster on both 16-bit and 24-bit displays. The patch has been
tested with Tk 4.2 and 8.0.
Here's a benchmark, made with a sample program which loads two
512x512 greyscale PGM's, and two 512x512 colour PPM's, and displays
each of them in a separate toplevel windows. Tcl/Tk was compiled
with Visual C 4.0, and run on a P100 under Win95. Image load times
are not included in the timings:
8-bit 16-bit 24-bit
--------------------------------------------------------------------
1. original 4.2 code 5.52 s 8.57 s 3.79 s
2. booster patch 5.49 s 1.87 s 1.82 s
speedup None 4.6x 2.1x
====================================================================
Here's the patches:
1. For portability and speed, the best thing under Windows is to
treat 16-bit displays as if they were 24-bit. The Windows device
drivers take care of the rest.
----------------------------------------------------------------
If you have Tk 4.1 or Tk 8.0b1, you don't have to apply this
patch! It only applies to Tk 4.2, Tk 8.0a[12] and Tk 8.0b2.
----------------------------------------------------------------
In win/tkWinImage.c, change the following line in XCreateImage:
imagePtr->bits_per_pixel = depth;
to
/* ==================================================================== */
/* The tk photo image booster patch -- patch section 1 */
/* ==================================================================== */
if (visual->class == TrueColor)
/* true colour is stored as 3 bytes: (blue, green, red) */
imagePtr->bits_per_pixel = 24;
else
imagePtr->bits_per_pixel = depth;
/* ==================================================================== */
2. The DitherInstance implementation is not good. It's especially
bad on highend truecolour displays. IMO, it should be rewritten from
scratch (some other day...).
Anyway, the following band-aid makes the situation a little bit
better under Windows. This hack trades some marginal quality (no
dithering on 16-bit displays) for a dramatic performance boost.
Requires patch 1, unless you're using Tk 4.1 or Tk 8.0b1.
In generic/tkImgPhoto.c, add the #ifdef section to the DitherInstance
function:
for (; height > 0; height -= nLines) {
if (nLines > height) {
nLines = height;
}
dstLinePtr = (unsigned char *) imagePtr->data;
yEnd = yStart + nLines;
/* ==================================================================== */
/* The tk photo image booster patch -- patch section 2 */
/* ==================================================================== */
#ifdef __WIN32__
if (colorPtr->visualInfo.class == TrueColor
&& instancePtr->gamma == 1.0) {
/* Windows hicolor/truecolor booster */
for (y = yStart; y < yEnd; ++y) {
destBytePtr = dstLinePtr;
srcPtr = srcLinePtr;
for (x = xStart; x < xEnd; ++x) {
destBytePtr[0] = srcPtr[2];
destBytePtr[1] = srcPtr[1];
destBytePtr[2] = srcPtr[0];
destBytePtr += 3; srcPtr += 3;
}
srcLinePtr += lineLength;
dstLinePtr += bytesPerLine;
}
} else
#endif
/* ==================================================================== */
for (y = yStart; y < yEnd; ++y) {
srcPtr = srcLinePtr;
errPtr = errLinePtr;
destBytePtr = dstLinePtr;
====================================================================
last updated: 97-07-03/fl

View File

@ -1,41 +0,0 @@
====================================================================
Using PIL With Tkinter
====================================================================
Starting with 1.0 final (release candidate 2 and later, to be
precise), PIL can attach itself to Tkinter in flight. As a result,
you no longer need to rebuild the Tkinter extension to be able to
use PIL.
However, if you cannot get the this to work on your platform, you
can do it in the old way:
* Adding Tkinter support
1. Compile Python's _tkinter.c with the WITH_APPINIT and WITH_PIL
flags set, and link it with tkImaging.c and tkappinit.c. To
do this, copy the former to the Modules directory, and edit
the _tkinter line in Setup (or Setup.in) according to the
instructions in that file.
NOTE: if you have an old Python version, the tkappinit.c
file is not included by default. If this is the case, you
will have to add the following lines to tkappinit.c, after
the MOREBUTTONS stuff:
{
extern void TkImaging_Init(Tcl_Interp* interp);
TkImaging_Init(interp);
}
This registers a Tcl command called "PyImagingPhoto", which is
use to communicate between PIL and Tk's PhotoImage handler.
You must also change the _tkinter line in Setup (or Setup.in)
to something like:
_tkinter _tkinter.c tkImaging.c tkappinit.c -DWITH_APPINIT
-I/usr/local/include -L/usr/local/lib -ltk8.0 -ltcl8.0 -lX11

View File

@ -1,149 +0,0 @@
====================================================================
The PIL Bitmap Booster Patch
====================================================================
The pilbitmap booster patch greatly improves performance of the
ImageTk.BitmapImage constructor. Unfortunately, the design of Tk
doesn't allow us to do this from the tkImaging interface module, so
you have to patch the Tk sources.
Once installed, the ImageTk module will automatically detect this
patch.
(Note: this patch has been tested with Tk 8.0 on Win32 only, but it
should work just fine on other platforms as well).
1. To the beginning of TkGetBitmapData (in generic/tkImgBmap.c), add
the following stuff:
------------------------------------------------------------------------
int width, height, numBytes, hotX, hotY;
char *p, *end, *expandedFileName;
ParseInfo pi;
char *data = NULL;
Tcl_DString buffer;
/* ==================================================================== */
/* The pilbitmap booster patch -- patch section */
/* ==================================================================== */
char *PILGetBitmapData();
if (string) {
/* Is this a PIL bitmap reference? */
data = PILGetBitmapData(string, widthPtr, heightPtr, hotXPtr, hotYPtr);
if (data)
return data;
}
/* ==================================================================== */
pi.string = string;
if (string == NULL) {
if (Tcl_IsSafe(interp)) {
------------------------------------------------------------------------
2. Append the following to the same file (you may wish to include
Imaging.h instead of copying the struct declaration...)
------------------------------------------------------------------------
/* ==================================================================== */
/* The pilbitmap booster patch -- code section */
/* ==================================================================== */
/* Imaging declaration boldly copied from Imaging.h (!) */
typedef struct ImagingInstance *Imaging; /* a.k.a. ImagingImage :-) */
typedef unsigned char UINT8;
typedef int INT32;
struct ImagingInstance {
/* Format */
char mode[4+1]; /* Band names ("1", "L", "P", "RGB", "RGBA", "CMYK") */
int type; /* Always 0 in this version */
int depth; /* Always 8 in this version */
int bands; /* Number of bands (1, 3, or 4) */
int xsize; /* Image dimension. */
int ysize;
/* Colour palette (for "P" images only) */
void* palette;
/* Data pointers */
UINT8 **image8; /* Set for 8-bit image (pixelsize=1). */
INT32 **image32; /* Set for 32-bit image (pixelsize=4). */
/* Internals */
char **image; /* Actual raster data. */
char *block; /* Set if data is allocated in a single block. */
int pixelsize; /* Size of a pixel, in bytes (1 or 4) */
int linesize; /* Size of a line, in bytes (xsize * pixelsize) */
/* Virtual methods */
void (*im_delete)(Imaging *);
};
/* The pilbitmap booster patch allows you to pass PIL images to the
Tk bitmap decoder. Passing images this way is much more efficient
than using the "tobitmap" method. */
char *
PILGetBitmapData(string, widthPtr, heightPtr, hotXPtr, hotYPtr)
char *string;
int *widthPtr, *heightPtr;
int *hotXPtr, *hotYPtr;
{
char* data;
char* p;
int y;
Imaging im;
if (strncmp(string, "PIL:", 4) != 0)
return NULL;
im = (Imaging) atol(string + 4);
if (strcmp(im->mode, "1") != 0 && strcmp(im->mode, "L") != 0)
return NULL;
data = p = (char *) ckalloc((unsigned) ((im->xsize+7)/8) * im->ysize);
for (y = 0; y < im->ysize; y++) {
char* in = im->image8[y];
int i, m, b;
b = 0; m = 1;
for (i = 0; i < im->xsize; i++) {
if (in[i] != 0)
b |= m;
m <<= 1;
if (m == 256){
*p++ = b;
b = 0; m = 1;
}
}
if (m != 1)
*p++ = b;
}
*widthPtr = im->xsize;
*heightPtr = im->ysize;
*hotXPtr = -1;
*hotYPtr = -1;
return data;
}
/* ==================================================================== */
------------------------------------------------------------------------
3. Recompile Tk and relink the _tkinter module (where necessary).
====================================================================
Last updated: 97-05-17/fl

303
_imagingmorph.c Normal file
View File

@ -0,0 +1,303 @@
/*
* The Python Imaging Library
*
* A binary morphology add-on for the Python Imaging Library
*
* History:
* 2014-06-04 Initial version.
*
* Copyright (c) 2014 Dov Grobgeld <dov.grobgeld@gmail.com>
*
* See the README file for information on usage and redistribution.
*/
#include "Python.h"
#include "Imaging.h"
#include "py3.h"
#define LUT_SIZE (1<<9)
/* Apply a morphologic LUT to a binary image. Outputs a
a new binary image.
Expected parameters:
1. a LUT - a 512 byte size lookup table.
2. an input Imaging image id.
3. an output Imaging image id
Returns number of changed pixels.
*/
static PyObject*
apply(PyObject *self, PyObject* args)
{
const char *lut;
PyObject *py_lut;
Py_ssize_t lut_len, i0, i1;
Imaging imgin, imgout;
int width, height;
int row_idx, col_idx;
UINT8 **inrows, **outrows;
int num_changed_pixels = 0;
if (!PyArg_ParseTuple(args, "Onn", &py_lut, &i0, &i1)) {
PyErr_SetString(PyExc_RuntimeError, "Argument parsing problem");
return NULL;
}
if (!PyBytes_Check(py_lut)) {
PyErr_SetString(PyExc_RuntimeError, "The morphology LUT is not a bytes object");
return NULL;
}
lut_len = PyBytes_Size(py_lut);
if (lut_len < LUT_SIZE) {
PyErr_SetString(PyExc_RuntimeError, "The morphology LUT has the wrong size");
return NULL;
}
lut = PyBytes_AsString(py_lut);
imgin = (Imaging) i0;
imgout = (Imaging) i1;
width = imgin->xsize;
height = imgin->ysize;
if (imgin->type != IMAGING_TYPE_UINT8 &&
imgin->bands != 1) {
PyErr_SetString(PyExc_RuntimeError, "Unsupported image type");
return NULL;
}
if (imgout->type != IMAGING_TYPE_UINT8 &&
imgout->bands != 1) {
PyErr_SetString(PyExc_RuntimeError, "Unsupported image type");
return NULL;
}
inrows = imgin->image8;
outrows = imgout->image8;
for (row_idx=0; row_idx < height; row_idx++) {
UINT8 *outrow = outrows[row_idx];
UINT8 *inrow = inrows[row_idx];
UINT8 *prow, *nrow; /* Previous and next row */
/* zero boundary conditions. TBD support other modes */
outrow[0] = outrow[width-1] = 0;
if (row_idx==0 || row_idx == height-1) {
for(col_idx=0; col_idx<width; col_idx++)
outrow[col_idx] = 0;
continue;
}
prow = inrows[row_idx-1];
nrow = inrows[row_idx+1];
for (col_idx=1; col_idx<width-1; col_idx++) {
int cim = col_idx-1;
int cip = col_idx+1;
unsigned char b0 = prow[cim] &1;
unsigned char b1 = prow[col_idx]&1;
unsigned char b2 = prow[cip]&1;
unsigned char b3 = inrow[cim]&1;
unsigned char b4 = inrow[col_idx]&1;
unsigned char b5 = inrow[cip]&1;
unsigned char b6 = nrow[cim]&1;
unsigned char b7 = nrow[col_idx]&1;
unsigned char b8 = nrow[cip]&1;
int lut_idx = (b0
|(b1 << 1)
|(b2 << 2)
|(b3 << 3)
|(b4 << 4)
|(b5 << 5)
|(b6 << 6)
|(b7 << 7)
|(b8 << 8));
outrow[col_idx] = 255*(lut[lut_idx]&1);
num_changed_pixels += ((b4&1)!=(outrow[col_idx]&1));
}
}
return Py_BuildValue("i",num_changed_pixels);
}
/* Match a morphologic LUT to a binary image and return a list
of the coordinates of all all matching pixels.
Expected parameters:
1. a LUT - a 512 byte size lookup table.
2. an input Imaging image id.
Returns list of matching pixels.
*/
static PyObject*
match(PyObject *self, PyObject* args)
{
const char *lut;
PyObject *py_lut;
Py_ssize_t lut_len, i0;
Imaging imgin;
int width, height;
int row_idx, col_idx;
UINT8 **inrows;
PyObject *ret = PyList_New(0);
if (!PyArg_ParseTuple(args, "On", &py_lut, &i0)) {
PyErr_SetString(PyExc_RuntimeError, "Argument parsing problem");
return NULL;
}
if (!PyBytes_Check(py_lut)) {
PyErr_SetString(PyExc_RuntimeError, "The morphology LUT is not a bytes object");
return NULL;
}
lut_len = PyBytes_Size(py_lut);
if (lut_len < LUT_SIZE) {
PyErr_SetString(PyExc_RuntimeError, "The morphology LUT has the wrong size");
return NULL;
}
lut = PyBytes_AsString(py_lut);
imgin = (Imaging) i0;
if (imgin->type != IMAGING_TYPE_UINT8 &&
imgin->bands != 1) {
PyErr_SetString(PyExc_RuntimeError, "Unsupported image type");
return NULL;
}
inrows = imgin->image8;
width = imgin->xsize;
height = imgin->ysize;
for (row_idx=1; row_idx < height-1; row_idx++) {
UINT8 *inrow = inrows[row_idx];
UINT8 *prow, *nrow;
prow = inrows[row_idx-1];
nrow = inrows[row_idx+1];
for (col_idx=1; col_idx<width-1; col_idx++) {
int cim = col_idx-1;
int cip = col_idx+1;
unsigned char b0 = prow[cim] &1;
unsigned char b1 = prow[col_idx]&1;
unsigned char b2 = prow[cip]&1;
unsigned char b3 = inrow[cim]&1;
unsigned char b4 = inrow[col_idx]&1;
unsigned char b5 = inrow[cip]&1;
unsigned char b6 = nrow[cim]&1;
unsigned char b7 = nrow[col_idx]&1;
unsigned char b8 = nrow[cip]&1;
int lut_idx = (b0
|(b1 << 1)
|(b2 << 2)
|(b3 << 3)
|(b4 << 4)
|(b5 << 5)
|(b6 << 6)
|(b7 << 7)
|(b8 << 8));
if (lut[lut_idx]) {
PyObject *coordObj = Py_BuildValue("(nn)",col_idx,row_idx);
PyList_Append(ret, coordObj);
}
}
}
return ret;
}
/* Return a list of the coordinates of all turned on pixels in an image.
May be used to extract features after a sequence of MorphOp's were applied.
This is faster than match as only 1x1 lookup is made.
*/
static PyObject*
get_on_pixels(PyObject *self, PyObject* args)
{
Py_ssize_t i0;
Imaging img;
UINT8 **rows;
int row_idx, col_idx;
int width, height;
PyObject *ret = PyList_New(0);
if (!PyArg_ParseTuple(args, "n", &i0)) {
PyErr_SetString(PyExc_RuntimeError, "Argument parsing problem");
return NULL;
}
img = (Imaging) i0;
rows = img->image8;
width = img->xsize;
height = img->ysize;
for (row_idx=0; row_idx < height; row_idx++) {
UINT8 *row = rows[row_idx];
for (col_idx=0; col_idx<width; col_idx++) {
if (row[col_idx]) {
PyObject *coordObj = Py_BuildValue("(nn)",col_idx,row_idx);
PyList_Append(ret, coordObj);
}
}
}
return ret;
}
static int
setup_module(PyObject* m)
{
PyObject* d = PyModule_GetDict(m);
PyDict_SetItemString(d, "__version", PyUnicode_FromString("0.1"));
return 0;
}
static PyMethodDef functions[] = {
/* Functions */
{"apply", (PyCFunction)apply, 1},
{"get_on_pixels", (PyCFunction)get_on_pixels, 1},
{"match", (PyCFunction)match, 1},
};
#if PY_VERSION_HEX >= 0x03000000
PyMODINIT_FUNC
PyInit__imagingmorph(void) {
PyObject* m;
static PyModuleDef module_def = {
PyModuleDef_HEAD_INIT,
"_imagingmorph", /* m_name */
"A module for doing image morphology", /* m_doc */
-1, /* m_size */
functions, /* m_methods */
};
m = PyModule_Create(&module_def);
if (setup_module(m) < 0)
return NULL;
return m;
}
#else
PyMODINIT_FUNC
init_imagingmorph(void)
{
PyObject* m = Py_InitModule("_imagingmorph", functions);
setup_module(m);
}
#endif

View File

@ -797,8 +797,9 @@ PyImaging_Jpeg2KDecoderNew(PyObject* self, PyObject* args)
int reduce = 0;
int layers = 0;
int fd = -1;
if (!PyArg_ParseTuple(args, "ss|iii", &mode, &format,
&reduce, &layers, &fd))
PY_LONG_LONG length = -1;
if (!PyArg_ParseTuple(args, "ss|iiiL", &mode, &format,
&reduce, &layers, &fd, &length))
return NULL;
if (strcmp(format, "j2k") == 0)
@ -821,6 +822,7 @@ PyImaging_Jpeg2KDecoderNew(PyObject* self, PyObject* args)
context = (JPEG2KDECODESTATE *)decoder->state.context;
context->fd = fd;
context->length = (off_t)length;
context->format = codec_format;
context->reduce = reduce;
context->layers = layers;

View File

@ -2,15 +2,16 @@
# install openjpeg
if [ ! -f openjpeg-2.0.0.tar.gz ]; then
wget 'https://openjpeg.googlecode.com/files/openjpeg-2.0.0.tar.gz'
if [ ! -f openjpeg-2.1.0.tar.gz ]; then
wget 'http://iweb.dl.sourceforge.net/project/openjpeg.mirror/2.1.0/openjpeg-2.1.0.tar.gz'
fi
rm -r openjpeg-2.0.0
tar -xvzf openjpeg-2.0.0.tar.gz
rm -r openjpeg-2.1.0
tar -xvzf openjpeg-2.1.0.tar.gz
pushd openjpeg-2.0.0
pushd openjpeg-2.1.0
cmake -DCMAKE_INSTALL_PREFIX=/usr . && make && sudo make install

View File

@ -73,7 +73,7 @@ Many of Pillow's features require external libraries:
* **openjpeg** provides JPEG 2000 functionality.
* Pillow has been tested with openjpeg **2.0.0**.
* Pillow has been tested with openjpeg **2.0.0** and **2.1.0**.
If the prerequisites are installed in the standard library locations for your
machine (e.g. :file:`/usr` or :file:`/usr/local`), no additional configuration

View File

@ -8,7 +8,7 @@
* Copyright (c) 2014 by Alastair Houghton
*/
#include <openjpeg-2.0/openjpeg.h>
#include <openjpeg.h>
/* -------------------------------------------------------------------- */
/* Decoder */
@ -20,6 +20,9 @@ typedef struct {
/* File descriptor, if available; otherwise, -1 */
int fd;
/* Length of data, if available; otherwise, -1 */
off_t length;
/* Specify the desired format */
OPJ_CODEC_FORMAT format;

View File

@ -517,7 +517,21 @@ j2k_decode_entry(Imaging im, ImagingCodecState state,
opj_stream_set_read_function(stream, j2k_read);
opj_stream_set_skip_function(stream, j2k_skip);
/* OpenJPEG 2.0 doesn't have OPJ_VERSION_MAJOR */
#ifndef OPJ_VERSION_MAJOR
opj_stream_set_user_data(stream, decoder);
#else
opj_stream_set_user_data(stream, decoder, NULL);
/* Hack: if we don't know the length, the largest file we can
possibly support is 4GB. We can't go larger than this, because
OpenJPEG truncates this value for the final box in the file, and
the box lengths in OpenJPEG are currently 32 bit. */
if (context->length < 0)
opj_stream_set_user_data_length(stream, 0xffffffff);
else
opj_stream_set_user_data_length(stream, context->length);
#endif
/* Setup decompression context */
context->error_msg = NULL;

View File

@ -259,7 +259,12 @@ j2k_encode_entry(Imaging im, ImagingCodecState state,
opj_stream_set_skip_function(stream, j2k_skip);
opj_stream_set_seek_function(stream, j2k_seek);
/* OpenJPEG 2.0 doesn't have OPJ_VERSION_MAJOR */
#ifndef OPJ_VERSION_MAJOR
opj_stream_set_user_data(stream, encoder);
#else
opj_stream_set_user_data(stream, encoder, NULL);
#endif
/* Setup an opj_image */
if (strcmp (im->mode, "L") == 0) {

View File

@ -13,11 +13,6 @@
#include <tiff.h>
#endif
#ifndef _UNISTD_H
#include <unistd.h>
#endif
#ifndef min
#define min(x,y) (( x > y ) ? y : x )
#define max(x,y) (( x < y ) ? y : x )
@ -43,11 +38,10 @@ extern int ImagingLibTiffEncodeInit(ImagingCodecState state, char *filename, int
extern int ImagingLibTiffSetField(ImagingCodecState state, ttag_t tag, ...);
#if defined(_MSC_VER) && (_MSC_VER == 1310)
/* VS2003/py2.4 can't use varargs. Skipping trace for now.*/
#define TRACE(args)
#else
/*
Trace debugging
legacy, don't enable for python 3.x, unicode issues.
*/
/*
#define VA_ARGS(...) __VA_ARGS__
@ -56,8 +50,5 @@ extern int ImagingLibTiffSetField(ImagingCodecState state, ttag_t tag, ...);
#define TRACE(args)
#endif /* _MSC_VER */
#endif

View File

@ -49,13 +49,13 @@ def testimage():
Or open existing files:
>>> im = Image.open(os.path.join(ROOT, "Images/lena.gif"))
>>> im = Image.open(os.path.join(ROOT, "Tests/images/lena.gif"))
>>> _info(im)
('GIF', 'P', (128, 128))
>>> _info(Image.open(os.path.join(ROOT, "Images/lena.ppm")))
>>> _info(Image.open(os.path.join(ROOT, "Tests/images/lena.ppm")))
('PPM', 'RGB', (128, 128))
>>> try:
... _info(Image.open(os.path.join(ROOT, "Images/lena.jpg")))
... _info(Image.open(os.path.join(ROOT, "Tests/images/lena.jpg")))
... except IOError as v:
... print(v)
('JPEG', 'RGB', (128, 128))
@ -63,7 +63,7 @@ def testimage():
PIL doesn't actually load the image data until it's needed,
or you call the "load" method:
>>> im = Image.open(os.path.join(ROOT, "Images/lena.ppm"))
>>> im = Image.open(os.path.join(ROOT, "Tests/images/lena.ppm"))
>>> print(im.im) # internal image attribute
None
>>> a = im.load()
@ -73,7 +73,7 @@ def testimage():
You can apply many different operations on images. Most
operations return a new image:
>>> im = Image.open(os.path.join(ROOT, "Images/lena.ppm"))
>>> im = Image.open(os.path.join(ROOT, "Tests/images/lena.ppm"))
>>> _info(im.convert("L"))
(None, 'L', (128, 128))
>>> _info(im.copy())

View File

@ -337,14 +337,23 @@ class pil_build_ext(build_ext):
_add_directory(include_dirs, "/usr/include")
# on Windows, look for the OpenJPEG libraries in the location that
# the official installed puts them
# the official installer puts them
if sys.platform == "win32":
_add_directory(library_dirs,
os.path.join(os.environ.get("ProgramFiles", ""),
"OpenJPEG 2.0", "lib"))
_add_directory(include_dirs,
os.path.join(os.environ.get("ProgramFiles", ""),
"OpenJPEG 2.0", "include"))
program_files = os.environ.get('ProgramFiles', '')
best_version = (0, 0)
best_path = None
for name in os.listdir(program_files):
if name.startswith('OpenJPEG '):
version = tuple([int(x) for x in name[9:].strip().split('.')])
if version > best_version:
best_version = version
best_path = os.path.join(program_files, name)
if best_path:
_add_directory(library_dirs,
os.path.join(best_path, 'lib'))
_add_directory(include_dirs,
os.path.join(best_path, 'include'))
#
# insert new dirs *before* default libs, to avoid conflicts
@ -374,11 +383,30 @@ class pil_build_ext(build_ext):
_find_library_file(self, "libjpeg")):
feature.jpeg = "libjpeg" # alternative name
feature.openjpeg_version = None
if feature.want('jpeg2000'):
if _find_include_file(self, "openjpeg-2.0/openjpeg.h"):
if _find_library_file(self, "openjp2"):
feature.jpeg2000 = "openjp2"
best_version = None
best_path = None
# Find the best version
for directory in self.compiler.include_dirs:
for name in os.listdir(directory):
if name.startswith('openjpeg-') and \
os.path.isfile(os.path.join(directory, name,
'openjpeg.h')):
version = tuple([int(x) for x in name[9:].split('.')])
if best_version is None or version > best_version:
best_version = version
best_path = os.path.join(directory, name)
if best_version and _find_library_file(self, 'openjp2'):
# Add the directory to the include path so we can include
# <openjpeg.h> rather than having to cope with the versioned
# include path
_add_directory(self.compiler.include_dirs, best_path, 0)
feature.jpeg2000 = 'openjp2'
feature.openjpeg_version = '.'.join([str(x) for x in best_version])
if feature.want('tiff'):
if _find_library_file(self, "tiff"):
feature.tiff = "tiff"
@ -543,6 +571,9 @@ class pil_build_ext(build_ext):
if os.path.isfile("_imagingmath.c"):
exts.append(Extension("PIL._imagingmath", ["_imagingmath.c"]))
if os.path.isfile("_imagingmorph.c"):
exts.append(Extension("PIL._imagingmorph", ["_imagingmorph.c"]))
self.extensions[:] = exts
build_ext.build_extensions(self)
@ -572,7 +603,7 @@ class pil_build_ext(build_ext):
options = [
(feature.tcl and feature.tk, "TKINTER"),
(feature.jpeg, "JPEG"),
(feature.jpeg2000, "OPENJPEG (JPEG2000)"),
(feature.jpeg2000, "OPENJPEG (JPEG2000)", feature.openjpeg_version),
(feature.zlib, "ZLIB (PNG/ZIP)"),
(feature.tiff, "LIBTIFF"),
(feature.freetype, "FREETYPE2"),
@ -583,7 +614,10 @@ class pil_build_ext(build_ext):
all = 1
for option in options:
if option[0]:
print("--- %s support available" % option[1])
version = ''
if len(option) >= 3 and option[2]:
version = ' (%s)' % option[2]
print("--- %s support available%s" % (option[1], version))
else:
print("*** %s support not available" % option[1])
if option[1] == "TKINTER" and _tkinter:

24
test-installed.py Executable file
View File

@ -0,0 +1,24 @@
#!/usr/bin/env python
import nose
import os
import sys
import glob
# monkey with the path, removing the local directory but adding the Tests/ directory
# for helper.py and the other local imports there.
del(sys.path[0])
sys.path.insert(0, os.path.abspath('./Tests'))
# if there's no test selected (mostly) choose a working default.
# Something is required, because if we import the tests from the local
# directory, once again, we've got the non-installed PIL in the way
if len(sys.argv) == 1:
sys.argv.extend(glob.glob('Tests/test*.py'))
# Make sure that nose doesn't muck with our paths.
if ('--no-path-adjustment' not in sys.argv) and ('-P' not in sys.argv):
sys.argv.insert(1, '--no-path-adjustment')
if __name__ == '__main__':
nose.main()