Improve YOLOv8 parsing in shoe detector
This commit is contained in:
parent
3383e13e88
commit
f71c7eee74
@ -5,8 +5,10 @@
|
||||
*/
|
||||
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
#include <limits>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
@ -45,6 +47,226 @@ struct DynamicRoiConfig {
|
||||
float height_scale = 0.38f;
|
||||
};
|
||||
|
||||
enum class V8BoxFormat { Auto, CxCyWh, XyXy, XyWh };
|
||||
enum class V8ClsActivation { Auto, None, Sigmoid };
|
||||
|
||||
uint32_t TensorTypeSizeBytes(rknn_tensor_type t) {
|
||||
switch (t) {
|
||||
case RKNN_TENSOR_INT8:
|
||||
case RKNN_TENSOR_UINT8:
|
||||
return 1;
|
||||
case RKNN_TENSOR_FLOAT16:
|
||||
return 2;
|
||||
case RKNN_TENSOR_FLOAT32:
|
||||
return 4;
|
||||
default:
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
inline float Sigmoid(float x) {
|
||||
return 1.0f / (1.0f + std::exp(-x));
|
||||
}
|
||||
|
||||
inline float Fp16ToFp32(uint16_t h) {
|
||||
const int sign = (h & 0x8000) ? -1 : 1;
|
||||
const int exp = (h >> 10) & 0x1F;
|
||||
const int mant = h & 0x03FF;
|
||||
|
||||
if (exp == 0) {
|
||||
if (mant == 0) return sign < 0 ? -0.0f : 0.0f;
|
||||
return static_cast<float>(sign) * std::ldexp(static_cast<float>(mant), -24);
|
||||
}
|
||||
if (exp == 0x1F) {
|
||||
if (mant == 0) return sign < 0 ? -INFINITY : INFINITY;
|
||||
return std::numeric_limits<float>::quiet_NaN();
|
||||
}
|
||||
return static_cast<float>(sign) *
|
||||
std::ldexp(static_cast<float>(mant + 1024), exp - 25);
|
||||
}
|
||||
|
||||
inline int32_t ClipFloat(float val, float min_val, float max_val) {
|
||||
return static_cast<int32_t>(val <= min_val ? min_val : (val >= max_val ? max_val : val));
|
||||
}
|
||||
|
||||
inline int8_t QuantizeF32ToAffine(float f32, int32_t zp, float scale) {
|
||||
float dst_val = (f32 / scale) + zp;
|
||||
return static_cast<int8_t>(ClipFloat(dst_val, -128, 127));
|
||||
}
|
||||
|
||||
inline float DequantizeAffineToF32(int8_t qnt, int32_t zp, float scale) {
|
||||
return (static_cast<float>(qnt) - static_cast<float>(zp)) * scale;
|
||||
}
|
||||
|
||||
int DefaultV8NumBoxes(int model_h, int model_w) {
|
||||
if (model_h <= 0 || model_w <= 0) return 0;
|
||||
return (model_h / 8) * (model_w / 8) +
|
||||
(model_h / 16) * (model_w / 16) +
|
||||
(model_h / 32) * (model_w / 32);
|
||||
}
|
||||
|
||||
struct V8LayoutInfo {
|
||||
int num_boxes = 0;
|
||||
bool channels_first = true;
|
||||
};
|
||||
|
||||
float ScoreBoxCandidate(float x, float y, float w, float h, int model_w, int model_h) {
|
||||
float s = 0.0f;
|
||||
if (w > 0.0f && h > 0.0f) s += 3.0f;
|
||||
if (w <= model_w * 1.2f) s += 1.0f;
|
||||
if (h <= model_h * 1.2f) s += 1.0f;
|
||||
if (x >= -model_w * 0.1f) s += 1.0f;
|
||||
if (y >= -model_h * 0.1f) s += 1.0f;
|
||||
if ((x + w) <= model_w * 1.2f) s += 1.0f;
|
||||
if ((y + h) <= model_h * 1.2f) s += 1.0f;
|
||||
return s;
|
||||
}
|
||||
|
||||
bool SeemsNormalized(float a, float b, float c, float d) {
|
||||
auto in_range = [](float v) { return v >= -0.05f && v <= 2.5f; };
|
||||
return in_range(a) && in_range(b) && in_range(c) && in_range(d);
|
||||
}
|
||||
|
||||
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) {
|
||||
if (SeemsNormalized(a, b, c, d)) {
|
||||
a *= static_cast<float>(model_w);
|
||||
b *= static_cast<float>(model_h);
|
||||
c *= static_cast<float>(model_w);
|
||||
d *= static_cast<float>(model_h);
|
||||
}
|
||||
|
||||
auto decode_cxcywh = [&](float& x, float& y, float& w, float& h) {
|
||||
x = a - c / 2.0f;
|
||||
y = b - d / 2.0f;
|
||||
w = c;
|
||||
h = d;
|
||||
};
|
||||
auto decode_xyxy = [&](float& x, float& y, float& w, float& h) {
|
||||
x = a;
|
||||
y = b;
|
||||
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);
|
||||
return;
|
||||
}
|
||||
if (fmt == V8BoxFormat::XyXy) {
|
||||
decode_xyxy(out_x, out_y, out_w, out_h);
|
||||
return;
|
||||
}
|
||||
if (fmt == V8BoxFormat::XyWh) {
|
||||
decode_xywh(out_x, out_y, out_w, out_h);
|
||||
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);
|
||||
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;
|
||||
} else if (s3 >= s1 && s3 >= s2) {
|
||||
out_x = x3; out_y = y3; out_w = w3; out_h = h3;
|
||||
} else {
|
||||
out_x = x1; out_y = y1; out_w = w1; out_h = h1;
|
||||
}
|
||||
}
|
||||
|
||||
bool ResolveV8ApplySigmoid(const float* output, int num_boxes, int num_classes, bool channels_first,
|
||||
V8ClsActivation act_mode) {
|
||||
if (act_mode == V8ClsActivation::None) return false;
|
||||
if (act_mode == V8ClsActivation::Sigmoid) return true;
|
||||
if (!output || num_boxes <= 0 || num_classes <= 0) return false;
|
||||
|
||||
const int num_channels = 4 + num_classes;
|
||||
const int sample_boxes = std::min(num_boxes, 64);
|
||||
float min_v = 1e9f;
|
||||
float max_v = -1e9f;
|
||||
for (int i = 0; i < sample_boxes; ++i) {
|
||||
for (int c = 0; c < num_classes; ++c) {
|
||||
const float v = channels_first ? output[(4 + c) * num_boxes + i]
|
||||
: output[i * num_channels + (4 + c)];
|
||||
if (v < min_v) min_v = v;
|
||||
if (v > max_v) max_v = v;
|
||||
}
|
||||
}
|
||||
return (min_v < -0.1f || max_v > 1.5f);
|
||||
}
|
||||
|
||||
V8LayoutInfo ResolveV8Layout(const std::vector<uint32_t>& dims, size_t byte_size,
|
||||
rknn_tensor_type type, int num_classes,
|
||||
int model_h, int model_w) {
|
||||
V8LayoutInfo info;
|
||||
const int num_channels = 4 + num_classes;
|
||||
const uint32_t elem_bytes = TensorTypeSizeBytes(type);
|
||||
const size_t total_elems = elem_bytes > 0 ? (byte_size / elem_bytes) : 0;
|
||||
const size_t max_boxes_from_data = static_cast<size_t>(num_channels) > 0
|
||||
? (total_elems / static_cast<size_t>(num_channels))
|
||||
: 0;
|
||||
|
||||
int ch_idx = -1;
|
||||
for (size_t i = 0; i < dims.size(); ++i) {
|
||||
if (dims[i] == static_cast<uint32_t>(num_channels)) {
|
||||
ch_idx = static_cast<int>(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (ch_idx >= 0 && total_elems >= static_cast<size_t>(num_channels)) {
|
||||
info.num_boxes = static_cast<int>(max_boxes_from_data);
|
||||
int prev_non1 = 1;
|
||||
for (int i = ch_idx - 1; i >= 0; --i) {
|
||||
if (dims[static_cast<size_t>(i)] > 1U) {
|
||||
prev_non1 = static_cast<int>(dims[static_cast<size_t>(i)]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
int next_non1 = 1;
|
||||
for (size_t i = static_cast<size_t>(ch_idx + 1); i < dims.size(); ++i) {
|
||||
if (dims[i] > 1U) {
|
||||
next_non1 = static_cast<int>(dims[i]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (next_non1 > 1 && prev_non1 <= 1) {
|
||||
info.channels_first = true;
|
||||
} else if (prev_non1 > 1 && next_non1 <= 1) {
|
||||
info.channels_first = false;
|
||||
} else if (next_non1 > 1 && prev_non1 > 1) {
|
||||
info.channels_first = next_non1 >= prev_non1;
|
||||
}
|
||||
}
|
||||
|
||||
if (info.num_boxes <= 0 && max_boxes_from_data > 0) {
|
||||
info.num_boxes = static_cast<int>(max_boxes_from_data);
|
||||
}
|
||||
if (info.num_boxes <= 0) {
|
||||
info.num_boxes = DefaultV8NumBoxes(model_h, model_w);
|
||||
}
|
||||
if (info.num_boxes <= 0) {
|
||||
info.num_boxes = 8400;
|
||||
}
|
||||
if (max_boxes_from_data > 0 && static_cast<size_t>(info.num_boxes) > max_boxes_from_data) {
|
||||
info.num_boxes = static_cast<int>(max_boxes_from_data);
|
||||
}
|
||||
return info;
|
||||
}
|
||||
|
||||
class AiShoeDetNode : public INode {
|
||||
public:
|
||||
std::string Id() const override { return id_; }
|
||||
@ -322,30 +544,23 @@ private:
|
||||
int win_x, int win_y, int win_w, int win_h) {
|
||||
std::vector<DetBox> dets;
|
||||
if (outputs.empty() || !outputs[0].data) return dets;
|
||||
|
||||
if (outputs[0].type != RKNN_TENSOR_FLOAT32 || outputs[0].size < sizeof(float) * 5) {
|
||||
LogWarn("[ai_shoe_det] unsupported output tensor type or size");
|
||||
return dets;
|
||||
}
|
||||
|
||||
// 假设 YOLOv8 输出格式: [num_boxes, 5] = x, y, w, h, conf
|
||||
// 实际格式可能不同,需要根据模型调整
|
||||
const float* data = reinterpret_cast<const float*>(outputs[0].data);
|
||||
const size_t value_count = outputs[0].size / sizeof(float);
|
||||
const int num_boxes = static_cast<int>(std::min<size_t>(8400, value_count / 5));
|
||||
constexpr int kNumClasses = 1;
|
||||
const V8LayoutInfo layout = ResolveV8Layout(
|
||||
outputs[0].dims, outputs[0].size, outputs[0].type, kNumClasses, model_h_, model_w_);
|
||||
const int num_boxes = layout.num_boxes;
|
||||
if (num_boxes <= 0) return dets;
|
||||
|
||||
float scale_x = static_cast<float>(win_w) / model_w_;
|
||||
float scale_y = static_cast<float>(win_h) / model_h_;
|
||||
const int num_channels = 4 + kNumClasses;
|
||||
|
||||
for (int i = 0; i < num_boxes; ++i) {
|
||||
float x = data[i * 5 + 0];
|
||||
float y = data[i * 5 + 1];
|
||||
float w = data[i * 5 + 2];
|
||||
float h = data[i * 5 + 3];
|
||||
float conf = data[i * 5 + 4];
|
||||
|
||||
if (conf < conf_thresh_) continue;
|
||||
auto append_box = [&](float a, float b, float c, float d, float conf) {
|
||||
if (!std::isfinite(a) || !std::isfinite(b) || !std::isfinite(c) || !std::isfinite(d)) return;
|
||||
float x = 0.0f, y = 0.0f, w = 0.0f, h = 0.0f;
|
||||
DecodeV8Box(a, b, c, d, model_w_, model_h_, V8BoxFormat::Auto, x, y, w, h);
|
||||
if (!std::isfinite(x) || !std::isfinite(y) || !std::isfinite(w) || !std::isfinite(h)) return;
|
||||
if (w <= 1e-3f || h <= 1e-3f) return;
|
||||
if (w > model_w_ * 1.2f || h > model_h_ * 1.2f) return;
|
||||
|
||||
DetBox box;
|
||||
box.x = win_x + x * scale_x;
|
||||
@ -355,6 +570,58 @@ private:
|
||||
box.conf = conf;
|
||||
box.class_id = dynamic_roi_.shoe_class_id;
|
||||
dets.push_back(box);
|
||||
};
|
||||
|
||||
if (outputs[0].type == RKNN_TENSOR_FLOAT32) {
|
||||
const float* data = reinterpret_cast<const float*>(outputs[0].data);
|
||||
const bool apply_sigmoid = ResolveV8ApplySigmoid(
|
||||
data, num_boxes, kNumClasses, layout.channels_first, V8ClsActivation::Auto);
|
||||
for (int i = 0; i < num_boxes; ++i) {
|
||||
const float score = layout.channels_first ? data[4 * num_boxes + i]
|
||||
: data[i * num_channels + 4];
|
||||
const float conf = apply_sigmoid ? Sigmoid(score) : score;
|
||||
if (conf < conf_thresh_) continue;
|
||||
const float a = layout.channels_first ? data[0 * num_boxes + i] : data[i * num_channels + 0];
|
||||
const float b = layout.channels_first ? data[1 * num_boxes + i] : data[i * num_channels + 1];
|
||||
const float c = layout.channels_first ? data[2 * num_boxes + i] : data[i * num_channels + 2];
|
||||
const float d = layout.channels_first ? data[3 * num_boxes + i] : data[i * num_channels + 3];
|
||||
append_box(a, b, c, d, conf);
|
||||
}
|
||||
} else if (outputs[0].type == RKNN_TENSOR_FLOAT16) {
|
||||
const uint16_t* data = reinterpret_cast<const uint16_t*>(outputs[0].data);
|
||||
fp32_decode_buf_.resize(outputs[0].size / sizeof(uint16_t));
|
||||
for (size_t i = 0; i < fp32_decode_buf_.size(); ++i) {
|
||||
fp32_decode_buf_[i] = Fp16ToFp32(data[i]);
|
||||
}
|
||||
const bool apply_sigmoid = ResolveV8ApplySigmoid(
|
||||
fp32_decode_buf_.data(), num_boxes, kNumClasses, layout.channels_first, V8ClsActivation::Auto);
|
||||
for (int i = 0; i < num_boxes; ++i) {
|
||||
const float score = layout.channels_first ? fp32_decode_buf_[4 * num_boxes + i]
|
||||
: fp32_decode_buf_[i * num_channels + 4];
|
||||
const float conf = apply_sigmoid ? Sigmoid(score) : score;
|
||||
if (conf < conf_thresh_) continue;
|
||||
const float a = layout.channels_first ? fp32_decode_buf_[0 * num_boxes + i] : fp32_decode_buf_[i * num_channels + 0];
|
||||
const float b = layout.channels_first ? fp32_decode_buf_[1 * num_boxes + i] : fp32_decode_buf_[i * num_channels + 1];
|
||||
const float c = layout.channels_first ? fp32_decode_buf_[2 * num_boxes + i] : fp32_decode_buf_[i * num_channels + 2];
|
||||
const float d = layout.channels_first ? fp32_decode_buf_[3 * num_boxes + i] : fp32_decode_buf_[i * num_channels + 3];
|
||||
append_box(a, b, c, d, conf);
|
||||
}
|
||||
} else if (outputs[0].type == RKNN_TENSOR_INT8) {
|
||||
const int8_t* data = reinterpret_cast<const int8_t*>(outputs[0].data);
|
||||
const int8_t thresh_i8 = QuantizeF32ToAffine(conf_thresh_, outputs[0].zp, outputs[0].scale);
|
||||
for (int i = 0; i < num_boxes; ++i) {
|
||||
const int8_t score_i8 = layout.channels_first ? data[4 * num_boxes + i]
|
||||
: data[i * num_channels + 4];
|
||||
if (score_i8 < thresh_i8) continue;
|
||||
const float conf = DequantizeAffineToF32(score_i8, outputs[0].zp, outputs[0].scale);
|
||||
const float a = DequantizeAffineToF32(layout.channels_first ? data[0 * num_boxes + i] : data[i * num_channels + 0], outputs[0].zp, outputs[0].scale);
|
||||
const float b = DequantizeAffineToF32(layout.channels_first ? data[1 * num_boxes + i] : data[i * num_channels + 1], outputs[0].zp, outputs[0].scale);
|
||||
const float c = DequantizeAffineToF32(layout.channels_first ? data[2 * num_boxes + i] : data[i * num_channels + 2], outputs[0].zp, outputs[0].scale);
|
||||
const float d = DequantizeAffineToF32(layout.channels_first ? data[3 * num_boxes + i] : data[i * num_channels + 3], outputs[0].zp, outputs[0].scale);
|
||||
append_box(a, b, c, d, conf);
|
||||
}
|
||||
} else {
|
||||
LogWarn("[ai_shoe_det] unsupported output tensor type");
|
||||
}
|
||||
|
||||
return dets;
|
||||
@ -447,6 +714,7 @@ private:
|
||||
DynamicRoiConfig dynamic_roi_;
|
||||
std::vector<DetWindow> windows_;
|
||||
std::vector<uint8_t> input_buf_;
|
||||
std::vector<float> fp32_decode_buf_;
|
||||
|
||||
std::shared_ptr<SpscQueue<FramePtr>> input_queue_;
|
||||
std::vector<std::shared_ptr<SpscQueue<FramePtr>>> output_queues_;
|
||||
|
||||
Loading…
Reference in New Issue
Block a user