Merge branch 'master' into jpeg_qtables
15
CHANGES.rst
|
@ -4,6 +4,21 @@ Changelog (Pillow)
|
||||||
2.5.0 (unreleased)
|
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
|
- Use unittest for tests
|
||||||
[hugovk]
|
[hugovk]
|
||||||
|
|
||||||
|
|
29
PIL/Image.py
|
@ -31,11 +31,18 @@ from PIL import VERSION, PILLOW_VERSION, _plugins
|
||||||
import warnings
|
import warnings
|
||||||
|
|
||||||
|
|
||||||
|
class DecompressionBombWarning(RuntimeWarning):
|
||||||
|
pass
|
||||||
|
|
||||||
class _imaging_not_installed:
|
class _imaging_not_installed:
|
||||||
# module placeholder
|
# module placeholder
|
||||||
def __getattr__(self, id):
|
def __getattr__(self, id):
|
||||||
raise ImportError("The _imaging C module is not installed")
|
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:
|
try:
|
||||||
# give Tk a chance to set up the environment, in case we're
|
# give Tk a chance to set up the environment, in case we're
|
||||||
# using an _imaging module linked against libtcl/libtk (use
|
# 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")
|
_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"):
|
def open(fp, mode="r"):
|
||||||
"""
|
"""
|
||||||
Opens and identifies the given image file.
|
Opens and identifies the given image file.
|
||||||
|
@ -2209,7 +2230,9 @@ def open(fp, mode="r"):
|
||||||
factory, accept = OPEN[i]
|
factory, accept = OPEN[i]
|
||||||
if not accept or accept(prefix):
|
if not accept or accept(prefix):
|
||||||
fp.seek(0)
|
fp.seek(0)
|
||||||
return factory(fp, filename)
|
im = factory(fp, filename)
|
||||||
|
_decompression_bomb_check(im.size)
|
||||||
|
return im
|
||||||
except (SyntaxError, IndexError, TypeError):
|
except (SyntaxError, IndexError, TypeError):
|
||||||
# import traceback
|
# import traceback
|
||||||
# traceback.print_exc()
|
# traceback.print_exc()
|
||||||
|
@ -2222,7 +2245,9 @@ def open(fp, mode="r"):
|
||||||
factory, accept = OPEN[i]
|
factory, accept = OPEN[i]
|
||||||
if not accept or accept(prefix):
|
if not accept or accept(prefix):
|
||||||
fp.seek(0)
|
fp.seek(0)
|
||||||
return factory(fp, filename)
|
im = factory(fp, filename)
|
||||||
|
_decompression_bomb_check(im.size)
|
||||||
|
return im
|
||||||
except (SyntaxError, IndexError, TypeError):
|
except (SyntaxError, IndexError, TypeError):
|
||||||
# import traceback
|
# import traceback
|
||||||
# traceback.print_exc()
|
# traceback.print_exc()
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
#
|
#
|
||||||
# For a background, see "Image Processing By Interpolation and
|
# For a background, see "Image Processing By Interpolation and
|
||||||
# Extrapolation", Paul Haeberli and Douglas Voorhies. Available
|
# 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:
|
# History:
|
||||||
# 1996-03-23 fl Created
|
# 1996-03-23 fl Created
|
||||||
|
|
|
@ -396,7 +396,7 @@ w7IkEbzhVQAAAABJRU5ErkJggg==
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
# create font data chunk for embedding
|
# create font data chunk for embedding
|
||||||
import base64, os, sys
|
import base64, os, sys
|
||||||
font = "../Images/courB08"
|
font = "../Tests/images/courB08"
|
||||||
print(" f._load_pilfont_data(")
|
print(" f._load_pilfont_data(")
|
||||||
print(" # %s" % os.path.basename(font))
|
print(" # %s" % os.path.basename(font))
|
||||||
print(" BytesIO(base64.decodestring(b'''")
|
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
|
self.layers = 0
|
||||||
|
|
||||||
fd = -1
|
fd = -1
|
||||||
|
length = -1
|
||||||
|
|
||||||
if hasattr(self.fp, "fileno"):
|
if hasattr(self.fp, "fileno"):
|
||||||
try:
|
try:
|
||||||
fd = self.fp.fileno()
|
fd = self.fp.fileno()
|
||||||
|
length = os.fstat(fd).st_size
|
||||||
except:
|
except:
|
||||||
fd = -1
|
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.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):
|
def load(self):
|
||||||
if self.reduce:
|
if self.reduce:
|
||||||
|
@ -178,7 +188,7 @@ class Jpeg2KImageFile(ImageFile.ImageFile):
|
||||||
if self.tile:
|
if self.tile:
|
||||||
# Update the reduce and layers settings
|
# Update the reduce and layers settings
|
||||||
t = self.tile[0]
|
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)]
|
self.tile = [(t[0], (0, 0) + self.size, t[2], t3)]
|
||||||
|
|
||||||
ImageFile.ImageFile.load(self)
|
ImageFile.ImageFile.load(self)
|
||||||
|
|
|
@ -14,11 +14,11 @@
|
||||||
#
|
#
|
||||||
|
|
||||||
# NOTE: This format cannot be automatically recognized, so the reader
|
# 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.
|
# the WalImageFile.open() function instead.
|
||||||
|
|
||||||
# This reader is based on the specification available from:
|
# 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.
|
# and has been tested with a few sample files found using google.
|
||||||
|
|
||||||
from __future__ import print_function
|
from __future__ import print_function
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
|
|
||||||
Python SANE module V1.1 (30 Sep. 2004)
|
Python SANE module V1.1 (30 Sep. 2004)
|
||||||
|
================================================================================
|
||||||
|
|
||||||
The SANE module provides an interface to the SANE scanner and frame
|
The SANE module provides an interface to the SANE scanner and frame
|
||||||
grabber interface for Linux. This module was contributed by Andrew
|
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.
|
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
|
python setup.py build
|
||||||
|
|
||||||
In order to install the module type:
|
In order to install the module type::
|
||||||
|
|
||||||
python setup.py install
|
python setup.py install
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
-------
|
|
||||||
Scripts
|
Scripts
|
||||||
-------
|
=======
|
||||||
|
|
||||||
This directory contains a number of more or less trivial utilities
|
This directory contains a number of more or less trivial utilities
|
||||||
and demo programs.
|
and demo programs.
|
||||||
|
@ -9,50 +8,50 @@ Comments and contributions are welcome.
|
||||||
|
|
||||||
</F>
|
</F>
|
||||||
|
|
||||||
--------------------------------------------------------------------
|
|
||||||
pildriver.py (by Eric S. Raymond)
|
pildriver.py (by Eric S. Raymond)
|
||||||
|
--------------------------------------------------------------------
|
||||||
|
|
||||||
A class implementing an image-processing calculator for scripts.
|
A class implementing an image-processing calculator for scripts.
|
||||||
Parses lists of commnds (or, called interactively, command-line
|
Parses lists of commnds (or, called interactively, command-line
|
||||||
arguments) into image loads, transformations, and saves.
|
arguments) into image loads, transformations, and saves.
|
||||||
|
|
||||||
--------------------------------------------------------------------
|
|
||||||
viewer.py
|
viewer.py
|
||||||
|
--------------------------------------------------------------------
|
||||||
|
|
||||||
A simple image viewer. Can display all file formats handled by
|
A simple image viewer. Can display all file formats handled by
|
||||||
PIL. Transparent images are properly handled.
|
PIL. Transparent images are properly handled.
|
||||||
|
|
||||||
--------------------------------------------------------------------
|
|
||||||
thresholder.py
|
thresholder.py
|
||||||
|
--------------------------------------------------------------------
|
||||||
|
|
||||||
A simple utility that demonstrates how a transparent 1-bit overlay
|
A simple utility that demonstrates how a transparent 1-bit overlay
|
||||||
can be used to show the current thresholding of an 8-bit image.
|
can be used to show the current thresholding of an 8-bit image.
|
||||||
|
|
||||||
--------------------------------------------------------------------
|
|
||||||
enhancer.py
|
enhancer.py
|
||||||
|
--------------------------------------------------------------------
|
||||||
|
|
||||||
Illustrates the ImageEnhance module. Drag the sliders to modify the
|
Illustrates the ImageEnhance module. Drag the sliders to modify the
|
||||||
images. This might be very slow on some platforms, depending on the
|
images. This might be very slow on some platforms, depending on the
|
||||||
Tk version.
|
Tk version.
|
||||||
|
|
||||||
--------------------------------------------------------------------
|
|
||||||
painter.py
|
painter.py
|
||||||
|
--------------------------------------------------------------------
|
||||||
|
|
||||||
Illustrates how a painting program could be based on PIL and Tk.
|
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
|
Press the left mouse button and drag over the image to remove the
|
||||||
colour. Some clever tricks have been used to get decent performance
|
colour. Some clever tricks have been used to get decent performance
|
||||||
when updating the screen; see the sources for details.
|
when updating the screen; see the sources for details.
|
||||||
|
|
||||||
--------------------------------------------------------------------
|
|
||||||
player.py
|
player.py
|
||||||
|
--------------------------------------------------------------------
|
||||||
|
|
||||||
A simple image sequence player. You can use either a sequence format
|
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
|
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
|
interpreted as frames in a sequence. All frames must have the same
|
||||||
size.
|
size.
|
||||||
|
|
||||||
--------------------------------------------------------------------
|
|
||||||
gifmaker.py
|
gifmaker.py
|
||||||
|
--------------------------------------------------------------------
|
||||||
|
|
||||||
Convert a sequence file to a GIF animation.
|
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
|
uncompressed GIF files only, so the resulting animations are rather
|
||||||
large compared with these created by other tools.
|
large compared with these created by other tools.
|
||||||
|
|
||||||
--------------------------------------------------------------------
|
|
||||||
explode.py
|
explode.py
|
||||||
|
--------------------------------------------------------------------
|
||||||
|
|
||||||
Split a sequence file into individual frames.
|
Split a sequence file into individual frames.
|
||||||
|
|
||||||
--------------------------------------------------------------------
|
|
||||||
image2py.py
|
image2py.py
|
||||||
|
--------------------------------------------------------------------
|
||||||
|
|
||||||
Convert an image to a Python module containing an IMAGE variable.
|
Convert an image to a Python module containing an IMAGE variable.
|
||||||
Note that the module using the module must include JPEG and ZIP
|
Note that the module using the module must include JPEG and ZIP
|
||||||
decoders, unless the -u option is used.
|
decoders, unless the -u option is used.
|
||||||
|
|
||||||
--------------------------------------------------------------------
|
|
||||||
olesummary.py
|
olesummary.py
|
||||||
|
--------------------------------------------------------------------
|
||||||
|
|
||||||
Uses the OleFileIO module to dump the summary information from an OLE
|
Uses the OleFileIO module to dump the summary information from an OLE
|
||||||
structured storage file. This works with most OLE files, including
|
structured storage file. This works with most OLE files, including
|
|
@ -233,7 +233,7 @@ def lena(mode="RGB", cache={}):
|
||||||
# im = cache.get(mode)
|
# im = cache.get(mode)
|
||||||
if im is None:
|
if im is None:
|
||||||
if mode == "RGB":
|
if mode == "RGB":
|
||||||
im = Image.open("Images/lena.ppm")
|
im = Image.open("Tests/images/lena.ppm")
|
||||||
elif mode == "F":
|
elif mode == "F":
|
||||||
im = lena("L").convert(mode)
|
im = lena("L").convert(mode)
|
||||||
elif mode[:4] == "I;16":
|
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
|
from PIL import Image
|
||||||
|
|
||||||
# sample ppm stream
|
# sample ppm stream
|
||||||
file = "Images/lena.fli"
|
file = "Tests/images/lena.fli"
|
||||||
data = open(file, "rb").read()
|
data = open(file, "rb").read()
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,7 @@ from PIL import Image
|
||||||
codecs = dir(Image.core)
|
codecs = dir(Image.core)
|
||||||
|
|
||||||
# sample gif stream
|
# sample gif stream
|
||||||
file = "Images/lena.gif"
|
file = "Tests/images/lena.gif"
|
||||||
with open(file, "rb") as f:
|
with open(file, "rb") as f:
|
||||||
data = f.read()
|
data = f.read()
|
||||||
|
|
||||||
|
@ -45,7 +45,7 @@ class TestFileGif(PillowTestCase):
|
||||||
def test_roundtrip2(self):
|
def test_roundtrip2(self):
|
||||||
# see https://github.com/python-pillow/Pillow/issues/403
|
# see https://github.com/python-pillow/Pillow/issues/403
|
||||||
out = self.tempfile('temp.gif')
|
out = self.tempfile('temp.gif')
|
||||||
im = Image.open('Images/lena.gif')
|
im = Image.open('Tests/images/lena.gif')
|
||||||
im2 = im.copy()
|
im2 = im.copy()
|
||||||
im2.save(out)
|
im2.save(out)
|
||||||
reread = Image.open(out)
|
reread = Image.open(out)
|
||||||
|
@ -55,7 +55,7 @@ class TestFileGif(PillowTestCase):
|
||||||
def test_palette_handling(self):
|
def test_palette_handling(self):
|
||||||
# see https://github.com/python-pillow/Pillow/issues/513
|
# 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.convert('RGB')
|
||||||
|
|
||||||
im = im.resize((100, 100), Image.ANTIALIAS)
|
im = im.resize((100, 100), Image.ANTIALIAS)
|
||||||
|
|
|
@ -3,7 +3,7 @@ from helper import unittest, PillowTestCase, tearDownModule
|
||||||
from PIL import Image
|
from PIL import Image
|
||||||
|
|
||||||
# sample icon file
|
# sample icon file
|
||||||
file = "Images/pillow.icns"
|
file = "Tests/images/pillow.icns"
|
||||||
data = open(file, "rb").read()
|
data = open(file, "rb").read()
|
||||||
|
|
||||||
enable_jpeg2k = hasattr(Image.core, 'jp2klib_version')
|
enable_jpeg2k = hasattr(Image.core, 'jp2klib_version')
|
||||||
|
|
|
@ -3,7 +3,7 @@ from helper import unittest, PillowTestCase, tearDownModule
|
||||||
from PIL import Image
|
from PIL import Image
|
||||||
|
|
||||||
# sample ppm stream
|
# sample ppm stream
|
||||||
file = "Images/lena.ico"
|
file = "Tests/images/lena.ico"
|
||||||
data = open(file, "rb").read()
|
data = open(file, "rb").read()
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -10,7 +10,7 @@ codecs = dir(Image.core)
|
||||||
|
|
||||||
# sample png stream
|
# sample png stream
|
||||||
|
|
||||||
file = "Images/lena.png"
|
file = "Tests/images/lena.png"
|
||||||
data = open(file, "rb").read()
|
data = open(file, "rb").read()
|
||||||
|
|
||||||
# stuff to create inline PNG images
|
# stuff to create inline PNG images
|
||||||
|
@ -204,10 +204,10 @@ class TestFilePng(PillowTestCase):
|
||||||
def test_load_verify(self):
|
def test_load_verify(self):
|
||||||
# Check open/load/verify exception (@PIL150)
|
# Check open/load/verify exception (@PIL150)
|
||||||
|
|
||||||
im = Image.open("Images/lena.png")
|
im = Image.open("Tests/images/lena.png")
|
||||||
im.verify()
|
im.verify()
|
||||||
|
|
||||||
im = Image.open("Images/lena.png")
|
im = Image.open("Tests/images/lena.png")
|
||||||
im.load()
|
im.load()
|
||||||
self.assertRaises(RuntimeError, lambda: im.verify())
|
self.assertRaises(RuntimeError, lambda: im.verify())
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,7 @@ from helper import unittest, PillowTestCase, tearDownModule
|
||||||
from PIL import Image
|
from PIL import Image
|
||||||
|
|
||||||
# sample ppm stream
|
# sample ppm stream
|
||||||
file = "Images/lena.ppm"
|
file = "Tests/images/lena.ppm"
|
||||||
data = open(file, "rb").read()
|
data = open(file, "rb").read()
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,7 @@ from helper import unittest, PillowTestCase, tearDownModule
|
||||||
from PIL import Image
|
from PIL import Image
|
||||||
|
|
||||||
# sample ppm stream
|
# sample ppm stream
|
||||||
file = "Images/lena.psd"
|
file = "Tests/images/lena.psd"
|
||||||
data = open(file, "rb").read()
|
data = open(file, "rb").read()
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,7 @@ from PIL import Image, TarIO
|
||||||
codecs = dir(Image.core)
|
codecs = dir(Image.core)
|
||||||
|
|
||||||
# sample ppm stream
|
# sample ppm stream
|
||||||
tarfile = "Images/lena.tar"
|
tarfile = "Tests/images/lena.tar"
|
||||||
|
|
||||||
|
|
||||||
class TestFileTar(PillowTestCase):
|
class TestFileTar(PillowTestCase):
|
||||||
|
|
|
@ -23,7 +23,7 @@ class TestFileWebp(PillowTestCase):
|
||||||
|
|
||||||
def test_read_rgb(self):
|
def test_read_rgb(self):
|
||||||
|
|
||||||
file_path = "Images/lena.webp"
|
file_path = "Tests/images/lena.webp"
|
||||||
image = Image.open(file_path)
|
image = Image.open(file_path)
|
||||||
|
|
||||||
self.assertEqual(image.mode, "RGB")
|
self.assertEqual(image.mode, "RGB")
|
||||||
|
@ -33,7 +33,7 @@ class TestFileWebp(PillowTestCase):
|
||||||
image.getdata()
|
image.getdata()
|
||||||
|
|
||||||
# generated with:
|
# 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')
|
target = Image.open('Tests/images/lena_webp_bits.ppm')
|
||||||
self.assert_image_similar(image, target, 20.0)
|
self.assert_image_similar(image, target, 20.0)
|
||||||
|
|
||||||
|
|
|
@ -22,7 +22,7 @@ class TestFileWebpAlpha(PillowTestCase):
|
||||||
|
|
||||||
def test_read_rgba(self):
|
def test_read_rgba(self):
|
||||||
# Generated with `cwebp transparent.png -o transparent.webp`
|
# Generated with `cwebp transparent.png -o transparent.webp`
|
||||||
file_path = "Images/transparent.webp"
|
file_path = "Tests/images/transparent.webp"
|
||||||
image = Image.open(file_path)
|
image = Image.open(file_path)
|
||||||
|
|
||||||
self.assertEqual(image.mode, "RGBA")
|
self.assertEqual(image.mode, "RGBA")
|
||||||
|
@ -33,7 +33,7 @@ class TestFileWebpAlpha(PillowTestCase):
|
||||||
|
|
||||||
image.tobytes()
|
image.tobytes()
|
||||||
|
|
||||||
target = Image.open('Images/transparent.png')
|
target = Image.open('Tests/images/transparent.png')
|
||||||
self.assert_image_similar(image, target, 20.0)
|
self.assert_image_similar(image, target, 20.0)
|
||||||
|
|
||||||
def test_write_lossless_rgb(self):
|
def test_write_lossless_rgb(self):
|
||||||
|
|
|
@ -15,7 +15,7 @@ class TestFileWebpMetadata(PillowTestCase):
|
||||||
|
|
||||||
def test_read_exif_metadata(self):
|
def test_read_exif_metadata(self):
|
||||||
|
|
||||||
file_path = "Images/flower.webp"
|
file_path = "Tests/images/flower.webp"
|
||||||
image = Image.open(file_path)
|
image = Image.open(file_path)
|
||||||
|
|
||||||
self.assertEqual(image.format, "WEBP")
|
self.assertEqual(image.format, "WEBP")
|
||||||
|
@ -54,7 +54,7 @@ class TestFileWebpMetadata(PillowTestCase):
|
||||||
|
|
||||||
def test_read_icc_profile(self):
|
def test_read_icc_profile(self):
|
||||||
|
|
||||||
file_path = "Images/flower2.webp"
|
file_path = "Tests/images/flower2.webp"
|
||||||
image = Image.open(file_path)
|
image = Image.open(file_path)
|
||||||
|
|
||||||
self.assertEqual(image.format, "WEBP")
|
self.assertEqual(image.format, "WEBP")
|
||||||
|
|
|
@ -3,7 +3,7 @@ from helper import unittest, PillowTestCase, tearDownModule
|
||||||
from PIL import Image
|
from PIL import Image
|
||||||
|
|
||||||
# sample ppm stream
|
# sample ppm stream
|
||||||
file = "Images/lena.xpm"
|
file = "Tests/images/lena.xpm"
|
||||||
data = open(file, "rb").read()
|
data = open(file, "rb").read()
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,7 @@ from helper import unittest, PillowTestCase, tearDownModule
|
||||||
|
|
||||||
from PIL import FontFile, BdfFontFile
|
from PIL import FontFile, BdfFontFile
|
||||||
|
|
||||||
filename = "Images/courB08.bdf"
|
filename = "Tests/images/courB08.bdf"
|
||||||
|
|
||||||
|
|
||||||
class TestFontBdf(PillowTestCase):
|
class TestFontBdf(PillowTestCase):
|
||||||
|
|
|
@ -36,7 +36,7 @@ class TestImageConvert(PillowTestCase):
|
||||||
self.assertEqual(orig, converted)
|
self.assertEqual(orig, converted)
|
||||||
|
|
||||||
def test_8bit(self):
|
def test_8bit(self):
|
||||||
im = Image.open('Images/lena.jpg')
|
im = Image.open('Tests/images/lena.jpg')
|
||||||
self._test_float_conversion(im.convert('L'))
|
self._test_float_conversion(im.convert('L'))
|
||||||
|
|
||||||
def test_16bit(self):
|
def test_16bit(self):
|
||||||
|
|
|
@ -3,7 +3,7 @@ from helper import unittest, PillowTestCase, tearDownModule, fromstring, tostrin
|
||||||
from PIL import Image
|
from PIL import Image
|
||||||
|
|
||||||
codecs = dir(Image.core)
|
codecs = dir(Image.core)
|
||||||
filename = "Images/lena.jpg"
|
filename = "Tests/images/lena.jpg"
|
||||||
data = tostring(Image.open(filename).resize((512, 512)), "JPEG")
|
data = tostring(Image.open(filename).resize((512, 512)), "JPEG")
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -16,14 +16,14 @@ class TestImageLoad(PillowTestCase):
|
||||||
self.assertEqual(pix[0, 0], (223, 162, 133))
|
self.assertEqual(pix[0, 0], (223, 162, 133))
|
||||||
|
|
||||||
def test_close(self):
|
def test_close(self):
|
||||||
im = Image.open("Images/lena.gif")
|
im = Image.open("Tests/images/lena.gif")
|
||||||
im.close()
|
im.close()
|
||||||
self.assertRaises(ValueError, lambda: im.load())
|
self.assertRaises(ValueError, lambda: im.load())
|
||||||
self.assertRaises(ValueError, lambda: im.getpixel((0, 0)))
|
self.assertRaises(ValueError, lambda: im.getpixel((0, 0)))
|
||||||
|
|
||||||
def test_contextmanager(self):
|
def test_contextmanager(self):
|
||||||
fn = None
|
fn = None
|
||||||
with Image.open("Images/lena.gif") as im:
|
with Image.open("Tests/images/lena.gif") as im:
|
||||||
fn = im.fp.fileno()
|
fn = im.fp.fileno()
|
||||||
os.fstat(fn)
|
os.fstat(fn)
|
||||||
|
|
||||||
|
|
|
@ -18,6 +18,8 @@ class TestImageCms(PillowTestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
try:
|
try:
|
||||||
from PIL import ImageCms
|
from PIL import ImageCms
|
||||||
|
# need to hit getattr to trigger the delayed import error
|
||||||
|
ImageCms.core.profile_open
|
||||||
except ImportError as v:
|
except ImportError as v:
|
||||||
self.skipTest(v)
|
self.skipTest(v)
|
||||||
|
|
||||||
|
|
|
@ -7,21 +7,21 @@ from PIL import ImageDraw
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
# Image size
|
# Image size
|
||||||
w, h = 100, 100
|
W, H = 100, 100
|
||||||
|
|
||||||
# Bounding box points
|
# Bounding box points
|
||||||
x0 = int(w / 4)
|
X0 = int(W / 4)
|
||||||
x1 = int(x0 * 3)
|
X1 = int(X0 * 3)
|
||||||
y0 = int(h / 4)
|
Y0 = int(H / 4)
|
||||||
y1 = int(x0 * 3)
|
Y1 = int(X0 * 3)
|
||||||
|
|
||||||
# Two kinds of bounding box
|
# Two kinds of bounding box
|
||||||
bbox1 = [(x0, y0), (x1, y1)]
|
BBOX1 = [(X0, Y0), (X1, Y1)]
|
||||||
bbox2 = [x0, y0, x1, y1]
|
BBOX2 = [X0, Y0, X1, Y1]
|
||||||
|
|
||||||
# Two kinds of coordinate sequences
|
# Two kinds of coordinate sequences
|
||||||
points1 = [(10, 10), (20, 40), (30, 30)]
|
POINTS1 = [(10, 10), (20, 40), (30, 30)]
|
||||||
points2 = [10, 10, 20, 40, 30, 30]
|
POINTS2 = [10, 10, 20, 40, 30, 30]
|
||||||
|
|
||||||
|
|
||||||
class TestImageDraw(PillowTestCase):
|
class TestImageDraw(PillowTestCase):
|
||||||
|
@ -47,7 +47,7 @@ class TestImageDraw(PillowTestCase):
|
||||||
|
|
||||||
def helper_arc(self, bbox):
|
def helper_arc(self, bbox):
|
||||||
# Arrange
|
# Arrange
|
||||||
im = Image.new("RGB", (w, h))
|
im = Image.new("RGB", (W, H))
|
||||||
draw = ImageDraw.Draw(im)
|
draw = ImageDraw.Draw(im)
|
||||||
|
|
||||||
# Act
|
# Act
|
||||||
|
@ -60,15 +60,15 @@ class TestImageDraw(PillowTestCase):
|
||||||
im, Image.open("Tests/images/imagedraw_arc.png"))
|
im, Image.open("Tests/images/imagedraw_arc.png"))
|
||||||
|
|
||||||
def test_arc1(self):
|
def test_arc1(self):
|
||||||
self.helper_arc(bbox1)
|
self.helper_arc(BBOX1)
|
||||||
|
|
||||||
def test_arc2(self):
|
def test_arc2(self):
|
||||||
self.helper_arc(bbox2)
|
self.helper_arc(BBOX2)
|
||||||
|
|
||||||
def test_bitmap(self):
|
def test_bitmap(self):
|
||||||
# Arrange
|
# Arrange
|
||||||
small = Image.open("Tests/images/pil123rgba.png").resize((50, 50))
|
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)
|
draw = ImageDraw.Draw(im)
|
||||||
|
|
||||||
# Act
|
# Act
|
||||||
|
@ -81,7 +81,7 @@ class TestImageDraw(PillowTestCase):
|
||||||
|
|
||||||
def helper_chord(self, bbox):
|
def helper_chord(self, bbox):
|
||||||
# Arrange
|
# Arrange
|
||||||
im = Image.new("RGB", (w, h))
|
im = Image.new("RGB", (W, H))
|
||||||
draw = ImageDraw.Draw(im)
|
draw = ImageDraw.Draw(im)
|
||||||
|
|
||||||
# Act
|
# Act
|
||||||
|
@ -93,14 +93,14 @@ class TestImageDraw(PillowTestCase):
|
||||||
im, Image.open("Tests/images/imagedraw_chord.png"))
|
im, Image.open("Tests/images/imagedraw_chord.png"))
|
||||||
|
|
||||||
def test_chord1(self):
|
def test_chord1(self):
|
||||||
self.helper_chord(bbox1)
|
self.helper_chord(BBOX1)
|
||||||
|
|
||||||
def test_chord2(self):
|
def test_chord2(self):
|
||||||
self.helper_chord(bbox2)
|
self.helper_chord(BBOX2)
|
||||||
|
|
||||||
def helper_ellipse(self, bbox):
|
def helper_ellipse(self, bbox):
|
||||||
# Arrange
|
# Arrange
|
||||||
im = Image.new("RGB", (w, h))
|
im = Image.new("RGB", (W, H))
|
||||||
draw = ImageDraw.Draw(im)
|
draw = ImageDraw.Draw(im)
|
||||||
|
|
||||||
# Act
|
# Act
|
||||||
|
@ -112,18 +112,18 @@ class TestImageDraw(PillowTestCase):
|
||||||
im, Image.open("Tests/images/imagedraw_ellipse.png"))
|
im, Image.open("Tests/images/imagedraw_ellipse.png"))
|
||||||
|
|
||||||
def test_ellipse1(self):
|
def test_ellipse1(self):
|
||||||
self.helper_ellipse(bbox1)
|
self.helper_ellipse(BBOX1)
|
||||||
|
|
||||||
def test_ellipse2(self):
|
def test_ellipse2(self):
|
||||||
self.helper_ellipse(bbox2)
|
self.helper_ellipse(BBOX2)
|
||||||
|
|
||||||
def helper_line(self, points):
|
def helper_line(self, points):
|
||||||
# Arrange
|
# Arrange
|
||||||
im = Image.new("RGB", (w, h))
|
im = Image.new("RGB", (W, H))
|
||||||
draw = ImageDraw.Draw(im)
|
draw = ImageDraw.Draw(im)
|
||||||
|
|
||||||
# Act
|
# Act
|
||||||
draw.line(points1, fill="yellow", width=2)
|
draw.line(points, fill="yellow", width=2)
|
||||||
del draw
|
del draw
|
||||||
|
|
||||||
# Assert
|
# Assert
|
||||||
|
@ -131,14 +131,14 @@ class TestImageDraw(PillowTestCase):
|
||||||
im, Image.open("Tests/images/imagedraw_line.png"))
|
im, Image.open("Tests/images/imagedraw_line.png"))
|
||||||
|
|
||||||
def test_line1(self):
|
def test_line1(self):
|
||||||
self.helper_line(points1)
|
self.helper_line(POINTS1)
|
||||||
|
|
||||||
def test_line2(self):
|
def test_line2(self):
|
||||||
self.helper_line(points2)
|
self.helper_line(POINTS2)
|
||||||
|
|
||||||
def helper_pieslice(self, bbox):
|
def helper_pieslice(self, bbox):
|
||||||
# Arrange
|
# Arrange
|
||||||
im = Image.new("RGB", (w, h))
|
im = Image.new("RGB", (W, H))
|
||||||
draw = ImageDraw.Draw(im)
|
draw = ImageDraw.Draw(im)
|
||||||
|
|
||||||
# Act
|
# Act
|
||||||
|
@ -150,18 +150,18 @@ class TestImageDraw(PillowTestCase):
|
||||||
im, Image.open("Tests/images/imagedraw_pieslice.png"))
|
im, Image.open("Tests/images/imagedraw_pieslice.png"))
|
||||||
|
|
||||||
def test_pieslice1(self):
|
def test_pieslice1(self):
|
||||||
self.helper_pieslice(bbox1)
|
self.helper_pieslice(BBOX1)
|
||||||
|
|
||||||
def test_pieslice2(self):
|
def test_pieslice2(self):
|
||||||
self.helper_pieslice(bbox2)
|
self.helper_pieslice(BBOX2)
|
||||||
|
|
||||||
def helper_point(self, points):
|
def helper_point(self, points):
|
||||||
# Arrange
|
# Arrange
|
||||||
im = Image.new("RGB", (w, h))
|
im = Image.new("RGB", (W, H))
|
||||||
draw = ImageDraw.Draw(im)
|
draw = ImageDraw.Draw(im)
|
||||||
|
|
||||||
# Act
|
# Act
|
||||||
draw.point(points1, fill="yellow")
|
draw.point(points, fill="yellow")
|
||||||
del draw
|
del draw
|
||||||
|
|
||||||
# Assert
|
# Assert
|
||||||
|
@ -169,18 +169,18 @@ class TestImageDraw(PillowTestCase):
|
||||||
im, Image.open("Tests/images/imagedraw_point.png"))
|
im, Image.open("Tests/images/imagedraw_point.png"))
|
||||||
|
|
||||||
def test_point1(self):
|
def test_point1(self):
|
||||||
self.helper_point(points1)
|
self.helper_point(POINTS1)
|
||||||
|
|
||||||
def test_point2(self):
|
def test_point2(self):
|
||||||
self.helper_point(points2)
|
self.helper_point(POINTS2)
|
||||||
|
|
||||||
def helper_polygon(self, points):
|
def helper_polygon(self, points):
|
||||||
# Arrange
|
# Arrange
|
||||||
im = Image.new("RGB", (w, h))
|
im = Image.new("RGB", (W, H))
|
||||||
draw = ImageDraw.Draw(im)
|
draw = ImageDraw.Draw(im)
|
||||||
|
|
||||||
# Act
|
# Act
|
||||||
draw.polygon(points1, fill="red", outline="blue")
|
draw.polygon(points, fill="red", outline="blue")
|
||||||
del draw
|
del draw
|
||||||
|
|
||||||
# Assert
|
# Assert
|
||||||
|
@ -188,14 +188,14 @@ class TestImageDraw(PillowTestCase):
|
||||||
im, Image.open("Tests/images/imagedraw_polygon.png"))
|
im, Image.open("Tests/images/imagedraw_polygon.png"))
|
||||||
|
|
||||||
def test_polygon1(self):
|
def test_polygon1(self):
|
||||||
self.helper_polygon(points1)
|
self.helper_polygon(POINTS1)
|
||||||
|
|
||||||
def test_polygon2(self):
|
def test_polygon2(self):
|
||||||
self.helper_polygon(points2)
|
self.helper_polygon(POINTS2)
|
||||||
|
|
||||||
def helper_rectangle(self, bbox):
|
def helper_rectangle(self, bbox):
|
||||||
# Arrange
|
# Arrange
|
||||||
im = Image.new("RGB", (w, h))
|
im = Image.new("RGB", (W, H))
|
||||||
draw = ImageDraw.Draw(im)
|
draw = ImageDraw.Draw(im)
|
||||||
|
|
||||||
# Act
|
# Act
|
||||||
|
@ -207,17 +207,17 @@ class TestImageDraw(PillowTestCase):
|
||||||
im, Image.open("Tests/images/imagedraw_rectangle.png"))
|
im, Image.open("Tests/images/imagedraw_rectangle.png"))
|
||||||
|
|
||||||
def test_rectangle1(self):
|
def test_rectangle1(self):
|
||||||
self.helper_rectangle(bbox1)
|
self.helper_rectangle(BBOX1)
|
||||||
|
|
||||||
def test_rectangle2(self):
|
def test_rectangle2(self):
|
||||||
self.helper_rectangle(bbox2)
|
self.helper_rectangle(BBOX2)
|
||||||
|
|
||||||
def test_floodfill(self):
|
def test_floodfill(self):
|
||||||
# Arrange
|
# Arrange
|
||||||
im = Image.new("RGB", (w, h))
|
im = Image.new("RGB", (W, H))
|
||||||
draw = ImageDraw.Draw(im)
|
draw = ImageDraw.Draw(im)
|
||||||
draw.rectangle(bbox2, outline="yellow", fill="green")
|
draw.rectangle(BBOX2, outline="yellow", fill="green")
|
||||||
centre_point = (int(w/2), int(h/2))
|
centre_point = (int(W/2), int(H/2))
|
||||||
|
|
||||||
# Act
|
# Act
|
||||||
ImageDraw.floodfill(im, centre_point, ImageColor.getrgb("red"))
|
ImageDraw.floodfill(im, centre_point, ImageColor.getrgb("red"))
|
||||||
|
@ -233,10 +233,10 @@ class TestImageDraw(PillowTestCase):
|
||||||
# floodfill() is experimental
|
# floodfill() is experimental
|
||||||
|
|
||||||
# Arrange
|
# Arrange
|
||||||
im = Image.new("RGB", (w, h))
|
im = Image.new("RGB", (W, H))
|
||||||
draw = ImageDraw.Draw(im)
|
draw = ImageDraw.Draw(im)
|
||||||
draw.rectangle(bbox2, outline="yellow", fill="green")
|
draw.rectangle(BBOX2, outline="yellow", fill="green")
|
||||||
centre_point = (int(w/2), int(h/2))
|
centre_point = (int(W/2), int(H/2))
|
||||||
|
|
||||||
# Act
|
# Act
|
||||||
ImageDraw.floodfill(
|
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 ImageOps
|
||||||
from PIL import ImageFilter
|
from PIL import ImageFilter
|
||||||
|
|
||||||
im = Image.open("Images/lena.ppm")
|
im = Image.open("Tests/images/lena.ppm")
|
||||||
|
|
||||||
|
|
||||||
class TestImageOpsUsm(PillowTestCase):
|
class TestImageOpsUsm(PillowTestCase):
|
||||||
|
|
|
@ -19,7 +19,7 @@ import locale
|
||||||
|
|
||||||
# one of string.whitespace is not freely convertable into ascii.
|
# one of string.whitespace is not freely convertable into ascii.
|
||||||
|
|
||||||
path = "Images/lena.jpg"
|
path = "Tests/images/lena.jpg"
|
||||||
|
|
||||||
|
|
||||||
class TestLocale(PillowTestCase):
|
class TestLocale(PillowTestCase):
|
||||||
|
|
|
@ -6,7 +6,7 @@ from PIL import Image
|
||||||
class TestPickle(PillowTestCase):
|
class TestPickle(PillowTestCase):
|
||||||
|
|
||||||
def helper_pickle_file(self, pickle, protocol=0):
|
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')
|
filename = self.tempfile('temp.pkl')
|
||||||
|
|
||||||
# Act
|
# Act
|
||||||
|
@ -19,7 +19,7 @@ class TestPickle(PillowTestCase):
|
||||||
self.assertEqual(im, loaded_im)
|
self.assertEqual(im, loaded_im)
|
||||||
|
|
||||||
def helper_pickle_string(
|
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)
|
im = Image.open(file)
|
||||||
|
|
||||||
# Act
|
# Act
|
||||||
|
|
|
@ -215,7 +215,7 @@ def lena(mode="RGB", cache={}):
|
||||||
im = cache.get(mode)
|
im = cache.get(mode)
|
||||||
if im is None:
|
if im is None:
|
||||||
if mode == "RGB":
|
if mode == "RGB":
|
||||||
im = Image.open("Images/lena.ppm")
|
im = Image.open("Tests/images/lena.ppm")
|
||||||
elif mode == "F":
|
elif mode == "F":
|
||||||
im = lena("L").convert(mode)
|
im = lena("L").convert(mode)
|
||||||
elif mode[:4] == "I;16":
|
elif mode[:4] == "I;16":
|
||||||
|
|
|
@ -9,7 +9,7 @@ try:
|
||||||
except:
|
except:
|
||||||
format = "PNG"
|
format = "PNG"
|
||||||
|
|
||||||
im = Image.open("Images/lena.ppm")
|
im = Image.open("Tests/images/lena.ppm")
|
||||||
im.load()
|
im.load()
|
||||||
|
|
||||||
queue = queue.Queue()
|
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 reduce = 0;
|
||||||
int layers = 0;
|
int layers = 0;
|
||||||
int fd = -1;
|
int fd = -1;
|
||||||
if (!PyArg_ParseTuple(args, "ss|iii", &mode, &format,
|
PY_LONG_LONG length = -1;
|
||||||
&reduce, &layers, &fd))
|
if (!PyArg_ParseTuple(args, "ss|iiiL", &mode, &format,
|
||||||
|
&reduce, &layers, &fd, &length))
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
if (strcmp(format, "j2k") == 0)
|
if (strcmp(format, "j2k") == 0)
|
||||||
|
@ -821,6 +822,7 @@ PyImaging_Jpeg2KDecoderNew(PyObject* self, PyObject* args)
|
||||||
context = (JPEG2KDECODESTATE *)decoder->state.context;
|
context = (JPEG2KDECODESTATE *)decoder->state.context;
|
||||||
|
|
||||||
context->fd = fd;
|
context->fd = fd;
|
||||||
|
context->length = (off_t)length;
|
||||||
context->format = codec_format;
|
context->format = codec_format;
|
||||||
context->reduce = reduce;
|
context->reduce = reduce;
|
||||||
context->layers = layers;
|
context->layers = layers;
|
||||||
|
|
|
@ -2,15 +2,16 @@
|
||||||
# install openjpeg
|
# install openjpeg
|
||||||
|
|
||||||
|
|
||||||
if [ ! -f openjpeg-2.0.0.tar.gz ]; then
|
if [ ! -f openjpeg-2.1.0.tar.gz ]; then
|
||||||
wget 'https://openjpeg.googlecode.com/files/openjpeg-2.0.0.tar.gz'
|
wget 'http://iweb.dl.sourceforge.net/project/openjpeg.mirror/2.1.0/openjpeg-2.1.0.tar.gz'
|
||||||
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
rm -r openjpeg-2.0.0
|
rm -r openjpeg-2.1.0
|
||||||
tar -xvzf openjpeg-2.0.0.tar.gz
|
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
|
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.
|
* **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
|
If the prerequisites are installed in the standard library locations for your
|
||||||
machine (e.g. :file:`/usr` or :file:`/usr/local`), no additional configuration
|
machine (e.g. :file:`/usr` or :file:`/usr/local`), no additional configuration
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
* Copyright (c) 2014 by Alastair Houghton
|
* Copyright (c) 2014 by Alastair Houghton
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <openjpeg-2.0/openjpeg.h>
|
#include <openjpeg.h>
|
||||||
|
|
||||||
/* -------------------------------------------------------------------- */
|
/* -------------------------------------------------------------------- */
|
||||||
/* Decoder */
|
/* Decoder */
|
||||||
|
@ -20,6 +20,9 @@ typedef struct {
|
||||||
/* File descriptor, if available; otherwise, -1 */
|
/* File descriptor, if available; otherwise, -1 */
|
||||||
int fd;
|
int fd;
|
||||||
|
|
||||||
|
/* Length of data, if available; otherwise, -1 */
|
||||||
|
off_t length;
|
||||||
|
|
||||||
/* Specify the desired format */
|
/* Specify the desired format */
|
||||||
OPJ_CODEC_FORMAT 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_read_function(stream, j2k_read);
|
||||||
opj_stream_set_skip_function(stream, j2k_skip);
|
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);
|
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 */
|
/* Setup decompression context */
|
||||||
context->error_msg = NULL;
|
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_skip_function(stream, j2k_skip);
|
||||||
opj_stream_set_seek_function(stream, j2k_seek);
|
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);
|
opj_stream_set_user_data(stream, encoder);
|
||||||
|
#else
|
||||||
|
opj_stream_set_user_data(stream, encoder, NULL);
|
||||||
|
#endif
|
||||||
|
|
||||||
/* Setup an opj_image */
|
/* Setup an opj_image */
|
||||||
if (strcmp (im->mode, "L") == 0) {
|
if (strcmp (im->mode, "L") == 0) {
|
||||||
|
|
|
@ -13,11 +13,6 @@
|
||||||
#include <tiff.h>
|
#include <tiff.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifndef _UNISTD_H
|
|
||||||
#include <unistd.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
#ifndef min
|
#ifndef min
|
||||||
#define min(x,y) (( x > y ) ? y : x )
|
#define min(x,y) (( x > y ) ? y : x )
|
||||||
#define max(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, ...);
|
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.*/
|
Trace debugging
|
||||||
#define TRACE(args)
|
legacy, don't enable for python 3.x, unicode issues.
|
||||||
#else
|
*/
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
#define VA_ARGS(...) __VA_ARGS__
|
#define VA_ARGS(...) __VA_ARGS__
|
||||||
|
@ -56,8 +50,5 @@ extern int ImagingLibTiffSetField(ImagingCodecState state, ttag_t tag, ...);
|
||||||
|
|
||||||
#define TRACE(args)
|
#define TRACE(args)
|
||||||
|
|
||||||
#endif /* _MSC_VER */
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
10
selftest.py
|
@ -49,13 +49,13 @@ def testimage():
|
||||||
|
|
||||||
Or open existing files:
|
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)
|
>>> _info(im)
|
||||||
('GIF', 'P', (128, 128))
|
('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))
|
('PPM', 'RGB', (128, 128))
|
||||||
>>> try:
|
>>> 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:
|
... except IOError as v:
|
||||||
... print(v)
|
... print(v)
|
||||||
('JPEG', 'RGB', (128, 128))
|
('JPEG', 'RGB', (128, 128))
|
||||||
|
@ -63,7 +63,7 @@ def testimage():
|
||||||
PIL doesn't actually load the image data until it's needed,
|
PIL doesn't actually load the image data until it's needed,
|
||||||
or you call the "load" method:
|
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
|
>>> print(im.im) # internal image attribute
|
||||||
None
|
None
|
||||||
>>> a = im.load()
|
>>> a = im.load()
|
||||||
|
@ -73,7 +73,7 @@ def testimage():
|
||||||
You can apply many different operations on images. Most
|
You can apply many different operations on images. Most
|
||||||
operations return a new image:
|
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"))
|
>>> _info(im.convert("L"))
|
||||||
(None, 'L', (128, 128))
|
(None, 'L', (128, 128))
|
||||||
>>> _info(im.copy())
|
>>> _info(im.copy())
|
||||||
|
|
54
setup.py
|
@ -337,14 +337,23 @@ class pil_build_ext(build_ext):
|
||||||
_add_directory(include_dirs, "/usr/include")
|
_add_directory(include_dirs, "/usr/include")
|
||||||
|
|
||||||
# on Windows, look for the OpenJPEG libraries in the location that
|
# 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":
|
if sys.platform == "win32":
|
||||||
|
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,
|
_add_directory(library_dirs,
|
||||||
os.path.join(os.environ.get("ProgramFiles", ""),
|
os.path.join(best_path, 'lib'))
|
||||||
"OpenJPEG 2.0", "lib"))
|
|
||||||
_add_directory(include_dirs,
|
_add_directory(include_dirs,
|
||||||
os.path.join(os.environ.get("ProgramFiles", ""),
|
os.path.join(best_path, 'include'))
|
||||||
"OpenJPEG 2.0", "include"))
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# insert new dirs *before* default libs, to avoid conflicts
|
# insert new dirs *before* default libs, to avoid conflicts
|
||||||
|
@ -374,10 +383,29 @@ class pil_build_ext(build_ext):
|
||||||
_find_library_file(self, "libjpeg")):
|
_find_library_file(self, "libjpeg")):
|
||||||
feature.jpeg = "libjpeg" # alternative name
|
feature.jpeg = "libjpeg" # alternative name
|
||||||
|
|
||||||
|
feature.openjpeg_version = None
|
||||||
if feature.want('jpeg2000'):
|
if feature.want('jpeg2000'):
|
||||||
if _find_include_file(self, "openjpeg-2.0/openjpeg.h"):
|
best_version = None
|
||||||
if _find_library_file(self, "openjp2"):
|
best_path = None
|
||||||
feature.jpeg2000 = "openjp2"
|
|
||||||
|
# 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 feature.want('tiff'):
|
||||||
if _find_library_file(self, "tiff"):
|
if _find_library_file(self, "tiff"):
|
||||||
|
@ -543,6 +571,9 @@ class pil_build_ext(build_ext):
|
||||||
if os.path.isfile("_imagingmath.c"):
|
if os.path.isfile("_imagingmath.c"):
|
||||||
exts.append(Extension("PIL._imagingmath", ["_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
|
self.extensions[:] = exts
|
||||||
|
|
||||||
build_ext.build_extensions(self)
|
build_ext.build_extensions(self)
|
||||||
|
@ -572,7 +603,7 @@ class pil_build_ext(build_ext):
|
||||||
options = [
|
options = [
|
||||||
(feature.tcl and feature.tk, "TKINTER"),
|
(feature.tcl and feature.tk, "TKINTER"),
|
||||||
(feature.jpeg, "JPEG"),
|
(feature.jpeg, "JPEG"),
|
||||||
(feature.jpeg2000, "OPENJPEG (JPEG2000)"),
|
(feature.jpeg2000, "OPENJPEG (JPEG2000)", feature.openjpeg_version),
|
||||||
(feature.zlib, "ZLIB (PNG/ZIP)"),
|
(feature.zlib, "ZLIB (PNG/ZIP)"),
|
||||||
(feature.tiff, "LIBTIFF"),
|
(feature.tiff, "LIBTIFF"),
|
||||||
(feature.freetype, "FREETYPE2"),
|
(feature.freetype, "FREETYPE2"),
|
||||||
|
@ -583,7 +614,10 @@ class pil_build_ext(build_ext):
|
||||||
all = 1
|
all = 1
|
||||||
for option in options:
|
for option in options:
|
||||||
if option[0]:
|
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:
|
else:
|
||||||
print("*** %s support not available" % option[1])
|
print("*** %s support not available" % option[1])
|
||||||
if option[1] == "TKINTER" and _tkinter:
|
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()
|