Files
paddlepaddle--paddle/paddle/phi/kernels/xpu/generate_proposals_kernel.cc
T
2026-07-13 12:40:42 +08:00

421 lines
15 KiB
C++

// Copyright (c) 2022 PaddlePaddle Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "paddle/phi/kernels/generate_proposals_kernel.h"
#include "paddle/phi/backends/xpu/enforce_xpu.h"
#include "paddle/phi/backends/xpu/xpu_context.h"
#include "paddle/phi/core/kernel_registry.h"
#include "paddle/phi/kernels/full_kernel.h"
#include "paddle/phi/kernels/funcs/math_function_impl.h"
#include "paddle/phi/common/memory_utils.h"
namespace phi {
template <typename T>
static void SortDescending(const XPUContext& dev_ctx,
const DenseTensor& value,
DenseTensor* index_out,
int pre_nms_top_n) {
auto* value_data = value.data<T>();
auto place = dev_ctx.GetPlace();
auto cpu_place = CPUPlace();
DenseTensor scores_slice_cpu;
scores_slice_cpu.Resize({value.numel()});
T* scores_slice_cpu_data = dev_ctx.template HostAlloc<T>(&scores_slice_cpu);
memory_utils::Copy(cpu_place,
scores_slice_cpu_data,
place,
value_data,
sizeof(T) * value.numel());
// Sort index
DenseTensor index_t;
index_t.Resize({value.numel()});
int* index = dev_ctx.template HostAlloc<int>(&index_t);
for (int64_t i = 0; i < value.numel(); ++i) {
index[i] = i;
}
auto compare = [scores_slice_cpu_data](const int64_t& i, const int64_t& j) {
return scores_slice_cpu_data[i] > scores_slice_cpu_data[j];
};
if (pre_nms_top_n <= 0 || pre_nms_top_n >= value.numel()) {
std::sort(index, index + value.numel(), compare);
} else {
std::nth_element(
index, index + pre_nms_top_n, index + value.numel(), compare);
std::sort(index, index + pre_nms_top_n, compare);
index_t.Resize({pre_nms_top_n});
}
index_out->Resize({index_t.numel()});
int* idx_out = dev_ctx.template Alloc<int>(index_out);
memory_utils::Copy(
place, idx_out, cpu_place, index, sizeof(T) * index_t.numel());
}
template <typename T>
std::pair<DenseTensor, DenseTensor> ProposalForOneImage(
const XPUContext& dev_ctx,
const DenseTensor& im_shape_slice,
const DenseTensor& anchors,
const DenseTensor& variances,
const DenseTensor& bbox_deltas_slice, // [M, 4]
const DenseTensor& scores_slice, // [N, 1]
int pre_nms_top_n,
int post_nms_top_n,
float nms_thresh,
float min_size,
float eta,
bool pixel_offset = true) {
// 1. pre nms
DenseTensor index_sort;
SortDescending<T>(dev_ctx, scores_slice, &index_sort, pre_nms_top_n);
DenseTensor scores_sel, bbox_sel, anchor_sel, var_sel;
scores_sel.Resize({index_sort.numel(), 1});
dev_ctx.template Alloc<T>(&scores_sel);
bbox_sel.Resize({index_sort.numel(), 4});
dev_ctx.template Alloc<T>(&bbox_sel);
anchor_sel.Resize({index_sort.numel(), 4});
dev_ctx.template Alloc<T>(&anchor_sel);
var_sel.Resize({index_sort.numel(), 4});
dev_ctx.template Alloc<T>(&var_sel);
int r = xpu::paddle_gather<T>(dev_ctx.x_context(),
scores_slice.data<T>(),
index_sort.data<int>(),
scores_sel.data<T>(),
{scores_slice.numel(), 1},
index_sort.numel(),
0);
PADDLE_ENFORCE_XDNN_SUCCESS(r, "paddle_gather");
r = xpu::paddle_gather<T>(dev_ctx.x_context(),
bbox_deltas_slice.data<T>(),
index_sort.data<int>(),
bbox_sel.data<T>(),
{bbox_deltas_slice.numel() / 4, 4},
index_sort.numel(),
0);
PADDLE_ENFORCE_XDNN_SUCCESS(r, "paddle_gather");
r = xpu::paddle_gather<T>(dev_ctx.x_context(),
anchors.data<T>(),
index_sort.data<int>(),
anchor_sel.data<T>(),
{anchors.numel() / 4, 4},
index_sort.numel(),
0);
PADDLE_ENFORCE_XDNN_SUCCESS(r, "paddle_gather");
r = xpu::paddle_gather<T>(dev_ctx.x_context(),
variances.data<T>(),
index_sort.data<int>(),
var_sel.data<T>(),
{variances.numel() / 4, 4},
index_sort.numel(),
0);
PADDLE_ENFORCE_XDNN_SUCCESS(r, "paddle_gather");
int num = scores_slice.numel();
int pre_nms_num = (pre_nms_top_n <= 0 || pre_nms_top_n > num)
? scores_slice.numel()
: pre_nms_top_n;
scores_sel.Resize({pre_nms_num, 1});
index_sort.Resize({pre_nms_num, 1});
// 2. box decode and clipping
DenseTensor proposals;
proposals.Resize({index_sort.numel(), 4});
dev_ctx.template Alloc<T>(&proposals);
r = xpu::box_decoder<T>(dev_ctx.x_context(),
anchor_sel.data<T>(),
var_sel.data<T>(),
bbox_sel.data<T>(),
proposals.data<T>(),
pre_nms_num,
!pixel_offset,
true,
im_shape_slice.data<T>());
PADDLE_ENFORCE_XDNN_SUCCESS(r, "box_decoder");
// 3. filter
DenseTensor keep_index, keep_num_t;
keep_index.Resize({pre_nms_num});
dev_ctx.template Alloc<int>(&keep_index);
keep_num_t.Resize({1});
dev_ctx.template Alloc<int>(&keep_num_t);
min_size = std::max(min_size, 1.0f);
r = xpu::remove_small_boxes<T>(dev_ctx.x_context(),
proposals.data<T>(),
im_shape_slice.data<T>(),
keep_index.data<int>(),
keep_num_t.data<int>(),
pre_nms_num,
min_size,
false,
pixel_offset);
PADDLE_ENFORCE_XDNN_SUCCESS(r, "remove_small_boxes");
int keep_num;
const auto xpu_place = dev_ctx.GetPlace();
memory_utils::Copy(
CPUPlace(), &keep_num, xpu_place, keep_num_t.data<int>(), sizeof(int));
keep_index.Resize({keep_num});
DenseTensor scores_filter, proposals_filter;
// Handle the case when there is no keep index left
if (keep_num == 0) {
funcs::SetConstant<XPUContext, T> set_zero;
proposals_filter.Resize({1, 4});
dev_ctx.template Alloc<T>(&proposals_filter);
scores_filter.Resize({1, 1});
dev_ctx.template Alloc<T>(&scores_filter);
set_zero(dev_ctx, &proposals_filter, static_cast<T>(0));
set_zero(dev_ctx, &scores_filter, static_cast<T>(0));
return std::make_pair(proposals_filter, scores_filter);
}
proposals_filter.Resize({keep_num, 4});
dev_ctx.template Alloc<T>(&proposals_filter);
scores_filter.Resize({keep_num, 1});
dev_ctx.template Alloc<T>(&scores_filter);
r = xpu::paddle_gather<T>(dev_ctx.x_context(),
proposals.data<T>(),
keep_index.data<int>(),
proposals_filter.data<T>(),
{pre_nms_num, 4},
keep_num,
0);
PADDLE_ENFORCE_XDNN_SUCCESS(r, "paddle_gather");
r = xpu::paddle_gather<T>(dev_ctx.x_context(),
scores_sel.data<T>(),
keep_index.data<int>(),
scores_filter.data<T>(),
{pre_nms_num, 1},
keep_num,
0);
PADDLE_ENFORCE_XDNN_SUCCESS(r, "paddle_gather");
if (nms_thresh <= 0) {
if (dev_ctx.x_context()->xpu_stream) {
dev_ctx.Wait();
}
return std::make_pair(proposals_filter, scores_filter);
}
// 4. nms
int64_t nms_keep_num = 0;
r = xpu::sorted_nms<T>(dev_ctx.x_context(),
proposals_filter.data<T>(),
keep_index.data<int>(),
nms_keep_num,
keep_num,
nms_thresh,
pixel_offset);
PADDLE_ENFORCE_XDNN_SUCCESS(r, "sorted_nms");
if (post_nms_top_n > 0 && post_nms_top_n < nms_keep_num) {
keep_index.Resize({post_nms_top_n});
} else {
keep_index.Resize({nms_keep_num});
}
DenseTensor scores_nms, proposals_nms;
proposals_nms.Resize({keep_index.numel(), 4});
dev_ctx.template Alloc<T>(&proposals_nms);
scores_nms.Resize({keep_index.numel(), 1});
dev_ctx.template Alloc<T>(&scores_nms);
r = xpu::paddle_gather<T>(dev_ctx.x_context(),
proposals_filter.data<T>(),
keep_index.data<int>(),
proposals_nms.data<T>(),
{keep_num, 4},
keep_index.numel(),
0);
PADDLE_ENFORCE_XDNN_SUCCESS(r, "paddle_gather");
r = xpu::paddle_gather<T>(dev_ctx.x_context(),
scores_filter.data<T>(),
keep_index.data<int>(),
scores_nms.data<T>(),
{keep_num, 1},
keep_index.numel(),
0);
PADDLE_ENFORCE_XDNN_SUCCESS(r, "paddle_gather");
if (dev_ctx.x_context()->xpu_stream) {
dev_ctx.Wait();
}
return std::make_pair(proposals_nms, scores_nms);
}
template <typename T, typename Context>
void GenerateProposalsKernel(const Context& dev_ctx,
const DenseTensor& scores,
const DenseTensor& bbox_deltas,
const DenseTensor& im_shape,
const DenseTensor& anchors,
const DenseTensor& variances,
int pre_nms_top_n,
int post_nms_top_n,
float nms_thresh,
float min_size,
float eta,
bool pixel_offset,
DenseTensor* rpn_rois,
DenseTensor* rpn_roi_probs,
DenseTensor* rpn_rois_num) {
PADDLE_ENFORCE_GE(eta,
1.,
common::errors::InvalidArgument(
"Not support adaptive NMS. The attribute 'eta' "
"should not less than 1. But received eta=[%d]",
eta));
auto& scores_dim = scores.dims();
// the shape of bbox score
int num = scores_dim[0];
int c_score = scores_dim[1];
int h_score = scores_dim[2];
int w_score = scores_dim[3];
auto& bbox_dim = bbox_deltas.dims();
int c_bbox = bbox_dim[1];
int h_bbox = bbox_dim[2];
int w_bbox = bbox_dim[3];
// output
rpn_rois->Resize({bbox_deltas.numel() / 4, 4});
dev_ctx.template Alloc<T>(rpn_rois);
rpn_roi_probs->Resize({scores.numel(), 1});
dev_ctx.template Alloc<T>(rpn_roi_probs);
if (scores.numel() == 0) {
rpn_rois->Resize({0, 4});
if (rpn_rois_num != nullptr) {
rpn_rois_num->Resize({});
Full<int64_t, Context>(dev_ctx, rpn_rois_num->dims(), 0, rpn_rois_num);
}
return;
}
DenseTensor bbox_deltas_swap, scores_swap;
bbox_deltas_swap.Resize({num, h_bbox, w_bbox, c_bbox});
dev_ctx.template Alloc<T>(&bbox_deltas_swap);
scores_swap.Resize({num, h_score, w_score, c_score});
dev_ctx.template Alloc<T>(&scores_swap);
std::vector<int64_t> axis = {0, 2, 3, 1};
int r = xpu::transpose<T>(dev_ctx.x_context(),
bbox_deltas.data<T>(),
bbox_deltas_swap.data<T>(),
{num, c_bbox, h_bbox, w_bbox},
axis);
PADDLE_ENFORCE_XDNN_SUCCESS(r, "transpose");
r = xpu::transpose<T>(dev_ctx.x_context(),
scores.data<T>(),
scores_swap.data<T>(),
{num, c_score, h_score, w_score},
axis);
PADDLE_ENFORCE_XDNN_SUCCESS(r, "transpose");
DenseTensor tmp_anchors = anchors;
DenseTensor tmp_variances = variances;
tmp_anchors.Resize({tmp_anchors.numel() / 4, 4});
tmp_variances.Resize({tmp_variances.numel() / 4, 4});
auto place = dev_ctx.GetPlace();
auto cpu_place = CPUPlace();
int num_proposals = 0;
std::vector<size_t> offset(1, 0);
std::vector<int> tmp_num;
for (int64_t i = 0; i < num; ++i) {
DenseTensor im_shape_slice = im_shape.Slice(i, i + 1);
DenseTensor bbox_deltas_slice = bbox_deltas_swap.Slice(i, i + 1);
DenseTensor scores_slice = scores_swap.Slice(i, i + 1);
bbox_deltas_slice.Resize({h_bbox * w_bbox * c_bbox / 4, 4});
scores_slice.Resize({h_score * w_score * c_score, 1});
std::pair<DenseTensor, DenseTensor> tensor_pair =
ProposalForOneImage<T>(dev_ctx,
im_shape_slice,
tmp_anchors,
tmp_variances,
bbox_deltas_slice,
scores_slice,
pre_nms_top_n,
post_nms_top_n,
nms_thresh,
min_size,
eta,
pixel_offset);
DenseTensor& proposals = tensor_pair.first;
DenseTensor& nscores = tensor_pair.second;
r = xpu::copy(dev_ctx.x_context(),
proposals.data<T>(),
rpn_rois->data<T>() + num_proposals * 4,
proposals.numel());
PADDLE_ENFORCE_XDNN_SUCCESS(r, "copy");
r = xpu::copy(dev_ctx.x_context(),
nscores.data<T>(),
rpn_roi_probs->data<T>() + num_proposals,
nscores.numel());
PADDLE_ENFORCE_XDNN_SUCCESS(r, "copy");
if (dev_ctx.x_context()->xpu_stream) {
dev_ctx.Wait();
}
num_proposals += proposals.dims()[0];
offset.emplace_back(num_proposals);
tmp_num.push_back(proposals.dims()[0]);
}
if (rpn_rois_num != nullptr) {
rpn_rois_num->Resize({num});
dev_ctx.template Alloc<int>(rpn_rois_num);
int* num_data = rpn_rois_num->data<int>();
memory_utils::Copy(
place, num_data, cpu_place, &tmp_num[0], sizeof(int) * num);
}
LegacyLoD lod;
lod.emplace_back(offset);
rpn_rois->set_lod(lod);
rpn_roi_probs->set_lod(lod);
rpn_rois->Resize({num_proposals, 4});
rpn_roi_probs->Resize({num_proposals, 1});
}
} // namespace phi
PD_REGISTER_KERNEL(
generate_proposals, XPU, ALL_LAYOUT, phi::GenerateProposalsKernel, float) {
kernel->OutputAt(2).SetDataType(phi::DataType::INT32);
}