Original Source Here
Counting Objects In Python
Serial problem solving is what we do right as programmers, “solve it one problem at a time”. Object tracking solves multiple problems such as keeping track of cars passing by the highway, objects on a conveyer belt the list goes on. In this article we are going to go through how its done.
Setting Up Our Environment
In my previous article I discussed setting up virtual environments in Conda, and for this particular project I wanted to set up my development environment using my command prompt .
lets fire up Windows command prompt terminal. And create a folder track_it.
We then need to cd into our track it folder;
Now we need to create our virtual environment inside our track_it folder; The command is python then m for the module, venv is the specific module followed by the name of our environment, ours is simply tcount. The name tcount is short for traffic count and I feel like working on Python 3.8, hence we have specified our Python version.
python -m venv tcount python=3.8
Lets check has the environment been created inside of our track_it folder;
Now we need to activate it, it is more complex than just ‘conda activate and then the name of the environment. With this method we activate as follows;
We can go ahead and run our Jupyter Notebook with the command
This will open Jupyter Notebook and we can see our virtual environment tcount inside there, but we are still using the global Python kernel. Starting Jupyter Notebook and Jupyter Lab inside of an in environment is quite futile as we must still associate our environment with Jupyter.
So we need to stop our Jupyter Notebook in the terminal and install ipykernel.
pip install ipykernel
Ipykernel allows us to attach a virtual environment to the jupyter notebook or lab, however you prefer it.
Now we want to use ipykernel and we will do this as follows;
python -m ipykernel install --name = tcount
We see that they are now linked. Lets also check all the different kernels we have running inside our Jupyter.
jupyter kernelspec list
Lets run jupyter notebook and select our new environment which is tcount.
After selecting the tcount environment we will see it as the selected virtual environment.
We are doing two things for this task namely object detection followed by object tracking.
Object detection is detecting objects on individual frames, what do I mean by this.
Lets say we have a video or image the detection algorithm check everything frame by frame. We further extend this with object tracking that keeps a counter and furthermore labels the objects counted.
We will start by installing open CV which is a Python framework used for computer vision projects. The command is as follows;
!pip install opencv-python
Then we need to import open cv with the command;
Now we need to create a capture object to read from our video. I will be using a video supplied by Sergio Canu.
cap = cv2.VideoCapture('highway.mp4)
Now we need to loop through the frames one after the other, we will do this using a while loop.
ret, frame = cap.read()
Now we need to show our video frames in real-time;
Finally we need an escape to the loop, we will use an await key event
key = cv2.waitKey(30)
if key == 27:
So we want to show the frame in realtime, open CV will loop continuously and we cannot close the window by simply pressing x. We need to press the key which is 27.
Now lets run our code;
Now our goal is to detect cars and we will be using object detection method for a stable camera. Stable camera means background is always the same only the objects change over time.
object_detector = cv2.createBackgroundSubtractorMOG2()
Our variable object_detector will extract the moving objects from a stable camera.
We will extract these creating a mask where we will apply the detection on the frame;
mask = object_detector.apply(frame)
Then we can apply the mask and run our code;
The goal of a mask is to make everything black and exempt the object we want to detect and make them white.
All the vehicles and motorbikes must be white and everything else needs to be black.
Now we want to get the coordinates of all the white elements and remove the small elements we don’t need such as traffic signs, the speed meter on the bike and some of the trees.
So from the mask we need to extract the co-ordinates of the objects. We will do that cv2.findContours() function. We want to detect the boundaries of the white objects on the mask and we will also extract the information using the cv2.RETR_TREE and cv2.CHAIN_APPROX_SIMPLE function.
cv2.findContours(mask, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
Will then assign this to contours and _(the _ symbol) represents the hierarchy).
contours, _ = cv2.findContours(mask, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE
Now we want to loop through the contours and draw them on the frame we will use a blue color and our thickness is 2;
for cnt in contours:
Lets run our code;
As you can see everything that was white is surrounded with blue, but we have a lot of things we do not need that have been drawn, we only want the cars and motor bikes.
We want to remove all of the small elements by first calculating the area then remove these elements.
area = cv2.contourArea(cnt)
Then we add a condition to the area statement, saying if our area is greater than 100px then we draw our contours. Remember the cars and bikes have larger pixels.
if area >100:
We still have a lot of things we do not need and to just simplify things we will select a region of interest and focus on it and the vehicles on the road.
To get the roi we need to say from which region of the image do we want to extract. So to check which position is to show the entire frame we have, we check the height, width and some channels
height,width, _ = frame.shape
Our height is 720 and width is 1280, of course it has printed it multiple times due to the while loop.
So if we want the region which is the middle of the screen then our x position will be around 400 and we can make our y 600.
roi = frame[340,600,400:600]
Now we want to show the roi;
Very small we can tweak it a bit play around with our numbers our height starts from 500 to 720 and width from 500 to 800.
Now we can get rid of our print statement(indicated as a comment) as we have already determined our width and height.
Now we want to apply our mask to the specific area, the roi area, we will replace frame with roi.
First we will replace frame and roi with for the mask;
Then replace it on the contours;
Now lets run our code;
Well the detection is still not 100% it can still be improved by applying history as this increases our precision.
As we have a stable camera and one that does not move we can assign our history to 100 and our varThreshold to 40. History is how many frames are used to build the background model, in our case a 100 frames. Both the history and threshold are done to reduce false positives.
Now we can construct a rectangle to formalize our detection.
x,y,w,h = cv2.boundingRect(cnt)
cv2.rectangle(roi, (x,y), (x + w,y + h), (255,0,0),3)
One last thing we need to clean up is the grey shadows by cleaning up the mask;
We will use the threshold function to remove the grey pixels.
_,mask = cv2.threshold(mask,254,255,cv2.THRESH_BINARY)
Inside the threshold function we pass mask,254,255,cv2.THRESH_BINARY. Why these values, well pixels range from 0 to 255. The value 255 is completely white and below that we have different variants of grey. Closer to 0 is darker grey shades and closer to 255 is lighter grey shades.
Lets run our code;
As we can see the detection has improved drastically by just removing the grey pixels.
We are done with the object detection part and now we want to do some object tracking.
For this section we are going to import the tracker.py file but before we import it, we want to drag and drop it into our track_it directory.
Now we can import everything from tracker;
Then we want to create a tracker object, which will take the bounding boxes of the object and we need to save these into one array.
Now we will save all those bounding boxes into one array, first lets print out our bounding box values.
As a car or motorbike passes it is attached to a bounding box which is attached to a set of values.
So we want these values in one array, we will create a variable detections which is assigned to an empty array,;
detections = 
So each time our model finds boxes it will put them inside an empty array and we want to append the x, y, width and height and print detections.
As there are no cars an bikes passing by our array is empty, when cars start passing by the values are captured into the array.
We need to give the detections array to the tracker, which will in turn give us the history that has a unique id that is associated to each.
boxes_ids = tracker.update(detections)
We have a variable boxes_ids and assign it to the function tracker.update and pass in detections as our argument.
Next we want to loop through box_ids;
for box_id in boxes_ids
And what are we looking for while looping x,y, width, height, id and we assign those to box_id;
x,y,w,h,id = box_id
Then we want to put text to the boxes as they pass. We have our function cv2.putText and we only want to put text where there is the roi, str(id), the x value and the y value(we want to distance the objects a bit as they pass, need to avoid cluster. Then we apply the font and color
cv2.putText(roi, str(id), (x,y -15),cv2.FONT_HERSHEY_PLAIN,1,(0,255,0),2)
Lets run our code;
If this post was helpful, please click the clap 👏 button below a few times to show your support for the author 👇
Trending AI/ML Article Identified & Digested via Granola by Ramsey Elbasheer; a Machine-Driven RSS Bot