EDIT: I bypassed the problem by adding a 2-bit frame to the image, then using my code, and finally cropping the image to delete the extra frame. It is an ugly solution, but it works!
I encountered a problem which I'm not sure if it´s a bug or just my lack of experience. I´ll try to summarize it as clearly as possible:
I get a binary image which contains the contours of the color image I want to analyse (pixels in white are the perimeter of the contours detected by my algorithm, the rest is black). Image is pretty complex as the objects I want to detect fill the image entirely (no "background").
I use findcontours with that image:
contours, hierarchy = cv2.findContours(image,cv2.RETR_CCOMP,cv2.CHAIN_APPROX_NONE)
Then a "for" loop detects the contours with an area lesser than "X" pixels and hierarchy[0][x][3] >= 0
(lets call the new array "contours_2")
I draw "contours_2" in a new image:
cv2.drawContours(image=image2, contours=contours_2, contourIdx=-1, color=(255,255,255), thickness=-1)
The problem is that drawcontours draws all the contours good, BUT it doesnt "fill" the contours that are in the border of the image (this is, the ones that have one boundary in the edge of the image). I tried setting the border pixels of the image (like a frame) at True but it doesnt work, as findcontours sets automatically those pixels to zero (its in the function description).
Using cv2.contourArea
in the previous loop detects those contours well and returns normal values, so there is no way to know when a contour will be ignored by drawcontours and when it will be correctly filled. cv2.isContourConvex
doesn´t work at all, as returns every contour as false.
I'm able to draw those "edge" contours using cv2.convexHull before drawcontours on them, but I need to only use it on edges (because it deforms the contours and I want to avoid that as much as possible). The problem is... I have no way to detect which contours are in the edge of the image, and which ones are not and work OK with drawcontours.
So I´d like to ask why drawContour behaves like that and if there is some way to make it fill the contours in the edge. Or, another solution would be to find how to detect which contours are in the borders of the image (so I can apply convexhull when needed).
To put in simple words findContours detects change in the image color and marks it as contour. As an example, the image of number written on paper the number would be detected as contour. The part that you want to detect should be white like above numbers in 1st image.
If you have only 1 contour and you use contourIdx=-1
it will think each point in your contour is a separate contour. You need to wrap your single contour as a list (i.e contours=[contours_2]
if there is only one contour in contours_2
)
I have no problem filling the contour in the edge, aside from a small white line showing up at the edge.
import cv2
import numpy as np
rows=512
cols=512
# Create image with new colour for replacement
img= np.zeros((rows,cols,3), np.uint8)
img[:,:]= (0,0,0)
#img=cv2.copyMakeBorder(img,1,1,1,1, cv2.BORDER_CONSTANT, value=[0,0,0])
# Draw rectangle
img = cv2.rectangle(img,(400,0),(512,100),(255,255,255),-1)
imggray=cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# Find Contour
_, contours, hierarchy = cv2.findContours( imggray.copy(), cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE)
cv2.drawContours(img, contours, 0, (0,0,255), -1)
cv2.imshow('image',img)
cv2.waitKey(0)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With