picodet.cpp 7.9 KB
Newer Older
qq_30618961's avatar
qq_30618961 已提交
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
// Copyright (c) 2021 PaddlePaddle Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// reference from https://github.com/RangiLyu/nanodet/tree/main/demo_ncnn

#include "picodet.h"
#include <benchmark.h>
#include <iostream>

G
Guanghua Yu 已提交
20 21 22 23 24 25 26
inline float fast_exp(float x) {
  union {
    uint32_t i;
    float f;
  } v{};
  v.i = (1 << 23) * (1.4426950409 * x + 126.93490512f);
  return v.f;
qq_30618961's avatar
qq_30618961 已提交
27 28
}

G
Guanghua Yu 已提交
29
inline float sigmoid(float x) { return 1.0f / (1.0f + fast_exp(-x)); }
qq_30618961's avatar
qq_30618961 已提交
30

G
Guanghua Yu 已提交
31 32 33 34
template <typename _Tp>
int activation_function_softmax(const _Tp *src, _Tp *dst, int length) {
  const _Tp alpha = *std::max_element(src, src + length);
  _Tp denominator{0};
qq_30618961's avatar
qq_30618961 已提交
35

G
Guanghua Yu 已提交
36 37 38 39
  for (int i = 0; i < length; ++i) {
    dst[i] = fast_exp(src[i] - alpha);
    denominator += dst[i];
  }
qq_30618961's avatar
qq_30618961 已提交
40

G
Guanghua Yu 已提交
41 42 43
  for (int i = 0; i < length; ++i) {
    dst[i] /= denominator;
  }
qq_30618961's avatar
qq_30618961 已提交
44

G
Guanghua Yu 已提交
45
  return 0;
qq_30618961's avatar
qq_30618961 已提交
46 47 48
}

bool PicoDet::hasGPU = false;
G
Guanghua Yu 已提交
49
PicoDet *PicoDet::detector = nullptr;
qq_30618961's avatar
qq_30618961 已提交
50

51 52 53
PicoDet::PicoDet(const char *param, const char *bin, int input_width,
                 int input_hight, bool useGPU, float score_threshold_ = 0.5,
                 float nms_threshold_ = 0.3) {
G
Guanghua Yu 已提交
54
  this->Net = new ncnn::Net();
qq_30618961's avatar
qq_30618961 已提交
55
#if NCNN_VULKAN
G
Guanghua Yu 已提交
56
  this->hasGPU = ncnn::get_gpu_count() > 0;
qq_30618961's avatar
qq_30618961 已提交
57
#endif
G
Guanghua Yu 已提交
58 59 60 61
  this->Net->opt.use_vulkan_compute = this->hasGPU && useGPU;
  this->Net->opt.use_fp16_arithmetic = true;
  this->Net->load_param(param);
  this->Net->load_model(bin);
62 63 64 65
  this->in_w = input_width;
  this->in_h = input_hight;
  this->score_threshold = score_threshold_;
  this->nms_threshold = nms_threshold_;
qq_30618961's avatar
qq_30618961 已提交
66 67
}

G
Guanghua Yu 已提交
68
PicoDet::~PicoDet() { delete this->Net; }
qq_30618961's avatar
qq_30618961 已提交
69

G
Guanghua Yu 已提交
70
void PicoDet::preprocess(cv::Mat &image, ncnn::Mat &in) {
71
  // cv::resize(image, image, cv::Size(this->in_w, this->in_h), 0.f, 0.f);
G
Guanghua Yu 已提交
72 73
  int img_w = image.cols;
  int img_h = image.rows;
74 75
  in = ncnn::Mat::from_pixels_resize(image.data, ncnn::Mat::PIXEL_BGR, img_w,
                                     img_h, this->in_w, this->in_h);
G
Guanghua Yu 已提交
76 77 78
  const float mean_vals[3] = {103.53f, 116.28f, 123.675f};
  const float norm_vals[3] = {0.017429f, 0.017507f, 0.017125f};
  in.substract_mean_normalize(mean_vals, norm_vals);
qq_30618961's avatar
qq_30618961 已提交
79 80
}

81 82 83
int PicoDet::detect(cv::Mat image, std::vector<BoxInfo> &result_list,
                    bool has_postprocess) {

G
Guanghua Yu 已提交
84 85 86 87 88
  ncnn::Mat input;
  preprocess(image, input);
  auto ex = this->Net->create_extractor();
  ex.set_light_mode(false);
  ex.set_num_threads(4);
qq_30618961's avatar
qq_30618961 已提交
89
#if NCNN_VULKAN
G
Guanghua Yu 已提交
90
  ex.set_vulkan_compute(this->hasGPU);
qq_30618961's avatar
qq_30618961 已提交
91
#endif
G
Guanghua Yu 已提交
92 93
  ex.input("image", input); // picodet

94 95 96
  this->image_h = image.rows;
  this->image_w = image.cols;

G
Guanghua Yu 已提交
97 98 99
  std::vector<std::vector<BoxInfo>> results;
  results.resize(this->num_class);

100
  if (has_postprocess) {
G
Guanghua Yu 已提交
101 102
    ncnn::Mat dis_pred;
    ncnn::Mat cls_pred;
103 104 105 106 107 108 109 110 111 112 113 114 115 116
    ex.extract(this->nms_heads_info[0].c_str(), dis_pred);
    ex.extract(this->nms_heads_info[1].c_str(), cls_pred);
    std::cout << dis_pred.h << "  " << dis_pred.w << std::endl;
    std::cout << cls_pred.h << "  " << cls_pred.w << std::endl;
    this->nms_boxes(cls_pred, dis_pred, this->score_threshold, results);
  } else {
    for (const auto &head_info : this->non_postprocess_heads_info) {
      ncnn::Mat dis_pred;
      ncnn::Mat cls_pred;
      ex.extract(head_info.dis_layer.c_str(), dis_pred);
      ex.extract(head_info.cls_layer.c_str(), cls_pred);
      this->decode_infer(cls_pred, dis_pred, head_info.stride,
                         this->score_threshold, results);
    }
G
Guanghua Yu 已提交
117 118 119
  }

  for (int i = 0; i < (int)results.size(); i++) {
120
    this->nms(results[i], this->nms_threshold);
G
Guanghua Yu 已提交
121 122

    for (auto box : results[i]) {
123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151
      box.x1 = box.x1 / this->in_w * this->image_w;
      box.x2 = box.x2 / this->in_w * this->image_w;
      box.y1 = box.y1 / this->in_h * this->image_h;
      box.y2 = box.y2 / this->in_h * this->image_h;
      result_list.push_back(box);
    }
  }
  return 0;
}

void PicoDet::nms_boxes(ncnn::Mat &cls_pred, ncnn::Mat &dis_pred,
                        float score_threshold,
                        std::vector<std::vector<BoxInfo>> &result_list) {
  BoxInfo bbox;
  int i, j;
  for (i = 0; i < dis_pred.h; i++) {
    bbox.x1 = dis_pred.row(i)[0];
    bbox.y1 = dis_pred.row(i)[1];
    bbox.x2 = dis_pred.row(i)[2];
    bbox.y2 = dis_pred.row(i)[3];
    const float *scores = cls_pred.row(i);
    float score = 0;
    int cur_label = 0;
    for (int label = 0; label < this->num_class; label++) {
      float score_ = cls_pred.row(label)[i];
      if (score_ > score) {
        score = score_;
        cur_label = label;
      }
qq_30618961's avatar
qq_30618961 已提交
152
    }
153 154 155
    bbox.score = score;
    bbox.label = cur_label;
    result_list[cur_label].push_back(bbox);
G
Guanghua Yu 已提交
156
  }
qq_30618961's avatar
qq_30618961 已提交
157 158
}

G
Guanghua Yu 已提交
159 160 161
void PicoDet::decode_infer(ncnn::Mat &cls_pred, ncnn::Mat &dis_pred, int stride,
                           float threshold,
                           std::vector<std::vector<BoxInfo>> &results) {
162 163
  int feature_h = ceil((float)this->in_w / stride);
  int feature_w = ceil((float)this->in_h / stride);
G
Guanghua Yu 已提交
164 165 166 167 168 169 170 171 172 173 174 175

  for (int idx = 0; idx < feature_h * feature_w; idx++) {
    const float *scores = cls_pred.row(idx);
    int row = idx / feature_w;
    int col = idx % feature_w;
    float score = 0;
    int cur_label = 0;
    for (int label = 0; label < this->num_class; label++) {
      if (scores[label] > score) {
        score = scores[label];
        cur_label = label;
      }
qq_30618961's avatar
qq_30618961 已提交
176
    }
G
Guanghua Yu 已提交
177 178 179 180 181 182
    if (score > threshold) {
      const float *bbox_pred = dis_pred.row(idx);
      results[cur_label].push_back(
          this->disPred2Bbox(bbox_pred, cur_label, score, col, row, stride));
    }
  }
qq_30618961's avatar
qq_30618961 已提交
183 184
}

G
Guanghua Yu 已提交
185 186 187 188 189 190 191 192 193 194 195 196 197
BoxInfo PicoDet::disPred2Bbox(const float *&dfl_det, int label, float score,
                              int x, int y, int stride) {
  float ct_x = (x + 0.5) * stride;
  float ct_y = (y + 0.5) * stride;
  std::vector<float> dis_pred;
  dis_pred.resize(4);
  for (int i = 0; i < 4; i++) {
    float dis = 0;
    float *dis_after_sm = new float[this->reg_max + 1];
    activation_function_softmax(dfl_det + i * (this->reg_max + 1), dis_after_sm,
                                this->reg_max + 1);
    for (int j = 0; j < this->reg_max + 1; j++) {
      dis += j * dis_after_sm[j];
qq_30618961's avatar
qq_30618961 已提交
198
    }
G
Guanghua Yu 已提交
199 200 201 202 203 204
    dis *= stride;
    dis_pred[i] = dis;
    delete[] dis_after_sm;
  }
  float xmin = (std::max)(ct_x - dis_pred[0], .0f);
  float ymin = (std::max)(ct_y - dis_pred[1], .0f);
205 206
  float xmax = (std::min)(ct_x + dis_pred[2], (float)this->in_w);
  float ymax = (std::min)(ct_y + dis_pred[3], (float)this->in_w);
G
Guanghua Yu 已提交
207
  return BoxInfo{xmin, ymin, xmax, ymax, score, label};
qq_30618961's avatar
qq_30618961 已提交
208 209
}

G
Guanghua Yu 已提交
210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233
void PicoDet::nms(std::vector<BoxInfo> &input_boxes, float NMS_THRESH) {
  std::sort(input_boxes.begin(), input_boxes.end(),
            [](BoxInfo a, BoxInfo b) { return a.score > b.score; });
  std::vector<float> vArea(input_boxes.size());
  for (int i = 0; i < int(input_boxes.size()); ++i) {
    vArea[i] = (input_boxes.at(i).x2 - input_boxes.at(i).x1 + 1) *
               (input_boxes.at(i).y2 - input_boxes.at(i).y1 + 1);
  }
  for (int i = 0; i < int(input_boxes.size()); ++i) {
    for (int j = i + 1; j < int(input_boxes.size());) {
      float xx1 = (std::max)(input_boxes[i].x1, input_boxes[j].x1);
      float yy1 = (std::max)(input_boxes[i].y1, input_boxes[j].y1);
      float xx2 = (std::min)(input_boxes[i].x2, input_boxes[j].x2);
      float yy2 = (std::min)(input_boxes[i].y2, input_boxes[j].y2);
      float w = (std::max)(float(0), xx2 - xx1 + 1);
      float h = (std::max)(float(0), yy2 - yy1 + 1);
      float inter = w * h;
      float ovr = inter / (vArea[i] + vArea[j] - inter);
      if (ovr >= NMS_THRESH) {
        input_boxes.erase(input_boxes.begin() + j);
        vArea.erase(vArea.begin() + j);
      } else {
        j++;
      }
qq_30618961's avatar
qq_30618961 已提交
234
    }
G
Guanghua Yu 已提交
235
  }
qq_30618961's avatar
qq_30618961 已提交
236
}