Merge branch 'master' into jpeg_qtables
15
CHANGES.rst
|
@ -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]
|
||||
|
||||
|
|
29
PIL/Image.py
|
@ -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()
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
@ -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
|
||||
|
||||
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
@ -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
|
|
@ -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
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 1.2 KiB |
BIN
Tests/images/dilation4.lut
Normal file
BIN
Tests/images/dilation8.lut
Normal file
BIN
Tests/images/edge.lut
Normal file
BIN
Tests/images/erosion4.lut
Normal file
BIN
Tests/images/erosion8.lut
Normal file
Before Width: | Height: | Size: 29 KiB After Width: | Height: | Size: 29 KiB |
Before Width: | Height: | Size: 21 KiB After Width: | Height: | Size: 21 KiB |
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 16 KiB |
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 1.4 KiB |
Before Width: | Height: | Size: 4.7 KiB After Width: | Height: | Size: 4.7 KiB |
Before Width: | Height: | Size: 37 KiB After Width: | Height: | Size: 37 KiB |
Before Width: | Height: | Size: 3.8 KiB After Width: | Height: | Size: 3.8 KiB |
BIN
Tests/images/morph_a.png
Normal file
After Width: | Height: | Size: 83 B |
Before Width: | Height: | Size: 100 KiB After Width: | Height: | Size: 100 KiB |
Before Width: | Height: | Size: 26 KiB After Width: | Height: | Size: 26 KiB |
Before Width: | Height: | Size: 7.9 KiB After Width: | Height: | Size: 7.9 KiB |
45
Tests/test_decompression_bomb.py
Normal 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
|
|
@ -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()
|
||||
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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')
|
||||
|
|
|
@ -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()
|
||||
|
||||
|
||||
|
|
|
@ -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())
|
||||
|
||||
|
|
|
@ -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()
|
||||
|
||||
|
||||
|
|
|
@ -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()
|
||||
|
||||
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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")
|
||||
|
|
|
@ -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()
|
||||
|
||||
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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")
|
||||
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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
|
@ -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)))
|
|
@ -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):
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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":
|
||||
|
|
|
@ -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
|
@ -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
|
108
Tk/booster.txt
|
@ -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
|
|
@ -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
|
||||
|
||||
|
||||
|
149
Tk/pilbitmap.txt
|
@ -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
|
@ -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
|
||||
|
6
decode.c
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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
|
||||
|
|
10
selftest.py
|
@ -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())
|
||||
|
|
58
setup.py
|
@ -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
|
@ -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()
|