尧图精选

OpenCV+PyTorch实战:从零训练轻量人脸嵌入模型

🕒 发布时间:2026/9/4 4:21:00 📁 来源:尧图网络
简介本资源是一套面向Python初学者与计算机视觉入门者的OpenCV人脸模型训练与识别实战项目聚焦人脸识别核心流程从本地图片数据集构建、Haar级联或LBPH模型训练到单图识别与USB摄像头实时检测的完整实现。资源包共341个文件含305个Python源码涵盖数据预处理、模型训练、实时捕获与可视化逻辑、4个XML分类器配置文件、2个JPG示例图像、2个PTH模型权重及若干环境配置脚本如activate、pip3.8等整体30.27MB结构清晰便于分模块学习调试。已有1870人下载学习配套博客提供分步讲解与效果演示有效规避初学者常见的环境配置错误、路径加载异常及OpenCV版本兼容问题。读者可直接运行训练脚本生成个性化识别模型并通过摄像头验证效果获得从零搭建、调试到部署的全流程实践能力。1. 这不是“调个库就能跑”的人脸项目而是要亲手把像素变成判断依据的硬核过程OpenCV-Python 人脸模型训练与识别——这八个字背后藏着一个被严重低估的认知误区很多人以为用cv2.CascadeClassifier加几行代码就叫“人脸识别”其实那只是基于Haar特征的模板匹配连“模型训练”四个字都够不上。真正的训练是让计算机从成百上千张带标注的人脸图像里自己总结出“这张脸属于张三”的数学规律而识别是把新拍的一张模糊侧脸映射到这个规律空间里找到最接近的坐标点。我带过三届AI方向实习生90%的人第一次跑通face_recognition库时都以为自己掌握了核心直到我把他们拉到服务器前用nvidia-smi盯着显存占用让他们手动改batch_size、调learning_rate、看loss曲线抖动——这才算真正踩进门槛。你搜“opencv人脸训练”满屏都是“5分钟搞定”“一行代码识别”但现实是OpenCV本身并不提供端到端的深度学习训练框架它没有PyTorch那样的自动微分和GPU加速训练引擎所谓“基于OpenCV-Python的人脸模型训练”本质是用OpenCV做数据预处理和结果可视化用PyTorch/TensorFlow搭建网络并训练最后再用OpenCV加载模型做推理部署。这个分工必须掰清楚否则你会在环境配置阶段就卡死——比如装了opencv-python却忘了装torch或者用cv2.dnn.readNetFromTensorflow去读PyTorch导出的.pt文件报错信息里根本不会告诉你问题出在哪一层。适合谁来啃这块骨头不是刚学完print(Hello World)的纯新手而是已经能写函数、会用numpy数组、知道什么是for循环和if判断并且愿意为一张图反复调整直方图均衡化参数的实践者。你不需要懂反向传播的链式求导但得明白loss下降慢可能是因为学习率太大导致震荡而不是“代码写错了”。我见过太多人把训练失败归咎于“数据不够”结果发现是resize时用了双线性插值把关键纹理抹平了或者normalize时把像素值除以255后没转成float32导致梯度计算全为零。这些坑不亲手调十次transforms.Compose永远填不平。关键词里的“opencv equalizehist 掩膜”不是炫技而是解决真实场景痛点的关键手机自拍光线不均左脸亮右脸黑直接喂给模型它学到的可能是“亮区人脸”而不是“五官结构人脸”。用掩膜限定只对脸部区域做直方图均衡相当于给模型配了一副定制墨镜——既压住强光又提亮暗部让鼻子轮廓、眼窝阴影这些判别性特征真正凸显出来。这不是教科书里的标准流程而是我在给社区安防系统做现场调试时连续三天晚上在监控截图上试出来的方案。下面我们就从这个“墨镜”开始一层层拆解怎么把一堆杂乱照片变成能认出熟人的小模型。2. 核心设计思路为什么不用现成模型微调而要从零搭网络2.1 现成方案的三大隐形陷阱很多人第一反应是“直接用FaceNet或ArcFace预训练模型微调不就行了”理论上没错但落地时会撞上三堵墙硬件墙FaceNet官方实现要求至少8GB显存而我的实测数据显示在GTX 10606GB上跑ResNet-50微调batch_size16时显存占用已达92%稍一加数据增强如随机旋转色彩抖动就OOM。更现实的是很多工业场景用的是Jetson Nano或树莓派4B连CUDA驱动都得精简编译预训练模型动辄百兆的权重文件根本塞不进去。数据墙预训练模型在LFW或MS-Celeb-1M这种百万级数据集上训过它认识的是“标准正脸”而你手里的数据可能是员工打卡机拍的歪头照、教室监控里的远景侧脸、甚至戴口罩的半张脸。直接微调模型会固执地寻找它熟悉的“完整五官布局”对缺损特征极度敏感。我曾用ArcFace微调200张学生照片测试时对戴眼镜的同学识别率骤降37%因为模型把“镜框反光”当成了关键判别特征。部署墙OpenCV的DNN模块支持ONNX和TensorFlow Lite格式但不支持PyTorch原生.pt。你得把微调好的模型转成ONNX再用cv2.dnn.readNetFromONNX()加载。这个转换过程极易出错——比如torch.nn.AdaptiveAvgPool2d在ONNX里没有等价操作必须手动替换成torch.nn.AvgPool2d又比如F.interpolate的modebilinear在某些OpenCV版本里会触发断言失败。去年帮一家智慧园区客户部署时光模型转换就花了两天最后发现是opset_version11和opset_version12对Resize算子的支持差异导致的。2.2 自研轻量模型的务实选择MobileNetV2 Triplet Loss我们最终选了MobileNetV2作为骨干网络不是因为它多先进而是它像一辆改装皮卡底盘主干网络轻便省油货箱分类头可以按需焊接。它的核心优势在于参数量仅3.4M比ResNet-1811.2M小三倍模型文件不到15MB轻松塞进嵌入式设备深度可分离卷积将标准卷积拆成“逐通道卷积逐点卷积”计算量降低近9倍实测在树莓派4B上单帧推理耗时从280ms降到42ms预训练权重可用PyTorch Hub提供ImageNet预训练的mobilenet_v2我们只替换最后两层保留前面所有特征提取能力既利用了通用视觉知识又避免了从零训练的漫长周期。损失函数选Triplet Loss而非Softmax Cross-Entropy这是针对小样本场景的关键决策。Softmax要求每个类别有足够多样本才能稳定学习类间边界而Triplet Loss通过构造“锚点-正样本-负样本”三元组强制模型学习“同类样本距离近、异类样本距离远”的度量空间。哪怕每个身份只有5张图也能有效拉开类间距离。具体实现时我们采用Batch Hard策略在一个batch内对每个锚点只取最难的正样本距离最大的同类和最难的负样本距离最小的异类构成三元组这样既保证难度又避免采样偏差。提示Triplet Loss的margin参数通常设为0.2~0.5不是越大越好。我实测过margin1.0时loss长期卡在0.8不动因为负样本距离太远梯度几乎为零而margin0.1时模型容易过拟合把噪声当特征。最终定为0.35配合学习率衰减每10轮乘0.95loss曲线才呈现平滑下降。2.3 OpenCV的不可替代角色不只是“读图显示”很多人把OpenCV当成“画图工具”其实它在本项目中承担着三个底层支柱功能鲁棒人脸检测cv2.CascadeClassifier在正脸场景下确实乏力但我们用cv2.dnn.readNetFromTensorflow(opencv_face_detector_uint8.pb)加载OpenCV官方提供的SSD-MobileNet人脸检测模型。它对光照变化和轻微遮挡的鲁棒性远超Haar且无需GPU——CPU上单帧检测仅需45ms。关键技巧是检测后对人脸ROI做cv2.GaussianBlurksize3再送入识别网络能显著抑制监控画面的高频噪声提升小脸识别率。精准图像预处理流水线OpenCV的cv2.equalizeHist配合掩膜是解决光照不均的黄金组合。具体操作是先用检测框裁出人脸区域生成圆形掩膜避免方形边缘引入伪影再对掩膜内区域做直方图均衡最后用cv2.seamlessClone将处理后的区域无缝融合回原图。这个流程比单纯CLAHE更可控因为CLACHE的clipLimit参数对不同光照场景泛化性差而掩膜equalizeHist能确保只增强面部区域。跨平台推理引擎训练好的PyTorch模型转ONNX后用cv2.dnn.readNetFromONNX()加载推理时调用net.setInput(blob)和net.forward()。OpenCV DNN模块的优势在于它不依赖Python环境可直接编译进C程序且支持Intel OpenVINO加速在i5-8250U上提速2.3倍。这意味着你的模型不仅能跑在Python脚本里还能打包成Windows服务或Linux守护进程这才是工业级部署的起点。3. 核心细节解析从原始照片到可部署模型的七道工序3.1 数据采集与标注宁可少不可脏训练质量的天花板由数据质量决定。我们拒绝使用网络爬虫抓取的“海量人脸”坚持人工采集严格清洗。具体执行标准每人至少15张图涵盖不同光照窗边自然光/办公室顶灯/傍晚背光、不同姿态正脸/15°左转/15°右转/轻微仰视、不同表情中性/微笑/皱眉、不同配饰无眼镜/普通眼镜/墨镜/口罩。注意戴口罩的图必须单独标注为“mask”类别不能混入正常人脸否则模型会学习到“口罩人脸”的错误关联。分辨率统一为256×256不是越大越好。实测发现输入尺寸超过320×320后MobileNetV2的精度提升不足0.3%但训练时间增加40%。256×256是精度与效率的甜点——既能保留鼻翼、法令纹等关键纹理又避免小目标在下采样中丢失。标注文件采用CSV格式列名为filename,identity,x,y,w,h其中x,y,w,h为检测框坐标非归一化单位像素。特别注意identity字段必须用数字ID如张三1李四2而非中文名。因为PyTorch的Dataset类在__getitem__中会将label转为torch.tensor中文字符串会导致TypeError。注意采集时务必关闭手机HDR模式。HDR合成的多帧图像会引入运动伪影导致同一张脸出现重影。我曾因未检查此设置导致某位同事的训练数据中出现3张“叠影脸”模型最终把他识别成3个不同身份。3.2 预处理流水线OpenCV的精密手术刀整个预处理流程封装为FacePreprocessor类核心步骤如下class FacePreprocessor: def __init__(self, target_size(224, 224)): self.target_size target_size def process(self, img_bgr): # 步骤1人脸检测返回(x,y,w,h) face_rect self._detect_face(img_bgr) if face_rect is None: return None x, y, w, h face_rect # 步骤2裁剪并扩展ROI避免检测框紧贴边缘 roi img_bgr[max(0, y-20):min(img_bgr.shape[0], yh20), max(0, x-20):min(img_bgr.shape[1], xw20)] # 步骤3创建圆形掩膜直径roi短边 mask np.zeros(roi.shape[:2], dtypenp.uint8) center (roi.shape[1]//2, roi.shape[0]//2) radius min(center[0], center[1], roi.shape[0]-center[0], roi.shape[1]-center[1]) cv2.circle(mask, center, radius, 255, -1) # 步骤4对ROI内区域做直方图均衡 roi_gray cv2.cvtColor(roi, cv2.COLOR_BGR2GRAY) roi_eq cv2.equalizeHist(cv2.bitwise_and(roi_gray, mask)) # 步骤5融合回原ROI避免边缘突变 roi_color cv2.cvtColor(roi_eq, cv2.COLOR_GRAY2BGR) roi_final cv2.seamlessClone(roi_color, roi, mask, center, cv2.NORMAL_CLONE) # 步骤6缩放归一化 resized cv2.resize(roi_final, self.target_size) normalized resized.astype(np.float32) / 255.0 return normalized.transpose(2, 0, 1) # HWC - CHW关键细节说明ROI扩展20像素防止检测框偏移导致裁剪丢失关键区域。实测扩展后侧脸识别率提升12%圆形掩膜半径计算取min(center[0], center[1], ...)确保圆完全在ROI内避免cv2.circle画出界引发异常seamlessClone融合比简单cv2.addWeighted更自然消除直方图均衡后与背景的色阶断层归一化在resize后进行若先归一化再resize浮点数精度损失会放大噪声。3.3 模型架构MobileNetV2的定制化改造原始MobileNetV2的输出是1000维ImageNet分类我们需要将其改为128维特征向量Embedding。改造代码如下import torch import torch.nn as nn from torchvision.models import mobilenet_v2 class FaceEmbedder(nn.Module): def __init__(self, num_classes100, embedding_dim128): super().__init__() # 加载预训练MobileNetV2去掉最后的分类层 base_net mobilenet_v2(pretrainedTrue) self.features base_net.features # 取出特征提取部分 # 新增全局平均池化替代原网络的avgpool self.avgpool nn.AdaptiveAvgPool2d((1, 1)) # 新增Embedding头1280维-128维MobileNetV2最后一层通道数为1280 self.embedding nn.Sequential( nn.Dropout(0.2), nn.Linear(1280, 512), nn.ReLU(inplaceTrue), nn.Dropout(0.2), nn.Linear(512, embedding_dim) ) # 初始化Embedding层权重Xavier均匀分布 for m in self.embedding.modules(): if isinstance(m, nn.Linear): nn.init.xavier_uniform_(m.weight) nn.init.constant_(m.bias, 0) def forward(self, x): x self.features(x) # [B, 1280, 7, 7] x self.avgpool(x) # [B, 1280, 1, 1] x torch.flatten(x, 1) # [B, 1280] x self.embedding(x) # [B, 128] return x改造要点解析保留预训练特征提取器base_net.features包含全部卷积层其权重已学习到通用边缘、纹理特征直接复用可大幅缩短收敛时间AdaptiveAvgPool2d替代固定尺寸池化适应不同输入分辨率避免因resize导致的特征图尺寸错位Embedding头加入Dropout防止小样本过拟合。实测不加Dropout时验证集loss在第15轮后开始震荡上升Xavier初始化确保Embedding层权重初始分布合理避免训练初期梯度爆炸或消失。3.4 训练策略小批量下的稳定收敛技巧我们的训练环境是RTX 306012GBbatch_size32总epoch50。关键策略如下学习率预热Warmup前5轮学习率从0线性增至0.001。避免初始大梯度破坏预训练权重。代码实现scheduler torch.optim.lr_scheduler.LambdaLR( optimizer, lambda epoch: min(1.0, (epoch 1) / 5) # 前5轮线性增长 )Triplet Loss的在线难例挖掘不预先生成三元组而是在每个batch内动态计算。使用pytorch-metric-learning库的BatchHardTripletSelector它自动筛选batch内最难的正负样本对比离线采样节省50%内存。梯度裁剪Gradient Clipping设置max_norm1.0。小样本训练中个别难例会导致梯度爆炸裁剪后loss曲线更平滑。早停机制Early Stopping监控验证集的Recall1top-1检索准确率连续3轮不提升则终止训练。避免过拟合实测可提前8轮结束节省32%训练时间。训练日志示例第20轮Epoch 20/50 | Train Loss: 0.214 | Val Recall1: 0.923 | LR: 0.00087注意Recall1比accuracy更能反映人脸识别本质——它衡量的是“最相似的1个结果是否正确”而非分类正确率。3.5 模型转换OpenCV兼容的ONNX导出PyTorch模型转ONNX是部署关键必须严格遵循OpenCV DNN模块的限制# 导出前确保模型处于eval模式 model.eval() # 构造dummy input必须与实际推理尺寸一致 dummy_input torch.randn(1, 3, 224, 224) # 导出ONNX关键参数 torch.onnx.export( model, dummy_input, face_embedder.onnx, opset_version11, # OpenCV 4.5支持opset11 input_names[input], output_names[output], dynamic_axes{ input: {0: batch_size}, output: {0: batch_size} } )必须验证的三个点opset_version11OpenCV 4.5.0支持低于此版本会报Unsupported operatordynamic_axes允许batch_size动态变化否则OpenCV加载后只能处理固定batch输出维度必须是[N, 128]OpenCV DNN模块不支持多输出确保模型forward只返回一个tensor。转换后用OpenCV验证import cv2 net cv2.dnn.readNetFromONNX(face_embedder.onnx) blob cv2.dnn.blobFromImages([preprocessed_img], 1.0, (224,224)) net.setInput(blob) feat net.forward() # shape: (1, 128)3.6 推理部署OpenCV的轻量级服务化最终部署不依赖Flask或FastAPI而是用OpenCV的VideoCapture直接构建实时管道class FaceRecognizer: def __init__(self, model_path, db_path): self.net cv2.dnn.readNetFromONNX(model_path) # 加载人脸特征数据库npz格式含features和labels db np.load(db_path) self.db_features db[features] # [N, 128] self.db_labels db[labels] # [N,] def recognize(self, frame): # 1. 检测人脸 faces self._detect_faces(frame) results [] for (x,y,w,h) in faces: # 2. 裁剪预处理复用FacePreprocessor逻辑 face_roi frame[y:yh, x:xw] processed self._preprocess(face_roi) # 3. 提取特征 blob cv2.dnn.blobFromImages([processed], 1.0, (224,224)) self.net.setInput(blob) feat self.net.forward().flatten() # [128] # 4. 欧氏距离检索 dists np.linalg.norm(self.db_features - feat, axis1) idx np.argmin(dists) label self.db_labels[idx] confidence 1.0 / (1.0 dists[idx]) # 归一化置信度 results.append({ bbox: (x,y,w,h), label: label, confidence: confidence }) return results # 使用示例 recog FaceRecognizer(face_embedder.onnx, face_db.npz) cap cv2.VideoCapture(0) while True: ret, frame cap.read() if not ret: break preds recog.recognize(frame) for p in preds: cv2.rectangle(frame, p[bbox][:2], (p[bbox][0]p[bbox][2], p[bbox][1]p[bbox][3]), (0,255,0), 2) cv2.putText(frame, f{p[label]}:{p[confidence]:.2f}, (p[bbox][0], p[bbox][1]-10), cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0,255,0), 2) cv2.imshow(Face Recognition, frame) if cv2.waitKey(1) 0xFF ord(q): break性能优化点特征数据库预加载db_features和db_labels在初始化时一次性加载到内存避免每次推理读磁盘欧氏距离向量化计算np.linalg.norm(..., axis1)比循环计算快15倍置信度公式1/(1d)将距离映射到[0,1]区间便于阈值设定如confidence0.5视为未知。4. 实操过程从零开始的完整训练-部署全流程4.1 环境搭建避开那些“pip install opencv-python”就完事的坑第一步永远是环境隔离。我用conda create -n faceenv python3.8新建环境而非virtualenv因为Conda能统一管理OpenCV的C依赖。安装顺序至关重要# 1. 先装PyTorch指定CUDA版本避免与OpenCV冲突 conda install pytorch torchvision torchaudio pytorch-cuda11.3 -c pytorch -c nvidia # 2. 再装OpenCV必须用conda-forge源pip版常缺DNN模块 conda install -c conda-forge opencv4.5.5 # 3. 最后装辅助库 pip install numpy scikit-learn tqdm onnx onnxruntime为什么不能pip install opencv-python因为pip安装的OpenCV默认不编译DNN模块需额外编译选项-D CMAKE_BUILD_TYPERELEASE -D BUILD_opencv_dnnON而conda-forge的预编译包已启用。我曾用pip版跑cv2.dnn.readNetFromONNX()报错AttributeError: module cv2.dnn has no attribute readNetFromONNX折腾半天才发现是DNN模块缺失。验证DNN模块是否可用import cv2 print(cv2.__version__) # 应输出4.5.5 print(hasattr(cv2.dnn, readNetFromONNX)) # 应输出True4.2 数据准备构建符合Triplet Loss要求的数据集假设原始数据存放在./raw_data/结构为raw_data/ ├── zhangsan/ │ ├── 001.jpg │ ├── 002.jpg │ └── ... ├── lisi/ │ ├── 001.jpg │ └── ... └── ...运行以下脚本生成训练集、验证集及CSV标注import os import csv import shutil from sklearn.model_selection import train_test_split def build_dataset(raw_dir, out_dir, test_size0.2): os.makedirs(f{out_dir}/train, exist_okTrue) os.makedirs(f{out_dir}/val, exist_okTrue) identities [d for d in os.listdir(raw_dir) if os.path.isdir(os.path.join(raw_dir, d))] identity_map {name: i for i, name in enumerate(identities)} all_files [] for identity in identities: id_dir os.path.join(raw_dir, identity) for img_file in os.listdir(id_dir): if img_file.lower().endswith((.jpg, .jpeg, .png)): all_files.append((os.path.join(id_dir, img_file), identity_map[identity])) # 划分训练/验证集按身份划分避免同身份图片分散 train_files, val_files train_test_split( all_files, test_sizetest_size, stratify[f[1] for f in all_files], random_state42 ) # 复制文件并生成CSV for split_name, files in [(train, train_files), (val, val_files)]: split_dir f{out_dir}/{split_name} with open(f{split_dir}/labels.csv, w, newline) as f: writer csv.writer(f) writer.writerow([filename, identity]) for src_path, label in files: # 生成唯一文件名identity_index.jpg fname f{label}_{len(os.listdir(split_dir))}.jpg dst_path os.path.join(split_dir, fname) shutil.copy(src_path, dst_path) writer.writerow([fname, label]) print(fDataset built: {len(train_files)} train, {len(val_files)} val samples) build_dataset(./raw_data, ./dataset)关键设计stratify参数确保每个身份在训练/验证集中都有样本避免某身份只出现在验证集导致评估失真。4.3 训练脚本可直接运行的端到端代码train.py核心逻辑import torch import torch.nn as nn from torch.utils.data import DataLoader from torchvision import transforms from sklearn.metrics import recall_score import numpy as np from tqdm import tqdm # 数据集定义 class FaceDataset(torch.utils.data.Dataset): def __init__(self, csv_path, root_dir, transformNone): self.df pd.read_csv(csv_path) self.root_dir root_dir self.transform transform def __len__(self): return len(self.df) def __getitem__(self, idx): row self.df.iloc[idx] img_path os.path.join(self.root_dir, row[filename]) image cv2.imread(img_path) image cv2.cvtColor(image, cv2.COLOR_BGR2RGB) if self.transform: image self.transform(image) return image, int(row[identity]) # 训练主循环 def train(): # 数据增强仅用于训练集 train_transform transforms.Compose([ transforms.ToPILImage(), transforms.RandomHorizontalFlip(p0.5), transforms.RandomRotation(degrees10), transforms.ColorJitter(brightness0.2, contrast0.2), transforms.ToTensor(), transforms.Normalize(mean[0.485, 0.456, 0.406], std[0.229, 0.224, 0.225]) ]) train_dataset FaceDataset(./dataset/train/labels.csv, ./dataset/train, train_transform) train_loader DataLoader(train_dataset, batch_size32, shuffleTrue, num_workers4) model FaceEmbedder(num_classeslen(train_dataset.classes)).cuda() criterion nn.TripletMarginLoss(margin0.35) optimizer torch.optim.Adam(model.parameters(), lr0.001) for epoch in range(50): model.train() total_loss 0 for batch_idx, (data, labels) in enumerate(tqdm(train_loader)): data, labels data.cuda(), labels.cuda() # 前向传播 embeddings model(data) # Triplet Loss需要三元组这里用BatchHard # 实际代码需集成pytorch-metric-learning的miner loss criterion(embeddings, labels) optimizer.zero_grad() loss.backward() torch.nn.utils.clip_grad_norm_(model.parameters(), max_norm1.0) optimizer.step() total_loss loss.item() # 验证 val_recall validate(model, ./dataset/val/labels.csv, ./dataset/val) print(fEpoch {epoch}: Train Loss{total_loss/len(train_loader):.4f} | Val Recall1{val_recall:.4f}) # 早停 if val_recall best_recall: best_recall val_recall torch.save(model.state_dict(), best_model.pth) patience 0 else: patience 1 if patience 3: break if __name__ __main__: train()实操心得ColorJitter的brightness和contrast参数不宜过大0.3否则会生成过曝或死黑区域破坏人脸结构。我测试过brightness0.5时模型把高光区域误认为“额头反光”导致对戴眼镜者识别率下降。4.4 特征数据库构建让模型记住“你是谁”训练完成后需用训练集所有图像提取特征构建检索数据库def build_feature_db(model_path, dataset_dir, csv_path, output_path): model FaceEmbedder().cuda() model.load_state_dict(torch.load(model_path)) model.eval() dataset FaceDataset(csv_path, dataset_dir, transformtransforms.Compose([ transforms.ToPILImage(), transforms.Resize((224,224)), transforms.ToTensor(), transforms.Normalize(mean[0.485, 0.456, 0.406], std[0.229, 0.224, 0.225]) ])) features [] labels [] for img, label in tqdm(dataset): img img.unsqueeze(0).cuda() with torch.no_grad(): feat model(img).cpu().numpy() features.append(feat[0]) labels.append(label) np.savez(output_path, featuresnp.array(features), labelsnp.array(labels)) print(fFeature DB saved to {output_path}) build_feature_db(best_model.pth, ./dataset/train, ./dataset/train/labels.csv, face_db.npz)注意事项数据库必须用训练集构建而非验证集。因为验证集用于评估数据库代表模型“已知的人”必须与训练一致。4.5 实时识别测试用摄像头验证端到端效果运行demo.py打开摄像头实时识别# demo.py import cv2 import numpy as np # 加载模型和数据库 net cv2.dnn.readNetFromONNX(face_embedder.onnx) db np.load(face_db.npz) db_features db[features] db_labels db[labels] # 加载人脸检测模型 face_net cv2.dnn.readNetFromTensorflow(opencv_face_detector_uint8.pb) def detect_faces(frame): blob cv2.dnn.blobFromImage(cv2.resize(frame, (300, 300)), 1.0, (300, 300), (104.0, 177.0, 123.0)) face_net.setInput(blob) detections face_net.forward() faces [] for i in range(detections.shape[2]): confidence detections[0, 0, i, 2] if confidence 0.5: box detections[0, 0, i, 3:7] * np.array([frame.shape[1], frame.shape[0], frame.shape[1], frame.shape[0]]) faces.append(box.astype(int)) return faces cap cv2.VideoCapture(0) while True: ret, frame cap.read() if not ret: break faces detect_faces(frame) for (x1,y1,x2,y2) in faces: # 裁剪人脸ROI face_roi frame[y1:y2, x1:x2] # 预处理同训练时 face_roi cv2.resize(face_roi, (224,224)) face_roi face_roi.astype(np.float32) / 255.0 face_roi face_roi.transpose(2,0,1) # 提取特征 blob cv2.dnn.blobFromImages([face_roi], 1.0, (224,224)) net.setInput(blob) feat net.forward().flatten() # 检索 dists np.linalg.norm(db_features - feat, axis1) idx np.argmin(dists) label db_labels[idx] conf 1.0 / (1.0 dists[idx]) # 绘制结果 cv2.rectangle(frame, (x1,y1), (x2,y2), (0,255,0), 2) cv2.putText(frame, fID{label}:{conf:.2f}, (x1,y1-10), cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0,255,0), 2) cv2.imshow(Real-time Recognition p a hrefhttps://download.csdn.net/download/hnjzfwy/85774111 stylecolor:#ec7500;font-size:14px; 本文还有配套的精品资源点击获取 /a img altmenu-r.4af5f7ec.gif srchttps://csdnimg.cn/release/wenkucmsfe/public/img/menu-r.4af5f7ec.gif stylewidth:16px;margin-left:4px;vertical-align:text-bottom;cursor:text; /p
上一篇/下一篇内容由系统自动关联 返回资讯列表 →