How can I identify objects inside the image using python opencv?

Question:

I’m trying to identify objects present inside the plane area as in below image for some automation

image1

for this I tried finding the contours on masked image obtained using thresholding the hsv range of object border colors which is yellowish then I did morphing operation to remove the small open lines and dilution operation to merge the area of object as shown in below code

    img = cv2.imread(img_f)
    img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
    imghsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
    lower_blue = np.array([0,255,206])
    upper_blue = np.array([179,255,255])
    mask_blue = cv2.inRange(imghsv, lower_blue, upper_blue)
    kernel = np.ones((2, 2), np.uint8)
    img_erosion = cv2.erode(mask_blue, kernel, iterations=1)
    kernel = np.ones((3, 3), np.uint8)
    img_erosion = cv2.dilate(img_erosion, kernel, iterations=30)
    contours, _ = cv2.findContours(img_erosion, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
    im = np.copy(img)
    cv2.drawContours(im, contours, -1, (0,255,0), 3)

contour obtained is as shown in below image
image2
and mask image is as below
image3

with this approach I’m getting many unwanted detection and failing to detect many objects.

How can I able to achieve this? any suggestion or guidance will be highly appreciated ,thanks

Asked By: appguy

||

Answers:

Not perfect but here is another possible method:

import cv2
from matplotlib import pyplot as plt
import matplotlib
import numpy as np

matplotlib.use('TkAgg')


def remove_noise(binary_image, max_noise_size=20):
    labels_count, labeled_image, stats, centroids = cv2.connectedComponentsWithStats(
        binary_image, 8, cv2.CV_32S)
    new_image = np.zeros_like(binary_image)
    for i in range(1, labels_count):
        if stats[i, -1] <= max_noise_size:
            continue
        new_image[labeled_image == i] = 255
    return new_image


def main():
    original_image = cv2.imread('BWQyx.png')

    image = np.array(original_image)
    for y in range(image.shape[0]):
        for x in range(image.shape[1]):
            # remove grey background
            if 150 <= image[y, x, 0] <= 180 and 
                    150 <= image[y, x, 1] <= 180 and 
                    150 <= image[y, x, 2] <= 180:
                image[y, x, 0] = 0
                image[y, x, 1] = 0
                image[y, x, 2] = 0

            # remove green dashes
            if image[y, x, 0] == 0 and 
                    image[y, x, 1] == 169 and 
                    image[y, x, 2] == 0:
                image[y, x, 0] = 0
                image[y, x, 1] = 0
                image[y, x, 2] = 0

    image = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
    image[image < 128] = 0
    image[image > 128] = 255

    image = cv2.dilate(image, np.ones((3, 3), np.uint8), iterations=2)
    image = cv2.erode(image, np.ones((3, 3), np.uint8), iterations=2)

    contours, _ = cv2.findContours(
        image, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
    cv2.drawContours(image, contours, -1, (0,), 2)

    image = remove_noise(image)

    contours, _ = cv2.findContours(
        image, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)

    for contour in contours:
        min_area_rect = cv2.minAreaRect(contour)
        width, height = min_area_rect[1]
        if width * height > 20000 or width * height < 400:
            continue
        box = np.int0(cv2.boxPoints(min_area_rect))
        cv2.drawContours(original_image, [box], 0, (0, 0, 255), 2)

    plt.imshow(original_image)
    plt.show()


if __name__ == '__main__':
    main()

The main idea is to remove all the grid lines by drawing over their contours. You can tune it and achieve better results. However, I feel that it is really difficult to get everything 100% right. You will need more heuristics to remove unwanted rectangles.

Answered By: Talal Alrawajfeh