2
votes

Background

I have two images, and I want to count the number of rectangles in both of them.

I have written some code that finds contours, and uses that to find rectangles. But its not working as expected, so I would like some help:

Issue

I'm confused as to why the code finds the number of rectangles it does. For example in the first image, it counts 8, I would expect 4.

In the second it counts 16, which I think it correct (15 interior, and 1 exterior).

Code

My code is as follows:

import cv2
import numpy as np

pic = 'boxes1'
image = cv2.imread(f'../Computer Vision/{pic}.jpg', 1)

blur = cv2.pyrMeanShiftFiltering(image, 11, 21)
gray = cv2.cvtColor(blur, cv2.COLOR_BGR2GRAY)
thresh = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)[1]

contours = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
contours = contours[0] if len(contours) == 2 else contours[1]

rect_list = []

for cont in contours:
    peri = cv2.arcLength(cont, True)
    approx = cv2.approxPolyDP(cont, 0.015 * peri, True)
    if len(approx) == 4:
        x,y,w,h = cv2.boundingRect(approx)
        rect = x,y,w,h
        rect_list.append(rect)
        cv2.rectangle(image,(x,y),(x+w,y+h),(36,255,12),2)

cv2.imshow('thresh', thresh)
cv2.imwrite(f'output_{pic}.png', thresh)
cv2.waitKey(0)
cv2.destroyAllWindows()
print(len(rect_list))

Current Outputs

The code finds 8 rectangles in the first image, and 16 in the second. I think the first should be 4, and the second is probably correct (?) (15 interior and 1 exterior).

The code saves the following outputs:

1
in the first image there are 8 rectangles, always alterning white and black onesMicka

1 Answers

1
votes

With the observation that a rectangle has exactly four corners, we can use this fact and simply count the number of corners in the image. The number of rectangles in the image should then be the number of corners divided by four. Here's the approach:

  1. Obtain binary image. Load image, grayscale, Gaussian blur, and Otsu's threshold.

  2. Remove small noise. We find contours then filter using contour area filtering with cv2.contourArea and remove the noise by filling in the contour with cv2.drawContours.

  3. Find corners. We use the Shi-Tomasi Corner Detector already implemented as cv2.goodFeaturesToTrack for corner detection. Take a look at this for an explanation of each parameter.


Corners highlighted in green

enter image description here

Rectangles: 4.0

enter image description here

Rectangles: 16.0

Code

import cv2

# Load image, grayscale, blur, Otsu's threshold
image = cv2.imread('1.jpg')
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
blur = cv2.GaussianBlur(gray, (3,3), 0)
thresh = cv2.threshold(blur, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)[1]

# Remove small noise with contour area filtering
cnts = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
cnts = cnts[0] if len(cnts) == 2 else cnts[1]
for c in cnts:
    area = cv2.contourArea(c)
    if area < 150:
        cv2.drawContours(thresh, [c], -1, 0, -1)

# Find corners and draw onto image
corners = cv2.goodFeaturesToTrack(thresh,150,0.5,5)
for corner in corners:
    x,y = corner.ravel()
    cv2.circle(image,(x,y),3,(36,255,12),-1)

# The number of rectangles is corners / 4
print('Rectangles: {}'.format(len(corners)/4))

cv2.imshow('image', image)
cv2.waitKey()