yolo_detector.cpp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321
  1. #include "yolo_detector.h"
  2. #include <iostream>
  3. #include <fstream>
  4. #include <algorithm>
  5. #include <cstring>
  6. #include <cmath>
  7. using namespace std;
  8. static const vector<string> COCO_NAMES = {
  9. "person","bicycle","car","motorcycle","airplane","bus","train","truck",
  10. "boat","traffic light","fire hydrant","stop sign","parking meter","bench",
  11. "bird","cat","dog","horse","sheep","cow","elephant","bear","zebra",
  12. "giraffe","backpack","umbrella","handbag","tie","suitcase","frisbee",
  13. "skis","snowboard","sports ball","kite","baseball bat","baseball glove",
  14. "skateboard","surfboard","tennis racket","bottle","wine glass","cup",
  15. "fork","knife","spoon","bowl","banana","apple","sandwich","orange",
  16. "broccoli","carrot","hot dog","pizza","donut","cake","chair","couch",
  17. "potted plant","bed","dining table","toilet","tv","laptop","mouse",
  18. "remote","keyboard","cell phone","microwave","oven","toaster","sink",
  19. "refrigerator","book","clock","vase","scissors","teddy bear",
  20. "hair drier","toothbrush"
  21. };
  22. YoloDetector::~YoloDetector() {
  23. if (ctx_) {
  24. rknn_destroy(ctx_);
  25. ctx_ = 0;
  26. }
  27. }
  28. bool YoloDetector::load(const string& model_path, int input_size,
  29. float conf_thresh, float nms_thresh) {
  30. input_size_ = input_size;
  31. conf_thresh_ = conf_thresh;
  32. nms_thresh_ = nms_thresh;
  33. ifstream ifs(model_path, ios::binary | ios::ate);
  34. if (!ifs.is_open()) {
  35. cerr << "[RKNN] Failed to open model: " << model_path << endl;
  36. return false;
  37. }
  38. size_t model_size = ifs.tellg();
  39. ifs.seekg(0, ios::beg);
  40. vector<char> model_buf(model_size);
  41. ifs.read(model_buf.data(), model_size);
  42. ifs.close();
  43. int ret = rknn_init(&ctx_, model_buf.data(), model_buf.size(), 0, nullptr);
  44. if (ret < 0) {
  45. cerr << "[RKNN] rknn_init failed: " << ret << endl;
  46. return false;
  47. }
  48. rknn_input_output_num io_num;
  49. ret = rknn_query(ctx_, RKNN_QUERY_IN_OUT_NUM, &io_num, sizeof(io_num));
  50. if (ret < 0) {
  51. cerr << "[RKNN] query IN_OUT_NUM failed: " << ret << endl;
  52. return false;
  53. }
  54. n_input_ = io_num.n_input;
  55. n_output_ = io_num.n_output;
  56. input_attrs_.resize(n_input_);
  57. for (uint32_t i = 0; i < n_input_; i++) {
  58. memset(&input_attrs_[i], 0, sizeof(rknn_tensor_attr));
  59. input_attrs_[i].index = i;
  60. rknn_query(ctx_, RKNN_QUERY_INPUT_ATTR, &input_attrs_[i], sizeof(rknn_tensor_attr));
  61. }
  62. output_attrs_.resize(n_output_);
  63. for (uint32_t i = 0; i < n_output_; i++) {
  64. memset(&output_attrs_[i], 0, sizeof(rknn_tensor_attr));
  65. output_attrs_[i].index = i;
  66. rknn_query(ctx_, RKNN_QUERY_OUTPUT_ATTR, &output_attrs_[i], sizeof(rknn_tensor_attr));
  67. }
  68. cout << "[RKNN] Model loaded: " << model_path << endl;
  69. for (uint32_t i = 0; i < n_input_; i++) {
  70. auto& a = input_attrs_[i];
  71. cout << " Input [" << i << "] " << a.name
  72. << " fmt=" << (a.fmt == RKNN_TENSOR_NCHW ? "NCHW" : "NHWC")
  73. << " dims=(";
  74. for (uint32_t d = 0; d < a.n_dims; d++) {
  75. if (d) cout << "x";
  76. cout << a.dims[d];
  77. }
  78. cout << ")" << endl;
  79. }
  80. cout << "[RKNN] n_output=" << n_output_ << endl;
  81. for (uint32_t i = 0; i < n_output_; i++) {
  82. auto& a = output_attrs_[i];
  83. cout << " Output [" << i << "] " << a.name
  84. << " fmt=" << (a.fmt == RKNN_TENSOR_NCHW ? "NCHW" : "NHWC")
  85. << " dims=(";
  86. for (uint32_t d = 0; d < a.n_dims; d++) {
  87. if (d) cout << "x";
  88. cout << a.dims[d];
  89. }
  90. cout << ")" << endl;
  91. }
  92. loaded_ = true;
  93. return true;
  94. }
  95. static float sigmoid(float x) {
  96. return 1.0f / (1.0f + expf(-x));
  97. }
  98. vector<Detection> YoloDetector::detect(const float* rgb_data, int img_w, int img_h,
  99. float scale, int pad_x, int pad_y) {
  100. if (!loaded_ || !rgb_data) return {};
  101. int num_pixels = input_size_ * input_size_ * 3;
  102. vector<int8_t> input_i8(num_pixels);
  103. for (int i = 0; i < num_pixels; i++) {
  104. int v = (int)(rgb_data[i] * 255.0f) - 128;
  105. input_i8[i] = (int8_t)(v < -128 ? -128 : (v > 127 ? 127 : v));
  106. }
  107. rknn_input inputs[1];
  108. memset(&inputs[0], 0, sizeof(rknn_input));
  109. inputs[0].index = 0;
  110. inputs[0].type = RKNN_TENSOR_INT8;
  111. inputs[0].fmt = RKNN_TENSOR_NHWC;
  112. inputs[0].size = input_i8.size() * sizeof(int8_t);
  113. inputs[0].buf = input_i8.data();
  114. inputs[0].pass_through = 1;
  115. int ret = rknn_inputs_set(ctx_, 1, inputs);
  116. if (ret < 0) {
  117. cerr << "[RKNN] rknn_inputs_set failed: " << ret << endl;
  118. return {};
  119. }
  120. ret = rknn_run(ctx_, nullptr);
  121. if (ret < 0) {
  122. cerr << "[RKNN] rknn_run failed: " << ret << endl;
  123. return {};
  124. }
  125. vector<rknn_output> outputs(n_output_);
  126. for (uint32_t i = 0; i < n_output_; i++) {
  127. memset(&outputs[i], 0, sizeof(rknn_output));
  128. outputs[i].want_float = 1;
  129. }
  130. ret = rknn_outputs_get(ctx_, n_output_, outputs.data(), nullptr);
  131. if (ret < 0) {
  132. cerr << "[RKNN] rknn_outputs_get failed: " << ret << endl;
  133. return {};
  134. }
  135. vector<Detection> dets;
  136. if (n_output_ == 1) {
  137. auto& attr = output_attrs_[0];
  138. int num_preds = attr.dims[1];
  139. int num_attrs = attr.dims[2];
  140. float* data = static_cast<float*>(outputs[0].buf);
  141. if (num_attrs == 85) {
  142. for (int i = 0; i < num_preds; i++) {
  143. float* pred = data + i * num_attrs;
  144. float obj = pred[4];
  145. if (obj < conf_thresh_) continue;
  146. float max_score = 0;
  147. int max_id = -1;
  148. for (int c = 5; c < 85; c++) {
  149. if (pred[c] > max_score) {
  150. max_score = pred[c];
  151. max_id = c - 5;
  152. }
  153. }
  154. float final_score = obj * max_score;
  155. if (final_score < conf_thresh_) continue;
  156. float cx = pred[0];
  157. float cy = pred[1];
  158. float w = pred[2];
  159. float h = pred[3];
  160. float x1 = (cx - w * 0.5f - pad_x) / scale;
  161. float y1 = (cy - h * 0.5f - pad_y) / scale;
  162. float x2 = (cx + w * 0.5f - pad_x) / scale;
  163. float y2 = (cy + h * 0.5f - pad_y) / scale;
  164. int ix1 = max(0, (int)x1);
  165. int iy1 = max(0, (int)y1);
  166. int ix2 = min(img_w, (int)x2);
  167. int iy2 = min(img_h, (int)y2);
  168. int bw = ix2 - ix1;
  169. int bh = iy2 - iy1;
  170. if (bw <= 0 || bh <= 0) continue;
  171. Detection det;
  172. det.bbox = cv::Rect(ix1, iy1, bw, bh);
  173. det.confidence = final_score;
  174. det.class_id = max_id;
  175. det.class_name = (max_id >= 0 && max_id < (int)COCO_NAMES.size())
  176. ? COCO_NAMES[max_id] : "unknown";
  177. dets.push_back(det);
  178. }
  179. }
  180. } else if (n_output_ == 3) {
  181. static const int ANCHORS[3][6] = {
  182. {10,13, 16,30, 33,23},
  183. {30,61, 62,45, 59,119},
  184. {116,90, 156,198, 373,326}
  185. };
  186. int grid_sizes[3] = {80, 40, 20};
  187. int strides[3] = {8, 16, 32};
  188. for (int g = 0; g < 3; g++) {
  189. float* data = static_cast<float*>(outputs[g].buf);
  190. auto& attr = output_attrs_[g];
  191. int grid_h = attr.dims[2];
  192. int grid_w = attr.dims[3];
  193. int grid_len = grid_h * grid_w;
  194. int stride = strides[g];
  195. for (int a = 0; a < 3; a++) {
  196. for (int i = 0; i < grid_h; i++) {
  197. for (int j = 0; j < grid_w; j++) {
  198. // YOLOv5 输出 255 通道 = 3锚点 x 85(tx,ty,tw,th,obj,80类),
  199. // 锚点步长为 85; 模型已融合 sigmoid, 输出值域 [0,1]
  200. int offset = (85 * a + 4) * grid_len + i * grid_w + j;
  201. float obj = data[offset];
  202. if (obj < conf_thresh_) continue;
  203. float* in_ptr = data + (85 * a) * grid_len + i * grid_w + j;
  204. float box_x = in_ptr[0] * 2.0f - 0.5f;
  205. float box_y = in_ptr[grid_len] * 2.0f - 0.5f;
  206. float box_w = in_ptr[2 * grid_len] * 2.0f;
  207. float box_h = in_ptr[3 * grid_len] * 2.0f;
  208. box_x = (box_x + j) * stride;
  209. box_y = (box_y + i) * stride;
  210. // 每个尺度的第a个锚点: ANCHORS[g][a*2], ANCHORS[g][a*2+1]
  211. box_w = box_w * box_w * ANCHORS[g][a * 2];
  212. box_h = box_h * box_h * ANCHORS[g][a * 2 + 1];
  213. float max_score = in_ptr[5 * grid_len];
  214. int max_id = 0;
  215. for (int k = 1; k < 80; k++) {
  216. float prob = in_ptr[(5 + k) * grid_len];
  217. if (prob > max_score) {
  218. max_score = prob;
  219. max_id = k;
  220. }
  221. }
  222. float final_score = obj * max_score;
  223. if (final_score < conf_thresh_) continue;
  224. float x1 = (box_x - box_w * 0.5f - pad_x) / scale;
  225. float y1 = (box_y - box_h * 0.5f - pad_y) / scale;
  226. float x2 = (box_x + box_w * 0.5f - pad_x) / scale;
  227. float y2 = (box_y + box_h * 0.5f - pad_y) / scale;
  228. int ix1 = max(0, (int)x1);
  229. int iy1 = max(0, (int)y1);
  230. int ix2 = min(img_w, (int)x2);
  231. int iy2 = min(img_h, (int)y2);
  232. int bw = ix2 - ix1;
  233. int bh = iy2 - iy1;
  234. if (bw <= 0 || bh <= 0) continue;
  235. Detection det;
  236. det.bbox = cv::Rect(ix1, iy1, bw, bh);
  237. det.confidence = final_score;
  238. det.class_id = max_id;
  239. det.class_name = (max_id >= 0 && max_id < (int)COCO_NAMES.size())
  240. ? COCO_NAMES[max_id] : "unknown";
  241. dets.push_back(det);
  242. }
  243. }
  244. }
  245. }
  246. }
  247. rknn_outputs_release(ctx_, n_output_, outputs.data());
  248. nms(dets, nms_thresh_);
  249. return dets;
  250. }
  251. void YoloDetector::nms(vector<Detection>& dets, float threshold) {
  252. if (dets.empty()) return;
  253. sort(dets.begin(), dets.end(), [](const Detection& a, const Detection& b) {
  254. return a.confidence > b.confidence;
  255. });
  256. vector<bool> suppressed(dets.size(), false);
  257. for (size_t i = 0; i < dets.size(); i++) {
  258. if (suppressed[i]) continue;
  259. for (size_t j = i + 1; j < dets.size(); j++) {
  260. if (suppressed[j]) continue;
  261. float ix1 = dets[i].bbox.x, iy1 = dets[i].bbox.y;
  262. float ix2 = ix1 + dets[i].bbox.width, iy2 = iy1 + dets[i].bbox.height;
  263. float jx1 = dets[j].bbox.x, jy1 = dets[j].bbox.y;
  264. float jx2 = jx1 + dets[j].bbox.width, jy2 = jy1 + dets[j].bbox.height;
  265. float xx1 = max(ix1, jx1), yy1 = max(iy1, jy1);
  266. float xx2 = min(ix2, jx2), yy2 = min(iy2, jy2);
  267. float inter = max(0.f, xx2 - xx1) * max(0.f, yy2 - yy1);
  268. float area_i = (ix2 - ix1) * (iy2 - iy1);
  269. float area_j = (jx2 - jx1) * (jy2 - jy1);
  270. float iou = inter / (area_i + area_j - inter + 1e-6f);
  271. if (iou > threshold) suppressed[j] = true;
  272. }
  273. }
  274. vector<Detection> result;
  275. for (size_t i = 0; i < dets.size(); i++)
  276. if (!suppressed[i]) result.push_back(dets[i]);
  277. dets = result;
  278. }