I\'m trying to find the contour of a rectangle object with round corner in a image. I tried HoughLinesP
and findContours
, but did not achieve the d
You need to find the bounding rectangle of the found contours.
img = cv2.imread("image.png", -1)
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
binary = cv2.bitwise_not(gray)
(_,contours,_) = cv2.findContours(binary, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)
for contour in contours:
(x,y,w,h) = cv2.boundingRect(contour)
cv2.rectangle(img, (x,y), (x+w,y+h), (0,255,0), 2)
I've been thinking to extend these lines and get intersection points of lines. Finally, I will get four coordinates of rectangle.
More or less, this is a good approach if contours doesn't work out for you. Indeed, as you say,
But if the image is more complex, I don't know how to deal with.
there are some complications to deal with. The main problem is that typical line detection doesn't give you perfect line segments every time. You may have multiple line segments along the same line, either stacked lengthwise or multiple overlapping. Additionally, you'll need to segment the lines automatically in some way so that you're not trying to find the intersection of parallel lines.
However, both of these issues are not too hard to deal with.
I answered a question awhile ago on this site about finding intersection points from HoughLinesP
that uses a lot of the below suggestions, though is not as robust (segmenting the lines into two groups for e.g. was done much more naively), but it should give you a good place to start from.
After you detect the lines, you need to segment the lines into groups or parallel segments. If your rectangle is of a defined orientation, then you can just filter the lines based on that, which would be the easy case. But if the rectangle can be in any orientation, you'll need some other way to segment them. You can use k-means clustering with k=2 to find the two main angles, and put the lines corresponding to one angle in a bin and the lines corresponding to the other angle in another bin and find the intersections of the lines in one bin with the lines in the other bin. What's nice about this approach is it would work for any parallelogram. You could also reject lines from each bin if they're not within some threshold (like 10 degrees or something) from being at a right angle with the mean angle from the other bin, or something, if you wanted to stick to rectangles.
Once you have all the lines binned accordingly, you can calculate their intersection points. There's actually a nice formula using determinants for calculating the intersection points between two lines given two points on the line, which you already have from the endpoints. So, that's handy! Each line in bin 0 will have an intersection point with the line from bin 1, and that's all you need to do.
So at the end here you'd have 4 clusters of intersection points. One option is to simply group these together, again with k-means with k=4, and you'll have the centroids of those four point clusters, representing the corners of your rectangle. Of course, since you've used a lot of approximation steps along the way, your points won't exactly define a rectangle, so you'll have to fit the closest possible rectangle to those points. Or, instead of k-means, another method would be to try to find a subset of your many intersection points that most accurately represent a rectangle, and then fit the closest rectangle. Probably some way to use linear regression, least squares, RANSAC, etc to this problem. Or if you want you can just find the bounding rectangle of the four points with boundingRect()
.
You can find bounding rectangle of nonzero points.
image = cv2.imread("./img/findrect0.png", 1)
gray = util.grayImage(image)
gray_inv = cv2.bitwise_not(gray)
points = cv2.findNonZero(gray)
rect = cv2.boundingRect(points)