Python Face Recognition with PCA: Error Handling and Optimization
This Python code implements face recognition using Principal Component Analysis (PCA) with OpenCV. It demonstrates the common error 'IndexError: tuple index out of range' and provides a solution for handling it. Here's a breakdown of the code, the error, and how to fix it.
import cv2
import os
import numpy as np
from sklearn.decomposition import PCA
def detect_face(img, face_cascade):
gray = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY)
faces = face_cascade.detectMultiScale(gray, scaleFactor=1.1, minNeighbors=5, minSize=(30, 30), flags=cv2.CASCADE_SCALE_IMAGE)
if (len(faces) == 0):
return None
for (x, y, w, h) in faces:
face = gray[y:y + w, x:x + h].astype(np.float32)
face = cv2.resize(face, (150,150))
return face
def prepare_training_data(data_folder_path):
face_cascade = cv2.CascadeClassifier('haarcascade_frontalface_alt.xml')
dirs = os.listdir(data_folder_path)
faces = []
labels = []
for dir_name in dirs:
label = int(dir_name)
subject_dir_path = data_folder_path + '/' + dir_name
subject_images_names = os.listdir(subject_dir_path)
for image_name in subject_images_names:
image_path = subject_dir_path + '/' + image_name
image = cv2.imread(image_path)
if image is None:
continue
gray = cv2.cvtColor(image, cv2.COLOR_RGB2GRAY)
rect = face_cascade.detectMultiScale(gray, scaleFactor=1.1, minNeighbors=5, minSize=(30, 30),flags=cv2.CASCADE_SCALE_IMAGE)
for (x, y, w, h) in rect:
face = gray[y:y + w, x:x + h].astype(np.float32)
face = cv2.resize(face, (150, 150))
if face is not None:
face_vector = np.reshape(face,(1,-1))[0]
faces.append(face_vector)
labels.append(label)
faces= np.array(faces)
labels= np.array(labels)
return faces, labels
def preprocess_image(img, face_cascade, pca):
face = detect_face(img, face_cascade)
if face is not None:
face_pca = pca.transform(face.reshape(1, -1))
img_processed = cv2.cvtColor(face, cv2.COLOR_GRAY2RGB)
return face_pca, img_processed
else:
return None, None
def main():
cap = cv2.VideoCapture(0)
stop = False
face_cascade = cv2.CascadeClassifier('haarcascade_frontalface_default.xml')
pca = PCA(n_components=60, svd_solver='full')
faces, labels = prepare_training_data('raw1')
faces = pca.fit_transform(faces)
while not stop:
success, img = cap.read()
face_pca, img_processed = preprocess_image(img, face_cascade, pca)
if face_pca is not None:
subjects = ['xu', 'LU', 'ZHOU']
img1 = img_processed.copy()
faces = face_cascade.detectMultiScale(img, scaleFactor=1.1, minNeighbors=5, minSize=(30, 30),flags=cv2.CASCADE_SCALE_IMAGE)
if len(faces) > 0:
(x, y, w, h) = faces[0]
cv2.rectangle(img1, (x, y), (x + w, y + h), (0, 255, 0), 2)
min_d = 1000000000000
c = -1
for i, f in enumerate(faces):
d = ((face_pca - f) ** 2).sum()
if d < min_d:
min_d = d
c = labels[i]
print(subjects[c])
cv2.putText(img1, subjects[c], (x, y), cv2.FONT_HERSHEY_COMPLEX, 1, (128, 128, 0), 2)
cv2.imshow('test', img1)
cv2.imshow('img', img1)
c = cv2.waitKey(1)
if c & 0xFF == ord('q'):
stop = True
cv2.destroyAllWindows()
if __name__ == '__main__':
main()
The Error: 'IndexError: tuple index out of range'
This error occurs when you try to access an element in a tuple using an index that is outside the bounds of the tuple's length. In this code, the error occurs in the main() function:
(x, y, w, h) = faces[0]
The face_cascade.detectMultiScale() function returns a tuple of rectangles representing detected faces. If no faces are found, the tuple will be empty. Attempting to access the first element (faces[0]) of an empty tuple causes the IndexError.
Error Handling
To fix the error, you need to check if the faces tuple is empty before accessing its elements. The corrected code snippet looks like this:
faces = face_cascade.detectMultiScale(img, scaleFactor=1.1, minNeighbors=5, minSize=(30, 30),flags=cv2.CASCADE_SCALE_IMAGE)
if len(faces) > 0:
(x, y, w, h) = faces[0]
# Rest of the code for drawing rectangle and processing the face
else:
# Code for handling the case when no face is detected
This code checks if len(faces) is greater than zero. If it is, then it proceeds to extract the coordinates of the first detected face. Otherwise, you can implement a different behavior, such as displaying a message indicating that no face was detected.
Additional Optimization
-
Reduce Unnecessary Calculations: You can improve efficiency by moving the calculation of
facesoutside thewhileloop. Since the face detection parameters don't change, you can calculatefacesonly once before the loop starts. -
Early Exit from Loops: If a face is detected, the
forloop iterating overfacescan be exited early. This can save unnecessary processing time.
Complete Optimized Code:
import cv2
import os
import numpy as np
from sklearn.decomposition import PCA
# ... (Previous functions remain the same)
def main():
cap = cv2.VideoCapture(0)
stop = False
face_cascade = cv2.CascadeClassifier('haarcascade_frontalface_default.xml')
pca = PCA(n_components=60, svd_solver='full')
faces, labels = prepare_training_data('raw1')
faces = pca.fit_transform(faces)
while not stop:
success, img = cap.read()
face_pca, img_processed = preprocess_image(img, face_cascade, pca)
if face_pca is not None:
subjects = ['xu', 'LU', 'ZHOU']
img1 = img_processed.copy()
faces = face_cascade.detectMultiScale(img, scaleFactor=1.1, minNeighbors=5, minSize=(30, 30),flags=cv2.CASCADE_SCALE_IMAGE)
if len(faces) > 0:
(x, y, w, h) = faces[0]
cv2.rectangle(img1, (x, y), (x + w, y + h), (0, 255, 0), 2)
min_d = 1000000000000
c = -1
for i, f in enumerate(faces):
d = ((face_pca - f) ** 2).sum()
if d < min_d:
min_d = d
c = labels[i]
break # Exit loop if a match is found
print(subjects[c])
cv2.putText(img1, subjects[c], (x, y), cv2.FONT_HERSHEY_COMPLEX, 1, (128, 128, 0), 2)
cv2.imshow('test', img1)
cv2.imshow('img', img1)
c = cv2.waitKey(1)
if c & 0xFF == ord('q'):
stop = True
cv2.destroyAllWindows()
if __name__ == '__main__':
main()
By handling the IndexError and implementing these optimization techniques, you've made your face recognition code more robust and efficient.
原文地址: https://www.cveoy.top/t/topic/mJPG 著作权归作者所有。请勿转载和采集!