一.下载好文件后点击detect.py文件
import argparse import csv import os import platform import sys from pathlib import Path import torch
1.
import argparse
argparse
是Python标准库中的一个模块,用于处理命令行参数。它可以让你的Python程序接收命令行输入,解析这些参数,并在程序内部使用。例如,在YOLO模型的训练或推理脚本中,常常会使用argparse
来处理输入的配置文件路径、模型参数等。2.
import csv
csv
模块用于读取和写入CSV文件(Comma Separated Values,逗号分隔值文件)。在YOLO的使用场景中,可能会用它来处理标注文件或存储检测结果。3.
import os
os
模块提供了与操作系统进行交互的功能,比如文件路径的操作、执行系统命令、环境变量的访问等。在YOLO的代码中,os
通常用于文件路径管理、创建文件夹或文件的读写操作。4.
import platform
platform
模块提供了访问底层平台数据(例如操作系统版本、硬件架构等)的能力。这个模块在YOLO代码中可能用于获取系统信息,以便在不同的操作系统环境下做出不同的配置或决策。5.
import sys
sys
模块提供了与Python解释器相关的功能,比如访问命令行参数、控制解释器的行为等。在YOLO代码中,sys
通常用来终止程序、添加模块路径或处理异常等。6.
from pathlib import Path
Pathlib
是Python3中的一个面向对象的文件和路径操作库。Path
类是其中的核心类,它为文件和目录路径的处理提供了更方便和直观的方法。相较于传统的os.path
,Pathlib
的使用更符合现代Python代码的风格。在YOLO代码中,Path
可以用于文件路径的创建、检查文件是否存在、遍历目录等操作。7.
import torch
torch
是PyTorch的核心模块。PyTorch是一个开源的深度学习框架,广泛用于计算机视觉、自然语言处理等领域。YOLO模型的实现和训练可以使用PyTorch来进行,它提供了张量计算(类似于NumPy)、自动微分、模型定义和训练等功能。
FILE = Path(__file__).resolve() # 获取当前脚本的绝对路径,并消除任何符号链接 ROOT = FILE.parents[0] # 获取当前脚本所在目录,即项目的根目录 if str(ROOT) not in sys.path: sys.path.append(str(ROOT)) # 如果根目录不在sys.path中,将其添加到sys.path,以便模块导入 ROOT = Path(os.path.relpath(ROOT, Path.cwd())) # 将根目录路径转换为相对于当前工作目录的相对路径
这段代码的目的是确保当前脚本的所在目录(即项目的根目录)被正确添加到Python的模块搜索路径(
sys.path
)中。这样可以确保在项目中导入其他模块时不会出现路径问题。最后一步将根目录路径转换为相对路径,可能是为了使路径表示更简洁或更便于跨平台使用。这样的设置在大型项目中很常见,尤其是需要在不同的脚本间共享模块和文件时,确保路径正确、代码可以跨环境运行。
from models.common import DetectMultiBackend # 导入用于处理多后端(如不同硬件加速器)的检测模型类 from utils.datasets import IMG_FORMATS, VID_FORMATS, LoadImages, LoadStreams # 导入支持的图像、视频格式及加载数据流的工具 from utils.general import (LOGGER, check_file, check_img_size, check_imshow, check_requirements, colorstr, increment_path, non_max_suppression, print_args, scale_coords, strip_optimizer, xyxy2xywh) # 导入各种通用工具函数,包括日志、文件检查、图像大小检查、非极大值抑制、坐标转换等 from utils.plots import Annotator, colors, save_one_box # 导入绘图工具,用于注释和保存检测框 from utils.torch_utils import select_device, time_sync # 导入设备选择工具(如GPU/CPU)和时间同步工具
各模块作用总结
models.common
:
DetectMultiBackend
: 用于处理不同后端的检测模型类,如支持在不同硬件加速器上运行(CPU、GPU、TPU等)。
utils.datasets
:
IMG_FORMATS
: 支持的图像格式列表(如.jpg
,.png
)。VID_FORMATS
: 支持的视频格式列表(如.mp4
,.avi
)。LoadImages
: 用于加载图像文件的类。LoadStreams
: 用于加载视频流或摄像头数据流的类。
utils.general
:
- 包含各种通用工具函数:
LOGGER
: 日志工具,用于记录信息。check_file
: 检查文件是否存在。check_img_size
: 检查并调整图像大小,以确保与模型输入大小兼容。check_imshow
: 检查是否支持显示窗口(通常在无显示的环境中使用)。check_requirements
: 检查依赖项是否满足。colorstr
: 用于着色字符串输出的工具。increment_path
: 生成不重复的路径,用于保存文件。non_max_suppression
: 实现非极大值抑制算法,用于去除重叠的检测框。print_args
: 打印命令行参数。scale_coords
: 缩放坐标,以适应原始图像尺寸。strip_optimizer
: 移除PyTorch模型中的优化器信息(用于导出模型)。xyxy2xywh
: 坐标转换函数,将xyxy
格式(左上角和右下角坐标)转换为xywh
格式(中心点和宽高)。
utils.plots
:
Annotator
: 用于在图像上绘制检测框和标签的工具类。colors
: 定义了一组用于绘图的颜色。save_one_box
: 用于保存单个检测框区域的图像。
utils.torch_utils
:
select_device
: 用于选择计算设备(如GPU或CPU)。time_sync
: 用于时间测量和同步,通常用于计算执行时间。总结
这段代码导入了YOLO项目中用于模型加载、数据处理、推理后处理、绘图和设备管理的各种自定义模块和函数。通过这些工具,开发者可以高效地进行图像和视频的检测任务,处理检测结果并进行可视化。
def main(opt): check_requirements(ROOT / "requirements.txt", exclude=("tensorboard", "thop")) run(**vars(opt)) if __name__ == "__main__": opt = parse_opt() main(opt)
def main(opt):
定义了一个名为main
的函数,它接收一个参数opt
,这个参数通常是解析后的命令行选项和配置。
check_requirements(ROOT / "requirements.txt", exclude=("tensorboard", "thop"))
check_requirements
: 这个函数用于检查项目所需的依赖项是否已安装。ROOT / "requirements.txt"
: 指定依赖项列表所在的文件路径(通常是requirements.txt
)。exclude=("tensorboard", "thop")
: 指定检查依赖时要排除的库,这意味着在这个场景中,即使tensorboard
和thop
没有安装,也不会影响程序的运行。
run(**vars(opt))
run
: 通常是项目中实际执行检测任务的主要函数。vars(opt)
: 将opt
对象(通常是argparse.Namespace
对象)转换为字典,然后使用**
运算符将字典解包为关键字参数传递给run
函数。这允许run
函数直接接收并处理命令行参数。
if __name__ == "__main__":
这是Python脚本的标准结构,用于检查当前模块是否是被直接运行的(而不是被导入到其他模块中)。如果条件为真,表示脚本是作为主程序运行。
opt = parse_opt()
这里调用了parse_opt
函数,通常这个函数用于解析命令行参数,并返回一个包含这些参数的对象opt
。
main(opt)
将解析后的参数对象opt
传递给main
函数,从而开始执行主要的程序逻辑。总结
这段代码是一个YOLO项目的主程序入口,它的流程大致如下:
- 解析命令行参数。
- 检查并确保运行该脚本所需的依赖项已安装(排除了
tensorboard
和thop
)。- 调用主要的运行函数
run
来执行YOLO模型的推理任务,传入解析后的参数。
def parse_opt(): parser = argparse.ArgumentParser() parser.add_argument("--weights", nargs="+", type=str, default=ROOT / "yolov5s.pt", help="model path or triton URL") parser.add_argument("--source", type=str, default=ROOT / "data/images", help="file/dir/URL/glob/screen/0(webcam)") parser.add_argument("--data", type=str, default=ROOT / "data/coco128.yaml", help="(optional) dataset.yaml path") parser.add_argument("--imgsz", "--img", "--img-size", nargs="+", type=int, default=[640], help="inference size h,w") parser.add_argument("--conf-thres", type=float, default=0.25, help="confidence threshold") parser.add_argument("--iou-thres", type=float, default=0.45, help="NMS IoU threshold") parser.add_argument("--max-det", type=int, default=1000, help="maximum detections per image") parser.add_argument("--device", default="", help="cuda device, i.e. 0 or 0,1,2,3 or cpu") parser.add_argument("--view-img", action="store_true", help="show results") parser.add_argument("--save-txt", action="store_true", help="save results to *.txt") parser.add_argument("--save-csv", action="store_true", help="save results in CSV format") parser.add_argument("--save-conf", action="store_true", help="save confidences in --save-txt labels") parser.add_argument("--save-crop", action="store_true", help="save cropped prediction boxes") parser.add_argument("--nosave", action="store_true", help="do not save images/videos") parser.add_argument("--classes", nargs="+", type=int, help="filter by class: --classes 0, or --classes 0 2 3") parser.add_argument("--agnostic-nms", action="store_true", help="class-agnostic NMS") parser.add_argument("--augment", action="store_true", help="augmented inference") parser.add_argument("--visualize", action="store_true", help="visualize features") parser.add_argument("--update", action="store_true", help="update all models") parser.add_argument("--project", default=ROOT / "runs/detect", help="save results to project/name") parser.add_argument("--name", default="exp", help="save results to project/name") parser.add_argument("--exist-ok", action="store_true", help="existing project/name ok, do not increment") parser.add_argument("--line-thickness", default=3, type=int, help="bounding box thickness (pixels)") parser.add_argument("--hide-labels", default=False, action="store_true", help="hide labels") parser.add_argument("--hide-conf", default=False, action="store_true", help="hide confidences") parser.add_argument("--half", action="store_true", help="use FP16 half-precision inference") parser.add_argument("--dnn", action="store_true", help="use OpenCV DNN for ONNX inference") parser.add_argument("--vid-stride", type=int, default=1, help="video frame-rate stride") opt = parser.parse_args() opt.imgsz *= 2 if len(opt.imgsz) == 1 else 1 # expand print_args(vars(opt)) return opt
parse_opt
函数参数功能总结
模型与设备相关
--weights
: 指定模型权重文件的路径或Triton URL,支持多个路径输入。默认使用yolov5s.pt
。--device
: 指定运行推理时使用的计算设备,例如 GPU (0
) 或 CPU。如果未指定,程序会自动选择适当的设备。--half
: 启用FP16半精度推理,加速推理过程。此选项仅在支持FP16的设备上有效。--dnn
: 使用OpenCV的DNN模块进行ONNX模型推理。输入与输出数据相关
--source
: 指定输入数据源,可以是文件、目录、URL、通配符、屏幕截图或摄像头(0
表示摄像头)。默认使用data/images
。--data
: 指定数据集配置文件的路径,默认使用coco128.yaml
。--imgsz
: 指定推理时输入图像的大小,可以是单一值(正方形)或宽高两个值。默认是 640。--project
: 结果保存的主目录,默认为runs/detect
。--name
: 结果保存的子目录名称,默认为exp
。--exist-ok
: 允许覆盖现有的项目/名称目录,而不自动增加序号。推理控制相关
--conf-thres
: 置信度阈值,低于该值的检测结果会被忽略。默认值为 0.25。--iou-thres
: 非极大值抑制(NMS)的IoU阈值,默认值为 0.45。--max-det
: 每张图像的最大检测数,默认值为 1000。--classes
: 通过类别ID过滤检测结果,可以指定一个或多个类别。--agnostic-nms
: 启用类别无关的非极大值抑制(NMS),即忽略类别区分进行NMS处理。--augment
: 启用增强推理,如多尺度测试,以提高模型的鲁棒性。--visualize
: 可视化中间特征图,用于调试和理解模型的工作原理。--vid-stride
: 视频帧率的步长,控制处理视频时的帧间隔。默认值为 1。结果输出控制
--view-img
: 显示检测结果图像。--save-txt
: 将检测结果保存为文本文件(.txt)。--save-csv
: 将检测结果保存为CSV格式文件。--save-conf
: 在保存的标签文件中包括置信度信息。--save-crop
: 保存裁剪后的预测框图像。--nosave
: 不保存检测结果图像或视频文件。--line-thickness
: 指定绘制边界框的线条厚度,默认是 3 像素。--hide-labels
: 隐藏检测框上的标签文字。--hide-conf
: 隐藏检测框上的置信度数值。模型管理
--update
: 更新所有模型权重至最新版本。总结
通过这些命令行参数,用户可以灵活控制YOLO模型的推理过程,指定输入输出路径、调整推理细节、选择计算设备以及配置输出选项。这些参数为模型应用提供了丰富的自定义选项,适用于多种场景和需求。
def run( weights=ROOT / "yolov5s.pt", # model path or triton URL source=ROOT / "data/images", # file/dir/URL/glob/screen/0(webcam) data=ROOT / "data/coco128.yaml", # dataset.yaml path imgsz=(640, 640), # inference size (height, width) conf_thres=0.25, # confidence threshold iou_thres=0.45, # NMS IOU threshold max_det=1000, # maximum detections per image device="", # cuda device, i.e. 0 or 0,1,2,3 or cpu view_img=False, # show results save_txt=False, # save results to *.txt save_csv=False, # save results in CSV format save_conf=False, # save confidences in --save-txt labels save_crop=False, # save cropped prediction boxes nosave=False, # do not save images/videos classes=None, # filter by class: --class 0, or --class 0 2 3 agnostic_nms=False, # class-agnostic NMS augment=False, # augmented inference visualize=False, # visualize features update=False, # update all models project=ROOT / "runs/detect", # save results to project/name name="exp", # save results to project/name exist_ok=False, # existing project/name ok, do not increment line_thickness=3, # bounding box thickness (pixels) hide_labels=False, # hide labels hide_conf=False, # hide confidences half=False, # use FP16 half-precision inference dnn=False, # use OpenCV DNN for ONNX inference vid_stride=1, # video frame-rate stride ):
run
函数参数功能总结
模型与设备相关
weights
: 模型权重文件的路径或Triton URL,默认为yolov5s.pt
。device
: 指定运行推理的设备,例如 GPU(如0
)或 CPU。默认为空字符串,表示自动选择设备。half
: 启用FP16半精度推理,通常用于加速推理过程。dnn
: 使用OpenCV的DNN模块进行ONNX模型推理。输入与输出数据相关
source
: 输入数据源,可以是文件、目录、URL、通配符、屏幕截图或摄像头(使用0
表示摄像头)。默认路径为data/images
。data
: 数据集配置文件的路径,默认为coco128.yaml
。imgsz
: 推理时输入图像的大小,以(height, width)
的形式指定。默认大小为(640, 640)
。project
: 结果保存的主目录,默认为runs/detect
。name
: 结果保存的子目录名称,默认为exp
。推理控制相关
conf_thres
: 置信度阈值,低于该值的检测结果将被忽略。默认值为 0.25。iou_thres
: 非极大值抑制(NMS)的IoU阈值,默认值为 0.45。max_det
: 每张图像的最大检测数,默认值为 1000。classes
: 通过类别ID过滤检测结果,可以指定一个或多个类别。agnostic_nms
: 启用类别无关的NMS,即忽略类别进行NMS处理。augment
: 启用增强推理,如多尺度测试,以提高模型的鲁棒性。visualize
: 可视化中间特征图,用于调试和理解模型的工作原理。vid_stride
: 视频帧率的步长,控制处理视频时的帧间隔。默认值为 1。结果输出控制
view_img
: 显示检测结果图像。save_txt
: 将检测结果保存为文本文件(.txt
)。save_csv
: 将检测结果保存为CSV格式文件。save_conf
: 在保存的标签文件中包括置信度信息。save_crop
: 保存裁剪后的预测框图像。nosave
: 不保存检测结果图像或视频文件。line_thickness
: 指定绘制边界框的线条厚度,默认是3个像素。hide_labels
: 隐藏检测框上的标签文字。hide_conf
: 隐藏检测框上的置信度数值。模型管理
update
: 更新所有模型权重至最新版本。exist_ok
: 允许覆盖现有的项目/名称目录,而不会自动增加序号。总结
run
函数通过这些参数来灵活地配置YOLO模型的运行环境,涵盖了从模型加载、设备选择、输入输出控制到结果保存等多方面的需求。它使得YOLOv5在推理过程中能够适应各种不同的应用场景,同时确保结果的准确性和可用性。
source = str(source) save_img = not nosave and not source.endswith(".txt") # save inference images is_file = Path(source).suffix[1:] in (IMG_FORMATS + VID_FORMATS) is_url = source.lower().startswith(("rtsp://", "rtmp://", "http://", "https://")) webcam = source.isnumeric() or source.endswith(".streams") or (is_url and not is_file) screenshot = source.lower().startswith("screen") if is_url and is_file: source = check_file(source) # download
这段代码的主要功能是对输入源 (
source
) 进行检查和分类,以便根据其类型正确处理和配置YOLO模型的推理过程。具体来说,它:
转换和识别输入源类型:
- 将输入源转换为字符串格式。
- 判断输入源是否是文件、URL、摄像头、截图等类型。
设定保存策略:
- 根据输入源类型和用户选项(如
nosave
)决定是否保存推理结果图像。处理URL输入:
- 如果输入是URL且指向文件(如在线图像或视频),则下载文件以供本地处理。
# Directories save_dir = increment_path(Path(project) / name, exist_ok=exist_ok) # increment run (save_dir / "labels" if save_txt else save_dir).mkdir(parents=True, exist_ok=True) # make dir
这段代码的主要功能是为保存推理结果创建和管理目录:
自动创建保存目录:通过
increment_path
函数,根据指定的project
和name
参数,创建一个保存推理结果的目录。如果目录已存在且不允许覆盖(exist_ok=False
),则会自动在目录名后加上数字递增(如exp
,exp1
,exp2
等),确保新结果不会覆盖旧的检测结果。设置输出文件路径:根据用户选项(如
save_txt
),决定是否在保存目录中创建额外的子目录。例如,如果选择保存检测结果的文本文件(save_txt=True
),则会在主目录下创建一个名为labels
的子目录。保证目录存在:使用
mkdir
方法确保所需的所有目录都存在,即使父级目录不存在,也会递归创建所有必要的路径。这为保存推理结果(如图像、视频、标签文件等)提供了必要的存储位置。
# Load model device = select_device(device) model = DetectMultiBackend(weights, device=device, dnn=dnn, data=data, fp16=half) stride, names, pt = model.stride, model.names, model.pt imgsz = check_img_size(imgsz, s=stride) # check image size
这段代码的主要目的是加载YOLO模型并准备推理环境:
- 选择计算设备:使用
select_device
函数选择合适的设备(如GPU或CPU)来运行推理。- 加载YOLO模型:通过
DetectMultiBackend
类加载YOLO模型权重,并根据输入参数配置模型(例如设备类型、数据集配置、是否使用FP16半精度、以及是否启用OpenCV的DNN模块)。- 提取模型信息:获取模型的步长(
stride
)、类别名称(names
)和格式类型(pt
)。- 检查输入图像大小:使用
check_img_size
函数验证并调整输入图像尺寸,确保其符合模型步长的要求。
# Dataloader bs = 1 # batch_size if webcam: view_img = check_imshow(warn=True) dataset = LoadStreams(source, img_size=imgsz, stride=stride, auto=pt, vid_stride=vid_stride) bs = len(dataset) elif screenshot: dataset = LoadScreenshots(source, img_size=imgsz, stride=stride, auto=pt) else: dataset = LoadImages(source, img_size=imgsz, stride=stride, auto=pt, vid_stride=vid_stride) vid_path, vid_writer = [None] * bs, [None] * bs
这段代码的主要功能是为YOLO模型的推理过程准备数据加载器(Dataloader)。根据输入源的不同类型,代码会选择合适的方法来加载图像或视频流,具体步骤如下:
设置批处理大小:定义
bs = 1
表示每次处理的批量大小默认为1。处理摄像头输入:
- 如果输入源是摄像头(
webcam
为True
),调用check_imshow()
函数检查是否可以显示图像,并设置view_img
变量。- 使用
LoadStreams
类来加载实时视频流,传入图像大小、步长、自动模式和视频帧率参数。处理截图输入:
- 如果输入源是截图(
screenshot
为True
),使用LoadScreenshots
类加载指定的截图,传入相应的参数。处理文件输入:
- 如果输入源是图像或视频文件,使用
LoadImages
类加载这些文件,传入图像大小、步长、自动模式和视频帧率参数。初始化视频路径及写入器:
- 为每个批次的图像或视频初始化路径(
vid_path
)和视频写入器(vid_writer
),这些变量将用于保存检测结果。
模型最关键的推理步骤
# Run inference model.warmup(imgsz=(1 if pt or model.triton else bs, 3, *imgsz)) # warmup seen, windows, dt = 0, [], (Profile(device=device), Profile(device=device), Profile(device=device))#seen 用于记录已处理图像的计数,windows 可以用于存储计算结果的窗口,dt 则是用于性能分析的时间记录器。 for path, im, im0s, vid_cap, s in dataset: with dt[0]: im = torch.from_numpy(im).to(model.device) im = im.half() if model.fp16 else im.float() # uint8 to fp16/32 im /= 255 # 0 - 255 to 0.0 - 1.0 if len(im.shape) == 3: im = im[None] # expand for batch dim if model.xml and im.shape[0] > 1: ims = torch.chunk(im, im.shape[0], 0)
这段代码来自 YOLOv5 的
detect.py
文件,主要负责模型推理过程中的图像预处理和准备。以下是该代码的总结:主要功能
模型预热:
- 通过
model.warmup
方法进行预热,确保模型在实际推理前优化性能。初始化变量:
- 记录已处理图像数(
seen
),用于存储结果的窗口(windows
),以及性能分析的时间记录器(dt
)。数据集处理:
- 遍历数据集中的每个图像或视频帧,以便进行推理。
图像转化和归一化:
- 使用 PyTorch 将输入图像从 NumPy 数组转换为张量,调整数据类型(半精度或单精度),并将像素值归一化到 [0, 1] 的范围。
批次处理:
- 如果输入的图像为 3 维,扩展为批次格式。如果模型要求 XML 格式并且输入包含多个图像,则拆分为单独的图像处理。
# Inference with dt[1]: 使用性能分析工具 dt[1] 开始记录推理所耗费的时间。 visualize = increment_path(save_dir / Path(path).stem, mkdir=True) if visualize else False #如果开启了可视化,使用 increment_path 方法创建一个用于保存结果的目录路径。Path(path).stem 获取文件名的基本部分,以避免文件名冲突。 if model.xml and im.shape[0] > 1: pred = None for image in ims: if pred is None: pred = model(image, augment=augment, visualize=visualize).unsqueeze(0) else: pred = torch.cat((pred, model(image, augment=augment, visualize=visualize).unsqueeze(0)), dim=0) pred = [pred, None] else: pred = model(im, augment=augment, visualize=visualize)
推理过程总结
推理时间测量:使用性能分析工具开始记录推理过程的时间。
可视化设置:
- 如果启用可视化,创建结果保存路径,以确保结果不会覆盖。
多图像推理:
- 当处理多个图像时,循环遍历每幅图像,分别进行推理。
- 将每幅图像的推理结果合并为一个张量,便于后续处理。
单图像推理:
- 如果只有一幅图像,直接调用模型进行推理
代码逻辑说明
增强与可视化选项:
- 在调用推理时,有提供
augment
和visualize
参数,这意味着模型可以根据需要进行数据增强(如翻转、缩放等),并能够在进行推理的同时可视化结果。这在训练阶段和推理阶段都是非常有用的功能,可以帮助提升模型的鲁棒性和分析效果。灵活性:
- 这段代码的设计展示了很高的灵活性,能够根据输入图像的数量和类型(单幅或多幅)采取不同的推理策略。这种灵活性在实际应用中非常重要,比如在视频分析、电商商品检测等场景中,输入图像的数量可能差异很大。
with dt[2]:#使用性能分析工具 dt[2] 开始记录后处理步骤所需的时间。这有助于评估整个推理过程中的性能瓶颈,尤其是在处理大量检测结果时。 pred = non_max_suppression(pred, conf_thres, iou_thres, classes, agnostic_nms, max_det=max_det) #这一行代码调用了 non_max_suppression 函数,对模型预测的结果应用 NMS。NMS 是一种常用的目标检测后处理技术,旨在消除重复的检测框,从而保留最可能真实的检测框。
NMS 参数说明
pred
:输入的预测结果。它通常是模型对输入图像的初步检测结果,包含图像中所有的边界框、置信度分数和类别信息。
conf_thres
:置信度阈值。只有当检测框的置信度超过这个阈值时,框才会被认为是有效的检测。这样可以过滤掉低信度的检测。
iou_thres
:交并比(Intersection over Union, IoU)阈值。在多个检测框重叠的情况下,IoU 用于判断哪些框是重复的。只有当 IoU 超过这个阈值时,才会认为是重复框。
classes
:可选参数,指定要考虑的类别。如果只对特定类别进行检测,可以将不相关类别的检测框排除在外。
agnostic_nms
:如果为True
,则 NMS 将在所有类别之间进行,而不是在类别内部进行抑制。这意味着即使检测框来自不同的类别,只要它们的置信度足够高,就会相互比较并进行抑制。
max_det
:最大检测框数。这个参数定义了对每张图像最大保留多少个检测框,有助于限制输出的数量。NMS 的作用
提升准确率:NMS 通过删除重叠的预测框,可以防止模型给出多个检测框对于同一目标的情况,从而提高了检测的准确性。
减少输出:经过 NMS 处理后,模型输出的检测框数量会显著减少,这对后续处理和视觉效果的展示至关重要。
预测第二重要的部分,预测过程
# Process predictions for i, det in enumerate(pred): # 这行代码通过 enumerate 函数遍历模型返回的预测结果 pred。pred 通常是一个列表,其中每个元素对应一张图片的检测结果,i 是当前图像的索引,det 是当前图像的检测框信息。 seen += 1 #每处理一幅图像,seen 计数器就增加1,表示已经处理过的图像数量。这在整个推理流程中有助于监控图像处理的进度 if webcam: # batch_size >= 1 p, im0, frame = path[i], im0s[i].copy(), dataset.count s += f"{i}: " else: p, im0, frame = path, im0s.copy(), getattr(dataset, "frame", 0)
if webcam:
这一条件判断检查输入是否来自网络摄像头(即实时视频流)。如果是,执行相应的处理:
p
保存当前图像的路径(对于视频流,路径通常暂时无用)。im0
复制当前图像的原始数据,通常用于后续结果的显示或保存。frame
变量跟踪当前帧的数量。s += f"{i}: "
用于构建输出字符串(可能用于调试或信息记录)。如果输入不是来自网络摄像头,则处理整个图像的结果:
p
仍然保存路径(在批量处理时可能为相同),而im0
复制原始图像数据。frame
则使用getattr
从数据集中获取当前帧的数量,默认为0。这保证了在没有帧信息时不会引起错误。重要性
灵活性:此段代码区分了不同输入来源(网络摄像头与预存图像或视频),并根据输入类型采取不同的处理方式。这样的设计增强了代码的灵活性和可扩展性。
进度跟踪:通过更新
seen
计数和构建输出字符串,代码能够对处理进度进行跟踪,这在推理过程中有助于反馈执行状态。
p = Path(p) # to Path save_path = str(save_dir / p.name) # im.jpg txt_path = str(save_dir / "labels" / p.stem) + ("" if dataset.mode == "image" else f"_{frame}") # im.txt s += "%gx%g " % im.shape[2:] # print string gn = torch.tensor(im0.shape)[[1, 0, 1, 0]] # normalization gain whwh imc = im0.copy() if save_crop else im0 # for save_crop annotator = Annotator(im0, line_width=line_thickness, example=str(names))
这段代码的主要功能是为检测结果准备保存路径和相关信息,并进行必要的处理和初始化,具体步骤如下:
路径处理:
- 将传入的路径
p
转换为Path
对象,以便后续操作变得更加便捷。- 创建保存图像的完整路径
save_path
,其格式为save_dir
目录下的图像文件名(p.name
)。文本文件路径创建:
- 创建用于保存检测结果标签的文件路径
txt_path
:
- 该路径位于
save_dir/labels
目录,文件名与图像文件相同,但扩展名改为.txt
。- 如果数据集模式是图像,则不附加任何后缀;如果是视频帧,则在文件名后加上帧数(
_frame
)。图像尺寸格式化:
- 使用字符串格式化将当前图像的宽度和高度(
im.shape[2:]
)添加到字符串s
中,以便于打印或记录。归一化处理:
- 创建一个归一化增益张量
gn
,它根据输入图像的形状计算宽高归一化比例。这里的torch.tensor(im0.shape)[[1, 0, 1, 0]]
生成了一个包含宽高比例的张量。图像复制:
- 如果启用了裁剪选项(
save_crop
),则复制原始图像im0
,用于保存裁剪后的图像。注释器初始化:
- 创建
Annotator
对象,用于在图像上绘制检测结果的框和标签。对象的初始化使用原始图像im0
、指定的线条宽度line_thickness
,以及类别名称列表names
作为参数。
if len(det): # Rescale boxes from img_size to im0 size det[:, :4] = scale_boxes(im.shape[2:], det[:, :4], im0.shape).round() # Print results for c in det[:, 5].unique(): n = (det[:, 5] == c).sum() # detections per class s += f"{n} {names[int(c)]}{'s' * (n > 1)}, " # add to string # Write results for *xyxy, conf, cls in reversed(det): c = int(cls) # integer class label = names[c] if hide_conf else f"{names[c]}" confidence = float(conf) confidence_str = f"{confidence:.2f}" if save_csv: write_to_csv(p.name, label, confidence_str) if save_txt: # Write to file xywh = (xyxy2xywh(torch.tensor(xyxy).view(1, 4)) / gn).view(-1).tolist() # normalized xywh line = (cls, *xywh, conf) if save_conf else (cls, *xywh) # label format with open(f"{txt_path}.txt", "a") as f: f.write(("%g " * len(line)).rstrip() % line + "\n") if save_img or save_crop or view_img: # Add bbox to image c = int(cls) # integer class label = None if hide_labels else (names[c] if hide_conf else f"{names[c]} {conf:.2f}") annotator.box_label(xyxy, label, color=colors(c, True)) if save_crop: save_one_box(xyxy, imc, file=save_dir / "crops" / names[c] / f"{p.stem}.jpg", BGR=True) # Stream results im0 = annotator.result() if view_img: if platform.system() == "Linux" and p not in windows: windows.append(p) cv2.namedWindow(str(p), cv2.WINDOW_NORMAL | cv2.WINDOW_KEEPRATIO) # allow window resize (Linux) cv2.resizeWindow(str(p), im0.shape[1], im0.shape[0]) cv2.imshow(str(p), im0) cv2.waitKey(1) # 1 millisecond
这段代码的核心功能是处理YOLO模型输出的检测结果,并根据用户的设定执行后续操作。具体步骤如下:
检测结果处理:
- 首先检查检测结果
det
是否为空(len(det)
)。- 如果有检测结果,将检测框的坐标从图像大小缩放到原始图像大小,使用
scale_boxes
函数进行调整。打印检测结果:
- 遍历检测结果中每个唯一的类别(
det[:, 5].unique()
),统计每个类别的检测数量,并将信息拼接到字符串s
中,格式为{数量} {类别名称}{'s'}
(如有多个则加上"s")。写入检测结果:
- 遍历每个检测框,提取坐标(
xyxy
)、置信度(conf
)和类别(cls
)。- 创建标签,决定是否包含置信度信息。
- 将检测信息写入 CSV 或文本文件,根据选项
save_csv
和save_txt
进行存储:
- 如果保存为文本文件,首先转换坐标格式(
xyxy2xywh
)为规范的xywh
格式,并进行归一化。- 根据是否保存置信度,生成相应格式的行并写入文件。
绘制检测框:
- 如果用户选择保存图像、裁剪图像或查看图像,调用
annotator.box_label
方法在图像上绘制检测框和标签。裁剪保存:
- 如果用户选择裁剪(
save_crop
),则将裁剪后的预测框保存到对应的文件夹中。结果流处理:
- 通过
annotator.result()
获取处理后的图像(im0
)。- 如果用户选择查看图像(
view_img
),则使用 OpenCV 显示结果:
- 在 Linux 系统中,检查是否需要创建一个新的可调节窗口。
- 使用
cv2.imshow
函数展示带有检测结果的图像,并通过cv2.waitKey(1)
确保窗口能快速刷新。
# Save results (image with detections) if save_img: if dataset.mode == "image": cv2.imwrite(save_path, im0) else: # 'video' or 'stream' if vid_path[i] != save_path: # new video vid_path[i] = save_path if isinstance(vid_writer[i], cv2.VideoWriter): vid_writer[i].release() # release previous video writer if vid_cap: # video fps = vid_cap.get(cv2.CAP_PROP_FPS) w = int(vid_cap.get(cv2.CAP_PROP_FRAME_WIDTH)) h = int(vid_cap.get(cv2.CAP_PROP_FRAME_HEIGHT)) else: # stream fps, w, h = 30, im0.shape[1], im0.shape[0] save_path = str(Path(save_path).with_suffix(".mp4")) # force *.mp4 suffix on results videos vid_writer[i] = cv2.VideoWriter(save_path, cv2.VideoWriter_fourcc(*"mp4v"), fps, (w, h)) vid_writer[i].write(im0) # Print time (inference-only) LOGGER.info(f"{s}{'' if len(det) else '(no detections), '}{dt[1].dt * 1E3:.1f}ms") # Print results t = tuple(x.t / seen * 1e3 for x in dt) # speeds per image LOGGER.info(f"Speed: %.1fms pre-process, %.1fms inference, %.1fms NMS per image at shape {(1, 3, *imgsz)}" % t) if save_txt or save_img: s = f"\n{len(list(save_dir.glob('labels/*.txt')))} labels saved to {save_dir / 'labels'}" if save_txt else "" LOGGER.info(f"Results saved to {colorstr('bold', save_dir)}{s}") if update: strip_optimizer(weights[0]) # update model (to fix SourceChangeWarning)
这段代码的主要功能是保存YOLO推理结果,记录推理时间,并进行一些模型更新操作。具体步骤包括:
保存检测结果图像:
- 如果
save_img
为True
,根据数据集的模式决定如何保存图像:
- 对于图像模式:使用 OpenCV 的
cv2.imwrite
函数将检测结果图像保存到指定路径save_path
。- 对于视频或流模式:
- 如果当前视频路径
vid_path[i]
不等于save_path
,说明需要开启一个新的视频写入器(VideoWriter
)。- 释放之前的视频写入器(如果存在),并根据视频或流的属性获取帧率和宽高(
fps
、w
、h
)。- 强制将结果视频的路径后缀设置为
.mp4
,然后初始化新的VideoWriter
对象。- 使用
vid_writer[i].write(im0)
将当前的图像帧写入视频文件。输出推理时间:
- 使用
LOGGER.info
输出结果字符串和推理时间。记录的信息包括每个检测的相关字符串s
,如果没有检测结果,输出相应的提示,记录推理持续时间(以毫秒为单位)。打印总体结果统计:
- 计算每张图像的速度,包括预处理、推理和非极大值抑制(NMS)的时长,记录其在日志中。
结果保存提示:
- 如果启用了保存文本或图像,输出保存标签数量的提示信息,表明结果保存的位置。
模型更新:
- 如果设置了
update
选项,则调用strip_optimizer(weights[0])
函数更新模型,主要用于解决源更改警告问题。
detect.py
是YOLO模型用于目标检测的主要脚本。其主要功能可总结为以下几个部分:
-
命令行参数解析:
- 使用
argparse
解析命令行传入的参数,包括模型权重、输入数据源、数据集配置、图像大小、置信度阈值、输出控制选项等,以支持灵活的用户配置。
- 使用
-
环境和依赖设置:
- 确保必要的目录存在,并根据命令行参数设置结果保存路径。
-
模型加载:
- 选择计算设备(例如GPU或CPU),并通过指定的权重加载YOLO模型。模型参数如步长、类别名称等会被提取用于后续推理。
-
数据加载:
- 定义数据加载器。根据输入源的类型(图像、视频、实时流等)选择合适的加载方式。支持对多个输入源的处理。
-
推理过程:
- 对输入数据进行推理,得到检测结果。包括:
- 检测框调整,确保检测框坐标与原始图像大小相匹配。
- 结果统计和标签生成,根据每个类别的检测数量和置信度生成标签信息。
- 对输入数据进行推理,得到检测结果。包括:
-
结果保存:
- 将处理后的检测结果保存为图像、文本文件或CSV文件。支持选项控制输出结果的格式与内容(例如,是否保存置信度信息、是否裁剪目标等)。
-
结果可视化:
- 可以选择在窗口中显示检测结果,便于实时查看。
-
性能监控和结果日志:
- 记录推理速度和时间,提供推理过程的性能信息。
-
模型更新:
- 选项控制时,支持更新模型以确保使用最新的权重。