I am trying to build a face detection application in python using opencv.
Please see below for my code snippets:
# Loading the Haar Cascade Classifier
c
To get the number of faces it should be:
print "Number of faces found in-> ", img_fname, " are ", len(faces)
.
I would also recommend that to convert image to gray scale you should write:
gray = cv2.cvtColor(im, cv2.COLOR_BGR2GRAY)
instead of gray = cv2.cvtColor(im, cv2.COLOR_RGB2GRAY)
as color images are loaded by openCV in BGR mode.
The cause of the problem is that detectMultiScale
returns an empty tuple ()
when there's no matches, but a numpy.ndarray
when there are matches.
>>> faces = classifier.detectMultiScale(cv2.imread('face.jpg'))
>>> print(type(faces), faces)
<class 'numpy.ndarray'> [[ 30 150 40 40]]
>>> faces = classifier.detectMultiScale(cv2.imread('wall.jpg'))
>>> print(type(faces), faces)
<class 'tuple'> ()
You might expect that a negative result would be a ndarray of shape (0,4), but that's not the case.
This behaviour and the reasoning behind it is not explained in the documentation, which instead indicates that the return value should be "objects".
OpenCV has a lot of warts like this, and the cryptic error messages doesn't help. One way deal with it is to add logging statements or asserts into your code to check that everything is the type you expected.
It's also very useful to explore how a library works in a repl such as ipython. This is used in Rahul K P's answer.
In this case, you can solve your problem by not using shape
. Python has many data types that are sequences or collections, for example tuple
, list
and dict
. All of these implement the len()
built-in function and you can also loop over them using for x in y
. In contrast shape
is only a property of numpy.ndarray
, and not found in any of the built-in python data types.
Your code should work if you rewrite it to use len(faces)
instead of faces.shape[0]
, since the former works with both tuple and ndarray.
for img_fname in os.listdir('/home/work/images/caltech_face_dataset/'):
img_path = '/home/work/images/caltech_face_dataset/' + img_fname
im = imread(img_path)
gray = cv2.cvtColor(im, cv2.COLOR_RGB2GRAY)
faces = faceCascade.detectMultiScale(gray) # use the grayscale image
print "Number of faces found in-> {} are {}".format(
img_fname, len(faces)) # len() works with both tuple and ndarray
num_faces_dict[img_fname] = len(faces)
# when faces is (), the following loop will never run, so it's safe.
for (x,y,w,h) in faces:
cv2.rectangle(im, (x,y), (x+w,y+h), (255,255,255), 3)
rect_img_path = '/home/work/face_detected/rect_' + img_fname
cv2.imwrite(rect_img_path,im)
From your error understand that you are trying to read the shape
. But shape is the attribute of numpy.ndarray
. You are trying to read the shape from the result of face detection. But that will only return the position only. Look at the types. Here img
is an image and faces
is the result of face detection. I hope you got the problem.
Updated with full code. For more clarification
In [1]: import cv2
In [2]: cap = cv2.VideoCapture(0)
In [3]: ret,img = cap.read()
In [4]: cascadePath = "/home/bikz05/Desktop/SNA_work/opencv-2.4.9/data/haarcascades/haarcascade_frontalface_default.xml"
In [5]: faceCascade = cv2.CascadeClassifier(cascadePath)
In [6]: faces = faceCascade.detectMultiScale(img)
In [7]: type(img)
Out[1]: numpy.ndarray
In [8]: type(faces)
Out[2]: tuple
Look at the diffrence.
In [9]: img.shape
Out[3]: (480, 640, 3)
In [10]: faces.shape
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
<ipython-input-40-392225a0e11a> in <module>()
----> 1 faces.shape
AttributeError: 'tuple' object has no attribute 'shape'
If you want the number of faces. It's in the form of list of tuple. You can find the number of faces using len
like len(faces)
import numpy as np
import cv2
face_cascade = cv2.CascadeClassifier('haarcascade_frontalface_default.xml')
image = cv2.imread('myfriends.jpg')
grayImage = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
faces = face_cascade.detectMultiScale(grayImage)
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()