1 #ifndef CAFFE2_OPERATORS_UTILS_NMS_H_ 2 #define CAFFE2_OPERATORS_UTILS_NMS_H_ 6 #include "caffe2/core/logging.h" 7 #include "caffe2/core/macros.h" 8 #include "caffe2/utils/eigen_utils.h" 9 #include "caffe2/utils/math.h" 24 template <
class Derived1,
class Derived2>
25 std::vector<int> nms_cpu_upright(
26 const Eigen::ArrayBase<Derived1>& proposals,
27 const Eigen::ArrayBase<Derived2>& scores,
28 const std::vector<int>& sorted_indices,
31 CAFFE_ENFORCE_EQ(proposals.rows(), scores.rows());
32 CAFFE_ENFORCE_EQ(proposals.cols(), 4);
33 CAFFE_ENFORCE_EQ(scores.cols(), 1);
34 CAFFE_ENFORCE_LE(sorted_indices.size(), proposals.rows());
36 using EArrX = EArrXt<typename Derived1::Scalar>;
38 auto x1 = proposals.col(0);
39 auto y1 = proposals.col(1);
40 auto x2 = proposals.col(2);
41 auto y2 = proposals.col(3);
43 EArrX areas = (x2 - x1 + 1.0) * (y2 - y1 + 1.0);
45 EArrXi order = AsEArrXt(sorted_indices);
46 std::vector<int> keep;
47 while (order.size() > 0) {
49 if (topN >= 0 && keep.size() >= topN) {
55 ConstEigenVectorArrayMap<int> rest_indices(
56 order.data() + 1, order.size() - 1);
57 EArrX xx1 = GetSubArray(x1, rest_indices).cwiseMax(x1[i]);
58 EArrX yy1 = GetSubArray(y1, rest_indices).cwiseMax(y1[i]);
59 EArrX xx2 = GetSubArray(x2, rest_indices).cwiseMin(x2[i]);
60 EArrX yy2 = GetSubArray(y2, rest_indices).cwiseMin(y2[i]);
62 EArrX w = (xx2 - xx1 + 1.0).cwiseMax(0.0);
63 EArrX h = (yy2 - yy1 + 1.0).cwiseMax(0.0);
65 EArrX ovr = inter / (areas[i] + GetSubArray(areas, rest_indices) - inter);
68 auto inds = GetArrayIndices(ovr <= thresh);
69 order = GetSubArray(order, AsEArrXt(inds) + 1);
91 template <
class Derived1,
class Derived2,
class Derived3>
92 std::vector<int> soft_nms_cpu_upright(
93 Eigen::ArrayBase<Derived3>* out_scores,
94 const Eigen::ArrayBase<Derived1>& proposals,
95 const Eigen::ArrayBase<Derived2>& scores,
96 const std::vector<int>& indices,
98 float overlap_thresh = 0.3,
99 float score_thresh = 0.001,
100 unsigned int method = 1,
102 CAFFE_ENFORCE_EQ(proposals.rows(), scores.rows());
103 CAFFE_ENFORCE_EQ(proposals.cols(), 4);
104 CAFFE_ENFORCE_EQ(scores.cols(), 1);
106 using EArrX = EArrXt<typename Derived1::Scalar>;
108 const auto& x1 = proposals.col(0);
109 const auto& y1 = proposals.col(1);
110 const auto& x2 = proposals.col(2);
111 const auto& y2 = proposals.col(3);
113 EArrX areas = (x2 - x1 + 1.0) * (y2 - y1 + 1.0);
117 *out_scores = scores;
119 std::vector<int> keep;
120 EArrXi pending = AsEArrXt(indices);
121 while (pending.size() > 0) {
123 if (topN >= 0 && keep.size() >= topN) {
129 auto max_score = GetSubArray(*out_scores, pending).maxCoeff(&max_pos);
130 int i = pending[max_pos];
134 std::swap(pending(0), pending(max_pos));
135 const auto& rest_indices = pending.tail(pending.size() - 1);
136 EArrX xx1 = GetSubArray(x1, rest_indices).cwiseMax(x1[i]);
137 EArrX yy1 = GetSubArray(y1, rest_indices).cwiseMax(y1[i]);
138 EArrX xx2 = GetSubArray(x2, rest_indices).cwiseMin(x2[i]);
139 EArrX yy2 = GetSubArray(y2, rest_indices).cwiseMin(y2[i]);
141 EArrX w = (xx2 - xx1 + 1.0).cwiseMax(0.0);
142 EArrX h = (yy2 - yy1 + 1.0).cwiseMax(0.0);
144 EArrX ovr = inter / (areas[i] + GetSubArray(areas, rest_indices) - inter);
147 for (
int j = 0; j < rest_indices.size(); ++j) {
148 typename Derived2::Scalar weight;
151 weight = (ovr(j) > overlap_thresh) ? (1.0 - ovr(j)) : 1.0;
154 weight = std::exp(-1.0 * ovr(j) * ovr(j) / sigma);
157 weight = (ovr(j) > overlap_thresh) ? 0.0 : 1.0;
159 (*out_scores)(rest_indices[j]) *= weight;
164 const auto& rest_scores = GetSubArray(*out_scores, rest_indices);
165 const auto& inds = GetArrayIndices(rest_scores >= score_thresh);
166 pending = GetSubArray(rest_indices, AsEArrXt(inds));
172 #if defined(CV_MAJOR_VERSION) && (CV_MAJOR_VERSION >= 3) 175 template <
class Derived>
176 cv::RotatedRect bbox_to_rotated_rect(
const Eigen::ArrayBase<Derived>& box) {
177 CAFFE_ENFORCE_EQ(box.size(), 5);
180 return cv::RotatedRect(
181 cv::Point2f(box[0], box[1]), cv::Size2f(box[2], box[3]), -box[4]);
191 int cvfix_rotatedRectangleIntersection(
192 const cv::RotatedRect& rect1,
193 const cv::RotatedRect& rect2,
194 cv::OutputArray intersectingRegion) {
196 const float samePointEps = 0.00001f;
197 const float EPS = 1e-14;
199 cv::Point2f vec1[4], vec2[4];
200 cv::Point2f pts1[4], pts2[4];
202 std::vector<cv::Point2f> intersection;
207 int ret = cv::INTERSECT_FULL;
213 for (
int i = 0; i < 4; i++) {
214 if (fabs(pts1[i].x - pts2[i].x) > samePointEps ||
215 (fabs(pts1[i].y - pts2[i].y) > samePointEps)) {
222 intersection.resize(4);
224 for (
int i = 0; i < 4; i++) {
225 intersection[i] = pts1[i];
228 cv::Mat(intersection).copyTo(intersectingRegion);
230 return cv::INTERSECT_FULL;
236 for (
int i = 0; i < 4; i++) {
237 vec1[i].x = pts1[(i + 1) % 4].x - pts1[i].x;
238 vec1[i].y = pts1[(i + 1) % 4].y - pts1[i].y;
240 vec2[i].x = pts2[(i + 1) % 4].x - pts2[i].x;
241 vec2[i].y = pts2[(i + 1) % 4].y - pts2[i].y;
245 for (
int i = 0; i < 4; i++) {
246 for (
int j = 0; j < 4; j++) {
248 float x21 = pts2[j].x - pts1[i].x;
249 float y21 = pts2[j].y - pts1[i].y;
251 const auto& l1 = vec1[i];
252 const auto& l2 = vec2[j];
255 float det = l2.x * l1.y - l1.x * l2.y;
256 if (std::fabs(det) <= EPS) {
260 float t1 = (l2.x * y21 - l2.y * x21) / det;
261 float t2 = (l1.x * y21 - l1.y * x21) / det;
263 if (t1 >= 0.0f && t1 <= 1.0f && t2 >= 0.0f && t2 <= 1.0f) {
264 float xi = pts1[i].x + vec1[i].x * t1;
265 float yi = pts1[i].y + vec1[i].y * t1;
266 intersection.push_back(cv::Point2f(xi, yi));
271 if (!intersection.empty()) {
272 ret = cv::INTERSECT_PARTIAL;
276 for (
int i = 0; i < 4; i++) {
286 for (
int j = 0; j < 4; j++) {
297 double A = -vec2[j].y;
298 double B = vec2[j].x;
299 double C = -(A * pts2[j].x + B * pts2[j].y);
300 double s = A * x + B * y + C;
309 if (posSign == 4 || negSign == 4) {
310 intersection.push_back(pts1[i]);
315 for (
int i = 0; i < 4; i++) {
325 for (
int j = 0; j < 4; j++) {
336 double A = -vec1[j].y;
337 double B = vec1[j].x;
338 double C = -(A * pts1[j].x + B * pts1[j].y);
339 double s = A * x + B * y + C;
348 if (posSign == 4 || negSign == 4) {
349 intersection.push_back(pts2[i]);
354 for (
int i = 0; i < (int)intersection.size() - 1; i++) {
355 for (
size_t j = i + 1; j < intersection.size(); j++) {
356 float dx = intersection[i].x - intersection[j].x;
357 float dy = intersection[i].y - intersection[j].y;
359 double d2 = dx * dx + dy * dy;
361 if (d2 < samePointEps * samePointEps) {
363 std::swap(intersection[j], intersection.back());
364 intersection.pop_back();
370 if (intersection.empty()) {
371 return cv::INTERSECT_NONE;
386 if (intersection.size() > 8) {
387 LOG(ERROR) <<
"Intersection size = " << intersection.size();
388 LOG(ERROR) <<
"Rect 1:";
389 for (
int i = 0; i < 4; i++) {
390 LOG(ERROR) <<
" (" << pts1[i].x <<
" ," << pts1[i].y <<
"),";
392 LOG(ERROR) <<
"Rect 2:";
393 for (
int i = 0; i < 4; i++) {
394 LOG(ERROR) <<
" (" << pts2[i].x <<
" ," << pts2[i].y <<
"),";
396 LOG(ERROR) <<
"Intersections:";
397 for (
auto& p : intersection) {
398 LOG(ERROR) <<
" (" << p.x <<
" ," << p.y <<
"),";
402 cv::Mat(intersection).copyTo(intersectingRegion);
410 double rotated_rect_intersection(
411 const cv::RotatedRect& rect1,
412 const cv::RotatedRect& rect2) {
413 std::vector<cv::Point2f> intersectPts, orderedPts;
425 auto ret = cvfix_rotatedRectangleIntersection(rect1, rect2, intersectPts);
426 if (intersectPts.size() <= 2) {
432 if (ret == cv::INTERSECT_FULL) {
433 return std::min(rect1.size.area(), rect2.size.area());
438 cv::convexHull(intersectPts, orderedPts);
439 return cv::contourArea(orderedPts);
449 template <
class Derived1,
class Derived2>
450 double bbox_intersection_rotated(
451 const Eigen::ArrayBase<Derived1>& box1,
452 const Eigen::ArrayBase<Derived2>& box2) {
453 CAFFE_ENFORCE(box1.size() == 5 && box2.size() == 5);
454 const auto& rect1 = bbox_to_rotated_rect(box1);
455 const auto& rect2 = bbox_to_rotated_rect(box2);
456 return rotated_rect_intersection(rect1, rect2);
465 template <
class Derived1,
class Derived2>
466 Eigen::ArrayXXf bbox_overlaps_rotated(
467 const Eigen::ArrayBase<Derived1>& boxes,
468 const Eigen::ArrayBase<Derived2>& query_boxes) {
469 CAFFE_ENFORCE(boxes.cols() == 5 && query_boxes.cols() == 5);
471 const auto& boxes_areas = boxes.col(2) * boxes.col(3);
472 const auto& query_boxes_areas = query_boxes.col(2) * query_boxes.col(3);
474 Eigen::ArrayXXf overlaps(boxes.rows(), query_boxes.rows());
475 for (
int i = 0; i < boxes.rows(); ++i) {
476 for (
int j = 0; j < query_boxes.rows(); ++j) {
477 auto inter = bbox_intersection_rotated(boxes.row(i), query_boxes.row(j));
478 overlaps(i, j) = (inter == 0.0)
480 : inter / (boxes_areas[i] + query_boxes_areas[j] - inter);
492 template <
class Derived1,
class Derived2>
493 std::vector<int> nms_cpu_rotated(
494 const Eigen::ArrayBase<Derived1>& proposals,
495 const Eigen::ArrayBase<Derived2>& scores,
496 const std::vector<int>& sorted_indices,
499 CAFFE_ENFORCE_EQ(proposals.rows(), scores.rows());
500 CAFFE_ENFORCE_EQ(proposals.cols(), 5);
501 CAFFE_ENFORCE_EQ(scores.cols(), 1);
502 CAFFE_ENFORCE_LE(sorted_indices.size(), proposals.rows());
504 using EArrX = EArrXt<typename Derived1::Scalar>;
506 auto widths = proposals.col(2);
507 auto heights = proposals.col(3);
508 EArrX areas = widths * heights;
510 std::vector<cv::RotatedRect> rotated_rects(proposals.rows());
511 for (
int i = 0; i < proposals.rows(); ++i) {
512 rotated_rects[i] = bbox_to_rotated_rect(proposals.row(i));
515 EArrXi order = AsEArrXt(sorted_indices);
516 std::vector<int> keep;
517 while (order.size() > 0) {
519 if (topN >= 0 && keep.size() >= topN) {
525 ConstEigenVectorArrayMap<int> rest_indices(
526 order.data() + 1, order.size() - 1);
528 EArrX inter(rest_indices.size());
529 for (
int j = 0; j < rest_indices.size(); ++j) {
530 inter[j] = rotated_rect_intersection(
531 rotated_rects[i], rotated_rects[rest_indices[j]]);
533 EArrX ovr = inter / (areas[i] + GetSubArray(areas, rest_indices) - inter);
537 auto inds = GetArrayIndices(ovr <= thresh);
538 order = GetSubArray(order, AsEArrXt(inds) + 1);
550 template <
class Derived1,
class Derived2,
class Derived3>
551 std::vector<int> soft_nms_cpu_rotated(
552 Eigen::ArrayBase<Derived3>* out_scores,
553 const Eigen::ArrayBase<Derived1>& proposals,
554 const Eigen::ArrayBase<Derived2>& scores,
555 const std::vector<int>& indices,
557 float overlap_thresh = 0.3,
558 float score_thresh = 0.001,
559 unsigned int method = 1,
561 CAFFE_ENFORCE_EQ(proposals.rows(), scores.rows());
562 CAFFE_ENFORCE_EQ(proposals.cols(), 5);
563 CAFFE_ENFORCE_EQ(scores.cols(), 1);
565 using EArrX = EArrXt<typename Derived1::Scalar>;
567 auto widths = proposals.col(2);
568 auto heights = proposals.col(3);
569 EArrX areas = widths * heights;
571 std::vector<cv::RotatedRect> rotated_rects(proposals.rows());
572 for (
int i = 0; i < proposals.rows(); ++i) {
573 rotated_rects[i] = bbox_to_rotated_rect(proposals.row(i));
578 *out_scores = scores;
580 std::vector<int> keep;
581 EArrXi pending = AsEArrXt(indices);
582 while (pending.size() > 0) {
584 if (topN >= 0 && keep.size() >= topN) {
590 auto max_score = GetSubArray(*out_scores, pending).maxCoeff(&max_pos);
591 int i = pending[max_pos];
595 std::swap(pending(0), pending(max_pos));
596 const auto& rest_indices = pending.tail(pending.size() - 1);
597 EArrX inter(rest_indices.size());
598 for (
int j = 0; j < rest_indices.size(); ++j) {
599 inter[j] = rotated_rect_intersection(
600 rotated_rects[i], rotated_rects[rest_indices[j]]);
602 EArrX ovr = inter / (areas[i] + GetSubArray(areas, rest_indices) - inter);
606 for (
int j = 0; j < rest_indices.size(); ++j) {
607 typename Derived2::Scalar weight;
610 weight = (ovr(j) > overlap_thresh) ? (1.0 - ovr(j)) : 1.0;
613 weight = std::exp(-1.0 * ovr(j) * ovr(j) / sigma);
616 weight = (ovr(j) > overlap_thresh) ? 0.0 : 1.0;
618 (*out_scores)(rest_indices[j]) *= weight;
623 const auto& rest_scores = GetSubArray(*out_scores, rest_indices);
624 const auto& inds = GetArrayIndices(rest_scores >= score_thresh);
625 pending = GetSubArray(rest_indices, AsEArrXt(inds));
630 #endif // CV_MAJOR_VERSION >= 3 632 template <
class Derived1,
class Derived2>
633 std::vector<int> nms_cpu(
634 const Eigen::ArrayBase<Derived1>& proposals,
635 const Eigen::ArrayBase<Derived2>& scores,
636 const std::vector<int>& sorted_indices,
639 #if defined(CV_MAJOR_VERSION) && (CV_MAJOR_VERSION >= 3) 640 CAFFE_ENFORCE(proposals.cols() == 4 || proposals.cols() == 5);
641 if (proposals.cols() == 4) {
643 return nms_cpu_upright(proposals, scores, sorted_indices, thresh, topN);
646 return nms_cpu_rotated(proposals, scores, sorted_indices, thresh, topN);
649 return nms_cpu_upright(proposals, scores, sorted_indices, thresh, topN);
650 #endif // CV_MAJOR_VERSION >= 3 663 template <
class Derived1,
class Derived2>
664 std::vector<int> nms_cpu(
665 const Eigen::ArrayBase<Derived1>& proposals,
666 const Eigen::ArrayBase<Derived2>& scores,
668 std::vector<int> indices(proposals.rows());
669 std::iota(indices.begin(), indices.end(), 0);
672 indices.data() + indices.size(),
673 [&scores](
int lhs,
int rhs) {
return scores(lhs) > scores(rhs); });
675 return nms_cpu(proposals, scores, indices, thres);
678 template <
class Derived1,
class Derived2,
class Derived3>
679 std::vector<int> soft_nms_cpu(
680 Eigen::ArrayBase<Derived3>* out_scores,
681 const Eigen::ArrayBase<Derived1>& proposals,
682 const Eigen::ArrayBase<Derived2>& scores,
683 const std::vector<int>& indices,
685 float overlap_thresh = 0.3,
686 float score_thresh = 0.001,
687 unsigned int method = 1,
689 #if defined(CV_MAJOR_VERSION) && (CV_MAJOR_VERSION >= 3) 690 CAFFE_ENFORCE(proposals.cols() == 4 || proposals.cols() == 5);
691 if (proposals.cols() == 4) {
693 return soft_nms_cpu_upright(
705 return soft_nms_cpu_rotated(
717 return soft_nms_cpu_upright(
727 #endif // CV_MAJOR_VERSION >= 3 730 template <
class Derived1,
class Derived2,
class Derived3>
731 std::vector<int> soft_nms_cpu(
732 Eigen::ArrayBase<Derived3>* out_scores,
733 const Eigen::ArrayBase<Derived1>& proposals,
734 const Eigen::ArrayBase<Derived2>& scores,
736 float overlap_thresh = 0.3,
737 float score_thresh = 0.001,
738 unsigned int method = 1,
740 std::vector<int> indices(proposals.rows());
741 std::iota(indices.begin(), indices.end(), 0);
757 #endif // CAFFE2_OPERATORS_UTILS_NMS_H_
A global dictionary that holds information about what Caffe2 modules have been loaded in the current ...