31

I'm trying to draw a polygon using the python interface to opencv, cv2. I've created an empty image, just a 640x480 numpy array. I have a list of polygons (four point quadrilaterals) that I want to draw on the image, however, I can't seem to get the formate right to instruct cv2 where the quadrilaterals should be, and I keep getting this error:

OpenCV Error: Assertion failed (points.checkVector(2, CV_32S) >= 0) in fillConvexPoly, file .../OpenCV-2.4.0/modules/core/src/drawing.cpp, line 2017

My code consists of essentially the following:

binary_image = np.zeros(image.shape,dtype='int8')
for rect in expected:
    print(np.array(rect['boundary']))
    cv2.fillConvexPoly(binary_image, np.array(rect['boundary']), 255)
fig = pyplot.figure(figsize=(16, 14))
ax = fig.add_subplot(111)
ax.imshow(binary_image)
pyplot.show()

where my list of rects in expected has the 'boundary' containing the value of a list of (x,y) points. The code prints:

[[ 91 233]
 [419 227]
 [410 324]
 [ 94 349]]

I figured that this is the list of points for a polygon, but apparently that list has an invalid points.checkvector, whatever that is. A google search for that error turned up nothing useful.

Alexis Pigeon
  • 7,423
  • 11
  • 39
  • 44
DaveA
  • 1,227
  • 2
  • 14
  • 19
  • I've tried both cv2.fillPoly and cv2.fillConvexPoly here, and both throw the same error... – DaveA Jun 29 '12 at 23:45
  • note, openCV wants 'uint8' for images. While I appeared to be able to get away with 'int8' here, it caused me trouble later... – DaveA Jul 02 '12 at 18:47
  • 4
    The openCV documentation is particularly annoying on this function: no specification about inputs types in Python, no examples. Just to note that the second argument is a `list` of `numpy` array of shape `n x 2`, the numpy array representing vertices of a polygon. – yuqli Aug 21 '19 at 02:41

5 Answers5

38
import numpy as np
import cv2
import matplotlib.pyplot as plt

a3 = np.array( [[[10,10],[100,10],[100,100],[10,100]]], dtype=np.int32 )
im = np.zeros([240,320],dtype=np.uint8)
cv2.fillPoly( im, a3, 255 )

plt.imshow(im)
plt.show()

result display

Check on colab.research.google.com

themadmax
  • 2,344
  • 1
  • 31
  • 36
  • Tip for those getting `ValueError: setting an array element with a sequence.` when creating the numpy array of vertices (`a3` in this answer): this could be due to the fact that your polygons do not have the same number of points, which means the list has a shape that numpy cannot convert into a ndarray (not a "nd-cube"). My solution is to make one call to fillPoly per polygon. An alternative could be padding each polygon vertices list with their last vertex so that they all have the same length. – Gabriel Devillers May 26 '19 at 18:22
  • @themadmax, you have to go counterclockwise from topleft for the points? – mLstudent33 Oct 17 '19 at 02:12
  • this is also a tough example to follow that does not show the ordering of x and y – mLstudent33 Oct 17 '19 at 03:56
  • 1
    @mLstudent33, it actually a3, as referred in the above example, should have the coordinates in the clock-wise direction. That is to say, [Coordinates of Top-Left, Top-Right, Bottom-Right, Bottom-Left]. – KPandian May 13 '20 at 03:38
17

The AssertionError is telling you that OpenCV wants a signed, 32-bit integer. The array of polygon points should have that particular data type (e.g. points = numpy.array(A,dtype='int32') ). You could also just cast it for the function call (i.e. my_array.astype('int32') ) or as a friend put it once...

" Changing

   cv2.fillConvexPoly(binary_image, np.array(rect['boundary']), 255) to

   cv2.fillConvexPoly(binary_image, np.array(rect['boundary'], 'int32'), 255) "

Jeru Luke
  • 20,118
  • 13
  • 80
  • 87
adrien g
  • 186
  • 1
  • 2
7

I have tried in opencv 2.4.2 and python 2.7. From the c++ interface

void fillPoly(Mat& img, 
              const Point** pts, 
              const int* npts, 
              int ncontours, 
              const Scalar& color, 
              int lineType=8, 
              int shift=0, 
              Point offset=Point() 
             )

we know the pts is the array of array of points, so you should change like this

cv2.fillConvexPoly(binary_image, np.array([rect['boundary']], 'int32'), 255)

add [ ] to the rect['boundary'].

Tommy Grovnes
  • 4,126
  • 2
  • 25
  • 40
Cheaster
  • 71
  • 3
1

Here's an example with annotated points. You can specify the polygon vertices in either clockwise or counter-clockwise order, as long as they follow the same direction.

import numpy as np
import matplotlib.pyplot as plt
import cv2

A = np.array([125,50])
B = np.array([50,50])
C = np.array([50,175])
D = np.array([150,150])

pts = np.array( [ A, B, C, D ] ) # counter-clockwise
#pts = np.array( [ A, D, C, B ] ) # clockwise

print(pts.shape) # (4, 2)

image = np.zeros((200,200), dtype=np.uint8)
image = np.dstack((image, image, image)) # Create three channels.

cv2.fillPoly(image, pts=[pts], color =(0,255,0))

for pt in pts:
    x = pt[0]
    y = pt[1]
    _ = plt.annotate(s='%d, %d' % (x, y), xy=(x, y), color='red', fontsize=20)

print(image.shape) # (200, 200, 3)

plt.imshow(image)
plt.grid(True)
plt.show()

enter image description here

What happens if your vertices are rearranged so that they don't follow the same direction?

pts = np.array( [ A, B, D, C ] )

You get this:

enter image description here

stackoverflowuser2010
  • 38,621
  • 48
  • 169
  • 217
-1

Here is an example that might help.

def region_of_interest(image):
    height = image.shape[0]
    polygons = np.array([
      [(200,height),(1100,height ),(550,250)]])
    mask = np.zeros_like(image)
    cv2.fillPoly(mask, polygons, 255)
    return mask
sayalok
  • 882
  • 3
  • 15
  • 30