Sythra

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.

vaibhavkothari· Aug 26, 2026· 6 min readIntermediate
ShareXLinkedIn
Real-time face detection in Python: MediaPipe vs Haar cascade cover

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:

  • scaleFactor1.1 is careful and slower, 1.3 is faster and misses more
  • minNeighbors — 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 cascadeMediaPipe
InstallBuilt into OpenCVExtra package
Speed (720p laptop)~30 FPS~30 FPS
Angled facesPoorGood
Masks / glassesPoorGood
Low lightPoorFair
False positivesFrequentRare
Confidence scoreNoYes
Extra landmarksNoEyes, nose, mouth, ears
Python version limitsNone3.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)

ProblemFix
Haar finds faces in the wallRaise minNeighbors to 8
Haar misses tilted facesSwitch to MediaPipe; Haar cannot do this
MediaPipe misses facesSet model_selection=1 for greater distance
Black windowAnother app is using the camera
Detection is slowResize before detecting
cascade.empty() is TrueUse cv2.data.haarcascades, not a hand-typed path

What you learned

  • Haar cascades need grayscale; MediaPipe needs RGB
  • scaleFactor and minNeighbors are 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).

Next reading on Sythra Articles

Keep reading

Related articles

Newsletter

Get new articles

Free essays on learning, Python, and ML — no account required. We’ll only email when there’s something worth reading.