Reputation: 693
I try to make face detection and recognition using OpenCV that capture image/video from pc cam. But when I run it this error happen
$ python face_recog.py
Traceback (most recent call last):
File "face_recog.py", line 28, in <module>
if face_extractor(frame) is not None:
File "face_recog.py", line 19, in face_extractor
return cropped_face | ''
UnboundLocalError: local variable 'cropped_face' referenced before assignment
Here the full code for my app. Basically, it imports the library and reads haar cascade object lib in the XML file. and then use face_extractor function to read image/video from cam
import cv2
import numpy as np
face_path = cv2.CascadeClassifier("haarcascade_frontalface_default.xml")
def face_extractor(img):
#img_umat = cv2.UMat(img)
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
face = face_path.detectMultiScale(gray, 1.3, 5)
if face in ():
return None
for(x, y, w, h) in face:
cropped_face = img[y: y+h, x: x+w]
return cropped_face
capture = cv2.VideoCapture(0, cv2.CAP_DSHOW) #captureDevice = camera
count = 0
while True:
ret, frame = capture.read()
if face_extractor(frame) is not None:
count = count + 1
face = cv2.resize(face_extractor(frame), (200, 200))
#face_umat = cv2.UMat(face)
face = cv2.cvtColor(face, cv2.COLOR_BGR2GRAY)
output_path = "C:/Users/bryan/Desktop/test/Cascade/Output_img/user"+str(count)+".jpg"
cv2.imwrite(output_path, face)
cv2.putText(face, str(count), (50, 50), cv2.FONT_HERSHEY_COMPLEX, 1, (0, 255, 0), 2)
cv2.imshow("face cropper", face)
else:
print("FACE NOT FOUND !")
pass
if cv2.waitKey(1) == 13 or count == 100:
break
capture.release()
cv2.destroyAllWindows()
print("ALL SAMPLES COLLECTED !!")
Upvotes: 0
Views: 583
Reputation: 104503
detectMultiScale
returns a list of tuples where each element in this list gives you the top left corner of where a face was detected as well as the width and height of the extent of the face. This returns an empty list if you detect no faces, so if face in ()
makes absolutely no sense. What you are essentially doing is checking to see if face
(which is a list) is contained inside an empty tuple which is always going to be False
as there are no elements in this tuple. Once that proceeds, you will potentially have an image with no faces, so the second for
loop doesn't execute because face
would be an empty list. cropped_face
is never assigned to anything and thus your error is produced.
As suggested by the comments, check to see if the list is empty by doing if not face
instead.
Also supposing you have faces detected, the next for
loop will only return the last face in the face
list as the cropped_face
variable keeps overwriting itself. You may want to return a list of crops if you have more than one face detected instead:
def face_extractor(img):
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
face = face_path.detectMultiScale(gray, 1.3, 5)
if not face: # Change
return None
return [img[y:y+h, x:x+w] for (x, y, w, h) in face] # Change
I would argue that you can get rid of the first if
statement because if the face
list is empty, the second for
loop written above will also return an empty list. Unless you are specifically ensuring that None
is returned if there are no faces, you could really just do:
def face_extractor(img):
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
face = face_path.detectMultiScale(gray, 1.3, 5)
return [img[y:y+h, x:x+w] for (x, y, w, h) in face] # Change
The empty list implicitly means no faces were detected which would be more uniform in terms of what is being returned - a list of faces which can be empty vs. a list of faces and None
for the edge case if we detect no faces.
Judging from your use case, it appears that only one face is ever going to be detected at one time, so a cheap way to do this is to simply sample into the list and grab the first face. This is not the way I would do this long-term, but to be compatible with the rest of your code let's do that.
def face_extractor(img):
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
face = face_path.detectMultiScale(gray, 1.3, 5)
if not face: # Change
return None
(x, y, w, h) = face[0] # Change
cropped_face = img[y:y+h, x:x+w]
return cropped_face
I am maintaining that you return None
if no faces can be found because now we're just returning a single crop instead of a list.
Upvotes: 1