diff --git a/README.md b/README.md
index f67033d..416b50a 100644
--- a/README.md
+++ b/README.md
@@ -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) |
diff --git a/yolov5/CMakeLists.txt b/yolov5/CMakeLists.txt
index 3cee96f..05630f3 100644
--- a/yolov5/CMakeLists.txt
+++ b/yolov5/CMakeLists.txt
@@ -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)
-
diff --git a/yolov5/README.md b/yolov5/README.md
index d5064cf..a315647 100644
--- a/yolov5/README.md
+++ b/yolov5/README.md
@@ -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
```
+
+
+
+
### 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
+```
+
+
+
+
+
# 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
-
-
-
## More Information
diff --git a/yolov5/common.hpp b/yolov5/common.hpp
index 0d42f5d..e7458de 100644
--- a/yolov5/common.hpp
+++ b/yolov5/common.hpp
@@ -162,6 +162,7 @@ ILayer* convBlock(INetworkDefinition *network, std::map& 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& weight
return cv2;
}
+ILayer* Proto(INetworkDefinition* network, std::map& 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> getAnchors(std::map& weightMap, std::string lname) {
std::vector> anchors;
Weights wts = weightMap[lname + ".anchor_grid"];
@@ -285,13 +301,13 @@ std::vector> getAnchors(std::map& weigh
return anchors;
}
-IPluginV2Layer* addYoLoLayer(INetworkDefinition *network, std::map& weightMap, std::string lname, std::vector dets) {
+IPluginV2Layer* addYoLoLayer(INetworkDefinition *network, std::map& weightMap, std::string lname, std::vector 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;
diff --git a/yolov5/gen_wts.py b/yolov5/gen_wts.py
index 4e1d670..1b1a980 100644
--- a/yolov5/gen_wts.py
+++ b/yolov5/gen_wts.py
@@ -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
diff --git a/yolov5/yololayer.cu b/yolov5/yololayer.cu
index 0bc9f70..02dcd4c 100644
--- a/yolov5/yololayer.cu
+++ b/yolov5/yololayer.cu
@@ -25,12 +25,13 @@ using namespace Yolo;
namespace nvinfer1
{
- YoloLayerPlugin::YoloLayerPlugin(int classCount, int netWidth, int netHeight, int maxOut, const std::vector& vYoloKernel)
+ YoloLayerPlugin::YoloLayerPlugin(int classCount, int netWidth, int netHeight, int maxOut, bool is_segmentation, const std::vector& 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 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;
}
diff --git a/yolov5/yololayer.h b/yolov5/yololayer.h
index 69c0eee..cbf6427 100644
--- a/yolov5/yololayer.h
+++ b/yolov5/yololayer.h
@@ -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& vYoloKernel);
+ YoloLayerPlugin(int classCount, int netWidth, int netHeight, int maxOut, bool is_segmentation, const std::vector& 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 mYoloKernel;
void** mAnchor;
};
diff --git a/yolov5/yolov5_seg.cpp b/yolov5/yolov5_seg.cpp
new file mode 100644
index 0000000..8977acf
--- /dev/null
+++ b/yolov5/yolov5_seg.cpp
@@ -0,0 +1,430 @@
+#include
+#include
+#include
+#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(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 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{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 process_mask(const float* proto, std::vector& dets) {
+ std::vector 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(y, x) = e;
+ // if (e > 0.5) {
+ // // TODO(Call for PR): Use different colors for different class ids
+ // mask_mat.at(y, x)[2] = 0xFF;
+ // mask_mat.at(y, x)[1] = 0x38;
+ // mask_mat.at(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& dets, std::vector& masks) {
+ static std::vector 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(y, x);
+ if (val <= 0.5) continue;
+ img.at(y, x)[0] = img.at(y, x)[0] / 2 + bgr[0] / 2;
+ img.at(y, x)[1] = img.at(y, x)[1] / 2 + bgr[1] / 2;
+ img.at(y, x)[2] = img.at(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(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 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 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(end - start).count() << "ms" << std::endl;
+ std::vector> 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;
+}
+