2. 人脸检测进阶

任务说明

本案例使用预训练的深度学习模型进行人脸检测,并与OpenCV自带的Haar Cascade检测器进行性能对比。你将获得一个可直接运行的Python脚本,它读取同一张测试图片,分别用三种方法检测人脸:Haar Cascade、OpenCV DNN加载的SSD模型(ResNet-10骨干网络),以及MTCNN(若已安装)。输出结果包括每张人脸的位置、置信度(深度学习模型)以及检测耗时,最终在图片上绘制检测框并保存对比图。

实现方案

代码采用模块化结构,核心函数为detect_haar、detect_ssd和detect_mtcnn。SSD模型使用OpenCV的cv2.dnn模块加载Caffe格式的预训练权重(res10_300x300_ssd_iter_140000.caffemodel)及其配置文件(deploy.prototxt)。MTCNN通过mtcnn库调用,若未安装则自动跳过并提示。Haar Cascade使用OpenCV自带的haarcascade_frontalface_default.xml。三种方法均返回检测框列表(格式为[x, y, w, h]),SSD和MTCNN额外返回置信度分数。主程序对同一输入图片依次调用三个检测器,记录各自耗时,并生成标注了检测结果的对比图。

代码实现

import cv2
import time
import numpy as np
import os

# ========== 配置 ==========
IMAGE_PATH = "test_face.jpg"          # 输入图片(请放置于同目录)
HAAR_CASCADE_PATH = cv2.data.haarcascades + "haarcascade_frontalface_default.xml"
SSD_PROTOTXT = "deploy.prototxt"      # SSD模型配置文件
SSD_MODEL = "res10_300x300_ssd_iter_140000.caffemodel"  # SSD预训练权重
CONF_THRESHOLD = 0.5                  # SSD置信度阈值

# ========== 检测函数 ==========
def detect_haar(img_gray):
    """Haar Cascade人脸检测,返回框列表 [x, y, w, h]"""
    cascade = cv2.CascadeClassifier(HAAR_CASCADE_PATH)
    faces = cascade.detectMultiScale(img_gray, scaleFactor=1.1, minNeighbors=5, minSize=(30, 30))
    return faces.tolist() if len(faces) > 0 else []

def detect_ssd(img):
    """OpenCV DNN加载SSD模型,返回 (框列表, 置信度列表)"""
    net = cv2.dnn.readNetFromCaffe(SSD_PROTOTXT, SSD_MODEL)
    h, w = img.shape[:2]
    blob = cv2.dnn.blobFromImage(cv2.resize(img, (300, 300)), 1.0, (300, 300), (104.0, 177.0, 123.0))
    net.setInput(blob)
    detections = net.forward()
    boxes, scores = [], []
    for i in range(detections.shape[2]):
        confidence = detections[0, 0, i, 2]
        if confidence > CONF_THRESHOLD:
            box = detections[0, 0, i, 3:7] * np.array([w, h, w, h])
            x1, y1, x2, y2 = box.astype("int")
            boxes.append([x1, y1, x2 - x1, y2 - y1])
            scores.append(float(confidence))
    return boxes, scores

def detect_mtcnn(img_rgb):
    """MTCNN人脸检测,返回 (框列表, 置信度列表);未安装则返回空"""
    try:
        from mtcnn import MTCNN
        detector = MTCNN()
        results = detector.detect_faces(img_rgb)
        boxes, scores = [], []
        for res in results:
            x, y, w, h = res["box"]
            boxes.append([x, y, w, h])
            scores.append(float(res["confidence"]))
        return boxes, scores
    except ImportError:
        print("[提示] 未安装 mtcnn 库,跳过MTCNN检测。安装命令: pip install mtcnn")
        return [], []

# ========== 主流程 ==========
def main():
    if not os.path.exists(IMAGE_PATH):
        print(f"错误:找不到图片 {IMAGE_PATH},请将测试图片放置于当前目录。")
        return

    img = cv2.imread(IMAGE_PATH)
    img_rgb = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
    img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

    # 1. Haar检测
    t0 = time.time()
    haar_boxes = detect_haar(img_gray)
    haar_time = time.time() - t0

    # 2. SSD检测
    t0 = time.time()
    ssd_boxes, ssd_scores = detect_ssd(img)
    ssd_time = time.time() - t0

    # 3. MTCNN检测
    t0 = time.time()
    mtcnn_boxes, mtcnn_scores = detect_mtcnn(img_rgb)
    mtcnn_time = time.time() - t0

    # 输出统计信息
    print(f"Haar 检测: {len(haar_boxes)} 张脸, 耗时 {haar_time*1000:.1f} ms")
    print(f"SSD  检测: {len(ssd_boxes)} 张脸, 耗时 {ssd_time*1000:.1f} ms")
    if mtcnn_boxes:
        print(f"MTCNN检测: {len(mtcnn_boxes)} 张脸, 耗时 {mtcnn_time*1000:.1f} ms")
        for i, s in enumerate(mtcnn_scores):
            print(f"  - 第{i+1}张脸置信度: {s:.3f}")

    # 绘制对比结果
    img_haar = img.copy()
    for (x, y, w, h) in haar_boxes:
        cv2.rectangle(img_haar, (x, y), (x+w, y+h), (0, 255, 0), 2)

    img_ssd = img.copy()
    for (x, y, w, h), s in zip(ssd_boxes, ssd_scores):
        cv2.rectangle(img_ssd, (x, y), (x+w, y+h), (0, 0, 255), 2)
        cv2.putText(img_ssd, f"{s:.2f}", (x, y-5), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 0, 255), 1)

    img_mtcnn = img.copy()
    for (x, y, w, h), s in zip(mtcnn_boxes, mtcnn_scores):
        cv2.rectangle(img_mtcnn, (x, y), (x+w, y+h), (255, 0, 0), 2)
        cv2.putText(img_mtcnn, f"{s:.2f}", (x, y-5), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255, 0, 0), 1)

    # 拼接并保存
    top = np.hstack([img_haar, img_ssd])
    bottom = np.hstack([img_mtcnn, np.zeros_like(img_mtcnn)]) if mtcnn_boxes else np.hstack([img_mtcnn, img_mtcnn])
    result = np.vstack([top, bottom])
    cv2.imwrite("comparison_result.jpg", result)
    print("对比结果已保存为 comparison_result.jpg")

if __name__ == "__main__":
    main()

文件与依赖

  • 模型文件(需提前下载并放置于脚本同目录):deploy.prototxt 和 res10_300x300_ssd_iter_140000.caffemodel(可从OpenCV官方GitHub获取)
  • Python库:opencv-python、numpy;可选 mtcnn(需TensorFlow支持)
  • 测试图片:test_face.jpg(建议包含1~3张正面或轻微侧转的人脸)

运行与验证

  1. 将上述代码保存为 face_detection_compare.py,并确保模型文件和测试图片位于同一目录。
  2. 在终端运行:python face_detection_compare.py
  3. 预期输出:控制台打印三种方法的检测数量与耗时;若MTCNN可用,还会打印每张脸的置信度。同时生成 comparison_result.jpg,其中四宫格分别显示Haar(绿框)、SSD(红框+置信度)、MTCNN(蓝框+置信度)的检测结果。
  4. 判定标准:观察对比图,深度学习模型(SSD/MTCNN)在侧脸、遮挡或光照变化下应比Haar检测到更多有效人脸;SSD和MTCNN标注的置信度数值应介于0到1之间。若Haar出现漏检而SSD/MTCNN正确检出,即说明深度学习方法的优势得到体现。
三种人脸检测方法对比流程 输入图片 test_face.jpg Haar Cascade haarcascade_frontalface OpenCV DNN + SSD ResNet-10 骨干 MTCNN mtcnn 库(可选) 框列表 无置信度 框 + 置信度 阈值 > 0.5 框 + 置信度 自带置信度 对比结果 耗时统计 + comparison_result.jpg 性能指标: • 检测数量 • 单张耗时(ms) • 置信度分数

性能差异说明

Haar Cascade基于手工设计的哈尔特征与级联分类器,速度较快但对光照、姿态变化敏感,容易漏检或误检。SSD(ResNet-10)通过卷积神经网络直接回归人脸位置,对遮挡和角度变化鲁棒性更好,且能输出置信度便于过滤。MTCNN采用三阶段级联网络,先粗后精地回归人脸框与关键点,在小脸和密集场景下通常精度最高,但速度相对较慢。实际对比时,建议使用包含侧脸、戴眼镜或复杂背景的图片,以观察深度学习模型的优势。