How to find rectangles in a full transparent object?

Viewed 92

I have an input image of a fully transparent object:

enter image description here

I need to detect the 42 rectangles in this image. This is an example of the output image I need (I marked 6 rectangles for better understanding):

enter image description here

The problem is that the rectangles look really different. I have to use this input image. How can I achieve this?

Edit 1: Here is a input image as png:

enter image description here

2 Answers

If you calculate the variance down the rows and across the columns, using:

import cv2
import numpy as np

im = cv2.imread('YOURIMAGE', cv2.IMREAD_GRAYSCALE)

# Calculate horizontal and vertical variance
h = np.var(im, axis=1)
v = np.var(im, axis=0)

You can plot them and hopefully locate the peaks of variance which should be your objects:

enter image description here

enter image description here

Mark Setchell's idea is out-of-the-box. Here is a more traditional approach.

Approach:

The image contains boxes whose intensity fades away in the lower rows. Using global equalization would fail here since the intensity changes of the entire image is taken into account. I opted for a local equalization approach in OpenCV this is available as CLAHE (Contrast Limited Adaptive Histogram Equalization))

Using CLAHE:

  • Equalization is applied on individual regions of the image whose size can be predefined.
  • To avoid over amplification, contrast limiting is applied, (hence the name).

Let's see how to use it in our problem:

Code:

# read image and store green channel
green_channel = img[:,:,1]

# grid-size for CLAHE
ts = 8
# initialize CLAHE function with parameters
clahe = cv2.createCLAHE(clipLimit=2.0, tileGridSize=(ts, ts))
# apply the function
cl = clahe.apply(green_channel)

enter image description here

Notice the image above, the boxes in the lower regions appear slightly darker as expected. This will help us later on.

# apply Otsu threshold
r,th_cl = cv2.threshold(cl, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)

enter image description here

# dilation performed using vertical kernels to connect disjoined boxes
vertical_kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (1, 3))
dilate = cv2.dilate(th_cl, vertical_kernel, iterations=1)
 

enter image description here

# find contours and draw bounding boxes
contours, hierarchy = cv2.findContours(dilate, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)
img2 = img.copy()
for c in contours:
    area = cv2.contourArea(c)
    if area > 100:
        x, y, w, h = cv2.boundingRect(c)
        img2 = cv2.rectangle(img2, (x, y), (x + w, y + h), (0,255,255), 1)

enter image description here

(The top-rightmost box isn't covered properly. You would need to tweak the various parameters to get an accurate result)

Other pre-processing approaches you can try:

  1. Global equalization
  2. Contrast stretching
  3. Normalization
Related