MOHAMMED ANAS IQBAL
Add face mask detection app
97781c5
Raw
History Blame Contribute Delete
4.04 kB
import os
import urllib.request
import numpy as np
import cv2
from pathlib import Path
from tensorflow.keras.applications.mobilenet_v2 import preprocess_input
BASE_DIR = Path(__file__).resolve().parent.parent
FACE_DETECTOR_DIR = BASE_DIR / "models" / "face_detector"
FACE_DETECTOR_FILES = {
"deploy.prototxt": (
"https://raw.githubusercontent.com/opencv/opencv/master/"
"samples/dnn/face_detector/deploy.prototxt"
),
"res10_300x300_ssd_iter_140000.caffemodel": (
"https://raw.githubusercontent.com/opencv/opencv_3rdparty/"
"dnn_samples_face_detector_20170830/"
"res10_300x300_ssd_iter_140000.caffemodel"
),
}
CLASSES = ["with_mask", "without_mask"]
def download_face_detector(dest_dir: Path = FACE_DETECTOR_DIR) -> None:
dest_dir.mkdir(parents=True, exist_ok=True)
for filename, url in FACE_DETECTOR_FILES.items():
dest = dest_dir / filename
if dest.exists():
print(f"[OK] {filename} already exists, skipping download.")
continue
print(f"[DOWNLOADING] {filename} ...")
urllib.request.urlretrieve(url, dest)
print(f"[DONE] Saved to {dest}")
def load_face_detector(model_dir: Path = FACE_DETECTOR_DIR):
prototxt = str(model_dir / "deploy.prototxt")
caffemodel = str(model_dir / "res10_300x300_ssd_iter_140000.caffemodel")
if not os.path.exists(prototxt) or not os.path.exists(caffemodel):
raise FileNotFoundError(
"Face detector model files not found. "
"Run: python src/utils.py --download-face-detector"
)
return cv2.dnn.readNet(prototxt, caffemodel)
def detect_faces(frame: np.ndarray, net, confidence_threshold: float = 0.5):
"""
Returns list of (startX, startY, endX, endY) for each face found.
Adds 5% padding around each detected face region.
"""
h, w = frame.shape[:2]
blob = cv2.dnn.blobFromImage(
cv2.resize(frame, (300, 300)), 1.0, (300, 300),
(104.0, 177.0, 123.0)
)
net.setInput(blob)
detections = net.forward()
faces = []
for i in range(detections.shape[2]):
confidence = detections[0, 0, i, 2]
if confidence < confidence_threshold:
continue
box = detections[0, 0, i, 3:7] * np.array([w, h, w, h])
startX, startY, endX, endY = box.astype(int)
# Add 5% padding for better crop context
pad_x = int((endX - startX) * 0.05)
pad_y = int((endY - startY) * 0.05)
startX = max(0, startX - pad_x)
startY = max(0, startY - pad_y)
endX = min(w, endX + pad_x)
endY = min(h, endY + pad_y)
faces.append((startX, startY, endX, endY))
return faces
def preprocess_face(face_roi: np.ndarray, target_size: tuple = (224, 224)) -> np.ndarray:
face = cv2.resize(face_roi, target_size)
face = cv2.cvtColor(face, cv2.COLOR_BGR2RGB)
face = preprocess_input(face.astype("float32")) # → [-1, 1] for MobileNetV2
return np.expand_dims(face, axis=0)
def get_label_color(label: str) -> tuple:
return (0, 255, 0) if label == "with_mask" else (0, 0, 255)
def load_dataset_paths(dataset_dir: Path):
"""
Returns (image_paths, labels) lists from a folder structured as:
dataset_dir/
with_mask/
without_mask/
"""
image_paths, labels = [], []
for class_name in CLASSES:
class_dir = dataset_dir / class_name
if not class_dir.exists():
print(f"[WARNING] Directory not found: {class_dir}")
continue
for ext in ("*.jpg", "*.jpeg", "*.png"):
for img_path in class_dir.glob(ext):
image_paths.append(str(img_path))
labels.append(class_name)
return image_paths, labels
if __name__ == "__main__":
import argparse
parser = argparse.ArgumentParser()
parser.add_argument("--download-face-detector", action="store_true")
args = parser.parse_args()
if args.download_face_detector:
download_face_detector()