This article was published as a part of the Data Science Blogathon.
In this article, we will be working on object motion detection (specifical vehicles) by implying the techniques of contours detection and background subtraction. In this, we will be using a video of vehicles for car detection. It’s going to be a real-time experience.
So let’s build this motion detection application.
We will start by importing all the necessary libraries.
import cv2 import numpy as np import matplotlib.pyplot as plt %matplotlib inline
Source: SpringerLink
Background subtraction
is one of the most effective and easiest methods to detect and extract objects from images or videos. But why other masking methods are not that effective? Let’s answer this question!
So let’s assume you have one picture of the highway with the cars, i.e., the foreground image, and on the other hand, you have the picture of the highway without the cars, i.e., this image can be used as the background image. Hence from this approach, we can easily use the segmentation technique to segment the background image from the foreground image (differentiate cars) and then use the mask to extract the cars.
So if we can use the above method then why is Background subtraction is needed?
This technique comes into play when you don’t have a clear background image, practically speaking, it’s hard to take a clear picture of the highway with the moving cars right! So in those cases, background subtraction techniques can also detect the real-time moment and not only in the images. The most important feature of this algorithm is that it is faster and has better adaptability, and it is way more efficient than the above-mentioned traditional technique.
Note: Though OpenCV has ample methods to solve the same problem here we will be discussing the Background Subtractor MOG2.
First, we will look for the syntax of this function.
background_subtractor = cv2.createBackgroundSubtractorMOG2(history, varThreshold, detectShadows)
Now, let’s discuss what kind of parameters this algorithm holds:
history
– It’s an int-type of argument, i.e., we specify a specific length to history. The default value is 500.varThreshold
– This parameter is also optional, and when I say optional, it means that it will take a default value only when it won’t be able to find a user-given value. As the name suggests, it holds the threshold value which is the squared distance between the pixel and the algorithm to decide whether the pixel is well defined by the model or not. The default value is 16.detectShadows
–This parameter holds the boolean-type of value (True/False), and it decides whether the algorithm will detect and mark the shadow or not in the output. It usually marks the shadow in gray color. The default value is True, but there is also a drawback of this feature as when it is mapped to True, it hampers the speed, so for that reason, if you don’t want this functionality, then you can set it to False.Returns:
object
– It will return the output in the format of the MOG2 Background Subtractor.
cap = cv2.VideoCapture('media/videos/vtest.avi') backgroundobject = cv2.createBackgroundSubtractorMOG2( history = 2, detectShadows = True ) while(1): ret, frame = cap.read() if not ret: break fgmask = backgroundobject.apply(frame) real_part = cv2.bitwise_and(frame,frame,mask=fgmask) fgmask_3 = cv2.cvtColor(fgmask, cv2.COLOR_GRAY2BGR) stacked = np.hstack((fgmask_3,frame,real_part)) cv2.imshow('All three',cv2.resize(stacked,None,fx=0.65,fy=0.65)) k = cv2.waitKey(30) & 0xff if k == 27: break cap.release() cv2.destroyAllWindows()
Output:
Code breakdown
Now we will be building our vehicle detection application using a background subtractor, for that, we need to follow a few steps. So first, we will see the code, and then its explanation.
video = cv2.VideoCapture('media/videos/carsvid.wmv') kernel = None backgroundObject = cv2.createBackgroundSubtractorMOG2(detectShadows = True) while True: ret, frame = video.read() if not ret: break foreground_mask = backgroundObject.apply(frame) _, foreground_mask = cv2.threshold(foreground_mask, 250, 255, cv2.THRESH_BINARY) foreground_mask = cv2.erode(foreground_mask, kernel, iterations = 1) foreground_mask = cv2.dilate(foreground_mask, kernel, iterations = 2) contours, _ = cv2.findContours(foreground_mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) frameCopy = frame.copy() # loop over each contour found in the frame. for cnt in contours: # We need to be sure about the area of the contours i.e. it should be higher than 400 to reduce the noise. if cv2.contourArea(cnt) > 400: # Accessing the x, y and height, width of the cars x, y, width, height = cv2.boundingRect(cnt) # Here we will be drawing the bounding box on the cars cv2.rectangle(frameCopy, (x , y), (x + width, y + height),(0, 0, 255), 2) # Then with the help of putText method we will write the 'Car detected' on every car with a bounding box cv2.putText(frameCopy, 'Car Detected', (x, y-10), cv2.FONT_HERSHEY_SIMPLEX, 0.3, (0,255,0), 1, cv2.LINE_AA) foregroundPart = cv2.bitwise_and(frame, frame, mask=foreground_mask) stacked_frame = np.hstack((frame, foregroundPart, frameCopy)) cv2.imshow('Original Frame, Extracted Foreground and Detected Cars', cv2.resize(stacked_frame, None, fx=0.5, fy=0.5)) k = cv2.waitKey(1) & 0xff if k == ord('q'): break video.release() cv2.destroyAllWindows()
Output:
Code breakdown:
None
. We can give our custom size but to remove the complexity of the model we will be giving it No value.background subtractor
model and then storing the instance of it in a variable.the backgroundObject
function, we will apply it to every frame to get the segmented mask.cv2.thereshold
) technique so that we could get rid of unwanted shadows.morphological operations
so that we could have a better mask (more efficient and accurate), for that, we will be using two cv2 functions:
cv2.erode()
: to improve the erosion of frame.cv2.dilate()
: to improve the dilation of frame.the cv2.bitwise
function.np.hstack
function to stack all the three frames together. And at last, we will display the stacked format with the help of the cv2.imshow
function.While we will look into the documentation of OpenCV, we could find ample algorithms for object detection or motion detection. You can try different algorithms and look at what fits best according to your requirements. Here, I used the background subtraction
because it gave me better results as well as it seems to be more reliable and easy to use.
Key takeaways from this article
Here’s the repo link to this article. Hope you liked my article on Vehicle motion detection using subtraction. If you have any opinions or questions, then comment below.
Read on AV Blog about various predictions using Machine Learning.
Greeting to everyone, I’m currently working in TCS and previously, I worked as a Data Science Analyst in Zorba Consulting India. Along with full-time work, I’ve got an immense interest in the same field, i.e., Data Science, along with its other subsets (Artificial Intelligence such as Computer Vision, Machine Learning, and Deep Learning). Feel free to collaborate with me on any project on the domains mentioned above (LinkedIn).
The media shown in this article is not owned by Analytics Vidhya and are used at the Author’s discretion.