diff --git a/PIL/ImageMorph.py b/PIL/ImageMorph.py index ca9b3bb80..fd14b452c 100644 --- a/PIL/ImageMorph.py +++ b/PIL/ImageMorph.py @@ -9,9 +9,11 @@ from PIL import Image from PIL import _imagingmorph import re -LUT_SIZE = 1<<9 +LUT_SIZE = 1 << 9 + + class LutBuilder: - """A class for building MorphLut's from a descriptive language + """A class for building a MorphLut from a descriptive language The input patterns is a list of a strings sequences like these: @@ -20,8 +22,8 @@ class LutBuilder: 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: + describes a series of symmetry operations (in this case a + 4-rotation), the pattern is described by: . or X - Ignore 1 - Pixel is on @@ -42,9 +44,9 @@ class LutBuilder: lb = LutBuilder(patterns = ["4:(... .1. 111)->1"]) lut = lb.build_lut() - + """ - def __init__(self,patterns = None,op_name=None): + def __init__(self, patterns=None, op_name=None): if patterns is not None: self.patterns = patterns else: @@ -52,19 +54,19 @@ class LutBuilder: 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'] + '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: + if op_name not in known_patterns: raise Exception('Unknown pattern '+op_name+'!') self.patterns = known_patterns[op_name] @@ -75,21 +77,21 @@ class LutBuilder: 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)]) + 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. + string permuted according to the permutation list. """ - assert(len(permutation)==9) + 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 + the pattern according to the modifications described in the $options parameter. It returns a list of all cloned patterns.""" patterns = [(basic_pattern, basic_result)] @@ -98,29 +100,28 @@ class LutBuilder: 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)) + (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]: + for pattern, res in patterns[0:n]: patterns.append( - (self._string_permute(pattern, [2,1,0, - 5,4,3, - 8,7,6]), res)) + (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]: + 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)) + .replace('0', 'Z') + .replace('1', '0') + .replace('Z', '1')) + res = '%d' % (1-int(res)) patterns.append((pattern, res)) return patterns @@ -135,7 +136,8 @@ class LutBuilder: # 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','')) + 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) @@ -143,7 +145,7 @@ class LutBuilder: result = int(m.group(3)) # Get rid of spaces - pattern= pattern.replace(' ','').replace('\n','') + pattern = pattern.replace(' ', '').replace('\n', '') patterns += self._pattern_permute(pattern, options, result) @@ -154,7 +156,7 @@ class LutBuilder: # compile the patterns into regular expressions for speed for i in range(len(patterns)): - p = patterns[i][0].replace('.','X').replace('X','[01]') + p = patterns[i][0].replace('.', 'X').replace('X', '[01]') p = re.compile(p) patterns[i] = (p, patterns[i][1]) @@ -166,25 +168,26 @@ class LutBuilder: bitpattern = bin(i)[2:] bitpattern = ('0'*(9-len(bitpattern)) + bitpattern)[::-1] - for p,r in patterns: + 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): + 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() + self.lut = LutBuilder(op_name=op_name).build_lut() elif patterns is not None: - self.lut = LutBuilder(patterns = patterns).build_lut() + self.lut = LutBuilder(patterns=patterns).build_lut() def apply(self, image): """Run a single morphological operation on an image @@ -193,32 +196,37 @@ class MorphOp: 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.""" + 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) + + 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) + + 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: + with open(filename, 'rb') as f: self.lut = bytearray(f.read()) - - if len(self.lut)!= 8192: + + if len(self.lut) != 8192: self.lut = None raise Exception('Wrong size operator file!') @@ -226,11 +234,11 @@ class MorphOp: """Load an operator save mrl file""" if self.lut is None: raise Exception('No operator loaded') - with open(filename,'wb') as f: + 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 - +# End of file diff --git a/Tests/test_imagemorph.py b/Tests/test_imagemorph.py index c0f8b4783..0ff32eb42 100644 --- a/Tests/test_imagemorph.py +++ b/Tests/test_imagemorph.py @@ -9,7 +9,7 @@ class MorphTests(PillowTestCase): def setUp(self): self.A = self.string_to_img( - """ + """ ....... ....... ..111.. @@ -18,29 +18,28 @@ class MorphTests(PillowTestCase): ....... ....... """ - ) - + ) 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)]) + [''.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') + 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)) + 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) + im.putpixel((i, j), v) return im @@ -51,34 +50,39 @@ class MorphTests(PillowTestCase): 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)) + 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'): + 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() + # create_lut() def test_lut(self): - for op in ('corner', 'dilation4', 'dilation8', 'erosion4', 'erosion8', 'edge'): + 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: + 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) + count, Aout = mop.apply(self.A) + self.assertEqual(count, 8) self.assert_img_equal_img_string(Aout, """ ....... @@ -93,8 +97,8 @@ class MorphTests(PillowTestCase): def test_dialation8(self): # dialation8 mop = ImageMorph.MorphOp(op_name='dilation8') - count,Aout = mop.apply(self.A) - self.assertEqual(count,16) + count, Aout = mop.apply(self.A) + self.assertEqual(count, 16) self.assert_img_equal_img_string(Aout, """ ....... @@ -109,8 +113,8 @@ class MorphTests(PillowTestCase): def test_erosion4(self): # erosion4 mop = ImageMorph.MorphOp(op_name='dilation4') - count,Aout = mop.apply(self.A) - self.assertEqual(count,12) + count, Aout = mop.apply(self.A) + self.assertEqual(count, 12) self.assert_img_equal_img_string(Aout, """ ....... @@ -123,10 +127,10 @@ class MorphTests(PillowTestCase): """) def test_edge(self): - # edge + # edge mop = ImageMorph.MorphOp(op_name='edge') - count,Aout = mop.apply(self.A) - self.assertEqual(count,1) + count, Aout = mop.apply(self.A) + self.assertEqual(count, 1) self.assert_img_equal_img_string(Aout, """ ....... @@ -138,13 +142,12 @@ class MorphTests(PillowTestCase): ....... """) - 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) + 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, """ ....... @@ -155,15 +158,14 @@ class MorphTests(PillowTestCase): ....... ....... """) - # 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))) + 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))) + self.assertEqual(tuple(coords), ((2, 2), (4, 2), (2, 4), (4, 4))) + +# End of file diff --git a/setup.py b/setup.py index 57a5821cd..2fbcc2959 100644 --- a/setup.py +++ b/setup.py @@ -106,6 +106,7 @@ class pil_build_ext(build_ext): def require(self, feat): return feat in self.required + def want(self, feat): return getattr(self, feat) is None @@ -137,8 +138,8 @@ class pil_build_ext(build_ext): setattr(self.feature, x, False) if getattr(self, 'enable_%s' % x): raise ValueError( - 'Conflicting options: --enable-%s and --disable-%s' - % (x, x)) + 'Conflicting options: --enable-%s and --disable-%s' + % (x, x)) if getattr(self, 'enable_%s' % x): self.feature.required.append(x) @@ -225,15 +226,17 @@ class pil_build_ext(build_ext): if ft_prefix and os.path.isdir(ft_prefix): # freetype might not be linked into Homebrew's prefix _add_directory(library_dirs, os.path.join(ft_prefix, 'lib')) - _add_directory(include_dirs, os.path.join(ft_prefix, 'include')) + _add_directory( + include_dirs, os.path.join(ft_prefix, 'include')) else: - # fall back to freetype from XQuartz if Homebrew's freetype is missing + # fall back to freetype from XQuartz if + # Homebrew's freetype is missing _add_directory(library_dirs, "/usr/X11/lib") _add_directory(include_dirs, "/usr/X11/include") elif sys.platform.startswith("linux"): arch_tp = (plat.processor(), plat.architecture()[0]) - if arch_tp == ("x86_64","32bit"): + if arch_tp == ("x86_64", "32bit"): # 32 bit build on 64 bit machine. _add_directory(library_dirs, "/usr/lib/i386-linux-gnu") else: @@ -245,30 +248,38 @@ class pil_build_ext(build_ext): if platform_ in ["x86_64", "64bit"]: _add_directory(library_dirs, "/lib64") _add_directory(library_dirs, "/usr/lib64") - _add_directory(library_dirs, "/usr/lib/x86_64-linux-gnu") + _add_directory( + library_dirs, "/usr/lib/x86_64-linux-gnu") break elif platform_ in ["i386", "i686", "32bit"]: - _add_directory(library_dirs, "/usr/lib/i386-linux-gnu") + _add_directory( + library_dirs, "/usr/lib/i386-linux-gnu") break elif platform_ in ["aarch64"]: _add_directory(library_dirs, "/usr/lib64") - _add_directory(library_dirs, "/usr/lib/aarch64-linux-gnu") + _add_directory( + library_dirs, "/usr/lib/aarch64-linux-gnu") break elif platform_ in ["arm", "armv7l"]: - _add_directory(library_dirs, "/usr/lib/arm-linux-gnueabi") + _add_directory( + library_dirs, "/usr/lib/arm-linux-gnueabi") break elif platform_ in ["ppc64"]: _add_directory(library_dirs, "/usr/lib64") - _add_directory(library_dirs, "/usr/lib/ppc64-linux-gnu") - _add_directory(library_dirs, "/usr/lib/powerpc64-linux-gnu") + _add_directory( + library_dirs, "/usr/lib/ppc64-linux-gnu") + _add_directory( + library_dirs, "/usr/lib/powerpc64-linux-gnu") break elif platform_ in ["ppc"]: _add_directory(library_dirs, "/usr/lib/ppc-linux-gnu") - _add_directory(library_dirs, "/usr/lib/powerpc-linux-gnu") + _add_directory( + library_dirs, "/usr/lib/powerpc-linux-gnu") break elif platform_ in ["s390x"]: _add_directory(library_dirs, "/usr/lib64") - _add_directory(library_dirs, "/usr/lib/s390x-linux-gnu") + _add_directory( + library_dirs, "/usr/lib/s390x-linux-gnu") break elif platform_ in ["s390"]: _add_directory(library_dirs, "/usr/lib/s390-linux-gnu") @@ -344,7 +355,8 @@ class pil_build_ext(build_ext): 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('.')]) + 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) @@ -371,7 +383,8 @@ class pil_build_ext(build_ext): if _find_include_file(self, "zlib.h"): if _find_library_file(self, "z"): feature.zlib = "z" - elif sys.platform == "win32" and _find_library_file(self, "zlib"): + elif (sys.platform == "win32" and + _find_library_file(self, "zlib")): feature.zlib = "zlib" # alternative name if feature.want('jpeg'): @@ -387,7 +400,7 @@ class pil_build_ext(build_ext): if feature.want('jpeg2000'): best_version = None best_path = None - + # Find the best version for directory in self.compiler.include_dirs: for name in os.listdir(directory): @@ -405,14 +418,16 @@ class pil_build_ext(build_ext): # 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]) - + feature.openjpeg_version = '.'.join( + [str(x) for x in best_version]) + if feature.want('tiff'): if _find_library_file(self, "tiff"): feature.tiff = "tiff" if sys.platform == "win32" and _find_library_file(self, "libtiff"): feature.tiff = "libtiff" - if sys.platform == "darwin" and _find_library_file(self, "libtiff"): + if (sys.platform == "darwin" and + _find_library_file(self, "libtiff")): feature.tiff = "libtiff" if feature.want('freetype'): @@ -459,20 +474,22 @@ class pil_build_ext(build_ext): if feature.want('webp'): if (_find_include_file(self, "webp/encode.h") and _find_include_file(self, "webp/decode.h")): - if _find_library_file(self, "webp"): # in googles precompiled zip it is call "libwebp" + # In Google's precompiled zip it is call "libwebp": + if _find_library_file(self, "webp"): feature.webp = "webp" if feature.want('webpmux'): if (_find_include_file(self, "webp/mux.h") and _find_include_file(self, "webp/demux.h")): - if _find_library_file(self, "webpmux") and _find_library_file(self, "webpdemux"): + if (_find_library_file(self, "webpmux") and + _find_library_file(self, "webpdemux")): feature.webpmux = "webpmux" for f in feature: if not getattr(feature, f) and feature.require(f): raise ValueError( - '--enable-%s requested but %s not found, aborting.' - % (f, f)) + '--enable-%s requested but %s not found, aborting.' + % (f, f)) # # core library @@ -527,7 +544,9 @@ class pil_build_ext(build_ext): if sys.platform == "win32": extra.extend(["user32", "gdi32"]) exts.append(Extension( - "PIL._imagingcms", ["_imagingcms.c"], libraries=["lcms2"] + extra)) + "PIL._imagingcms", + ["_imagingcms.c"], + libraries=["lcms2"] + extra)) if os.path.isfile("_webp.c") and feature.webp: libs = ["webp"] @@ -603,7 +622,8 @@ class pil_build_ext(build_ext): options = [ (feature.tcl and feature.tk, "TKINTER"), (feature.jpeg, "JPEG"), - (feature.jpeg2000, "OPENJPEG (JPEG2000)", feature.openjpeg_version), + (feature.jpeg2000, "OPENJPEG (JPEG2000)", + feature.openjpeg_version), (feature.zlib, "ZLIB (PNG/ZIP)"), (feature.tiff, "LIBTIFF"), (feature.freetype, "FREETYPE2"), @@ -715,8 +735,9 @@ setup( packages=find_packages(), scripts=glob.glob("Scripts/pil*.py"), test_suite='PIL.tests', - keywords=["Imaging",], + keywords=["Imaging", ], license='Standard PIL License', zip_safe=True, ) +# End of file