Menaga
Menaga

Reputation: 115

Opencv Python - Shape detection

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.enter image description here

Please find the input image,

enter image description here

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. enter image description here

Upvotes: 0

Views: 3751

Answers (1)

bfris
bfris

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:

output image

Upvotes: 3

Related Questions