Counting Objects in OpenCV.

Victor Olufemi
5 min readFeb 24, 2022

--

written on: August 2020

Link to code on Github: https://github.com/osinkolu/Tutorial-3-Outro-to-OpenCV

Well done traveler, welcome back to the bus ride. This episode involves counting simple objects in a picture. We have an image containing some shapes, and in this lesson, we would identify, locate and count the number of shapes in the image. During this process, we would 1) Learn how to convert images to grayscale 2) detect Edges 3) Thresholding the grayscale image 4) Find, count, and draw contours 5) perform erosion, and dilation 6) mask an image.

Sit tight, put on your seat belt, the road is quite bumpy!!.

Image credit: Image by 200 Degrees from Pixabay

Pipeline : load image >> gray scale >> Threshold >> find contours >> grab contours >> draw contours

Import the required packages.

# First we get the necessary packages.
import cv2
import imutils

Load in the image.

we load in the image, the name of the image is “shapes.png”, then we display it.

# load the input image via opencv's imread
image = cv2.imread("shapes.png")
cv2.imshow("Shapes_image", image)
cv2.waitKey(0)

Grayscale The Image.

Here, we grayscale the image, to do that is quite simple, we use cv2.cvtcolor() to convert from one color scale to another. In this case, we use it to convert to a grayscale type.

# convert  the image to a grayscale type.
gray_scaled = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
cv2.imshow("Gray-scale-rep", gray_scaled)
cv2.waitKey(0)

Edge Detection.

Edge detection is very useful for finding the boundaries of an object in any image. The Canny algorithm is very much suitable for this, we’ll use the cv2.canny(). the input image is the gray_scale image, then the minimum and maximum threshold. Different values for minimum and maximum thresholds will return different edge maps.

#Applying Edge detection to find outlines of objects.
edged = cv2.Canny(gray_scaled, 30,150)
cv2.imshow("Edge Detection", edged)
cv2.waitKey(0)

Thresholding The Image.

Thresholding an image assists greatly in image processing, it removes lighter or darker regions and contours of the gray-scaled images. This helps greatly to segment the image.

#Thresholding the image.
# i set all pixels greater than 225 to 225
# i also set pixels less than 225 to 225
thresh = cv2.threshold(gray_scaled, 225,225, cv2.THRESH_BINARY_INV)[1]
cv2.imshow("Thresholding Applied", thresh)
cv2.waitKey(0)

Drawing Contours on an image.

Having well distinguished the image foreground and background, we can now proceed to find the Contours in the image. This proves to us how important thresholding an image is. To find the contours, we use the cv2.findcontours() to detect the contours, where the input image is a copy of the threshold image. After detection, we use the imutils library to grab the contours.

#detecting and drawing contours.
# first we find the contours in the thresholded image.
contours = cv2.findContours(thresh.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
contours = imutils.grab_contours(contours)

now, we have grabbed the contours, we the loop over each contour found, and then draw it on the image. it's just like tracing. The cv2 library has a draw contours function which takes in the image, takes in the contours as grabbed by imutils, the negative value as used there forces it to draw all the contours, it takes in the color as well, here I used purple, Then of course and lastly is the thickness.

output = image.copy()
for contour in contours:# loop over each contour found
cv2.drawContours(output, [contour], -1,(240,0,159),3) # outline and display them, one by one.
cv2.imshow("Contours", output)
cv2.waitKey(0)

Count and Write.

Here, we count the number of contours found, which of course corresponds to the number of images. so the idea here is to count the images and display the number of counts as text. Firstly we create the text to be displayed, Secondly, we put the text on the image…. A,B,C right?…

# count the number of objects found in the image
# write count as text too.
text = "There are {} objects here.".format(len(contours))
cv2.putText(output, text, (10,25), cv2.FONT_HERSHEY_SIMPLEX, 0.7,(240, 0, 159), 2)
cv2.imshow("Description", output)
cv2.waitKey(0)

Erosions and Dilations. (parking in the bus park).

Erosions

Thresholding an image has some side effects of the addition of noise. Erosions are used to further reduce noise in the thresholded image, to reduce the size of foregrounds in an image, we erode the image with some number of iterations. This is easily done using the cv2.erode()

# reduce noise futher in image using erosion.
mask = thresh.copy()
mask = cv2.erode(mask, None, iterations =5)
cv2.imshow("Eroded", mask)
cv2.waitKey(0)
cv2.imwrite("output10.jpg", mask)

Dilations.

very similarly, we can foreground images in the mask using the cv2.dilate().

# reduce noise futher using Dilation.
mask = thresh.copy()
mask = cv2.dilate(mask, None, iterations = 5)
cv2.imshow("Dilated", mask)
cv2.waitKey(0)

Masking and Bitwise Operations.

As the name implies, Masks allow us to hide regions of an image that we are uninterested in Since we do not care about them.

#Masking and Bitwise operations.
mask = thresh.copy()
output = cv2.bitwise_and(image, image, mask = mask)
cv2.imshow("FINAL OUTPUT", output)
cv2.waitKey(0)
cv2.imwrite("output12.jpg", output)

Bus Stop Reached

You’ve arrived at your destination. Welcome to OpenCV kings island…..You are now a pro-amateur….😎

kindly step down from the vehicle and explore.

I am always open to assisting enthusiasts with difficulties they are facing in Machine learning and Deep learning. Feel free to reach out to me: most preferably LinkedIn.

. Twitter.

. Linkedin.

. Github

Author: Olufemi Victor Tolulope

--

--