Recognize Open and Closed Shapes Opencv

Recognize open and closed shapes opencv

Just use findContours() in your image, then decide whether the contour is closed or not by examining the hierarchy passed to the findContours() function. From the second figure it is clearer that no contour has child contour as compared to the first image, you will get this data from hierarchy parameter which is optional output vector, containing information about the image topology. It has as many elements as the number of contours.

Here we will use hierarchy as

vector< Vec4i > hierarchy

where for an i-th contour

hierarchy[i][0] = next contour at the same hierarchical level
hierarchy[i][1] = previous contour at the same hierarchical level
hierarchy[i][2] = denotes its first child contour
hierarchy[i][3] = denotes index of its parent contour

If for the contour i there are no next, previous, parent, or nested contours, the corresponding elements of hierarchy[i] will be negative. See findContours() function for more details.

So by checking the value hierarchy[i][2] you can decide the contour belongs to closed or not, that is for a contour if the hierarchy[i][2] = -1 then no child and it belongs to opened.

And one more thing is that in findContours() function you should use CV_RETR_CCOMP which retrieves all of the contours and organizes them into a two-level hierarchy.

Here is the C++ code how to implement this.

    Mat tmp,thr;
Mat src=imread("1.png",1);
cvtColor(src,tmp,CV_BGR2GRAY);
threshold(tmp,thr,200,255,THRESH_BINARY_INV);

vector< vector <Point> > contours; // Vector for storing contour
vector< Vec4i > hierarchy;
findContours( thr, contours, hierarchy,CV_RETR_CCOMP, CV_CHAIN_APPROX_SIMPLE );

for( int i = 0; i< contours.size(); i=hierarchy[i][0] ) // iterate through each contour.
{
Rect r= boundingRect(contours[i]);
if(hierarchy[i][2]<0) //Check if there is a child contour
rectangle(src,Point(r.x-10,r.y-10), Point(r.x+r.width+10,r.y+r.height+10), Scalar(0,0,255),2,8,0); //Opened contour
else
rectangle(src,Point(r.x-10,r.y-10), Point(r.x+r.width+10,r.y+r.height+10), Scalar(0,255,0),2,8,0); //closed contour
}

Result:

Sample Image

How can i know if a contour is open or closed in opencv?

You're looking for the terms concave (like a C) vs convex (like an O) contours.

And guess what, there is a method to check for convexity:

cv2.isContourConvex(contour)

Shape Openness detection in OpenCV

There is a great way to do it using ImageDraw.floodfill function.
Attached is a working code:

from PIL import Image, ImageDraw

def openOrClose(img2):
isCLosed = False
img = cv2.cvtColor(img2, cv2.COLOR_GRAY2BGR)
target_pixel = (0,0) #Corner of the image
target_color = (255,255,0) #Yellow
im_pil = Image.fromarray(img)

ImageDraw.floodfill(im_pil,target_pixel,target_color)
im = np.asarray(im_pil)

count =0
for i in range(im.shape[0]):
for j in range(im.shape[1]):
if ((im[i][j] == [255,255,255]).all() == True):
count+=1
if count != 0:
isCLosed = True
return isCLosed

and this is the result:
Sample Image

find closed contour for sample image using opencv

To use simple OpenCV functions to detect the box, you'll have to make sure the input image conditions are ideal. Like distinct background color from box etc.

First convert the image to HSV color space and segment out the box. Use the following trackbar code to figure out the proper values -

import cv2
import numpy as np

def nothing(x):
pass

cap = cv2.VideoCapture(0)
cv2.namedWindow('img')

cv2.createTrackbar('lH','img',0,255,nothing)
cv2.createTrackbar('hH','img',255,255,nothing)

cv2.createTrackbar('lS','img',0,255,nothing)
cv2.createTrackbar('hS','img',255,255,nothing)

cv2.createTrackbar('lV','img',0,255,nothing)
cv2.createTrackbar('hV','img',255,255,nothing)

while(True):

ret,frame = cap.read()

l_H = cv2.getTrackbarPos('lH', 'img')
h_H = cv2.getTrackbarPos('hH', 'img')
l_S = cv2.getTrackbarPos('lS', 'img')
h_S = cv2.getTrackbarPos('hS', 'img')
l_V = cv2.getTrackbarPos('lV', 'img')
h_V = cv2.getTrackbarPos('hV', 'img')

hsv = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV)
lower_hsv = np.array([l_H, l_S, l_V])
higher_hsv = np.array([h_H, h_S, h_V])

mask = cv2.inRange(hsv, lower_hsv, higher_hsv)

frame = cv2.bitwise_and(frame, frame, mask=mask)

cv2.imshow('image', mask)
if(cv2.waitKey(10)==27):break

Perform morphological operations if needed. If your background is distinct, simply finding contours and bounding box will get you the box.

If not, you can find the horizontal and vertical lines as shown in this link.

To find polygons in an image, check this.

You can use a proper combination of these to find your box.

Detect non-closed contour on opencv

There are multiple possible solutions.

The simplest one may be:

  • if FindContours does not find a closed contour, repeat the canny filter with a slightly decreased low_threshold, until you find a closed contour. If the closed contour has roughly the right size and shape, it is a card. The answer linked by Haris explains how to check whether a contour is closed

Another rather simple solution:

  • Don't apply Canny to the image at all. Execute findContours on the otsu thresholded image. Optionally use morphological opening and closing on the thresholded image to remove noise before findContours

FindContours does not need an edge image, it is usually executed with a thresholded image. I don't know your source image, so I cannot say how good this would work, but you would definitely avoid the problem of holes in the shape.

If the source image does not allow this, then the following may help:

  • use watershed to separate the card from the background. Use a high threshold to get some seed pixels that are definitely foreground and a low threshold to get pixels that are definitely background, then grow those two seeds using cv:watershed().

If the background in that image is the same color as the card, then the previous two methods may not work so well. In that case, your best bet may be the solution suggested by Micka:

  • use hough transform to find the 4 most prominent lines in the image. Form a rectangle with these 4 lines.


Related Topics



Leave a reply



Submit