修复人脸画框4
Some checks are pending
CI / host-build (push) Waiting to run
CI / rk3588-cross-build (push) Waiting to run

This commit is contained in:
sladro 2026-01-07 18:03:48 +08:00
parent 63f448c108
commit c1d4162d08
2 changed files with 101 additions and 28 deletions

View File

@ -24,8 +24,8 @@
"type": "preprocess",
"role": "filter",
"enable": true,
"dst_w": 640,
"dst_h": 640,
"dst_w": 1280,
"dst_h": 720,
"dst_format": "rgb",
"keep_ratio": false,
"use_rga": true
@ -36,12 +36,13 @@
"role": "filter",
"enable": true,
"model_path": "./models/RetinaFace_mobile320.rknn",
"conf": 0.2,
"conf": 0.3,
"nms": 0.4,
"max_faces": 10,
"output_landmarks": true,
"input_format": "bgr",
"face_class_index": -1
"face_class_index": 1,
"letterbox": true
},
{
"id": "osd_cam1",

View File

@ -368,6 +368,42 @@ void ResizeRgbBilinear(const uint8_t* src, int src_w, int src_h, int src_stride,
}
}
struct LetterboxInfo {
float scale = 1.0f;
int pad_x = 0;
int pad_y = 0;
};
void LetterboxRgb(const uint8_t* src, int src_w, int src_h, int src_stride,
uint8_t* dst, int dst_w, int dst_h, bool swap_rb,
uint8_t pad_value, LetterboxInfo& info) {
info = {};
if (!src || !dst || src_w <= 0 || src_h <= 0 || dst_w <= 0 || dst_h <= 0 || src_stride <= 0) return;
const float s = std::min(static_cast<float>(dst_w) / static_cast<float>(src_w),
static_cast<float>(dst_h) / static_cast<float>(src_h));
const int rw = std::max(1, static_cast<int>(std::round(static_cast<float>(src_w) * s)));
const int rh = std::max(1, static_cast<int>(std::round(static_cast<float>(src_h) * s)));
const int px = (dst_w - rw) / 2;
const int py = (dst_h - rh) / 2;
info.scale = s;
info.pad_x = px;
info.pad_y = py;
// fill background
std::fill(dst, dst + static_cast<size_t>(dst_w) * static_cast<size_t>(dst_h) * 3, pad_value);
std::vector<uint8_t> tmp(static_cast<size_t>(rw) * static_cast<size_t>(rh) * 3);
ResizeRgbBilinear(src, src_w, src_h, src_stride, tmp.data(), rw, rh, swap_rb);
for (int y = 0; y < rh; ++y) {
uint8_t* drow = dst + (static_cast<size_t>(py + y) * static_cast<size_t>(dst_w) + static_cast<size_t>(px)) * 3;
const uint8_t* srow = tmp.data() + static_cast<size_t>(y) * static_cast<size_t>(rw) * 3;
memcpy(drow, srow, static_cast<size_t>(rw) * 3);
}
}
} // namespace
class AiFaceDetNode : public INode {
@ -382,6 +418,7 @@ public:
nms_thresh_ = config.ValueOr<float>("nms", 0.4f);
max_faces_ = std::max(1, config.ValueOr<int>("max_faces", 10));
output_landmarks_ = config.ValueOr<bool>("output_landmarks", true);
letterbox_ = config.ValueOr<bool>("letterbox", letterbox_);
face_class_index_ = config.ValueOr<int>("face_class_index", face_class_index_);
if (face_class_index_ != 0 && face_class_index_ != 1) {
face_class_index_ = -1; // auto
@ -541,22 +578,30 @@ private:
const uint8_t* input_ptr = nullptr;
// Fast path: already packed, correct size, no channel swap.
if (!need_swap && src_w == in_w && src_h == in_h &&
static_cast<size_t>(src_stride) == src_row && frame->data_size >= src_row * static_cast<size_t>(src_h)) {
input_ptr = src;
LetterboxInfo lb;
if (!letterbox_) {
// Fast path: already packed, correct size, no channel swap.
if (!need_swap && src_w == in_w && src_h == in_h &&
static_cast<size_t>(src_stride) == src_row && frame->data_size >= src_row * static_cast<size_t>(src_h)) {
input_ptr = src;
} else {
input_buf_.resize(in_size);
if (src_w == in_w && src_h == in_h && static_cast<size_t>(src_stride) == src_row) {
memcpy(input_buf_.data(), src, in_size);
if (need_swap) {
for (size_t i = 0; i < in_size; i += 3) {
std::swap(input_buf_[i], input_buf_[i + 2]);
}
}
} else {
ResizeRgbBilinear(src, src_w, src_h, src_stride, input_buf_.data(), in_w, in_h, need_swap);
}
input_ptr = input_buf_.data();
}
} else {
input_buf_.resize(in_size);
if (src_w == in_w && src_h == in_h && static_cast<size_t>(src_stride) == src_row) {
memcpy(input_buf_.data(), src, in_size);
if (need_swap) {
for (size_t i = 0; i < in_size; i += 3) {
std::swap(input_buf_[i], input_buf_[i + 2]);
}
}
} else {
ResizeRgbBilinear(src, src_w, src_h, src_stride, input_buf_.data(), in_w, in_h, need_swap);
}
LetterboxRgb(src, src_w, src_h, src_stride, input_buf_.data(), in_w, in_h,
need_swap, 0, lb);
input_ptr = input_buf_.data();
}
@ -609,7 +654,7 @@ private:
det.img_h = src_h;
det.model_name = "retinaface";
DecodeRetinaFace(tensors, src_w, src_h, in_w, in_h, det);
DecodeRetinaFace(tensors, src_w, src_h, in_w, in_h, lb, det);
if (!logged_decode_.exchange(true)) {
LogInfo("[ai_face_det] decoded faces=" + std::to_string(det.faces.size()));
}
@ -619,6 +664,7 @@ private:
void DecodeRetinaFace(const std::vector<Tensor>& outs,
int orig_w, int orig_h,
int in_w, int in_h,
const LetterboxInfo& lb,
FaceDetResult& out) {
// Find loc/conf/landms tensors.
std::vector<NcTensor> locs;
@ -681,6 +727,8 @@ private:
int face_idx = face_class_index_;
float max_p0 = 0.0f;
float max_p1 = 0.0f;
double sum_p0 = 0.0;
double sum_p1 = 0.0;
if (face_idx < 0) {
for (int i = 0; i < n; ++i) {
const float s0 = conf.data[static_cast<size_t>(i) * 2 + 0];
@ -694,8 +742,13 @@ private:
const float p0 = 1.0f - p1;
if (p0 > max_p0) max_p0 = p0;
if (p1 > max_p1) max_p1 = p1;
sum_p0 += p0;
sum_p1 += p1;
}
face_idx = (max_p1 >= max_p0) ? 1 : 0;
const double mean_p0 = sum_p0 / static_cast<double>(n);
const double mean_p1 = sum_p1 / static_cast<double>(n);
// Face channel is typically sparse (low mean). Background channel has high mean.
face_idx = (mean_p1 <= mean_p0) ? 1 : 0;
}
if (!logged_conf_probe_.exchange(true)) {
@ -712,8 +765,8 @@ private:
return;
}
const float sx = static_cast<float>(orig_w) / static_cast<float>(in_w);
const float sy = static_cast<float>(orig_h) / static_cast<float>(in_h);
const bool use_lb = letterbox_ && lb.scale > 0.0f;
const float inv_s = use_lb ? (1.0f / lb.scale) : 1.0f;
std::vector<Rect> boxes;
std::vector<float> scores;
@ -756,10 +809,19 @@ private:
float x2 = (cx + ww * 0.5f) * static_cast<float>(in_w);
float y2 = (cy + hh * 0.5f) * static_cast<float>(in_h);
x1 *= sx;
x2 *= sx;
y1 *= sy;
y2 *= sy;
if (use_lb) {
x1 = (x1 - static_cast<float>(lb.pad_x)) * inv_s;
x2 = (x2 - static_cast<float>(lb.pad_x)) * inv_s;
y1 = (y1 - static_cast<float>(lb.pad_y)) * inv_s;
y2 = (y2 - static_cast<float>(lb.pad_y)) * inv_s;
} else {
const float sx = static_cast<float>(orig_w) / static_cast<float>(in_w);
const float sy = static_cast<float>(orig_h) / static_cast<float>(in_h);
x1 *= sx;
x2 *= sx;
y1 *= sy;
y2 *= sy;
}
Rect bb;
bb.x = static_cast<float>(ClampInt(static_cast<int>(x1), 0, orig_w - 1));
@ -778,8 +840,17 @@ private:
for (int k = 0; k < 5; ++k) {
const float lx = lmk.data[static_cast<size_t>(i) * 10 + k * 2 + 0];
const float ly = lmk.data[static_cast<size_t>(i) * 10 + k * 2 + 1];
const float px = (p.cx + lx * var0 * p.w) * static_cast<float>(in_w) * sx;
const float py = (p.cy + ly * var0 * p.h) * static_cast<float>(in_h) * sy;
float px = (p.cx + lx * var0 * p.w) * static_cast<float>(in_w);
float py = (p.cy + ly * var0 * p.h) * static_cast<float>(in_h);
if (use_lb) {
px = (px - static_cast<float>(lb.pad_x)) * inv_s;
py = (py - static_cast<float>(lb.pad_y)) * inv_s;
} else {
const float sx = static_cast<float>(orig_w) / static_cast<float>(in_w);
const float sy = static_cast<float>(orig_h) / static_cast<float>(in_h);
px *= sx;
py *= sy;
}
pts[k].x = static_cast<float>(ClampInt(static_cast<int>(px), 0, orig_w - 1));
pts[k].y = static_cast<float>(ClampInt(static_cast<int>(py), 0, orig_h - 1));
}
@ -825,6 +896,7 @@ private:
int max_faces_ = 10;
bool output_landmarks_ = true;
int face_class_index_ = -1; // -1=auto, 0/1=explicit
bool letterbox_ = false;
std::string input_format_ = "rgb";