Article
Real-time face detection in Python: MediaPipe vs Haar cascade
Two ways to detect faces in a webcam feed with Python. See the code for both, then a straight comparison of speed, accuracy and when each one is the right choice.
On this page0%
- What you need
- Step 1 — Face detection with Haar cascade
- Step 2 — Face detection with MediaPipe
- Step 3 — Compare them honestly
- Step 4 — Blur faces for privacy
- Step 5 — Make it fast enough
- Common problems (and fixes)
- What you learned
- FAQ
- Which is better for face detection, MediaPipe or Haar cascade?
- How do I detect faces in a webcam feed with Python?
- What do scaleFactor and minNeighbors do in detectMultiScale?
- Why does Haar cascade detect faces that are not there?
- How can I speed up real-time face detection?
- Next reading on Sythra Articles
There are two popular ways to detect faces in Python, and beginners usually pick the wrong one for their situation.
Haar cascade ships inside OpenCV, needs no extra install, and has been around since 2001. MediaPipe Face Detection is Google's modern neural model — more accurate, better with angled faces, and roughly as fast.
Short version: use MediaPipe for webcam apps, use Haar when you cannot add dependencies. This guide gives you working code for both and an honest comparison.
What you need
python -m pip install opencv-python mediapipe
Step 1 — Face detection with Haar cascade
The classifier file ships with OpenCV, so there is nothing to download:
import cv2
cascade_path = cv2.data.haarcascades + "haarcascade_frontalface_default.xml"
face_cascade = cv2.CascadeClassifier(cascade_path)
if face_cascade.empty():
raise RuntimeError("Cascade file did not load")
cap = cv2.VideoCapture(0)
while True:
ok, frame = cap.read()
if not ok:
break
gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
faces = face_cascade.detectMultiScale(
gray,
scaleFactor=1.1, # how much the image shrinks each pass
minNeighbors=5, # how much agreement before it counts as a face
minSize=(60, 60), # ignore anything smaller
)
for (x, y, w, h) in faces:
cv2.rectangle(frame, (x, y), (x + w, y + h), (255, 120, 40), 2)
cv2.putText(frame, f"Faces: {len(faces)}", (20, 40),
cv2.FONT_HERSHEY_SIMPLEX, 0.9, (40, 40, 40), 2)
cv2.imshow("Haar", frame)
if cv2.waitKey(1) & 0xFF == ord("q"):
break
cap.release()
cv2.destroyAllWindows()
Two knobs control everything:
scaleFactor—1.1is careful and slower,1.3is faster and misses moreminNeighbors— raise it to kill false positives, lower it to catch more faces
Haar only handles faces looking straight at the camera. Tilt your head 30 degrees and the box usually vanishes.
Step 2 — Face detection with MediaPipe
import cv2
import mediapipe as mp
mp_face = mp.solutions.face_detection
cap = cv2.VideoCapture(0)
with mp_face.FaceDetection(
model_selection=0, # 0 = within 2 m, 1 = up to 5 m
min_detection_confidence=0.6,
) as detector:
while True:
ok, frame = cap.read()
if not ok:
break
h, w = frame.shape[:2]
result = detector.process(cv2.cvtColor(frame, cv2.COLOR_BGR2RGB))
count = 0
if result.detections:
count = len(result.detections)
for det in result.detections:
box = det.location_data.relative_bounding_box
x, y = int(box.xmin * w), int(box.ymin * h)
bw, bh = int(box.width * w), int(box.height * h)
score = det.score[0]
cv2.rectangle(frame, (x, y), (x + bw, y + bh), (255, 120, 40), 2)
cv2.putText(frame, f"{score:.2f}", (x, y - 8),
cv2.FONT_HERSHEY_SIMPLEX, 0.6, (255, 120, 40), 2)
cv2.putText(frame, f"Faces: {count}", (20, 40),
cv2.FONT_HERSHEY_SIMPLEX, 0.9, (40, 40, 40), 2)
cv2.imshow("MediaPipe", frame)
if cv2.waitKey(1) & 0xFF == ord("q"):
break
cap.release()
cv2.destroyAllWindows()
Two differences worth noticing. MediaPipe returns a confidence scoreHow sure the model is, from 0 to 1 you can threshold on, and its box coordinates are fractions of the frame, so you multiply by width and height to get pixels.
Step 3 — Compare them honestly
| Haar cascade | MediaPipe | |
|---|---|---|
| Install | Built into OpenCV | Extra package |
| Speed (720p laptop) | ~30 FPS | ~30 FPS |
| Angled faces | Poor | Good |
| Masks / glasses | Poor | Good |
| Low light | Poor | Fair |
| False positives | Frequent | Rare |
| Confidence score | No | Yes |
| Extra landmarks | No | Eyes, nose, mouth, ears |
| Python version limits | None | 3.8–3.12 |
The decision is simple:
- Webcam app, normal project → MediaPipe
- Locked-down machine, no new installs → Haar
- Batch-processing old photos → either; try Haar first
- You also need eye or nose positions → MediaPipe
Step 4 — Blur faces for privacy
A genuinely useful five-line addition. Blur each detected region before showing or saving the frame:
def blur_region(frame, x, y, w, h):
x, y = max(0, x), max(0, y)
region = frame[y:y + h, x:x + w]
if region.size == 0:
return
frame[y:y + h, x:x + w] = cv2.GaussianBlur(region, (55, 55), 30)
The region.size == 0 check matters: when a face is partly off-screen the slice comes back empty, and blurring an empty array raises an error. That is the same class of bug covered in NoneType is not subscriptable.
Step 5 — Make it fast enough
If your frame rate drops, fix it in this order:
# 1. Process a smaller frame, draw on the big one
small = cv2.resize(frame, (0, 0), fx=0.5, fy=0.5)
# 2. Only detect every other frame
if frame_index % 2 == 0:
faces = detect(small)
# 3. Cap the capture resolution
cap.set(cv2.CAP_PROP_FRAME_WIDTH, 640)
cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 480)
Detecting on a half-size frame roughly quadruples throughput. Just remember to multiply coordinates back up before drawing.
Common problems (and fixes)
| Problem | Fix |
|---|---|
| Haar finds faces in the wall | Raise minNeighbors to 8 |
| Haar misses tilted faces | Switch to MediaPipe; Haar cannot do this |
| MediaPipe misses faces | Set model_selection=1 for greater distance |
| Black window | Another app is using the camera |
| Detection is slow | Resize before detecting |
cascade.empty() is True | Use cv2.data.haarcascades, not a hand-typed path |
What you learned
- Haar cascades need grayscale; MediaPipe needs RGB
scaleFactorandminNeighborsare Haar's only real controls- MediaPipe gives normalised boxes plus a confidence score
- Downscaling before detection is the cheapest speed win
- Blurring detected regions is a small, genuinely useful feature
FAQ
Which is better for face detection, MediaPipe or Haar cascade?
MediaPipe is more accurate, handles angled faces and glasses, and returns a confidence score. Choose Haar cascade only when you cannot install extra packages, since it ships inside OpenCV.
How do I detect faces in a webcam feed with Python?
Read frames with cv2.VideoCapture(0), pass each frame to either CascadeClassifier.detectMultiScale or MediaPipe's FaceDetection.process, then draw a rectangle around each result.
What do scaleFactor and minNeighbors do in detectMultiScale?
scaleFactor sets how much the image shrinks on each detection pass — smaller values are more thorough and slower. minNeighbors sets how many overlapping detections are required before a region counts as a face, so higher values reduce false positives.
Why does Haar cascade detect faces that are not there?
Haar matches simple light-and-dark patterns, which also appear in textures and shadows. Increase minNeighbors and set a sensible minSize to filter them out.
How can I speed up real-time face detection?
Resize frames to half size before detecting, run detection every second frame, and lower the capture resolution with cap.set(cv2.CAP_PROP_FRAME_WIDTH, 640).