Face recognition has evolved significantly—from classical statistical methods like Eigenfaces (PCA) and texture-based descriptors like LBPH (Local Binary Pattern Histograms) to modern deep learning frameworks like DeepFace.
In this post, we will implement and compare all three approaches on a small dataset in Python, test how they hold up against illumination changes (e.g., low lighting), and look at how to run the code effortlessly without running into environment issues.
Eigenfaces (PCA-based Representation)
LBPH (Local Binary Pattern Histograms)
DeepFace (Deep Learning-based Recognition)
ModuleNotFoundError: No module named 'cv2'
A common mistake when running this code is executing it inside lightweight online compilers (like Programiz or OnlineGDB), which results in:
Traceback (most recent call last):
File "/box/main.py", line 1, in <module>
import cv2
ModuleNotFoundError: No module named 'cv2'
opencv-contrib-python`` opencv-python. Standard OpenCV does cv2.face module needed for Eigenfaces and LBPH!
Run this in your terminal:
pip uninstall opencv-python -y
pip install opencv-contrib-python deepface scikit-learn tf-keras
If you have 4 sample face images in your project directory (p1_1.jpg, p1_2.jpg for Person 0, and p2_1.jpg, p2_2.jpg for Person 1), here is the cleanest implementation:
import cv2
import numpy as np
from deepface import DeepFace
def load_img(path):
img = cv2.imread(path, cv2.IMREAD_GRAYSCALE)
return cv2.resize(img, (100, 100))
train_imgs = [load_img('p1_1.jpg'), load_img('p1_2.jpg'), load_img('p2_1.jpg')]
labels = np.array([0, 0, 1])
test_img = load_img('p2_2.jpg')
dark_test_img = np.clip(test_img * 0.5, 0, 255).astype(np.uint8)
cv2.imwrite('dark_test.jpg', dark_test_img) # Save for DeepFace
eigen_model = cv2.face.EigenFaceRecognizer_create()
eigen_model.train(train_imgs, labels)
pred_eigen_norm, _ = eigen_model.predict(test_img)
pred_eigen_dark, _ = eigen_model.predict(dark_test_img)
lbph_model = cv2.face.LBPHFaceRecognizer_create()
lbph_model.train(train_imgs, labels)
pred_lbph_norm, _ = lbph_model.predict(test_img)
pred_lbph_dark, _ = lbph_model.predict(dark_test_img)
df_norm = DeepFace.verify('p2_1.jpg', 'p2_2.jpg', enforce_detection=False)
df_dark = DeepFace.verify('p2_1.jpg', 'dark_test.jpg', enforce_detection=False)
print("--- COMPARISON RESULTS (Target: Person 1) ---")
print(f"1. Eigenfaces | Normal: {pred_eigen_norm == 1} | Dark: {pred_eigen_dark == 1}")
print(f"2. LBPH | Normal: {pred_lbph_norm == 1} | Dark: {pred_lbph_dark == 1}")
print(f"3. DeepFace | Normal: {df_norm['verified']} | Dark: {df_dark['verified']}")
Don't have sample photos handy? Paste this directly into a Google Colab cell. It automatically installs the dependencies and fetches real faces from Scikit-Learn's built-in LFW (Labeled Faces in the Wild) dataset:
!pip install -q opencv-contrib-python deepface scikit-learn
import cv2
import numpy as np
from deepface import DeepFace
from sklearn.datasets import fetch_lfw_people
lfw = fetch_lfw_people(min_faces_per_person=10, resize=1.0)
img_p1_1 = (lfw.images[0] * 255).astype(np.uint8) # Person 0, Image 1
img_p1_2 = (lfw.images[1] * 255).astype(np.uint8) # Person 0, Image 2
img_p2_1 = (lfw.images[2] * 255).astype(np.uint8) # Person 1, Image 1 (Train)
test_img = (lfw.images[3] * 255).astype(np.uint8) # Person 1, Image 2 (Test)
train_imgs = [cv2.resize(x, (100, 100)) for x in [img_p1_1, img_p1_2, img_p2_1]]
test_img = cv2.resize(test_img, (100, 100))
labels = np.array([0, 0, 1])
dark_test_img = (test_img * 0.5).astype(np.uint8)
cv2.imwrite('train_p2.jpg', cv2.cvtColor(train_imgs[2], cv2.COLOR_GRAY2BGR))
cv2.imwrite('test_norm.jpg', cv2.cvtColor(test_img, cv2.COLOR_GRAY2BGR))
cv2.imwrite('test_dark.jpg', cv2.cvtColor(dark_test_img, cv2.COLOR_GRAY2BGR))
eigen = cv2.face.EigenFaceRecognizer_create()
eigen.train(train_imgs, labels)
print("Eigenfaces (Normal, Dark):", eigen.predict(test_img)[0], eigen.predict(dark_test_img)[0])
lbph = cv2.face.LBPHFaceRecognizer_create()
lbph.train(train_imgs, labels)
print("LBPH (Normal, Dark):", lbph.predict(test_img)[0], lbph.predict(dark_test_img)[0])
df_norm = DeepFace.verify('train_p2.jpg', 'test_norm.jpg', enforce_detection=False)
df_dark = DeepFace.verify('train_p2.jpg', 'test_dark.jpg', enforce_detection=False)
print("DeepFace (Normal, Dark):", df_norm['verified'], df_dark['verified'])
Here is how the three approaches stack up across key metrics:
| Evaluation Metric | 1. Eigenfaces (PCA) | 2. LBPH | 3. DeepFace |
|---|---|---|---|
| Accuracy | Low to Moderate (struggles with pose and lighting variations). | Moderate to High on small, controlled datasets. | Very High (Backed by state-of-the-art CNNs). |
| Robustness Under Illumination Changes | Poor — Global pixel intensity changes drastically shift PCA eigenvectors. | Good — Local pixel thresholding preserves texture patterns even when darkened. | Excellent — Pre-trained on millions of faces across diverse lighting conditions. |
| Ease of Implementation | Medium — Requires strictly identical image dimensions and grayscale conversion. | Easy — Handles varied sizes and uses the straightforwardcv2.face API. |
Easiest — Just 1 line of code (DeepFace.verify() ) with no training loop needed. |
Happy coding! Feel free to drop any questions in the comments below. 👇