Reputation: 115
How to split the two rectangles in the image. Also to extract the coordinates of the rectangle with removing the extra projections. Contour detection gives the full image as a circle, instead of splitting it into two rectangles.
Please find the input image,
detect_shapes.py
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
blurred = cv2.GaussianBlur(gray, (5, 5), 0)
thresh = cv2.threshold(blurred, 60, 255, cv2.THRESH_BINARY)[1]
cnts = cv2.findContours(thresh.copy(), cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)
cnts = cnts[0] if imutils.is_cv2() else cnts[1]
sd = ShapeDetector()
for c in cnts:
M = cv2.moments(c)
cX = int((M["m10"] / M["m00"]))
cY = int((M["m01"] / M["m00"]))
shape = sd.detect(c)
c = c.astype("float")
#c *= ratio
c = c.astype("int")
cv2.drawContours(image, [c], -1, (0, 255, 0), 2)
cv2.putText(image, shape, (cX, cY), cv2.FONT_HERSHEY_SIMPLEX,
0.5, (255, 0, 0), 2)
cv2.imshow("Image", image)
cv2.waitKey(0)
shapedetector.py
class ShapeDetector:
def __init__(self):
pass
def detect(self, c):
shape = "unidentified"
peri = cv2.arcLength(c, True)
approx = cv2.approxPolyDP(c, 0.04 * peri, True)
if len(approx) == 3:
shape = "triangle"
elif len(approx) == 4:
print("value of approx", approx)
(x, y, w, h) = cv2.boundingRect(approx)
ar = w / float(h)
print("value of ar",ar)
if (ar >= 0.95 and ar <= 1.05): shape = "Square"
elif (ar <= 5 and ar >= 3): shape = "Obround"
else: shape = "rectangle"
elif len(approx) == 5:
shape = "pentagon"
elif len(approx) == 2:
shape = "line"
print("value of approx", approx)
else:
shape = "circle"
print("value of approx", approx)
return shape
My required output is the below.
Upvotes: 0
Views: 3751
Reputation: 5835
As Akhilesh suggested, a morphology OPEN operation will remove the line connecting the two rectangles. An excellent Python tutorial is here: https://docs.opencv.org/3.0.0/d9/d61/tutorial_py_morphological_ops.html. Use the smallest kernel size that will work for your application to prevent too much distortion. For your test image, I used a kernel size of 5.
detect_shapes.py:
import cv2
from shapedetector import ShapeDetector
image = cv2.imread('rectangles.jpg')
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
blurred = cv2.GaussianBlur(gray, (5, 5), 0)
thresh = cv2.threshold(blurred, 60, 255, cv2.THRESH_BINARY)[1]
ksize = 5
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (ksize,ksize))
thresh = cv2.morphologyEx(thresh, cv2.MORPH_OPEN, kernel)
cnts = cv2.findContours(thresh.copy(), cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)
# ~ cnts = cnts[0] if imutils.is_cv2() else cnts[1]
cnts = cnts[1]
sd = ShapeDetector()
for c in cnts:
M = cv2.moments(c)
if M["m00"] != 0: # prevent divide by zero
cX = int((M["m10"] / M["m00"]))
cY = int((M["m01"] / M["m00"]))
shape = sd.detect(c)
c = c.astype("float")
#c *= ratio
c = c.astype("int")
cv2.drawContours(image, [c], -1, (0, 255, 0), 2)
cv2.putText(image, shape, (cX, cY), cv2.FONT_HERSHEY_SIMPLEX,
0.5, (255, 0, 0), 2)
cv2.imshow("Image", image)
cv2.waitKey(0)
shapedetector.py:
import cv2
class ShapeDetector:
def __init__(self):
pass
def detect(self, c):
shape = "unidentified"
peri = cv2.arcLength(c, True)
approx = cv2.approxPolyDP(c, 0.04 * peri, True)
if len(approx) == 3:
shape = "triangle"
elif len(approx) == 4:
print("value of approx", approx)
(x, y, w, h) = cv2.boundingRect(approx)
ar = w / float(h)
print("value of ar",ar)
if (ar >= 0.95 and ar <= 1.05): shape = "Square"
elif (ar <= 5 and ar >= 3): shape = "Obround"
else: shape = "rectangle"
elif len(approx) == 5:
shape = "pentagon"
elif len(approx) == 2:
shape = "line"
print("value of approx", approx)
else:
shape = "circle"
print("value of approx", approx)
return shape
Output image:
Upvotes: 3