feature improvement: improved performance of ImageDraw.floodfill with Python built-in set() datatype.

This commit is contained in:
yo1995 2018-08-06 18:13:57 +08:00
parent 69918dcbef
commit 24388addb6

View File

@ -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