Draw and fill a polygon from 2 lines using openCV

I have a picture with 2 curves and multiple small lines which can be interpreted as noise.

I need to create a polygon with these curves and fill them with white like the output below :

https://i.stack.imgur.com/SRt5Y.png

I tried the following code :

import cv2
from google.colab.patches import cv2_imshow
from google.colab import drive
import numpy as np

filePath = '/gdrive/My Drive/teste17.png'
image = cv2.imread(filePath)

gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) 

cv2_imshow(image)
thresh = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)[1]

coords = np.column_stack(np.where(thresh > 0))

c2=np.stack((coords[:,1], coords[:,0]), axis=-1)


cv2.fillPoly(image, [c2], (255, 0, 0))

cv2_imshow(image)

But the output was a bit different from what I wanted :

https://i.stack.imgur.com/DovEt.png

Is there a better approach using openCV ? Any suggestions is appreciated.

I would try to use findContours() to find all white objects, next use contourArea() to get two objects with the biggest areas, and finally I would use only these objects to draw blue polygon.

Doc: Contour: Getting Started, Contour Features


At this moment I can get only

with this code (I don’t use Google Colab)

import cv2
#from google.colab.patches import cv2_imshow
#from google.colab import drive
import numpy as np

# emulate `cv2_imshow` without using `google.colab
cv2_imshow = lambda img:cv2.imshow('image', img) or cv2.waitKey(0)

#filePath = '/gdrive/My Drive/teste17.png'
filePath = 'image.png'

image = cv2.imread(filePath)

gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) 
#cv2_imshow(image)

thresh = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)[1]

# ---- 

# find all contours
contours, hierarchy = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE) 
print('len(contours):', len(contours))
#print(contours, hierarchy)

# calculate areas for all contours 
# and keep on list as `(area, contour)`
contour_area = []
for c in contours:
    print('area:', cv2.contourArea(c))
    contour_area.append((cv2.contourArea(c), c))

print('--- contour_area ---')
for item in contour_area:
    print('contour_area:', item[0])
    
# sort list with `(area, contour)` using only `area`    
contour_area = sorted(contour_area, key=lambda x:x[0], reverse=True)

print('--- contour_area - sorted ---')
for item in contour_area:
    print('contour_area:', item[0])
    
# get two the biggest contours    
print('--- two the biggest contours ---')    
print('p0:', contour_area[0][0])
print('p1:', contour_area[1][0])

# draw them
coords = np.vstack([contour_area[0][1], contour_area[1][1]])
cv2.fillPoly(image, [coords], (255, 0, 0))

cv2_imshow(image)

1 Like

Thank you !

I actually created a black image (image2) and filled it with the blue contours calculated in white. Then I found all white pixels in the picture and used these coordinates to apply cv2.fillPoly on the original image.

Here is the code :

import cv2

from google.colab.patches import cv2_imshow

from google.colab import drive

import numpy as np

filePath = '/gdrive/My Drive/teste17.png'

image = cv2.imread(filePath)

height,width,channel = image.shape

print(channel)

#image = cv2.rotate(image, cv2.cv2.ROTATE_90_CLOCKWISE)

gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) 

cv2_imshow(image)

thresh = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)[1]

contours, hierarchy = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE) 

contour_area = []

for c in contours:

    print('area:', cv2.contourArea(c))

    contour_area.append((cv2.contourArea(c), c))

print('--- contour_area ---')

for item in contour_area:

    print('contour_area:', item[0])

    

# sort list with `(area, contour)` using only `area`    

contour_area = sorted(contour_area, key=lambda x:x[0], reverse=True)

print('--- contour_area - sorted ---')

for item in contour_area:

    print('contour_area:', item[0])

    

# get two the biggest contours    

print('--- two the biggest contours ---')    

print('p0:', contour_area[0][0])

print('p1:', contour_area[1][0])

image2 = np.zeros((height, width, 3), dtype = "uint8")

# draw them

coords1 = np.vstack([contour_area[0][1], contour_area[1][1]])

cv2.fillPoly(image2, [coords1], (255, 255, 255))

gray = cv2.cvtColor(image2, cv2.COLOR_BGR2GRAY) 

thresh = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)[1]

coords = np.column_stack(np.where(thresh1 > 0))

c2=np.stack((coords[:,1], coords[:,0]), axis=-1)

cv2.fillPoly(image, [c2], (255, 255, 255))

cv2_imshow(image)

And I got the right output :

Once again, thank you for your help !

1 Like