From 24388addb647dbb7599bfefa739d56c7a180e8f8 Mon Sep 17 00:00:00 2001 From: yo1995 Date: Mon, 6 Aug 2018 18:13:57 +0800 Subject: [PATCH 1/5] feature improvement: improved performance of ImageDraw.floodfill with Python built-in set() datatype. --- src/PIL/ImageDraw.py | 52 ++++++++++++++++++++++++++------------------ 1 file changed, 31 insertions(+), 21 deletions(-) diff --git a/src/PIL/ImageDraw.py b/src/PIL/ImageDraw.py index ca8c1d17b..2db19af18 100644 --- a/src/PIL/ImageDraw.py +++ b/src/PIL/ImageDraw.py @@ -325,22 +325,23 @@ def getdraw(im=None, hints=None): def floodfill(image, xy, value, border=None, thresh=0): """ - (experimental) Fills a bounded region with a given color. + (experimental) Fills a bounded region with a given color. - :param image: Target image. - :param xy: Seed position (a 2-item coordinate tuple). See - :ref:`coordinate-system`. - :param value: Fill color. - :param border: Optional border value. If given, the region consists of - pixels with a color different from the border color. If not given, - the region consists of pixels having the same color as the seed - pixel. - :param thresh: Optional threshold value which specifies a maximum - tolerable difference of a pixel value from the 'background' in - order for it to be replaced. Useful for filling regions of non- - homogeneous, but similar, colors. - """ + :param image: Target image. + :param xy: Seed position (a 2-item coordinate tuple). See + :ref:`coordinate-system`. + :param value: Fill color. + :param border: Optional border value. If given, the region consists of + pixels with a color different from the border color. If not given, + the region consists of pixels having the same color as the seed + pixel. + :param thresh: Optional threshold value which specifies a maximum + tolerable difference of a pixel value from the 'background' in + order for it to be replaced. Useful for filling regions of non- + homogeneous, but similar, colors. + """ # based on an implementation by Eric S. Raymond + # amended by yo1995 @20180806 pixel = image.load() x, y = xy try: @@ -350,12 +351,15 @@ def floodfill(image, xy, value, border=None, thresh=0): pixel[x, y] = value except (ValueError, IndexError): return # seed point outside image - edge = [(x, y)] + edge = {(x, y)} + full_edge = set() # use a set to record each unique pixel processed if border is None: while edge: - newedge = [] - for (x, y) in edge: + new_edge = set() + for (x, y) in edge: # 4 adjacent method for (s, t) in ((x+1, y), (x-1, y), (x, y+1), (x, y-1)): + if (s, t) in full_edge: + continue # if already processed, skip try: p = pixel[s, t] except IndexError: @@ -363,13 +367,17 @@ def floodfill(image, xy, value, border=None, thresh=0): else: if _color_diff(p, background) <= thresh: pixel[s, t] = value - newedge.append((s, t)) - edge = newedge + new_edge.add((s, t)) + full_edge.add((s, t)) + full_edge = edge # do not record useless pixels to reduce memory consumption + edge = new_edge else: while edge: - newedge = [] + new_edge = set() for (x, y) in edge: for (s, t) in ((x+1, y), (x-1, y), (x, y+1), (x, y-1)): + if (s, t) in full_edge: + continue try: p = pixel[s, t] except IndexError: @@ -377,7 +385,9 @@ def floodfill(image, xy, value, border=None, thresh=0): else: if p != value and p != border: pixel[s, t] = value - newedge.append((s, t)) + new_edge.add((s, t)) + full_edge.add((s, t)) + full_edge = edge edge = newedge From 8676044a272f7d716892440429a5e45b4ca7960f Mon Sep 17 00:00:00 2001 From: yo1995 Date: Mon, 6 Aug 2018 18:47:49 +0800 Subject: [PATCH 2/5] fix docstring tab --- src/PIL/ImageDraw.py | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/src/PIL/ImageDraw.py b/src/PIL/ImageDraw.py index 2db19af18..4f0144d35 100644 --- a/src/PIL/ImageDraw.py +++ b/src/PIL/ImageDraw.py @@ -325,21 +325,21 @@ def getdraw(im=None, hints=None): def floodfill(image, xy, value, border=None, thresh=0): """ - (experimental) Fills a bounded region with a given color. + (experimental) Fills a bounded region with a given color. - :param image: Target image. - :param xy: Seed position (a 2-item coordinate tuple). See - :ref:`coordinate-system`. - :param value: Fill color. - :param border: Optional border value. If given, the region consists of - pixels with a color different from the border color. If not given, - the region consists of pixels having the same color as the seed - pixel. - :param thresh: Optional threshold value which specifies a maximum - tolerable difference of a pixel value from the 'background' in - order for it to be replaced. Useful for filling regions of non- - homogeneous, but similar, colors. - """ + :param image: Target image. + :param xy: Seed position (a 2-item coordinate tuple). See + :ref:`coordinate-system`. + :param value: Fill color. + :param border: Optional border value. If given, the region consists of + pixels with a color different from the border color. If not given, + the region consists of pixels having the same color as the seed + pixel. + :param thresh: Optional threshold value which specifies a maximum + tolerable difference of a pixel value from the 'background' in + order for it to be replaced. Useful for filling regions of non- + homogeneous, but similar, colors. + """ # based on an implementation by Eric S. Raymond # amended by yo1995 @20180806 pixel = image.load() From a221420ec399851f674761129648f4075c4ef8e4 Mon Sep 17 00:00:00 2001 From: yo1995 Date: Mon, 6 Aug 2018 19:36:18 +0800 Subject: [PATCH 3/5] fixe typo --- src/PIL/ImageDraw.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/PIL/ImageDraw.py b/src/PIL/ImageDraw.py index 4f0144d35..87ee4de0a 100644 --- a/src/PIL/ImageDraw.py +++ b/src/PIL/ImageDraw.py @@ -388,7 +388,7 @@ def floodfill(image, xy, value, border=None, thresh=0): new_edge.add((s, t)) full_edge.add((s, t)) full_edge = edge - edge = newedge + edge = new_edge def _color_diff(rgb1, rgb2): From 3cf1a4ea87b8f17b59c623ccac4a62a16825464d Mon Sep 17 00:00:00 2001 From: yo1995 Date: Tue, 4 Sep 2018 12:03:20 -0400 Subject: [PATCH 4/5] improved comments and one logic according to PR 3294 discussion --- src/PIL/ImageDraw.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/PIL/ImageDraw.py b/src/PIL/ImageDraw.py index 3335760f4..517135a44 100644 --- a/src/PIL/ImageDraw.py +++ b/src/PIL/ImageDraw.py @@ -352,7 +352,7 @@ def floodfill(image, xy, value, border=None, thresh=0): except (ValueError, IndexError): return # seed point outside image edge = {(x, y)} - full_edge = set() # use a set to record each unique pixel processed + full_edge = set() # use a set to keep record of current and previous edge pixels to reduce memory consumption while edge: new_edge = set() for (x, y) in edge: # 4 adjacent method @@ -364,6 +364,7 @@ def floodfill(image, xy, value, border=None, thresh=0): except (ValueError, IndexError): pass else: + full_edge.add((s, t)) if border is None: fill = _color_diff(p, background) <= thresh else: @@ -371,8 +372,7 @@ def floodfill(image, xy, value, border=None, thresh=0): if fill: pixel[s, t] = value new_edge.add((s, t)) - full_edge.add((s, t)) - full_edge = edge # do not record useless pixels to reduce memory consumption + full_edge = edge # discard pixels proceeded edge = new_edge From bb77f62586d31193f22388182ac2420ffcdcd440 Mon Sep 17 00:00:00 2001 From: yo1995 Date: Tue, 4 Sep 2018 21:15:25 -0400 Subject: [PATCH 5/5] fix typo: proceeded -> processed --- src/PIL/ImageDraw.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/PIL/ImageDraw.py b/src/PIL/ImageDraw.py index 517135a44..ab328fe53 100644 --- a/src/PIL/ImageDraw.py +++ b/src/PIL/ImageDraw.py @@ -372,7 +372,7 @@ def floodfill(image, xy, value, border=None, thresh=0): if fill: pixel[s, t] = value new_edge.add((s, t)) - full_edge = edge # discard pixels proceeded + full_edge = edge # discard pixels processed edge = new_edge