For the shape-detection there is a great-tutorial called opencv-shape-detection. However the pre-processing in the tutorial won't help you to find the big-box in the image. You need to apply adaptiveThreshold
instead threshold
. Here are the steps:
-
- Resize the image and calculate the ratio
-
- Smooth the image
-
- Apply adaptive-threshold
-
- Find and grab the contours.
-
- Calculate perimeter and the approximate length
-
- If length equals to 4 (means either square or rectangle), draw the contour.
Step-1
- We resize the image to make the computation and detection easier. However, we also need to calculate the ratio so we don't lose the center of each contour.
Step-2

We applied gaussian-blur to smooth the image. Most of the artifacts in the image was removed.
-
blr = cv2.GaussianBlur(gry, (5, 5), 0)
Step-3
Simple-thresholding was not producing satisfactory results with different parameters. Therefore I used adaptiveThreshold
to get the result:

-
thr = cv2.adaptiveThreshold(blr, 255, cv2.ADAPTIVE_THRESH_MEAN_C, cv2.THRESH_BINARY, 41, 21)
Step-4
- Finding the contour in the same way you did with a different hierarchy parameter. See Contours-hierarchy
Step-5
- For each contour, perimeter and approximation parameters were calculated. See Contour-features
Step-6
Code:
import cv2
import imutils
# Load the image
img = cv2.imread("zE2lg.jpg")
# Resize the image
rsz = imutils.resize(img, width=300)
# Calculate the ratio
ratio = img.shape[0] / float(rsz.shape[0])
# Convert to gray-scale
gry = cv2.cvtColor(rsz, cv2.COLOR_BGR2GRAY)
# Apply Gaussian-blur
blr = cv2.GaussianBlur(gry, (5, 5), 0)
# Apply threshold
thr = cv2.adaptiveThreshold(blr, 255, cv2.ADAPTIVE_THRESH_MEAN_C, cv2.THRESH_BINARY, 41, 21)
# Find and grab contours
cnt = cv2.findContours(thr.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cnt = imutils.grab_contours(cnt)
# Loop over contours
for c in cnt:
mmn = cv2.moments(c)
if mmn["m00"] != 0:
x = int((mmn["m10"] / mmn["m00"]) * ratio)
y = int((mmn["m01"] / mmn["m00"]) * ratio)
prm = cv2.arcLength(c, True) # perimeter
apx = cv2.approxPolyDP(c, 0.09 * prm, True) # approximation
if len(apx) == 4:
c = c.astype("float")
c *= ratio
c = c.astype("int")
cv2.drawContours(img, [c], -1, (0, 0, 255), thickness=5)