Files
nvidia--tensorrt/plugin/efficientNMSPlugin/efficientNMSInference.cu
T
wehub-resource-sync c8a779b1bb
Docker Image CI / build-ubuntu2004 (push) Waiting to run
chore: import upstream snapshot with attribution
2026-07-13 13:36:55 +08:00

726 lines
30 KiB
Plaintext

/*
* SPDX-FileCopyrightText: Copyright (c) 1993-2024 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
* SPDX-License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "common/bboxUtils.h"
#include "cub/cub.cuh"
#include "cuda_runtime_api.h"
#include "efficientNMSInference.cuh"
#include "efficientNMSInference.h"
#define NMS_TILES 5
using namespace nvinfer1;
using namespace nvinfer1::plugin;
template <typename T>
__device__ float IOU(EfficientNMSParameters param, BoxCorner<T> box1, BoxCorner<T> box2)
{
// Regardless of the selected box coding, IOU is always performed in BoxCorner coding.
// The boxes are copied so that they can be reordered without affecting the originals.
BoxCorner<T> b1 = box1;
BoxCorner<T> b2 = box2;
b1.reorder();
b2.reorder();
float intersectArea = BoxCorner<T>::intersect(b1, b2).area();
if (intersectArea <= 0.f)
{
return 0.f;
}
float unionArea = b1.area() + b2.area() - intersectArea;
if (unionArea <= 0.f)
{
return 0.f;
}
return intersectArea / unionArea;
}
template <typename T, typename Tb>
__device__ BoxCorner<T> DecodeBoxes(EfficientNMSParameters param, int boxIdx, int anchorIdx,
const Tb* __restrict__ boxesInput, const Tb* __restrict__ anchorsInput)
{
// The inputs will be in the selected coding format, as well as the decoding function. But the decoded box
// will always be returned as BoxCorner.
Tb box = boxesInput[boxIdx];
if (!param.boxDecoder)
{
return BoxCorner<T>(box);
}
Tb anchor = anchorsInput[anchorIdx];
box.reorder();
anchor.reorder();
return BoxCorner<T>(box.decode(anchor));
}
template <typename T, typename Tb>
__device__ void MapNMSData(EfficientNMSParameters param, int idx, int imageIdx, const Tb* __restrict__ boxesInput,
const Tb* __restrict__ anchorsInput, const int* __restrict__ topClassData, const int* __restrict__ topAnchorsData,
const int* __restrict__ topNumData, const T* __restrict__ sortedScoresData, const int* __restrict__ sortedIndexData,
T& scoreMap, int& classMap, BoxCorner<T>& boxMap, int& boxIdxMap)
{
// idx: Holds the NMS box index, within the current batch.
// idxSort: Holds the batched NMS box index, which indexes the (filtered, but sorted) score buffer.
// scoreMap: Holds the score that corresponds to the indexed box being processed by NMS.
if (idx >= topNumData[imageIdx])
{
return;
}
int idxSort = imageIdx * param.numScoreElements + idx;
scoreMap = sortedScoresData[idxSort];
// idxMap: Holds the re-mapped index, which indexes the (filtered, but unsorted) buffers.
// classMap: Holds the class that corresponds to the idx'th sorted score being processed by NMS.
// anchorMap: Holds the anchor that corresponds to the idx'th sorted score being processed by NMS.
int idxMap = imageIdx * param.numScoreElements + sortedIndexData[idxSort];
classMap = topClassData[idxMap];
int anchorMap = topAnchorsData[idxMap];
// boxIdxMap: Holds the re-re-mapped index, which indexes the (unfiltered, and unsorted) boxes input buffer.
boxIdxMap = -1;
if (param.shareLocation) // Shape of boxesInput: [batchSize, numAnchors, 1, 4]
{
boxIdxMap = imageIdx * param.numAnchors + anchorMap;
}
else // Shape of boxesInput: [batchSize, numAnchors, numClasses, 4]
{
int batchOffset = imageIdx * param.numAnchors * param.numClasses;
int anchorOffset = anchorMap * param.numClasses;
boxIdxMap = batchOffset + anchorOffset + classMap;
}
// anchorIdxMap: Holds the re-re-mapped index, which indexes the (unfiltered, and unsorted) anchors input buffer.
int anchorIdxMap = -1;
if (param.shareAnchors) // Shape of anchorsInput: [1, numAnchors, 4]
{
anchorIdxMap = anchorMap;
}
else // Shape of anchorsInput: [batchSize, numAnchors, 4]
{
anchorIdxMap = imageIdx * param.numAnchors + anchorMap;
}
// boxMap: Holds the box that corresponds to the idx'th sorted score being processed by NMS.
boxMap = DecodeBoxes<T, Tb>(param, boxIdxMap, anchorIdxMap, boxesInput, anchorsInput);
}
template <typename T>
__device__ void WriteNMSResult(EfficientNMSParameters param, int* __restrict__ numDetectionsOutput,
T* __restrict__ nmsScoresOutput, int* __restrict__ nmsClassesOutput, BoxCorner<T>* __restrict__ nmsBoxesOutput,
T threadScore, int threadClass, BoxCorner<T> threadBox, int imageIdx, unsigned int resultsCounter)
{
int outputIdx = imageIdx * param.numOutputBoxes + resultsCounter - 1;
if (param.scoreSigmoid)
{
nmsScoresOutput[outputIdx] = sigmoid_mp(threadScore);
}
else if (param.scoreBits > 0)
{
nmsScoresOutput[outputIdx] = add_mp(threadScore, (T) -1);
}
else
{
nmsScoresOutput[outputIdx] = threadScore;
}
nmsClassesOutput[outputIdx] = threadClass;
if (param.clipBoxes)
{
nmsBoxesOutput[outputIdx] = threadBox.clip((T) 0, (T) 1);
}
else
{
nmsBoxesOutput[outputIdx] = threadBox;
}
numDetectionsOutput[imageIdx] = resultsCounter;
}
__device__ void WriteONNXResult(EfficientNMSParameters param, int* outputIndexData, int* __restrict__ nmsIndicesOutput,
int imageIdx, int threadClass, int boxIdxMap)
{
int index = boxIdxMap % param.numAnchors;
int idx = atomicAdd((unsigned int*) &outputIndexData[0], 1);
nmsIndicesOutput[idx * 3 + 0] = imageIdx;
nmsIndicesOutput[idx * 3 + 1] = threadClass;
nmsIndicesOutput[idx * 3 + 2] = index;
}
__global__ void PadONNXResult(EfficientNMSParameters param, int* outputIndexData, int* __restrict__ nmsIndicesOutput)
{
if (threadIdx.x > 0)
{
return;
}
int pidx = outputIndexData[0] - 1;
if (pidx < 0)
{
return;
}
for (int idx = pidx + 1; idx < param.batchSize * param.numOutputBoxes; idx++)
{
nmsIndicesOutput[idx * 3 + 0] = nmsIndicesOutput[pidx * 3 + 0];
nmsIndicesOutput[idx * 3 + 1] = nmsIndicesOutput[pidx * 3 + 1];
nmsIndicesOutput[idx * 3 + 2] = nmsIndicesOutput[pidx * 3 + 2];
}
}
template <typename T, typename Tb>
__global__ void EfficientNMS(EfficientNMSParameters param, const int* topNumData, int* outputIndexData,
int* outputClassData, const int* sortedIndexData, const T* __restrict__ sortedScoresData,
const int* __restrict__ topClassData, const int* __restrict__ topAnchorsData, const Tb* __restrict__ boxesInput,
const Tb* __restrict__ anchorsInput, int* __restrict__ numDetectionsOutput, T* __restrict__ nmsScoresOutput,
int* __restrict__ nmsClassesOutput, int* __restrict__ nmsIndicesOutput, BoxCorner<T>* __restrict__ nmsBoxesOutput)
{
unsigned int thread = threadIdx.x;
unsigned int imageIdx = blockIdx.y;
unsigned int tileSize = blockDim.x;
if (imageIdx >= param.batchSize)
{
return;
}
int numSelectedBoxes = min(topNumData[imageIdx], param.numSelectedBoxes);
int numTiles = (numSelectedBoxes + tileSize - 1) / tileSize;
if (thread >= numSelectedBoxes)
{
return;
}
__shared__ int blockState;
__shared__ unsigned int resultsCounter;
if (thread == 0)
{
blockState = 0;
resultsCounter = 0;
}
int threadState[NMS_TILES];
unsigned int boxIdx[NMS_TILES];
T threadScore[NMS_TILES];
int threadClass[NMS_TILES];
BoxCorner<T> threadBox[NMS_TILES];
int boxIdxMap[NMS_TILES];
for (int tile = 0; tile < numTiles; tile++)
{
threadState[tile] = 0;
boxIdx[tile] = thread + tile * blockDim.x;
MapNMSData<T, Tb>(param, boxIdx[tile], imageIdx, boxesInput, anchorsInput, topClassData, topAnchorsData,
topNumData, sortedScoresData, sortedIndexData, threadScore[tile], threadClass[tile], threadBox[tile],
boxIdxMap[tile]);
}
// Iterate through all boxes to NMS against.
for (int i = 0; i < numSelectedBoxes; i++)
{
int tile = i / tileSize;
if (boxIdx[tile] == i)
{
// Iteration lead thread, figure out what the other threads should do,
// this will be signaled via the blockState shared variable.
if (threadState[tile] == -1)
{
// Thread already dead, this box was already dropped in a previous iteration,
// because it had a large IOU overlap with another lead thread previously, so
// it would never be kept anyway, therefore it can safely be skip all IOU operations
// in this iteration.
blockState = -1; // -1 => Signal all threads to skip iteration
}
else if (threadState[tile] == 0)
{
// As this box will be kept, this is a good place to find what index in the results buffer it
// should have, as this allows to perform an early loop exit if there are enough results.
if (resultsCounter >= param.numOutputBoxes)
{
blockState = -2; // -2 => Signal all threads to do an early loop exit.
}
else
{
// Thread is still alive, because it has not had a large enough IOU overlap with
// any other kept box previously. Therefore, this box will be kept for sure. However,
// we need to check against all other subsequent boxes from this position onward,
// to see how those other boxes will behave in future iterations.
blockState = 1; // +1 => Signal all (higher index) threads to calculate IOU against this box
threadState[tile] = 1; // +1 => Mark this box's thread to be kept and written out to results
// If the numOutputBoxesPerClass check is enabled, write the result only if the limit for this
// class on this image has not been reached yet. Other than (possibly) skipping the write, this
// won't affect anything else in the NMS threading.
bool write = true;
if (param.numOutputBoxesPerClass >= 0)
{
int classCounterIdx = imageIdx * param.numClasses + threadClass[tile];
write = (outputClassData[classCounterIdx] < param.numOutputBoxesPerClass);
outputClassData[classCounterIdx]++;
}
if (write)
{
// This branch is visited by one thread per iteration, so it's safe to do non-atomic increments.
resultsCounter++;
if (param.outputONNXIndices)
{
WriteONNXResult(
param, outputIndexData, nmsIndicesOutput, imageIdx, threadClass[tile], boxIdxMap[tile]);
}
else
{
WriteNMSResult<T>(param, numDetectionsOutput, nmsScoresOutput, nmsClassesOutput,
nmsBoxesOutput, threadScore[tile], threadClass[tile], threadBox[tile], imageIdx,
resultsCounter);
}
}
}
}
else
{
// This state should never be reached, but just in case...
blockState = 0; // 0 => Signal all threads to not do any updates, nothing happens.
}
}
__syncthreads();
if (blockState == -2)
{
// This is the signal to exit from the loop.
return;
}
if (blockState == -1)
{
// This is the signal for all threads to just skip this iteration, as no IOU's need to be checked.
continue;
}
// Grab a box and class to test the current box against. The test box corresponds to iteration i,
// therefore it will have a lower index than the current thread box, and will therefore have a higher score
// than the current box because it's located "before" in the sorted score list.
T testScore;
int testClass;
BoxCorner<T> testBox;
int testBoxIdxMap;
MapNMSData<T, Tb>(param, i, imageIdx, boxesInput, anchorsInput, topClassData, topAnchorsData, topNumData,
sortedScoresData, sortedIndexData, testScore, testClass, testBox, testBoxIdxMap);
for (int tile = 0; tile < numTiles; tile++)
{
bool ignoreClass = true;
if (!param.classAgnostic)
{
ignoreClass = threadClass[tile] == testClass;
}
// IOU
if (boxIdx[tile] > i && // Make sure two different boxes are being tested, and that it's a higher index;
boxIdx[tile] < numSelectedBoxes && // Make sure the box is within numSelectedBoxes;
blockState == 1 && // Signal that allows IOU checks to be performed;
threadState[tile] == 0 && // Make sure this box hasn't been either dropped or kept already;
ignoreClass && // Compare only boxes of matching classes when classAgnostic is false;
lte_mp(threadScore[tile], testScore) && // Make sure the sorting order of scores is as expected;
IOU<T>(param, threadBox[tile], testBox) >= param.iouThreshold) // And... IOU overlap.
{
// Current box overlaps with the box tested in this iteration, this box will be skipped.
threadState[tile] = -1; // -1 => Mark this box's thread to be dropped.
}
}
}
}
template <typename T>
cudaError_t EfficientNMSLauncher(EfficientNMSParameters& param, int* topNumData, int* outputIndexData,
int* outputClassData, int* sortedIndexData, T* sortedScoresData, int* topClassData, int* topAnchorsData,
const void* boxesInput, const void* anchorsInput, int* numDetectionsOutput, T* nmsScoresOutput,
int* nmsClassesOutput, int* nmsIndicesOutput, void* nmsBoxesOutput, cudaStream_t stream)
{
unsigned int tileSize = param.numSelectedBoxes / NMS_TILES;
if (param.numSelectedBoxes <= 512)
{
tileSize = 512;
}
if (param.numSelectedBoxes <= 256)
{
tileSize = 256;
}
const dim3 blockSize = {tileSize, 1, 1};
const dim3 gridSize = {1, (unsigned int) param.batchSize, 1};
if (param.boxCoding == 0)
{
EfficientNMS<T, BoxCorner<T>><<<gridSize, blockSize, 0, stream>>>(param, topNumData, outputIndexData,
outputClassData, sortedIndexData, sortedScoresData, topClassData, topAnchorsData,
(BoxCorner<T>*) boxesInput, (BoxCorner<T>*) anchorsInput, numDetectionsOutput, nmsScoresOutput,
nmsClassesOutput, nmsIndicesOutput, (BoxCorner<T>*) nmsBoxesOutput);
}
else if (param.boxCoding == 1)
{
// Note that nmsBoxesOutput is always coded as BoxCorner<T>, regardless of the input coding type.
EfficientNMS<T, BoxCenterSize<T>><<<gridSize, blockSize, 0, stream>>>(param, topNumData, outputIndexData,
outputClassData, sortedIndexData, sortedScoresData, topClassData, topAnchorsData,
(BoxCenterSize<T>*) boxesInput, (BoxCenterSize<T>*) anchorsInput, numDetectionsOutput, nmsScoresOutput,
nmsClassesOutput, nmsIndicesOutput, (BoxCorner<T>*) nmsBoxesOutput);
}
if (param.outputONNXIndices)
{
PadONNXResult<<<1, 1, 0, stream>>>(param, outputIndexData, nmsIndicesOutput);
}
return cudaGetLastError();
}
__global__ void EfficientNMSFilterSegments(EfficientNMSParameters param, const int* __restrict__ topNumData,
int* __restrict__ topOffsetsStartData, int* __restrict__ topOffsetsEndData)
{
int imageIdx = threadIdx.x;
if (imageIdx > param.batchSize)
{
return;
}
topOffsetsStartData[imageIdx] = imageIdx * param.numScoreElements;
topOffsetsEndData[imageIdx] = imageIdx * param.numScoreElements + topNumData[imageIdx];
}
template <typename T>
__global__ void EfficientNMSFilter(EfficientNMSParameters param, const T* __restrict__ scoresInput,
int* __restrict__ topNumData, int* __restrict__ topIndexData, int* __restrict__ topAnchorsData,
T* __restrict__ topScoresData, int* __restrict__ topClassData)
{
int elementIdx = blockDim.x * blockIdx.x + threadIdx.x;
int imageIdx = blockDim.y * blockIdx.y + threadIdx.y;
// Boundary Conditions
if (elementIdx >= param.numScoreElements || imageIdx >= param.batchSize)
{
return;
}
// Shape of scoresInput: [batchSize, numAnchors, numClasses]
int scoresInputIdx = imageIdx * param.numScoreElements + elementIdx;
// For each class, check its corresponding score if it crosses the threshold, and if so select this anchor,
// and keep track of the maximum score and the corresponding (argmax) class id
T score = scoresInput[scoresInputIdx];
if (gte_mp(score, (T) param.scoreThreshold))
{
// Unpack the class and anchor index from the element index
int classIdx = elementIdx % param.numClasses;
int anchorIdx = elementIdx / param.numClasses;
// If this is a background class, ignore it.
if (classIdx == param.backgroundClass)
{
return;
}
// Use an atomic to find an open slot where to write the selected anchor data.
if (topNumData[imageIdx] >= param.numScoreElements)
{
return;
}
int selectedIdx = atomicAdd((unsigned int*) &topNumData[imageIdx], 1);
if (selectedIdx >= param.numScoreElements)
{
topNumData[imageIdx] = param.numScoreElements;
return;
}
// Shape of topScoresData / topClassData: [batchSize, numScoreElements]
int topIdx = imageIdx * param.numScoreElements + selectedIdx;
if (param.scoreBits > 0)
{
score = add_mp(score, (T) 1);
if (gt_mp(score, (T) (2.f - 1.f / 1024.f)))
{
// Ensure the incremented score fits in the mantissa without changing the exponent
score = (2.f - 1.f / 1024.f);
}
}
topIndexData[topIdx] = selectedIdx;
topAnchorsData[topIdx] = anchorIdx;
topScoresData[topIdx] = score;
topClassData[topIdx] = classIdx;
}
}
template <typename T>
__global__ void EfficientNMSDenseIndex(EfficientNMSParameters param, int* __restrict__ topNumData,
int* __restrict__ topIndexData, int* __restrict__ topAnchorsData, int* __restrict__ topOffsetsStartData,
int* __restrict__ topOffsetsEndData, T* __restrict__ topScoresData, int* __restrict__ topClassData)
{
int elementIdx = blockDim.x * blockIdx.x + threadIdx.x;
int imageIdx = blockDim.y * blockIdx.y + threadIdx.y;
if (elementIdx >= param.numScoreElements || imageIdx >= param.batchSize)
{
return;
}
int dataIdx = imageIdx * param.numScoreElements + elementIdx;
int anchorIdx = elementIdx / param.numClasses;
int classIdx = elementIdx % param.numClasses;
if (param.scoreBits > 0)
{
T score = topScoresData[dataIdx];
if (lt_mp(score, (T) param.scoreThreshold))
{
score = (T) 1;
}
else if (classIdx == param.backgroundClass)
{
score = (T) 1;
}
else
{
score = add_mp(score, (T) 1);
if (gt_mp(score, (T) (2.f - 1.f / 1024.f)))
{
// Ensure the incremented score fits in the mantissa without changing the exponent
score = (2.f - 1.f / 1024.f);
}
}
topScoresData[dataIdx] = score;
}
else
{
T score = topScoresData[dataIdx];
if (lt_mp(score, (T) param.scoreThreshold))
{
topScoresData[dataIdx] = -(1 << 15);
}
else if (classIdx == param.backgroundClass)
{
topScoresData[dataIdx] = -(1 << 15);
}
}
topIndexData[dataIdx] = elementIdx;
topAnchorsData[dataIdx] = anchorIdx;
topClassData[dataIdx] = classIdx;
if (elementIdx == 0)
{
// Saturate counters
topNumData[imageIdx] = param.numScoreElements;
topOffsetsStartData[imageIdx] = imageIdx * param.numScoreElements;
topOffsetsEndData[imageIdx] = (imageIdx + 1) * param.numScoreElements;
}
}
template <typename T>
cudaError_t EfficientNMSFilterLauncher(EfficientNMSParameters& param, const T* scoresInput, int* topNumData,
int* topIndexData, int* topAnchorsData, int* topOffsetsStartData, int* topOffsetsEndData, T* topScoresData,
int* topClassData, cudaStream_t stream)
{
const unsigned int elementsPerBlock = 512;
const unsigned int imagesPerBlock = 1;
const unsigned int elementBlocks = (param.numScoreElements + elementsPerBlock - 1) / elementsPerBlock;
const unsigned int imageBlocks = (param.batchSize + imagesPerBlock - 1) / imagesPerBlock;
const dim3 blockSize = {elementsPerBlock, imagesPerBlock, 1};
const dim3 gridSize = {elementBlocks, imageBlocks, 1};
float kernelSelectThreshold = 0.007f;
if (param.scoreSigmoid)
{
// Inverse Sigmoid
if (param.scoreThreshold <= 0.f)
{
param.scoreThreshold = -(1 << 15);
}
else
{
param.scoreThreshold = logf(param.scoreThreshold / (1.f - param.scoreThreshold));
}
kernelSelectThreshold = logf(kernelSelectThreshold / (1.f - kernelSelectThreshold));
// Disable Score Bits Optimization
param.scoreBits = -1;
}
if (param.scoreThreshold < kernelSelectThreshold)
{
// A full copy of the buffer is necessary because sorting will scramble the input data otherwise.
PLUGIN_CHECK_CUDA(cudaMemcpyAsync(topScoresData, scoresInput,
param.batchSize * param.numScoreElements * sizeof(T), cudaMemcpyDeviceToDevice, stream));
EfficientNMSDenseIndex<T><<<gridSize, blockSize, 0, stream>>>(param, topNumData, topIndexData, topAnchorsData,
topOffsetsStartData, topOffsetsEndData, topScoresData, topClassData);
}
else
{
EfficientNMSFilter<T><<<gridSize, blockSize, 0, stream>>>(
param, scoresInput, topNumData, topIndexData, topAnchorsData, topScoresData, topClassData);
EfficientNMSFilterSegments<<<1, param.batchSize, 0, stream>>>(
param, topNumData, topOffsetsStartData, topOffsetsEndData);
}
return cudaGetLastError();
}
template <typename T>
size_t EfficientNMSSortWorkspaceSize(int batchSize, int numScoreElements)
{
size_t sortedWorkspaceSize = 0;
cub::DoubleBuffer<T> keysDB(nullptr, nullptr);
cub::DoubleBuffer<int> valuesDB(nullptr, nullptr);
cub::DeviceSegmentedRadixSort::SortPairsDescending(nullptr, sortedWorkspaceSize, keysDB, valuesDB,
numScoreElements, batchSize, (const int*) nullptr, (const int*) nullptr);
return sortedWorkspaceSize;
}
size_t EfficientNMSWorkspaceSize(int batchSize, int numScoreElements, int numClasses, DataType datatype)
{
size_t total = 0;
const size_t align = 256;
// Counters
// 3 for Filtering
// 1 for Output Indexing
// C for Max per Class Limiting
size_t size = (3 + 1 + numClasses) * batchSize * sizeof(int);
total += size + (size % align ? align - (size % align) : 0);
// Int Buffers
for (int i = 0; i < 4; i++)
{
size = batchSize * numScoreElements * sizeof(int);
total += size + (size % align ? align - (size % align) : 0);
}
// Float Buffers
for (int i = 0; i < 2; i++)
{
size = batchSize * numScoreElements * dataTypeSize(datatype);
total += size + (size % align ? align - (size % align) : 0);
}
// Sort Workspace
if (datatype == DataType::kHALF)
{
size = EfficientNMSSortWorkspaceSize<__half>(batchSize, numScoreElements);
total += size + (size % align ? align - (size % align) : 0);
}
else if (datatype == DataType::kFLOAT)
{
size = EfficientNMSSortWorkspaceSize<float>(batchSize, numScoreElements);
total += size + (size % align ? align - (size % align) : 0);
}
return total;
}
template <typename T>
T* EfficientNMSWorkspace(void* workspace, size_t& offset, size_t elements)
{
T* buffer = (T*) ((size_t) workspace + offset);
size_t align = 256;
size_t size = elements * sizeof(T);
size_t sizeAligned = size + (size % align ? align - (size % align) : 0);
offset += sizeAligned;
return buffer;
}
template <typename T>
pluginStatus_t EfficientNMSDispatch(EfficientNMSParameters param, const void* boxesInput, const void* scoresInput,
const void* anchorsInput, void* numDetectionsOutput, void* nmsBoxesOutput, void* nmsScoresOutput,
void* nmsClassesOutput, void* nmsIndicesOutput, void* workspace, cudaStream_t stream)
{
// Clear Outputs (not all elements will get overwritten by the kernels, so safer to clear everything out)
if (param.outputONNXIndices)
{
CSC(cudaMemsetAsync(nmsIndicesOutput, 0xFF, param.batchSize * param.numOutputBoxes * 3 * sizeof(int), stream), STATUS_FAILURE);
}
else
{
CSC(cudaMemsetAsync(numDetectionsOutput, 0x00, param.batchSize * sizeof(int), stream), STATUS_FAILURE);
CSC(cudaMemsetAsync(nmsScoresOutput, 0x00, param.batchSize * param.numOutputBoxes * sizeof(T), stream), STATUS_FAILURE);
CSC(cudaMemsetAsync(nmsBoxesOutput, 0x00, param.batchSize * param.numOutputBoxes * 4 * sizeof(T), stream), STATUS_FAILURE);
CSC(cudaMemsetAsync(nmsClassesOutput, 0x00, param.batchSize * param.numOutputBoxes * sizeof(int), stream), STATUS_FAILURE);
}
// Empty Inputs
if (param.numScoreElements < 1)
{
return STATUS_SUCCESS;
}
// Counters Workspace
size_t workspaceOffset = 0;
int countersTotalSize = (3 + 1 + param.numClasses) * param.batchSize;
int* topNumData = EfficientNMSWorkspace<int>(workspace, workspaceOffset, countersTotalSize);
int* topOffsetsStartData = topNumData + param.batchSize;
int* topOffsetsEndData = topNumData + 2 * param.batchSize;
int* outputIndexData = topNumData + 3 * param.batchSize;
int* outputClassData = topNumData + 4 * param.batchSize;
CSC(cudaMemsetAsync(topNumData, 0x00, countersTotalSize * sizeof(int), stream), STATUS_FAILURE);
cudaError_t status = cudaGetLastError();
CSC(status, STATUS_FAILURE);
// Other Buffers Workspace
int* topIndexData
= EfficientNMSWorkspace<int>(workspace, workspaceOffset, param.batchSize * param.numScoreElements);
int* topClassData
= EfficientNMSWorkspace<int>(workspace, workspaceOffset, param.batchSize * param.numScoreElements);
int* topAnchorsData
= EfficientNMSWorkspace<int>(workspace, workspaceOffset, param.batchSize * param.numScoreElements);
int* sortedIndexData
= EfficientNMSWorkspace<int>(workspace, workspaceOffset, param.batchSize * param.numScoreElements);
T* topScoresData = EfficientNMSWorkspace<T>(workspace, workspaceOffset, param.batchSize * param.numScoreElements);
T* sortedScoresData
= EfficientNMSWorkspace<T>(workspace, workspaceOffset, param.batchSize * param.numScoreElements);
size_t sortedWorkspaceSize = EfficientNMSSortWorkspaceSize<T>(param.batchSize, param.numScoreElements);
char* sortedWorkspaceData = EfficientNMSWorkspace<char>(workspace, workspaceOffset, sortedWorkspaceSize);
cub::DoubleBuffer<T> scoresDB(topScoresData, sortedScoresData);
cub::DoubleBuffer<int> indexDB(topIndexData, sortedIndexData);
// Kernels
status = EfficientNMSFilterLauncher<T>(param, (T*) scoresInput, topNumData, topIndexData, topAnchorsData,
topOffsetsStartData, topOffsetsEndData, topScoresData, topClassData, stream);
CSC(status, STATUS_FAILURE);
status = cub::DeviceSegmentedRadixSort::SortPairsDescending(sortedWorkspaceData, sortedWorkspaceSize, scoresDB,
indexDB, param.batchSize * param.numScoreElements, param.batchSize, topOffsetsStartData, topOffsetsEndData,
param.scoreBits > 0 ? (10 - param.scoreBits) : 0, param.scoreBits > 0 ? 10 : sizeof(T) * 8, stream);
CSC(status, STATUS_FAILURE);
status = EfficientNMSLauncher<T>(param, topNumData, outputIndexData, outputClassData, indexDB.Current(),
scoresDB.Current(), topClassData, topAnchorsData, boxesInput, anchorsInput, (int*) numDetectionsOutput,
(T*) nmsScoresOutput, (int*) nmsClassesOutput, (int*) nmsIndicesOutput, nmsBoxesOutput, stream);
CSC(status, STATUS_FAILURE);
return STATUS_SUCCESS;
}
pluginStatus_t EfficientNMSInference(EfficientNMSParameters param, const void* boxesInput, const void* scoresInput,
const void* anchorsInput, void* numDetectionsOutput, void* nmsBoxesOutput, void* nmsScoresOutput,
void* nmsClassesOutput, void* nmsIndicesOutput, void* workspace, cudaStream_t stream)
{
if (param.datatype == DataType::kFLOAT)
{
param.scoreBits = -1;
return EfficientNMSDispatch<float>(param, boxesInput, scoresInput, anchorsInput, numDetectionsOutput,
nmsBoxesOutput, nmsScoresOutput, nmsClassesOutput, nmsIndicesOutput, workspace, stream);
}
else if (param.datatype == DataType::kHALF)
{
if (param.scoreBits <= 0 || param.scoreBits > 10)
{
param.scoreBits = -1;
}
return EfficientNMSDispatch<__half>(param, boxesInput, scoresInput, anchorsInput, numDetectionsOutput,
nmsBoxesOutput, nmsScoresOutput, nmsClassesOutput, nmsIndicesOutput, workspace, stream);
}
else
{
return STATUS_NOT_SUPPORTED;
}
}