当前位置: 首页 > news >正文

YOLOv8 级联检测:在人脸 ROI 内检测眼镜(零改源码方案)

在复杂背景中,直接对整图进行精细检测可能导致精度不高,尤其是当目标的尺寸较小或背景较为复杂时。通过限制检测范围,仅在人脸区域进行进一步的目标检测(如眼镜检测),可以避免背景干扰,提高检测的准确性。

为了说明二次目标检测的实际应用,以下以人脸识别和眼镜检测为例,展示具体的流程。

已拥有两个 YOLOv8 目标检测模型(face.pt 用于人脸;glasses.pt 用于眼镜),希望不改动 Ultralytics 源码,通过脚本实现“先做人脸检测,再在人脸 ROI 内做人眼镜检测”的两级推理。
该方案通过调用 YOLOv8 的官方 Python API 进行串联,而不需要修改 Ultralytics 的源代码。不仅保证了代码的兼容性,还能够利用 YOLOv8 现有的优化和功能。

关键步骤:

  1. 加载人脸检测模型(一级检测)

    首先,我们加载一个用于人脸检测的YOLOv8模型(face.pt)。该模型能够快速准确地识别图像中的人脸区域。
  2. 对输入图像进行人脸检测

    将输入图像传入人脸检测模型,获取图像中的人脸框(bounding boxes)。这些框标识了图像中所有检测到的人脸的位置。
  3. 裁剪出人脸区域(ROI)

    基于检测到的人脸框,将每个框内的区域裁剪出来,得到单独的人脸子图。每个子图即为接下来进行眼镜检测的区域。
  4. 加载眼镜检测模型(二级检测)

    在提取出的人脸区域内,我们使用另一个YOLOv8模型(glasses.pt)进行眼镜检测。该模型专门用于识别眼镜,并能高效地检测出每个人脸中的眼镜框。
  5. 回填坐标并绘制结果

    由于眼镜检测得到的坐标是相对于人脸区域的,我们需要将这些坐标映射回原图的坐标系。通过回填坐标,将眼镜框准确地绘制在人脸区域上。
  6. 显示或保存检测结果

    最后,根据需求,展示实时检测结果,或者将检测后的图像保存到指定目录中。

代码如下:

#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
Cascade inference: face ➜ glasses(两级推理脚本)
- 不修改 Ultralytics 源码,仅用官方 Python API 串联两次推理
- 支持图片/文件夹/视频文件/摄像头/网络流(与 YOLO 原生接口保持一致)
- 图片会保存到 runs/cascade/;视频或摄像头默认仅显示(可按需扩展为视频写出)
"""import argparse
from pathlib import Path
import itertools
import cv2
import torch
from ultralytics import YOLOdef clamp(v, lo, hi):"""将坐标值限制在 [lo, hi] 区间内,避免越界。"""return max(lo, min(hi, v))def is_image_path(p: str) -> bool:"""简单判断是否为图片路径(用于保存命名)。"""if not isinstance(p, str):return Falsesuffix = Path(p).suffix.lower()return suffix in {".jpg", ".jpeg", ".png", ".bmp", ".tif", ".tiff", ".webp"}def run(face_w,                 # 人脸检测权重路径,如 "face.pt"glass_w,                # 眼镜检测权重路径,如 "glasses.pt"source,                 # 输入源:图片/文件夹/视频/摄像头 ID/URLimgsz=640,              # 一级(人脸)输入尺寸g_imgsz=256,            # 二级(眼镜)输入尺寸(ROI 更小,建议取 224~320)conf_face=0.25,         # 人脸检测置信度阈值conf_glass=0.50,        # 眼镜检测置信度阈值(可适当更严)iou_face=0.45,          # 人脸 NMS IoU 阈值iou_glass=0.45,         # 眼镜 NMS IoU 阈值save=False,             # 是否保存图片结果(视频/摄像头默认逐帧图片保存,可按需扩展为视频写出)device=''               # 计算设备:''(自动)、'cpu'、'cuda:0' 等
):"""两级推理主流程:1) 先对整帧做人脸检测,得到 face boxes;2) 对每个 face ROI 批量运行眼镜检测;3) 将眼镜框坐标平移回原图坐标系后叠加绘制与保存/显示。"""# ① 加载两个模型,可自动选择设备或手动指定face_model = YOLO(face_w).to(device)glass_model = YOLO(glass_w).to(device)# 计数器:用于对无文件名来源(如摄像头)生成递增帧名frame_counter = itertools.count(start=0, step=1)# ② 遍历输入流:支持文件夹/视频/摄像头/URL(与 YOLO 保持一致)#    使用 stream=True 逐帧生成 Result,避免一次性占用过多内存with torch.inference_mode():for res in face_model(source,stream=True,imgsz=imgsz,conf=conf_face,iou=iou_face,verbose=False):# 原始 BGR 图像(numpy.ndarray)frame = res.orig_imgH, W = frame.shape[:2]# 收集“眼镜”的最终框 [(x1,y1,x2,y2), score]eyeglass_dets = []# ③ 批量裁剪所有人脸 ROI,并记录偏移量以便回填坐标crops, offsets = [], []for b in res.boxes:# b.xyxy[0] 是 tensor,需转为 Python 数值(.tolist() 会拷到 CPU)x1, y1, x2, y2 = map(int, b.xyxy[0].tolist())# 安全裁剪:坐标边界约束,避免越界报错x1 = clamp(x1, 0, W - 1)y1 = clamp(y1, 0, H - 1)x2 = clamp(x2, 0, W - 1)y2 = clamp(y2, 0, H - 1)# 跳过异常或过小的框if x2 - x1 <= 1 or y2 - y1 <= 1:continue# 取 ROI(注意 copy 避免引用同一内存导致画图时污染)roi = frame[y1:y2, x1:x2].copy()crops.append(roi)offsets.append((x1, y1))# ④ 在所有 ROI 上批量跑眼镜检测(比逐个调用更快)if crops:g_results = glass_model(crops,imgsz=g_imgsz,conf=conf_glass,iou=iou_glass,verbose=False)# 将每个 ROI 的眼镜框坐标平移回原图坐标for g_res, (ox, oy) in zip(g_results, offsets):for g in g_res.boxes:gx1, gy1, gx2, gy2 = map(int, g.xyxy[0].tolist())# 平移至原图坐标系X1, Y1 = gx1 + ox, gy1 + oyX2, Y2 = gx2 + ox, gy2 + oy# 再次边界约束(理论上已在人脸框内,无需,但稳妥起见)X1 = clamp(X1, 0, W - 1)Y1 = clamp(Y1, 0, H - 1)X2 = clamp(X2, 0, W - 1)Y2 = clamp(Y2, 0, H - 1)eyeglass_dets.append(((X1, Y1, X2, Y2), float(g.conf)))# ⑤ 在原图上绘制眼镜框与置信度for (x1, y1, x2, y2), score in eyeglass_dets:cv2.rectangle(frame, (x1, y1), (x2, y2), (0, 255, 0), 2)  # 绿色框cv2.putText(frame, f'{score:.2f}', (x1, max(0, y1 - 6)),cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 255, 0), 1)if save:save_dir = Path('runs/cascade')save_dir.mkdir(parents=True, exist_ok=True)# 有文件名的(图片/视频帧)尽量沿用原文件名;否则用递增帧号if is_image_path(res.path):out_name = Path(res.path).name  # 保留原图名else:idx = next(frame_counter)stem = Path(res.path).stem if isinstance(res.path, str) else "frame"out_name = f"{stem}_{idx:06d}.jpg"cv2.imwrite(str(save_dir / out_name), frame)cv2.destroyAllWindows()def parse_args():"""命令行参数解析。"""p = argparse.ArgumentParser(description="YOLOv8 级联检测脚本(face ➜ glasses)· 零改源码")p.add_argument('--face', help='人脸检测权重,如 face.pt')p.add_argument('--glasses', help='眼镜检测权重,如 glasses.pt')p.add_argument('--source', help='输入源:图片/文件夹/视频/摄像头 ID/网络流 URL(如 0/rtsp://...)')p.add_argument('--imgsz', type=int, default=640, help='一级检测(人脸)输入尺寸')p.add_argument('--g-imgsz', type=int, default=256, help='二级检测(眼镜)输入尺寸')p.add_argument('--conf-face', type=float, default=0.25, help='人脸检测置信度阈值')p.add_argument('--conf-glass', type=float, default=0.4, help='眼镜检测置信度阈值')p.add_argument('--iou-face', type=float, default=0.45, help='人脸 NMS IoU 阈值')p.add_argument('--iou-glass', type=float, default=0.45, help='眼镜 NMS IoU 阈值')p.add_argument('--save', default=True, help='将结果保存到 runs/cascade/')p.add_argument('--device', default=0, help='计算设备:""(自动)、"cpu"、"cuda:0"、"cuda:1" 等')return p.parse_args()if __name__ == "__main__":args = parse_args()run(face_w=args.face,glass_w=args.glasses,source=args.source,imgsz=args.imgsz,g_imgsz=args.g_imgsz,conf_face=args.conf_face,conf_glass=args.conf_glass,iou_face=args.iou_face,iou_glass=args.iou_glass,save=args.save,device=args.device)

经过实验,代码逻辑没问题,能跑通。

http://www.lryc.cn/news/619344.html

相关文章:

  • 第十六届蓝桥杯青少组C++省赛[2025.8.9]第二部分编程题(1 、庆典队列)
  • Excel怎么筛选重复项?【图文详解】查找/删除重复项?查找重复项公式?如何去重?
  • [QtADS]解析demo.pro
  • HarmonyOS NDK的JavaScript/TypeScript与C++交互机制
  • Electron自定义菜单栏及Mac最大化无效的问题解决
  • XML头部声明发送者信息的实现方法
  • C# 微软依赖注入 (Microsoft.Extensions.DependencyInjection) 详解
  • CV 医学影像分类、分割、目标检测,之【肝脏分割】项目拆解
  • windows常用的快捷命令
  • 机器学习实战·第三章 分类(2)
  • docker 容器内编译onnxruntime
  • git clone 支持在命令行临时设置proxy
  • CV 医学影像分类、分割、目标检测,之【腹腔多器官语义分割】项目拆解
  • 何解决PyCharm中pip install安装Python报错ModuleNotFoundError: No module named ‘json’问题
  • Video_AVI_Packet(2)
  • 基于RTSP|RTMP低延迟视频链路的多模态情绪识别系统构建与实现
  • 日志数据链路的 “搬运工”:Flume 分布式采集的组件分工与原理
  • 进阶向:Python编写自动化邮件发送程序
  • Jenkins一直无法启动,怎么办?
  • 论文分享 | Flashboom:一种声东击西攻击手段以致盲基于大语言模型的代码审计
  • 守拙以致远:个人IP的长青之道|创客匠人
  • Hive 创建事务表的方法
  • 自建知识库,向量数据库 体系建设(四)之文本向量与相似度计算——仙盟创梦IDE
  • java中list的api详细使用
  • 无人机航拍数据集|第15期 无人机人员目标检测YOLO数据集4923张yolov11/yolov8/yolov5可训练
  • pt-online-schema-change 全解析:MySQL 表结构变更的安全之道
  • clickhouse集群的安装与部署
  • Vue3 使用 echarts 甘特图(GanttChart)
  • Java -- Vector底层结构-- ArrayList和LinkedList的比较
  • C++主流string的使用