dnn: make NMS function public
authorVladislav Sovrasov <sovrasov.vlad@gmail.com>
Mon, 16 Oct 2017 16:16:52 +0000 (19:16 +0300)
committerVladislav Sovrasov <sovrasov.vlad@gmail.com>
Wed, 25 Oct 2017 10:35:49 +0000 (13:35 +0300)
modules/dnn/include/opencv2/dnn/dnn.hpp
modules/dnn/include/opencv2/dnn/nms.inl.hpp [new file with mode: 0644]
modules/dnn/src/layers/detection_output_layer.cpp
modules/dnn/src/nms.cpp [new file with mode: 0644]

index ff222d7..d276abf 100644 (file)
@@ -734,6 +734,19 @@ CV__DNN_EXPERIMENTAL_NS_BEGIN
      */
     CV_EXPORTS_W void shrinkCaffeModel(const String& src, const String& dst);
 
+    /** @brief
+     * @param bboxes
+     * @param scores
+     * @param score_threshold
+     * @param nms_threshold
+     * @param eta
+     * @param top_k
+     * @param indices
+     */
+    CV_EXPORTS_W void NMSBoxes(const std::vector<Rect>& bboxes, const std::vector<float>& scores,
+                               const float score_threshold, const float nms_threshold,
+                               const float eta, const int top_k, CV_OUT std::vector<int>& indices);
+
 
 //! @}
 CV__DNN_EXPERIMENTAL_NS_END
diff --git a/modules/dnn/include/opencv2/dnn/nms.inl.hpp b/modules/dnn/include/opencv2/dnn/nms.inl.hpp
new file mode 100644 (file)
index 0000000..26183a0
--- /dev/null
@@ -0,0 +1,114 @@
+// This file is part of OpenCV project.
+// It is subject to the license terms in the LICENSE file found in the top-level directory
+// of this distribution and at http://opencv.org/license.html.
+//
+// Copyright (C) 2017, Intel Corporation, all rights reserved.
+// Third party copyrights are property of their respective owners.
+
+#ifndef OPENCV_DNN_NMS_INL_HPP
+#define OPENCV_DNN_NMS_INL_HPP
+
+#include <opencv2/dnn.hpp>
+
+namespace cv {
+namespace dnn {
+
+namespace
+{
+
+template <typename T>
+static inline bool SortScorePairDescend(const std::pair<float, T>& pair1,
+                          const std::pair<float, T>& pair2)
+{
+    return pair1.first > pair2.first;
+}
+
+} // namespace
+
+// Get max scores with corresponding indices.
+//    scores: a set of scores.
+//    threshold: only consider scores higher than the threshold.
+//    top_k: if -1, keep all; otherwise, keep at most top_k.
+//    score_index_vec: store the sorted (score, index) pair.
+inline void GetMaxScoreIndex(const std::vector<float>& scores, const float threshold, const int top_k,
+                      std::vector<std::pair<float, int> >& score_index_vec)
+{
+    CV_DbgAssert(score_index_vec.empty());
+    // Generate index score pairs.
+    for (size_t i = 0; i < scores.size(); ++i)
+    {
+        if (scores[i] > threshold)
+        {
+            score_index_vec.push_back(std::make_pair(scores[i], i));
+        }
+    }
+
+    // Sort the score pair according to the scores in descending order
+    std::stable_sort(score_index_vec.begin(), score_index_vec.end(),
+                     SortScorePairDescend<int>);
+
+    // Keep top_k scores if needed.
+    if (top_k > -1 && top_k < (int)score_index_vec.size())
+    {
+        score_index_vec.resize(top_k);
+    }
+}
+
+template <typename BoxType>
+struct NMSOverlap
+{
+    float operator() (const BoxType& a, const BoxType& b);
+};
+
+template <>
+inline float NMSOverlap<Rect>::operator() (const Rect& a, const Rect& b)
+{
+    float rectIntersectionArea = (float)(a & b).area();
+    return rectIntersectionArea / (a.area() + b.area() - rectIntersectionArea);
+}
+
+// Do non maximum suppression given bboxes and scores.
+// Inspired by Piotr Dollar's NMS implementation in EdgeBox.
+// https://goo.gl/jV3JYS
+//    bboxes: a set of bounding boxes.
+//    scores: a set of corresponding confidences.
+//    score_threshold: a threshold used to filter detection results.
+//    nms_threshold: a threshold used in non maximum suppression.
+//    top_k: if not -1, keep at most top_k picked indices.
+//    indices: the kept indices of bboxes after nms.
+template <typename BoxType>
+inline void NMSFast_(const std::vector<BoxType>& bboxes,
+      const std::vector<float>& scores, const float score_threshold,
+      const float nms_threshold, const float eta, const int top_k,
+      std::vector<int>& indices, NMSOverlap<BoxType> computeOverlap)
+{
+    CV_Assert(bboxes.size() == scores.size());
+
+    // Get top_k scores (with corresponding indices).
+    std::vector<std::pair<float, int> > score_index_vec;
+    GetMaxScoreIndex(scores, score_threshold, top_k, score_index_vec);
+
+    // Do nms.
+    float adaptive_threshold = nms_threshold;
+    indices.clear();
+    while (score_index_vec.size() != 0) {
+        const int idx = score_index_vec.front().second;
+        bool keep = true;
+        for (int k = 0; k < (int)indices.size() && keep; ++k) {
+            const int kept_idx = indices[k];
+            float overlap = computeOverlap(bboxes[idx], bboxes[kept_idx]);
+            keep = overlap <= adaptive_threshold;
+        }
+        if (keep)
+            indices.push_back(idx);
+        score_index_vec.erase(score_index_vec.begin());
+        if (keep && eta < 1 && adaptive_threshold > 0.5) {
+          adaptive_threshold *= eta;
+        }
+    }
+}
+
+}// dnn
+}// cv
+
+#endif
index 505b9c7..1312a81 100644 (file)
@@ -45,6 +45,7 @@
 #include <float.h>
 #include <string>
 #include <caffe.pb.h>
+#include <opencv2/dnn/nms.inl.hpp>
 
 namespace cv
 {
@@ -619,73 +620,14 @@ public:
         }
     }
 
-    // Do non maximum suppression given bboxes and scores.
-    // Inspired by Piotr Dollar's NMS implementation in EdgeBox.
-    // https://goo.gl/jV3JYS
-    //    bboxes: a set of bounding boxes.
-    //    scores: a set of corresponding confidences.
-    //    score_threshold: a threshold used to filter detection results.
-    //    nms_threshold: a threshold used in non maximum suppression.
-    //    top_k: if not -1, keep at most top_k picked indices.
-    //    indices: the kept indices of bboxes after nms.
+
+
     static void ApplyNMSFast(const std::vector<caffe::NormalizedBBox>& bboxes,
           const std::vector<float>& scores, const float score_threshold,
           const float nms_threshold, const float eta, const int top_k,
           std::vector<int>& indices)
     {
-        CV_Assert(bboxes.size() == scores.size());
-
-        // Get top_k scores (with corresponding indices).
-        std::vector<std::pair<float, int> > score_index_vec;
-        GetMaxScoreIndex(scores, score_threshold, top_k, score_index_vec);
-
-        // Do nms.
-        float adaptive_threshold = nms_threshold;
-        indices.clear();
-        while (score_index_vec.size() != 0) {
-            const int idx = score_index_vec.front().second;
-            bool keep = true;
-            for (int k = 0; k < (int)indices.size() && keep; ++k) {
-                const int kept_idx = indices[k];
-                float overlap = JaccardOverlap<true>(bboxes[idx], bboxes[kept_idx]);
-                keep = overlap <= adaptive_threshold;
-            }
-            if (keep)
-                indices.push_back(idx);
-            score_index_vec.erase(score_index_vec.begin());
-            if (keep && eta < 1 && adaptive_threshold > 0.5) {
-              adaptive_threshold *= eta;
-            }
-        }
-    }
-
-    // Get max scores with corresponding indices.
-    //    scores: a set of scores.
-    //    threshold: only consider scores higher than the threshold.
-    //    top_k: if -1, keep all; otherwise, keep at most top_k.
-    //    score_index_vec: store the sorted (score, index) pair.
-    static void GetMaxScoreIndex(const std::vector<float>& scores, const float threshold, const int top_k,
-                          std::vector<std::pair<float, int> >& score_index_vec)
-    {
-        CV_DbgAssert(score_index_vec.empty());
-        // Generate index score pairs.
-        for (size_t i = 0; i < scores.size(); ++i)
-        {
-            if (scores[i] > threshold)
-            {
-                score_index_vec.push_back(std::make_pair(scores[i], i));
-            }
-        }
-
-        // Sort the score pair according to the scores in descending order
-        std::stable_sort(score_index_vec.begin(), score_index_vec.end(),
-                         util::SortScorePairDescend<int>);
-
-        // Keep top_k scores if needed.
-        if (top_k > -1 && top_k < (int)score_index_vec.size())
-        {
-            score_index_vec.resize(top_k);
-        }
+        NMSFast_(bboxes, scores, score_threshold, nms_threshold, eta, top_k, indices, NMSOverlap<caffe::NormalizedBBox>());
     }
 
     // Compute the jaccard (intersection over union IoU) overlap between two bboxes.
@@ -733,6 +675,12 @@ public:
     }
 };
 
+template <>
+float NMSOverlap<caffe::NormalizedBBox>::operator() (const caffe::NormalizedBBox& a, const caffe::NormalizedBBox& b)
+{
+    return DetectionOutputLayerImpl::JaccardOverlap<true>(a, b);
+}
+
 const std::string DetectionOutputLayerImpl::_layerName = std::string("DetectionOutput");
 
 Ptr<DetectionOutputLayer> DetectionOutputLayer::create(const LayerParams &params)
diff --git a/modules/dnn/src/nms.cpp b/modules/dnn/src/nms.cpp
new file mode 100644 (file)
index 0000000..5f433db
--- /dev/null
@@ -0,0 +1,24 @@
+// This file is part of OpenCV project.
+// It is subject to the license terms in the LICENSE file found in the top-level directory
+// of this distribution and at http://opencv.org/license.html.
+//
+// Copyright (C) 2017, Intel Corporation, all rights reserved.
+// Third party copyrights are property of their respective owners.
+
+#include "precomp.hpp"
+#include <opencv2/dnn/nms.inl.hpp>
+
+namespace cv
+{
+namespace dnn
+{
+
+void NMSBoxes(const std::vector<Rect>& bboxes, const std::vector<float>& scores,
+                          const float score_threshold, const float nms_threshold,
+                          const float eta, const int top_k, std::vector<int>& indices)
+{
+    NMSFast_(bboxes, scores, score_threshold, nms_threshold, eta, top_k, indices, NMSOverlap<Rect>());
+}
+
+}// dnn
+}// cv