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:
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:
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
C++ Object Instantiation VS Assignment
Is C++ Considered Weakly Typed? Why
Opencv Image Loading for Opengl Texture
Qt 5.5 Embed External Application into Qwidget
C++11 Scope Exit Guard, a Good Idea
Qthread Emits Finished() Signal But Isrunning() Returns True and Isfinished() Returns False
Can #If Pre-Processor Directives Be Nested in C++
Is It Ever Not Safe to Throw an Exception in a Constructor
C++ Function Call Wrapper with Function as Template Argument
In C++ I Cannot Grasp Pointers and Classes
C++11 Compiler: Closest to the Standard and How Close
How to Asynchronously Copy Memory from the Host to the Device Using Thrust and Cuda Streams
Does an Unused Member Variable Take Up Memory
Efficiency of Postincrement V.S. Preincrement in C++
Concatenate Compile-Time Strings in a Template at Compile Time