From bbf987fe87d9ca030f27e8e9e6d28258065ce481 Mon Sep 17 00:00:00 2001 From: sladro Date: Fri, 27 Feb 2026 19:46:27 +0800 Subject: [PATCH] Update YOLO node to support automatic V8 box format and enhance box decoding --- configs/test_cam1_ppe11_yolo_debug.json | 2 +- plugins/ai_yolo/ai_yolo_node.cpp | 86 +++++++++++++++++++++---- 2 files changed, 74 insertions(+), 14 deletions(-) diff --git a/configs/test_cam1_ppe11_yolo_debug.json b/configs/test_cam1_ppe11_yolo_debug.json index 526a8ca..a9eae75 100644 --- a/configs/test_cam1_ppe11_yolo_debug.json +++ b/configs/test_cam1_ppe11_yolo_debug.json @@ -49,7 +49,7 @@ "model_w": 768, "model_h": 768, "num_classes": 11, - "v8_box_format": "cxcywh", + "v8_box_format": "auto", "v8_cls_activation": "auto", "conf": 0.25, "nms": 0.45, diff --git a/plugins/ai_yolo/ai_yolo_node.cpp b/plugins/ai_yolo/ai_yolo_node.cpp index 0910018..0ddddd9 100644 --- a/plugins/ai_yolo/ai_yolo_node.cpp +++ b/plugins/ai_yolo/ai_yolo_node.cpp @@ -34,7 +34,7 @@ const int kAnchor1[6] = {30, 61, 62, 45, 59, 119}; const int kAnchor2[6] = {116, 90, 156, 198, 373, 326}; enum class YoloVersion { V5, V8 }; -enum class V8BoxFormat { Auto, CxCyWh, XyXy }; +enum class V8BoxFormat { Auto, CxCyWh, XyXy, XyWh }; enum class V8ClsActivation { Auto, None, Sigmoid }; const char* kCocoLabels[kObjClassNum] = { @@ -248,8 +248,17 @@ bool SeemsNormalized(float a, float b, float c, float d) { return in_range(a) && in_range(b) && in_range(c) && in_range(d); } +const char* V8BoxFormatName(V8BoxFormat fmt) { + switch (fmt) { + case V8BoxFormat::CxCyWh: return "cxcywh"; + case V8BoxFormat::XyXy: return "xyxy"; + case V8BoxFormat::XyWh: return "xywh"; + default: return "auto"; + } +} + void DecodeV8Box(float a, float b, float c, float d, int model_w, int model_h, V8BoxFormat fmt, - float& out_x, float& out_y, float& out_w, float& out_h) { + float& out_x, float& out_y, float& out_w, float& out_h, V8BoxFormat* used_fmt = nullptr) { if (SeemsNormalized(a, b, c, d)) { a *= static_cast(model_w); b *= static_cast(model_h); @@ -269,27 +278,48 @@ void DecodeV8Box(float a, float b, float c, float d, int model_w, int model_h, V w = c - a; h = d - b; }; + auto decode_xywh = [&](float& x, float& y, float& w, float& h) { + x = a; + y = b; + w = c; + h = d; + }; if (fmt == V8BoxFormat::CxCyWh) { decode_cxcywh(out_x, out_y, out_w, out_h); + if (used_fmt) *used_fmt = V8BoxFormat::CxCyWh; return; } if (fmt == V8BoxFormat::XyXy) { decode_xyxy(out_x, out_y, out_w, out_h); + if (used_fmt) *used_fmt = V8BoxFormat::XyXy; + return; + } + if (fmt == V8BoxFormat::XyWh) { + decode_xywh(out_x, out_y, out_w, out_h); + if (used_fmt) *used_fmt = V8BoxFormat::XyWh; return; } float x1 = 0.0f, y1 = 0.0f, w1 = 0.0f, h1 = 0.0f; float x2 = 0.0f, y2 = 0.0f, w2 = 0.0f, h2 = 0.0f; + float x3 = 0.0f, y3 = 0.0f, w3 = 0.0f, h3 = 0.0f; decode_cxcywh(x1, y1, w1, h1); decode_xyxy(x2, y2, w2, h2); + decode_xywh(x3, y3, w3, h3); const float s1 = ScoreBoxCandidate(x1, y1, w1, h1, model_w, model_h); const float s2 = ScoreBoxCandidate(x2, y2, w2, h2, model_w, model_h); - if (s2 > s1) { + const float s3 = ScoreBoxCandidate(x3, y3, w3, h3, model_w, model_h); + if (s2 >= s1 && s2 >= s3) { out_x = x2; out_y = y2; out_w = w2; out_h = h2; + if (used_fmt) *used_fmt = V8BoxFormat::XyXy; + } else if (s3 >= s1 && s3 >= s2) { + out_x = x3; out_y = y3; out_w = w3; out_h = h3; + if (used_fmt) *used_fmt = V8BoxFormat::XyWh; } else { out_x = x1; out_y = y1; out_w = w1; out_h = h1; + if (used_fmt) *used_fmt = V8BoxFormat::CxCyWh; } } @@ -396,7 +426,8 @@ int ProcessOutputV8(float* output, int num_boxes, int num_classes, int model_h, int model_w, std::vector& boxes, std::vector& obj_probs, std::vector& class_ids, float conf_thresh, - bool channels_first, V8BoxFormat box_format, bool apply_sigmoid) { + bool channels_first, V8BoxFormat box_format, bool apply_sigmoid, + bool debug_decode, int* debug_left) { int valid_count = 0; const int num_channels = 4 + num_classes; @@ -422,11 +453,20 @@ int ProcessOutputV8(float* output, int num_boxes, int num_classes, continue; } float x1 = 0.0f, y1 = 0.0f, w = 0.0f, h = 0.0f; - DecodeV8Box(a, b, c, d, model_w, model_h, box_format, x1, y1, w, h); + V8BoxFormat used_fmt = box_format; + DecodeV8Box(a, b, c, d, model_w, model_h, box_format, x1, y1, w, h, &used_fmt); if (!std::isfinite(x1) || !std::isfinite(y1) || !std::isfinite(w) || !std::isfinite(h)) { continue; } if (w <= 1e-3f || h <= 1e-3f) continue; + if (debug_decode && debug_left && *debug_left > 0) { + --(*debug_left); + LogInfo("[ai_yolo] v8 decode f32: raw4(" + std::to_string(a) + "," + std::to_string(b) + "," + + std::to_string(c) + "," + std::to_string(d) + ") fmt=" + V8BoxFormatName(used_fmt) + + " -> xywh(" + std::to_string(x1) + "," + std::to_string(y1) + "," + + std::to_string(w) + "," + std::to_string(h) + ") cls=" + + std::to_string(max_cls_id) + " score=" + std::to_string(max_score)); + } boxes.push_back(x1); boxes.push_back(y1); @@ -445,7 +485,8 @@ int ProcessOutputV8Int8(int8_t* output, int num_boxes, int num_classes, int model_h, int model_w, std::vector& boxes, std::vector& obj_probs, std::vector& class_ids, float conf_thresh, - int32_t zp, float scale, bool channels_first, V8BoxFormat box_format) { + int32_t zp, float scale, bool channels_first, V8BoxFormat box_format, + bool debug_decode, int* debug_left) { int valid_count = 0; int8_t thresh_i8 = QuantizeF32ToAffine(conf_thresh, zp, scale); const int num_channels = 4 + num_classes; @@ -476,11 +517,20 @@ int ProcessOutputV8Int8(int8_t* output, int num_boxes, int num_classes, continue; } float x1 = 0.0f, y1 = 0.0f, w = 0.0f, h = 0.0f; - DecodeV8Box(a, b, c, d, model_w, model_h, box_format, x1, y1, w, h); + V8BoxFormat used_fmt = box_format; + DecodeV8Box(a, b, c, d, model_w, model_h, box_format, x1, y1, w, h, &used_fmt); if (!std::isfinite(x1) || !std::isfinite(y1) || !std::isfinite(w) || !std::isfinite(h)) { continue; } if (w <= 1e-3f || h <= 1e-3f) continue; + if (debug_decode && debug_left && *debug_left > 0) { + --(*debug_left); + LogInfo("[ai_yolo] v8 decode int8: raw4(" + std::to_string(a) + "," + std::to_string(b) + "," + + std::to_string(c) + "," + std::to_string(d) + ") fmt=" + V8BoxFormatName(used_fmt) + + " -> xywh(" + std::to_string(x1) + "," + std::to_string(y1) + "," + + std::to_string(w) + "," + std::to_string(h) + ") cls=" + + std::to_string(max_cls_id) + " score=" + std::to_string(max_score)); + } boxes.push_back(x1); boxes.push_back(y1); @@ -514,6 +564,8 @@ public: const std::string bf = config.ValueOr("v8_box_format", "auto"); if (bf == "xyxy") { v8_box_format_ = V8BoxFormat::XyXy; + } else if (bf == "xywh") { + v8_box_format_ = V8BoxFormat::XyWh; } else if (bf == "cxcywh") { v8_box_format_ = V8BoxFormat::CxCyWh; } else { @@ -767,6 +819,7 @@ private: outputs[0].type, num_classes_, model_input_h_, model_input_w_); const int num_boxes = layout.num_boxes; + int debug_decode_left = (debug_det_ && processed_ < 20) ? 5 : 0; if (num_boxes <= 0) return; if (debug_det_ && processed_ < 5) { std::string dims_s; @@ -793,7 +846,8 @@ private: num_boxes, num_classes_, model_input_h_, model_input_w_, boxes, obj_probs, class_ids, conf_thresh_, - layout.channels_first, v8_box_format_, apply_sigmoid); + layout.channels_first, v8_box_format_, apply_sigmoid, + debug_det_, &debug_decode_left); } else if (outputs[0].type == RKNN_TENSOR_FLOAT16) { // Convert FP16 to FP32 size_t num_elements = outputs[0].size / sizeof(uint16_t); @@ -811,14 +865,16 @@ private: num_boxes, num_classes_, model_input_h_, model_input_w_, boxes, obj_probs, class_ids, conf_thresh_, - layout.channels_first, v8_box_format_, apply_sigmoid); + layout.channels_first, v8_box_format_, apply_sigmoid, + debug_det_, &debug_decode_left); } else { valid_count = ProcessOutputV8Int8(reinterpret_cast(const_cast(outputs[0].data)), num_boxes, num_classes_, model_input_h_, model_input_w_, boxes, obj_probs, class_ids, conf_thresh_, outputs[0].zp, outputs[0].scale, - layout.channels_first, v8_box_format_); + layout.channels_first, v8_box_format_, + debug_det_, &debug_decode_left); } } @@ -917,6 +973,7 @@ private: outputs[0].type, num_classes_, model_input_h_, model_input_w_); const int num_boxes = layout.num_boxes; + int debug_decode_left = (debug_det_ && processed_ < 20) ? 5 : 0; if (num_boxes <= 0) return; if (debug_det_ && processed_ < 5) { std::string dims_s; @@ -943,7 +1000,8 @@ private: num_boxes, num_classes_, model_input_h_, model_input_w_, boxes, obj_probs, class_ids, conf_thresh_, - layout.channels_first, v8_box_format_, apply_sigmoid); + layout.channels_first, v8_box_format_, apply_sigmoid, + debug_det_, &debug_decode_left); } else if (outputs[0].type == RKNN_TENSOR_FLOAT16) { // Convert FP16 to FP32 size_t num_elements = outputs[0].data.size() / sizeof(uint16_t); @@ -961,14 +1019,16 @@ private: num_boxes, num_classes_, model_input_h_, model_input_w_, boxes, obj_probs, class_ids, conf_thresh_, - layout.channels_first, v8_box_format_, apply_sigmoid); + layout.channels_first, v8_box_format_, apply_sigmoid, + debug_det_, &debug_decode_left); } else { valid_count = ProcessOutputV8Int8(reinterpret_cast(outputs[0].data.data()), num_boxes, num_classes_, model_input_h_, model_input_w_, boxes, obj_probs, class_ids, conf_thresh_, outputs[0].zp, outputs[0].scale, - layout.channels_first, v8_box_format_); + layout.channels_first, v8_box_format_, + debug_det_, &debug_decode_left); } }