| #ifndef CAFFE2_OPERATORS_GENERATE_PROPOSALS_OP_H_ |
| #define CAFFE2_OPERATORS_GENERATE_PROPOSALS_OP_H_ |
| |
| #include "caffe2/core/context.h" |
| #include "caffe2/core/export_caffe2_op_to_c10.h" |
| #include "caffe2/core/operator.h" |
| #include "caffe2/utils/eigen_utils.h" |
| #include "caffe2/utils/math.h" |
| |
| C10_DECLARE_EXPORT_CAFFE2_OP_TO_C10(GenerateProposals); |
| |
| namespace caffe2 { |
| |
| namespace utils { |
| |
| // A sub tensor view |
| // TODO: Remove??? |
| template <class T> |
| class ConstTensorView { |
| public: |
| ConstTensorView(const T* data, const std::vector<int>& dims) |
| : data_(data), dims_(dims) {} |
| |
| int ndim() const { |
| return dims_.size(); |
| } |
| const std::vector<int>& dims() const { |
| return dims_; |
| } |
| int dim(int i) const { |
| TORCH_DCHECK_LE(i, dims_.size()); |
| return dims_[i]; |
| } |
| const T* data() const { |
| return data_; |
| } |
| size_t size() const { |
| return std::accumulate( |
| dims_.begin(), dims_.end(), 1, std::multiplies<size_t>()); |
| } |
| |
| private: |
| const T* data_ = nullptr; |
| std::vector<int> dims_; |
| }; |
| |
| // Generate a list of bounding box shapes for each pixel based on predefined |
| // bounding box shapes 'anchors'. |
| // anchors: predefined anchors, size(A, 4) |
| // Return: all_anchors_vec: (H * W, A * 4) |
| // Need to reshape to (H * W * A, 4) to match the format in python |
| TORCH_API ERMatXf ComputeAllAnchors( |
| const TensorCPU& anchors, |
| int height, |
| int width, |
| float feat_stride); |
| |
| // Like ComputeAllAnchors, but instead of computing anchors for every single |
| // spatial location, only computes anchors for the already sorted and filtered |
| // positions after NMS is applied to avoid unnecessary computation. |
| // `order` is a raveled array of sorted indices in (A, H, W) format. |
| TORCH_API ERArrXXf ComputeSortedAnchors( |
| const Eigen::Map<const ERArrXXf>& anchors, |
| int height, |
| int width, |
| float feat_stride, |
| const vector<int>& order); |
| |
| } // namespace utils |
| |
| // C++ implementation of GenerateProposalsOp |
| // Generate bounding box proposals for Faster RCNN. The proposals are generated |
| // for a list of images based on image score 'score', bounding box |
| // regression result 'deltas' as well as predefined bounding box shapes |
| // 'anchors'. Greedy non-maximum suppression is applied to generate the |
| // final bounding boxes. |
| // Reference: facebookresearch/Detectron/detectron/ops/generate_proposals.py |
| template <class Context> |
| class GenerateProposalsOp final : public Operator<Context> { |
| public: |
| USE_OPERATOR_CONTEXT_FUNCTIONS; |
| template <class... Args> |
| explicit GenerateProposalsOp(Args&&... args) |
| : Operator<Context>(std::forward<Args>(args)...), |
| spatial_scale_( |
| this->template GetSingleArgument<float>("spatial_scale", 1.0 / 16)), |
| feat_stride_(1.0 / spatial_scale_), |
| rpn_pre_nms_topN_( |
| this->template GetSingleArgument<int>("pre_nms_topN", 6000)), |
| rpn_post_nms_topN_( |
| this->template GetSingleArgument<int>("post_nms_topN", 300)), |
| rpn_nms_thresh_( |
| this->template GetSingleArgument<float>("nms_thresh", 0.7f)), |
| rpn_min_size_(this->template GetSingleArgument<float>("min_size", 16)), |
| angle_bound_on_( |
| this->template GetSingleArgument<bool>("angle_bound_on", true)), |
| angle_bound_lo_( |
| this->template GetSingleArgument<int>("angle_bound_lo", -90)), |
| angle_bound_hi_( |
| this->template GetSingleArgument<int>("angle_bound_hi", 90)), |
| clip_angle_thresh_( |
| this->template GetSingleArgument<float>("clip_angle_thresh", 1.0)), |
| legacy_plus_one_( |
| this->template GetSingleArgument<bool>("legacy_plus_one", true)) {} |
| |
| ~GenerateProposalsOp() override {} |
| |
| bool RunOnDevice() override; |
| |
| // Generate bounding box proposals for a given image |
| // im_info: [height, width, im_scale] |
| // all_anchors: (H * W * A, 4) |
| // bbox_deltas_tensor: (4 * A, H, W) |
| // scores_tensor: (A, H, W) |
| // out_boxes: (n, 5) |
| // out_probs: n |
| void ProposalsForOneImage( |
| const Eigen::Array3f& im_info, |
| const Eigen::Map<const ERArrXXf>& anchors, |
| const utils::ConstTensorView<float>& bbox_deltas_tensor, |
| const utils::ConstTensorView<float>& scores_tensor, |
| ERArrXXf* out_boxes, |
| EArrXf* out_probs) const; |
| |
| protected: |
| // spatial_scale_ must be declared before feat_stride_ |
| float spatial_scale_{1.0}; |
| float feat_stride_{1.0}; |
| |
| // RPN_PRE_NMS_TOP_N |
| int rpn_pre_nms_topN_{6000}; |
| // RPN_POST_NMS_TOP_N |
| int rpn_post_nms_topN_{300}; |
| // RPN_NMS_THRESH |
| float rpn_nms_thresh_{0.7}; |
| // RPN_MIN_SIZE |
| float rpn_min_size_{16}; |
| // If set, for rotated boxes in RRPN, output angles are normalized to be |
| // within [angle_bound_lo, angle_bound_hi]. |
| bool angle_bound_on_{true}; |
| int angle_bound_lo_{-90}; |
| int angle_bound_hi_{90}; |
| // For RRPN, clip almost horizontal boxes within this threshold of |
| // tolerance for backward compatibility. Set to negative value for |
| // no clipping. |
| float clip_angle_thresh_{1.0}; |
| // The infamous "+ 1" for box width and height dating back to the DPM days |
| bool legacy_plus_one_{true}; |
| |
| // Scratch space required by the CUDA version |
| // CUB buffers |
| Tensor dev_cub_sort_buffer_{Context::GetDeviceType()}; |
| Tensor dev_cub_select_buffer_{Context::GetDeviceType()}; |
| Tensor dev_image_offset_{Context::GetDeviceType()}; |
| Tensor dev_conv_layer_indexes_{Context::GetDeviceType()}; |
| Tensor dev_sorted_conv_layer_indexes_{Context::GetDeviceType()}; |
| Tensor dev_sorted_scores_{Context::GetDeviceType()}; |
| Tensor dev_boxes_{Context::GetDeviceType()}; |
| Tensor dev_boxes_keep_flags_{Context::GetDeviceType()}; |
| |
| // prenms proposals (raw proposals minus empty boxes) |
| Tensor dev_image_prenms_boxes_{Context::GetDeviceType()}; |
| Tensor dev_image_prenms_scores_{Context::GetDeviceType()}; |
| Tensor dev_prenms_nboxes_{Context::GetDeviceType()}; |
| Tensor host_prenms_nboxes_{CPU}; |
| |
| Tensor dev_image_boxes_keep_list_{Context::GetDeviceType()}; |
| |
| // Tensors used by NMS |
| Tensor dev_nms_mask_{Context::GetDeviceType()}; |
| Tensor host_nms_mask_{CPU}; |
| |
| // Buffer for output |
| Tensor dev_postnms_rois_{Context::GetDeviceType()}; |
| Tensor dev_postnms_rois_probs_{Context::GetDeviceType()}; |
| }; |
| |
| } // namespace caffe2 |
| |
| #endif // CAFFE2_OPERATORS_GENERATE_PROPOSALS_OP_H_ |