yolov5: upgrade to v7.0 and support instance segmentation (#1177)
* add seg * yolov5 seg * update wts and readme * add todo * update readme
This commit is contained in:
parent
165b0a428c
commit
fbad2261dc
@ -15,6 +15,7 @@ The basic workflow of TensorRTx is:
|
||||
|
||||
## News
|
||||
|
||||
- `18 Dec 2022`. [YOLOv5](./yolov5) upgrade to support v7.0, including instance segmention.
|
||||
- `12 Dec 2022`. [East-Face](https://github.com/East-Face): [UNet](./unet) upgrade to support v3.0 of [Pytorch-UNet](https://github.com/milesial/Pytorch-UNet).
|
||||
- `26 Oct 2022`. [ausk](https://github.com/ausk): YoloP(You Only Look Once for Panopitic Driving Perception).
|
||||
- `19 Sep 2022`. [QIANXUNZDL123](https://github.com/QIANXUNZDL123) and [lindsayshuo](https://github.com/lindsayshuo): YOLOv7.
|
||||
@ -29,7 +30,6 @@ The basic workflow of TensorRTx is:
|
||||
- `18 Oct 2021`. [xupengao](https://github.com/xupengao): YOLOv5 updated to v6.0, supporting n/s/m/l/x/n6/s6/m6/l6/x6.
|
||||
- `31 Aug 2021`. [FamousDirector](https://github.com/FamousDirector): update retinaface to support TensorRT 8.0.
|
||||
- `27 Aug 2021`. [HaiyangPeng](https://github.com/HaiyangPeng): add a python wrapper for hrnet segmentation.
|
||||
- `1 Jul 2021`. [freedenS](https://github.com/freedenS): DE⫶TR: End-to-End Object Detection with Transformers. First Transformer model!
|
||||
|
||||
## Tutorials
|
||||
|
||||
@ -75,7 +75,7 @@ Following models are implemented.
|
||||
|[yolov3](./yolov3)| darknet-53, weights and pytorch implementation from [ultralytics/yolov3](https://github.com/ultralytics/yolov3) |
|
||||
|[yolov3-spp](./yolov3-spp)| darknet-53, weights and pytorch implementation from [ultralytics/yolov3](https://github.com/ultralytics/yolov3) |
|
||||
|[yolov4](./yolov4)| CSPDarknet53, weights from [AlexeyAB/darknet](https://github.com/AlexeyAB/darknet#pre-trained-models), pytorch implementation from [ultralytics/yolov3](https://github.com/ultralytics/yolov3) |
|
||||
|[yolov5](./yolov5)| yolov5 v1.0-v6.2, pytorch implementation from [ultralytics/yolov5](https://github.com/ultralytics/yolov5) |
|
||||
|[yolov5](./yolov5)| yolov5 v1.0-v7.0 of [ultralytics/yolov5](https://github.com/ultralytics/yolov5), detection, classification and instance segmentation |
|
||||
|[yolov7](./yolov7)| yolov7 v0.1, pytorch implementation from [WongKinYiu/yolov7](https://github.com/WongKinYiu/yolov7) |
|
||||
|[yolop](./yolop)| yolop, pytorch implementation from [hustvl/YOLOP](https://github.com/hustvl/YOLOP) |
|
||||
|[retinaface](./retinaface)| resnet50 and mobilnet0.25, weights from [biubug6/Pytorch_Retinaface](https://github.com/biubug6/Pytorch_Retinaface) |
|
||||
|
||||
@ -20,6 +20,7 @@ include_directories(${PROJECT_SOURCE_DIR}/include)
|
||||
include_directories(/usr/local/cuda/include)
|
||||
link_directories(/usr/local/cuda/lib64)
|
||||
# tensorrt
|
||||
# TODO(Call for PR): make TRT path configurable from command line
|
||||
include_directories(/usr/include/x86_64-linux-gnu/)
|
||||
link_directories(/usr/lib/x86_64-linux-gnu/)
|
||||
|
||||
@ -44,8 +45,14 @@ target_link_libraries(yolov5-cls cudart)
|
||||
target_link_libraries(yolov5-cls myplugins)
|
||||
target_link_libraries(yolov5-cls ${OpenCV_LIBS})
|
||||
|
||||
cuda_add_executable(yolov5-seg calibrator.cpp yolov5_seg.cpp preprocess.cu)
|
||||
|
||||
target_link_libraries(yolov5-seg nvinfer)
|
||||
target_link_libraries(yolov5-seg cudart)
|
||||
target_link_libraries(yolov5-seg myplugins)
|
||||
target_link_libraries(yolov5-seg ${OpenCV_LIBS})
|
||||
|
||||
if(UNIX)
|
||||
add_definitions(-O2 -pthread)
|
||||
endif(UNIX)
|
||||
|
||||
|
||||
|
||||
@ -33,8 +33,9 @@ TensorRTx inference code base for [ultralytics/yolov5](https://github.com/ultral
|
||||
|
||||
## Different versions of yolov5
|
||||
|
||||
Currently, we support yolov5 v1.0, v2.0, v3.0, v3.1, v4.0, v5.0, v6.0, v6.2
|
||||
Currently, we support yolov5 v1.0, v2.0, v3.0, v3.1, v4.0, v5.0, v6.0, v6.2, v7.0
|
||||
|
||||
- For yolov5 v7.0, download .pt from [yolov5 release v7.0](https://github.com/ultralytics/yolov5/releases/tag/v7.0), `git clone -b v7.0 https://github.com/ultralytics/yolov5.git` and `git clone -b yolov5-v7.0 https://github.com/wang-xinyu/tensorrtx.git`, then follow how-to-run in [tensorrtx/yolov5-v7.0](https://github.com/wang-xinyu/tensorrtx/tree/yolov5-v7.0/yolov5)
|
||||
- For yolov5 v6.2, download .pt from [yolov5 release v6.2](https://github.com/ultralytics/yolov5/releases/tag/v6.2), `git clone -b v6.2 https://github.com/ultralytics/yolov5.git` and `git clone -b yolov5-v6.2 https://github.com/wang-xinyu/tensorrtx.git`, then follow how-to-run in [tensorrtx/yolov5-v6.2](https://github.com/wang-xinyu/tensorrtx/tree/yolov5-v6.2/yolov5)
|
||||
- For yolov5 v6.0, download .pt from [yolov5 release v6.0](https://github.com/ultralytics/yolov5/releases/tag/v6.0), `git clone -b v6.0 https://github.com/ultralytics/yolov5.git` and `git clone -b yolov5-v6.0 https://github.com/wang-xinyu/tensorrtx.git`, then follow how-to-run in [tensorrtx/yolov5-v6.0](https://github.com/wang-xinyu/tensorrtx/tree/yolov5-v6.0/yolov5).
|
||||
- For yolov5 v5.0, download .pt from [yolov5 release v5.0](https://github.com/ultralytics/yolov5/releases/tag/v5.0), `git clone -b v5.0 https://github.com/ultralytics/yolov5.git` and `git clone -b yolov5-v5.0 https://github.com/wang-xinyu/tensorrtx.git`, then follow how-to-run in [tensorrtx/yolov5-v5.0](https://github.com/wang-xinyu/tensorrtx/tree/yolov5-v5.0/yolov5).
|
||||
@ -63,7 +64,7 @@ Currently, we support yolov5 v1.0, v2.0, v3.0, v3.1, v4.0, v5.0, v6.0, v6.2
|
||||
|
||||
```
|
||||
// clone code according to above #Different versions of yolov5
|
||||
// download https://github.com/ultralytics/yolov5/releases/download/v6.2/yolov5s.pt
|
||||
// download https://github.com/ultralytics/yolov5/releases/download/v7.0/yolov5s.pt
|
||||
cp {tensorrtx}/yolov5/gen_wts.py {ultralytics}/yolov5
|
||||
cd {ultralytics}/yolov5
|
||||
python gen_wts.py -w yolov5s.pt -o yolov5s.wts
|
||||
@ -103,6 +104,10 @@ python yolov5_trt.py
|
||||
python yolov5_trt_cuda_python.py
|
||||
```
|
||||
|
||||
<p align="center">
|
||||
<img src="https://user-images.githubusercontent.com/15235574/78247927-4d9fac00-751e-11ea-8b1b-704a0aeb3fcf.jpg" height="360px;">
|
||||
</p>
|
||||
|
||||
### Classification
|
||||
|
||||
```
|
||||
@ -116,6 +121,20 @@ wget https://github.com/joannzhang00/ImageNet-dataset-classes-labels/blob/main/i
|
||||
./yolov5-cls -d yolov5s-cls.engine ../samples
|
||||
```
|
||||
|
||||
### Instance Segmentation
|
||||
|
||||
```
|
||||
# Build and serialize TensorRT engine
|
||||
./yolov5-seg -s yolov5s-seg.wts yolov5s-seg.engine s
|
||||
|
||||
# Run inference
|
||||
./yolov5-seg -d yolov5s-seg.engine ../samples
|
||||
```
|
||||
|
||||
<p align="center">
|
||||
<img src="https://user-images.githubusercontent.com/15235574/208305921-0a2ee358-6550-4d36-bb86-867685bfe069.jpg" height="360px;">
|
||||
</p>
|
||||
|
||||
# INT8 Quantization
|
||||
|
||||
1. Prepare calibration images, you can randomly select 1000s images from your train set. For coco, you can also download my calibration images `coco_calib` from [GoogleDrive](https://drive.google.com/drive/folders/1s7jE9DtOngZMzJC1uL307J2MiaGwdRSI?usp=sharing) or [BaiduPan](https://pan.baidu.com/s/1GOm_-JobpyLMAqZWCDUhKg) pwd: a9wh
|
||||
@ -126,9 +145,6 @@ wget https://github.com/joannzhang00/ImageNet-dataset-classes-labels/blob/main/i
|
||||
|
||||
4. serialize the model and test
|
||||
|
||||
<p align="center">
|
||||
<img src="https://user-images.githubusercontent.com/15235574/78247927-4d9fac00-751e-11ea-8b1b-704a0aeb3fcf.jpg" height="360px;">
|
||||
</p>
|
||||
|
||||
## More Information
|
||||
|
||||
|
||||
@ -162,6 +162,7 @@ ILayer* convBlock(INetworkDefinition *network, std::map<std::string, Weights>& w
|
||||
conv1->setStrideNd(DimsHW{ s, s });
|
||||
conv1->setPaddingNd(DimsHW{ p, p });
|
||||
conv1->setNbGroups(g);
|
||||
conv1->setName((lname + ".conv").c_str());
|
||||
IScaleLayer* bn1 = addBatchNorm2d(network, weightMap, *conv1->getOutput(0), lname + ".bn", 1e-3);
|
||||
|
||||
// silu = x * sigmoid
|
||||
@ -273,6 +274,21 @@ ILayer* SPPF(INetworkDefinition *network, std::map<std::string, Weights>& weight
|
||||
return cv2;
|
||||
}
|
||||
|
||||
ILayer* Proto(INetworkDefinition* network, std::map<std::string, Weights>& weightMap, ITensor& input, int c_, int c2, std::string lname) {
|
||||
auto cv1 = convBlock(network, weightMap, input, c_, 3, 1, 1, lname + ".cv1");
|
||||
|
||||
auto upsample = network->addResize(*cv1->getOutput(0));
|
||||
assert(upsample);
|
||||
upsample->setResizeMode(ResizeMode::kNEAREST);
|
||||
const float scales[] = {1, 2, 2};
|
||||
upsample->setScales(scales, 3);
|
||||
|
||||
auto cv2 = convBlock(network, weightMap, *upsample->getOutput(0), c_, 3, 1, 1, lname + ".cv2");
|
||||
auto cv3 = convBlock(network, weightMap, *cv2->getOutput(0), c2, 1, 1, 1, lname + ".cv3");
|
||||
assert(cv3);
|
||||
return cv3;
|
||||
}
|
||||
|
||||
std::vector<std::vector<float>> getAnchors(std::map<std::string, Weights>& weightMap, std::string lname) {
|
||||
std::vector<std::vector<float>> anchors;
|
||||
Weights wts = weightMap[lname + ".anchor_grid"];
|
||||
@ -285,13 +301,13 @@ std::vector<std::vector<float>> getAnchors(std::map<std::string, Weights>& weigh
|
||||
return anchors;
|
||||
}
|
||||
|
||||
IPluginV2Layer* addYoLoLayer(INetworkDefinition *network, std::map<std::string, Weights>& weightMap, std::string lname, std::vector<IConvolutionLayer*> dets) {
|
||||
IPluginV2Layer* addYoLoLayer(INetworkDefinition *network, std::map<std::string, Weights>& weightMap, std::string lname, std::vector<IConvolutionLayer*> dets, bool is_segmentation = false) {
|
||||
auto creator = getPluginRegistry()->getPluginCreator("YoloLayer_TRT", "1");
|
||||
auto anchors = getAnchors(weightMap, lname);
|
||||
PluginField plugin_fields[2];
|
||||
int netinfo[4] = {Yolo::CLASS_NUM, Yolo::INPUT_W, Yolo::INPUT_H, Yolo::MAX_OUTPUT_BBOX_COUNT};
|
||||
int netinfo[5] = {Yolo::CLASS_NUM, Yolo::INPUT_W, Yolo::INPUT_H, Yolo::MAX_OUTPUT_BBOX_COUNT, (int)is_segmentation};
|
||||
plugin_fields[0].data = netinfo;
|
||||
plugin_fields[0].length = 4;
|
||||
plugin_fields[0].length = 5;
|
||||
plugin_fields[0].name = "netinfo";
|
||||
plugin_fields[0].type = PluginFieldType::kFLOAT32;
|
||||
|
||||
|
||||
@ -13,7 +13,7 @@ def parse_args():
|
||||
parser.add_argument(
|
||||
'-o', '--output', help='Output (.wts) file path (optional)')
|
||||
parser.add_argument(
|
||||
'-t', '--type', type=str, default='detect', choices=['detect', 'cls'],
|
||||
'-t', '--type', type=str, default='detect', choices=['detect', 'cls', 'seg'],
|
||||
help='determines the model is detection/classification')
|
||||
args = parser.parse_args()
|
||||
if not os.path.isfile(args.weights):
|
||||
@ -37,7 +37,7 @@ print(f'Loading {pt_file}')
|
||||
model = torch.load(pt_file, map_location=device) # load to FP32
|
||||
model = model['ema' if model.get('ema') else 'model'].float()
|
||||
|
||||
if m_type == "detect":
|
||||
if m_type in ['detect', 'seg']:
|
||||
# update anchor_grid info
|
||||
anchor_grid = model.model[-1].anchors * model.model[-1].stride[..., None, None]
|
||||
# model.model[-1].anchor_grid = anchor_grid
|
||||
|
||||
@ -25,12 +25,13 @@ using namespace Yolo;
|
||||
|
||||
namespace nvinfer1
|
||||
{
|
||||
YoloLayerPlugin::YoloLayerPlugin(int classCount, int netWidth, int netHeight, int maxOut, const std::vector<Yolo::YoloKernel>& vYoloKernel)
|
||||
YoloLayerPlugin::YoloLayerPlugin(int classCount, int netWidth, int netHeight, int maxOut, bool is_segmentation, const std::vector<Yolo::YoloKernel>& vYoloKernel)
|
||||
{
|
||||
mClassCount = classCount;
|
||||
mYoloV5NetWidth = netWidth;
|
||||
mYoloV5NetHeight = netHeight;
|
||||
mMaxOutObject = maxOut;
|
||||
is_segmentation_ = is_segmentation;
|
||||
mYoloKernel = vYoloKernel;
|
||||
mKernelCount = vYoloKernel.size();
|
||||
|
||||
@ -63,6 +64,7 @@ namespace nvinfer1
|
||||
read(d, mYoloV5NetWidth);
|
||||
read(d, mYoloV5NetHeight);
|
||||
read(d, mMaxOutObject);
|
||||
read(d, is_segmentation_);
|
||||
mYoloKernel.resize(mKernelCount);
|
||||
auto kernelSize = mKernelCount * sizeof(YoloKernel);
|
||||
memcpy(mYoloKernel.data(), d, kernelSize);
|
||||
@ -88,6 +90,7 @@ namespace nvinfer1
|
||||
write(d, mYoloV5NetWidth);
|
||||
write(d, mYoloV5NetHeight);
|
||||
write(d, mMaxOutObject);
|
||||
write(d, is_segmentation_);
|
||||
auto kernelSize = mKernelCount * sizeof(YoloKernel);
|
||||
memcpy(d, mYoloKernel.data(), kernelSize);
|
||||
d += kernelSize;
|
||||
@ -97,7 +100,7 @@ namespace nvinfer1
|
||||
|
||||
size_t YoloLayerPlugin::getSerializationSize() const TRT_NOEXCEPT
|
||||
{
|
||||
return sizeof(mClassCount) + sizeof(mThreadCount) + sizeof(mKernelCount) + sizeof(Yolo::YoloKernel) * mYoloKernel.size() + sizeof(mYoloV5NetWidth) + sizeof(mYoloV5NetHeight) + sizeof(mMaxOutObject);
|
||||
return sizeof(mClassCount) + sizeof(mThreadCount) + sizeof(mKernelCount) + sizeof(Yolo::YoloKernel) * mYoloKernel.size() + sizeof(mYoloV5NetWidth) + sizeof(mYoloV5NetHeight) + sizeof(mMaxOutObject) + sizeof(is_segmentation_);
|
||||
}
|
||||
|
||||
int YoloLayerPlugin::initialize() TRT_NOEXCEPT
|
||||
@ -172,7 +175,7 @@ namespace nvinfer1
|
||||
// Clone the plugin
|
||||
IPluginV2IOExt* YoloLayerPlugin::clone() const TRT_NOEXCEPT
|
||||
{
|
||||
YoloLayerPlugin* p = new YoloLayerPlugin(mClassCount, mYoloV5NetWidth, mYoloV5NetHeight, mMaxOutObject, mYoloKernel);
|
||||
YoloLayerPlugin* p = new YoloLayerPlugin(mClassCount, mYoloV5NetWidth, mYoloV5NetHeight, mMaxOutObject, is_segmentation_, mYoloKernel);
|
||||
p->setPluginNamespace(mPluginNamespace);
|
||||
return p;
|
||||
}
|
||||
@ -180,7 +183,7 @@ namespace nvinfer1
|
||||
__device__ float Logist(float data) { return 1.0f / (1.0f + expf(-data)); };
|
||||
|
||||
__global__ void CalDetection(const float *input, float *output, int noElements,
|
||||
const int netwidth, const int netheight, int maxoutobject, int yoloWidth, int yoloHeight, const float anchors[CHECK_COUNT * 2], int classes, int outputElem)
|
||||
const int netwidth, const int netheight, int maxoutobject, int yoloWidth, int yoloHeight, const float anchors[CHECK_COUNT * 2], int classes, int outputElem, bool is_segmentation)
|
||||
{
|
||||
|
||||
int idx = threadIdx.x + blockDim.x * blockIdx.x;
|
||||
@ -190,6 +193,7 @@ namespace nvinfer1
|
||||
int bnIdx = idx / total_grid;
|
||||
idx = idx - total_grid * bnIdx;
|
||||
int info_len_i = 5 + classes;
|
||||
if (is_segmentation) info_len_i += 32;
|
||||
const float* curInput = input + bnIdx * (info_len_i * total_grid * CHECK_COUNT);
|
||||
|
||||
for (int k = 0; k < CHECK_COUNT; ++k) {
|
||||
@ -197,7 +201,7 @@ namespace nvinfer1
|
||||
if (box_prob < IGNORE_THRESH) continue;
|
||||
int class_id = 0;
|
||||
float max_cls_prob = 0.0;
|
||||
for (int i = 5; i < info_len_i; ++i) {
|
||||
for (int i = 5; i < 5 + classes; ++i) {
|
||||
float p = Logist(curInput[idx + k * info_len_i * total_grid + i * total_grid]);
|
||||
if (p > max_cls_prob) {
|
||||
max_cls_prob = p;
|
||||
@ -230,6 +234,10 @@ namespace nvinfer1
|
||||
det->bbox[3] = det->bbox[3] * det->bbox[3] * anchors[2 * k + 1];
|
||||
det->conf = box_prob * max_cls_prob;
|
||||
det->class_id = class_id;
|
||||
|
||||
for (int i = 0; is_segmentation && i < 32; i++) {
|
||||
det->mask[i] = curInput[idx + k * info_len_i * total_grid + (i + 5 + classes) * total_grid];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -247,7 +255,7 @@ namespace nvinfer1
|
||||
|
||||
//printf("Net: %d %d \n", mYoloV5NetWidth, mYoloV5NetHeight);
|
||||
CalDetection << < (numElem + mThreadCount - 1) / mThreadCount, mThreadCount, 0, stream >> >
|
||||
(inputs[i], output, numElem, mYoloV5NetWidth, mYoloV5NetHeight, mMaxOutObject, yolo.width, yolo.height, (float*)mAnchor[i], mClassCount, outputElem);
|
||||
(inputs[i], output, numElem, mYoloV5NetWidth, mYoloV5NetHeight, mMaxOutObject, yolo.width, yolo.height, (float*)mAnchor[i], mClassCount, outputElem, is_segmentation_);
|
||||
}
|
||||
}
|
||||
|
||||
@ -294,9 +302,10 @@ namespace nvinfer1
|
||||
int input_w = p_netinfo[1];
|
||||
int input_h = p_netinfo[2];
|
||||
int max_output_object_count = p_netinfo[3];
|
||||
bool is_segmentation = (bool)p_netinfo[4];
|
||||
std::vector<Yolo::YoloKernel> kernels(fc->fields[1].length);
|
||||
memcpy(&kernels[0], fc->fields[1].data, kernels.size() * sizeof(Yolo::YoloKernel));
|
||||
YoloLayerPlugin* obj = new YoloLayerPlugin(class_count, input_w, input_h, max_output_object_count, kernels);
|
||||
YoloLayerPlugin* obj = new YoloLayerPlugin(class_count, input_w, input_h, max_output_object_count, is_segmentation, kernels);
|
||||
obj->setPluginNamespace(mNamespace.c_str());
|
||||
return obj;
|
||||
}
|
||||
|
||||
@ -27,6 +27,7 @@ namespace Yolo
|
||||
float bbox[LOCATIONS];
|
||||
float conf; // bbox_conf * cls_conf
|
||||
float class_id;
|
||||
float mask[32];
|
||||
};
|
||||
}
|
||||
|
||||
@ -35,7 +36,7 @@ namespace nvinfer1
|
||||
class API YoloLayerPlugin : public IPluginV2IOExt
|
||||
{
|
||||
public:
|
||||
YoloLayerPlugin(int classCount, int netWidth, int netHeight, int maxOut, const std::vector<Yolo::YoloKernel>& vYoloKernel);
|
||||
YoloLayerPlugin(int classCount, int netWidth, int netHeight, int maxOut, bool is_segmentation, const std::vector<Yolo::YoloKernel>& vYoloKernel);
|
||||
YoloLayerPlugin(const void* data, size_t length);
|
||||
~YoloLayerPlugin();
|
||||
|
||||
@ -96,6 +97,7 @@ namespace nvinfer1
|
||||
int mYoloV5NetWidth;
|
||||
int mYoloV5NetHeight;
|
||||
int mMaxOutObject;
|
||||
bool is_segmentation_;
|
||||
std::vector<Yolo::YoloKernel> mYoloKernel;
|
||||
void** mAnchor;
|
||||
};
|
||||
|
||||
430
yolov5/yolov5_seg.cpp
Normal file
430
yolov5/yolov5_seg.cpp
Normal file
@ -0,0 +1,430 @@
|
||||
#include <iostream>
|
||||
#include <chrono>
|
||||
#include <cmath>
|
||||
#include "cuda_utils.h"
|
||||
#include "logging.h"
|
||||
#include "common.hpp"
|
||||
#include "utils.h"
|
||||
#include "calibrator.h"
|
||||
#include "preprocess.h"
|
||||
|
||||
#define USE_FP32 // set USE_INT8 or USE_FP16 or USE_FP32
|
||||
#define DEVICE 0 // GPU id
|
||||
#define NMS_THRESH 0.4
|
||||
#define CONF_THRESH 0.5
|
||||
#define BATCH_SIZE 1
|
||||
#define MAX_IMAGE_INPUT_SIZE_THRESH 3000 * 3000 // ensure it exceed the maximum size in the input images !
|
||||
|
||||
// stuff we know about the network and the input/output blobs
|
||||
static const int INPUT_H = Yolo::INPUT_H;
|
||||
static const int INPUT_W = Yolo::INPUT_W;
|
||||
static const int CLASS_NUM = Yolo::CLASS_NUM;
|
||||
static const int OUTPUT_SIZE1 = Yolo::MAX_OUTPUT_BBOX_COUNT * sizeof(Yolo::Detection) / sizeof(float) + 1; // we assume the yololayer outputs no more than MAX_OUTPUT_BBOX_COUNT boxes that conf >= 0.1
|
||||
static const int OUTPUT_SIZE2 = 32 * (INPUT_H / 4) * (INPUT_W / 4);
|
||||
const char* INPUT_BLOB_NAME = "data";
|
||||
const char* OUTPUT_BLOB_NAME = "prob";
|
||||
static Logger gLogger;
|
||||
|
||||
static int get_width(int x, float gw, int divisor = 8) {
|
||||
return int(ceil((x * gw) / divisor)) * divisor;
|
||||
}
|
||||
|
||||
static int get_depth(int x, float gd) {
|
||||
if (x == 1) return 1;
|
||||
int r = round(x * gd);
|
||||
if (x * gd - int(x * gd) == 0.5 && (int(x * gd) % 2) == 0) {
|
||||
--r;
|
||||
}
|
||||
return std::max<int>(r, 1);
|
||||
}
|
||||
|
||||
ICudaEngine* build_engine(unsigned int maxBatchSize, IBuilder* builder, IBuilderConfig* config, DataType dt, float& gd, float& gw, std::string& wts_name) {
|
||||
INetworkDefinition* network = builder->createNetworkV2(0U);
|
||||
ITensor* data = network->addInput(INPUT_BLOB_NAME, dt, Dims3{ 3, INPUT_H, INPUT_W });
|
||||
assert(data);
|
||||
std::map<std::string, Weights> weightMap = loadWeights(wts_name);
|
||||
|
||||
// Backbone
|
||||
auto conv0 = convBlock(network, weightMap, *data, get_width(64, gw), 6, 2, 1, "model.0");
|
||||
assert(conv0);
|
||||
auto conv1 = convBlock(network, weightMap, *conv0->getOutput(0), get_width(128, gw), 3, 2, 1, "model.1");
|
||||
auto bottleneck_CSP2 = C3(network, weightMap, *conv1->getOutput(0), get_width(128, gw), get_width(128, gw), get_depth(3, gd), true, 1, 0.5, "model.2");
|
||||
auto conv3 = convBlock(network, weightMap, *bottleneck_CSP2->getOutput(0), get_width(256, gw), 3, 2, 1, "model.3");
|
||||
auto bottleneck_csp4 = C3(network, weightMap, *conv3->getOutput(0), get_width(256, gw), get_width(256, gw), get_depth(6, gd), true, 1, 0.5, "model.4");
|
||||
auto conv5 = convBlock(network, weightMap, *bottleneck_csp4->getOutput(0), get_width(512, gw), 3, 2, 1, "model.5");
|
||||
auto bottleneck_csp6 = C3(network, weightMap, *conv5->getOutput(0), get_width(512, gw), get_width(512, gw), get_depth(9, gd), true, 1, 0.5, "model.6");
|
||||
auto conv7 = convBlock(network, weightMap, *bottleneck_csp6->getOutput(0), get_width(1024, gw), 3, 2, 1, "model.7");
|
||||
auto bottleneck_csp8 = C3(network, weightMap, *conv7->getOutput(0), get_width(1024, gw), get_width(1024, gw), get_depth(3, gd), true, 1, 0.5, "model.8");
|
||||
auto spp9 = SPPF(network, weightMap, *bottleneck_csp8->getOutput(0), get_width(1024, gw), get_width(1024, gw), 5, "model.9");
|
||||
|
||||
// Head
|
||||
auto conv10 = convBlock(network, weightMap, *spp9->getOutput(0), get_width(512, gw), 1, 1, 1, "model.10");
|
||||
|
||||
auto upsample11 = network->addResize(*conv10->getOutput(0));
|
||||
assert(upsample11);
|
||||
upsample11->setResizeMode(ResizeMode::kNEAREST);
|
||||
upsample11->setOutputDimensions(bottleneck_csp6->getOutput(0)->getDimensions());
|
||||
|
||||
ITensor* inputTensors12[] = { upsample11->getOutput(0), bottleneck_csp6->getOutput(0) };
|
||||
auto cat12 = network->addConcatenation(inputTensors12, 2);
|
||||
auto bottleneck_csp13 = C3(network, weightMap, *cat12->getOutput(0), get_width(1024, gw), get_width(512, gw), get_depth(3, gd), false, 1, 0.5, "model.13");
|
||||
auto conv14 = convBlock(network, weightMap, *bottleneck_csp13->getOutput(0), get_width(256, gw), 1, 1, 1, "model.14");
|
||||
|
||||
auto upsample15 = network->addResize(*conv14->getOutput(0));
|
||||
assert(upsample15);
|
||||
upsample15->setResizeMode(ResizeMode::kNEAREST);
|
||||
upsample15->setOutputDimensions(bottleneck_csp4->getOutput(0)->getDimensions());
|
||||
|
||||
ITensor* inputTensors16[] = { upsample15->getOutput(0), bottleneck_csp4->getOutput(0) };
|
||||
auto cat16 = network->addConcatenation(inputTensors16, 2);
|
||||
|
||||
auto bottleneck_csp17 = C3(network, weightMap, *cat16->getOutput(0), get_width(512, gw), get_width(256, gw), get_depth(3, gd), false, 1, 0.5, "model.17");
|
||||
|
||||
// Segmentation
|
||||
IConvolutionLayer* det0 = network->addConvolutionNd(*bottleneck_csp17->getOutput(0), 3 * (32 + Yolo::CLASS_NUM + 5), DimsHW{ 1, 1 }, weightMap["model.24.m.0.weight"], weightMap["model.24.m.0.bias"]);
|
||||
auto conv18 = convBlock(network, weightMap, *bottleneck_csp17->getOutput(0), get_width(256, gw), 3, 2, 1, "model.18");
|
||||
ITensor* inputTensors19[] = { conv18->getOutput(0), conv14->getOutput(0) };
|
||||
auto cat19 = network->addConcatenation(inputTensors19, 2);
|
||||
auto bottleneck_csp20 = C3(network, weightMap, *cat19->getOutput(0), get_width(512, gw), get_width(512, gw), get_depth(3, gd), false, 1, 0.5, "model.20");
|
||||
IConvolutionLayer* det1 = network->addConvolutionNd(*bottleneck_csp20->getOutput(0), 3 * (32 + Yolo::CLASS_NUM + 5), DimsHW{ 1, 1 }, weightMap["model.24.m.1.weight"], weightMap["model.24.m.1.bias"]);
|
||||
auto conv21 = convBlock(network, weightMap, *bottleneck_csp20->getOutput(0), get_width(512, gw), 3, 2, 1, "model.21");
|
||||
ITensor* inputTensors22[] = { conv21->getOutput(0), conv10->getOutput(0) };
|
||||
auto cat22 = network->addConcatenation(inputTensors22, 2);
|
||||
auto bottleneck_csp23 = C3(network, weightMap, *cat22->getOutput(0), get_width(1024, gw), get_width(1024, gw), get_depth(3, gd), false, 1, 0.5, "model.23");
|
||||
IConvolutionLayer* det2 = network->addConvolutionNd(*bottleneck_csp23->getOutput(0), 3 * (32 + Yolo::CLASS_NUM + 5), DimsHW{ 1, 1 }, weightMap["model.24.m.2.weight"], weightMap["model.24.m.2.bias"]);
|
||||
|
||||
auto yolo = addYoLoLayer(network, weightMap, "model.24", std::vector<IConvolutionLayer*>{det0, det1, det2}, true);
|
||||
yolo->getOutput(0)->setName(OUTPUT_BLOB_NAME);
|
||||
network->markOutput(*yolo->getOutput(0));
|
||||
|
||||
auto proto = Proto(network, weightMap, *bottleneck_csp17->getOutput(0), get_width(256, gw), 32, "model.24.proto");
|
||||
proto->getOutput(0)->setName("proto");
|
||||
network->markOutput(*proto->getOutput(0));
|
||||
|
||||
// Build engine
|
||||
builder->setMaxBatchSize(maxBatchSize);
|
||||
config->setMaxWorkspaceSize(16 * (1 << 20)); // 16MB
|
||||
#if defined(USE_FP16)
|
||||
config->setFlag(BuilderFlag::kFP16);
|
||||
#elif defined(USE_INT8)
|
||||
std::cout << "Your platform support int8: " << (builder->platformHasFastInt8() ? "true" : "false") << std::endl;
|
||||
assert(builder->platformHasFastInt8());
|
||||
config->setFlag(BuilderFlag::kINT8);
|
||||
Int8EntropyCalibrator2* calibrator = new Int8EntropyCalibrator2(1, INPUT_W, INPUT_H, "./coco_calib/", "int8calib.table", INPUT_BLOB_NAME);
|
||||
config->setInt8Calibrator(calibrator);
|
||||
#endif
|
||||
|
||||
std::cout << "Building engine, please wait for a while..." << std::endl;
|
||||
ICudaEngine* engine = builder->buildEngineWithConfig(*network, *config);
|
||||
std::cout << "Build engine successfully!" << std::endl;
|
||||
|
||||
// Don't need the network any more
|
||||
network->destroy();
|
||||
|
||||
// Release host memory
|
||||
for (auto& mem : weightMap) {
|
||||
free((void*)(mem.second.values));
|
||||
}
|
||||
|
||||
return engine;
|
||||
}
|
||||
|
||||
void APIToModel(unsigned int maxBatchSize, IHostMemory** modelStream, float& gd, float& gw, std::string& wts_name) {
|
||||
// Create builder
|
||||
IBuilder* builder = createInferBuilder(gLogger);
|
||||
IBuilderConfig* config = builder->createBuilderConfig();
|
||||
|
||||
// Create model to populate the network, then set the outputs and create an engine
|
||||
ICudaEngine* engine = build_engine(maxBatchSize, builder, config, DataType::kFLOAT, gd, gw, wts_name);
|
||||
assert(engine != nullptr);
|
||||
|
||||
// Serialize the engine
|
||||
(*modelStream) = engine->serialize();
|
||||
|
||||
// Close everything down
|
||||
engine->destroy();
|
||||
builder->destroy();
|
||||
config->destroy();
|
||||
}
|
||||
|
||||
void doInference(IExecutionContext& context, cudaStream_t& stream, void **buffers, float* output1, float* output2, int batchSize) {
|
||||
// infer on the batch asynchronously, and DMA output back to host
|
||||
context.enqueue(batchSize, buffers, stream, nullptr);
|
||||
CUDA_CHECK(cudaMemcpyAsync(output1, buffers[1], batchSize * OUTPUT_SIZE1 * sizeof(float), cudaMemcpyDeviceToHost, stream));
|
||||
CUDA_CHECK(cudaMemcpyAsync(output2, buffers[2], batchSize * OUTPUT_SIZE2 * sizeof(float), cudaMemcpyDeviceToHost, stream));
|
||||
cudaStreamSynchronize(stream);
|
||||
}
|
||||
|
||||
bool parse_args(int argc, char** argv, std::string& wts, std::string& engine, float& gd, float& gw, std::string& img_dir) {
|
||||
if (argc < 4) return false;
|
||||
if (std::string(argv[1]) == "-s" && (argc == 5 || argc == 7)) {
|
||||
wts = std::string(argv[2]);
|
||||
engine = std::string(argv[3]);
|
||||
auto net = std::string(argv[4]);
|
||||
if (net[0] == 'n') {
|
||||
gd = 0.33;
|
||||
gw = 0.25;
|
||||
} else if (net[0] == 's') {
|
||||
gd = 0.33;
|
||||
gw = 0.50;
|
||||
} else if (net[0] == 'm') {
|
||||
gd = 0.67;
|
||||
gw = 0.75;
|
||||
} else if (net[0] == 'l') {
|
||||
gd = 1.0;
|
||||
gw = 1.0;
|
||||
} else if (net[0] == 'x') {
|
||||
gd = 1.33;
|
||||
gw = 1.25;
|
||||
} else if (net[0] == 'c' && argc == 7) {
|
||||
gd = atof(argv[5]);
|
||||
gw = atof(argv[6]);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
} else if (std::string(argv[1]) == "-d" && argc == 4) {
|
||||
engine = std::string(argv[2]);
|
||||
img_dir = std::string(argv[3]);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
cv::Rect get_downscale_rect(float bbox[4], float scale) {
|
||||
float left = bbox[0] - bbox[2] / 2;
|
||||
float top = bbox[1] - bbox[3] / 2;
|
||||
float right = bbox[0] + bbox[2] / 2;
|
||||
float bottom = bbox[1] + bbox[3] / 2;
|
||||
left /= scale;
|
||||
top /= scale;
|
||||
right /= scale;
|
||||
bottom /= scale;
|
||||
return cv::Rect(round(left), round(top), round(right - left), round(bottom - top));
|
||||
}
|
||||
|
||||
std::vector<cv::Mat> process_mask(const float* proto, std::vector<Yolo::Detection>& dets) {
|
||||
std::vector<cv::Mat> masks;
|
||||
for (size_t i = 0; i < dets.size(); i++) {
|
||||
cv::Mat mask_mat = cv::Mat::zeros(INPUT_H / 4, INPUT_W / 4, CV_32FC1);
|
||||
auto r = get_downscale_rect(dets[i].bbox, 4);
|
||||
for (int x = r.x; x < r.x + r.width; x++) {
|
||||
for (int y = r.y; y < r.y + r.height; y++) {
|
||||
float e = 0.0f;
|
||||
for (int j = 0; j < 32; j++) {
|
||||
e += dets[i].mask[j] * proto[j * OUTPUT_SIZE2 / 32 + y * mask_mat.cols + x];
|
||||
}
|
||||
e = 1.0f / (1.0f + expf(-e));
|
||||
mask_mat.at<float>(y, x) = e;
|
||||
// if (e > 0.5) {
|
||||
// // TODO(Call for PR): Use different colors for different class ids
|
||||
// mask_mat.at<cv::Vec3b>(y, x)[2] = 0xFF;
|
||||
// mask_mat.at<cv::Vec3b>(y, x)[1] = 0x38;
|
||||
// mask_mat.at<cv::Vec3b>(y, x)[0] = 0x38;
|
||||
// }
|
||||
}
|
||||
}
|
||||
cv::resize(mask_mat, mask_mat, cv::Size(INPUT_W, INPUT_H));
|
||||
masks.push_back(mask_mat);
|
||||
}
|
||||
return masks;
|
||||
}
|
||||
|
||||
cv::Mat scale_mask(cv::Mat mask, cv::Mat img) {
|
||||
int x, y, w, h;
|
||||
float r_w = INPUT_W / (img.cols * 1.0);
|
||||
float r_h = INPUT_H / (img.rows * 1.0);
|
||||
if (r_h > r_w) {
|
||||
w = INPUT_W;
|
||||
h = r_w * img.rows;
|
||||
x = 0;
|
||||
y = (INPUT_H - h) / 2;
|
||||
} else {
|
||||
w = r_h * img.cols;
|
||||
h = INPUT_H;
|
||||
x = (INPUT_W - w) / 2;
|
||||
y = 0;
|
||||
}
|
||||
cv::Rect r(x, y, w, h);
|
||||
cv::Mat res;
|
||||
cv::resize(mask(r), res, img.size());
|
||||
return res;
|
||||
}
|
||||
|
||||
void draw_mask_bbox(cv::Mat& img, std::vector<Yolo::Detection>& dets, std::vector<cv::Mat>& masks) {
|
||||
static std::vector<uint32_t> colors = {0xFF3838, 0xFF9D97, 0xFF701F, 0xFFB21D, 0xCFD231, 0x48F90A,
|
||||
0x92CC17, 0x3DDB86, 0x1A9334, 0x00D4BB, 0x2C99A8, 0x00C2FF,
|
||||
0x344593, 0x6473FF, 0x0018EC, 0x8438FF, 0x520085, 0xCB38FF,
|
||||
0xFF95C8, 0xFF37C7};
|
||||
for (size_t i = 0; i < dets.size(); i++) {
|
||||
cv::Mat img_mask = scale_mask(masks[i], img);
|
||||
auto color = colors[(int)dets[i].class_id % colors.size()];
|
||||
auto bgr = cv::Scalar(color & 0xFF, color >> 8 & 0xFF, color >> 16 & 0xFF);
|
||||
|
||||
cv::Rect r = get_rect(img, dets[i].bbox);
|
||||
for (int x = r.x; x < r.x + r.width; x++) {
|
||||
for (int y = r.y; y < r.y + r.height; y++) {
|
||||
float val = img_mask.at<float>(y, x);
|
||||
if (val <= 0.5) continue;
|
||||
img.at<cv::Vec3b>(y, x)[0] = img.at<cv::Vec3b>(y, x)[0] / 2 + bgr[0] / 2;
|
||||
img.at<cv::Vec3b>(y, x)[1] = img.at<cv::Vec3b>(y, x)[1] / 2 + bgr[1] / 2;
|
||||
img.at<cv::Vec3b>(y, x)[2] = img.at<cv::Vec3b>(y, x)[2] / 2 + bgr[2] / 2;
|
||||
}
|
||||
}
|
||||
|
||||
cv::rectangle(img, r, bgr, 2);
|
||||
// TODO(Call for PR): convert class id to class name
|
||||
cv::putText(img, std::to_string((int)dets[i].class_id), cv::Point(r.x, r.y - 1), cv::FONT_HERSHEY_PLAIN, 1.2, cv::Scalar::all(0xFF), 2);
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
cudaSetDevice(DEVICE);
|
||||
|
||||
std::string wts_name = "";
|
||||
std::string engine_name = "";
|
||||
float gd = 0.0f, gw = 0.0f;
|
||||
std::string img_dir;
|
||||
if (!parse_args(argc, argv, wts_name, engine_name, gd, gw, img_dir)) {
|
||||
std::cerr << "arguments not right!" << std::endl;
|
||||
std::cerr << "./yolov5-seg -s [.wts] [.engine] [n/s/m/l/x or c gd gw] // serialize model to plan file" << std::endl;
|
||||
std::cerr << "./yolov5-seg -d [.engine] ../samples // deserialize plan file and run inference" << std::endl;
|
||||
return -1;
|
||||
}
|
||||
|
||||
// create a model using the API directly and serialize it to a stream
|
||||
if (!wts_name.empty()) {
|
||||
IHostMemory* modelStream{ nullptr };
|
||||
APIToModel(BATCH_SIZE, &modelStream, gd, gw, wts_name);
|
||||
assert(modelStream != nullptr);
|
||||
std::ofstream p(engine_name, std::ios::binary);
|
||||
if (!p) {
|
||||
std::cerr << "could not open plan output file" << std::endl;
|
||||
return -1;
|
||||
}
|
||||
p.write(reinterpret_cast<const char*>(modelStream->data()), modelStream->size());
|
||||
modelStream->destroy();
|
||||
return 0;
|
||||
}
|
||||
|
||||
// deserialize the .engine and run inference
|
||||
std::ifstream file(engine_name, std::ios::binary);
|
||||
if (!file.good()) {
|
||||
std::cerr << "read " << engine_name << " error!" << std::endl;
|
||||
return -1;
|
||||
}
|
||||
char *trtModelStream = nullptr;
|
||||
size_t size = 0;
|
||||
file.seekg(0, file.end);
|
||||
size = file.tellg();
|
||||
file.seekg(0, file.beg);
|
||||
trtModelStream = new char[size];
|
||||
assert(trtModelStream);
|
||||
file.read(trtModelStream, size);
|
||||
file.close();
|
||||
|
||||
std::vector<std::string> file_names;
|
||||
if (read_files_in_dir(img_dir.c_str(), file_names) < 0) {
|
||||
std::cerr << "read_files_in_dir failed." << std::endl;
|
||||
return -1;
|
||||
}
|
||||
|
||||
static float prob[BATCH_SIZE * OUTPUT_SIZE1];
|
||||
static float proto[BATCH_SIZE * OUTPUT_SIZE2];
|
||||
IRuntime* runtime = createInferRuntime(gLogger);
|
||||
assert(runtime != nullptr);
|
||||
ICudaEngine* engine = runtime->deserializeCudaEngine(trtModelStream, size);
|
||||
assert(engine != nullptr);
|
||||
IExecutionContext* context = engine->createExecutionContext();
|
||||
assert(context != nullptr);
|
||||
delete[] trtModelStream;
|
||||
assert(engine->getNbBindings() == 3);
|
||||
float* buffers[3];
|
||||
// In order to bind the buffers, we need to know the names of the input and output tensors.
|
||||
// Note that indices are guaranteed to be less than IEngine::getNbBindings()
|
||||
const int inputIndex = engine->getBindingIndex(INPUT_BLOB_NAME);
|
||||
const int outputIndex1 = engine->getBindingIndex(OUTPUT_BLOB_NAME);
|
||||
const int outputIndex2 = engine->getBindingIndex("proto");
|
||||
assert(inputIndex == 0);
|
||||
assert(outputIndex1 == 1);
|
||||
assert(outputIndex2 == 2);
|
||||
// Create GPU buffers on device
|
||||
CUDA_CHECK(cudaMalloc((void**)&buffers[inputIndex], BATCH_SIZE * 3 * INPUT_H * INPUT_W * sizeof(float)));
|
||||
CUDA_CHECK(cudaMalloc((void**)&buffers[outputIndex1], BATCH_SIZE * OUTPUT_SIZE1 * sizeof(float)));
|
||||
CUDA_CHECK(cudaMalloc((void**)&buffers[outputIndex2], BATCH_SIZE * OUTPUT_SIZE2 * sizeof(float)));
|
||||
|
||||
// Create stream
|
||||
cudaStream_t stream;
|
||||
CUDA_CHECK(cudaStreamCreate(&stream));
|
||||
uint8_t* img_host = nullptr;
|
||||
uint8_t* img_device = nullptr;
|
||||
// prepare input data cache in pinned memory
|
||||
CUDA_CHECK(cudaMallocHost((void**)&img_host, MAX_IMAGE_INPUT_SIZE_THRESH * 3));
|
||||
// prepare input data cache in device memory
|
||||
CUDA_CHECK(cudaMalloc((void**)&img_device, MAX_IMAGE_INPUT_SIZE_THRESH * 3));
|
||||
int fcount = 0;
|
||||
std::vector<cv::Mat> imgs_buffer(BATCH_SIZE);
|
||||
for (int f = 0; f < (int)file_names.size(); f++) {
|
||||
fcount++;
|
||||
if (fcount < BATCH_SIZE && f + 1 != (int)file_names.size()) continue;
|
||||
//auto start = std::chrono::system_clock::now();
|
||||
float *buffer_idx = (float*)buffers[inputIndex];
|
||||
for (int b = 0; b < fcount; b++) {
|
||||
cv::Mat img = cv::imread(img_dir + "/" + file_names[f - fcount + 1 + b]);
|
||||
if (img.empty()) continue;
|
||||
imgs_buffer[b] = img;
|
||||
size_t size_image = img.cols * img.rows * 3;
|
||||
size_t size_image_dst = INPUT_H * INPUT_W * 3;
|
||||
//copy data to pinned memory
|
||||
memcpy(img_host, img.data, size_image);
|
||||
//copy data to device memory
|
||||
CUDA_CHECK(cudaMemcpyAsync(img_device, img_host, size_image, cudaMemcpyHostToDevice, stream));
|
||||
preprocess_kernel_img(img_device, img.cols, img.rows, buffer_idx, INPUT_W, INPUT_H, stream);
|
||||
buffer_idx += size_image_dst;
|
||||
}
|
||||
// Run inference
|
||||
auto start = std::chrono::system_clock::now();
|
||||
doInference(*context, stream, (void**)buffers, prob, proto, BATCH_SIZE);
|
||||
auto end = std::chrono::system_clock::now();
|
||||
std::cout << "inference time: " << std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count() << "ms" << std::endl;
|
||||
std::vector<std::vector<Yolo::Detection>> batch_res(fcount);
|
||||
for (int b = 0; b < fcount; b++) {
|
||||
auto& res = batch_res[b];
|
||||
nms(res, &prob[b * OUTPUT_SIZE1], CONF_THRESH, NMS_THRESH);
|
||||
}
|
||||
for (int b = 0; b < fcount; b++) {
|
||||
auto& res = batch_res[b];
|
||||
cv::Mat img = imgs_buffer[b];
|
||||
|
||||
auto masks = process_mask(&proto[b * OUTPUT_SIZE2], res);
|
||||
draw_mask_bbox(img, res, masks);
|
||||
cv::imwrite("_" + file_names[f - fcount + 1 + b], img);
|
||||
}
|
||||
fcount = 0;
|
||||
}
|
||||
|
||||
// Release stream and buffers
|
||||
cudaStreamDestroy(stream);
|
||||
CUDA_CHECK(cudaFree(img_device));
|
||||
CUDA_CHECK(cudaFreeHost(img_host));
|
||||
CUDA_CHECK(cudaFree(buffers[inputIndex]));
|
||||
CUDA_CHECK(cudaFree(buffers[outputIndex1]));
|
||||
CUDA_CHECK(cudaFree(buffers[outputIndex2]));
|
||||
// Destroy the engine
|
||||
context->destroy();
|
||||
engine->destroy();
|
||||
runtime->destroy();
|
||||
|
||||
|
||||
// Print histogram of the output distribution
|
||||
//std::cout << "\nOutput:\n\n";
|
||||
//for (unsigned int i = 0; i < OUTPUT_SIZE; i++)
|
||||
//{
|
||||
// std::cout << prob[i] << ", ";
|
||||
// if (i % 10 == 0) std::cout << std::endl;
|
||||
//}
|
||||
//std::cout << std::endl;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user