Home » Python » python – Separate objects countours with OpenCV-Exceptionshub

python – Separate objects countours with OpenCV-Exceptionshub

Posted by: admin February 24, 2020 Leave a comment


I have been working with OpenCV in order to detect an squared obstacle. So far this is the image I get after applying filters and canny.Canny Result

The obstacle I am trying to identify is the horizontal one, the three vertical rectangles are guide lines on the floor.My goal is to keep only the horizontal rectangle, separating it from the others, but after applying find Contours I only get I single object that includes all the shapes.This is the code I have been using in order to fin only the biggest rectangle by their area:

# find the biggest countour (c) by the area
    if contours != 0:
        if not contours:

            bigone = max(contours, key=cv2.contourArea) if max else None
            area = cv2.contourArea(bigone)
            if area > 10000:
                x, y, w, h = cv2.boundingRect(bigone)
                cv2.rectangle(img, (x, y), (x+w, y+h), (0, 0, 255), 2)
                cv2.putText(img, "Obstacle", (x+w/2, y-20),
                            cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255, 255, 255), 2)

                pts = np.array(
                    [[[x, y], [x+w, y], [x+w, y+h], [x, y+h]]], dtype=np.int32)
                cv2.fillPoly(mask, pts, (255, 255, 255))
                #values = img[np.where((mask == (255, 255, 255)).all(axis=2))]
                res = cv2.bitwise_and(img, mask)  # View only the obstacle
                obs_area = w*h
                if obs_area <= 168000:

                        img, "GO", (380, 400), cv2.FONT_HERSHEY_SIMPLEX, 2, (0, 255, 255), 1)

                    cv2.putText(img, "STOP", (380, 400),
                                cv2.FONT_HERSHEY_SIMPLEX, 2, (0, 255, 255), 1)

                # show the output image
                cv2.imshow("Image", img)

And this is the result I am getting:


Is there a way of separating my obstacle from the lines on the floor with some kind of filter or algorithm?

Here is an example image to work with:

Test Image

How to&Answers:

Here is one way to do that using Python/OpenCV.

 - Read the input
 - Convert to HSV and extract only the saturation channel (black/white/gray have zero saturation)
 - Threshold
 - Apply morphology open and close to remove the extranous white regions
 - Get the contour and approximate to simple polygon
 - Draw the polygon on the input
 - Save the results


enter image description here

import cv2
import numpy as np

# read image
img = cv2.imread('board.png')

# convert to HSV and extract saturation channel
sat = cv2.cvtColor(img, cv2.COLOR_RGB2HSV)[:,:,1]

# threshold
thresh = cv2.threshold(sat, 90, 255, 0)[1]

# apply morphology close to fill interior regions in mask
kernel = np.ones((7,7), np.uint8)
morph = cv2.morphologyEx(thresh, cv2.MORPH_OPEN, kernel)
kernel = np.ones((13,13), np.uint8)
morph = cv2.morphologyEx(morph, cv2.MORPH_CLOSE, kernel)

# get contours (presumably only 1) and fit to simple polygon (quadrilateral)
cntrs = cv2.findContours(morph, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cntrs = cntrs[0] if len(cntrs) == 2 else cntrs[1]
c = cntrs[0]
peri = cv2.arcLength(c, True)
approx = cv2.approxPolyDP(c, 0.05 * peri, True)

# draw polygon on input
result = img.copy()
cv2.polylines(result, [np.int32(approx)], True, (0,0,255), 1, cv2.LINE_AA)

# write result to disk
cv2.imwrite("board_saturation.png", sat)
cv2.imwrite("board_thresh.png", thresh)
cv2.imwrite("board_morph.png", morph)
cv2.imwrite("board_contour.png", result)

# display it
cv2.imshow("IMAGE", img)
cv2.imshow("SAT", sat)
cv2.imshow("THRESH", thresh)
cv2.imshow("MORPH", morph)
cv2.imshow("RESULT", result)

Saturation channel image:

enter image description here

Thresholded image:

enter image description here

Morphology cleaned image:

enter image description here

Contour on input:

enter image description here


In your image the problem seems white rectangles. My approach is checking each line and if line consist many pixels which are close to white(255,255,255) then make the line black.

Here is my code:

import cv2
import numpy as np
import random as rng

height, width, channels = img.shape

# Check each line and eliminate white rectangles(if line consist white pixels more than limit)
for x in range(0,height):
    white_counter = 0
    for y in range(0,width):
        if  img[x,y,0] >= 180 and img[x,y,1] >= 180 and img[x,y,2] >= 180: 
            white_counter = white_counter + 1

    if white_counter>10:
        for y in range(0,width):
            img[x,y,0] = 0
            img[x,y,1] = 0
            img[x,y,2] = 0

cv2.imshow('Elimination White Rectangles', img)

# Find contours and draw rectangle for each

src_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
threshold = 300
canny_output = cv2.Canny(src_gray, threshold, threshold * 2)
contours, _ = cv2.findContours(canny_output, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)

contours_poly = [None]*len(contours)
boundRect = [None]*len(contours)
for i, c in enumerate(contours):
        contours_poly[i] = cv2.approxPolyDP(c, 3, True)
        boundRect[i] = cv2.boundingRect(contours_poly[i])

drawing = np.zeros((canny_output.shape[0], canny_output.shape[1], 3), dtype=np.uint8)

for i in range(len(contours)):
        color = (rng.randint(0,256), rng.randint(0,256), rng.randint(0,256))
        cv2.rectangle(drawing, (int(boundRect[i][0]), int(boundRect[i][1])), \
          (int(boundRect[i][0]+boundRect[i][2]), int(boundRect[i][1]+boundRect[i][3])), color, 2)

cv2.imshow('Output', drawing)


Eliminate White Rectangles:

enter image description here


enter image description here