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:
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).
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))
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:
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:
Obtain binary image. Load image, grayscale, Gaussian blur, and Otsu's threshold.
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
.
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
Rectangles: 4.0
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()