diff --git a/paddle/fluid/operators/detection_map_op.cc b/paddle/fluid/operators/detection_map_op.cc deleted file mode 100644 index cee37d49eb69bb..00000000000000 --- a/paddle/fluid/operators/detection_map_op.cc +++ /dev/null @@ -1,229 +0,0 @@ -/* Copyright (c) 2018 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/fluid/operators/detection_map_op.h" - -#include - -namespace paddle { -namespace operators { - -class DetectionMAPOp : public framework::OperatorWithKernel { - public: - using framework::OperatorWithKernel::OperatorWithKernel; - - void InferShape(framework::InferShapeContext* ctx) const override { - OP_INOUT_CHECK( - ctx->HasInput("DetectRes"), "Input", "DetectRes", "DetectionMAP"); - OP_INOUT_CHECK(ctx->HasInput("Label"), "Input", "Label", "DetectionMAP"); - OP_INOUT_CHECK(ctx->HasOutput("AccumPosCount"), - "Output", - "AccumPosCount", - "DetectionMAP"); - OP_INOUT_CHECK(ctx->HasOutput("AccumTruePos"), - "Output", - "AccumTruePos", - "DetectionMAP"); - OP_INOUT_CHECK(ctx->HasOutput("AccumFalsePos"), - "Output", - "AccumFalsePos", - "DetectionMAP"); - OP_INOUT_CHECK(ctx->HasOutput("MAP"), "Output", "MAP", "DetectionMAP"); - - auto det_dims = ctx->GetInputDim("DetectRes"); - PADDLE_ENFORCE_EQ( - det_dims.size(), - 2UL, - phi::errors::InvalidArgument( - "Input(DetectRes) ndim must be 2, the shape is [N, 6]," - "but received the ndim is %d", - det_dims.size())); - PADDLE_ENFORCE_EQ( - det_dims[1], - 6UL, - phi::errors::InvalidArgument( - "The shape is of Input(DetectRes) [N, 6], but received" - " shape is [N, %d]", - det_dims[1])); - auto label_dims = ctx->GetInputDim("Label"); - PADDLE_ENFORCE_EQ(label_dims.size(), - 2, - phi::errors::InvalidArgument( - "The ndim of Input(Label) must be 2, but received %d", - label_dims.size())); - if (ctx->IsRuntime() || label_dims[1] > 0) { - PADDLE_ENFORCE_EQ( - (label_dims[1] == 6 || label_dims[1] == 5), - true, - phi::errors::InvalidArgument( - "The shape of Input(Label) is [N, 6] or [N, 5], but received " - "[N, %d]", - label_dims[1])); - } - - if (ctx->HasInput("PosCount")) { - PADDLE_ENFORCE( - ctx->HasInput("TruePos"), - phi::errors::InvalidArgument( - "Input(TruePos) of DetectionMAPOp should not be null when " - "Input(PosCount) is not null.")); - PADDLE_ENFORCE( - ctx->HasInput("FalsePos"), - phi::errors::InvalidArgument( - "Input(FalsePos) of DetectionMAPOp should not be null when " - "Input(PosCount) is not null.")); - } - - ctx->SetOutputDim("MAP", common::make_ddim({1})); - } - - protected: - phi::KernelKey GetExpectedKernelType( - const framework::ExecutionContext& ctx) const override { - return phi::KernelKey( - OperatorWithKernel::IndicateVarDataType(ctx, "DetectRes"), - platform::CPUPlace()); - } -}; - -class DetectionMAPOpMaker : public framework::OpProtoAndCheckerMaker { - public: - void Make() override { - AddInput("DetectRes", - "(phi::DenseTensor) A 2-D phi::DenseTensor with shape [M, 6] " - "represents the " - "detections. Each row has 6 values: " - "[label, confidence, xmin, ymin, xmax, ymax], M is the total " - "number of detect results in this mini-batch. For each instance, " - "the offsets in first dimension are called LoD, the number of " - "offset is N + 1, if LoD[i + 1] - LoD[i] == 0, means there is " - "no detected data."); - AddInput("Label", - "(phi::DenseTensor) A 2-D phi::DenseTensor represents the" - "Labeled ground-truth data. Each row has 6 values: " - "[label, xmin, ymin, xmax, ymax, is_difficult] or 5 values: " - "[label, xmin, ymin, xmax, ymax], where N is the total " - "number of ground-truth data in this mini-batch. For each " - "instance, the offsets in first dimension are called LoD, " - "the number of offset is N + 1, if LoD[i + 1] - LoD[i] == 0, " - "means there is no ground-truth data."); - AddInput("HasState", - "(Tensor) A tensor with shape [1], 0 means ignoring input " - "states, which including PosCount, TruePos, FalsePos.") - .AsDispensable(); - AddInput("PosCount", - "(Tensor) A tensor with shape [Ncls, 1], store the " - "input positive example count of each class, Ncls is the count of " - "input classification. " - "This input is used to pass the AccumPosCount generated by the " - "previous mini-batch when the multi mini-batches cumulative " - "calculation carried out. " - "When the input(PosCount) is empty, the cumulative " - "calculation is not carried out, and only the results of the " - "current mini-batch are calculated.") - .AsDispensable(); - AddInput("TruePos", - "(phi::DenseTensor) A 2-D phi::DenseTensor with shape [Ntp, 2], " - "store the " - "input true positive example of each class." - "This input is used to pass the AccumTruePos generated by the " - "previous mini-batch when the multi mini-batches cumulative " - "calculation carried out. ") - .AsDispensable(); - AddInput("FalsePos", - "(phi::DenseTensor) A 2-D phi::DenseTensor with shape [Nfp, 2], " - "store the " - "input false positive example of each class." - "This input is used to pass the AccumFalsePos generated by the " - "previous mini-batch when the multi mini-batches cumulative " - "calculation carried out. ") - .AsDispensable(); - AddOutput("AccumPosCount", - "(Tensor) A tensor with shape [Ncls, 1], store the " - "positive example count of each class. It combines the input " - "input(PosCount) and the positive example count computed from " - "input(Detection) and input(Label)."); - AddOutput( - "AccumTruePos", - "(phi::DenseTensor) A phi::DenseTensor with shape [Ntp', 2], store the " - "true positive example of each class. It combines the " - "input(TruePos) and the true positive examples computed from " - "input(Detection) and input(Label)."); - AddOutput( - "AccumFalsePos", - "(phi::DenseTensor) A phi::DenseTensor with shape [Nfp', 2], store the " - "false positive example of each class. It combines the " - "input(FalsePos) and the false positive examples computed from " - "input(Detection) and input(Label)."); - AddOutput("MAP", - "(Tensor) A tensor with shape [1], store the mAP evaluate " - "result of the detection."); - AddAttr("class_num", - "(int) " - "The class number."); - AddAttr( - "background_label", - "(int, default: 0) " - "The index of background label, the background label will be ignored. " - "If set to -1, then all categories will be considered.") - .SetDefault(0); - AddAttr( - "overlap_threshold", - "(float) " - "The lower bound jaccard overlap threshold of detection output and " - "ground-truth data.") - .SetDefault(.5f); - AddAttr("evaluate_difficult", - "(bool, default true) " - "Switch to control whether the difficult data is evaluated.") - .SetDefault(true); - AddAttr("ap_type", - "(string, default 'integral') " - "The AP algorithm type, 'integral' or '11point'.") - .SetDefault("integral") - .InEnum({"integral", "11point"}) - .AddCustomChecker([](const std::string& ap_type) { - PADDLE_ENFORCE_NE( - GetAPType(ap_type), - APType::kNone, - phi::errors::InvalidArgument( - "The ap_type should be 'integral' or '11point.")); - }); - AddComment(R"DOC( -Detection mAP evaluate operator. -The general steps are as follows. First, calculate the true positive and -false positive according to the input of detection and labels, then -calculate the mAP evaluate value. -Supporting '11 point' and 'integral' mAP algorithm. Please get more information -from the following articles: -https://sanchom.wordpress.com/tag/average-precision/ -https://arxiv.org/abs/1512.02325 - -)DOC"); - } -}; - -} // namespace operators -} // namespace paddle - -namespace ops = paddle::operators; -REGISTER_OPERATOR( - detection_map, - ops::DetectionMAPOp, - ops::DetectionMAPOpMaker, - paddle::framework::EmptyGradOpMaker, - paddle::framework::EmptyGradOpMaker); - -PD_REGISTER_STRUCT_KERNEL( - detection_map, CPU, ALL_LAYOUT, ops::DetectionMAPOpKernel, float, double) {} diff --git a/paddle/fluid/operators/detection_map_op.h b/paddle/fluid/operators/detection_map_op.h deleted file mode 100644 index 9d01accf030427..00000000000000 --- a/paddle/fluid/operators/detection_map_op.h +++ /dev/null @@ -1,518 +0,0 @@ -/* Copyright (c) 2018 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. */ - -#pragma once -#include -#include -#include -#include -#include - -#include "paddle/fluid/framework/op_registry.h" -#include "paddle/phi/kernels/funcs/eigen/common.h" - -namespace paddle { -namespace operators { - -enum APType { kNone = 0, kIntegral, k11point }; - -APType GetAPType(std::string str) { - if (str == "integral") { - return APType::kIntegral; - } else if (str == "11point") { - return APType::k11point; - } else { - return APType::kNone; - } -} - -template -inline bool SortScorePairDescend(const std::pair& pair1, - const std::pair& pair2) { - return pair1.first > pair2.first; -} - -template -inline void GetAccumulation(std::vector> in_pairs, - std::vector* accu_vec) { - std::stable_sort(in_pairs.begin(), in_pairs.end(), SortScorePairDescend); - accu_vec->clear(); - size_t sum = 0; - for (size_t i = 0; i < in_pairs.size(); ++i) { - auto count = in_pairs[i].second; - sum += count; - accu_vec->push_back(sum); - } -} - -template -class DetectionMAPOpKernel : public framework::OpKernel { - public: - void Compute(const framework::ExecutionContext& ctx) const override { - auto* in_detect = ctx.Input("DetectRes"); - auto* in_label = ctx.Input("Label"); - auto* out_map = ctx.Output("MAP"); - - auto* in_pos_count = ctx.Input("PosCount"); - auto* in_true_pos = ctx.Input("TruePos"); - auto* in_false_pos = ctx.Input("FalsePos"); - - auto* out_pos_count = ctx.Output("AccumPosCount"); - auto* out_true_pos = ctx.Output("AccumTruePos"); - auto* out_false_pos = ctx.Output("AccumFalsePos"); - - float overlap_threshold = ctx.Attr("overlap_threshold"); - bool evaluate_difficult = ctx.Attr("evaluate_difficult"); - auto ap_type = GetAPType(ctx.Attr("ap_type")); - int class_num = ctx.Attr("class_num"); - - auto& label_lod = in_label->lod(); - auto& detect_lod = in_detect->lod(); - PADDLE_ENFORCE_EQ( - label_lod.size(), - 1UL, - phi::errors::InvalidArgument("Only support LodTensor of lod_level " - "with 1 in label, but received %d.", - label_lod.size())); - PADDLE_ENFORCE_EQ(label_lod[0].size(), - detect_lod[0].size(), - phi::errors::InvalidArgument( - "The batch_size of input(Label) and input(Detection) " - "must be the same, but received %d:%d", - label_lod[0].size(), - detect_lod[0].size())); - - std::vector>> gt_boxes; - std::vector>>> detect_boxes; - - GetBoxes(*in_label, *in_detect, >_boxes, detect_boxes); - - std::map label_pos_count; - std::map>> true_pos; - std::map>> false_pos; - - auto* has_state = ctx.Input("HasState"); - int state = 0; - if (has_state) { - state = has_state->data()[0]; - } - - if (in_pos_count != nullptr && state) { - GetInputPos(*in_pos_count, - *in_true_pos, - *in_false_pos, - &label_pos_count, - &true_pos, - &false_pos, - class_num); - } - - CalcTrueAndFalsePositive(gt_boxes, - detect_boxes, - evaluate_difficult, - overlap_threshold, - &label_pos_count, - &true_pos, - &false_pos); - - int background_label = ctx.Attr("background_label"); - T map = CalcMAP( - ap_type, label_pos_count, true_pos, false_pos, background_label); - - GetOutputPos(ctx, - label_pos_count, - true_pos, - false_pos, - out_pos_count, - out_true_pos, - out_false_pos, - class_num); - - T* map_data = out_map->mutable_data(ctx.GetPlace()); - map_data[0] = map; - } - - protected: - struct Box { - Box(T xmin, T ymin, T xmax, T ymax) - : xmin(xmin), ymin(ymin), xmax(xmax), ymax(ymax), is_difficult(false) {} - - T xmin, ymin, xmax, ymax; - bool is_difficult; - }; - - inline T JaccardOverlap(const Box& box1, const Box& box2) const { - if (box2.xmin > box1.xmax || box2.xmax < box1.xmin || - box2.ymin > box1.ymax || box2.ymax < box1.ymin) { - return 0.0; - } else { - T inter_xmin = std::max(box1.xmin, box2.xmin); - T inter_ymin = std::max(box1.ymin, box2.ymin); - T inter_xmax = std::min(box1.xmax, box2.xmax); - T inter_ymax = std::min(box1.ymax, box2.ymax); - - T inter_width = inter_xmax - inter_xmin; - T inter_height = inter_ymax - inter_ymin; - T inter_area = inter_width * inter_height; - - T bbox_area1 = (box1.xmax - box1.xmin) * (box1.ymax - box1.ymin); - T bbox_area2 = (box2.xmax - box2.xmin) * (box2.ymax - box2.ymin); - - return inter_area / (bbox_area1 + bbox_area2 - inter_area); - } - } - - inline void ClipBBox(const Box& bbox, Box* clipped_bbox) const { - T one = static_cast(1.0); - T zero = static_cast(0.0); - clipped_bbox->xmin = std::max(std::min(bbox.xmin, one), zero); - clipped_bbox->ymin = std::max(std::min(bbox.ymin, one), zero); - clipped_bbox->xmax = std::max(std::min(bbox.xmax, one), zero); - clipped_bbox->ymax = std::max(std::min(bbox.ymax, one), zero); - } - - void GetBoxes(const phi::DenseTensor& input_label, - const phi::DenseTensor& input_detect, - std::vector>>* gt_boxes, - std::vector>>>& - detect_boxes) const { - auto labels = phi::EigenTensor::From(input_label); - auto detect = phi::EigenTensor::From(input_detect); - - auto& label_lod = input_label.lod(); - auto& detect_lod = input_detect.lod(); - - int batch_size = label_lod[0].size() - 1; - auto& label_index = label_lod[0]; - - for (int n = 0; n < batch_size; ++n) { - std::map> boxes; - for (size_t i = label_index[n]; i < label_index[n + 1]; ++i) { - int label = labels(i, 0); - if (input_label.dims()[1] == 6) { - Box box(labels(i, 2), labels(i, 3), labels(i, 4), labels(i, 5)); - auto is_difficult = labels(i, 1); - if (std::abs(is_difficult - 0.0) < 1e-6) - box.is_difficult = false; - else - box.is_difficult = true; - boxes[label].push_back(box); - } else { - PADDLE_ENFORCE_EQ( - input_label.dims()[1], - 5, - phi::errors::InvalidArgument( - "The input label width" - " must be 5, but received %d, please check your input data", - input_label.dims()[1])); - Box box(labels(i, 1), labels(i, 2), labels(i, 3), labels(i, 4)); - boxes[label].push_back(box); - } - } - gt_boxes->push_back(boxes); - } - - auto detect_index = detect_lod[0]; - for (int n = 0; n < batch_size; ++n) { - std::map>> boxes; - for (size_t i = detect_index[n]; i < detect_index[n + 1]; ++i) { - Box box(detect(i, 2), detect(i, 3), detect(i, 4), detect(i, 5)); - int label = detect(i, 0); - auto score = detect(i, 1); - boxes[label].push_back(std::make_pair(score, box)); - } - detect_boxes.push_back(boxes); - } - } - - void GetOutputPos( - const framework::ExecutionContext& ctx, - const std::map& label_pos_count, - const std::map>>& true_pos, - const std::map>>& false_pos, - phi::DenseTensor* output_pos_count, - phi::DenseTensor* output_true_pos, - phi::DenseTensor* output_false_pos, - const int class_num) const { - int true_pos_count = 0; - int false_pos_count = 0; - for (auto it = true_pos.begin(); it != true_pos.end(); ++it) { - auto tp = it->second; - true_pos_count += tp.size(); - } - for (auto it = false_pos.begin(); it != false_pos.end(); ++it) { - auto fp = it->second; - false_pos_count += fp.size(); - } - - int* pos_count_data = output_pos_count->mutable_data( - common::make_ddim({class_num, 1}), ctx.GetPlace()); - - T* true_pos_data = output_true_pos->mutable_data( - common::make_ddim({true_pos_count, 2}), ctx.GetPlace()); - T* false_pos_data = output_false_pos->mutable_data( - common::make_ddim({false_pos_count, 2}), ctx.GetPlace()); - true_pos_count = 0; - false_pos_count = 0; - std::vector true_pos_starts = {0}; - std::vector false_pos_starts = {0}; - for (int i = 0; i < class_num; ++i) { - auto it_count = label_pos_count.find(i); - pos_count_data[i] = 0; - if (it_count != label_pos_count.end()) { - pos_count_data[i] = it_count->second; - } - auto it_true_pos = true_pos.find(i); - if (it_true_pos != true_pos.end()) { - const std::vector>& true_pos_vec = - it_true_pos->second; - for (const std::pair& tp : true_pos_vec) { - true_pos_data[true_pos_count * 2] = tp.first; - true_pos_data[true_pos_count * 2 + 1] = static_cast(tp.second); - true_pos_count++; - } - } - true_pos_starts.push_back(true_pos_count); - - auto it_false_pos = false_pos.find(i); - if (it_false_pos != false_pos.end()) { - const std::vector>& false_pos_vec = - it_false_pos->second; - for (const std::pair& fp : false_pos_vec) { - false_pos_data[false_pos_count * 2] = fp.first; - false_pos_data[false_pos_count * 2 + 1] = static_cast(fp.second); - false_pos_count++; - } - } - false_pos_starts.push_back(false_pos_count); - } - - framework::LoD true_pos_lod; - true_pos_lod.emplace_back(true_pos_starts); - framework::LoD false_pos_lod; - false_pos_lod.emplace_back(false_pos_starts); - - output_true_pos->set_lod(true_pos_lod); - output_false_pos->set_lod(false_pos_lod); - } - - void GetInputPos(const phi::DenseTensor& input_pos_count, - const phi::DenseTensor& input_true_pos, - const phi::DenseTensor& input_false_pos, - std::map* label_pos_count, - std::map>>* true_pos, - std::map>>* false_pos, - const int class_num) const { - const int* pos_count_data = input_pos_count.data(); - for (int i = 0; i < class_num; ++i) { - (*label_pos_count)[i] = pos_count_data[i]; - } - - auto SetData = [](const phi::DenseTensor& pos_tensor, - std::map>>& pos) { - const T* pos_data = pos_tensor.data(); - auto& pos_data_lod = pos_tensor.lod()[0]; - for (size_t i = 0; i < pos_data_lod.size() - 1; ++i) { - for (size_t j = pos_data_lod[i]; j < pos_data_lod[i + 1]; ++j) { - T score = pos_data[j * 2]; - int flag = pos_data[j * 2 + 1]; - pos[i].push_back(std::make_pair(score, flag)); - } - } - }; - - SetData(input_true_pos, *true_pos); - SetData(input_false_pos, *false_pos); - return; - } - - void CalcTrueAndFalsePositive( - const std::vector>>& gt_boxes, - const std::vector>>>& - detect_boxes, - bool evaluate_difficult, - float overlap_threshold, - std::map* label_pos_count, - std::map>>* true_pos, - std::map>>* false_pos) const { - int batch_size = gt_boxes.size(); - for (int n = 0; n < batch_size; ++n) { - auto& image_gt_boxes = gt_boxes[n]; - for (auto& image_gt_box : image_gt_boxes) { - size_t count = 0; - auto& labeled_bboxes = image_gt_box.second; - if (evaluate_difficult) { - count = labeled_bboxes.size(); - } else { - for (auto& box : labeled_bboxes) { - if (!box.is_difficult) { - ++count; - } - } - } - if (count == 0) { - continue; - } - int label = image_gt_box.first; - if (label_pos_count->find(label) == label_pos_count->end()) { - (*label_pos_count)[label] = count; - } else { - (*label_pos_count)[label] += count; - } - } - } - - for (size_t n = 0; n < detect_boxes.size(); ++n) { - auto image_gt_boxes = gt_boxes[n]; - auto detections = detect_boxes[n]; - - if (image_gt_boxes.size() == 0) { - for (auto it = detections.begin(); it != detections.end(); ++it) { - auto pred_boxes = it->second; - int label = it->first; - for (size_t i = 0; i < pred_boxes.size(); ++i) { - auto score = pred_boxes[i].first; - (*true_pos)[label].push_back(std::make_pair(score, 0)); - (*false_pos)[label].push_back(std::make_pair(score, 1)); - } - } - continue; - } - - for (auto it = detections.begin(); it != detections.end(); ++it) { - int label = it->first; - auto pred_boxes = it->second; - if (image_gt_boxes.find(label) == image_gt_boxes.end()) { - for (size_t i = 0; i < pred_boxes.size(); ++i) { - auto score = pred_boxes[i].first; - (*true_pos)[label].push_back(std::make_pair(score, 0)); - (*false_pos)[label].push_back(std::make_pair(score, 1)); - } - continue; - } - - auto matched_bboxes = image_gt_boxes.find(label)->second; - std::vector visited(matched_bboxes.size(), false); - // Sort detections in descend order based on scores - std::sort( - pred_boxes.begin(), pred_boxes.end(), SortScorePairDescend); - for (size_t i = 0; i < pred_boxes.size(); ++i) { - T max_overlap = -1.0; - size_t max_idx = 0; - auto score = pred_boxes[i].first; - for (size_t j = 0; j < matched_bboxes.size(); ++j) { - Box& pred_box = pred_boxes[i].second; - ClipBBox(pred_box, &pred_box); - T overlap = JaccardOverlap(pred_box, matched_bboxes[j]); - if (overlap > max_overlap) { - max_overlap = overlap; - max_idx = j; - } - } - if (max_overlap > overlap_threshold) { - bool match_evaluate_difficult = - evaluate_difficult || - (!evaluate_difficult && !matched_bboxes[max_idx].is_difficult); - if (match_evaluate_difficult) { - if (!visited[max_idx]) { - (*true_pos)[label].push_back(std::make_pair(score, 1)); - (*false_pos)[label].push_back(std::make_pair(score, 0)); - visited[max_idx] = true; - } else { - (*true_pos)[label].push_back(std::make_pair(score, 0)); - (*false_pos)[label].push_back(std::make_pair(score, 1)); - } - } - } else { - (*true_pos)[label].push_back(std::make_pair(score, 0)); - (*false_pos)[label].push_back(std::make_pair(score, 1)); - } - } - } - } - } - - T CalcMAP(APType ap_type, - const std::map& label_pos_count, - const std::map>>& true_pos, - const std::map>>& false_pos, - const int background_label) const { - T mAP = 0.0; - int count = 0; - for (auto it = label_pos_count.begin(); it != label_pos_count.end(); ++it) { - int label = it->first; - int label_num_pos = it->second; - if (label_num_pos == background_label) { - continue; - } - if (true_pos.find(label) == true_pos.end()) { - count++; - continue; - } - auto label_true_pos = true_pos.find(label)->second; - auto label_false_pos = false_pos.find(label)->second; - // Compute average precision. - std::vector tp_sum; - GetAccumulation(label_true_pos, &tp_sum); - std::vector fp_sum; - GetAccumulation(label_false_pos, &fp_sum); - std::vector precision, recall; - size_t num = tp_sum.size(); - // Compute Precision. - for (size_t i = 0; i < num; ++i) { - precision.push_back(static_cast(tp_sum[i]) / - static_cast(tp_sum[i] + fp_sum[i])); - recall.push_back(static_cast(tp_sum[i]) / label_num_pos); - } - // VOC2007 style - if (ap_type == APType::k11point) { - std::vector max_precisions(11, 0.0); - int start_idx = num - 1; - for (int j = 10; j >= 0; --j) - for (int i = start_idx; i >= 0; --i) { - if (recall[i] < j / 10.) { - start_idx = i; - if (j > 0) max_precisions[j - 1] = max_precisions[j]; - break; - } else { - if (max_precisions[j] < precision[i]) - max_precisions[j] = precision[i]; - } - } - for (int j = 10; j >= 0; --j) mAP += max_precisions[j] / 11; - ++count; - } else if (ap_type == APType::kIntegral) { - // Nature integral - float average_precisions = 0.; - float prev_recall = 0.; - for (size_t i = 0; i < num; ++i) { - if (fabs(recall[i] - prev_recall) > 1e-6) - average_precisions += precision[i] * fabs(recall[i] - prev_recall); - prev_recall = recall[i]; - } - mAP += average_precisions; - ++count; - } else { - PADDLE_THROW(phi::errors::Unimplemented( - "Unkown ap version %s. Now only supports integral and l1point.", - ap_type)); - } - } - if (count != 0) mAP /= count; - return mAP; - } -}; // namespace operators - -} // namespace operators -} // namespace paddle diff --git a/paddle/phi/api/yaml/op_compat.yaml b/paddle/phi/api/yaml/op_compat.yaml index 301835ed5bc7a6..a637c96c33eae5 100755 --- a/paddle/phi/api/yaml/op_compat.yaml +++ b/paddle/phi/api/yaml/op_compat.yaml @@ -3846,6 +3846,13 @@ outputs: {param_out : ParamOut, moment_out : MomentOut} +- op: detection_map + backward: detection_map_grad + inputs: + {detect_res : DetectRes, label : Label, has_state : HasState, pos_count : PosCount, true_pos : TruePos, false_pos : FalsePos} + outputs: + {accum_pos_count : AccumPosCount, accum_true_pos : AccumTruePos, accum_false_pos : AccumFalsePos, m_ap : MAP} + - op: dgc inputs: {u: U, v: V, grad: Grad} diff --git a/paddle/phi/api/yaml/ops.yaml b/paddle/phi/api/yaml/ops.yaml index 0c77952bd9743f..52c03632e7f19e 100755 --- a/paddle/phi/api/yaml/ops.yaml +++ b/paddle/phi/api/yaml/ops.yaml @@ -806,6 +806,19 @@ func : determinant backward : det_grad +- op : detection_map + args: (Tensor detect_res, Tensor label, Tensor has_state, Tensor pos_count, Tensor + true_pos, Tensor false_pos, int class_num, int background_label = 0, float overlap_threshold + = .5f, bool evaluate_difficult = true, str ap_type = "integral") + output: Tensor (accum_pos_count), Tensor (accum_true_pos), Tensor (accum_false_pos), + Tensor (m_ap) + infer_meta: + func: DetectionMapInferMeta + kernel: + func: detection_map + data_type: detect_res + optional: has_state, pos_count, true_pos, false_pos + - op : diag args : (Tensor x, int offset = 0, float padding_value = 0.0) output : Tensor diff --git a/paddle/phi/infermeta/multiary.cc b/paddle/phi/infermeta/multiary.cc index 6cd2b9d406ffcc..c085f75796c838 100644 --- a/paddle/phi/infermeta/multiary.cc +++ b/paddle/phi/infermeta/multiary.cc @@ -1429,6 +1429,69 @@ void DeformableConvInferMeta(const MetaTensor& x, out->set_dtype(x.dtype()); } +void DetectionMapInferMeta(const MetaTensor& detect_res, + const MetaTensor& label, + const MetaTensor& has_state, + const MetaTensor& pos_count, + const MetaTensor& true_pos, + const MetaTensor& false_pos, + int class_num, + int background_label, + float overlap_threshold, + bool evaluate_difficult, + const std::string& ap_type, + MetaTensor* accum_pos_count, + MetaTensor* accum_true_pos, + MetaTensor* accum_false_pos, + MetaTensor* m_ap, + MetaConfig config) { + auto det_dims = detect_res.dims(); + PADDLE_ENFORCE_EQ(det_dims.size(), + 2UL, + phi::errors::InvalidArgument( + "Input(DetectRes) ndim must be 2, the shape is [N, 6]," + "but received the ndim is %d", + det_dims.size())); + PADDLE_ENFORCE_EQ(det_dims[1], + 6UL, + phi::errors::InvalidArgument( + "The shape is of Input(DetectRes) [N, 6], but received" + " shape is [N, %d]", + det_dims[1])); + auto label_dims = label.dims(); + PADDLE_ENFORCE_EQ(label_dims.size(), + 2, + phi::errors::InvalidArgument( + "The ndim of Input(Label) must be 2, but received %d", + label_dims.size())); + if (config.is_runtime || label_dims[1] > 0) { + PADDLE_ENFORCE_EQ( + (label_dims[1] == 6 || label_dims[1] == 5), + true, + phi::errors::InvalidArgument( + "The shape of Input(Label) is [N, 6] or [N, 5], but received " + "[N, %d]", + label_dims[1])); + } + + if (pos_count.initialized()) { + PADDLE_ENFORCE_EQ( + true_pos.initialized(), + true, + phi::errors::InvalidArgument( + "Input(TruePos) of DetectionMAPOp should not be null when " + "Input(PosCount) is not null.")); + PADDLE_ENFORCE_EQ( + false_pos.initialized(), + true, + phi::errors::InvalidArgument( + "Input(FalsePos) of DetectionMAPOp should not be null when " + "Input(PosCount) is not null.")); + } + + m_ap->set_dims(common::make_ddim({1})); +} + void DGCMomentumInferMeta(const MetaTensor& param, const MetaTensor& grad, const MetaTensor& velocity, diff --git a/paddle/phi/infermeta/multiary.h b/paddle/phi/infermeta/multiary.h index 81fc8745e4989a..6ae96b69a4de6d 100644 --- a/paddle/phi/infermeta/multiary.h +++ b/paddle/phi/infermeta/multiary.h @@ -294,6 +294,23 @@ void DeformableConvInferMeta(const MetaTensor& x, MetaTensor* out, MetaConfig config = MetaConfig()); +void DetectionMapInferMeta(const MetaTensor& detect_res, + const MetaTensor& label, + const MetaTensor& has_state, + const MetaTensor& pos_count, + const MetaTensor& true_pos, + const MetaTensor& false_pos, + int class_num, + int background_label, + float overlap_threshold, + bool evaluate_difficult, + const std::string& ap_type, + MetaTensor* accum_pos_count, + MetaTensor* accum_true_pos, + MetaTensor* accum_false_pos, + MetaTensor* m_ap, + MetaConfig config = MetaConfig()); + void DGCMomentumInferMeta(const MetaTensor& param, const MetaTensor& grad, const MetaTensor& velocity, diff --git a/paddle/phi/kernels/cpu/detection_map_kernel.cc b/paddle/phi/kernels/cpu/detection_map_kernel.cc new file mode 100644 index 00000000000000..2e1b3e8155b576 --- /dev/null +++ b/paddle/phi/kernels/cpu/detection_map_kernel.cc @@ -0,0 +1,534 @@ +// Copyright (c) 2024 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/detection_map_kernel.h" +#include +#include +#include +#include +#include + +#include "paddle/phi/core/kernel_registry.h" +#include "paddle/phi/kernels/funcs/eigen/common.h" + +namespace phi { + +enum APType { kNone = 0, kIntegral, k11point }; + +APType GetAPType(const std::string& str) { + if (str == "integral") { + return APType::kIntegral; + } else if (str == "11point") { + return APType::k11point; + } else { + return APType::kNone; + } +} + +template +inline bool SortScorePairDescend(const std::pair& pair1, + const std::pair& pair2) { + return pair1.first > pair2.first; +} + +template +inline void GetAccumulation(std::vector> in_pairs, + std::vector* accu_vec) { + std::stable_sort(in_pairs.begin(), in_pairs.end(), SortScorePairDescend); + accu_vec->clear(); + size_t sum = 0; + for (size_t i = 0; i < in_pairs.size(); ++i) { + auto count = in_pairs[i].second; + sum += count; + accu_vec->push_back(sum); + } +} + +template +struct Box { + Box(T xmin, T ymin, T xmax, T ymax) + : xmin(xmin), ymin(ymin), xmax(xmax), ymax(ymax), is_difficult(false) {} + + T xmin, ymin, xmax, ymax; + bool is_difficult; +}; + +template +inline T JaccardOverlap(const Box& box1, const Box& box2) { + if (box2.xmin > box1.xmax || box2.xmax < box1.xmin || box2.ymin > box1.ymax || + box2.ymax < box1.ymin) { + return 0.0; + } else { + T inter_xmin = std::max(box1.xmin, box2.xmin); + T inter_ymin = std::max(box1.ymin, box2.ymin); + T inter_xmax = std::min(box1.xmax, box2.xmax); + T inter_ymax = std::min(box1.ymax, box2.ymax); + + T inter_width = inter_xmax - inter_xmin; + T inter_height = inter_ymax - inter_ymin; + T inter_area = inter_width * inter_height; + + T bbox_area1 = (box1.xmax - box1.xmin) * (box1.ymax - box1.ymin); + T bbox_area2 = (box2.xmax - box2.xmin) * (box2.ymax - box2.ymin); + + return inter_area / (bbox_area1 + bbox_area2 - inter_area); + } +} + +template +inline void ClipBBox(const Box& bbox, Box* clipped_bbox) { + T one = static_cast(1.0); + T zero = static_cast(0.0); + clipped_bbox->xmin = std::max(std::min(bbox.xmin, one), zero); + clipped_bbox->ymin = std::max(std::min(bbox.ymin, one), zero); + clipped_bbox->xmax = std::max(std::min(bbox.xmax, one), zero); + clipped_bbox->ymax = std::max(std::min(bbox.ymax, one), zero); +} + +template +void GetOutputPos( + const Context& dev_ctx, + const std::map& label_pos_count, + const std::map>>& true_pos, + const std::map>>& false_pos, + phi::DenseTensor* output_pos_count, + phi::DenseTensor* output_true_pos, + phi::DenseTensor* output_false_pos, + const int class_num) { + int true_pos_count = 0; + int false_pos_count = 0; + for (auto it = true_pos.begin(); it != true_pos.end(); ++it) { + auto tp = it->second; + true_pos_count += tp.size(); + } + for (auto it = false_pos.begin(); it != false_pos.end(); ++it) { + auto fp = it->second; + false_pos_count += fp.size(); + } + + output_pos_count->Resize(common::make_ddim({class_num, 1})); + int* pos_count_data = dev_ctx.template Alloc(output_pos_count); + + output_true_pos->Resize(common::make_ddim({true_pos_count, 2})); + T* true_pos_data = dev_ctx.template Alloc(output_true_pos); + output_false_pos->Resize(common::make_ddim({false_pos_count, 2})); + T* false_pos_data = dev_ctx.template Alloc(output_false_pos); + true_pos_count = 0; + false_pos_count = 0; + std::vector true_pos_starts = {0}; + std::vector false_pos_starts = {0}; + for (int i = 0; i < class_num; ++i) { + auto it_count = label_pos_count.find(i); + pos_count_data[i] = 0; + if (it_count != label_pos_count.end()) { + pos_count_data[i] = it_count->second; + } + auto it_true_pos = true_pos.find(i); + if (it_true_pos != true_pos.end()) { + const std::vector>& true_pos_vec = it_true_pos->second; + for (const std::pair& tp : true_pos_vec) { + true_pos_data[true_pos_count * 2] = tp.first; + true_pos_data[true_pos_count * 2 + 1] = static_cast(tp.second); + true_pos_count++; + } + } + true_pos_starts.push_back(true_pos_count); + + auto it_false_pos = false_pos.find(i); + if (it_false_pos != false_pos.end()) { + const std::vector>& false_pos_vec = + it_false_pos->second; + for (const std::pair& fp : false_pos_vec) { + false_pos_data[false_pos_count * 2] = fp.first; + false_pos_data[false_pos_count * 2 + 1] = static_cast(fp.second); + false_pos_count++; + } + } + false_pos_starts.push_back(false_pos_count); + } + + phi::LoD true_pos_lod; + true_pos_lod.emplace_back(true_pos_starts); + phi::LoD false_pos_lod; + false_pos_lod.emplace_back(false_pos_starts); + + output_true_pos->set_lod(true_pos_lod); + output_false_pos->set_lod(false_pos_lod); +} + +template +void GetInputPos(const phi::DenseTensor& input_pos_count, + const phi::DenseTensor& input_true_pos, + const phi::DenseTensor& input_false_pos, + std::map* label_pos_count, + std::map>>* true_pos, + std::map>>* false_pos, + const int class_num) { + const int* pos_count_data = input_pos_count.data(); + for (int i = 0; i < class_num; ++i) { + (*label_pos_count)[i] = pos_count_data[i]; + } + + auto SetData = [](const phi::DenseTensor& pos_tensor, + std::map>>& pos) { + const T* pos_data = pos_tensor.data(); + auto& pos_data_lod = pos_tensor.lod()[0]; + for (size_t i = 0; i < pos_data_lod.size() - 1; ++i) { + for (size_t j = pos_data_lod[i]; j < pos_data_lod[i + 1]; ++j) { + T score = pos_data[j * 2]; + int flag = pos_data[j * 2 + 1]; + pos[i].push_back(std::make_pair(score, flag)); + } + } + }; + + SetData(input_true_pos, *true_pos); + SetData(input_false_pos, *false_pos); + return; +} + +template +void CalcTrueAndFalsePositive( + const std::vector>>>& gt_boxes, + const std::vector>>>>& + detect_boxes, + bool evaluate_difficult, + float overlap_threshold, + std::map* label_pos_count, + std::map>>* true_pos, + std::map>>* false_pos) { + int batch_size = gt_boxes.size(); + for (int n = 0; n < batch_size; ++n) { + auto& image_gt_boxes = gt_boxes[n]; + for (auto& image_gt_box : image_gt_boxes) { + size_t count = 0; + auto& labeled_bboxes = image_gt_box.second; + if (evaluate_difficult) { + count = labeled_bboxes.size(); + } else { + for (auto& box : labeled_bboxes) { + if (!box.is_difficult) { + ++count; + } + } + } + if (count == 0) { + continue; + } + int label = image_gt_box.first; + if (label_pos_count->find(label) == label_pos_count->end()) { + (*label_pos_count)[label] = count; + } else { + (*label_pos_count)[label] += count; + } + } + } + + for (size_t n = 0; n < detect_boxes.size(); ++n) { + auto image_gt_boxes = gt_boxes[n]; + auto detections = detect_boxes[n]; + + if (image_gt_boxes.size() == 0) { + for (auto it = detections.begin(); it != detections.end(); ++it) { + auto pred_boxes = it->second; + int label = it->first; + for (size_t i = 0; i < pred_boxes.size(); ++i) { + auto score = pred_boxes[i].first; + (*true_pos)[label].push_back(std::make_pair(score, 0)); + (*false_pos)[label].push_back(std::make_pair(score, 1)); + } + } + continue; + } + + for (auto it = detections.begin(); it != detections.end(); ++it) { + int label = it->first; + auto pred_boxes = it->second; + if (image_gt_boxes.find(label) == image_gt_boxes.end()) { + for (size_t i = 0; i < pred_boxes.size(); ++i) { + auto score = pred_boxes[i].first; + (*true_pos)[label].push_back(std::make_pair(score, 0)); + (*false_pos)[label].push_back(std::make_pair(score, 1)); + } + continue; + } + + auto matched_bboxes = image_gt_boxes.find(label)->second; + std::vector visited(matched_bboxes.size(), false); + // Sort detections in descend order based on scores + std::sort( + pred_boxes.begin(), pred_boxes.end(), SortScorePairDescend>); + for (size_t i = 0; i < pred_boxes.size(); ++i) { + T max_overlap = -1.0; + size_t max_idx = 0; + auto score = pred_boxes[i].first; + for (size_t j = 0; j < matched_bboxes.size(); ++j) { + Box& pred_box = pred_boxes[i].second; + ClipBBox(pred_box, &pred_box); + T overlap = JaccardOverlap(pred_box, matched_bboxes[j]); + if (overlap > max_overlap) { + max_overlap = overlap; + max_idx = j; + } + } + if (max_overlap > overlap_threshold) { + bool match_evaluate_difficult = + evaluate_difficult || + (!evaluate_difficult && !matched_bboxes[max_idx].is_difficult); + if (match_evaluate_difficult) { + if (!visited[max_idx]) { + (*true_pos)[label].push_back(std::make_pair(score, 1)); + (*false_pos)[label].push_back(std::make_pair(score, 0)); + visited[max_idx] = true; + } else { + (*true_pos)[label].push_back(std::make_pair(score, 0)); + (*false_pos)[label].push_back(std::make_pair(score, 1)); + } + } + } else { + (*true_pos)[label].push_back(std::make_pair(score, 0)); + (*false_pos)[label].push_back(std::make_pair(score, 1)); + } + } + } + } +} + +template +T CalcMAP(APType ap_type, + const std::map& label_pos_count, + const std::map>>& true_pos, + const std::map>>& false_pos, + const int background_label) { + T mAP = 0.0; + int count = 0; + for (auto it = label_pos_count.begin(); it != label_pos_count.end(); ++it) { + int label = it->first; + int label_num_pos = it->second; + if (label_num_pos == background_label) { + continue; + } + if (true_pos.find(label) == true_pos.end()) { + count++; + continue; + } + auto label_true_pos = true_pos.find(label)->second; + auto label_false_pos = false_pos.find(label)->second; + // Compute average precision. + std::vector tp_sum; + GetAccumulation(label_true_pos, &tp_sum); + std::vector fp_sum; + GetAccumulation(label_false_pos, &fp_sum); + std::vector precision, recall; + size_t num = tp_sum.size(); + // Compute Precision. + for (size_t i = 0; i < num; ++i) { + precision.push_back(static_cast(tp_sum[i]) / + static_cast(tp_sum[i] + fp_sum[i])); + recall.push_back(static_cast(tp_sum[i]) / label_num_pos); + } + // VOC2007 style + if (ap_type == APType::k11point) { + std::vector max_precisions(11, 0.0); + int start_idx = num - 1; + for (int j = 10; j >= 0; --j) + for (int i = start_idx; i >= 0; --i) { + if (recall[i] < j / 10.) { + start_idx = i; + if (j > 0) max_precisions[j - 1] = max_precisions[j]; + break; + } else { + if (max_precisions[j] < precision[i]) + max_precisions[j] = precision[i]; + } + } + for (int j = 10; j >= 0; --j) mAP += max_precisions[j] / 11; + ++count; + } else if (ap_type == APType::kIntegral) { + // Nature integral + float average_precisions = 0.; + float prev_recall = 0.; + for (size_t i = 0; i < num; ++i) { + if (fabs(recall[i] - prev_recall) > 1e-6) + average_precisions += precision[i] * fabs(recall[i] - prev_recall); + prev_recall = recall[i]; + } + mAP += average_precisions; + ++count; + } else { + PADDLE_THROW(phi::errors::Unimplemented( + "Unkown ap version %s. Now only supports integral and l1point.", + ap_type)); + } + } + if (count != 0) mAP /= count; + return mAP; +} + +template +void GetBoxes(const phi::DenseTensor& input_label, + const phi::DenseTensor& input_detect, + std::vector>>>* gt_boxes, + std::vector>>>>& + detect_boxes) { + auto labels = phi::EigenTensor::From(input_label); + auto detect = phi::EigenTensor::From(input_detect); + + auto& label_lod = input_label.lod(); + auto& detect_lod = input_detect.lod(); + + int batch_size = label_lod[0].size() - 1; + auto& label_index = label_lod[0]; + + for (int n = 0; n < batch_size; ++n) { + std::map>> boxes; + for (size_t i = label_index[n]; i < label_index[n + 1]; ++i) { + int label = labels(i, 0); + if (input_label.dims()[1] == 6) { + Box box(labels(i, 2), labels(i, 3), labels(i, 4), labels(i, 5)); + auto is_difficult = labels(i, 1); + if (std::abs(is_difficult - 0.0) < 1e-6) + box.is_difficult = false; + else + box.is_difficult = true; + boxes[label].push_back(box); + } else { + PADDLE_ENFORCE_EQ( + input_label.dims()[1], + 5, + phi::errors::InvalidArgument( + "The input label width" + " must be 5, but received %d, please check your input data", + input_label.dims()[1])); + Box box(labels(i, 1), labels(i, 2), labels(i, 3), labels(i, 4)); + boxes[label].push_back(box); + } + } + gt_boxes->push_back(boxes); + } + + auto detect_index = detect_lod[0]; + for (int n = 0; n < batch_size; ++n) { + std::map>>> boxes; + for (size_t i = detect_index[n]; i < detect_index[n + 1]; ++i) { + Box box(detect(i, 2), detect(i, 3), detect(i, 4), detect(i, 5)); + int label = detect(i, 0); + auto score = detect(i, 1); + boxes[label].push_back(std::make_pair(score, box)); + } + detect_boxes.push_back(boxes); + } +} + +template +void DetectionMAPOpKernel(const Context& dev_ctx, + const DenseTensor& detect_res, + const DenseTensor& label, + const paddle::optional& has_state, + const paddle::optional& pos_count, + const paddle::optional& true_pos, + const paddle::optional& false_pos, + int class_num, + int background_label, + float overlap_threshold, + bool evaluate_difficult, + const std::string& ap_type, + DenseTensor* accum_pos_count, + DenseTensor* accum_true_pos, + DenseTensor* accum_false_pos, + DenseTensor* m_ap) { + auto* in_detect = &detect_res; + auto* in_label = &label; + auto* out_map = m_ap; + + auto* in_pos_count = pos_count.get_ptr(); + auto* in_true_pos = true_pos.get_ptr(); + auto* in_false_pos = false_pos.get_ptr(); + + auto* out_pos_count = accum_pos_count; + auto* out_true_pos = accum_true_pos; + auto* out_false_pos = accum_false_pos; + + auto& label_lod = in_label->lod(); + auto& detect_lod = in_detect->lod(); + PADDLE_ENFORCE_EQ( + label_lod.size(), + 1UL, + phi::errors::InvalidArgument("Only support LodTensor of lod_level " + "with 1 in label, but received %d.", + label_lod.size())); + PADDLE_ENFORCE_EQ(label_lod[0].size(), + detect_lod[0].size(), + phi::errors::InvalidArgument( + "The batch_size of input(Label) and input(Detection) " + "must be the same, but received %d:%d", + label_lod[0].size(), + detect_lod[0].size())); + + std::vector>>> gt_boxes; + std::vector>>>> detect_boxes; + + GetBoxes(*in_label, *in_detect, >_boxes, detect_boxes); + + std::map label_pos_count; + std::map>> true_pos_map; + std::map>> false_pos_map; + + auto* has_state_p = has_state.get_ptr(); + int state = 0; + if (has_state_p != nullptr) { + state = has_state_p->data()[0]; + } + + if (in_pos_count != nullptr && state) { + GetInputPos(*in_pos_count, + *in_true_pos, + *in_false_pos, + &label_pos_count, + &true_pos_map, + &false_pos_map, + class_num); + } + + CalcTrueAndFalsePositive(gt_boxes, + detect_boxes, + evaluate_difficult, + overlap_threshold, + &label_pos_count, + &true_pos_map, + &false_pos_map); + + auto ap_type_enum = GetAPType(ap_type); + T map = CalcMAP(ap_type_enum, + label_pos_count, + true_pos_map, + false_pos_map, + background_label); + + GetOutputPos(dev_ctx, + label_pos_count, + true_pos_map, + false_pos_map, + out_pos_count, + out_true_pos, + out_false_pos, + class_num); + + T* map_data = dev_ctx.template Alloc(out_map); + map_data[0] = map; +} + +} // namespace phi +PD_REGISTER_KERNEL( + detection_map, CPU, ALL_LAYOUT, phi::DetectionMAPOpKernel, float, double) {} diff --git a/paddle/phi/kernels/detection_map_kernel.h b/paddle/phi/kernels/detection_map_kernel.h new file mode 100644 index 00000000000000..7f0b892de66789 --- /dev/null +++ b/paddle/phi/kernels/detection_map_kernel.h @@ -0,0 +1,38 @@ +// Copyright (c) 2024 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. + +#pragma once +#include "paddle/phi/backends/all_context.h" +#include "paddle/phi/core/dense_tensor.h" +namespace phi { + +template +void DetectionMAPOpKernel(const Context& dev_ctx, + const DenseTensor& detect_res, + const DenseTensor& label, + const paddle::optional& has_state, + const paddle::optional& pos_count, + const paddle::optional& true_pos, + const paddle::optional& false_pos, + int class_num, + int background_label, + float overlap_threshold, + bool evaluate_difficult, + const std::string& ap_type, + DenseTensor* accum_pos_count, + DenseTensor* accum_true_pos, + DenseTensor* accum_false_pos, + DenseTensor* m_ap); + +} // namespace phi