Differentiation between rectangle and square in image using opencv2 python

Question:

I’m learning how to recognize shapes that’re within image provided. I’m able to recognize shape by number of edges present to the geometrical body. But now I’m wondering is there any way to differentiate between square and rectangle within image?
Here’s my code. Currently I’m just drawing contours to the geometrical figures.

import cv2

raw_image = cv2.imread('test1.png')
cv2.imshow('Original Image', raw_image)
cv2.waitKey(0)

bilateral_filtered_image = cv2.bilateralFilter(raw_image, 5, 175, 175)
cv2.imshow('Bilateral', bilateral_filtered_image)
cv2.waitKey(0)

edge_detected_image = cv2.Canny(bilateral_filtered_image, 75, 200)
cv2.imshow('Edge', edge_detected_image)
cv2.waitKey(0)

_, contours, hierarchy = cv2.findContours(edge_detected_image, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)

contour_list = []
for contour in contours:
    approx = cv2.approxPolyDP(contour,0.01*cv2.arcLength(contour,True),True)
    area = cv2.contourArea(contour)
    if ((len(approx) >= 3)):
        contour_list.append(contour)

cv2.drawContours(raw_image, contour_list,  -1, (0,0,0), 2)
cv2.imshow('Objects Detected',raw_image)
cv2.waitKey(0)

This is sample image

Asked By: Nuance

||

Answers:

I have an alternate way:

  1. Find all the connected components.

  2. Find the feature points in the original image, since in this case we have simple geometrical bodies not complex objects, you can use corner points as feature points using Harris corner algorithm.

  3. Group these feature points lying on the same connected components.

  4. Use the relation(distance/angle) between these features points on all the connected components and accordingly you can classify these geometrical shapes.

Answered By: flamelite

It is a square if its width and height are same. Otherwise its a rectangle.

for contour in contours:
    approx = cv2.approxPolyDP(contour,0.01*cv2.arcLength(contour,True),True)
    area = cv2.contourArea(contour)
    if ((len(approx) == 4)):
        (x, y, w, h) = cv2.boundingRect(approx)
        if  ((float(w)/h)==1):
            cv2.putText(raw_image, "square", (x, y), cv2.FONT_HERSHEY_SIMPLEX, 0.7, 0, 2)
        else:
            cv2.putText(raw_image, "rectangle", (x, y), cv2.FONT_HERSHEY_SIMPLEX, 0.7, 0, 2)

        contour_list.append(contour)

This change only detects squares and rectangles. Make necessary changes in the for loop to detect circles. You can work with len(approx) to make that distinction.

enter image description here

Answered By: I.Newton

The difference between a rectangle and a square is that a square has 4 equal sides making it a special rectangle. I learned this in kindergarden.

There are countless ways of identifying a square.

Its circumference always equals sqrt(area) hence its circularity is always 0.0625

The distance between all corners is either a or a
* sqrt(2)

The centroid has the same distance a / 2 to all 4 sides

You name it.

Answered By: Piglet

I read in the comments that the shapes might be rotated. In that case cv2.boundingRect() will not give correct results as this function always gives an upright rectangle. For detecting rectangles in general, (rotated or not), you should try cv2.minAreaRect() which will return a rectangle which will be rotated according to the contour and will have the minimum area to cover the contour. You can then check its aspect ratio to see if it is a square or a rectangle.

Answered By: Ojas.M