'How to detect and extract rectangles with Python OpenCV?

I am trying to detect three rectangles from the contour. I have already extracted the entire contour from the whole image. I am attaching the image of extracted contour below. I would like to figure out a way to further extract three rectangular kind panels from the whole contour.

Extracted Contour Image:

enter image description here



Solution 1:[1]

Here's a simple approach:

  • Convert image to grayscale
  • Threshold to obtain binary image
  • Perform morphological operations to smooth image
  • Find contours and extract ROI

After converting to grayscale, we threshold to obtain a binary image

image = cv2.imread('1.png')
original = image.copy()

gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
thresh = cv2.threshold(gray, 120, 255, cv2.THRESH_BINARY)[1]

Next we create a kernel and perform morphological operations to smooth the image. This step "breaks" the joints connecting the three rectangles by eroding the image

kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (25,25))
opening = cv2.morphologyEx(thresh, cv2.MORPH_OPEN, kernel, iterations=3)

From here we find contours and extract the ROI with numpy slicing. The bounding boxes for the desired rectangles are drawn on the original image

cnts = cv2.findContours(opening, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cnts = cnts[0] if len(cnts) == 2 else cnts[1]

image_number = 0
for c in cnts:
    x,y,w,h = cv2.boundingRect(c)
    cv2.rectangle(image, (x, y), (x + w, y + h), (36,255,12), 3)
    ROI = original[y:y+h, x:x+w]
    cv2.imwrite("ROI_{}.png".format(image_number), ROI)
    image_number += 1

Here's each individual saved ROI

Full code

import cv2

image = cv2.imread('1.png')
original = image.copy()

gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
thresh = cv2.threshold(gray, 120, 255, cv2.THRESH_BINARY)[1]
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (25,25))
opening = cv2.morphologyEx(thresh, cv2.MORPH_OPEN, kernel, iterations=3)

cnts = cv2.findContours(opening, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cnts = cnts[0] if len(cnts) == 2 else cnts[1]

image_number = 0
for c in cnts:
    x,y,w,h = cv2.boundingRect(c)
    cv2.rectangle(image, (x, y), (x + w, y + h), (36,255,12), 3)
    ROI = original[y:y+h, x:x+w]
    cv2.imwrite("ROI_{}.png".format(image_number), ROI)
    image_number += 1

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

Solution 2:[2]

This looks like a case for opening with a kernel sufficiently large to erode the lines in between your rectangles.

Another way would be to do the erode multiple times and then call dilate the same number of times.

A little suggestion, you might want to apply a thresh-hold on your image, because it looks like you have some noise (especially on the left and top rectangles).

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 nathancy
Solution 2 Zlatomir