In this tutorial we will learn how to detect cat faces with Python and OpenCV. We will use a Haar feature-based cascade classifier for the face detection.
OpenCV has some pre-trained Haar classifiers, which can be found here. In our case, we are interested in the haarcascade_frontalcatface.xml file, which we will need to download to use in our tutorial.
This tutorial was tested on Windows 8.1, with version 4.1.2 of OpenCV. The Python version used was 3.7.2.
We will start by importing the cv2 module.
Then we will create an object of class CascadeClassifier, which we will use to detect cat faces in an image. As input of the constructor we need to pass the path to the classifier file we have downloaded.
catFaceCascade = cv2.CascadeClassifier('C:/Users/N/Desktop/haarcascade_frontalcatface.xml')
After this we will read the image from the file system. This is done with a call to the imread function from the cv2 module, passing as input the path to the image, as a string. Naturally, this is the image where we want to find the cat faces.
image = cv2.imread('C:/Users/N/Desktop/test.jpg')
Now that we have both the image and the classifier, we will perform the cat faces detection. This is done with a call to the detectMultiScale method on our CascadeClassifier object, passing as input the image. Although we are going to pass only the image, this method supports some additional parameters that allow to fine tune the detection. We won’t be making use of them on this tutorial, but you can read more about them here.
As output, this method returns the detected faces as a ndarray. More particularly, this ndarray is a matrix containing N rows and 4 columns, where N is the number of faces detected and each column corresponds to a dimension of the rectangle delimiting the detected face.
faces = catFaceCascade.detectMultiScale(image)
Before moving on with drawing the rectangles on the image, we will check if actually any face was detected. Like mentioned before, the number of lines of the matrix returned by the detectMultiScale method corresponds to the number of faces. So, if the length of that matrix is greater than zero, at least a face was detected.
if len(faces) == 0: print("No faces found") else: print("Number of faces detected: " + str(faces.shape))
So, if faces were detected, we will iterate through each face and draw it in the image. We will do that with a for in loop where we will also unpack each rectangle to its individual components:
- x coordinate of the top left corner of the rectangle;
- y coordinate of the top left corner of the rectangle;
- width of the rectangle;
- height of the rectangle.
for (x, y, w, h) in faces: # draw rectangle in image
For a detailed tutorial on how to draw rectangles in images with OpenCV, please check here. In short, we simply need to call the rectangle function from the cv2 module, passing the following inputs:
- The image where to draw the rectangle.
- A tuple with the x and y coordinates of one of the vertices of the rectangle. To simplify, we will pass a tuple with the coordinates of the top left corner of the rectangle.
- A tuple with the x and y coordinates of the opposite vertex of the rectangle, regarding the previous one. Also to simplify, we can consider this the bottom right corner of the rectangle, which can be obtained from the top left corner and the width and the height.
- A tuple with the BGR color of the rectangle. We will set it to green.
cv2.rectangle(image, (x, y), (x + w, y + h), (0, 255, 0))
After drawing all the detected cat faces, we will display the image in a window. We do this by calling the imshow function, passing as first input the name of the window and as second the image. We will then wait for the user to press any key on the keyboard and, after that event, destroy the window and end the program.
cv2.imshow('Image with faces', image) cv2.waitKey(0) cv2.destroyAllWindows()
The complete code can be seen below.
import cv2 catFaceCascade = cv2.CascadeClassifier('C:/Users/N/Desktop/haarcascade_frontalcatface.xml') image = cv2.imread('C:/Users/N/Desktop/test.jpg') faces = catFaceCascade.detectMultiScale(image) if len(faces) == 0: print("No faces found") else: print("Number of faces detected: " + str(faces.shape)) for (x, y, w, h) in faces: cv2.rectangle(image, (x, y), (x + w, y + h), (0, 255, 0)) cv2.imshow('Image with faces', image) cv2.waitKey(0) cv2.destroyAllWindows()
Testing the code
To test the code, simply run it using a tool of your choice. I’m using PyCharm, a Python IDE. You should obtain a result similar to figure 1, which shows the face of my cute cat being detected.
As a final note, it is important to consider that we should not expect 100% accuracy. Naturally, we can get both false negatives and false positives is some images. Also, like mentioned before, there are some additional parameters on the detectMultiScale method that can be fine tuned to improve the results for a given application.