Python OpenCV: Face detection and counting

The objective of this post is to demonstrate how to detect and count faces in an image, using OpenCV and Python.


Introduction

The objective of this post is to demonstrate how to detect and count faces in an image, using OpenCV and Python. In this simple example, we will use a Haar feature-based cascade classifier.

 

The code

As usual, we will start by including the modules needed for the image processing. These are the OpenCV and the Numpy modules.

import numpy as np
import cv2

Then, we will load the classifier with the CascadeClassifier function of the cv2 module, which receives as input the path to the classifier file. In this case, we are going to use the haarcascade_frontalface_default.xml classifier file. This file can be found at the opencv\sources\data\haarcascades directory of the OpenCV uncompressed files we get after downloading. You can also get the file here.

In order to guarantee that the file is correctly accessed, I’m going to put the whole file path in the CascadeClassifier function. As shown bellow, I have the file in my desktop directory, but you can put it in any directory you like, as long as you pass the correct path. This function will return a CascadeClassifier object.

face_cascade = cv2.CascadeClassifier('C:/Users/N/Desktop/haarcascade_frontalface_default.xml')

Then we will load the image with the imread function of the cv2 module. This function will receive as input the path to the image. We will pass as input the image in which we want to to the face detection.

We will also convert the image to grayscale, in order to apply the classification. To do so, we use the cvtColor function. This will receive as input the original image and as second input the code for the color space conversion. In this case, to convert from RGB to gray, we use the COLOR_BGR2GRAY code.

image = cv2.imread('C:/Users/N/Desktop/Test.jpg')
grayImage = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)

Now, to do the actual face detection, we will call the detectMultiScale method on our face_cascade object. As input we will pass our converted gray image. Additionally, this function can receive some tuning parameters, such as the scaleFactor or the object to detect minSize. You can read more about these parameters here but, for the sake of simplicity, we will just pass as input the image.

This method will return the detected objects (in this case, the faces) as rectangles [1], so we can easily mark them in the output image.

Just to confirm the type of the output, we will use Python’s type function to check the type of the returned object. It will be a Numpy ndarray.

faces = face_cascade.detectMultiScale(grayImage)
print type(faces)
print faces

We can also know more about our output by calling the shape method on our array, which will return its dimensions. As we will see, we will get a matrix of N rows and 4 columns, being N the number of faces detected and 4 the dimensions of the rectangle of each face.

So, if we get the N rows, we now how many faces were detected, as indicated bellow.

print faces.shape
print "Number of faces detected: " + str(faces.shape[0])

Now, we will iterate the results and draw the rectangles around the faces in our original image. To do so, we call the rectangle function on the cv2 module. This function receives as input the image, the initial and final vertexes of the rectangle, the RGB color for the rectangle and the thickness of the rectangle [2]. We will do this in a for in loop, to get and draw all the rectangles.

for (x,y,w,h) in faces:
    cv2.rectangle(image,(x,y),(x+w,y+h),(0,255,0),1)

We will also put the number of faces detected in the bottom left of our image. First, we will draw another rectangle to put the text on top, because we don’t know the color of the images. So, this guarantees that the text will always be easily readable.

The starting vertex will be at coordinates (x = 0, y = image height – 25) and the end vertex will be at (x= 270, image height). Note that to avoid more complex code, this is hardcoded for the size of the text we will specify.

Note that, to get the height of the image, we can do the same trick we did before with the ndarray. Since the image is a matrix, we call the shape method and get the number of rows.

We will pass the color white and a thickness of -1 to the rectangle function, meaning the rectangle will be filled.

To write the text, we call the putText function, passing as input the image, the text, the bottom left corner where to put the text, the font, the scale of the text, the color and the thickness [3].

cv2.rectangle(image, ((0,image.shape[0] -25)),(270, image.shape[0]), (255,255,255), -1)
cv2.putText(image, "Number of faces detected: " + str(faces.shape[0]), (0,image.shape[0] -10), cv2.FONT_HERSHEY_TRIPLEX, 0.5, (0,0,0), 1)

Note that this is just an add on, but if you prefer, for simplicity, you can remove it from the code, since the number of faces will be printed on the console.

Finally, we show the edited image with the rectangles and the text, and wait for a key to be pressed to destroy the windows.

cv2.imshow('Image with faces',image)
cv2.waitKey(0)
cv2.destroyAllWindows()

You can check the full working code bellow.

import numpy as np
import cv2
face_cascade = cv2.CascadeClassifier('C:/Users/N/Desktop/haarcascade_frontalface_default.xml')

image = cv2.imread('C:/Users/N/Desktop/test.jpg')
grayImage = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)

faces = face_cascade.detectMultiScale(grayImage)

print type(faces)
print faces
print faces.shape
print "Number of faces detected: " + str(faces.shape[0])

for (x,y,w,h) in faces:
    cv2.rectangle(image,(x,y),(x+w,y+h),(0,255,0),1)

cv2.rectangle(image, ((0,image.shape[0] -25)),(270, image.shape[0]), (255,255,255), -1)
cv2.putText(image, "Number of faces detected: " + str(faces.shape[0]), (0,image.shape[0] -10), cv2.FONT_HERSHEY_TRIPLEX, 0.5,  (0,0,0), 1)

cv2.imshow('Image with faces',image)
cv2.waitKey(0)
cv2.destroyAllWindows()


Testing the code

To test the code, just change the paths for your classifier and picture and run it on IDLE. In my case, I ran it against a picture of Portugal team (original picture obtained from here).

As can be seen in figure 1, the result was pretty accurate, with the faces of the 11 players being recognized.

Python OpenCV face detection.png

Figure 1 – Result of face detection with OpenCV.

 

You can check in figure 2 the corresponding output in the console.

Python OpenCV Face detection printed data

Figure 2 – Output on the console.

 

Check on figure 3 an example of the program running for a picture with just a person on it.

Python OpenCV one face detection.png

Figure 3 – Face detection in an image with only one person.


Final notes

As can be seen by the examples provided, the classifier works pretty well. Nevertheless, don’t expect an 100% accuracy. There was no tuning on the parameters and the images were relatively simple. Naturally, there will be use cases where false positives and false negatives will appear, which is normal.


Related content

 

References

[1] http://docs.opencv.org/3.0-beta/modules/objdetect/doc/cascade_classification.html?highlight=detectmultiscale#cv2.CascadeClassifier.detectMultiScale

[2] http://docs.opencv.org/3.0-beta/modules/imgproc/doc/drawing_functions.html?highlight=rectangle#cv2.rectangle

[3] http://docs.opencv.org/3.0-beta/modules/imgproc/doc/drawing_functions.html?highlight=rectangle#puttext

 

Technical details

  • Python version: 2.7.8
  • OpenCV version: 3.2.0
Advertisements
This entry was posted in Python and tagged , , , , . Bookmark the permalink.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s