Update YOLO node to support automatic V8 box format and enhance box decoding

This commit is contained in:
sladro 2026-02-27 19:46:27 +08:00
parent 60ccab8e67
commit bbf987fe87
2 changed files with 74 additions and 14 deletions

View File

@ -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,

View File

@ -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<float>(model_w);
b *= static_cast<float>(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<float>& boxes, std::vector<float>& obj_probs,
std::vector<int>& 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<float>& boxes, std::vector<float>& obj_probs,
std::vector<int>& 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<std::string>("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<int8_t*>(const_cast<uint8_t*>(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<int8_t*>(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);
}
}