YOLOv6-4.0部分代码阅读笔记-inferer.py

inferer.py

yolov6\core\inferer.py

目录

inferer.py

1.所需的库和模块

2.class Inferer: 

3.class CalcFPS: 


1.所需的库和模块

#!/usr/bin/env python3
# -*- coding:utf-8 -*-
# 用于模型的推理。
import os
import cv2
import time
import math
import torch
import numpy as np
import os.path as ospfrom tqdm import tqdm
from pathlib import Path
from PIL import ImageFont
from collections import dequefrom yolov6.utils.events import LOGGER, load_yaml
from yolov6.layers.common import DetectBackend
from yolov6.data.data_augment import letterbox
from yolov6.data.datasets import LoadData
from yolov6.utils.nms import non_max_suppression
from yolov6.utils.torch_utils import get_model_info

2.class Inferer: 

class Inferer:def __init__(self, source, webcam, webcam_addr, weights, device, yaml, img_size, half):# 将构造函数的参数直接添加到实例属性中。self.__dict__.update(locals())# Init model    初始化模型。# 确保设备属性被正确设置。self.device = device# 确保图像尺寸属性被正确设置。self.img_size = img_size# 使用 PyTorch 的 torch.device 来设置模型运行的设备。如果 cuda 为 True (即用户指定了 GPU 设备并且 CUDA 可用),则设备设置为 cuda:device ,其中 device 是传入的参数。否则,设备设置为 'cpu' 。cuda = self.device != 'cpu' and torch.cuda.is_available()# torch.device(device_str)# torch.device 是 PyTorch 中的一个函数,用于创建一个表示设备的对象,该对象可以是 CPU 或者 CUDA(GPU)。这个函数的主要作用是抽象出设备的选择,使得代码可以在不同的设备上运行而不需要做太多修改。# 其中 device_str 是一个字符串,可以是以下几种形式 :# 1. 'cpu' :表示使用 CPU。# 2. 'cuda' :表示使用第一个 CUDA 设备(GPU)。# 3. 'cuda:N' :表示使用编号为   N   的 CUDA 设备,其中   N   是一个非负整数。# 4. 'cuda:0' :表示使用编号为 0 的 CUDA 设备。# 5. 'cuda:1' :表示使用编号为 1 的 CUDA 设备,以此类推。# 如果指定了 CUDA 设备,但是机器上没有可用的 CUDA 设备, torch.device 会抛出一个错误。# 除了指定设备, torch.device 还可以接受一个字典或者一个 torch.device 对象作为参数,这时候它会返回一个与输入相同的 torch.device 对象。self.device = torch.device(f'cuda:{device}' if cuda else 'cpu')# 使用 DetectBackend 类来加载模型。 weights 参数是模型权重文件的路径, device 是模型将要运行的设备。 DetectBackend 类可能是 YOLOv6 框架中用于模型推理的后端。# class DetectBackend(nn.Module): -> def __init__(self, weights='yolov6s.pt', device=None, dnn=True):self.model = DetectBackend(weights, device=self.device)# 从模型中获取步长( stride ),这个属性通常用于图像尺寸的调整和锚点(anchor)的计算。self.stride = self.model.stride# def load_yaml(file_path): -> 从 yaml 文件加载数据。函数返回从 YAML 文件中加载的数据,通常是一个字典或列表,具体取决于 YAML 文件的内容。 -> return data_dicts# 使用 load_yaml 函数从 YAML 文件中加载类别名称。这个 YAML 文件包含了数据集的类别信息, ['names'] 键对应的值是一个包含所有类别名称的列表。self.class_names = load_yaml(yaml)['names']# 调用 self.check_img_size 方法来检查和调整 img_size ,以确保它与模型的步长兼容。这个方法可能是自定义的,用于确保输入图像的尺寸是模型所支持的。# def check_img_size(self, img_size, s=32, floor=0):# -> 作用是确保输入的图像尺寸 img_size 是模型步长 s 的倍数。如果 img_size 是列表,直接返回调整后的尺寸列表。如果 img_size 是整数,返回一个包含两个相同值的列表,这两个值都是调整后的尺寸。# -> return new_size if isinstance(img_size,list) else [new_size]*2self.img_size = self.check_img_size(self.img_size, s=self.stride)  # check image size    检查图像大小。# 将 half 属性设置为传入的值,这个属性可能用于控制是否使用半精度(FP16)进行推理,以加速推理过程并减少内存使用。self.half = half# 切换模型至部署状态。# Switch model to deploy statusself.model_switch(self.model.model, self.img_size)# Half precision# self.half :一个布尔值,指示是否使用半精度(FP16)。# self.device.type != 'cpu' :确保设备不是CPU,因为CPU不支持半精度运算。if self.half & (self.device.type != 'cpu'):# 如果条件为真,则调用 self.model.model.half() 将模型转换为半精度。self.model.model.half()else:# 否则,调用 self.model.model.float() 将模型转换为全精度(FP32),并将 self.half 设置为 False 。self.model.model.float()self.half = False# 这个条件确保只在非CPU设备上执行预热操作。if self.device.type != 'cpu':# 这一行执行一次模型推理,使用一个全零的张量作为输入 :# torch.zeros(1, 3, *self.img_size) :创建一个形状为 (1, 3, img_size, img_size) 的全零张量,其中 1 是批量大小, 3 是颜色通道数(对于RGB图像), img_size 是图像的尺寸。# .to(self.device) :将张量移动到指定的设备(如GPU)。# .type_as(next(self.model.model.parameters())) :确保输入张量的数据类型与模型参数的数据类型一致。self.model(torch.zeros(1, 3, *self.img_size).to(self.device).type_as(next(self.model.model.parameters())))  # warmup# 加载数据。# Load data# 一个布尔值,指示是否使用网络摄像头作为输入源。self.webcam = webcam# 如果使用网络摄像头,这是摄像头的地址。self.webcam_addr = webcam_addr# 调用 LoadData 类(可能是自定义的)来加载数据。这个类根据提供的 source 、 webcam 和 webcam_addr 参数确定数据来源,并返回一个包含数据的实例。# 如果 webcam 为 True ,则 LoadData 可能会连接到网络摄像头并开始捕获视频流。# 如果 source 是文件路径或文件夹, LoadData 可能会读取文件列表或视频帧。self.files = LoadData(source, webcam, webcam_addr)# 输入源的路径,可以是单个文件、文件夹或摄像头。self.source = source# 将模型中的 RepVGGBlock 层切换到部署(deploy)状态。 RepVGGBlock 是一个可重参数化的网络层,它在训练和部署时有不同的结构。在训练时, RepVGGBlock 会使用一种结构以便于学习,而在部署时,它会切换到另一种更高效的结构以减少计算量和模型大小。# 1.self :类的实例本身。# 2.model :需要被切换到部署状态的模型。# 3.img_size :图像的尺寸,尽管在这个函数中并没有直接使用。def model_switch(self, model, img_size):# 模型切换到部署状态。''' Model switch to deploy status '''# 从 yolov6.layers.common 模块导入 RepVGGBlock 类。from yolov6.layers.common import RepVGGBlock# 使用 model.modules() 遍历模型中的所有层(模块)。for layer in model.modules():# 对于每个层,检查它是否是 RepVGGBlock 的实例。if isinstance(layer, RepVGGBlock):# 如果层是 RepVGGBlock 的实例,调用该层的 switch_to_deploy 方法将其切换到部署状态。# def switch_to_deploy(self):layer.switch_to_deploy()# 切换模型以部署模式。LOGGER.info("Switch model to deploy modality.")# conf_thres :置信度阈值,用于过滤检测结果。# iou_thres :交并比(Intersection over Union)阈值,用于非极大值抑制(NMS)。# classes :要检测的类别列表。# agnostic_nms :是否使用与类别无关的NMS。# max_det :每张图像最大检测数量。# save_dir :保存结果的目录。# save_txt :是否保存检测结果的文本文件。# save_img :是否保存带有检测框的图像。# hide_labels :是否隐藏标签。# hide_conf :是否隐藏置信度分数。# view_img :是否在推理时显示图像。def infer(self, conf_thres, iou_thres, classes, agnostic_nms, max_det, save_dir, save_txt, save_img, hide_labels, hide_conf, view_img=True):# 模型推理和结果可视化。''' Model Inference and results visualization '''# vid_path :用于存储视频路径,初始设置为 None 。# vid_writer :用于存储视频写入对象,初始设置为 None 。这可能用于将检测结果写入视频文件。# windows :用于存储窗口信息的列表,初始为空列表。这可能用于处理多窗口或多图像的情况。vid_path, vid_writer, windows = None, None, []# fps_calculator :创建一个 CalcFPS 类的实例,用于计算帧率。# class CalcFPS: -> def __init__(self, nsamples: int = 50):fps_calculator = CalcFPS()# 使用 tqdm 进度条遍历 self.files ,这是一个由 LoadData 类创建的数据迭代器。# img_src :当前帧图像。# img_path :图像的文件路径。# vid_cap :视频捕获对象,用于从视频文件中读取帧。for img_src, img_path, vid_cap in tqdm(self.files):# 调用 process_image 方法对图像进行预处理,包括缩放、归一化等操作。# def process_image(img_src, img_size, stride, half): -> 在图像推理之前处理图像。返回值 : 1.image :预处理后的图像张量。 2.img_src  :原始图像数据或路径。 -> return image, img_srcimg, img_src = self.process_image(img_src, self.img_size, self.stride, self.half)# 将预处理后的图像移动到指定的设备(如GPU)。img = img.to(self.device)# 检查图像的维度,如果只有3维(通道、高度、宽度),则添加一个批次维度。if len(img.shape) == 3:# 通过 None 索引添加批次维度。img = img[None]# expand for batch dim    扩展批量维度。# 记录推理开始的时间。t1 = time.time()# 将预处理后的图像输入模型,获取预测结果。pred_results = self.model(img)# 调用 non_max_suppression 函数对预测结果应用NMS,过滤掉低置信度的检测框,并去除重叠的检测框。# 1.pred_results :模型的预测结果。# 2.conf_thres :置信度阈值。# 3.iou_thres :交并比阈值。# 4.classes :要检测的类别列表。# 5.agnostic_nms :是否使用与类别无关的NMS。# 6.max_det :每张图像最大检测数量。det = non_max_suppression(pred_results, conf_thres, iou_thres, classes, agnostic_nms, max_det=max_det)[0]# 记录推理结束的时间。t2 = time.time()# self.webcam :一个布尔值,指示是否使用网络摄像头作为输入源。if self.webcam:#  如果 self.webcam 为 True ,则保存路径 save_path 和文本路径 txt_path 都设置为 save_dir 目录下,以 self.webcam_addr 为名称的子目录。save_path = osp.join(save_dir, self.webcam_addr)txt_path = osp.join(save_dir, self.webcam_addr)else:# 在嵌套目录中创建镜像图像目录结构的输出文件。# Create output files in nested dirs that mirrors the structure of the images' dirs# dirname = os.path.dirname(path)# osp.dirname 是 Python os.path 模块中的一个函数,它用于返回路径中的目录名称。# 如果给定的路径是一个文件路径, osp.dirname 将返回该文件所在的目录路径;如果给定的路径本身就是一个目录,它将返回该目录的父目录路径。# path :输入的路径字符串。# 返回值 : dirname 是一个字符串,表示输入路径的目录部分。# 如果 self.webcam 为 False ,则根据输入图像的路径和输入源的路径来创建一个相对路径 rel_path 。rel_path = osp.relpath(osp.dirname(img_path), osp.dirname(self.source))# 保存图像结果的路径,设置为 save_dir 下,与输入图像相对位置相同的路径。save_path = osp.join(save_dir, rel_path, osp.basename(img_path))  # im.jpg# 保存检测结果文本文件的路径,设置为 save_dir 下,与输入图像相对位置相同的路径,但在 labels 子目录中,并去掉文件扩展名。txt_path = osp.join(save_dir, rel_path, 'labels', osp.splitext(osp.basename(img_path))[0])# 创建保存图像结果所需的目录结构,如果目录已存在,则不会抛出异常。os.makedirs(osp.join(save_dir, rel_path), exist_ok=True)# 这行代码创建了一个包含图像原始尺寸的 PyTorch 张量,然后通过索引 [1, 0, 1, 0] 来选择宽度和高度(重复两次)以形成 whwh 的格式。这种格式通常用于归一化图像尺寸,其中 w 是宽度, h 是高度。# 例如,如果 img_src 的形状是 (高度, 宽度, 通道数) ,那么 gn 将是一个包含 (宽度,高度,宽度,高度) 的张量。gn = torch.tensor(img_src.shape)[[1, 0, 1, 0]]  # normalization gain whwh    归一化增益 whwh。# 这行代码创建了 img_src 图像的副本。这通常在需要保留原始图像数据进行后续操作或比较时使用。img_ori = img_src.copy()# 检查图像和字体。# check image and font# assert 是 Python 中的断言语句,用于验证某个条件是否为真。如果条件为假,则抛出 AssertionError 。# 这里检查 img_ori 的数据是否是连续的。在 PyTorch 中, Tensor 的内存布局可以是连续的或非连续的。连续的 Tensor 在内存中是一块连续的空间,这通常对于某些操作(如某些类型的复制或数学运算)是必需的。# 图像需要连续,请使用np.ascontiguousarray(im)来输入图像。assert img_ori.data.contiguous, 'Image needs to be contiguous. Please apply to input images with np.ascontiguousarray(im).'# 这个方法调用类实例 self 的一个方法,名为 font_check 。# def font_check(font='./yolov6/utils/Arial.ttf', size=10):# -> 用于检查指定路径的字体文件是否存在,并返回一个 PIL(Python Imaging Library)TrueType 字体对象。如果字体文件不存在,它将尝试下载字体并加载。# -> return ImageFont.truetype(str(font), size)self.font_check()# 检查检测结果 det 是否非空。if len(det):# 调整检测框的坐标以匹配原始图像的尺寸,并四舍五入到最近的整数。 self.rescale 方法用于将检测框从模型输出的坐标系转换回原始图像的坐标系。# def rescale(ori_shape, boxes, target_shape): -> 它用于将检测框的坐标从模型输入图像的尺寸重新缩放到原始图像的尺寸。重新缩放后的检测框坐标。 -> return boxesdet[:, :4] = self.rescale(img.shape[2:], det[:, :4], img_src.shape).round()# 遍历检测结果, xyxy 是检测框的坐标, conf 是置信度, cls 是类别索引。for *xyxy, conf, cls in reversed(det):# 检查是否需要保存检测结果到文本文件。if save_txt:  # Write to file    写入文件。# 将检测框坐标从 xyxy 格式转换为 xywh 格式(中心点坐标和宽高),并归一化。# def box_convert(x):# -> 它用于将检测框的坐标从 [x1, y1, x2, y2] 格式(其中 (x1, y1) 是左上角坐标, (x2, y2) 是右下角坐标)转换为 [x, y, w, h] 格式(其中 (x, y) 是中心点坐标, w 是宽度, h 是高度)。y :转换后的检测框坐标,格式为 [x, y, w, h] 。# -> return yxywh = (self.box_convert(torch.tensor(xyxy).view(1, 4)) / gn).view(-1).tolist()  # normalized xywh    归一化 xywh。# 创建一个包含 类别索引 、 归一化的 xywh 坐标 和 置信度 的元组。line = (cls, *xywh, conf)# 打开文本文件以追加模式。with open(txt_path + '.txt', 'a') as f:# 将检测结果写入文件。f.write(('%g ' * len(line)).rstrip() % line + '\n')# 检查是否需要在图像上绘制检测结果。if save_img:# 获取类别索引的整数值。class_num = int(cls)  # integer class    整数类。# 根据是否隐藏标签和置信度,确定要绘制的标签文本。label = None if hide_labels else (self.class_names[class_num] if hide_conf else f'{self.class_names[class_num]} {conf:.2f}')# 在原始图像 img_ori 上绘制检测框和标签。# def plot_box_and_label(image, lw, box, label='', color=(128, 128, 128), txt_color=(255, 255, 255), font=cv2.FONT_HERSHEY_COMPLEX):# -> 它用于在图像上绘制一个边界框(bounding box)以及与之关联的标签(label)。该方法没有返回值,它直接在输入的 image 上进行绘制。# def generate_colors(i, bgr=False):# -> 它用于生成一组预定义颜色的调色板,并根据给定的索引 i 返回相应的颜色。根据 bgr 参数返回 RGB 或 BGR 格式的颜色元组。# -> return (color[2], color[1], color[0]) if bgr else colorself.plot_box_and_label(img_ori, max(round(sum(img_ori.shape) / 2 * 0.003), 2), xyxy, label, color=self.generate_colors(class_num, True))# 将 PIL 图像 img_ori 转换回 NumPy 数组,以便进行后续处理或保存。img_src = np.asarray(img_ori)# FPS counter# 使用上一回答中提到的 CalcFPS 类的 update 方法更新帧率计算器。这里, 1.0 / (t2 - t1) 计算了上一帧图像处理的时间间隔的倒数,即当前帧的帧率,并更新到 fps_calculator 中。fps_calculator.update(1.0 / (t2 - t1))# 调用 CalcFPS 类的 accumulate 方法来计算平均帧率。avg_fps = fps_calculator.accumulate()# 检查 self.files 的类型是否为视频。这里的 self.files 可能是一个包含视频流信息的对象, type 属性表示文件类型。if self.files.type == 'video':# 如果类型为视频,则调用 draw_text 方法在视频帧上绘制文本。# def draw_text(img, text, font=cv2.FONT_HERSHEY_SIMPLEX, pos=(0, 0), font_scale=1, font_thickness=2, text_color=(0, 255, 0), text_color_bg=(0, 0, 0),):# -> 它用于在图像上绘制文本,并在其下方绘制一个背景矩形以突出显示文本。返回文本的宽度和高度。# -> return text_sizeself.draw_text(img_src,f"FPS: {avg_fps:0.1f}",pos=(20, 20),font_scale=1.0,text_color=(204, 85, 17),text_color_bg=(255, 255, 255),font_thickness=2,)# 检查 view_img 变量是否为 True ,这个变量控制是否在窗口中显示图像。if view_img:# 检查当前图像的路径 img_path 是否已经存在于 windows 列表中。 windows 列表用于跟踪已经创建的窗口,以避免重复创建。if img_path not in windows:# 如果窗口不存在,则将 img_path 添加到 windows 列表中。windows.append(img_path)# 创建一个名为 img_path 的新窗口,并设置窗口属性。 cv2.WINDOW_NORMAL 允许用户调整窗口大小, cv2.WINDOW_KEEPRATIO 保持窗口的宽高比。cv2.namedWindow(str(img_path), cv2.WINDOW_NORMAL | cv2.WINDOW_KEEPRATIO)  # allow window resize (Linux)    允许调整窗口大小(Linux)。# 设置窗口的大小为图像的宽度和高度。cv2.resizeWindow(str(img_path), img_src.shape[1], img_src.shape[0])# 在创建的窗口中显示图像 img_src 。cv2.imshow(str(img_path), img_src)# cv2.imshow(winname, img)# cv2.imshow 是 OpenCV 库中的一个函数,用于在窗口中显示图像。这个函数创建一个窗口并显示指定的图像,如果窗口不存在,则创建它;如果窗口已存在,则显示在该窗口中。# 参数 :# winname :窗口的名称,是一个字符串。如果名称相同,则会显示在同一个窗口中。# img :要显示的图像,是一个 numpy 数组。# 返回值 :# 该函数没有返回值。# 说明 :# cv2.imshow 函数创建一个名为 winname 的窗口,并在其中显示 img 图像。# 如果窗口不存在,则创建它;如果窗口已存在,则显示在该窗口中。 # 窗口的大小默认与图像大小相同。如果需要调整窗口大小,可以使用 cv2.resizeWindow 或 cv2.namedWindow 函数设置窗口属性。# 显示图像时,OpenCV 会阻塞程序执行,直到窗口被关闭或超时。可以使用 cv2.waitKey 函数设置超时时间。# 等待1毫秒的键盘事件。这允许 OpenCV 处理窗口事件(如关闭窗口),而不会导致程序完全冻结。1毫秒的超时时间意味着程序将继续执行,而不会等待用户输入。cv2.waitKey(1)  # 1 millisecond    1 毫秒。# 保存结果(带有检测的图像)。# Save results (image with detections)# 一个布尔值,指示是否需要保存图像或视频。if save_img:# 如果文件类型为 'image',则直接使用 cv2.imwrite 将图像保存到 save_path 指定的路径。if self.files.type == 'image':# success = cv2.imwrite(filename, img, [params])# cv2.imwrite 是 OpenCV 库中的一个函数,用于将图像写入文件。这个函数以指定的文件名保存图像,支持多种图像格式,如 BMP、JPEG、PNG 等。# 参数 :# filename :要写入的图像文件的名称,包括路径。# img :要保存的图像,是一个 numpy 数组。# params :一个可选参数列表,用于指定图像编码的额外参数。例如,对于 JPEG 图像,可以指定质量参数。# 返回值 :# success :一个布尔值,如果图像成功写入文件,则返回 True ;否则返回 False 。# 说明 :# cv2.imwrite 函数将 img 图像保存到 filename 指定的文件中。如果文件已存在,则会被覆盖。# 支持的图像格式包括 BMP、JPG、PNG、TIFF 等,具体支持的格式取决于 OpenCV 编译时的配置。# 通过指定 params 参数,可以控制图像的编码质量。例如,对于 JPEG 图像,可以指定压缩质量。cv2.imwrite(save_path, img_src)# 如果文件类型为 'video' 或 'stream',则执行以下操作。else:  # 'video' or 'stream'    “视频”或“流”。# 如果 save_path 发生变化,表示需要开始一个新的视频文件。if vid_path != save_path:  # new video    新视频。# 更新 vid_path 为新的保存路径。vid_path = save_path# 如果 vid_writer 已经存在,释放之前的 VideoWriter 对象。if isinstance(vid_writer, cv2.VideoWriter):# 释放之前的 vid_writer 。vid_writer.release()  # release previous video writer# 如果 vid_cap 存在,即处理的是视频文件。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))# 如果 vid_cap 不存在,即处理的是视频流。else:  # stream    流。# 设置默认的帧率、宽度和高度。fps, w, h = 30, img_ori.shape[1], img_ori.shape[0]# 确保保存路径的文件扩展名为 '.mp4'。save_path = str(Path(save_path).with_suffix('.mp4'))  # force *.mp4 suffix on results videos    强制结果视频使用 *.mp4 后缀。# 创建一个新的 cv2.VideoWriter 对象,用于写入视频。vid_writer = cv2.VideoWriter(save_path, cv2.VideoWriter_fourcc(*'mp4v'), fps, (w, h))# 将图像 img_src 作为视频帧写入视频文件。vid_writer.write(img_src)# 它用于在图像推理之前对图像进行预处理。@staticmethod# 1.img_src :原始图像数据,可以是图像文件路径或图像数组。# 2.img_size :目标图像尺寸,用于调整图像大小。# 3.stride :模型的步长,用于确定图像调整时的对齐。# 4.half :布尔值,指示是否将图像转换为半精度(FP16)。def process_image(img_src, img_size, stride, half):# 在图像推理之前处理图像。'''Process image before image inference.'''# def letterbox(im, new_shape=(640, 640), color=(114, 114, 114), auto=True, scaleup=True, stride=32):# -> 在满足步幅倍数约束的同时调整图像大小并填充。# -> return im, r, (left, top)# 使用 letterbox 函数将图像调整到 img_size 指定的尺寸,同时保持图像的长宽比。 letterbox 函数返回一个包含调整后的图像和一些额外信息的元组,这里只取第一个元素,即调整后的图像。image = letterbox(img_src, img_size, stride=stride)[0]# Convert# 将图像从 HWC(高度、宽度、通道)格式转换为 CHW(通道、高度、宽度)格式,并将 BGR 颜色空间转换为 RGB。image = image.transpose((2, 0, 1))[::-1]  # HWC to CHW, BGR to RGB# np.ascontiguousarray(a, dtype=None)# 是 NumPy 库中的一个函数,用于返回一个连续存储的数组(在内存中是按行优先顺序存储的)。# 如果输入数组已经是连续的,它将返回输入数组的引用;否则,它会返回一个新的、连续的副本。# a :要转换的输入数组。# dtype(可选) :所需的数组数据类型。如果未指定,则保持输入数组的数据类型。# 返回 :返回一个内存中连续存储的数组。# 将 NumPy 数组转换为 PyTorch 张量,并确保数组是连续的。image = torch.from_numpy(np.ascontiguousarray(image))# 如果 half 为 True ,则将图像转换为半精度(FP16),否则转换为全精度(FP32)。image = image.half() if half else image.float()  # uint8 to fp16/32# 将图像的像素值从 [0, 255] 范围归一化到 [0.0, 1.0] 范围。image /= 255  # 0 - 255 to 0.0 - 1.0# 返回值 :# 1.image :预处理后的图像张量。# 2.img_src  :原始图像数据或路径。return image, img_src# 它用于将检测框的坐标从模型输入图像的尺寸重新缩放到原始图像的尺寸。@staticmethod# ori_shape :原始图像的尺寸,格式为 (高度, 宽度) 。# boxes :检测框的坐标,格式为 (x1, y1, x2, y2) ,其中 (x1, y1) 是左上角坐标, (x2, y2) 是右下角坐标。# target_shape :模型输入图像的尺寸,格式为 (高度, 宽度) 。def rescale(ori_shape, boxes, target_shape):# 将输出重新缩放为原始图像形状。'''Rescale the output to the original image shape'''# 计算原始图像和目标图像之间的最小缩放比例。ratio = min(ori_shape[0] / target_shape[0], ori_shape[1] / target_shape[1])# 计算在宽度和高度方向上的填充量,以便将目标图像居中放置在原始图像中。padding = (ori_shape[1] - target_shape[1] * ratio) / 2, (ori_shape[0] - target_shape[0] * ratio) / 2# 将检测框的 x 坐标向左移动填充量。boxes[:, [0, 2]] -= padding[0]# 将检测框的 y 坐标向上移动填充量。boxes[:, [1, 3]] -= padding[1]# 将检测框的坐标按缩放比例进行缩放。boxes[:, :4] /= ratio# 确保检测框的左上角 x 坐标在目标图像宽度范围内。boxes[:, 0].clamp_(0, target_shape[1])  # x1# 确保检测框的左上角 y 坐标在目标图像高度范围内。boxes[:, 1].clamp_(0, target_shape[0])  # y1# 确保检测框的右下角 x 坐标在目标图像宽度范围内。boxes[:, 2].clamp_(0, target_shape[1])  # x2# 确保检测框的右下角 y 坐标在目标图像高度范围内。boxes[:, 3].clamp_(0, target_shape[0])  # y2# 重新缩放后的检测框坐标。return boxes# 作用是确保输入的图像尺寸 img_size 是模型步长 s 的倍数。这个功能在深度学习模型中很常见,因为许多模型要求输入图像的尺寸必须是特定的步长(例如32、64等)的倍数。# 1.self :类的实例本身。# 2.img_size :输入的图像尺寸,可以是一个整数或一个列表。# 3.s :步长,默认为32。# 4.floor :最小尺寸限制,默认为0。def check_img_size(self, img_size, s=32, floor=0):# 确保图像大小在每个维度上都是 stride 的倍数,并返回一个新的图像形状列表。"""Make sure image size is a multiple of stride s in each dimension, and return a new shape list of image."""# 如果 img_size 是一个整数(例如640),则表示图像的宽度和高度相同。# 使用 self.make_divisible 方法(这个方法未在代码中定义,但应该是一个自定义方法)来确保每个维度的尺寸是步长 s 的倍数。# max 函数确保新的尺寸不会低于 floor 指定的最小尺寸。if isinstance(img_size, int):  # integer i.e. img_size=640    整数,即 img_size=640。new_size = max(self.make_divisible(img_size, int(s)), floor)# 如果 img_size 是一个列表(例如[640, 480]),则表示图像的宽度和高度不同。elif isinstance(img_size, list):  # list i.e. img_size=[640, 480]    列表,即 img_size=[640, 480]。new_size = [max(self.make_divisible(x, int(s)), floor) for x in img_size]else:# 如果 img_size 既不是整数也不是列表,抛出异常。raise Exception(f"Unsupported type of img_size: {type(img_size)}")    # 不支持的 img_size 类型:{type(img_size)}。if new_size != img_size:# 如果原始尺寸和计算后的新尺寸不同,打印一条警告信息,提示用户图像尺寸已被调整。print(f'WARNING: --img-size {img_size} must be multiple of max stride {s}, updating to {new_size}')    # 警告:--img-size {img_size} 必须是最大步幅 {s} 的倍数,更新为 {new_size}# 如果 img_size 是列表,直接返回调整后的尺寸列表。# 如果 img_size 是整数,返回一个包含两个相同值的列表,这两个值都是调整后的尺寸。return new_size if isinstance(img_size,list) else [new_size]*2def make_divisible(self, x, divisor):# 向上修正 x 的值,使其可以被除数整除。# Upward revision the value x to make it evenly divisible by the divisor.return math.ceil(x / divisor) * divisor# 它用于在图像上绘制文本,并在其下方绘制一个背景矩形以突出显示文本。@staticmethod# img :要绘制文本的原始图像。# text :要绘制的文本字符串。# font :用于绘制文本的字体,默认为 cv2.FONT_HERSHEY_SIMPLEX 。# pos :文本在图像上的位置,默认为 (0, 0) 。# font_scale :字体的缩放比例,默认为 1 。# font_thickness :字体的线条宽度,默认为 2 。# text_color :文本的颜色,默认为 (0, 255, 0) ,即绿色。# text_color_bg :文本背景的颜色,默认为 (0, 0, 0) ,即黑色。def draw_text(img,text,font=cv2.FONT_HERSHEY_SIMPLEX,pos=(0, 0),font_scale=1,font_thickness=2,text_color=(0, 255, 0),text_color_bg=(0, 0, 0),):# 设置文本背景的偏移量。offset = (5, 5)# 获取文本的位置坐标。x, y = pos# (cv2.Size(width, height), baseline) = cv2.getTextSize(text, fontFace, fontScale, thickness)# cv2.getTextSize 函数是 OpenCV 库中的一个函数,它用于计算给定文本的尺寸(宽度和高度)。这个函数在绘制文本之前非常有用,因为它可以帮助你确定文本的尺寸,从而可以进行适当的定位和布局。# 参数 :# text :要测量的文本字符串。# fontFace :字体类型,可以是 OpenCV 预定义的字体,如 cv2.FONT_HERSHEY_SIMPLEX 、 cv2.FONT_HERSHEY_PLAIN 等。# fontScale :字体缩放因子,用于调整字体大小。# thickness :字体线条的厚度。# 返回值 :# cv2.Size(width, height) :一个 cv2.Size 对象,包含文本的宽度和高度。# baseline :文本基线(即文本底部到基线的距离),这个值可以用来确定文本的位置,以确保基线对齐。# 使用 OpenCV 的 getTextSize 函数计算文本的宽度和高度。text_size, _ = cv2.getTextSize(text, font, font_scale, font_thickness)# 获取文本的宽度和高度。text_w, text_h = text_size# 计算背景矩形的左上角坐标。rec_start = tuple(x - y for x, y in zip(pos, offset))# 计算背景矩形的右下角坐标。rec_end = tuple(x + y for x, y in zip((x + text_w, y + text_h), offset))# 使用 OpenCV 的 rectangle 函数绘制填充的背景矩形。cv2.rectangle(img, rec_start, rec_end, text_color_bg, -1)# 使用 OpenCV 的 putText 函数在图像上绘制文本。cv2.putText(img,text,# 计算文本的基线位置,确保文本位于背景矩形的上方。(x, int(y + text_h + font_scale - 1)),font,font_scale,text_color,font_thickness,cv2.LINE_AA,)# 返回文本的宽度和高度。return text_size# 它用于在图像上绘制一个边界框(bounding box)以及与之关联的标签(label)。@staticmethod# 1.image :要绘制边界框和标签的原始图像。# 2.lw :边界框的线条宽度。# 3.box :边界框的坐标,格式为 (x1, y1, x2, y2) 。# 4.label :边界框内的标签文本,默认为空字符串。# 5.color :边界框的颜色,默认为 (128, 128, 128) ,即灰色。# 6.txt_color :标签文本的颜色,默认为 (255, 255, 255) ,即白色。# 7.font :用于绘制文本的字体,默认为 cv2.FONT_HERSHEY_COMPLEX 。def plot_box_and_label(image, lw, box, label='', color=(128, 128, 128), txt_color=(255, 255, 255), font=cv2.FONT_HERSHEY_COMPLEX):# 将一个 xyxy 框添加到带标签的图像中。# Add one xyxy box to image with label# 将边界框坐标转换为整数,并分别表示左上角和右下角。p1, p2 = (int(box[0]), int(box[1])), (int(box[2]), int(box[3]))# 使用 OpenCV 的 rectangle 函数在图像上绘制边界框。cv2.rectangle(image, p1, p2, color, thickness=lw, lineType=cv2.LINE_AA)# 如果标签不为空,则继续绘制标签。if label:# 计算文本的线条宽度。tf = max(lw - 1, 1)  # font thickness    字体粗细。# 使用 OpenCV 的 getTextSize 函数计算标签文本的宽度和高度。w, h = cv2.getTextSize(label, 0, fontScale=lw / 3, thickness=tf)[0]  # text width, height    文字宽度、高度。# 判断标签是否适合放在边界框的上方。outside = p1[1] - h - 3 >= 0  # label fits outside box    标签适合边界框外面。# 根据标签位置计算背景矩形的右下角坐标。p2 = p1[0] + w, p1[1] - h - 3 if outside else p1[1] + h + 3# 使用 OpenCV 的 rectangle 函数绘制填充的背景矩形。cv2.rectangle(image, p1, p2, color, -1, cv2.LINE_AA)  # filled    填充。# 使用 OpenCV 的 putText 函数在图像上绘制标签文本。cv2.putText(image, label, (p1[0], p1[1] - 2 if outside else p1[1] + h + 2), font, lw / 3, txt_color,thickness=tf, lineType=cv2.LINE_AA)# 该方法没有返回值,它直接在输入的 image 上进行绘制。# 用于检查指定路径的字体文件是否存在,并返回一个 PIL(Python Imaging Library)TrueType 字体对象。如果字体文件不存在,它将尝试下载字体并加载。@staticmethod# font :字体文件的路径,默认为 './yolov6/utils/Arial.ttf' 。# size :字体大小,默认为 10 。def font_check(font='./yolov6/utils/Arial.ttf', size=10):# 返回 PIL TrueType 字体,必要时下载到 CONFIG_DIR 。# Return a PIL TrueType Font, downloading to CONFIG_DIR if necessary# 使用 osp.exists 函数( osp 是 os.path 的别名)检查字体文件是否存在。如果文件不存在,将抛出一个 AssertionError 。assert osp.exists(font), f'font path not exists: {font}'    # 字体路径不存在:{font}。# 尝试执行以下代码块。try:# 使用 PIL 的 ImageFont.truetype 方法加载字体文件,并返回一个字体对象。这里有一个逻辑错误,因为 font 是一个路径字符串,而不是一个具有 exists 方法的对象,所以 font.exists() 将导致错误。return ImageFont.truetype(str(font) if font.exists() else font.name, size)# 如果加载字体时发生任何异常,将捕获异常。except Exception as e:  # download if missing# 在异常处理中再次尝试加载字体。这里假设即使在异常处理中,也不需要下载字体,而是直接加载。return ImageFont.truetype(str(font), size)# 它用于将检测框的坐标从 [x1, y1, x2, y2] 格式(其中 (x1, y1) 是左上角坐标, (x2, y2) 是右下角坐标)转换为 [x, y, w, h] 格式(其中 (x, y) 是中心点坐标, w 是宽度, h 是高度)。@staticmethod# x :检测框的坐标,格式为 [n, 4] ,其中 n 是检测框的数量。def box_convert(x):# 将形状为 [n, 4] 的框从 [x1, y1, x2, y2] 转换为 [x, y, w, h],其中 x1y1=左上角,x2y2=右下角# Convert boxes with shape [n, 4] from [x1, y1, x2, y2] to [x, y, w, h] where x1y1=top-left, x2y2=bottom-right# 如果输入 x 是 PyTorch 张量,则使用 clone() 方法创建一个副本;如果 x 是 NumPy 数组,则使用 np.copy() 创建一个副本。这一步确保了原始输入数据不会被修改。y = x.clone() if isinstance(x, torch.Tensor) else np.copy(x)# 计算 x 坐标的中心点。y[:, 0] = (x[:, 0] + x[:, 2]) / 2  # x center# 计算 y 坐标的中心点。y[:, 1] = (x[:, 1] + x[:, 3]) / 2  # y center# 计算检测框的宽度。y[:, 2] = x[:, 2] - x[:, 0]  # width# 计算检测框的高度。y[:, 3] = x[:, 3] - x[:, 1]  # height# y :转换后的检测框坐标,格式为 [x, y, w, h] 。return y# 它用于生成一组预定义颜色的调色板,并根据给定的索引 i 返回相应的颜色。@staticmethod# 1.i :颜色索引,用于从调色板中选择颜色。# 2.bgr :布尔值,指示返回的颜色格式是否为 BGR(Blue Green Red),默认为 False,即返回 RGB(Red Green Blue)格式。def generate_colors(i, bgr=False):# hex :包含多个十六进制颜色代码的元组。hex = ('FF3838', 'FF9D97', 'FF701F', 'FFB21D', 'CFD231', '48F90A', '92CC17', '3DDB86', '1A9334', '00D4BB','2C99A8', '00C2FF', '344593', '6473FF', '0018EC', '8438FF', '520085', 'CB38FF', 'FF95C8', 'FF37C7')# 初始化一个空列表来存储 RGB 颜色元组。palette = []# 遍历 hex 中的每个十六进制颜色代码。for iter in hex:# 在十六进制颜色代码前加上 # 。h = '#' + iter# 将十六进制颜色代码转换为 RGB 元组,并添加到 palette 列表中。palette.append(tuple(int(h[1 + i:1 + i + 2], 16) for i in (0, 2, 4)))# 获取调色板中颜色的数量。num = len(palette)# 根据索引 i 从调色板中选择颜色,使用模运算确保索引在有效范围内。color = palette[int(i) % num]# 如果 bgr 为 True,则返回 BGR 格式的颜色元组;否则返回 RGB 格式的颜色元组。# 根据 bgr 参数返回 RGB 或 BGR 格式的颜色元组。return (color[2], color[1], color[0]) if bgr else color

3.class CalcFPS: 

# 定义了一个名为 CalcFPS 的类,它用于计算帧率(Frames Per Second)。这个类可以帮助你测量和跟踪处理视频流或图像序列时的性能。
class CalcFPS:def __init__(self, nsamples: int = 50):# 构造函数,初始化一个 deque (双端队列)来存储最近 nsamples 个样本的帧处理时间。# nsamples :队列的最大长度,默认为50。self.framerate = deque(maxlen=nsamples)# 更新方法,用于添加新的帧处理时间到队列中。# duration :处理一帧图像所花费的时间,以秒为单位。def update(self, duration: float):self.framerate.append(duration)# 计算并返回平均帧处理时间。# 如果队列中有超过一个样本,使用 np.average 计算平均值。# 如果队列中只有一个样本或为空,返回0.0。def accumulate(self):if len(self.framerate) > 1:return np.average(self.framerate)else:return 0.0

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/885410.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Linux:git的了解和基础使用(保姆级教程)

文章目录 引言一、git是什么1.1 版本控制器git1.2 git的历史 二、git的使用2.1 安装git2.2 创建gitee账号2.3 git三板斧2.3.1 add2.3.2 commit2.3.3 push 三. git的补充总结 引言 git是一款软件,它用于帮助我们来管理代码以及文件,掌握并使用git可以很有…

探索LINQ在C#中的应用:从基本查询到数据联接

LINQ(语言集成查询)是微软为.NET框架开发的一种强大功能,于2007年作为C# 3.0和Visual Basic .NET 9.0的一部分引入。LINQ的诞生旨在提供一种一致且直观的方式来查询和操作数据,无论数据来源是内存中的集合、数据库还是XML文档。 …

鸿蒙UI开发——实现环形文字

1、背 景 有朋友提问:您好关于鸿蒙UI想咨询一个问题 如果我想实现展示环形文字是需要通过在Text组件中设置transition来实现么,还是需要通过其他方式来实现。 针对这位粉丝朋友的提问,我们做一下解答。 2、实现环形文字效果 ❓ 什么是环形…

搭建轻量级文件服务器Dufs

前言 Dufs是什么? 答:是一款轻量级文件管理服务器,类似于FTP服务器但又比FTP更好用易于管理。 Dufs有什么特性? 答: ‌静态文件服务…

【软考】系统分析师第二版 新增章节 第20章微服务系统分析与设计

微服务系统是一类基于微服务架构风格的分布式系统,它将应用程序拆分成多个独立的小型服务,每个服务都运行在独立的进程中,并采用轻量级通信协议进行通信。这些服务可以由不同的团队开发、不同的编程语言编写,并且可以按需部署。微…

基于SSM的校园美食交流系统【附源码】

基于SSM的校园美食交流系统 效果如下: 管理员主页面 用户主页面 美食信息页面 美食资讯页面 修改密码页面 论坛中心页面 研究背景 随着高校信息化建设的不断推进,校园生活日益丰富多样,学生对于美食的需求与探索也愈发旺盛。然而&#xff…

PICO+Unity MR空间网格

官方链接:空间网格 | PICO 开发者平台 注意:该功能只能打包成APK在PICO 4 Ultra上真机运行,无法通过串流或PICO developer center在PC上运行。使用之前要开启视频透视。 在 Inspector 窗口中的 PXR_Manager (Script) 面板上,勾选…

斗破QT编程入门系列之前言:认识Qt:获取与安装(四星斗师)

本系列是在学习完C之后,然后通过Qt构建界面来,赋予枯燥的代码新的样貌,这样我们才能开发出更人性化的程序,同时会进一步提高初学者对编程的兴趣,大家加油,斗破Qt来了。 斗破Qt目录: 斗破Qt编程…

PyTorch核心概念:从梯度、计算图到连续性的全面解析(三)

文章目录 Contiguous vs Non-Contiguous TensorTensor and ViewStrides非连续数据结构:Transpose( )在 PyTorch 中检查Contiguous and Non-Contiguous将不连续张量(或视图)转换为连续张量view() 和 reshape() 之间的区别总结 参考文献 Contig…

家庭宽带如何开启公网ipv4和ipv6

好久没更新了,最近在家里折腾nas。一来自己有下电影的习惯,二来手机的icloud容量也不够了。所以买了群晖的423,但是nas要想用的畅快,外网访问必不可少。所以我之前研究了下,打家里的电信快带打通了外网。 一般nas的服务商基本都会…

【Linux】Ansible集中化运维工具(详解)安装、常用模块、playbook脚本

文章目录 一、Ansible安装及远程控制1、关闭防火墙和SELinux2、安装ansible3、配置SSH无密码登录1、在管理机上生成一对密钥2、将公钥下发到远程主机3、保管密钥 4、主机目录 二、常用模块1、setup模块2、copy模块3、file模块4、shell模块5、script模块6、ping模块7、group模块…

基于 RNN 的语言模型

基于 RNN 的语言模型 循环神经网络(Recurrent Neural Network, RNN)是一类网络连接中包含环路的 神经网络的总称。 给定一个序列,RNN 的环路用于将历史状态叠加到当前状态上。沿着时间维度,历史状态被循环累积,并作为…

第二十九篇——线性代数:“矩阵”到底怎么用?

目录 一、背景介绍二、思路&方案三、过程1.思维导图2.文章中经典的句子理解3.学习之后对于投资市场的理解4.通过这篇文章结合我知道的东西我能想到什么? 四、总结五、升华 一、背景介绍 数学中的线性代数,再生活中的落地和应用,是我这个…

nodejs:下载,安装,系统环境配置,更换镜像

​​​​下载 地址:https://nodejs.org/zh-cn/download/prebuilt-installer 安装包 开始安装 安装完成 给文件夹添加权限 创建两个文件夹 node_cache node_global 更新环境变量 修改环境变量,新的全局模块路径,这样在任何位置运行命令时都…

AMD显卡低负载看视频掉驱动(chrome edge浏览器) 高负载玩游戏却稳定 解决方法——关闭MPO

2024.11.9更新 开关mpo ulps 感觉有用但是还是掉驱动,现在确定是window顶驱动问题 按网上的改注册表和组策略会让自己也打不上驱动 目前感觉最好的办法就是,重置此电脑,然后你就摆着电脑挂个十分钟半小时别动,一开始他是不显示…

案例精选 | 河北省某检察院安全运营中异构日志数据融合的实践探索

河北省某检察院是当地重要的法律监督机构,肩负着维护法律尊严和社会公平正义的重要职责。该机构依法独立行使检察权,负责对犯罪行为提起公诉,并监督整个诉讼过程,同时积极参与社会治理,保护公民权益,推动法…

【论文阅读】火星语义分割的半监督学习

【论文阅读】火星语义分割的半监督学习 文章目录 【论文阅读】火星语义分割的半监督学习一、介绍二、联系工作3.1Deep Learning for Mars3.2 数据集可以分为三类:3.3 半监督学习 三、提出的火星图像分割数据集四、方法四、实验 S 5Mars: Semi-Supervised Learning …

蓝桥杯 懒洋洋字符串--字符串读入

题目 代码 #include <iostream>using namespace std;int main(){int n;cin>>n;char s[210][4];int ans0;for(int i0;i<n;i){scanf("%s",s[i]);}for(int i0;i<n;i){char as[i][0];char bs[i][1];char cs[i][2];// cout<<a<< <<b…

免费送源码:Java+ssm+MySQL 在线购票影城 计算机毕业设计原创定制

摘要 随着互联网趋势的到来&#xff0c;各行各业都在考虑利用互联网将自己推广出去&#xff0c;最好方式就是建立自己的互联网系统&#xff0c;并对其进行维护和管理。在现实运用中&#xff0c;应用软件的工作规则和开发步骤&#xff0c;采用Java技术建设在线购票影城。 本设计…

Qt 软键盘设计

最近有客户用的电脑是触屏的&#xff0c;所以不用键盘与鼠标&#xff0c;系统的键盘不好看&#xff0c;所以自己设计一个键盘显示&#xff0c;先看下效果图&#xff1b; 设计思路&#xff0c;构建一个软键盘设计界面并重写输入框&#xff0c;然后做界面提升,直接上代码 class …