Created
September 27, 2021 09:25
-
-
Save gigijoe/a292832eeb6df6423e4b8184e17eb20c to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. | |
* | |
* 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. | |
*/ | |
//! | |
//! sampleOnnxMiDasV2.cpp | |
//! This file contains the implementation of the ONNX MiDasV2 sample. It creates the network using | |
//! the MiDasV2 onnx model. | |
//! It can be run with the following command line: | |
//! Command: ./sample_onnx_MiDasV2 [-h or --help] [-d=/path/to/data/dir or --datadir=/path/to/data/dir] | |
//! [--useDLACore=<int>] | |
//! | |
#include "argsParser.h" | |
#include "buffers.h" | |
#include "common.h" | |
#include "logger.h" | |
#include "parserOnnxConfig.h" | |
#include "NvInfer.h" | |
#include <cuda_runtime_api.h> | |
#include <cstdlib> | |
#include <fstream> | |
#include <iostream> | |
#include <sstream> | |
#include <opencv2/opencv.hpp> | |
using samplesCommon::SampleUniquePtr; | |
const std::string gSampleName = "TensorRT.sample_onnx_midas"; | |
//! \brief The SampleOnnxMiDasV2 class implements the ONNX MiDasV2 sample | |
//! | |
//! \details It creates the network using an ONNX model | |
//! | |
class SampleOnnxMiDasV2 | |
{ | |
public: | |
SampleOnnxMiDasV2(const samplesCommon::OnnxSampleParams& params) | |
: mParams(params) | |
, mEngine(nullptr) | |
{ | |
} | |
//! | |
//! \brief Function builds the network engine | |
//! | |
bool build(); | |
//! | |
//! \brief Runs the TensorRT inference engine for this sample | |
//! | |
bool infer(); | |
private: | |
samplesCommon::OnnxSampleParams mParams; //!< The parameters for the sample. | |
nvinfer1::Dims mInputDims; //!< The dimensions of the input to the network. | |
nvinfer1::Dims mOutputDims; //!< The dimensions of the output to the network. | |
int mNumber{0}; //!< The number to classify | |
std::shared_ptr<nvinfer1::ICudaEngine> mEngine; //!< The TensorRT engine used to run the network | |
//! | |
//! \brief Parses an ONNX model for MiDasV2 and creates a TensorRT network | |
//! | |
bool constructNetwork(SampleUniquePtr<nvinfer1::IBuilder>& builder, | |
SampleUniquePtr<nvinfer1::INetworkDefinition>& network, SampleUniquePtr<nvinfer1::IBuilderConfig>& config, | |
SampleUniquePtr<nvonnxparser::IParser>& parser); | |
//! | |
//! \brief Reads the input and stores the result in a managed buffer | |
//! | |
bool processInput(const samplesCommon::BufferManager& buffers, cv::Mat & image); | |
//! | |
//! \brief Classifies digits and verify result | |
//! | |
bool verifyOutput(const samplesCommon::BufferManager& buffers, cv::Mat & originImage); | |
}; | |
//! | |
//! \brief Creates the network, configures the builder and creates the network engine | |
//! | |
//! \details This function creates the Onnx MiDasV2 network by parsing the Onnx model and builds | |
//! the engine that will be used to run MiDasV2 (mEngine) | |
//! | |
//! \return Returns true if the engine was created successfully and false otherwise | |
//! | |
bool SampleOnnxMiDasV2::build() | |
{ | |
auto builder = SampleUniquePtr<nvinfer1::IBuilder>(nvinfer1::createInferBuilder(sample::gLogger.getTRTLogger())); | |
if (!builder) | |
{ | |
return false; | |
} | |
const auto explicitBatch = 1U << static_cast<uint32_t>(NetworkDefinitionCreationFlag::kEXPLICIT_BATCH); | |
auto network = SampleUniquePtr<nvinfer1::INetworkDefinition>(builder->createNetworkV2(explicitBatch)); | |
if (!network) | |
{ | |
return false; | |
} | |
auto config = SampleUniquePtr<nvinfer1::IBuilderConfig>(builder->createBuilderConfig()); | |
if (!config) | |
{ | |
return false; | |
} | |
auto parser | |
= SampleUniquePtr<nvonnxparser::IParser>(nvonnxparser::createParser(*network, sample::gLogger.getTRTLogger())); | |
if (!parser) | |
{ | |
return false; | |
} | |
auto constructed = constructNetwork(builder, network, config, parser); | |
if (!constructed) | |
{ | |
return false; | |
} | |
// CUDA stream used for profiling by the builder. | |
auto profileStream = samplesCommon::makeCudaStream(); | |
if (!profileStream) | |
{ | |
return false; | |
} | |
config->setProfileStream(*profileStream); | |
SampleUniquePtr<IHostMemory> plan{builder->buildSerializedNetwork(*network, *config)}; | |
if (!plan) | |
{ | |
return false; | |
} | |
SampleUniquePtr<IRuntime> runtime{createInferRuntime(sample::gLogger.getTRTLogger())}; | |
if (!runtime) | |
{ | |
return false; | |
} | |
mEngine = std::shared_ptr<nvinfer1::ICudaEngine>( | |
runtime->deserializeCudaEngine(plan->data(), plan->size()), samplesCommon::InferDeleter()); | |
if (!mEngine) | |
{ | |
return false; | |
} | |
ASSERT(network->getNbInputs() == 1); | |
mInputDims = network->getInput(0)->getDimensions(); | |
ASSERT(mInputDims.nbDims == 4); // Input is 1 x 256 x 256 x 3 | |
ASSERT(network->getNbOutputs() == 1); | |
mOutputDims = network->getOutput(0)->getDimensions(); | |
ASSERT(mOutputDims.nbDims == 3); // Output is 1 x 256 x 256 | |
return true; | |
} | |
//! | |
//! \brief Uses a ONNX parser to create the Onnx MiDasV2 Network and marks the | |
//! output layers | |
//! | |
//! \param network Pointer to the network that will be populated with the Onnx MiDasV2 network | |
//! | |
//! \param builder Pointer to the engine builder | |
//! | |
bool SampleOnnxMiDasV2::constructNetwork(SampleUniquePtr<nvinfer1::IBuilder>& builder, | |
SampleUniquePtr<nvinfer1::INetworkDefinition>& network, SampleUniquePtr<nvinfer1::IBuilderConfig>& config, | |
SampleUniquePtr<nvonnxparser::IParser>& parser) | |
{ | |
auto parsed = parser->parseFromFile(locateFile(mParams.onnxFileName, mParams.dataDirs).c_str(), | |
static_cast<int>(sample::gLogger.getReportableSeverity())); | |
if (!parsed) | |
{ | |
return false; | |
} | |
config->setMaxWorkspaceSize(16_MiB); | |
if (mParams.fp16) | |
{ | |
config->setFlag(BuilderFlag::kFP16); | |
} | |
if (mParams.int8) | |
{ | |
config->setFlag(BuilderFlag::kINT8); | |
samplesCommon::setAllDynamicRanges(network.get(), 127.0f, 127.0f); | |
} | |
samplesCommon::enableDLA(builder.get(), config.get(), mParams.dlaCore); | |
return true; | |
} | |
//! | |
//! \brief Runs the TensorRT inference engine for this sample | |
//! | |
//! \details This function is the main execution function of the sample. It allocates the buffer, | |
//! sets inputs and executes the engine. | |
//! | |
bool SampleOnnxMiDasV2::infer() | |
{ | |
// Create RAII buffer manager object | |
samplesCommon::BufferManager buffers(mEngine); | |
auto context = SampleUniquePtr<nvinfer1::IExecutionContext>(mEngine->createExecutionContext()); | |
if (!context) | |
{ | |
return false; | |
} | |
cv::Mat image = cv::imread("dog.jpg"); | |
if (image.cols == 0 || image.rows == 0) | |
{ | |
printf("image is empty\n"); | |
return false; | |
} | |
// Read the input data into the managed buffers | |
ASSERT(mParams.inputTensorNames.size() == 1); | |
if (!processInput(buffers, image)) | |
{ | |
return false; | |
} | |
// Memcpy from host input buffers to device input buffers | |
buffers.copyInputToDevice(); | |
bool status = context->executeV2(buffers.getDeviceBindings().data()); | |
if (!status) | |
{ | |
return false; | |
} | |
// Memcpy from device output buffers to host output buffers | |
buffers.copyOutputToHost(); | |
// Verify results | |
if (!verifyOutput(buffers, image)) | |
{ | |
return false; | |
} | |
return true; | |
} | |
//! | |
//! \brief Reads the input and stores the result in a managed buffer | |
//! | |
bool SampleOnnxMiDasV2::processInput(const samplesCommon::BufferManager& buffers, cv::Mat & image) | |
{ | |
const int inputChannels = mInputDims.d[3]; | |
const int inputH = mInputDims.d[1]; | |
const int inputW = mInputDims.d[2]; | |
printf("inputs:0 - %d x %d x %d x %d\n", mInputDims.d[0], mInputDims.d[1], mInputDims.d[2], mInputDims.d[3]); | |
cv::Mat resized_image; | |
cv::resize(image, resized_image, cv::Size(inputW, inputH)); | |
int batchIndex = 0; | |
int batchOffset = batchIndex * inputW * inputH * inputChannels; | |
float* hostDataBuffer = static_cast<float*>(buffers.getHostBuffer(mParams.inputTensorNames[0])); | |
// input shape [B,H,W,C] | |
// inputs:0 - 1 x 256 x 256 x 3 | |
for (size_t h = 0; h < inputH; h++) { | |
for (size_t w = 0; w < inputW; w++) { | |
for (size_t c = 0; c < inputChannels; c++) { | |
hostDataBuffer[batchOffset + (h * inputW + w) * inputChannels + c] = | |
float(float(resized_image.at<cv::Vec3b>(h, w)[c]) / 255.0); // Division 255.0 is to convert uint8_t color to float_t | |
} | |
} | |
} | |
return true; | |
} | |
//! | |
//! \brief Classifies digits and verify result | |
//! | |
//! \return whether the classification output matches expectations | |
//! | |
bool SampleOnnxMiDasV2::verifyOutput(const samplesCommon::BufferManager& buffers, cv::Mat & originImage ) | |
{ | |
float* output = static_cast<float*>(buffers.getHostBuffer(mParams.outputTensorNames[0])); | |
const int output0_row = mOutputDims.d[1]; | |
const int output0_col = mOutputDims.d[2]; | |
printf("Identity:0 - %d x %d x %d\n", mOutputDims.d[0], mOutputDims.d[1], mOutputDims.d[2]); | |
cv::Mat image = cv::Mat::zeros(cv::Size(output0_row, output0_col), CV_8U); | |
for (int row = 0; row < output0_row; row++) { | |
for (int col = 0;col < output0_col; col++) { | |
image.at<uint8_t>(row, col) = (uint8_t)(*(output + (row * output0_col) + col) / 8); | |
} | |
} | |
cv::imshow("img", image); | |
cv::imshow("orgimg", originImage); | |
int key = cv::waitKey(0); | |
cv::destroyAllWindows(); | |
return true; | |
} | |
//! | |
//! \brief Initializes members of the params struct using the command line args | |
//! | |
samplesCommon::OnnxSampleParams initializeSampleParams(const samplesCommon::Args& args) | |
{ | |
samplesCommon::OnnxSampleParams params; | |
if (args.dataDirs.empty()) //!< Use default directories if user hasn't provided directory paths | |
{ | |
params.dataDirs.push_back("data/midas/"); | |
} | |
else //!< Use the data directory provided by the user | |
{ | |
params.dataDirs = args.dataDirs; | |
} | |
params.onnxFileName = "model_float32.onnx"; | |
params.inputTensorNames.push_back("inputs:0"); | |
params.outputTensorNames.push_back("Identity:0"); | |
params.dlaCore = args.useDLACore; | |
params.int8 = args.runInInt8; | |
params.fp16 = args.runInFp16; | |
return params; | |
} | |
//! | |
//! \brief Prints the help information for running this sample | |
//! | |
void printHelpInfo() | |
{ | |
std::cout | |
<< "Usage: ./sample_onnx_MiDasV2 [-h or --help] [-d or --datadir=<path to data directory>] [--useDLACore=<int>]" | |
<< std::endl; | |
std::cout << "--help Display help information" << std::endl; | |
std::cout << "--datadir Specify path to a data directory, overriding the default. This option can be used " | |
"multiple times to add multiple directories. If no data directories are given, the default is to use " | |
"(data/samples/MiDasV2/, data/MiDasV2/)" | |
<< std::endl; | |
std::cout << "--useDLACore=N Specify a DLA engine for layers that support DLA. Value can range from 0 to n-1, " | |
"where n is the number of DLA engines on the platform." | |
<< std::endl; | |
std::cout << "--int8 Run in Int8 mode." << std::endl; | |
std::cout << "--fp16 Run in FP16 mode." << std::endl; | |
} | |
int main(int argc, char** argv) | |
{ | |
samplesCommon::Args args; | |
bool argsOK = samplesCommon::parseArgs(args, argc, argv); | |
if (!argsOK) | |
{ | |
sample::gLogError << "Invalid arguments" << std::endl; | |
printHelpInfo(); | |
return EXIT_FAILURE; | |
} | |
if (args.help) | |
{ | |
printHelpInfo(); | |
return EXIT_SUCCESS; | |
} | |
auto sampleTest = sample::gLogger.defineTest(gSampleName, argc, argv); | |
sample::gLogger.reportTestStart(sampleTest); | |
SampleOnnxMiDasV2 sample(initializeSampleParams(args)); | |
sample::gLogInfo << "Building and running a GPU inference engine for Onnx MiDasV2" << std::endl; | |
if (!sample.build()) | |
{ | |
return sample::gLogger.reportFail(sampleTest); | |
} | |
if (!sample.infer()) | |
{ | |
return sample::gLogger.reportFail(sampleTest); | |
} | |
return sample::gLogger.reportPass(sampleTest); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment