mirror of https://github.com/alibaba/MNN.git
				
				
				
			
		
			
				
	
	
		
			162 lines
		
	
	
		
			6.0 KiB
		
	
	
	
		
			C++
		
	
	
	
			
		
		
	
	
			162 lines
		
	
	
		
			6.0 KiB
		
	
	
	
		
			C++
		
	
	
	
| //
 | |
| //  NMSModule.cpp
 | |
| //  MNN
 | |
| //
 | |
| //  Created by MNN on b'2020/09/10'.
 | |
| //  Copyright © 2018, Alibaba Group Holding Limited
 | |
| //
 | |
| 
 | |
| #include "NMSModule.hpp"
 | |
| #include "backend/cpu/CPUNonMaxSuppressionV2.hpp"
 | |
| #include <MNN/expr/ExprCreator.hpp>
 | |
| #include <MNN/Tensor.hpp>
 | |
| #include "MNN_generated.h"
 | |
| //#define MNN_OPEN_TIME_TRACE
 | |
| #include <MNN/AutoTime.hpp>
 | |
| #include <queue>
 | |
| namespace MNN {
 | |
| namespace Express {
 | |
| 
 | |
| NMSModule* NMSModule::create(const Op* op) {
 | |
|     auto module = new NMSModule;
 | |
|     module->setType("NMSModule");
 | |
|     if (nullptr != op->name()) {
 | |
|         module->setName(op->name()->str());
 | |
|     }
 | |
|     return module;
 | |
| }
 | |
| 
 | |
| // Return intersection-over-union overlap between boxes i and j
 | |
| static inline float IOU(const float* boxes, int i, int j) {
 | |
|     const float yMinI = std::min<float>(boxes[i * 4 + 0], boxes[i * 4 + 2]);
 | |
|     const float xMinI = std::min<float>(boxes[i * 4 + 1], boxes[i * 4 + 3]);
 | |
|     const float yMaxI = std::max<float>(boxes[i * 4 + 0], boxes[i * 4 + 2]);
 | |
|     const float xMaxI = std::max<float>(boxes[i * 4 + 1], boxes[i * 4 + 3]);
 | |
|     const float yMinJ = std::min<float>(boxes[j * 4 + 0], boxes[j * 4 + 2]);
 | |
|     const float xMinJ = std::min<float>(boxes[j * 4 + 1], boxes[j * 4 + 3]);
 | |
|     const float yMaxJ = std::max<float>(boxes[j * 4 + 0], boxes[j * 4 + 2]);
 | |
|     const float xMaxJ = std::max<float>(boxes[j * 4 + 1], boxes[j * 4 + 3]);
 | |
|     const float areaI = (yMaxI - yMinI) * (xMaxI - xMinI);
 | |
|     const float areaJ = (yMaxJ - yMinJ) * (xMaxJ - xMinJ);
 | |
|     if (areaI <= 0 || areaJ <= 0)
 | |
|         return 0.0;
 | |
|     const float intersectionYMin = std::max<float>(yMinI, yMinJ);
 | |
|     const float intersectionXMin = std::max<float>(xMinI, xMinJ);
 | |
|     const float intersectionYMax = std::min<float>(yMaxI, yMaxJ);
 | |
|     const float intersectionXMax = std::min<float>(xMaxI, xMaxJ);
 | |
|     const float intersectionArea = std::max<float>(intersectionYMax - intersectionYMin, 0.0) *
 | |
|                                    std::max<float>(intersectionXMax - intersectionXMin, 0.0);
 | |
|     return intersectionArea / (areaI + areaJ - intersectionArea);
 | |
| }
 | |
| 
 | |
| static void NonMaxSuppressionSingleClasssImpl(const float* boxesPtr, const float* scores, int numBoxes, int maxDetections,
 | |
|                                               float iouThreshold, float scoreThreshold, std::vector<int32_t>* selected) {
 | |
|     MNN_ASSERT(iouThreshold >= 0.0f && iouThreshold <= 1.0f);
 | |
| 
 | |
|     const int outputNum = std::min(maxDetections, numBoxes);
 | |
|     std::vector<float> scoresData(numBoxes);
 | |
|     std::copy_n(scores, numBoxes, scoresData.begin());
 | |
| 
 | |
|     struct Candidate {
 | |
|         int boxIndex;
 | |
|         float score;
 | |
|     };
 | |
| 
 | |
|     auto cmp = [](const Candidate bsI, const Candidate bsJ) { return bsI.score < bsJ.score; };
 | |
| 
 | |
|     std::priority_queue<Candidate, std::deque<Candidate>, decltype(cmp)> candidatePriorityQueue(cmp);
 | |
| 
 | |
|     for (int i = 0; i < scoresData.size(); ++i) {
 | |
|         if (scoresData[i] > scoreThreshold) {
 | |
|             candidatePriorityQueue.emplace(Candidate({i, scoresData[i]}));
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     // std::vector<float> selectedScores;
 | |
|     Candidate nextCandidate;
 | |
|     float iou, originalScore;
 | |
| 
 | |
|     while (selected->size() < outputNum && !candidatePriorityQueue.empty()) {
 | |
|         nextCandidate = candidatePriorityQueue.top();
 | |
|         originalScore = nextCandidate.score;
 | |
|         candidatePriorityQueue.pop();
 | |
| 
 | |
|         // Overlapping boxes are likely to have similar scores,
 | |
|         // therefore we iterate through the previously selected boxes backwards
 | |
|         // in order to see if `next_candidate` should be suppressed.
 | |
|         bool shouldSelect = true;
 | |
|         for (int j = (int)selected->size() - 1; j >= 0; --j) {
 | |
|             iou = IOU(boxesPtr, nextCandidate.boxIndex, selected->at(j));
 | |
|             if (iou == 0.0) {
 | |
|                 continue;
 | |
|             }
 | |
|             if (iou > iouThreshold) {
 | |
|                 shouldSelect = false;
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         if (shouldSelect) {
 | |
|             selected->push_back(nextCandidate.boxIndex);
 | |
|             // selectedScores.push_back(nextCandidate.score);
 | |
|         }
 | |
|     }
 | |
| }
 | |
| 
 | |
| std::vector<Express::VARP> NMSModule::onForward(const std::vector<Express::VARP>& inputs) {
 | |
|     const int maxDetections = inputs[2]->readMap<int>()[0];
 | |
|     float iouThreshold = 0, scoreThreshold = std::numeric_limits<float>::lowest();
 | |
|     if (inputs.size() > 3) {
 | |
|         iouThreshold = inputs[3]->readMap<float>()[0];
 | |
|     }
 | |
|     if (inputs.size() > 4) {
 | |
|         scoreThreshold = inputs[4]->readMap<float>()[0];
 | |
|     }
 | |
|     auto boxes = inputs[0], score = inputs[1];
 | |
|     auto info = boxes->getInfo(), infoScore = score->getInfo();
 | |
|     MNN_ASSERT(info->dim[info->dim.size() - 1] == 4);
 | |
|     int batch = 1, numClass = 1, numBoxes = info->dim[0];
 | |
|     bool onnxFormat = (infoScore->dim.size() > 1);
 | |
|     if (onnxFormat) {
 | |
|         batch = infoScore->dim[0];
 | |
|         numClass = infoScore->dim[1];
 | |
|         numBoxes = infoScore->dim[2];
 | |
|     }
 | |
|     INTS outputData;
 | |
|     for (int b = 0; b < batch; ++b) {
 | |
|         const auto boxesPtr = boxes->readMap<float>() + b * numBoxes * 4;
 | |
|         for (int c = 0; c < numClass; ++c) {
 | |
|             std::vector<int> selected;
 | |
|             const auto scorePtr = score->readMap<float>() + (b * numClass + c) * numBoxes;
 | |
|             NonMaxSuppressionSingleClasssImpl(boxesPtr, scorePtr, numBoxes, maxDetections, iouThreshold, scoreThreshold, &selected);
 | |
|             for (int i = 0; i < selected.size(); ++i) {
 | |
|                 if (onnxFormat) {
 | |
|                     outputData.push_back(b);
 | |
|                     outputData.push_back(c);
 | |
|                 }
 | |
|                 outputData.push_back(selected[i]);
 | |
|             }
 | |
|         }
 | |
|     }
 | |
|     
 | |
|     Variable::Info outInfo;
 | |
|     outInfo.order = info->order;
 | |
|     outInfo.type = halide_type_of<int>();
 | |
|     if (onnxFormat) {
 | |
|         outInfo.dim.assign({(int)outputData.size() / 3, 3});
 | |
|     } else {
 | |
|         outInfo.dim.assign({(int)outputData.size()});
 | |
|     }
 | |
|     outInfo.syncSize();
 | |
|     VARPS outputs;
 | |
|     outputs.push_back(Variable::create(Expr::create(std::move(outInfo), outputData.data(), VARP::CONSTANT)));
 | |
|     return outputs;
 | |
| }
 | |
| 
 | |
| Module* NMSModule::clone(CloneContext* ctx) const {
 | |
|     NMSModule* module(new NMSModule);
 | |
|     return this->cloneBaseTo(ctx, module);
 | |
| }
 | |
| 
 | |
| }
 | |
| }
 |