Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

like image 768
Fnord Avatar asked Oct 26 '25 13:10

Fnord


2 Answers

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

like image 157
Fnord Avatar answered Oct 29 '25 02:10

Fnord


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

Original example Updated with floodfill

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)
like image 39
Ben Avatar answered Oct 29 '25 03:10

Ben



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!