[edited] It appears there is a new bug in opencv that introduces an issue causing fillPoly's boundaries to exceed polylines's.
Here is humble code to draw a red filled polygon with a blue outline.
import cv2
import numpy as np
def draw_polygon(points, resolution=50):
    
    # create a blank black canvas
    img = np.zeros((resolution, resolution, 3), dtype=np.uint8)
    pts = np.array(points, np.int32)
    pts = pts.reshape((-1, 1, 2))
    
    # draw a filled polygon in blue
    cv2.fillPoly(img, [pts], (0, 0, 255))
    
    # draw an outline in red
    cv2.polylines(img, [pts], True, (255, 0, 0), 1)
    
    # show the image
    cv2.imshow("Polygon", img)
    cv2.waitKey(0)
    cv2.destroyAllWindows()
    
    
# why is the infill outside the line?
if __name__ == "__main__":
    # 4 vertices of the quad (clockwise)
    quad = np.array([[[44, 27],
                      [7, 37],
                      [7, 19],
                      [38, 19]]])
    
    draw_polygon(quad)
QUESTION
The polygon's infill appears to bleed outside of the outline (two highlighted pixels). I'm looking for a temporary solution until this bug is addressed so the infill stays completely inside the outline.
Solution has to work with concave polygons.

Where there's a will there's a way. fillPoly appears to be able to draw as a line when given only two vertices. And that line matches the edges of the previously drawn polygon.   \o/
I modified my code to draw the edges as a single fillPoly call and it seems to work decently.
I would still prefer if fillPoly and polyLines would match, but for now I am unblocked.
import cv2
import numpy as np
def draw_polygon(points, resolution=50):
    
    # create a blank black canvas
    img = np.zeros((resolution, resolution, 3), dtype=np.uint8)
    poly_pts = np.array(points, np.int32)
    edge_pts = np.vstack([poly_pts, poly_pts[0]])
    
    # draw a filled polygon in blue
    cv2.fillPoly(img, [poly_pts], (0, 0, 255))
    
    # draw the outlines as individual edges,
    # drawn in a single fillPoly call, in red
    e0 = edge_pts[:-1]
    e1 = edge_pts[1:]
    edge_polygons = np.hstack((e0[:,None], e1[:,None]))
    cv2.fillPoly(img, edge_polygons, (255, 0, 0))  
    
    # show the image
    cv2.imshow("Polygon", img)
    cv2.waitKey(0)
    cv2.destroyAllWindows()
    
    
# better outline
if __name__ == "__main__":
    # 4 vertices of the quad (clockwise)
    quad = np.array([[44, 27],
                      [7, 37],
                      [7, 19],
                      [38, 19]])
    
    draw_polygon(quad)

This was trickier than I thought.
The only solution I could come up with is to use floodfill. It works, but I'm not sure about my seed point calculation.
x = sum([p[0] for p in points[0]]) // len(points[0])
y = sum([p[1] for p in points[0]]) // len(points[0])
seed = (x,y)
cv2.floodFill(img, None, seed, (0, 0, 255))
import cv2
import numpy as np
def draw_polygon(points, resolution=50):
    # create a blank black canvas
    img = np.zeros((resolution, resolution, 3), dtype=np.uint8)
    pts = np.array(points, np.int32)
    pts = pts.reshape((-1, 1, 2))
    # draw an outline in red
    cv2.polylines(img, [pts], True, (255, 0, 0), 1)
    # Need an inside point to start flooding from.
    # !! Not sure if this method is guaranteed to be inside the polygon !!
    x = sum([p[0] for p in points[0]]) // len(points[0])
    y = sum([p[1] for p in points[0]]) // len(points[0])
    inside_point = (x, y)
    # floodfill the polygon with blue
    cv2.floodFill(img, None, inside_point, (0, 0, 255))
    # show the image
    cv2.imshow("Polygon", img)
    cv2.waitKey(0)
    cv2.destroyAllWindows()
# why is the infill outside the line?
if __name__ == "__main__":
    # 4 vertices of the quad (clockwise)
    quad = np.array([[[44, 27],
                      [7, 37],
                      [7, 19],
                      [38, 19]]])
    draw_polygon(quad)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With