基于Yolov5+Deepsort+Pyqt的单目标跟踪器设计track.py:

import

  • sys.path.insert(0, './yolov5')
  • sys.path=E:\Yolov5\DeepSort\Yolov5_DeepSort\Yolov5_DeepSort
  • track.py是 YOLOv5 的目标跟踪脚本,通常需要调用 yolov5 目录下的模块。

串口

  • port_list = list(serial.tools.list_ports.comports())获取当前计算机上所有可用的串口设备列表,并转换为 Python 列表。
  • if len(port_list) == 0:如果 port_list 为空(len(port_list) == 0),打印 'none',表示未检测到任何串口设备。
    否则,遍历列表并逐个打印每个串口的详细信息(如名称、描述、制造商等)。
    portName = "COM3" # 端口名
    baudRate = 9600 # 波特率
    timeOut = 3
    portName:指定要使用的串口名称(如 COM3 是 Windows 下的典型串口名,Linux/Mac 下可能是 /dev/ttyUSB0)。
    baudRate:设置通信波特率(常见值如 9600、115200 等),需与设备端配置一致。
    timeOut:设置串口读取超时时间(秒),超过该时间未收到数据则抛出异常。
  • 作用:
    硬件交互:与 Arduino、传感器或其他通过串口通信的设备连接。
    调试:确认系统中可用的串口设备,避免手动输入错误端口名。
    初始化配置:为后续的串口通信(如 serial.Serial(portName, baudRate, timeout=timeOut))提供参数。

palette

  • palette = (2 ** 11 - 1, 2 ** 15 - 1, 2 ** 20 - 1)生成一个包含三个整数的元组,用于表示某种颜色或ID的调色板(Palette)。
    2 ** 11 - 1 = 2047
    (二进制:11111111111,11位全1)
    2 ** 15 - 1 = 32767
    (二进制:111111111111111,15位全1)
    2 ** 20 - 1 = 1048575
    (二进制:11111111111111111111,20位全1)

datect

initialize deepsort

  • cfg = get_config()调用 get_config() 函数获取一个默认的配置对象(通常是一个 Config 或 EasyDict 类型的对象)。
  • cfg.merge_from_file(opt.config_deepsort)从文件 opt.config_deepsort(路径由命令行参数 opt 提供)加载 DeepSort 的配置文件,并合并到 cfg 中。

    REID_CKPT:指定 ReID(重识别)模型的权重文件路径。
    MAX_DIST: 特征匹配的最大余弦距离阈值。当两个目标的特征向量距离超过 0.2 时,认为它们不匹配。值越小,匹配越严格。
    MIN_CONFIDENCE:检测结果的最小置信度阈值。忽略置信度低于 0.3 的检测框.
    NMS_MAX_OVERLAP:非极大值抑制(NMS)的重叠阈值。若两个检测框的 IoU(交并比)超过 0.5,则保留置信度更高的框,抑制另一个。
    MAX_IOU_DISTANCE:ioU 距离的最大阈值,用于关联检测框和跟踪轨迹。当检测框与预测轨迹的 IoU 距离 ≤ 0.7 时,认为它们可能属于同一目标。
    MAX_AGE:轨迹的最大存活帧数,若轨迹连续 70 帧未被匹配到检测框,则删除该轨迹。
    N_INIT:新检测目标需连续 3 帧成功匹配,才确认为有效轨迹
    NN_BUDGET: 每个轨迹最多保留 100 个历史特征,用于后续匹配,平衡内存和跟踪精度。
  • if os.path.exists(out):检查输出目录 out 是否已存在。如果目录存在,可能需要先删除旧数据以避免冲突。
  • shutil.rmtree(out)递归删除输出目录 out 及其所有内容.确保每次运行时输出目录是空的,避免旧结果干扰新结果
  • os.makedirs(out)重新创建输出目录 out。
  • half = device.type != 'cpu'根据设备类型决定是否启用半精度(FP16)计算。
    evice.type 是运行设备(如 'cuda' 或 'cpu')。
    半精度(half=True)仅支持 GPU(CUDA),可加速计算并减少显存占用。
    如果是 CPU 运行,则强制使用全精度(half=False)。

load model

  • model = attempt_load(yolo_weights, map_location=device)
    加载预训练的 YOLO 模型。
    yolo_weights:YOLO 模型权重文件的路径(如 yolov5s.pt)。
    map_location=device:指定模型加载的设备(如 'cuda:0' 或 'cpu')。
    默认加载全精度(FP32)模型。
  • stride = int(model.stride.max())
    获取模型的最大步长(stride)。stride 是 YOLO 模型下采样的倍数(如 8、16、32),影响输入图像的尺寸要求。YOLOv5 的 stride 通常是 32(最终特征图的缩放比例)。
  • imgsz = check_img_size(imgsz, s=stride)check_img_size 确保 imgsz 是 stride 的整数倍(避免尺寸对齐问题)。
    例如,若 stride=32,imgsz=640 会被接受,而 imgsz=641 会被调整为 640。
  • names = model.module.names if hasattr(model, 'module') else model.names
    获取模型训练的类别名称列表
  • if half: model.half()将模型转换为半精度(FP16)模式。
  • vid_path, vid_writer = None, None初始化视频路径和视频写入器变量为 None。
  • show_vid = check_imshow()检查当前环境是否支持图像显示
    check_imshow():通常尝试创建一个测试窗口,验证是否支持 cv2.imshow()。
    如果环境不支持(如无 GUI 的服务器),则 show_vid 会被设为 False,避免显示报错。
  • cudnn.benchmark = True启用 cuDNN 的自动优化模式,加速固定尺寸图像的推理(适用于摄像头输入分辨率不变的情况)。

Run inference

  • model(torch.zeros(1, 3, imgsz, imgsz).to(device).type_as(next(model.parameters())))模型预热(Warm-up)。
    生成一个全零的虚拟输入张量,形状为 (1, 3, imgsz, imgsz)(批大小 1,3 通道,尺寸 imgsz x imgsz)。
    to(device):将张量移动到指定设备(GPU/CPU)。
    type_as(...):确保张量数据类型与模型参数一致(如 FP16/FP32)。
    执行一次前向推理(model(...))。
    初始化 GPU 的 CUDA 上下文,避免首次正式推理时的延迟。

模型推理 NMS/process/detections

  • for frame_idx, (path, img, im0s, vid_cap) in enumerate(dataset):
    从数据加载器 dataset 中逐帧读取数据,返回:
    path:当前帧的路径(如视频文件路径或摄像头索引)。
    img:预处理后的图像(尺寸调整、归一化等,形状为 [H, W, C] 或 [B, H, W, C])。
    im0s:原始图像(未缩放的 OpenCV 格式,用于可视化或保存结果)。
    vid_cap:视频捕获对象(仅视频文件有效)。
  • img = torch.from_numpy(img).to(device)将 img 转换为 PyTorch 张量并移动到指定设备(GPU/CPU)。
  • img = img.half() if half else img.float() # uint8 to fp16/32根据 half 参数选择半精度(FP16)或全精度(FP32)。
  • img /= 255.0 # 0 - 255 to 0.0 - 1.0归一化像素值到 [0.0, 1.0] 范围。
  • if img.ndimension() == 3:img = img.unsqueeze(0)如果输入是单张图像(ndimension() == 3),增加批次维度(unsqueeze(0))。
  • t1 = time_synchronized()time_synchronized():同步 GPU 操作并记录时间戳 t1(用于计算推理耗时)。
  • pred = model(img, augment=opt.augment)[0]
    执行模型推理:augment:是否启用数据增强(如多尺度测试)。
    pred:模型的原始输出(未处理的检测框,形状为 [N, 6],其中 6 为 [x1, y1, x2, y2, conf, cls])。
  • non_max_suppression过滤冗余检测框:
    opt.conf_thres:置信度阈值(低于此值的检测框被丢弃)。
    opt.iou_thres:IoU 阈值(重叠高于此值的框会被合并)。
    classes:指定保留的类别(如只检测行人)。
    agnostic:是否跨类别进行 NMS(如合并不同类别的重叠框)。
  • p, s, im0 = path[i], '%g: ' % i, im0s[i].copy()webcam 为 True 时,处理多路视频流(path[i] 和 im0s[i])。
  • p, s, im0 = path, '', im0swebcam 为 False 时,处理单路输入(path 和 im0s)。
  • s += '%gx%g ' % img.shape[2:] # print string构建日志字符串(如 "640x480 ")。

boxes

  • det[:, :4] = scale_coords(img.shape[2:], det[:, :4], im0.shape).round()
    将检测框坐标从模型输入尺寸(img.shape[2:],如 640x640)缩放到原始图像尺寸(im0.shape,如 1920x1080)。输出:det 的坐标变为 [x1, y1, x2, y2](原始图像尺度)。
    scale_coords:线性缩放坐标(保持宽高比)。
    .round():将坐标四舍五入为整数(便于后续绘制或处理)。
  • s += '%g %ss, ' % (n, names[int(c)])
    示例输出:若检测到 2 人和 1 车,s 变为 "2 persons, 1 cars, "。
  • x_c, y_c, bbox_w, bbox_h = xyxy_to_xywh(*xyxy)将 YOLO 的 xyxy 格式转换为 DeepSort 所需的 [x_center, y_center, width, height] 格式。
  • outputs = deepsort.update(xywhs, confss, im0)
    输入:
    xywhs:检测框的中心坐标和宽高。
    confss:检测置信度。
    im0:原始图像(用于视觉特征提取)。
    输出:outputs 形状为 [M, 5],每行为 [x1, y1, x2, y2, track_id](跟踪后的目标框和 ID)

Stream results

xyxy to xywh


输入:*xyxy
可变参数,接受 4 个值(x1, y1, x2, y2),表示检测框的左上角和右下角坐标。
输出:返回 4 个值(x_c, y_c, w, h),即中心点坐标和宽高。

  • bbox_left = min([xyxy[0].item(), xyxy[2].item()])
  • bbox_top = min([xyxy[1].item(), xyxy[3].item()])
    确保 bbox_left 和 bbox_top 是左上角坐标
  • bbox_w = abs(xyxy[0].item() - xyxy[2].item())
  • bbox_h = abs(xyxy[1].item() - xyxy[3].item())
  • .item() 的作用:
    如果输入是 PyTorch 张量,.item() 将其转换为 Python 标量(数值)。
    使用绝对值保证宽高为正数。
  • x_c = (bbox_left + bbox_w / 2)
  • y_c = (bbox_top + bbox_h / 2)

xyxy to tlwh


用于将目标检测框的坐标从 [x1, y1, x2, y2](左上角和右下角坐标)格式转换为 [top_left_x, top_left_y, width, height](左上角坐标 + 宽高)

  • for i, box in enumerate(bbox_xyxy):遍历每个检测框
  • x1, y1, x2, y2 = [int(i) for i in box]将坐标值强制转换为整数(适用于浮点数输入或张量)。

label color


根据目标类别标签(label)生成一个固定的颜色值

  • def compute_color_for_labels(label):
    输入:label
    目标的类别标签(整数,如 0 表示 "person",1 表示 "car")。
    输出:一个 RGB 颜色元组(如 (255, 128, 0)),用于可视化。
  • color = [int((p * (label ** 2 - label + 1)) % 255) for p in palette]
    公式 label ** 2 - label + 1 是一个简单的哈希函数,确保不同 label 映射到不同的颜色值。
    % 255 保证结果在 [0, 255] 范围内(RGB 值的合法区间)。
  • return tuple(color)
    将列表 color 转换为元组(如 (R, G, B)),便于 OpenCV 等库使用。

draw boxes







  • id = int(identities[i]) if identities is not None else 0
    若未提供 identities,默认 ID 为 0。
  • color = compute_color_for_labels(id)
    调用 compute_color_for_labels根据 ID 生成唯一颜色(用于框和文本)。
赞赏