Khaled
Khaled

Reputation: 563

How to filter contours based on their approximate shape in a Binary video frames

I'm working on a project where i have to detect a red vehicle (please see image below).

Main image

As i believe that this can be achieved with out using Deep learning (overkill in this case), i used histogram Back projection depending on the object color(red). The results were satisfying

result

except when there are objects other than the target red-vehicle having the same color distribution as the target (see example below my T-shirt) are in the scene, the algorithm thinks it is also an object of interest and thus detect both the object of interest and the irrelevant object (my T-shirt).

second case

The result are

enter image description here

In this case, it's easy to only choose the contour that belongs to the car based on ratio and area,since the contour that belongs to the T-shirt is lager and has different ratio

I applied the follwoing example code

contours = cv2.findContours(clean, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)


area_thresh1 = 500
area_thresh2 = 1000
aspect_thresh1 = 2
aspect_thresh2 = 4
result1 = image.copy()
result2 = image.copy()
for c in contours:

    # get rotated rectangle from contour
    # get its dimensions
    # get angle relative to horizontal from rotated rectangle
    rotrect = cv2.minAreaRect(c)
    box = cv2.boxPoints(rotrect)
    box = np.int0(box)
    (center), (dim1,dim2), angle = rotrect
    maxdim = max(dim1,dim2)
    mindim = min(dim1,dim2)
    area = dim1 * dim2
    if area > 0:
        aspect = maxdim / mindim
        #print(area, aspect)

    if area > area_thresh1 and area < area_thresh2 and aspect > aspect_thresh1 and aspect < 
       aspect_thresh2:
       # draw contour on input
       cv2.drawContours(result1,[c],0,(255,255,255),1)
       # draw rectangle on input
       cv2.drawContours(result2,[box],0,(255,255,255),1)
       print(area, aspect)

However, as I'm working on a video, this doesn't work well in some frames since sometimes it detects shapes that fulfill the conditions like the case below result2

As you can see in the above binary image, an irrelevant object is detected (the below contour).

So my question is:

As you see the red vehicle to be detected always has the same shape (almost rectangle but for sure a convex shape). So how can i filter only the contour that belongs to the red vehicle using a the shape property ?(of course I mean a property other than ratio and area since some sopts of my short falls into the same area and ration boundaries of the red vehicle).

In other words, How can i filter the target object based on the exact shape of the vehicle??

Thanks in Advance

Upvotes: 0

Views: 3173

Answers (2)

Vallabh Karanjkar
Vallabh Karanjkar

Reputation: 19

You can use approxPolyDp and calculate the approximate area of the shape and set a threshold for a range where it can be termed as the shape that you're looking for.

Upvotes: 0

Ziri
Ziri

Reputation: 736

You can get shape descriptors and use some kind of rules (or machine learning) to decide if that's the right object you're searching for :

import numpy as np
import argparse
import cv2
import sys

target = cv2.imread('YourPath\\target.jpg' , 
cv2.IMREAD_COLOR)
mask = cv2.imread('YourPath\\mask.jpg',cv2.IMREAD_GRAYSCALE)
SearchImage = cv2.bitwise_and(target,target,mask = mask)

cv2.imshow("Search Region" , SearchImage)
cv2.waitKey()

#convert RGBto Lab
LabImage = cv2.cvtColor(SearchImage,cv2.COLOR_BGR2LAB)

cv2.imshow("Lab(b)" , LabImage[:, :, 1])
cv2.waitKey()

ret,Binary = cv2.threshold(LabImage[:, :, 1], 0, 255, cv2.THRESH_OTSU)
cv2.imshow('win1', Binary)
cv2.waitKey(0)

 #find contours
 contours, hierarchy = cv2.findContours(Binary, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)

#create an empty image for contours
img_contours = np.zeros(target.shape)
# draw the contours on the empty image
cv2.drawContours(img_contours, contours, -1, (0,255,0), 3)

for cnt in contours:

   x, y, w, h = cv2.boundingRect(cnt)
   aspect_ratio = float(w) / h

   area = cv2.contourArea(cnt)
   x, y, w, h = cv2.boundingRect(cnt)
   rect_area = w * h
   extent = float(area) / rect_area

   hull = cv2.convexHull(cnt)
   hull_area = cv2.contourArea(hull)
   solidity = float(area) / hull_area

   equi_diameter = np.sqrt(4 * area / np.pi)

   (x, y), (MA, ma), Orientation = cv2.fitEllipse(cnt)

   print(" Width = {}  Height = {} area = {}  aspect ration = {}  extent  = {}  
   solidity = {}   equi_diameter = {}   orientation = {}".format(  w , h , area , 
   aspect_ratio , extent , solidity , equi_diameter , Orientation))



cv2.imshow('win1', img_contours)
cv2.waitKey(0)

OUTPUT:

Width = 42  
Height = 18 
area = 632.5  
aspect ratio = 2.3333333333333335  
extent  = 0.8366402116402116  
solidity = 0.9412202380952381   
equi_diameter = 28.37823130579125   
orientation = 89.93299865722656

enter image description here

Upvotes: 3

Related Questions