news 2026/8/20 13:42:18

opencv学习中——Caffe人脸检测算法原理

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
opencv学习中——Caffe人脸检测算法原理

原先在学Haar级联法识别人脸,发现效果很差,可能是因为识别的是婴儿,加上不是正脸,大模型推荐了Caffe,调用之后效果很好:

1 概念

1.1.1 基础概念

(1)算法、框架与文件的三个概念:

(2)OpenCV DNN模块支持多种框架的模型格式:

cv2.dnn.readNetFromCaffe(prototxt, caffemodel) # ← Caffe 格式 cv2.dnn.readNetFromTensorflow(pb, pbtxt) # ← TensorFlow 格式 cv2.dnn.readNetFromONNX(onnx_path) # ← ONNX 通用格式 cv2.dnn.readNetFromDarknet(cfg, weights) # ← YOLO/Darknet 格式 cv2.dnn.readNetFromTorch(t7_path) # ← Torch 格式

(3)OpenCV DNN 模块最早就是为Caffe设计的,所以大量经典 CV 模型都以 Caffe 格式分发,形成了事实标准。即使今天 PyTorch 已经是训练主流,推理部署时仍然经常转成 Caffe/ONNX 格式

(4)当我们说一个模型时,完整的描述应该包含三层信息:

1.1.2 推理流程

原始图片 → resize到300×300 → 均值减法 → 送入网络 → 前向传播 → 输出检测结果 → NMS去重 → 画框

1.1.2.1 resize到300×300

(1)这个 SSD 模型在训练时就固定了输入尺寸为 300×300。看下图 deploy.prototxt结构文件的第一行定义。网络内部所有层的权重矩阵形状都是基于 300×300 计算出来的。

(2)300 是原论文作者实验后的结果:对人脸这种中等大小目标足够用,同时 CPU 上也能实时跑:

(3)resize会导致图片变形,但 SSD训练时就是这么做的——所有训练图片都被暴力 resize 到 300×300。网络已经学会了在这种变形下依然检测人脸。推理时保持一致就行。(有些改进版 SSD 会用 letterbox padding(等比缩放+填充黑边)来避免变形,但这个经典版本没有。)

1.1.2.2 均值减法

(1)居然真的就是每个像素点减去一个固定常数。它们是ImageNet 数据集120 万张自然图片上所有图片的 BGR 通道平均值。(这个人脸检测模型的 G 通道均值是177而不是标准的 117,说明它是在特定人脸数据集上重新统计的均值。必须用模型配套的均值,不能随便换。

(2)原因有三个:

  • 数据中心化(神经网络本质是做矩阵乘法 + 非线性激活):

  • 让网络别操心"图片有多亮",专心去认"哪里像脸":

  • 训练和推理必须一致:

1.1.2.3 送入网络

(1)指的是ResNet-10 + SSD网络。ResNet-10 有10层网络负责提取特征,同时残差Res可以将前面几层信息一起叠加传递到当层(称为快捷连接,逐段跳跃,每一层收到的是“前一个跳跃起点的输出 + 当前层的输出"”)。

(2)SSD则是在图片上提前撒好各种大小与比例(小框测小脸,大框测大脸,横框测侧脸等)的框(锚框),将"在整张图上找脸"变成了"调整几百个预设框",速度极快。锚框本身不找脸,网络对锚框做"微调"才找到脸。

(3)ResNet-10 是"看得清"(提取特征,且不丢细节),SSD 是"找得快"(用预设框同时检测各种大小的脸)。两者配合 = 又准又快地找到人脸。

(4)这块的详细原理后面学到深度学习要再补充一下,目前只是浅浅的了解

1.1.2.4 前向传播

(1)一层层传递计算结果。先是卷积层内,卷积核在输入图上与每个通道上的每个位置的像素值进行点积,得到的标量值组成新的不同通道的特征图;

(2)之后BatchNorm 层,对每个通道的特征图做归一化;

(3)然后引入非线性的激活函数(ReLu),负值为0,正值保持不变;

(4)再到池化层,比如2×2的池化窗口,步长2滑动,取最大值(或平均值)进行降采样,减少计算量;

(5)最后进入DetectionOutput层,合并特征图(这个项目只有3层,一遍通用的SSD有6层)检测结果,解码锚框偏移量,NMS去除重叠框。

  • 骨干网络太浅: ResNet-10 总共才 10 层,有效语义特征层只有 conv3、conv4、conv5 三个阶段性输出,再往后就没有足够深度的特征了。
  • 人脸检测的特殊性: 人脸是刚性物体,尺度变化相对有限(不像通用目标检测需要覆盖从猫到汽车再到人的巨大跨度),3 个尺度对人脸已经够用。
  • 速度优先: Caffe 部署的人脸检测模型追求实时性,减少检测层数直接降低计算量。

1.1.2.5 输出检测结果

(1)原始输出:detections.shape = (1, 1, N, 7)。N通常是200(由prototxt中keep_top_k参数决定)。即使图中只有1张脸,也会输出200个候选框,其中199个置信度接近0。

  • 第 1 个1 = batch size(推理时通常为1)
  • 第 2 个1 = 固定占位符(Caffe SSD 的历史设计)
  • 这两维永远是 1,没有实际意义,所以开发者习惯直接用[0, 0]把它们"剥掉",拿到干净的(N, 7)矩阵方便后续处理。

(2)‘7’是检测7个字段。

1.1.2.6 NMS去重

(1)1张脸可能被好几个框覆盖,需要最好的那一个,NMS就是用来去重的。

(2)NMS依赖IoU来衡量两个框的重叠程度。

(3)具体算法步骤:

(4)局限性:

(5)改进方案:Soft-NMS、DIoU-NMS、Adaptive-NMS 等,但都不在这个经典模型中使用。回头可以试下这些方案

1.1.2.7 画框

2 代码

import cv2 import numpy as np import os # ============ 路径配置 ============ pic_dir = r'C:\D\picture' img_path = os.path.join(pic_dir, 'alex-005.jpg') proto_path = os.path.join(pic_dir, 'deploy.prototxt') model_path = os.path.join(pic_dir, 'res10_300x300_ssd_iter_140000.caffemodel') # ============ 第一步:加载 DNN 模型 ============ net = cv2.dnn.readNetFromCaffe(proto_path, model_path) print('Caffe模型加载成功') # ============ 第二步:读取图片 ============ img = cv2.imread(img_path) if img is None: raise IOError(f'图片读取失败: {img_path}') h, w = img.shape[:2] print(f'图片尺寸: {w} x {h}') # ============ 第三步:预处理 ============ # 将图片缩放到 300x300,并做均值减法(Caffe 模型要求的预处理) blob = cv2.dnn.blobFromImage( cv2.resize(img, (300, 300)), # 模型输入尺寸固定 300x300 scalefactor=1.0, size=(300, 300), mean=(104.0, 177.0, 123.0), # BGR 均值减法 swapRB=False, crop=False ) # ============ 第四步:前向推理 ============ net.setInput(blob) detections = net.forward() # detections shape: (1, 1, N, 7) # 每个检测: [batch_id, class_id, confidence, x1, y1, x2, y2] # ============ 第五步:解析结果并画框 ============ confidence_threshold = 0.5 # 置信度阈值,可根据需要调整 face_count = 0 for i in range(detections.shape[2]): confidence = detections[0, 0, i, 2] if confidence > confidence_threshold: face_count += 1 # 坐标是归一化的 [0,1],需要映射回原图尺寸 box = detections[0, 0, i, 3:7] * np.array([w, h, w, h]) (x1, y1, x2, y2) = box.astype(int) # 画绿色矩形框 cv2.rectangle(img, (x1, y1), (x2, y2), (0, 255, 0), 20) # 在框上方标注置信度 label = f'{confidence:.2f}' label_size, _ = cv2.getTextSize(label, cv2.FONT_HERSHEY_SIMPLEX, 0.6, 1) cv2.rectangle(img, (x1, y1 - label_size[1] - 6), (x1 + label_size[0], y1), (0, 255, 0), -1) cv2.putText(img, label, (x1, y1 - 4), cv2.FONT_HERSHEY_SIMPLEX, 0.6, (255, 255, 255), 1) print(f' 人脸 #{face_count}: 置信度={confidence:.3f}, 位置=({x1},{y1})-({x2},{y2})') print(f'共检测到 {face_count} 张人脸') # ============ 第六步:显示结果 ============ cv2.namedWindow('Face Detection (DNN)', cv2.WINDOW_NORMAL) cv2.resizeWindow('Face Detection (DNN)', 550, 850) while True: cv2.imshow('Face Detection (DNN)', img) key = cv2.waitKey(1) & 0xFF if key == ord('q'): break cv2.destroyAllWindows() # 可选:保存结果 # output_path = os.path.join(pic_dir, 'result_dnn.jpg') # cv2.imwrite(output_path, img) # print(f'结果已保存: {output_path}')

3 感想

(1)最近蛮焦虑的,和大模型聊天,发现计算机视觉这块要学的还有好多,去力扣上刷题,刷的也好艰难。不过好在,每天都在学一点,也在坚持记录,面试前还能临时抱下佛脚~

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/8/20 13:41:10

LLM智能体如何自动化分子动力学模拟:MDForge项目解析

1. 项目概述:当分子动力学遇上智能体 最近在计算化学和材料模拟的圈子里,一个概念正在被频繁讨论:如何让大语言模型(LLM)驱动的智能体(Agent)去自动化执行复杂的科学计算流程。这听起来像是科幻…

作者头像 李华
网站建设 2026/8/20 13:40:59

从零搭建AI服务器:硬件选型、环境配置与深度学习框架部署实战

AI服务器在近两年成为技术圈和投资圈的热点,其背后是生成式AI、大模型训练和推理需求的爆发式增长。对于开发者、架构师和运维工程师而言,理解AI服务器的核心构成、部署流程以及如何从零开始搭建一个可用于模型训练或推理的AI服务器环境,是一…

作者头像 李华
网站建设 2026/8/20 13:39:28

考研复试训练:专业能力与面试表达提升策略

1. 复试训练的核心价值与定位 复试训练是每位考研学子在初试通过后面临的关键战役。与初试侧重考察基础知识不同,复试更注重综合素质和专业能力的立体呈现。根据我多年辅导经验,复试成绩往往能拉开30%以上的分差,这在竞争激烈的热门院校中直接…

作者头像 李华
网站建设 2026/8/20 13:37:58

QQ飞车.luc文件反编译与VFS资源管理工具实战指南

这次我们来看一个专门处理《QQ飞车》游戏资源文件的工具。这个项目的核心目标非常直接:它能够将游戏中的 .luc 格式文件,逆向解析成人类可读的 .lua 脚本。对于游戏开发者、逆向爱好者,或者想研究《QQ飞车》客户端逻辑的人来说&#xff0…

作者头像 李华