yolov3-spp dynamic shapes
This commit is contained in:
parent
659fd2b234
commit
3d022d9114
@ -10,6 +10,8 @@ All the models are implemented in pytorch or mxnet first, and export a weights f
|
||||
|
||||
## News
|
||||
|
||||
- `19 Nov 2020`. YOLOv3-SPP supports dynamic input shape, including a dynamic plugin.
|
||||
- `17 Nov 2020`. [AlfengYuan](https://github.com/AlfengYuan) added a Dockerfile.
|
||||
- `7 Nov 2020`. All models migrated to trt7 API, and clean up the master branch.
|
||||
- `29 Oct 2020`. First INT8 quantization implementation! Please check retinaface.
|
||||
- `23 Oct 2020`. Add a .wts model zoo for quick evaluation.
|
||||
@ -66,7 +68,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-s/m/l/x v1.0 v2.0 v3.0, pytorch implementation from [ultralytics/yolov5](https://github.com/ultralytics/yolov5) |
|
||||
|[yolov5](./yolov5)| yolov5-s/m/l/x v1.0 v2.0 v3.0 v3.1, pytorch implementation from [ultralytics/yolov5](https://github.com/ultralytics/yolov5) |
|
||||
|[retinaface](./retinaface)| resnet50 and mobilnet0.25, weights from [biubug6/Pytorch_Retinaface](https://github.com/biubug6/Pytorch_Retinaface) |
|
||||
|[arcface](./arcface)| LResNet50E-IR, weights from [deepinsight/insightface](https://github.com/deepinsight/insightface) |
|
||||
|[retinafaceAntiCov](./retinafaceAntiCov)| mobilenet0.25, weights from [deepinsight/insightface](https://github.com/deepinsight/insightface), retinaface anti-COVID-19, detect face and mask attribute |
|
||||
|
||||
@ -13,20 +13,16 @@ find_package(CUDA REQUIRED)
|
||||
set(CUDA_NVCC_PLAGS ${CUDA_NVCC_PLAGS};-std=c++11;-g;-G;-gencode;arch=compute_30;code=sm_30)
|
||||
|
||||
include_directories(${PROJECT_SOURCE_DIR}/include)
|
||||
if (CMAKE_SYSTEM_PROCESSOR MATCHES "aarch64")
|
||||
message("embed_platform on")
|
||||
include_directories(/usr/local/cuda/targets/aarch64-linux/include)
|
||||
link_directories(/usr/local/cuda/targets/aarch64-linux/lib)
|
||||
else()
|
||||
message("embed_platform off")
|
||||
include_directories(/usr/local/cuda/include)
|
||||
link_directories(/usr/local/cuda/lib64)
|
||||
endif()
|
||||
|
||||
# include and link dirs of cuda and tensorrt, you need adapt them if yours are different
|
||||
# cuda
|
||||
include_directories(/usr/local/cuda/include)
|
||||
link_directories(/usr/local/cuda/lib64)
|
||||
# tensorrt
|
||||
include_directories(/usr/include/x86_64-linux-gnu/)
|
||||
link_directories(/usr/lib/x86_64-linux-gnu/)
|
||||
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -Wall -Ofast -Wfatal-errors -D_MWAITXINTRIN_H_INCLUDED")
|
||||
|
||||
#cuda_add_library(leaky ${PROJECT_SOURCE_DIR}/leaky.cu)
|
||||
cuda_add_library(yololayer SHARED ${PROJECT_SOURCE_DIR}/yololayer.cu)
|
||||
target_link_libraries(yololayer nvinfer cudart)
|
||||
|
||||
|
||||
@ -1,41 +1,48 @@
|
||||
# yolov3-spp
|
||||
|
||||
yolov4 is [here](../yolov4).
|
||||
Currently this is supporting dynamic input shape, if you want to use non-dynamic version, please checkout commit [659fd2b](https://github.com/wang-xinyu/tensorrtx/commit/659fd2b23482197b19dccf746a5a3dbff1611381).
|
||||
|
||||
The Pytorch implementation is [ultralytics/yolov3](https://github.com/ultralytics/yolov3). It provides two trained weights of yolov3-spp, `yolov3-spp.pt` and `yolov3-spp-ultralytics.pt`(originally named `ultralytics68.pt`).
|
||||
|
||||
Following tricks are used in this yolov3-spp:
|
||||
## Config
|
||||
|
||||
- Yololayer plugin is different from the plugin used in [this repo's yolov3](https://github.com/wang-xinyu/tensorrtx/tree/master/yolov3). In this version, three yololayer are implemented in one plugin to improve speed, codes derived from [lewes6369/TensorRT-Yolov3](https://github.com/lewes6369/TensorRT-Yolov3)
|
||||
- Batchnorm layer, implemented by scale layer.
|
||||
- Number of classes defined in yololayer.h
|
||||
- FP16/FP32 can be selected by the macro in yolov3-spp.cpp
|
||||
- GPU id can be selected by the macro in yolov3-spp.cpp
|
||||
- NMS thresh in yolov3-spp.cpp
|
||||
- BBox confidence thresh in yolov3-spp.cpp
|
||||
- MIN and MAX input size defined in yolov3-spp.cpp
|
||||
- Optimization width and height for IOptimizationProfile defined in yolov3-spp.cpp
|
||||
|
||||
## Excute:
|
||||
## How to Run
|
||||
|
||||
```
|
||||
1. generate yolov3-spp_ultralytics68.wts from pytorch implementation with yolov3-spp.cfg and yolov3-spp-ultralytics.pt, or download .wts from model zoo
|
||||
|
||||
```
|
||||
git clone https://github.com/wang-xinyu/tensorrtx.git
|
||||
git clone https://github.com/ultralytics/yolov3.git
|
||||
// download its weights 'yolov3-spp-ultralytics.pt'
|
||||
cd yolov3
|
||||
cp ../tensorrtx/yolov3-spp/gen_wts.py .
|
||||
// copy gen_wts.py from tensorrtx/yolov3-spp/ to ultralytics/yolov3/
|
||||
// go to ultralytics/yolov3/
|
||||
python gen_wts.py yolov3-spp-ultralytics.pt
|
||||
// a file 'yolov3-spp_ultralytics68.wts' will be generated.
|
||||
// the master branch of yolov3 should work, if not, you can checkout 4ac60018f6e6c1e24b496485f126a660d9c793d8
|
||||
```
|
||||
|
||||
2. put yolov3-spp_ultralytics68.wts into yolov3-spp, build and run
|
||||
2. build tensorrtx/yolov3-spp and run
|
||||
|
||||
mv yolov3-spp_ultralytics68.wts ../tensorrtx/yolov3-spp/
|
||||
cd ../tensorrtx/yolov3-spp
|
||||
```
|
||||
// put yolov3-spp_ultralytics68.wts into tensorrtx/yolov3-spp/
|
||||
// go to tensorrtx/yolov3-spp/
|
||||
mkdir build
|
||||
cd build
|
||||
cmake ..
|
||||
make
|
||||
sudo ./yolov3-spp -s // serialize model to plan file i.e. 'yolov3-spp.engine'
|
||||
sudo ./yolov3-spp -d ../samples // deserialize plan file and run inference, the images in samples will be processed.
|
||||
```
|
||||
|
||||
3. check the images generated, as follows. _zidane.jpg and _bus.jpg
|
||||
```
|
||||
|
||||
<p align="center">
|
||||
<img src="https://user-images.githubusercontent.com/15235574/78247927-4d9fac00-751e-11ea-8b1b-704a0aeb3fcf.jpg">
|
||||
@ -45,15 +52,6 @@ sudo ./yolov3-spp -d ../samples // deserialize plan file and run inference, the
|
||||
<img src="https://user-images.githubusercontent.com/15235574/78247970-60b27c00-751e-11ea-88df-41473fed4823.jpg">
|
||||
</p>
|
||||
|
||||
## Config
|
||||
|
||||
- Input shape defined in yololayer.h
|
||||
- Number of classes defined in yololayer.h
|
||||
- FP16/FP32 can be selected by the macro in yolov3-spp.cpp
|
||||
- GPU id can be selected by the macro in yolov3-spp.cpp
|
||||
- NMS thresh in yolov3-spp.cpp
|
||||
- BBox confidence thresh in yolov3-spp.cpp
|
||||
|
||||
## More Information
|
||||
|
||||
See the readme in [home page.](https://github.com/wang-xinyu/tensorrtx)
|
||||
|
||||
@ -11,12 +11,25 @@ namespace nvinfer1
|
||||
mYoloKernel.push_back(yolo1);
|
||||
mYoloKernel.push_back(yolo2);
|
||||
mYoloKernel.push_back(yolo3);
|
||||
|
||||
mKernelCount = mYoloKernel.size();
|
||||
|
||||
CUDA_CHECK(cudaMallocHost(&mAnchor, mKernelCount * sizeof(void*)));
|
||||
size_t anchorLen = sizeof(float) * CHECK_COUNT * 2;
|
||||
for (int i = 0; i < mKernelCount; i++)
|
||||
{
|
||||
CUDA_CHECK(cudaMalloc(&mAnchor[i], anchorLen));
|
||||
const auto& yolo = mYoloKernel[i];
|
||||
CUDA_CHECK(cudaMemcpy(mAnchor[i], yolo.anchors, anchorLen, cudaMemcpyHostToDevice));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
YoloLayerPlugin::~YoloLayerPlugin()
|
||||
{
|
||||
for (int i = 0; i < mKernelCount; i++)
|
||||
{
|
||||
CUDA_CHECK(cudaFree(mAnchor[i]));
|
||||
}
|
||||
CUDA_CHECK(cudaFreeHost(mAnchor));
|
||||
}
|
||||
|
||||
// create the plugin at runtime from a byte stream
|
||||
@ -28,11 +41,19 @@ namespace nvinfer1
|
||||
read(d, mThreadCount);
|
||||
read(d, mKernelCount);
|
||||
mYoloKernel.resize(mKernelCount);
|
||||
auto kernelSize = mKernelCount*sizeof(YoloKernel);
|
||||
memcpy(mYoloKernel.data(),d,kernelSize);
|
||||
auto kernelSize = mKernelCount * sizeof(YoloKernel);
|
||||
memcpy(mYoloKernel.data(), d, kernelSize);
|
||||
d += kernelSize;
|
||||
|
||||
assert(d == a + length);
|
||||
|
||||
CUDA_CHECK(cudaMallocHost(&mAnchor, mKernelCount * sizeof(void*)));
|
||||
size_t anchorLen = sizeof(float) * CHECK_COUNT * 2;
|
||||
for (int i = 0; i < mKernelCount; i++)
|
||||
{
|
||||
CUDA_CHECK(cudaMalloc(&mAnchor[i], anchorLen));
|
||||
const auto& yolo = mYoloKernel[i];
|
||||
CUDA_CHECK(cudaMemcpy(mAnchor[i], yolo.anchors, anchorLen, cudaMemcpyHostToDevice));
|
||||
}
|
||||
}
|
||||
|
||||
void YoloLayerPlugin::serialize(void* buffer) const
|
||||
@ -42,29 +63,32 @@ namespace nvinfer1
|
||||
write(d, mClassCount);
|
||||
write(d, mThreadCount);
|
||||
write(d, mKernelCount);
|
||||
auto kernelSize = mKernelCount*sizeof(YoloKernel);
|
||||
memcpy(d,mYoloKernel.data(),kernelSize);
|
||||
auto kernelSize = mKernelCount * sizeof(YoloKernel);
|
||||
memcpy(d,mYoloKernel.data(), kernelSize);
|
||||
d += kernelSize;
|
||||
|
||||
assert(d == a + getSerializationSize());
|
||||
}
|
||||
|
||||
|
||||
size_t YoloLayerPlugin::getSerializationSize() const
|
||||
{
|
||||
{
|
||||
return sizeof(mClassCount) + sizeof(mThreadCount) + sizeof(mKernelCount) + sizeof(Yolo::YoloKernel) * mYoloKernel.size();
|
||||
}
|
||||
|
||||
int YoloLayerPlugin::initialize()
|
||||
{
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
Dims YoloLayerPlugin::getOutputDimensions(int index, const Dims* inputs, int nbInputDims)
|
||||
|
||||
DimsExprs YoloLayerPlugin::getOutputDimensions(int outputIndex, const DimsExprs* inputs, int nbInputs, IExprBuilder& exprBuilder)
|
||||
{
|
||||
//output the result to channel
|
||||
int totalsize = MAX_OUTPUT_BBOX_COUNT * sizeof(Detection) / sizeof(float);
|
||||
|
||||
return Dims3(totalsize + 1, 1, 1);
|
||||
DimsExprs de;
|
||||
de.nbDims = 2;
|
||||
de.d[0] = exprBuilder.constant(inputs[0].d[0]->getConstantValue()); // batchsize
|
||||
de.d[1] = exprBuilder.constant(totalsize + 1); // outputsize
|
||||
return de;
|
||||
}
|
||||
|
||||
// Set plugin namespace
|
||||
@ -84,19 +108,7 @@ namespace nvinfer1
|
||||
return DataType::kFLOAT;
|
||||
}
|
||||
|
||||
// Return true if output tensor is broadcast across a batch.
|
||||
bool YoloLayerPlugin::isOutputBroadcastAcrossBatch(int outputIndex, const bool* inputIsBroadcasted, int nbInputs) const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Return true if plugin can use input that is broadcast across batch without replication.
|
||||
bool YoloLayerPlugin::canBroadcastInputAcrossBatch(int inputIndex) const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
void YoloLayerPlugin::configurePlugin(const PluginTensorDesc* in, int nbInput, const PluginTensorDesc* out, int nbOutput)
|
||||
void YoloLayerPlugin::configurePlugin(const DynamicPluginTensorDesc* in, int nbInputs, const DynamicPluginTensorDesc* out, int nbOutputs)
|
||||
{
|
||||
}
|
||||
|
||||
@ -124,7 +136,7 @@ namespace nvinfer1
|
||||
}
|
||||
|
||||
// Clone the plugin
|
||||
IPluginV2IOExt* YoloLayerPlugin::clone() const
|
||||
IPluginV2DynamicExt* YoloLayerPlugin::clone() const
|
||||
{
|
||||
YoloLayerPlugin *p = new YoloLayerPlugin();
|
||||
p->setPluginNamespace(mPluginNamespace);
|
||||
@ -133,9 +145,9 @@ namespace nvinfer1
|
||||
|
||||
__device__ float Logist(float data){ return 1.0f / (1.0f + expf(-data)); };
|
||||
|
||||
__global__ void CalDetection(const float *input, float *output,int noElements,
|
||||
int yoloWidth,int yoloHeight,const float anchors[CHECK_COUNT*2],int classes,int outputElem) {
|
||||
|
||||
__global__ void CalDetection(const float *input, float *output, int noElements,
|
||||
int yoloWidth, int yoloHeight, int yoloStride, const float anchors[CHECK_COUNT * 2], int classes, int outputElem) {
|
||||
|
||||
int idx = threadIdx.x + blockDim.x * blockIdx.x;
|
||||
if (idx >= noElements) return;
|
||||
|
||||
@ -158,20 +170,20 @@ namespace nvinfer1
|
||||
float box_prob = Logist(curInput[idx + k * info_len_i * total_grid + 4 * total_grid]);
|
||||
if (max_cls_prob < IGNORE_THRESH || box_prob < IGNORE_THRESH) continue;
|
||||
|
||||
float *res_count = output + bnIdx*outputElem;
|
||||
float *res_count = output + bnIdx * outputElem;
|
||||
int count = (int)atomicAdd(res_count, 1);
|
||||
if (count >= MAX_OUTPUT_BBOX_COUNT) return;
|
||||
char* data = (char * )res_count + sizeof(float) + count*sizeof(Detection);
|
||||
Detection* det = (Detection*)(data);
|
||||
char* data = (char*)res_count + sizeof(float) + count * sizeof(Detection);
|
||||
Detection* det = (Detection*)(data);
|
||||
|
||||
int row = idx / yoloWidth;
|
||||
int col = idx % yoloWidth;
|
||||
|
||||
//Location
|
||||
det->bbox[0] = (col + Logist(curInput[idx + k * info_len_i * total_grid + 0 * total_grid])) * INPUT_W / yoloWidth;
|
||||
det->bbox[1] = (row + Logist(curInput[idx + k * info_len_i * total_grid + 1 * total_grid])) * INPUT_H / yoloHeight;
|
||||
det->bbox[2] = expf(curInput[idx + k * info_len_i * total_grid + 2 * total_grid]) * anchors[2*k];
|
||||
det->bbox[3] = expf(curInput[idx + k * info_len_i * total_grid + 3 * total_grid]) * anchors[2*k + 1];
|
||||
det->bbox[0] = (col + Logist(curInput[idx + k * info_len_i * total_grid + 0 * total_grid])) * yoloStride;
|
||||
det->bbox[1] = (row + Logist(curInput[idx + k * info_len_i * total_grid + 1 * total_grid])) * yoloStride;
|
||||
det->bbox[2] = expf(curInput[idx + k * info_len_i * total_grid + 2 * total_grid]) * anchors[2 * k];
|
||||
det->bbox[3] = expf(curInput[idx + k * info_len_i * total_grid + 3 * total_grid]) * anchors[2 * k + 1];
|
||||
det->det_confidence = box_prob;
|
||||
det->class_id = class_id;
|
||||
det->class_confidence = max_cls_prob;
|
||||
@ -179,38 +191,27 @@ namespace nvinfer1
|
||||
}
|
||||
|
||||
void YoloLayerPlugin::forwardGpu(const float *const * inputs, float* output, cudaStream_t stream, int batchSize) {
|
||||
void* devAnchor;
|
||||
size_t AnchorLen = sizeof(float)* CHECK_COUNT*2;
|
||||
CUDA_CHECK(cudaMalloc(&devAnchor,AnchorLen));
|
||||
|
||||
int outputElem = 1 + MAX_OUTPUT_BBOX_COUNT * sizeof(Detection) / sizeof(float);
|
||||
|
||||
for(int idx = 0 ; idx < batchSize; ++idx) {
|
||||
CUDA_CHECK(cudaMemset(output + idx*outputElem, 0, sizeof(float)));
|
||||
CUDA_CHECK(cudaMemset(output + idx * outputElem, 0, sizeof(float)));
|
||||
}
|
||||
int numElem = 0;
|
||||
for (unsigned int i = 0;i< mYoloKernel.size();++i)
|
||||
{
|
||||
for (size_t i = 0; i < mYoloKernel.size(); ++i) {
|
||||
const auto& yolo = mYoloKernel[i];
|
||||
numElem = yolo.width*yolo.height*batchSize;
|
||||
if (numElem < mThreadCount)
|
||||
mThreadCount = numElem;
|
||||
CUDA_CHECK(cudaMemcpy(devAnchor, yolo.anchors, AnchorLen, cudaMemcpyHostToDevice));
|
||||
CalDetection<<< (yolo.width*yolo.height*batchSize + mThreadCount - 1) / mThreadCount, mThreadCount>>>
|
||||
(inputs[i],output, numElem, yolo.width, yolo.height, (float *)devAnchor, mClassCount ,outputElem);
|
||||
numElem = yolo.width * yolo.height * batchSize;
|
||||
CalDetection<<<(yolo.width * yolo.height * batchSize + mThreadCount - 1) / mThreadCount, mThreadCount>>>
|
||||
(inputs[i], output, numElem, yolo.width, yolo.height, yolo.stride, (float*)mAnchor[i], mClassCount, outputElem);
|
||||
}
|
||||
|
||||
CUDA_CHECK(cudaFree(devAnchor));
|
||||
}
|
||||
|
||||
|
||||
int YoloLayerPlugin::enqueue(int batchSize, const void*const * inputs, void** outputs, void* workspace, cudaStream_t stream)
|
||||
int YoloLayerPlugin::enqueue(const PluginTensorDesc* inputDesc, const PluginTensorDesc* outputDesc, const void* const* inputs, void* const* outputs, void* workspace, cudaStream_t stream)
|
||||
{
|
||||
//assert(batchSize == 1);
|
||||
//GPU
|
||||
//CUDA_CHECK(cudaStreamSynchronize(stream));
|
||||
int batchSize = inputDesc[0].dims.d[0];
|
||||
for (size_t i = 0; i < mYoloKernel.size(); ++i) {
|
||||
mYoloKernel[i].width = inputDesc[i].dims.d[3];
|
||||
mYoloKernel[i].height = inputDesc[i].dims.d[2];
|
||||
}
|
||||
forwardGpu((const float *const *)inputs, (float*)outputs[0], stream, batchSize);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -240,17 +241,17 @@ namespace nvinfer1
|
||||
return &mFC;
|
||||
}
|
||||
|
||||
IPluginV2IOExt* YoloPluginCreator::createPlugin(const char* name, const PluginFieldCollection* fc)
|
||||
IPluginV2DynamicExt* YoloPluginCreator::createPlugin(const char* name, const PluginFieldCollection* fc)
|
||||
{
|
||||
YoloLayerPlugin* obj = new YoloLayerPlugin();
|
||||
obj->setPluginNamespace(mNamespace.c_str());
|
||||
return obj;
|
||||
}
|
||||
|
||||
IPluginV2IOExt* YoloPluginCreator::deserializePlugin(const char* name, const void* serialData, size_t serialLength)
|
||||
IPluginV2DynamicExt* YoloPluginCreator::deserializePlugin(const char* name, const void* serialData, size_t serialLength)
|
||||
{
|
||||
// This object will be deleted when the network is destroyed, which will
|
||||
// call MishPlugin::destroy()
|
||||
// call YoloLayerPlugin::destroy()
|
||||
YoloLayerPlugin* obj = new YoloLayerPlugin(serialData, serialLength);
|
||||
obj->setPluginNamespace(mNamespace.c_str());
|
||||
return obj;
|
||||
|
||||
@ -15,29 +15,31 @@ namespace Yolo
|
||||
static constexpr float IGNORE_THRESH = 0.1f;
|
||||
static constexpr int MAX_OUTPUT_BBOX_COUNT = 1000;
|
||||
static constexpr int CLASS_NUM = 80;
|
||||
static constexpr int INPUT_H = 608;
|
||||
static constexpr int INPUT_W = 608;
|
||||
|
||||
struct YoloKernel
|
||||
{
|
||||
int width;
|
||||
int height;
|
||||
int stride;
|
||||
float anchors[CHECK_COUNT*2];
|
||||
};
|
||||
|
||||
static constexpr YoloKernel yolo1 = {
|
||||
INPUT_W / 32,
|
||||
INPUT_H / 32,
|
||||
-1, // dynamic width and height
|
||||
-1,
|
||||
32,
|
||||
{116,90, 156,198, 373,326}
|
||||
};
|
||||
static constexpr YoloKernel yolo2 = {
|
||||
INPUT_W / 16,
|
||||
INPUT_H / 16,
|
||||
-1,
|
||||
-1,
|
||||
16,
|
||||
{30,61, 62,45, 59,119}
|
||||
};
|
||||
static constexpr YoloKernel yolo3 = {
|
||||
INPUT_W / 8,
|
||||
INPUT_H / 8,
|
||||
-1,
|
||||
-1,
|
||||
8,
|
||||
{10,13, 16,30, 33,23}
|
||||
};
|
||||
|
||||
@ -51,10 +53,9 @@ namespace Yolo
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
namespace nvinfer1
|
||||
{
|
||||
class YoloLayerPlugin: public IPluginV2IOExt
|
||||
class YoloLayerPlugin: public IPluginV2DynamicExt
|
||||
{
|
||||
public:
|
||||
explicit YoloLayerPlugin();
|
||||
@ -67,21 +68,24 @@ namespace nvinfer1
|
||||
return 1;
|
||||
}
|
||||
|
||||
Dims getOutputDimensions(int index, const Dims* inputs, int nbInputDims) override;
|
||||
//virtual Dims getOutputDimensions(int index, const Dims* inputs, int nbInputDims) final;
|
||||
virtual DimsExprs getOutputDimensions(int outputIndex, const DimsExprs* inputs, int nbInputs, IExprBuilder& exprBuilder) override;
|
||||
|
||||
int initialize() override;
|
||||
|
||||
virtual void terminate() override {};
|
||||
|
||||
virtual size_t getWorkspaceSize(int maxBatchSize) const override { return 0;}
|
||||
//virtual size_t getWorkspaceSize(int maxBatchSize) const override { return 0;}
|
||||
size_t getWorkspaceSize(const PluginTensorDesc* inputs, int nbInputs, const PluginTensorDesc* outputs, int nbOutputs) const override { return 0; }
|
||||
|
||||
virtual int enqueue(int batchSize, const void*const * inputs, void** outputs, void* workspace, cudaStream_t stream) override;
|
||||
//virtual int enqueue(int batchSize, const void*const * inputs, void** outputs, void* workspace, cudaStream_t stream) override;
|
||||
int enqueue(const PluginTensorDesc* inputDesc, const PluginTensorDesc* outputDesc, const void* const* inputs, void* const* outputs, void* workspace, cudaStream_t stream) override;
|
||||
|
||||
virtual size_t getSerializationSize() const override;
|
||||
|
||||
virtual void serialize(void* buffer) const override;
|
||||
|
||||
bool supportsFormatCombination(int pos, const PluginTensorDesc* inOut, int nbInputs, int nbOutputs) const override {
|
||||
bool supportsFormatCombination(int pos, const PluginTensorDesc* inOut, int nbInputs, int nbOutputs) override {
|
||||
return inOut[pos].format == TensorFormat::kLINEAR && inOut[pos].type == DataType::kFLOAT;
|
||||
}
|
||||
|
||||
@ -91,7 +95,7 @@ namespace nvinfer1
|
||||
|
||||
void destroy() override;
|
||||
|
||||
IPluginV2IOExt* clone() const override;
|
||||
IPluginV2DynamicExt* clone() const override;
|
||||
|
||||
void setPluginNamespace(const char* pluginNamespace) override;
|
||||
|
||||
@ -99,14 +103,10 @@ namespace nvinfer1
|
||||
|
||||
DataType getOutputDataType(int index, const nvinfer1::DataType* inputTypes, int nbInputs) const override;
|
||||
|
||||
bool isOutputBroadcastAcrossBatch(int outputIndex, const bool* inputIsBroadcasted, int nbInputs) const override;
|
||||
|
||||
bool canBroadcastInputAcrossBatch(int inputIndex) const override;
|
||||
|
||||
void attachToContext(
|
||||
cudnnContext* cudnnContext, cublasContext* cublasContext, IGpuAllocator* gpuAllocator) override;
|
||||
|
||||
void configurePlugin(const PluginTensorDesc* in, int nbInput, const PluginTensorDesc* out, int nbOutput) override;
|
||||
void configurePlugin(const DynamicPluginTensorDesc* in, int nbInputs, const DynamicPluginTensorDesc* out, int nbOutputs) override;
|
||||
|
||||
void detachFromContext() override;
|
||||
|
||||
@ -116,6 +116,7 @@ namespace nvinfer1
|
||||
int mKernelCount;
|
||||
std::vector<Yolo::YoloKernel> mYoloKernel;
|
||||
int mThreadCount = 256;
|
||||
void** mAnchor;
|
||||
const char* mPluginNamespace;
|
||||
};
|
||||
|
||||
@ -132,9 +133,9 @@ namespace nvinfer1
|
||||
|
||||
const PluginFieldCollection* getFieldNames() override;
|
||||
|
||||
IPluginV2IOExt* createPlugin(const char* name, const PluginFieldCollection* fc) override;
|
||||
IPluginV2DynamicExt* createPlugin(const char* name, const PluginFieldCollection* fc) override;
|
||||
|
||||
IPluginV2IOExt* deserializePlugin(const char* name, const void* serialData, size_t serialLength) override;
|
||||
IPluginV2DynamicExt* deserializePlugin(const char* name, const void* serialData, size_t serialLength) override;
|
||||
|
||||
void setPluginNamespace(const char* libNamespace) override
|
||||
{
|
||||
|
||||
@ -22,7 +22,6 @@
|
||||
}\
|
||||
} while (0)
|
||||
|
||||
|
||||
#define USE_FP16 // comment out this if want to use FP32
|
||||
#define DEVICE 0 // GPU id
|
||||
#define NMS_THRESH 0.4
|
||||
@ -31,58 +30,46 @@
|
||||
using namespace nvinfer1;
|
||||
|
||||
// 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 OUTPUT_SIZE = 1000 * 7 + 1; // we assume the yololayer outputs no more than 1000 boxes that conf >= 0.1
|
||||
static const int MAX_INPUT_SIZE = 608;
|
||||
static const int MIN_INPUT_SIZE = 128;
|
||||
static const int OPT_INPUT_W = 608;
|
||||
static const int OPT_INPUT_H = 608;
|
||||
static const int DET_LEN = sizeof(Yolo::Detection) / sizeof(float);
|
||||
static const int OUTPUT_SIZE = Yolo::MAX_OUTPUT_BBOX_COUNT * DET_LEN + 1; // we limit the yololayer to output no more than MAX_OUTPUT_BBOX_COUNT bboxes
|
||||
const char* INPUT_BLOB_NAME = "data";
|
||||
const char* OUTPUT_BLOB_NAME = "prob";
|
||||
static Logger gLogger;
|
||||
|
||||
cv::Mat preprocess_img(cv::Mat& img) {
|
||||
int w, h, x, y;
|
||||
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::Mat re(h, w, CV_8UC3);
|
||||
cv::resize(img, re, re.size(), 0, 0, cv::INTER_CUBIC);
|
||||
cv::Mat out(INPUT_H, INPUT_W, CV_8UC3, cv::Scalar(128, 128, 128));
|
||||
re.copyTo(out(cv::Rect(x, y, re.cols, re.rows)));
|
||||
cv::Mat letterbox(cv::Mat& img) {
|
||||
float r = std::min(MAX_INPUT_SIZE / (img.cols*1.0), MAX_INPUT_SIZE / (img.rows*1.0));
|
||||
r = std::min(r, 1.0f);
|
||||
int unpad_w = r * img.cols;
|
||||
int unpad_h = r * img.rows;
|
||||
int dw = (MAX_INPUT_SIZE - unpad_w) % 32;
|
||||
int dh = (MAX_INPUT_SIZE - unpad_h) % 32;
|
||||
cv::Mat re(unpad_h, unpad_w, CV_8UC3);
|
||||
cv::resize(img, re, re.size());
|
||||
cv::Mat out(unpad_h + dh, unpad_w + dw, CV_8UC3, cv::Scalar(128, 128, 128));
|
||||
re.copyTo(out(cv::Rect(dw / 2, dh / 2, re.cols, re.rows)));
|
||||
return out;
|
||||
}
|
||||
|
||||
cv::Rect get_rect(cv::Mat& img, float bbox[4]) {
|
||||
int l, r, t, b;
|
||||
float r_w = INPUT_W / (img.cols * 1.0);
|
||||
float r_h = INPUT_H / (img.rows * 1.0);
|
||||
if (r_h > r_w) {
|
||||
l = bbox[0] - bbox[2]/2.f;
|
||||
r = bbox[0] + bbox[2]/2.f;
|
||||
t = bbox[1] - bbox[3]/2.f - (INPUT_H - r_w * img.rows) / 2;
|
||||
b = bbox[1] + bbox[3]/2.f - (INPUT_H - r_w * img.rows) / 2;
|
||||
l = l / r_w;
|
||||
r = r / r_w;
|
||||
t = t / r_w;
|
||||
b = b / r_w;
|
||||
} else {
|
||||
l = bbox[0] - bbox[2]/2.f - (INPUT_W - r_h * img.cols) / 2;
|
||||
r = bbox[0] + bbox[2]/2.f - (INPUT_W - r_h * img.cols) / 2;
|
||||
t = bbox[1] - bbox[3]/2.f;
|
||||
b = bbox[1] + bbox[3]/2.f;
|
||||
l = l / r_h;
|
||||
r = r / r_h;
|
||||
t = t / r_h;
|
||||
b = b / r_h;
|
||||
}
|
||||
cv::Rect get_rect(cv::Size src_shape, cv::Size pre_shape, float bbox[4]) {
|
||||
float ra = std::min(MAX_INPUT_SIZE / (src_shape.width * 1.0), MAX_INPUT_SIZE / (src_shape.height * 1.0));
|
||||
ra = std::min(ra, 1.0f);
|
||||
int unpad_w = ra * src_shape.width;
|
||||
int unpad_h = ra * src_shape.height;
|
||||
int dw = (MAX_INPUT_SIZE - unpad_w) % 32;
|
||||
int dh = (MAX_INPUT_SIZE - unpad_h) % 32;
|
||||
|
||||
int l = bbox[0] - bbox[2]/2.f - dw / 2;
|
||||
int r = bbox[0] + bbox[2]/2.f - dw / 2;
|
||||
int t = bbox[1] - bbox[3]/2.f - dh / 2;
|
||||
int b = bbox[1] + bbox[3]/2.f - dh / 2;
|
||||
l /= ra;
|
||||
r /= ra;
|
||||
t /= ra;
|
||||
b /= ra;
|
||||
return cv::Rect(l, t, r-l, b-t);
|
||||
}
|
||||
|
||||
@ -107,10 +94,10 @@ bool cmp(const Yolo::Detection& a, const Yolo::Detection& b) {
|
||||
|
||||
void nms(std::vector<Yolo::Detection>& res, float *output, float nms_thresh = NMS_THRESH) {
|
||||
std::map<float, std::vector<Yolo::Detection>> m;
|
||||
for (int i = 0; i < output[0] && i < 1000; i++) {
|
||||
if (output[1 + 7 * i + 4] <= BBOX_CONF_THRESH) continue;
|
||||
for (int i = 0; i < output[0] && i < Yolo::MAX_OUTPUT_BBOX_COUNT; i++) {
|
||||
if (output[1 + DET_LEN * i + 4] <= BBOX_CONF_THRESH) continue;
|
||||
Yolo::Detection det;
|
||||
memcpy(&det, &output[1 + 7 * i], 7 * sizeof(float));
|
||||
memcpy(&det, &output[1 + DET_LEN * i], DET_LEN * sizeof(float));
|
||||
if (m.count(det.class_id) == 0) m.emplace(det.class_id, std::vector<Yolo::Detection>());
|
||||
m[det.class_id].push_back(det);
|
||||
}
|
||||
@ -221,10 +208,10 @@ ILayer* convBnLeaky(INetworkDefinition *network, std::map<std::string, Weights>&
|
||||
|
||||
// Creat the engine using only the API and not any parser.
|
||||
ICudaEngine* createEngine(unsigned int maxBatchSize, IBuilder* builder, IBuilderConfig* config, DataType dt) {
|
||||
INetworkDefinition* network = builder->createNetworkV2(0U);
|
||||
const auto explicitBatch = 1U << static_cast<uint32_t>(NetworkDefinitionCreationFlag::kEXPLICIT_BATCH);
|
||||
auto network = builder->createNetworkV2(explicitBatch);
|
||||
|
||||
// Create input tensor of shape {3, INPUT_H, INPUT_W} with name INPUT_BLOB_NAME
|
||||
ITensor* data = network->addInput(INPUT_BLOB_NAME, dt, Dims3{3, INPUT_H, INPUT_W});
|
||||
ITensor* data = network->addInput(INPUT_BLOB_NAME, dt, Dims4{1, 3, -1, -1});
|
||||
assert(data);
|
||||
|
||||
std::map<std::string, Weights> weightMap = loadWeights("../yolov3-spp_ultralytics68.wts");
|
||||
@ -309,7 +296,7 @@ ICudaEngine* createEngine(unsigned int maxBatchSize, IBuilder* builder, IBuilder
|
||||
auto lr75 = convBnLeaky(network, weightMap, *ew74->getOutput(0), 512, 1, 1, 0, 75);
|
||||
auto lr76 = convBnLeaky(network, weightMap, *lr75->getOutput(0), 1024, 3, 1, 1, 76);
|
||||
auto lr77 = convBnLeaky(network, weightMap, *lr76->getOutput(0), 512, 1, 1, 0, 77);
|
||||
|
||||
|
||||
auto pool78 = network->addPoolingNd(*lr77->getOutput(0), PoolingType::kMAX, DimsHW{5,5});
|
||||
pool78->setPaddingNd(DimsHW{2, 2});
|
||||
pool78->setStrideNd(DimsHW{1, 1});
|
||||
@ -341,7 +328,7 @@ ICudaEngine* createEngine(unsigned int maxBatchSize, IBuilder* builder, IBuilder
|
||||
deconv92->setStrideNd(DimsHW{2, 2});
|
||||
deconv92->setNbGroups(256);
|
||||
weightMap["deconv92"] = deconvwts92;
|
||||
|
||||
|
||||
ITensor* inputTensors[] = {deconv92->getOutput(0), ew61->getOutput(0)};
|
||||
auto cat93 = network->addConcatenation(inputTensors, 2);
|
||||
auto lr94 = convBnLeaky(network, weightMap, *cat93->getOutput(0), 256, 1, 1, 0, 94);
|
||||
@ -375,11 +362,22 @@ ICudaEngine* createEngine(unsigned int maxBatchSize, IBuilder* builder, IBuilder
|
||||
ITensor* inputTensors_yolo[] = {conv88->getOutput(0), conv100->getOutput(0), conv112->getOutput(0)};
|
||||
auto yolo = network->addPluginV2(inputTensors_yolo, 3, *pluginObj);
|
||||
|
||||
auto dim = yolo->getOutput(0)->getDimensions();
|
||||
std::cout << "yololayer output shape: ";
|
||||
for (int i = 0; i < dim.nbDims; i++) {
|
||||
std::cout << dim.d[i] << " ";
|
||||
}
|
||||
std::cout << std::endl;
|
||||
yolo->getOutput(0)->setName(OUTPUT_BLOB_NAME);
|
||||
network->markOutput(*yolo->getOutput(0));
|
||||
|
||||
IOptimizationProfile* profile = builder->createOptimizationProfile();
|
||||
profile->setDimensions(INPUT_BLOB_NAME, OptProfileSelector::kMIN, Dims4(1, 3, MIN_INPUT_SIZE, MIN_INPUT_SIZE));
|
||||
profile->setDimensions(INPUT_BLOB_NAME, OptProfileSelector::kOPT, Dims4(1, 3, OPT_INPUT_H, OPT_INPUT_W));
|
||||
profile->setDimensions(INPUT_BLOB_NAME, OptProfileSelector::kMAX, Dims4(1, 3, MAX_INPUT_SIZE, MAX_INPUT_SIZE));
|
||||
config->addOptimizationProfile(profile);
|
||||
|
||||
// Build engine
|
||||
builder->setMaxBatchSize(maxBatchSize);
|
||||
config->setMaxWorkspaceSize(16 * (1 << 20)); // 16MB
|
||||
#ifdef USE_FP16
|
||||
config->setFlag(BuilderFlag::kFP16);
|
||||
@ -417,7 +415,7 @@ void APIToModel(unsigned int maxBatchSize, IHostMemory** modelStream) {
|
||||
builder->destroy();
|
||||
}
|
||||
|
||||
void doInference(IExecutionContext& context, float* input, float* output, int batchSize) {
|
||||
void doInference(IExecutionContext& context, float* input, float* output, cv::Size input_shape) {
|
||||
const ICudaEngine& engine = context.getEngine();
|
||||
|
||||
// Pointers to input and output device buffers to pass to engine.
|
||||
@ -429,19 +427,20 @@ void doInference(IExecutionContext& context, float* input, float* output, int ba
|
||||
// Note that indices are guaranteed to be less than IEngine::getNbBindings()
|
||||
const int inputIndex = engine.getBindingIndex(INPUT_BLOB_NAME);
|
||||
const int outputIndex = engine.getBindingIndex(OUTPUT_BLOB_NAME);
|
||||
context.setBindingDimensions(inputIndex, Dims4(1, 3, input_shape.height, input_shape.width));
|
||||
|
||||
// Create GPU buffers on device
|
||||
CHECK(cudaMalloc(&buffers[inputIndex], batchSize * 3 * INPUT_H * INPUT_W * sizeof(float)));
|
||||
CHECK(cudaMalloc(&buffers[outputIndex], batchSize * OUTPUT_SIZE * sizeof(float)));
|
||||
CHECK(cudaMalloc(&buffers[inputIndex], 3 * input_shape.height * input_shape.width * sizeof(float)));
|
||||
CHECK(cudaMalloc(&buffers[outputIndex], OUTPUT_SIZE * sizeof(float)));
|
||||
|
||||
// Create stream
|
||||
cudaStream_t stream;
|
||||
CHECK(cudaStreamCreate(&stream));
|
||||
|
||||
// DMA input batch data to device, infer on the batch asynchronously, and DMA output back to host
|
||||
CHECK(cudaMemcpyAsync(buffers[inputIndex], input, batchSize * 3 * INPUT_H * INPUT_W * sizeof(float), cudaMemcpyHostToDevice, stream));
|
||||
context.enqueue(batchSize, buffers, stream, nullptr);
|
||||
CHECK(cudaMemcpyAsync(output, buffers[outputIndex], batchSize * OUTPUT_SIZE * sizeof(float), cudaMemcpyDeviceToHost, stream));
|
||||
CHECK(cudaMemcpyAsync(buffers[inputIndex], input, 3 * input_shape.height * input_shape.width * sizeof(float), cudaMemcpyHostToDevice, stream));
|
||||
context.enqueueV2(buffers, stream, nullptr);
|
||||
CHECK(cudaMemcpyAsync(output, buffers[outputIndex], OUTPUT_SIZE * sizeof(float), cudaMemcpyDeviceToHost, stream));
|
||||
cudaStreamSynchronize(stream);
|
||||
|
||||
// Release stream and buffers
|
||||
@ -514,10 +513,6 @@ int main(int argc, char** argv) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
// prepare input data ---------------------------
|
||||
static float data[3 * INPUT_H * INPUT_W];
|
||||
//for (int i = 0; i < 3 * INPUT_H * INPUT_W; i++)
|
||||
// data[i] = 1.0;
|
||||
static float prob[OUTPUT_SIZE];
|
||||
IRuntime* runtime = createInferRuntime(gLogger);
|
||||
assert(runtime != nullptr);
|
||||
@ -526,6 +521,7 @@ int main(int argc, char** argv) {
|
||||
IExecutionContext* context = engine->createExecutionContext();
|
||||
assert(context != nullptr);
|
||||
delete[] trtModelStream;
|
||||
context->setOptimizationProfile(0);
|
||||
|
||||
int fcount = 0;
|
||||
for (auto f: file_names) {
|
||||
@ -533,31 +529,21 @@ int main(int argc, char** argv) {
|
||||
std::cout << fcount << " " << f << std::endl;
|
||||
cv::Mat img = cv::imread(std::string(argv[2]) + "/" + f);
|
||||
if (img.empty()) continue;
|
||||
cv::Mat pr_img = preprocess_img(img);
|
||||
for (int i = 0; i < INPUT_H * INPUT_W; i++) {
|
||||
data[i] = pr_img.at<cv::Vec3b>(i)[2] / 255.0;
|
||||
data[i + INPUT_H * INPUT_W] = pr_img.at<cv::Vec3b>(i)[1] / 255.0;
|
||||
data[i + 2 * INPUT_H * INPUT_W] = pr_img.at<cv::Vec3b>(i)[0] / 255.0;
|
||||
}
|
||||
cv::Mat pr_img = letterbox(img);
|
||||
std::cout << "letterbox shape: " << pr_img.cols << ", " << pr_img.rows << std::endl;
|
||||
if (pr_img.cols < MIN_INPUT_SIZE || pr_img.rows < MIN_INPUT_SIZE) continue;
|
||||
cv::Mat blob = cv::dnn::blobFromImage(pr_img, 1.0 / 255.0, pr_img.size(), cv::Scalar(0, 0, 0), true, false);
|
||||
|
||||
// Run inference
|
||||
auto start = std::chrono::system_clock::now();
|
||||
doInference(*context, data, prob, 1);
|
||||
doInference(*context, blob.ptr<float>(0), prob, pr_img.size());
|
||||
auto end = std::chrono::system_clock::now();
|
||||
std::cout << std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count() << "ms" << std::endl;
|
||||
std::vector<Yolo::Detection> res;
|
||||
nms(res, prob);
|
||||
for (int i=0; i<20; i++) {
|
||||
std::cout << prob[i] << ",";
|
||||
}
|
||||
std::cout << res.size() << std::endl;
|
||||
std::cout << "num of bbox: " << res.size() << std::endl;
|
||||
for (size_t j = 0; j < res.size(); j++) {
|
||||
float *p = (float*)&res[j];
|
||||
for (size_t k = 0; k < 7; k++) {
|
||||
std::cout << p[k] << ", ";
|
||||
}
|
||||
std::cout << std::endl;
|
||||
cv::Rect r = get_rect(img, res[j].bbox);
|
||||
cv::Rect r = get_rect(img.size(), pr_img.size(), res[j].bbox);
|
||||
cv::rectangle(img, r, cv::Scalar(0x27, 0xC1, 0x36), 2);
|
||||
cv::putText(img, std::to_string((int)res[j].class_id), cv::Point(r.x, r.y - 1), cv::FONT_HERSHEY_PLAIN, 1.2, cv::Scalar(0xFF, 0xFF, 0xFF), 2);
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user