diff --git a/PIL/ImageDraw.py b/PIL/ImageDraw.py index 71e29ee48..ee0405fab 100644 --- a/PIL/ImageDraw.py +++ b/PIL/ImageDraw.py @@ -315,7 +315,7 @@ def getdraw(im=None, hints=None): return im, handler -def floodfill(image, xy, value, border=None): +def floodfill(image, xy, value, border=None, thresh=0): """ (experimental) Fills a bounded region with a given color. @@ -326,13 +326,17 @@ def floodfill(image, xy, value, border=None): 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 pixel = image.load() x, y = xy try: background = pixel[x, y] - if background == value: + if _color_diff(value, background) <= thresh: return # seed point already has fill color pixel[x, y] = value except (ValueError, IndexError): @@ -348,7 +352,7 @@ def floodfill(image, xy, value, border=None): except IndexError: pass else: - if p == background: + if _color_diff(p, background) <= thresh: pixel[s, t] = value newedge.append((s, t)) edge = newedge @@ -366,3 +370,10 @@ def floodfill(image, xy, value, border=None): pixel[s, t] = value newedge.append((s, t)) edge = newedge + + +def _color_diff(rgb1, rgb2): + """ + Uses 1-norm distance to calculate difference between two rgb values. + """ + return abs(rgb1[0]-rgb2[0]) + abs(rgb1[1]-rgb2[1]) + abs(rgb1[2]-rgb2[2]) diff --git a/Tests/test_imagedraw.py b/Tests/test_imagedraw.py index 2ca5d0882..9c989d2e5 100644 --- a/Tests/test_imagedraw.py +++ b/Tests/test_imagedraw.py @@ -400,6 +400,26 @@ class TestImageDraw(PillowTestCase): self.assert_image_equal( im, Image.open("Tests/images/imagedraw_floodfill2.png")) + + def test_floodfill_thresh(self): + # floodfill() is experimental + + # Arrange + im = Image.new("RGB", (W, H)) + draw = ImageDraw.Draw(im) + draw.rectangle(BBOX2, outline="darkgreen", fill="green") + centre_point = (int(W/2), int(H/2)) + + # Act + ImageDraw.floodfill( + im, centre_point, ImageColor.getrgb("red"), + thresh=30) + del draw + + # Assert + self.assert_image_equal( + im, Image.open("Tests/images/imagedraw_floodfill2.png")) + def create_base_image_draw(self, size, mode=DEFAULT_MODE, background1=WHITE,