Tom Stock
Tom Stock

Reputation: 1298

Python PIL ImageDraw.polygon not drawing the polygon if it is a line or a point

As can be seen in the examples below, if a polygon is a line or a point it is not drawn. I do not think that this is the desired behaviour. In theory a line is also a polygon right?

From the docs of PIL:

ImageDraw.polygon(xy, fill=None, outline=None)

Draws a polygon.

The polygon outline consists of straight lines between the given coordinates, plus a straight line between the last and the first coordinate.

...

To me this suggests that the .polygon method works by drawing lines between points, however, as seen in example #2 it definitely does not.

Could anybody tell me why this is the case? Could someone also tell me how I can make it so that, even if the polygon is a line or point, it is still drawn?

#1 Point as polygon:

Python 3.7.6 (v3.7.6:43364a7ae0, Dec 18 2019, 14:18:50) 
[Clang 6.0 (clang-600.0.57)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> from PIL import Image, ImageDraw
>>> import numpy as np
>>> img = Image.new('L', (5, 5), 0)
>>> ImageDraw.Draw(img).polygon([2, 2, 2, 2, 2, 2, 2, 2], outline=1, fill=1)
>>> np.array(img)
array([[0, 0, 0, 0, 0],
       [0, 0, 0, 0, 0],
       [0, 0, 0, 0, 0],
       [0, 0, 0, 0, 0],
       [0, 0, 0, 0, 0]], dtype=uint8)

#2 Line as polygon:

>>> img = Image.new('L', (5, 5), 0)
>>> ImageDraw.Draw(img).polygon([1, 1, 2, 1, 2, 1, 1, 1], outline=1, fill=1)
>>> np.array(img)
array([[0, 0, 0, 0, 0],
       [0, 0, 0, 0, 0],
       [0, 0, 0, 0, 0],
       [0, 0, 0, 0, 0],
       [0, 0, 0, 0, 0]], dtype=uint8)

#3 Squire as polygon:

>>> img = Image.new('L', (5, 5), 0)
>>> ImageDraw.Draw(img).polygon([1, 1, 2, 1, 2, 2, 1, 2], outline=1, fill=1)
>>> np.array(img)
array([[0, 0, 0, 0, 0],
       [0, 1, 1, 0, 0],
       [0, 1, 1, 0, 0],
       [0, 0, 0, 0, 0],
       [0, 0, 0, 0, 0]], dtype=uint8)

Edit: I googled a bit for the definition of a polygon;

In geometry, a polygon can be defined as a flat or plane, two-dimensional closed shape with straight sides.

source

A plane figure with at least three straight sides and angles, and typically five or more.

source

So a new question arises; are angles of 0 degrees also counted as angles in geometry? According to this source it is:

Zero Angles

An angle with a measure of zero degrees is called a zero angle. If this is hard to visualize, consider two rays that form some angle greater than zero degrees, like the rays in the . Then picture one of the rays rotating toward the other ray until they both lie in the same line. The angle they create has been shrunk from its original measure to zero degrees. The angle that is now formed has a measure of zero degrees.

Upvotes: 1

Views: 5314

Answers (2)

nigh_anxiety
nigh_anxiety

Reputation: 2348

It appears that since this was originally posted, PIL has been updated such that ImageDraw.Draw.polygon() will now fill in pixels for a point or a line. I did not dig deeply into the change logs to track down when this may have been fixed, as ImageDraw.Draw.polygon() calls into Image.core.draw.draw_polygon(), which is a C module and its interface is not publicly documented.

I'm running Python 3.11.1 with PIL version 9.4.0

Example 1 - Point:

>>> from PIL import Image, ImageDraw
>>> import numpy as np
>>> imgP = Image.new('L', (5,5), 0)
>>> ImageDraw.Draw(imgP).polygon([2, 2, 2, 2, 2, 2], outline=1, fill=1)
>>> np.array(imgP)
array([[0, 0, 0, 0, 0],
       [0, 0, 0, 0, 0],
       [0, 0, 1, 0, 0],
       [0, 0, 0, 0, 0],
       [0, 0, 0, 0, 0]], dtype=uint8)

Example 2 - Line:

>>> from PIL import Image, ImageDraw
>>> import numpy as np
>>> imgL = Image.new('L', (5, 5), 0) 
>>> ImageDraw.Draw(imgL).polygon([1, 1, 2, 1, 2, 1, 1, 1], outline=1, fill=1)  
>>> np.array(imgL)
array([[0, 0, 0, 0, 0],
       [0, 1, 1, 0, 0],
       [0, 0, 0, 0, 0],
       [0, 0, 0, 0, 0],
       [0, 0, 0, 0, 0]], dtype=uint8)

Upvotes: 1

Tom Stock
Tom Stock

Reputation: 1298

While this does not explain the behaviour of the .polygon method, I have found a workaround.

By combining .polygon with .point .line you will get the desired result. (See edit below on why to use .line over .point)

#1 Point

Python 3.7.6 (v3.7.6:43364a7ae0, Dec 18 2019, 14:18:50) 
[Clang 6.0 (clang-600.0.57)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> from PIL import Image, ImageDraw
>>> import numpy as np
>>> img = Image.new('L', (5, 5), 0)
>>> ImageDraw.Draw(img).polygon([2, 2, 2, 2, 2, 2, 2, 2], outline=1, fill=1)
>>> ImageDraw.Draw(img).line([2, 2, 2, 2, 2, 2, 2, 2], fill=1)
>>> np.array(img)
array([[0, 0, 0, 0, 0],
       [0, 0, 0, 0, 0],
       [0, 0, 1, 0, 0],
       [0, 0, 0, 0, 0],
       [0, 0, 0, 0, 0]], dtype=uint8)

#2 Line

>>> img = Image.new('L', (5, 5), 0)
>>> ImageDraw.Draw(img).polygon([1, 1, 2, 1, 2, 1, 1, 1], outline=1, fill=1)
>>> ImageDraw.Draw(img).line([1, 1, 2, 1, 2, 1, 1, 1], fill=1)
>>> np.array(img)
array([[0, 0, 0, 0, 0],
       [0, 1, 1, 0, 0],
       [0, 0, 0, 0, 0],
       [0, 0, 0, 0, 0],
       [0, 0, 0, 0, 0]], dtype=uint8)

#3 Rectangle

>>> img = Image.new('L', (5, 5), 0)
>>> ImageDraw.Draw(img).polygon([1, 1, 2, 1, 2, 2, 1, 2], outline=1, fill=1)
>>> ImageDraw.Draw(img).line([1, 1, 2, 1, 2, 2, 1, 2], fill=1)
>>> np.array(img)
array([[0, 0, 0, 0, 0],
       [0, 1, 1, 0, 0],
       [0, 1, 1, 0, 0],
       [0, 0, 0, 0, 0],
       [0, 0, 0, 0, 0]], dtype=uint8)

EDIT: To make this also work for longer lines (see below) .line has to be used instead of .point.

With .point

>>> img = Image.new('L', (5, 5), 0)
>>> ImageDraw.Draw(img).polygon([1, 1, 3, 1, 3, 1, 1, 1], outline=1, fill=1)
>>> ImageDraw.Draw(img).point([1, 1, 3, 1, 3, 1, 1, 1], fill=1)
>>> np.array(img)
array([[0, 0, 0, 0, 0],
       [0, 1, 0, 1, 0],
       [0, 0, 0, 0, 0],
       [0, 0, 0, 0, 0],
       [0, 0, 0, 0, 0]], dtype=uint8)

With .line

>>> img = Image.new('L', (5, 5), 0)
>>> ImageDraw.Draw(img).polygon([1, 1, 3, 1, 3, 1, 1, 1], outline=1, fill=1)
>>> ImageDraw.Draw(img).line([1, 1, 3, 1, 3, 1, 1, 1], fill=1)
>>> np.array(img)
array([[0, 0, 0, 0, 0],
       [0, 1, 1, 1, 0],
       [0, 0, 0, 0, 0],
       [0, 0, 0, 0, 0],
       [0, 0, 0, 0, 0]], dtype=uint8)

Upvotes: 1

Related Questions