From 24388addb647dbb7599bfefa739d56c7a180e8f8 Mon Sep 17 00:00:00 2001 From: yo1995 Date: Mon, 6 Aug 2018 18:13:57 +0800 Subject: [PATCH] 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