#!/usr/bin/env python3 import argparse from pathlib import Path import cv2 as cv default_cascade = ( Path(cv.data.haarcascades) / "haarcascade_frontalface_default.xml" ) parser = argparse.ArgumentParser( description="Detect frontal faces with an OpenCV Haar cascade." ) parser.add_argument("image", help="input image path") parser.add_argument( "--output", default="output/faces.jpg", help="annotated image path to write", ) parser.add_argument( "--cascade", default=str(default_cascade), help="Haar cascade XML file", ) parser.add_argument( "--scale-factor", type=float, default=1.1, help="image pyramid step used by detectMultiScale", ) parser.add_argument( "--min-neighbors", type=int, default=5, help="neighbor count required to keep a detection", ) args = parser.parse_args() image_path = Path(args.image) output_path = Path(args.output) cascade_path = Path(args.cascade) image = cv.imread(str(image_path)) if image is None: raise SystemExit(f"Could not read image: {image_path}") classifier = cv.CascadeClassifier(str(cascade_path)) if classifier.empty(): raise SystemExit(f"Could not load cascade: {cascade_path}") gray = cv.cvtColor(image, cv.COLOR_BGR2GRAY) gray = cv.equalizeHist(gray) faces = classifier.detectMultiScale( gray, scaleFactor=args.scale_factor, minNeighbors=args.min_neighbors, minSize=(30, 30), ) annotated = image.copy() for x, y, width, height in faces: cv.rectangle( annotated, (x, y), (x + width, y + height), (0, 255, 0), 3, ) output_path.parent.mkdir(parents=True, exist_ok=True) if not cv.imwrite(str(output_path), annotated): raise SystemExit(f"Could not write output image: {output_path}") height, width = image.shape[:2] print(f"cascade: {cascade_path.name}") print(f"image size: {width}x{height}") print(f"faces detected: {len(faces)}") for index, (x, y, face_width, face_height) in enumerate(faces, start=1): print( f"face {index}: x={x} y={y} " f"width={face_width} height={face_height}" ) print(f"wrote: {output_path}")