Search code examples
pythonopencvgraphics2ddrawing

Python opencv draws polygons outside of lines


[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.

enter image description here


Solution

  • 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)
    
    

    enter image description here