首先执行扩展包的导入:

import argparse
import os
import platform
import sys
from pathlib import Path

import torch

FILE = Path(__file__).resolve() #获取detect.py在电脑中的绝对路径
ROOT = FILE.parents[0] # 获取detect.py的父目录(绝对路径)
if str(ROOT) not in sys.path: # 判断detect.py的父目录是否存在于模块的查询路径列表
sys.path.append(str(ROOT)) # add ROOT to PATH
ROOT = Path(os.path.relpath(ROOT, Path.cwd())) # 将绝对路径转换为相对路径

from models.common import DetectMultiBackend
from utils.dataloaders import IMG_FORMATS, VID_FORMATS, LoadImages, LoadScreenshots, LoadStreams
from utils.general import (LOGGER, Profile, check_file, check_img_size, check_imshow, check_requirements, colorstr, cv2,
increment_path, non_max_suppression, print_args, scale_boxes, strip_optimizer, xyxy2xywh)
from utils.plots import Annotator, colors, save_one_box
from utils.torch_utils import select_device, smart_inference_mode

包导入完成之后,执行最下面的这段代码:

if __name__ == '__main__':
opt = parse_opt() #解析参数
main(opt)

这段代码用到了parse_opt()这个函数,它的功能主要是解析参数,主要参数解析如下:

"""
--weights:权重的路径地址
--source:测试数据,可以是图片/视频路径,也可以是'0'(电脑自带摄像头),也可以是rtsp等视频流
--output:网络预测之后的图片/视频的保存路径
--img-size:网络输入图片大小
--conf-thres:置信度阈值
--iou-thres:做nms的iou阈值
--device:是用GPU还是CPU做推理
--view-img:是否展示预测之后的图片/视频,默认False
--save-txt:是否将预测的框坐标以txt文件形式保存,默认False
--classes:设置只保留某一部分类别,形如0或者0 2 3
--agnostic-nms:进行nms是否也去除不同类别之间的框,默认False
--augment:推理的时候进行多尺度,翻转等操作(TTA)推理
--update:如果为True,则对所有模型进行strip_optimizer操作,去除pt文件中的优化器等信息,默认为False
--project:推理的结果保存在runs/detect目录下
--name:结果保存的文件夹名称
"""
该部分来源于博主“炮哥带你学”——‘目标检测---教你利用yolov5训练自己的目标检测模型’一文,
原文地址:https://blog.csdn.net/didiaopao/article/details/119954291?spm=1001.2014.3001.5502

在parse_opt()执行完成之后,会将opt传给函数main():

def main(opt):
check_requirements(exclude=('tensorboard', 'thop')) #检测中的扩展包是否安装
run(**vars(opt))

main()函数中调用了函数run(),run()主要代码解析如下:

run()主要分为了六个部分:

  1. 处理预测路径

    #处理预测路径
    source = str(source) #将路径转为字符串类型(data\\images\\bus.jpg)
    save_img = not nosave and not source.endswith('.txt') # 保存预测结果 #suffix函数表示文件类型,suffix[1:]表示从.jpg中截取jpg,然后判断jpg是否位于(IMG_FORMATS + VID_FORMATS)中
    is_file = Path(source).suffix[1:] in (IMG_FORMATS + VID_FORMATS) #判断路径是否为网络流的格式(lower()作用是将字母全部转换为小写)
    is_url = source.lower().startswith(('rtsp://', 'rtmp://', 'http://', 'https://')) #判断路径是否为‘0’(如果为‘0’会打开电脑摄像头),是否是.streams文件格式,是否是网络流地址
    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,下载图片或视频
  2. 新建保存结果的文件夹

    # Directories,新建保存结果的文件夹
    
        #增量式地产生文件夹(exp,exp1,exp2...)
    save_dir = increment_path(Path(project) / name, exist_ok=exist_ok) # increment run #在exp文件夹下新建labels文件夹
    (save_dir / 'labels' if save_txt else save_dir).mkdir(parents=True, exist_ok=True) # make dir
  3. 加载模型的权重

    # 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
  4. 加载待预测的图片

    # Dataloader,加载待预测的图片
    bs = 1 # batch_size
    if webcam: #根据‘处理预测路径’代码部分得webcam一般为false
    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: #根据‘处理预测路径’代码部分得screenshot一般为false
    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
  5. 执行模型的推理过程

    # Run inference,执行模型的推理过程

    #warmup初始化一张空白图片并传入到模型当中,让模型执行一次前向传播
    model.warmup(imgsz=(1 if pt or model.triton else bs, 3, *imgsz)) # warmup seen, windows, dt = 0, [], (Profile(), Profile(), Profile()) #定义变量存储中间结果信息 #path:路径 im:处理后的图片 im0s:原图 vid_cap:none s:图片的打印信息
    for path, im, im0s, vid_cap, s in dataset:
    with dt[0]:
    im = torch.from_numpy(im).to(model.device) #将im转化为pytorch支持的格式并放到设备中
    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

    # Inference,对上面整理好的图片进行预测
    with dt[1]:
    visualize = increment_path(save_dir / Path(path).stem, mkdir=True) if visualize else False
    pred = model(im, augment=augment, visualize=visualize)

    # NMS,进行非极大值过滤
    with dt[2]:
    pred = non_max_suppression(pred, conf_thres, iou_thres, classes, agnostic_nms, max_det=max_det)

    # Second-stage classifier (optional)
    # pred = utils.general.apply_classifier(pred, classifier_model, im, im0s)

    # Process predictions
    for i, det in enumerate(pred): # 遍历每张图片
    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)

    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]] #获取原图宽和高
    imc = im0.copy() if save_crop else im0 #判断是否将检测框部分裁剪下来
    annotator = Annotator(im0, line_width=line_thickness, example=str(names)) #定义绘图工具
    if len(det):
    #坐标映射,方便在原图上画检测框
    det[:, :4] = scale_boxes(im.shape[2:], det[:, :4], im0.shape).round()

    # 遍历det
    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

    # 是否保存预测结果
    for *xyxy, conf, cls in reversed(det):
    if save_txt: # 保存为txt
    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: # 只在图片上添加检测框
    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

    # 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")
  6. 打印输出信息

    # Print results,打印输出信息
    t = tuple(x.t / seen * 1E3 for x in dt) # 统计每张图片的平均时间
    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)

Yolov5代码详解——detect.py的更多相关文章

  1. Kaggle网站流量预测任务第一名解决方案:从模型到代码详解时序预测

    Kaggle网站流量预测任务第一名解决方案:从模型到代码详解时序预测 2017年12月13日 17:39:11 机器之心V 阅读数:5931   近日,Artur Suilin 等人发布了 Kaggl ...

  2. Github-jcjohnson/torch-rnn代码详解

    Github-jcjohnson/torch-rnn代码详解 zoerywzhou@gmail.com http://www.cnblogs.com/swje/ 作者:Zhouwan  2016-3- ...

  3. ARM Cortex-M底层技术(2)—启动代码详解

    杂谈 工作了一天,脑袋比较乱.一直想把底层的知识写成一个系列,希望可以坚持下去.为什么要写底层的东西呢?首先,工作用到了这部分内容,最近和内部Flash打交道比较多,自然而然会接触到一些底层的东西:第 ...

  4. python golang中grpc 使用示例代码详解

    python 1.使用前准备,安装这三个库 pip install grpcio pip install protobuf pip install grpcio_tools 2.建立一个proto文件 ...

  5. BM算法  Boyer-Moore高质量实现代码详解与算法详解

    Boyer-Moore高质量实现代码详解与算法详解 鉴于我见到对算法本身分析非常透彻的文章以及实现的非常精巧的文章,所以就转载了,本文的贡献在于将两者结合起来,方便大家了解代码实现! 算法详解转自:h ...

  6. ASP.NET MVC 5 学习教程:生成的代码详解

    原文 ASP.NET MVC 5 学习教程:生成的代码详解 起飞网 ASP.NET MVC 5 学习教程目录: 添加控制器 添加视图 修改视图和布局页 控制器传递数据给视图 添加模型 创建连接字符串 ...

  7. Github-karpathy/char-rnn代码详解

    Github-karpathy/char-rnn代码详解 zoerywzhou@gmail.com http://www.cnblogs.com/swje/ 作者:Zhouwan  2016-1-10 ...

  8. 代码详解:TensorFlow Core带你探索深度神经网络“黑匣子”

    来源商业新知网,原标题:代码详解:TensorFlow Core带你探索深度神经网络“黑匣子” 想学TensorFlow?先从低阶API开始吧~某种程度而言,它能够帮助我们更好地理解Tensorflo ...

  9. JAVA类与类之间的全部关系简述+代码详解

    本文转自: https://blog.csdn.net/wq6ylg08/article/details/81092056类和类之间关系包括了 is a,has a, use a三种关系(1)is a ...

  10. Java中String的intern方法,javap&cfr.jar反编译,javap反编译后二进制指令代码详解,Java8常量池的位置

    一个例子 public class TestString{ public static void main(String[] args){ String a = "a"; Stri ...

随机推荐

  1. DataGear 制作基于Vue2、Element UI前端框架的数据可视化看板

    DataGear 数据可视化看板内置了一些基本.简单的页面交互组件,当它们无法满足实际看板需求时,可以引入更流行和强大的前端框架. 本文以Vue2.Element UI前端框架为例,介绍如何制作具有更 ...

  2. Linux 多进程服务配置 systemd

    目录 Linux 多进程服务配置 systemd sysvinit和systemd 多进程保活 创建配置文件(设定重试次数) 多进程服务管理 链式启动(服务依赖) 指定关闭进程方式 - ExecSto ...

  3. python使用selenium适配谷歌浏览器插件, chromedriver与chrome各版本及下载地址

    python selenium使用,需要谷歌chromedriver.exe插件 chromedriver.exe插件是放在python的安装目录下(亲测,其它的都不对) 以下是chromedrive ...

  4. PhpStorm设置FTP功能

    1.版本介绍 本文操作针对PhpStorm 2020.1版本 2.[ctrl + alt + s]打开设置,选择"Build,Execution,Deployment" 3.选择& ...

  5. Zabbix_get基础命令浅析

    zabbix_get是Zabbix监控系统的一个命令行工具,可以用于从Zabbix服务器或代理获取数据.以下是zabbix_get的基本使用方法: 1.获取一个单独的键值对 使用以下命令可以获取一个单 ...

  6. 重新认识 tag 快照 git (项目临时添加需求,之前有分支合并,导致从节点拉分支不行了,因为没有tag快照)

    之前的tag认知 之前一直以为tag就是在git的提交commit上打一个标,然后可以拉出分支.之前没太重视. 因为我觉得 可以直接从某个commit直接拉出分支,这打不打tag无所谓 翻车现场 今天 ...

  7. vscode 格式化 vue 等文件的 配置 eslint vetur prettier Beautify

    需求 自动格式化需求 多行回车 合并一行,去分号 最后一个逗号,自动删除,符合eslint 结果 虽然能用了,但是 百度好几个方案,也不知道哪个对哪个,太忙没时间弄了. 配置文件记录 eslint 得 ...

  8. ubuntu添加桌面快捷打开方式

    不太喜欢ubuntu开机后空荡荡的桌面,希望可以有些像windows一样的快捷打开方式.看了一些博客,也自己探索了一下,发现了在ubuntu中添加软件自带的桌面快捷打开方式的方法. 在终端 cd /u ...

  9. ble的notification和indication的区别和联系

    Ble服务端传输消息有两个常用手段,notification和indication.那么这两者之间有什么区别呢? Notification 不需要应答,所以服务端发送的消息,它自己并不知道消息是否发送 ...

  10. 获取Android设备系统apk

    前提条件是:电脑adb连接Android设备 打开命令好窗口,输入指令adb shell "dumpsys window|grep mCurrentFocus" 输入指令adb s ...