205 lines
6.8 KiB
C++
205 lines
6.8 KiB
C++
#include <algorithm>
|
|
#include <cstdint>
|
|
#include <string>
|
|
#include <vector>
|
|
|
|
#include "hw/i_image_processor.h"
|
|
#include "node.h"
|
|
#include "utils/logger.h"
|
|
|
|
namespace rk3588 {
|
|
|
|
namespace {
|
|
|
|
PixelFormat ParseFormat(const std::string& s) {
|
|
if (s == "nv12" || s == "NV12") return PixelFormat::NV12;
|
|
if (s == "yuv420" || s == "YUV420") return PixelFormat::YUV420;
|
|
if (s == "rgb" || s == "RGB") return PixelFormat::RGB;
|
|
if (s == "bgr" || s == "BGR") return PixelFormat::BGR;
|
|
return PixelFormat::UNKNOWN;
|
|
}
|
|
|
|
} // namespace
|
|
|
|
class PreprocessNode : public INode {
|
|
public:
|
|
std::string Id() const override { return id_; }
|
|
std::string Type() const override { return "preprocess"; }
|
|
|
|
bool Init(const SimpleJson& config, const NodeContext& ctx) override {
|
|
id_ = config.ValueOr<std::string>("id", "preprocess");
|
|
dst_w_ = config.ValueOr<int>("dst_w", 640);
|
|
dst_h_ = config.ValueOr<int>("dst_h", 640);
|
|
keep_ratio_ = config.ValueOr<bool>("keep_ratio", false);
|
|
|
|
std::string fmt_str = config.ValueOr<std::string>("dst_format", "");
|
|
if (!fmt_str.empty()) {
|
|
dst_fmt_ = ParseFormat(fmt_str);
|
|
}
|
|
|
|
const bool requested_use_rga = config.ValueOr<bool>("use_rga", true);
|
|
use_rga_ = requested_use_rga;
|
|
|
|
if (const SimpleJson* dbg = config.Find("debug"); dbg && dbg->IsObject()) {
|
|
stats_log_ = dbg->ValueOr<bool>("stats", stats_log_);
|
|
stats_interval_ = std::max<uint64_t>(
|
|
1, static_cast<uint64_t>(dbg->ValueOr<int>("stats_interval", static_cast<int>(stats_interval_))));
|
|
}
|
|
|
|
input_queue_ = ctx.input_queue;
|
|
if (!input_queue_) {
|
|
LogError("[preprocess] no input queue for node " + id_);
|
|
return false;
|
|
}
|
|
if (ctx.output_queues.empty()) {
|
|
LogError("[preprocess] no output queue for node " + id_);
|
|
return false;
|
|
}
|
|
output_queues_ = ctx.output_queues;
|
|
|
|
#if !defined(RK3588_ENABLE_RGA)
|
|
if (requested_use_rga) {
|
|
LogError("[preprocess] use_rga=true but RGA not enabled at build time");
|
|
return false;
|
|
}
|
|
use_rga_ = false;
|
|
#endif
|
|
#if !defined(RK3588_ENABLE_FFMPEG)
|
|
if (!use_rga_) {
|
|
LogError("[preprocess] neither RGA nor FFmpeg enabled");
|
|
return false;
|
|
}
|
|
#endif
|
|
|
|
image_processor_ = ctx.image_processor;
|
|
if (!image_processor_) {
|
|
LogError("[preprocess] no image processor for node " + id_);
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool Start() override {
|
|
LogInfo("[preprocess] start id=" + id_ + " dst=" + std::to_string(dst_w_) + "x" +
|
|
std::to_string(dst_h_) + (use_rga_ ? " (rga)" : " (swscale)"));
|
|
return true;
|
|
}
|
|
|
|
void Stop() override {}
|
|
|
|
NodeStatus Process(FramePtr frame) override {
|
|
if (!frame) return NodeStatus::DROP;
|
|
if (!image_processor_) return NodeStatus::ERROR;
|
|
|
|
PixelFormat out_fmt = (dst_fmt_ != PixelFormat::UNKNOWN) ? dst_fmt_ : frame->format;
|
|
int out_w = dst_w_;
|
|
int out_h = dst_h_;
|
|
|
|
if (out_w <= 0) out_w = frame->width;
|
|
if (out_h <= 0) out_h = frame->height;
|
|
|
|
if (keep_ratio_ && dst_w_ > 0 && dst_h_ > 0 && frame->width > 0 && frame->height > 0) {
|
|
float scale = std::min(static_cast<float>(dst_w_) / frame->width,
|
|
static_cast<float>(dst_h_) / frame->height);
|
|
out_w = static_cast<int>(frame->width * scale);
|
|
out_h = static_cast<int>(frame->height * scale);
|
|
out_w = (out_w + 1) & ~1;
|
|
out_h = (out_h + 1) & ~1;
|
|
}
|
|
|
|
const bool need_resize = (frame->width != out_w || frame->height != out_h);
|
|
const bool need_cvt = (frame->format != out_fmt);
|
|
|
|
if (need_resize) {
|
|
WarnMetaResizeOnce(frame, out_w, out_h);
|
|
}
|
|
|
|
if (!need_resize && !need_cvt) {
|
|
ProcessPassthrough(frame);
|
|
return NodeStatus::OK;
|
|
}
|
|
|
|
Frame out;
|
|
out.width = out_w;
|
|
out.height = out_h;
|
|
out.format = out_fmt;
|
|
|
|
Status st = image_processor_->Resize(*frame, out);
|
|
if (st.Failed()) {
|
|
if (!use_rga_ && st.ErrMessage().find("unsupported format") != std::string::npos) {
|
|
ProcessPassthrough(frame);
|
|
return NodeStatus::OK;
|
|
}
|
|
LogError("[preprocess] " + st.ErrMessage());
|
|
return NodeStatus::ERROR;
|
|
}
|
|
|
|
auto out_frame = std::make_shared<Frame>(out);
|
|
out_frame->pts = frame->pts;
|
|
out_frame->frame_id = frame->frame_id;
|
|
out_frame->det = frame->det;
|
|
out_frame->face_det = frame->face_det;
|
|
out_frame->face_recog = frame->face_recog;
|
|
out_frame->user_meta = frame->user_meta;
|
|
|
|
PushToDownstream(out_frame);
|
|
++processed_;
|
|
|
|
if (stats_log_ && stats_interval_ > 0 && (processed_ % stats_interval_) == 0) {
|
|
LogInfo("[preprocess] " + std::string(use_rga_ ? "rga" : "swscale") +
|
|
" frame=" + std::to_string(out_frame->frame_id) +
|
|
" " + std::to_string(frame->width) + "x" + std::to_string(frame->height) +
|
|
" -> " + std::to_string(out_w) + "x" + std::to_string(out_h) +
|
|
" id=" + id_);
|
|
}
|
|
|
|
return NodeStatus::OK;
|
|
}
|
|
|
|
private:
|
|
void PushToDownstream(FramePtr frame) {
|
|
for (auto& q : output_queues_) {
|
|
q->Push(frame);
|
|
}
|
|
}
|
|
|
|
void WarnMetaResizeOnce(const FramePtr& frame, int out_w, int out_h) {
|
|
if (warned_meta_resize_) return;
|
|
if (!frame) return;
|
|
if (frame->width == out_w && frame->height == out_h) return;
|
|
if (!frame->det && !frame->face_det && !frame->face_recog) return;
|
|
warned_meta_resize_ = true;
|
|
LogWarn("[preprocess] resized frame but forwarded det/face meta without coordinate scaling; ensure det/recog/osd use same resolution (id=" + id_ + ")");
|
|
}
|
|
|
|
void ProcessPassthrough(FramePtr frame) {
|
|
PushToDownstream(frame);
|
|
++processed_;
|
|
if (stats_log_ && stats_interval_ > 0 && (processed_ % stats_interval_) == 0) {
|
|
LogInfo("[preprocess] passthrough frame=" + std::to_string(frame->frame_id) + " id=" + id_);
|
|
}
|
|
}
|
|
|
|
std::string id_;
|
|
int dst_w_ = 640;
|
|
int dst_h_ = 640;
|
|
bool keep_ratio_ = false;
|
|
PixelFormat dst_fmt_ = PixelFormat::UNKNOWN;
|
|
bool use_rga_ = true;
|
|
|
|
bool stats_log_ = false;
|
|
uint64_t stats_interval_ = 100;
|
|
|
|
bool warned_meta_resize_ = false;
|
|
|
|
std::shared_ptr<SpscQueue<FramePtr>> input_queue_;
|
|
std::vector<std::shared_ptr<SpscQueue<FramePtr>>> output_queues_;
|
|
std::shared_ptr<IImageProcessor> image_processor_;
|
|
uint64_t processed_ = 0;
|
|
};
|
|
|
|
REGISTER_NODE(PreprocessNode, "preprocess");
|
|
|
|
} // namespace rk3588
|