37

I have binary image with polylines created with:

cv2.polylines(binaryImage,contours,1, (255,255,255))

What I need now is effective method to fill all polylines. I haven't found such method in opencv, but maybe it exists. Alternatively, maybe I could implement algorithm to do the job (but fast one- I have HD ready pictures). Please share your thoughts..

user2233244
  • 417
  • 1
  • 4
  • 6

4 Answers4

65

Use cv2.drawContours() with thickness=cv2.FILLED:

cv2.drawContours(img, contours, -1, color=(255, 255, 255), thickness=cv2.FILLED)
Mateen Ulhaq
  • 24,552
  • 19
  • 101
  • 135
Ash Ketchum
  • 1,940
  • 1
  • 12
  • 6
63

I think what you are looking for is cv2.fillPoly, which fills the area bounded by one or more polygons. This is a simple snippet, I generate a contour of four points representing vertices of a square, then I fill the polygon with a white color.

import numpy as np
import cv2

contours = np.array( [ [50,50], [50,150], [150, 150], [150,50] ] )
img = np.zeros( (200,200) ) # create a single channel 200x200 pixel black image 
cv2.fillPoly(img, pts =[contours], color=(255,255,255))
cv2.imshow(" ", img)
cv2.waitKey()

enter image description here

jabaldonedo
  • 25,822
  • 8
  • 77
  • 77
4

You can use fillPoly or drawContours if your contour is closed. Pulling together @jabaldonedo and @ash-ketchum answers:

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

# Lets first create a contour to use in example
cir = np.zeros((255,255))
cv2.circle(cir,(128,128),10,1)
res = cv2.findContours(cir.astype(np.uint8), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)
contours = res[-2] # for cv2 v3 and v4+ compatibility

# An open circle; the points are in contours[0]
plt.figure()
plt.imshow(cir)

# Option 1: Using fillPoly
img_pl = np.zeros((255,255))
cv2.fillPoly(img_pl,pts=contours,color=(255,255,255))
plt.figure()
plt.imshow(img_pl)

# Option 2: Using drawContours
img_c = np.zeros((255,255))
cv2.drawContours(img_c, contours, contourIdx=-1, color=(255,255,255),thickness=-1)
plt.figure()
plt.imshow(img_c)

plt.show()

both img_pl and img_c contain a filled in circle from the points in contour[0]

For context, this was tested with python 3.6.2, OpenCV (cv2.version) 3.2.0, numpy 1.13.1 and matplotlib 2.0.2. I suspect it would work with anything cv2 3+ and python 3.5+. Per @elyas-karimi (and the OpenCV docs) findContours returns 3 values in 3.* and 2 in 4.* dropping the image return (and since 3.2 unmodified).

Brian
  • 313
  • 2
  • 7
  • Running the code gives this error : ```ValueError: not enough values to unpack (expected 3, got 2)``` – Omar Shabab Nov 18 '20 at 12:50
  • 2
    @OmarShabab Maybe your OpenCV version is different. In higher versions `cv2.findContours()` returns only two arguments, not three. You just need to use this method like this: `contours, _ = cv2.findContours(cir.astype(np.uint8), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)`(just removed first "_" sign). Here is the OpenCV documentation(OpenCV 4.3.0): https://docs.opencv.org/4.3.0/d4/d73/tutorial_py_contours_begin.html – Elyas Karimi Nov 19 '20 at 23:14
1

I know the OP asked regarding using OpenCV specifically, but I ended up here just trying to fill the segmentation polygons that I had despite (also, OpenCV was a bit problematic for my case) the library and I believe many other users did too, so here is my solution using scikit image's polygon function.

From the docs:

import matplotlib.pyplot as plt

from skimage.draw import line, polygon, circle, ellipse
import numpy as np


img = np.zeros((500, 500, 3), 'uint8')

# draw line
rr, cc = line(120, 123, 20, 400)
img[rr,cc,0] = 255

# fill polygon
poly = np.array((
    (300, 300),
    (480, 320),
    (380, 430),
    (220, 590),
    (300, 300),
))
rr, cc = polygon(poly[:,0], poly[:,1], img.shape)
img[rr,cc,1] = 255

# fill circle
rr, cc = circle(200, 200, 100, img.shape)
img[rr,cc,:] = (255, 255, 0)

# fill ellipse
rr, cc = ellipse(300, 300, 100, 200, img.shape)
img[rr,cc,2] = 255

plt.imshow(img)
plt.show()

Result: Filled polygons

Gabriel Ziegler
  • 361
  • 3
  • 18