Object detection in OpenCV



OpenCV is a project that was started back in 1999 by Intel. The primary purpose? to achieve computer vision. In this article we will be taking a look at OpenCV-python, the python API for the OpenCV project.

This python library allows us to detect objects and shapes in both photos and images, as well as in a live camera feed. Before we dive into the workings of the OpenCV library, let us understand what an image is in the world of computers.
 
An image in computer vision is basically a binary matrix. A matrix that contains a series of meaningfully placed 1s and 0s. In the following image, see if you can discern the shape of the English alphabet 'A' by connecting the series of 1s in the matrix.

                                          

Now that we know how outlines in an image are represented, lets take a look at how color is represented. Lets take a look at the RGB model which contains three primary colors that form the color space. This model contains 3 colors namely Red, Green and Blue, and hence the name RGB.

Each color is made up of 8 bits, that can have an integer value range from 0 to 255 each. Hence a total of 256 x 256 x 256 = 16777216 colors are possible using varying combinations of these colors.
File:Matl.jpg - Wikipedia

As can be seen above, any image can be represented in the form of an RBG matrix.

Now that we have a basic understanding of what an image is lets move on to exploring some of the basic functionalities that OpenCV provides us with. Before we begin, install the library using pip -

pip3 install opencv-python

As always, we'll explore the library as a series of tasks. Let us take the following sample image of a simple shapes to be detected.



Task 1 - Reading and Displaying an image

For this process we will be using two main functions -

cv2.imread() - To read the image
cv2.imshow() - To display the image


We'll also add a waitkey() function at the end so that the output of our code doesn't disappear as soon as the function execution ends. The value "0" indicates an infinite wait timer until the time a keyboard input is pressed.

import cv2

img = cv2.imread("C:\\Users\\aditya\\Desktop\\shapes.png");
cv2.imshow("Apple",img)

cv2.waitKey(0)

The output for which would be -



Task 2 - Converting the image to a grey-scale

To convert the image to a grey-scale we use a function called cv2.cvtColor(). Converting the image to gray-scale means converting a colored image into shades of grey. This is a concept that is useful for thresholding, a concept that we will see in subsequent sections in the tutorial.

import cv2

img = cv2.imread("C:\\Users\\aditya\\Desktop\\shapes.png");
cv2.imshow("Shapes",img)

#convert image to greyscale
imgGray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
cv2.imshow("Grey Image",imgGray)


cv2.waitKey(0) 

The converted grey-scale would be as follows,



Task 3 - Using Thresholding and Contours to detect all the outlines

Threshold is a method of segmenting the image image based on a specific value or limit. A threshold value is basically a value that demarcates the segments that have been formed.

Contours are defined as the lines formed by connecting the pixels of same intensity or color along the boundaries of an image.

Now, in order to find all the contours present in an image, we will make use of 2 main OpenCV functions -

cv2.threshold() - To set the threshold value to segment the image on
cv2.findContours() - To find all the contours present in the image

The cv2.threshold() function takes the following arguments -

    + imgGray : The gray-scale image to be fed as input
    + 127 : Threshold value
    + 255 : The maximum value of the pixel range
    + 0 : setting the thresholding technique

The cv2.findContours() function takes the following arguments -
 
     + thresh : The returned image with the thresholding applied
     + cv2.RETR_TREE : The contour retrieval mode
     + cv2.CHAIN_APPROX_NONE : The contour approximation method


NOTE - Be sure to run the thresholding and findContours command on the grey-scale image.

import cv2
img = cv2.imread("C:\\Users\\aditya\\Desktop\\shapes.png");
cv2.imshow("Shapes",img)
imgGray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
cv2.imshow("Gray Image",imgGray)
#Find all the contours in an image
ret,thresh = cv2.threshold(imgGray,127,255,0)
contours,heirarchy = cv2.findContours(thresh,cv2.RETR_TREE,cv2.CHAIN_APPROX_NONE)
print("Number of contours: ", str(len(contours)))
print(contours[0])
cv2.waitKey(0)

The output with the total number of contours and the numpy array of the boundry points would be as follows,




Task 4 - Detecting the shapes and drawing an outline around them

In this task we will make use of a function named drawContours() to draw a yellow outline outside all the shapes.

cv2.drawContours() - To draw the contours around all shapes in the image

Arguments -
    + img : The original image to draw the contour on
    + contours : The contours array we got from the previous task
    + -1 : The contour no. to be drawn. A value of -1 indicates that all the contours will be drawn
    + (0,255,255) : The color of the contour being drawn using the RGB model, which in this case is yellow.

    + 2 : The thickness of the contour being drawn



import cv2

img = cv2.imread("C:\\Users\\aditya\\Desktop\\shapes.png");
cv2.imshow("Shapes",img)

imgGray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
cv2.imshow("Grey Image",imgGray)

ret,thresh = cv2.threshold(imgGray,127,255,0)
contours,heirarchy = cv2.findContours(thresh,cv2.RETR_TREE,cv2.CHAIN_APPROX_NONE)
print("Number of contours: ", str(len(contours)))
print(contours[0])

#drawing contours around all the shapes

cv2.drawContours(img,contours,-1,(0,255,255),2)

cv2.imshow("Shapes",img)

cv2.waitKey(0)

Which would give the following output -


Now that we have this output in hand, it will be a relatively easy process to detect each of the shapes.

We will use the no. of edges in each contour as a method for differentiating between the 3 shapes given above. We'll use 2 main functions to determine each of the shapes -

cv2.arcLength() - To find the perimeter of the contours
cv2.approxPolyDP() - To approximate a poligon's contours based on the specified parameters

The cv2.arcLength() function takes the following arguments -

    + c : Each contour in the list of recieved contours
    + True : To check if the contour is a closed figure or not

The cv2.approxPolyDP() function takes the following arguments -
 
    + c : Each contour in the list of recieved contours
    + 0005 * peri : A value called as epsilon specifying the approximation accuracy which is the maximum distance between the original contour and its approximation.
    +True : To check if the contour is a closed figure or not

import cv2

img = cv2.imread("C:\\Users\\adchella\\Desktop\\shapes.png");

imgGray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)

ret,thresh = cv2.threshold(imgGray,127,255,0)
contours,heirarchy = cv2.findContours(thresh,cv2.RETR_TREE,cv2.CHAIN_APPROX_NONE)
print("Number of contours: ", str(len(contours)))
print(contours[0])

for c in contours:
    peri = cv2.arcLength(c, True)
    approx = cv2.approxPolyDP(c, 0.005 * peri, True)

# To detect a triangle
    if len(approx) == 3:
        scrnCnt = approx    
        cv2.drawContours(img, [scrnCnt], -1, (0, 255, 0), 3)

# To detect a square
    elif len(approx) == 4:
        scrnCnt1 = approx    
        cv2.drawContours(img, [scrnCnt1], -1, (0, 0, 255), 3)
    
# To detect a circle    
    elif len(approx) > 5:
        scrnCnt2 = approx    
        cv2.drawContours(img, [scrnCnt2], -1, (255, 0, 0), 3)

cv2.imshow("Shapes",img)
cv2.waitKey(0)

Which will detect the following shapes for us -


We now have successfully detected all the 3 shapes in the images and drawn borders of different colors along their edges so as to identify them correctly.

Task 5 - Labelling the images detected

Now that we have successfully identified all the shapes in the image, it would be a good idea to label each detected shape with an appropriate name. For this purpose we would be using a function called as putText().

The cv2.putText() function takes in the following arguments -

    + img : The image to write the labels on
    + "Square" : The text to be written as a label
    + pos[0] : The bottom right corner of the text being displayed called as org.
    + 3 : Font scale
    + cv2.FONT_HERSHEY_PLAIN : Font family
    + (0,0,0) : Font color

import cv2

img = cv2.imread("C:\\Users\\adchella\\Desktop\\shapes.png");

imgGray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)

ret,thresh = cv2.threshold(imgGray,127,255,0)
contours,heirarchy = cv2.findContours(thresh,cv2.RETR_TREE,cv2.CHAIN_APPROX_NONE)

for c in contours:
    peri = cv2.arcLength(c, True)
    approx = cv2.approxPolyDP(c, 0.005 * peri, True)
    pos = tuple(map(tuple,c))
    print(pos[0])
    print(type(pos))
    
# To detect a triangle
    if len(approx) == 3:
        scrnCnt = approx    
        cv2.drawContours(img, [scrnCnt], -1, (0, 255, 0), 3)
        pos = tuple(map(tuple,c[0]))
        cv2.putText(img,"Triangle",pos[0],3,cv2.FONT_HERSHEY_PLAIN,(0,0,0))

# To detect a square
    elif len(approx) == 4:
        scrnCnt1 = approx    
        cv2.drawContours(img, [scrnCnt1], -1, (0, 0, 255), 3)
        pos = tuple(map(tuple,c[0]))
        cv2.putText(img,"Square",pos[0],3,cv2.FONT_HERSHEY_PLAIN,(0,0,0))
    
# To detect a circle    
    elif len(approx) > 5:
        scrnCnt2 = approx    
        cv2.drawContours(img, [scrnCnt2], -1, (255, 0, 0), 3)
        pos = tuple(map(tuple,c[0]))
        cv2.putText(img,"Circle",pos[0],3,cv2.FONT_HERSHEY_PLAIN,(0,0,0))

cv2.imshow("Shapes",img)
cv2.waitKey(0)



Reactions

Post a Comment

0 Comments