Here's an simple approach using thresholding + morphological operations.
Obtain binary image. Load image, convert to grayscale, then adaptive threshold
Fill rectangular contours. Find contours and fill the contours to create filled rectangular blocks.
Perform morph open. We create a rectangular structuring element and morph open to remove the lines
Draw rectangle. Find contours and draw bounding rectangles.
Here's each step visualized:
Using this screenshotted image (contains more border since the provided image has the rectangles too close to the border). You could add a border to the input image instead of screenshotting for more border area. Take a look at add border to image
Binary image
Filled rectangular contours
Morph open
Result
Code
import cv2
# Load iamge, grayscale, adaptive threshold
image = cv2.imread('1.png')
result = image.copy()
gray = cv2.cvtColor(image,cv2.COLOR_BGR2GRAY)
thresh = cv2.adaptiveThreshold(gray,255,cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY_INV,51,9)
# Fill rectangular contours
cnts = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cnts = cnts[0] if len(cnts) == 2 else cnts[1]
for c in cnts:
cv2.drawContours(thresh, [c], -1, (255,255,255), -1)
# Morph open
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (9,9))
opening = cv2.morphologyEx(thresh, cv2.MORPH_OPEN, kernel, iterations=4)
# Draw rectangles
cnts = cv2.findContours(opening, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cnts = cnts[0] if len(cnts) == 2 else cnts[1]
for c in cnts:
x,y,w,h = cv2.boundingRect(c)
cv2.rectangle(image, (x, y), (x + w, y + h), (36,255,12), 3)
cv2.imshow('thresh', thresh)
cv2.imshow('opening', opening)
cv2.imshow('image', image)
cv2.waitKey()
Note: Depending on the image, you may have to modify the kernel size. For instance, it may be necessary to increase the kernel from (5, 5)
to say (11, 11)
. In addition, you could increase or decrease the number of iterations when performing cv2.morphologyEx()
. There is a trade-off when increasing or decreasing the kernel size as you may remove more or less of the lines. Again, it all varies depending on the input image.