picodet_mnn.cpp 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253
  1. // Copyright (c) 2021 PaddlePaddle Authors. All Rights Reserved.
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. // reference from https://github.com/RangiLyu/nanodet/tree/main/demo_mnn
  15. #include "picodet_mnn.hpp"
  16. using namespace std;
  17. PicoDet::PicoDet(const std::string &mnn_path, int input_width, int input_length,
  18. int num_thread_, float score_threshold_,
  19. float nms_threshold_) {
  20. num_thread = num_thread_;
  21. in_w = input_width;
  22. in_h = input_length;
  23. score_threshold = score_threshold_;
  24. nms_threshold = nms_threshold_;
  25. PicoDet_interpreter = std::shared_ptr<MNN::Interpreter>(
  26. MNN::Interpreter::createFromFile(mnn_path.c_str()));
  27. MNN::ScheduleConfig config;
  28. config.numThread = num_thread;
  29. MNN::BackendConfig backendConfig;
  30. backendConfig.precision = (MNN::BackendConfig::PrecisionMode)2;
  31. config.backendConfig = &backendConfig;
  32. PicoDet_session = PicoDet_interpreter->createSession(config);
  33. input_tensor = PicoDet_interpreter->getSessionInput(PicoDet_session, nullptr);
  34. }
  35. PicoDet::~PicoDet() {
  36. PicoDet_interpreter->releaseModel();
  37. PicoDet_interpreter->releaseSession(PicoDet_session);
  38. }
  39. int PicoDet::detect(cv::Mat &raw_image, std::vector<BoxInfo> &result_list,
  40. bool has_postprocess) {
  41. if (raw_image.empty()) {
  42. std::cout << "image is empty ,please check!" << std::endl;
  43. return -1;
  44. }
  45. image_h = raw_image.rows;
  46. image_w = raw_image.cols;
  47. cv::Mat image;
  48. cv::resize(raw_image, image, cv::Size(in_w, in_h));
  49. PicoDet_interpreter->resizeTensor(input_tensor, {1, 3, in_h, in_w});
  50. PicoDet_interpreter->resizeSession(PicoDet_session);
  51. std::shared_ptr<MNN::CV::ImageProcess> pretreat(MNN::CV::ImageProcess::create(
  52. MNN::CV::BGR, MNN::CV::BGR, mean_vals, 3, norm_vals, 3));
  53. pretreat->convert(image.data, in_w, in_h, image.step[0], input_tensor);
  54. auto start = chrono::steady_clock::now();
  55. // run network
  56. PicoDet_interpreter->runSession(PicoDet_session);
  57. // get output data
  58. std::vector<std::vector<BoxInfo>> results;
  59. results.resize(num_class);
  60. if (has_postprocess) {
  61. auto bbox_out_tensor = PicoDet_interpreter->getSessionOutput(
  62. PicoDet_session, nms_heads_info[0].c_str());
  63. auto class_out_tensor = PicoDet_interpreter->getSessionOutput(
  64. PicoDet_session, nms_heads_info[1].c_str());
  65. // bbox branch
  66. auto tensor_bbox_host =
  67. new MNN::Tensor(bbox_out_tensor, MNN::Tensor::CAFFE);
  68. bbox_out_tensor->copyToHostTensor(tensor_bbox_host);
  69. auto bbox_output_shape = tensor_bbox_host->shape();
  70. int output_size = 1;
  71. for (int j = 0; j < bbox_output_shape.size(); ++j) {
  72. output_size *= bbox_output_shape[j];
  73. }
  74. std::cout << "output_size:" << output_size << std::endl;
  75. bbox_output_data_.resize(output_size);
  76. std::copy_n(tensor_bbox_host->host<float>(), output_size,
  77. bbox_output_data_.data());
  78. delete tensor_bbox_host;
  79. // class branch
  80. auto tensor_class_host =
  81. new MNN::Tensor(class_out_tensor, MNN::Tensor::CAFFE);
  82. class_out_tensor->copyToHostTensor(tensor_class_host);
  83. auto class_output_shape = tensor_class_host->shape();
  84. output_size = 1;
  85. for (int j = 0; j < class_output_shape.size(); ++j) {
  86. output_size *= class_output_shape[j];
  87. }
  88. std::cout << "output_size:" << output_size << std::endl;
  89. class_output_data_.resize(output_size);
  90. std::copy_n(tensor_class_host->host<float>(), output_size,
  91. class_output_data_.data());
  92. delete tensor_class_host;
  93. } else {
  94. for (const auto &head_info : non_postprocess_heads_info) {
  95. MNN::Tensor *tensor_scores = PicoDet_interpreter->getSessionOutput(
  96. PicoDet_session, head_info.cls_layer.c_str());
  97. MNN::Tensor *tensor_boxes = PicoDet_interpreter->getSessionOutput(
  98. PicoDet_session, head_info.dis_layer.c_str());
  99. MNN::Tensor tensor_scores_host(tensor_scores,
  100. tensor_scores->getDimensionType());
  101. tensor_scores->copyToHostTensor(&tensor_scores_host);
  102. MNN::Tensor tensor_boxes_host(tensor_boxes,
  103. tensor_boxes->getDimensionType());
  104. tensor_boxes->copyToHostTensor(&tensor_boxes_host);
  105. decode_infer(&tensor_scores_host, &tensor_boxes_host, head_info.stride,
  106. score_threshold, results);
  107. }
  108. }
  109. auto end = chrono::steady_clock::now();
  110. chrono::duration<double> elapsed = end - start;
  111. cout << "inference time:" << elapsed.count() << " s, ";
  112. for (int i = 0; i < (int)results.size(); i++) {
  113. nms(results[i], nms_threshold);
  114. for (auto box : results[i]) {
  115. box.x1 = box.x1 / in_w * image_w;
  116. box.x2 = box.x2 / in_w * image_w;
  117. box.y1 = box.y1 / in_h * image_h;
  118. box.y2 = box.y2 / in_h * image_h;
  119. result_list.push_back(box);
  120. }
  121. }
  122. cout << "detect " << result_list.size() << " objects" << endl;
  123. return 0;
  124. }
  125. void PicoDet::decode_infer(MNN::Tensor *cls_pred, MNN::Tensor *dis_pred,
  126. int stride, float threshold,
  127. std::vector<std::vector<BoxInfo>> &results) {
  128. int feature_h = ceil((float)in_h / stride);
  129. int feature_w = ceil((float)in_w / stride);
  130. for (int idx = 0; idx < feature_h * feature_w; idx++) {
  131. const float *scores = cls_pred->host<float>() + (idx * num_class);
  132. int row = idx / feature_w;
  133. int col = idx % feature_w;
  134. float score = 0;
  135. int cur_label = 0;
  136. for (int label = 0; label < num_class; label++) {
  137. if (scores[label] > score) {
  138. score = scores[label];
  139. cur_label = label;
  140. }
  141. }
  142. if (score > threshold) {
  143. const float *bbox_pred =
  144. dis_pred->host<float>() + (idx * 4 * (reg_max + 1));
  145. results[cur_label].push_back(
  146. disPred2Bbox(bbox_pred, cur_label, score, col, row, stride));
  147. }
  148. }
  149. }
  150. BoxInfo PicoDet::disPred2Bbox(const float *&dfl_det, int label, float score,
  151. int x, int y, int stride) {
  152. float ct_x = (x + 0.5) * stride;
  153. float ct_y = (y + 0.5) * stride;
  154. std::vector<float> dis_pred;
  155. dis_pred.resize(4);
  156. for (int i = 0; i < 4; i++) {
  157. float dis = 0;
  158. float *dis_after_sm = new float[reg_max + 1];
  159. activation_function_softmax(dfl_det + i * (reg_max + 1), dis_after_sm,
  160. reg_max + 1);
  161. for (int j = 0; j < reg_max + 1; j++) {
  162. dis += j * dis_after_sm[j];
  163. }
  164. dis *= stride;
  165. dis_pred[i] = dis;
  166. delete[] dis_after_sm;
  167. }
  168. float xmin = (std::max)(ct_x - dis_pred[0], .0f);
  169. float ymin = (std::max)(ct_y - dis_pred[1], .0f);
  170. float xmax = (std::min)(ct_x + dis_pred[2], (float)in_w);
  171. float ymax = (std::min)(ct_y + dis_pred[3], (float)in_h);
  172. return BoxInfo{xmin, ymin, xmax, ymax, score, label};
  173. }
  174. void PicoDet::nms(std::vector<BoxInfo> &input_boxes, float NMS_THRESH) {
  175. std::sort(input_boxes.begin(), input_boxes.end(),
  176. [](BoxInfo a, BoxInfo b) { return a.score > b.score; });
  177. std::vector<float> vArea(input_boxes.size());
  178. for (int i = 0; i < int(input_boxes.size()); ++i) {
  179. vArea[i] = (input_boxes.at(i).x2 - input_boxes.at(i).x1 + 1) *
  180. (input_boxes.at(i).y2 - input_boxes.at(i).y1 + 1);
  181. }
  182. for (int i = 0; i < int(input_boxes.size()); ++i) {
  183. for (int j = i + 1; j < int(input_boxes.size());) {
  184. float xx1 = (std::max)(input_boxes[i].x1, input_boxes[j].x1);
  185. float yy1 = (std::max)(input_boxes[i].y1, input_boxes[j].y1);
  186. float xx2 = (std::min)(input_boxes[i].x2, input_boxes[j].x2);
  187. float yy2 = (std::min)(input_boxes[i].y2, input_boxes[j].y2);
  188. float w = (std::max)(float(0), xx2 - xx1 + 1);
  189. float h = (std::max)(float(0), yy2 - yy1 + 1);
  190. float inter = w * h;
  191. float ovr = inter / (vArea[i] + vArea[j] - inter);
  192. if (ovr >= NMS_THRESH) {
  193. input_boxes.erase(input_boxes.begin() + j);
  194. vArea.erase(vArea.begin() + j);
  195. } else {
  196. j++;
  197. }
  198. }
  199. }
  200. }
  201. inline float fast_exp(float x) {
  202. union {
  203. uint32_t i;
  204. float f;
  205. } v{};
  206. v.i = (1 << 23) * (1.4426950409 * x + 126.93490512f);
  207. return v.f;
  208. }
  209. inline float sigmoid(float x) { return 1.0f / (1.0f + fast_exp(-x)); }
  210. template <typename _Tp>
  211. int activation_function_softmax(const _Tp *src, _Tp *dst, int length) {
  212. const _Tp alpha = *std::max_element(src, src + length);
  213. _Tp denominator{0};
  214. for (int i = 0; i < length; ++i) {
  215. dst[i] = fast_exp(src[i] - alpha);
  216. denominator += dst[i];
  217. }
  218. for (int i = 0; i < length; ++i) {
  219. dst[i] /= denominator;
  220. }
  221. return 0;
  222. }