Tensorrt-Sample-python-yolov3_onnx

#!/usr/bin/env python3
#
# SPDX-FileCopyrightText: Copyright (c) 1993-2024 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
# SPDX-License-Identifier: Apache-2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#

from __future__ import print_function

import os
import sys

import numpy as np
import tensorrt as trt
from data_processing import ALL_CATEGORIES, PostprocessYOLO, PreprocessYOLO
from PIL import ImageDraw

sys.path.insert(1, os.path.join(sys.path[0], ".."))
from downloader import getFilePath

from samples.python import common

TRT_LOGGER = trt.Logger()  # 创建一个Tensorrt日志器,用于记录推理过程中的日志


# 这个函数使用PIL.ImageDraw绘制矩形框,并显示检测到的物理类别和置信度
def draw_bboxes(
    image_raw, bboxes, confidences, categories, all_categories, bbox_color="blue"
):
    """Draw the bounding boxes on the original input image and return it.

    Keyword arguments:
    image_raw -- a raw PIL Image
    bboxes -- NumPy array containing the bounding box coordinates of N objects, with shape (N,4).
    categories -- NumPy array containing the corresponding category for each object,
    with shape (N,)
    confidences -- NumPy array containing the corresponding confidence for each object,
    with shape (N,)
    all_categories -- a list of all categories in the correct ordered (required for looking up
    the category name)
    bbox_color -- an optional string specifying the color of the bounding boxes (default: 'blue')
    """

    draw = ImageDraw.Draw(image_raw)
    print(bboxes, confidences, categories)
    for box, score, category in zip(bboxes, confidences, categories):
        x_coord, y_coord, width, height = box
        left = max(0, np.floor(x_coord + 0.5).astype(int))
        top = max(0, np.floor(y_coord + 0.5).astype(int))
        right = min(image_raw.width, np.floor(x_coord + width + 0.5).astype(int))
        bottom = min(image_raw.height, np.floor(y_coord + height + 0.5).astype(int))

        draw.rectangle(((left, top), (right, bottom)), outline=bbox_color)
        draw.text(
            (left, top - 12),
            "{0} {1:.2f}".format(all_categories[category], score),
            fill=bbox_color,
        )

    return image_raw


def get_engine(onnx_file_path, engine_file_path=""):
    """Attempts to load a serialized engine if available, otherwise builds a new TensorRT engine and saves it."""
    #接受两个参数,一个是onnx模型的文件路径,第二个是序列化的TensorRT引擎文件路径,用于保存或加载TensorRT引擎
    def build_engine():  #定义一个内部函数,用于从ONNX文件构建一个TensorRT引擎
        """Takes an ONNX file and creates a TensorRT engine to run inference with"""
        with (trt.Builder(TRT_LOGGER) as builder, # trt.Builder(TRT_LOGGER):创建一个TensorRT构建器对象(builder),用于构建网络
              builder.create_network(0) as network,  # 创建一个空的网络定义对象(network),其中0表示默认的网络标志
              builder.create_builder_config() as config,  #构建一个构建配置对象,用于设置tensorrt引擎构建过程中的参数
              trt.OnnxParser(network,TRT_LOGGER) as parser, # 创建一个onnx解析器,用于将onnx文件解析并转换为Tensorrt网络
              trt.Runtime(TRT_LOGGER) as runtime): # 创建一个Tensorrt运行对象,用于加载和运行引擎

            #设置工作区内存池的大小,用于构建引擎的临时存储,这里设置为256Mib
            config.set_memory_pool_limit(
                trt.MemoryPoolType.WORKSPACE, 1 << 28
            )  # 256MiB

            # Parse model file检查onnx文件是否存在
            if not os.path.exists(onnx_file_path):
                print(
                    "ONNX file {} not found, please run yolov3_to_onnx.py first to generate it.".format(
                        onnx_file_path
                    )
                )
                exit(0)
            print("Loading ONNX file from path {}...".format(onnx_file_path))
            # 打开onnx文件并使用parser.pase解析文件,如果解析失败,则输出所有错误信息并返回None
            with open(onnx_file_path, "rb") as model:
                print("Beginning ONNX file parsing")
                if not parser.parse(model.read()):
                    print("ERROR: Failed to parse the ONNX file.")
                    for error in range(parser.num_errors):
                        print(parser.get_error(error))
                    return None


            # The actual yolov3.onnx is generated with batch size 64. Reshape input to batch size 1
            # 重塑网络的输入形状
            network.get_input(0).shape = [1, 3, 608, 608]

            print("Completed parsing of ONNX file")
            print(
                "Building an engine from file {}; this may take a while...".format(
                    onnx_file_path
                )
            )

            plan = builder.build_serialized_network(network, config) #调用函数,根据网络和配置构建TensorRT引擎,并将其序列化为二进制格式(plan)

            engine = runtime.deserialize_cuda_engine(plan)#调用函数将序列化的引擎反序列化为可以使用的Tensorrt引擎对象(engine)
            print("Completed creating Engine")
            #保存引擎
            with open(engine_file_path, "wb") as f:
                f.write(plan)
            return engine

    if os.path.exists(engine_file_path): #判断是否存在序列化的引擎文件
        # If a serialized engine exists, use it instead of building an engine.
        print("Reading engine from file {}".format(engine_file_path))
        with open(engine_file_path, "rb") as f, trt.Runtime(TRT_LOGGER) as runtime:
            return runtime.deserialize_cuda_engine(f.read())
    else:
        return build_engine()


def main():
    """Create a TensorRT engine for ONNX-based YOLOv3-608 and run inference."""

    # Try to load a previously generated YOLOv3-608 network graph in ONNX format:
    onnx_file_path = "yolov3.onnx"   #定义两个路径
    engine_file_path = "yolov3.trt"
    # Download a dog image and save it to the following file path:
    input_image_path = getFilePath("samples/python/yolov3_onnx/dog.jpg")
    # Two-dimensional tuple with the target network's (spatial) input resolution in HW ordered
    input_resolution_yolov3_HW = (608, 608)  #网络输入分辨率
    # Create a pre-processor object by specifying the required input resolution for YOLOv3
    preprocessor = PreprocessYOLO(input_resolution_yolov3_HW)  #预处理对象,通过传入YOLO3所需要的输入分辨率来初始化
    # Load an image from the specified input path, and return it together with  a pre-processed version
    image_raw, image = preprocessor.process(input_image_path)  #加载并预处理图像
    # Store the shape of the original input image in WH format, we will need it for later
    shape_orig_WH = image_raw.size  #保存原始图像的宽高,以供后续的使用

    # Output shapes expected by the post-processor
    output_shapes = [(1, 255, 19, 19), (1, 255, 38, 38), (1, 255, 76, 76)]
    # Do inference with TensorRT
    trt_outputs = []

    with get_engine(  #进行推理
        onnx_file_path, engine_file_path
    ) as engine, engine.create_execution_context() as context:  #创建推理的执行上下文
        inputs, outputs, bindings, stream = common.allocate_buffers(engine)  #分配输入、输出、绑定和CUDA流的内存缓冲区,用于推理过程中传递数据
        # Do inference
        print("Running inference on image {}...".format(input_image_path))
        # Set host input to the image. The common.do_inference function will copy the input to the GPU before executing.
        inputs[0].host = image  #将预处理后的图像 image 作为输入,赋值给输入缓冲区
        #调用common.do_inference函数进行推理,该函数会将输入数据拷贝到GPU,并执行推理操作 ,最后将结果拷贝回主机内存
        trt_outputs = common.do_inference(
            context, #推理上下文
            engine=engine,
            bindings=bindings, #绑定输入输出的内存
            inputs=inputs,
            outputs=outputs, #用于存储输入和输出数据的缓冲区
            stream=stream,#CUDA流,用于异步操作
        )

    # Before doing post-processing, we need to reshape the outputs as the common.do_inference will give us flat arrays.
    #重塑推理输出,由于推理后的结果是扁平化的一维数组,需要根据模型输出的形状将其恢复为多维数组
    trt_outputs = [
        output.reshape(shape) for output, shape in zip(trt_outputs, output_shapes)
    ]

    postprocessor_args = {
        "yolo_masks": [   #yolo_mask yolo3使用的掩玛,用于不同特征图上的检测
            (6, 7, 8),
            (3, 4, 5),
            (0, 1, 2),
        ],  # A list of 3 three-dimensional tuples for the YOLO masks
        "yolo_anchors": [  #yolo3与定义的anchor大小,用于检测不同尺度的对象
            (10, 13),
            (16, 30),
            (33, 23),
            (30, 61),
            (62, 45),  # A list of 9 two-dimensional tuples for the YOLO anchors
            (59, 119),
            (116, 90),
            (156, 198),
            (373, 326),
        ],
        "obj_threshold": 0.6,  # Threshold for object coverage, float value between 0 and 1 #检测对象的置信度阈值
        "nms_threshold": 0.5,  # Threshold for non-max suppression algorithm, float value between 0 and 1 #非极大值抑制的阈值
        "yolo_input_resolution": input_resolution_yolov3_HW,
    }

    postprocessor = PostprocessYOLO(**postprocessor_args)  #使用定义的参数创建yolov3的后处理器postprocessor

    # Run the post-processing algorithms on the TensorRT outputs and get the bounding box details of detected objects
    boxes, classes, scores = postprocessor.process(trt_outputs, (shape_orig_WH))  #运行后处理算法,提取检测结果的边框(boxes)、类别(classes)和置信度(scores)
    # Draw the bounding boxes onto the original input image and save it as a PNG file
    obj_detected_img = draw_bboxes(image_raw, boxes, scores, classes, ALL_CATEGORIES)  #调用draw_bboxes函数在原始图像上绘制检测到的物理边框
    output_image_path = "dog_bboxes.png"  #保存包含边框的图像
    obj_detected_img.save(output_image_path, "PNG")  #
    print(
        "Saved image with bounding boxes of detected objects to {}.".format(
            output_image_path
        )
    )

    # Free host and device memory used for inputs and outputs
    common.free_buffers(inputs, outputs, stream)  #释放内存防止内存泄漏


if __name__ == "__main__":
    main()

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值