519 lines
21 KiB
C++
519 lines
21 KiB
C++
#include "action_recog_node.h"
|
|
|
|
#include <algorithm>
|
|
#include <cmath>
|
|
#include <cstdint>
|
|
#include <deque>
|
|
#include <limits>
|
|
#include <map>
|
|
#include <memory>
|
|
#include <set>
|
|
#include <string>
|
|
#include <utility>
|
|
#include <vector>
|
|
|
|
#include "behavior/behavior_event.h"
|
|
#include "pose/pose_result.h"
|
|
#include "utils/logger.h"
|
|
|
|
namespace rk3588 {
|
|
namespace {
|
|
|
|
enum class ActionEventKind {
|
|
Fall,
|
|
Fight
|
|
};
|
|
|
|
enum class ActionSignalMode {
|
|
Any,
|
|
All
|
|
};
|
|
|
|
struct FallBboxRule {
|
|
bool enabled = true;
|
|
float min_drop_pixels = 0.0f;
|
|
float min_aspect_ratio_delta = 0.0f;
|
|
};
|
|
|
|
struct FallPoseRule {
|
|
bool enabled = false;
|
|
float min_torso_drop_pixels = 0.0f;
|
|
float max_upright_ratio = 0.0f;
|
|
};
|
|
|
|
struct FightBboxRule {
|
|
bool enabled = true;
|
|
float proximity_pixels = 0.0f;
|
|
float min_motion_pixels = 0.0f;
|
|
};
|
|
|
|
struct FightPoseRule {
|
|
bool enabled = false;
|
|
float min_wrist_motion_pixels = 0.0f;
|
|
float max_wrist_distance_pixels = 0.0f;
|
|
};
|
|
|
|
struct ActionEventRule {
|
|
ActionEventKind kind = ActionEventKind::Fall;
|
|
uint64_t window_ms = 1000;
|
|
uint64_t activate_duration_ms = 0;
|
|
ActionSignalMode signal_mode = ActionSignalMode::Any;
|
|
FallBboxRule fall_bbox{};
|
|
FallPoseRule fall_pose{};
|
|
FightBboxRule fight_bbox{};
|
|
FightPoseRule fight_pose{};
|
|
};
|
|
|
|
struct PoseTrackSample {
|
|
bool matched = false;
|
|
float torso_center_y = 0.0f;
|
|
float upright_ratio = 0.0f;
|
|
float left_wrist_x = 0.0f;
|
|
float left_wrist_y = 0.0f;
|
|
float right_wrist_x = 0.0f;
|
|
float right_wrist_y = 0.0f;
|
|
bool has_left_wrist = false;
|
|
bool has_right_wrist = false;
|
|
};
|
|
|
|
struct TrackSample {
|
|
uint64_t pts = 0;
|
|
Rect bbox{};
|
|
PoseTrackSample pose{};
|
|
};
|
|
|
|
static ActionSignalMode ParseSignalMode(const std::string& value) {
|
|
if (value == "all") return ActionSignalMode::All;
|
|
return ActionSignalMode::Any;
|
|
}
|
|
|
|
static bool EvaluateSignalMode(ActionSignalMode mode, bool bbox_trigger, bool pose_trigger,
|
|
bool bbox_enabled, bool pose_enabled) {
|
|
if (bbox_enabled && pose_enabled) {
|
|
return mode == ActionSignalMode::All ? (bbox_trigger && pose_trigger) : (bbox_trigger || pose_trigger);
|
|
}
|
|
if (bbox_enabled) return bbox_trigger;
|
|
if (pose_enabled) return pose_trigger;
|
|
return false;
|
|
}
|
|
|
|
static bool ParseFallRule(const SimpleJson& ev, ActionEventRule& rule) {
|
|
rule.kind = ActionEventKind::Fall;
|
|
rule.fall_bbox.min_drop_pixels = ev.ValueOr<float>("min_drop_pixels", 0.0f);
|
|
rule.fall_bbox.min_aspect_ratio_delta = ev.ValueOr<float>("min_aspect_ratio_delta", 0.0f);
|
|
rule.fall_pose.min_torso_drop_pixels = ev.ValueOr<float>("pose_min_torso_drop_pixels", 0.0f);
|
|
rule.fall_pose.max_upright_ratio = ev.ValueOr<float>("pose_max_upright_ratio", 0.0f);
|
|
|
|
if (const SimpleJson* bbox = ev.Find("bbox"); bbox && bbox->IsObject()) {
|
|
rule.fall_bbox.enabled = bbox->ValueOr<bool>("enabled", true);
|
|
rule.fall_bbox.min_drop_pixels = bbox->ValueOr<float>("min_drop_pixels", rule.fall_bbox.min_drop_pixels);
|
|
rule.fall_bbox.min_aspect_ratio_delta =
|
|
bbox->ValueOr<float>("min_aspect_ratio_delta", rule.fall_bbox.min_aspect_ratio_delta);
|
|
} else {
|
|
rule.fall_bbox.enabled = true;
|
|
}
|
|
|
|
if (const SimpleJson* pose = ev.Find("pose"); pose && pose->IsObject()) {
|
|
rule.fall_pose.enabled = pose->ValueOr<bool>("enabled", true);
|
|
rule.fall_pose.min_torso_drop_pixels =
|
|
pose->ValueOr<float>("min_torso_drop_pixels", rule.fall_pose.min_torso_drop_pixels);
|
|
rule.fall_pose.max_upright_ratio =
|
|
pose->ValueOr<float>("max_upright_ratio", rule.fall_pose.max_upright_ratio);
|
|
} else {
|
|
rule.fall_pose.enabled = rule.fall_pose.min_torso_drop_pixels > 0.0f || rule.fall_pose.max_upright_ratio > 0.0f;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
static bool ParseFightRule(const SimpleJson& ev, ActionEventRule& rule) {
|
|
rule.kind = ActionEventKind::Fight;
|
|
rule.fight_bbox.proximity_pixels = ev.ValueOr<float>("proximity_pixels", 0.0f);
|
|
rule.fight_bbox.min_motion_pixels = ev.ValueOr<float>("min_motion_pixels", 0.0f);
|
|
rule.fight_pose.min_wrist_motion_pixels = ev.ValueOr<float>("pose_min_wrist_motion_pixels", 0.0f);
|
|
rule.fight_pose.max_wrist_distance_pixels = ev.ValueOr<float>("pose_max_wrist_distance_pixels", 0.0f);
|
|
|
|
if (const SimpleJson* bbox = ev.Find("bbox"); bbox && bbox->IsObject()) {
|
|
rule.fight_bbox.enabled = bbox->ValueOr<bool>("enabled", true);
|
|
rule.fight_bbox.proximity_pixels = bbox->ValueOr<float>("proximity_pixels", rule.fight_bbox.proximity_pixels);
|
|
rule.fight_bbox.min_motion_pixels = bbox->ValueOr<float>("min_motion_pixels", rule.fight_bbox.min_motion_pixels);
|
|
} else {
|
|
rule.fight_bbox.enabled = true;
|
|
}
|
|
|
|
if (const SimpleJson* pose = ev.Find("pose"); pose && pose->IsObject()) {
|
|
rule.fight_pose.enabled = pose->ValueOr<bool>("enabled", true);
|
|
rule.fight_pose.min_wrist_motion_pixels =
|
|
pose->ValueOr<float>("min_wrist_motion_pixels", rule.fight_pose.min_wrist_motion_pixels);
|
|
rule.fight_pose.max_wrist_distance_pixels =
|
|
pose->ValueOr<float>("max_wrist_distance_pixels", rule.fight_pose.max_wrist_distance_pixels);
|
|
} else {
|
|
rule.fight_pose.enabled =
|
|
rule.fight_pose.min_wrist_motion_pixels > 0.0f || rule.fight_pose.max_wrist_distance_pixels > 0.0f;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
static bool ParseRules(const SimpleJson& config, std::vector<ActionEventRule>& out, std::string& err) {
|
|
const SimpleJson* events = config.Find("events");
|
|
if (!events || !events->IsArray()) {
|
|
err = "events must be array";
|
|
return false;
|
|
}
|
|
|
|
out.clear();
|
|
for (const auto& ev : events->AsArray()) {
|
|
if (!ev.IsObject()) {
|
|
err = "event entry must be object";
|
|
return false;
|
|
}
|
|
|
|
ActionEventRule rule;
|
|
const std::string type = ev.ValueOr<std::string>("type", "");
|
|
rule.window_ms = static_cast<uint64_t>(std::max(0, ev.ValueOr<int>("window_ms", 1000)));
|
|
rule.activate_duration_ms = static_cast<uint64_t>(std::max(0, ev.ValueOr<int>("activate_duration_ms", 0)));
|
|
rule.signal_mode = ParseSignalMode(ev.ValueOr<std::string>("signal_mode", "any"));
|
|
if (const SimpleJson* fusion = ev.Find("fusion"); fusion && fusion->IsObject()) {
|
|
rule.signal_mode = ParseSignalMode(fusion->ValueOr<std::string>("match_mode", "any"));
|
|
}
|
|
|
|
if (type == "fall") {
|
|
ParseFallRule(ev, rule);
|
|
} else if (type == "fight") {
|
|
ParseFightRule(ev, rule);
|
|
} else {
|
|
err = "unsupported event type: " + type;
|
|
return false;
|
|
}
|
|
|
|
out.push_back(std::move(rule));
|
|
}
|
|
return true;
|
|
}
|
|
|
|
static float CenterX(const Rect& rect) {
|
|
return rect.x + (rect.w * 0.5f);
|
|
}
|
|
|
|
static float CenterY(const Rect& rect) {
|
|
return rect.y + (rect.h * 0.5f);
|
|
}
|
|
|
|
static float CenterDistance(const Rect& lhs, const Rect& rhs) {
|
|
const float dx = CenterX(lhs) - CenterX(rhs);
|
|
const float dy = CenterY(lhs) - CenterY(rhs);
|
|
return std::sqrt((dx * dx) + (dy * dy));
|
|
}
|
|
|
|
static float MotionDistance(const Rect& lhs, const Rect& rhs) {
|
|
const float dx = CenterX(lhs) - CenterX(rhs);
|
|
const float dy = CenterY(lhs) - CenterY(rhs);
|
|
return std::sqrt((dx * dx) + (dy * dy));
|
|
}
|
|
|
|
static float AspectRatio(const Rect& rect) {
|
|
return rect.h > 0.0f ? (rect.w / rect.h) : 0.0f;
|
|
}
|
|
|
|
static float BboxIoU(const Rect& lhs, const Rect& rhs) {
|
|
const float x1 = std::max(lhs.x, rhs.x);
|
|
const float y1 = std::max(lhs.y, rhs.y);
|
|
const float x2 = std::min(lhs.x + lhs.w, rhs.x + rhs.w);
|
|
const float y2 = std::min(lhs.y + lhs.h, rhs.y + rhs.h);
|
|
const float iw = std::max(0.0f, x2 - x1);
|
|
const float ih = std::max(0.0f, y2 - y1);
|
|
const float inter = iw * ih;
|
|
const float uni = lhs.w * lhs.h + rhs.w * rhs.h - inter;
|
|
return uni <= 0.0f ? 0.0f : (inter / uni);
|
|
}
|
|
|
|
static bool IsValidKeypoint(const PoseItem& pose, int index) {
|
|
return index >= 0 &&
|
|
static_cast<size_t>(index) < pose.keypoints.size() &&
|
|
pose.keypoints[static_cast<size_t>(index)].score > 0.0f;
|
|
}
|
|
|
|
static PoseTrackSample BuildPoseTrackSample(const PoseItem& pose) {
|
|
PoseTrackSample sample;
|
|
sample.matched = true;
|
|
|
|
const bool has_left_shoulder = IsValidKeypoint(pose, 5);
|
|
const bool has_right_shoulder = IsValidKeypoint(pose, 6);
|
|
const bool has_left_hip = IsValidKeypoint(pose, 11);
|
|
const bool has_right_hip = IsValidKeypoint(pose, 12);
|
|
if (has_left_shoulder && has_right_shoulder && has_left_hip && has_right_hip) {
|
|
const PosePoint2f& ls = pose.keypoints[5].point;
|
|
const PosePoint2f& rs = pose.keypoints[6].point;
|
|
const PosePoint2f& lh = pose.keypoints[11].point;
|
|
const PosePoint2f& rh = pose.keypoints[12].point;
|
|
const float shoulder_center_y = (ls.y + rs.y) * 0.5f;
|
|
const float hip_center_y = (lh.y + rh.y) * 0.5f;
|
|
sample.torso_center_y = (shoulder_center_y + hip_center_y) * 0.5f;
|
|
const float min_x = std::min(std::min(ls.x, rs.x), std::min(lh.x, rh.x));
|
|
const float max_x = std::max(std::max(ls.x, rs.x), std::max(lh.x, rh.x));
|
|
const float min_y = std::min(std::min(ls.y, rs.y), std::min(lh.y, rh.y));
|
|
const float max_y = std::max(std::max(ls.y, rs.y), std::max(lh.y, rh.y));
|
|
const float horizontal_span = std::fabs(max_x - min_x);
|
|
const float vertical_span = std::fabs(max_y - min_y);
|
|
sample.upright_ratio = vertical_span > 1e-3f ? (horizontal_span / vertical_span) : std::numeric_limits<float>::infinity();
|
|
}
|
|
|
|
if (IsValidKeypoint(pose, 9)) {
|
|
sample.left_wrist_x = pose.keypoints[9].point.x;
|
|
sample.left_wrist_y = pose.keypoints[9].point.y;
|
|
sample.has_left_wrist = true;
|
|
}
|
|
if (IsValidKeypoint(pose, 10)) {
|
|
sample.right_wrist_x = pose.keypoints[10].point.x;
|
|
sample.right_wrist_y = pose.keypoints[10].point.y;
|
|
sample.has_right_wrist = true;
|
|
}
|
|
return sample;
|
|
}
|
|
|
|
static PoseTrackSample MatchPoseToDetection(const Rect& bbox, const std::shared_ptr<PoseResult>& pose_result) {
|
|
if (!pose_result) return {};
|
|
|
|
float best_iou = 0.0f;
|
|
const PoseItem* best_pose = nullptr;
|
|
for (const auto& pose : pose_result->items) {
|
|
const float iou = BboxIoU(bbox, pose.bbox);
|
|
if (iou > best_iou) {
|
|
best_iou = iou;
|
|
best_pose = &pose;
|
|
}
|
|
}
|
|
if (!best_pose || best_iou <= 0.0f) return {};
|
|
return BuildPoseTrackSample(*best_pose);
|
|
}
|
|
|
|
static PoseTrackSample MatchPoseToTrackId(int track_id, const Rect& bbox, const std::shared_ptr<PoseResult>& pose_result) {
|
|
if (!pose_result) return {};
|
|
|
|
const PoseItem* best_pose = nullptr;
|
|
float best_iou = 0.0f;
|
|
for (const auto& pose : pose_result->items) {
|
|
if (pose.track_id != track_id) continue;
|
|
const float iou = BboxIoU(bbox, pose.bbox);
|
|
if (iou >= best_iou) {
|
|
best_iou = iou;
|
|
best_pose = &pose;
|
|
}
|
|
}
|
|
if (best_pose) return BuildPoseTrackSample(*best_pose);
|
|
return MatchPoseToDetection(bbox, pose_result);
|
|
}
|
|
|
|
static float WristMotion(const PoseTrackSample& newer, const PoseTrackSample& older) {
|
|
float motion = 0.0f;
|
|
if (newer.has_left_wrist && older.has_left_wrist) {
|
|
const float dx = newer.left_wrist_x - older.left_wrist_x;
|
|
const float dy = newer.left_wrist_y - older.left_wrist_y;
|
|
motion += std::sqrt(dx * dx + dy * dy);
|
|
}
|
|
if (newer.has_right_wrist && older.has_right_wrist) {
|
|
const float dx = newer.right_wrist_x - older.right_wrist_x;
|
|
const float dy = newer.right_wrist_y - older.right_wrist_y;
|
|
motion += std::sqrt(dx * dx + dy * dy);
|
|
}
|
|
return motion;
|
|
}
|
|
|
|
static float MinimumWristDistance(const PoseTrackSample& lhs, const PoseTrackSample& rhs) {
|
|
float best = std::numeric_limits<float>::infinity();
|
|
auto update = [&](bool has_a, float ax, float ay, bool has_b, float bx, float by) {
|
|
if (!has_a || !has_b) return;
|
|
const float dx = ax - bx;
|
|
const float dy = ay - by;
|
|
best = std::min(best, std::sqrt(dx * dx + dy * dy));
|
|
};
|
|
update(lhs.has_left_wrist, lhs.left_wrist_x, lhs.left_wrist_y,
|
|
rhs.has_left_wrist, rhs.left_wrist_x, rhs.left_wrist_y);
|
|
update(lhs.has_left_wrist, lhs.left_wrist_x, lhs.left_wrist_y,
|
|
rhs.has_right_wrist, rhs.right_wrist_x, rhs.right_wrist_y);
|
|
update(lhs.has_right_wrist, lhs.right_wrist_x, lhs.right_wrist_y,
|
|
rhs.has_left_wrist, rhs.left_wrist_x, rhs.left_wrist_y);
|
|
update(lhs.has_right_wrist, lhs.right_wrist_x, lhs.right_wrist_y,
|
|
rhs.has_right_wrist, rhs.right_wrist_x, rhs.right_wrist_y);
|
|
return std::isfinite(best) ? best : std::numeric_limits<float>::infinity();
|
|
}
|
|
|
|
static BehaviorEventType ToBehaviorEventType(ActionEventKind kind) {
|
|
return kind == ActionEventKind::Fall ? BehaviorEventType::Fall : BehaviorEventType::Fight;
|
|
}
|
|
|
|
} // namespace
|
|
|
|
struct ActionRecogNode::Impl {
|
|
std::string init_err;
|
|
std::vector<ActionEventRule> rules;
|
|
std::map<int, std::deque<TrackSample>> history;
|
|
};
|
|
|
|
ActionRecogNode::ActionRecogNode() : impl_(std::make_unique<Impl>()) {}
|
|
|
|
ActionRecogNode::~ActionRecogNode() = default;
|
|
|
|
std::string ActionRecogNode::Id() const {
|
|
return id_;
|
|
}
|
|
|
|
std::string ActionRecogNode::Type() const {
|
|
return "action_recog";
|
|
}
|
|
|
|
bool ActionRecogNode::Init(const SimpleJson& config, const NodeContext& ctx) {
|
|
id_ = config.ValueOr<std::string>("id", "action_recog");
|
|
if (!ParseRules(config, impl_->rules, impl_->init_err)) {
|
|
LogError("[action_recog] invalid config: " + impl_->init_err);
|
|
return false;
|
|
}
|
|
output_queues_ = ctx.output_queues;
|
|
return true;
|
|
}
|
|
|
|
bool ActionRecogNode::Start() {
|
|
return true;
|
|
}
|
|
|
|
void ActionRecogNode::Stop() {}
|
|
|
|
NodeStatus ActionRecogNode::Process(FramePtr frame) {
|
|
if (!frame) return NodeStatus::DROP;
|
|
|
|
EnsureBehaviorEvents(*frame);
|
|
if (!frame->det) {
|
|
PushToDownstream(frame);
|
|
return NodeStatus::OK;
|
|
}
|
|
|
|
std::map<int, Rect> current_tracks;
|
|
for (const auto& det : frame->det->items) {
|
|
if (det.track_id < 0) continue;
|
|
current_tracks[det.track_id] = det.bbox;
|
|
auto& history = impl_->history[det.track_id];
|
|
history.push_back(TrackSample{frame->pts, det.bbox, MatchPoseToTrackId(det.track_id, det.bbox, frame->pose)});
|
|
while (!history.empty() && frame->pts > history.front().pts &&
|
|
(frame->pts - history.front().pts) > impl_->rules.front().window_ms) {
|
|
history.pop_front();
|
|
}
|
|
}
|
|
|
|
for (const auto& rule : impl_->rules) {
|
|
if (rule.kind == ActionEventKind::Fall) {
|
|
for (const auto& [track_id, bbox] : current_tracks) {
|
|
auto it = impl_->history.find(track_id);
|
|
if (it == impl_->history.end() || it->second.empty()) continue;
|
|
const auto& first = it->second.front();
|
|
const float drop = CenterY(bbox) - CenterY(first.bbox);
|
|
const float aspect_delta = AspectRatio(bbox) - AspectRatio(first.bbox);
|
|
const uint64_t duration = frame->pts >= first.pts ? (frame->pts - first.pts) : 0;
|
|
const bool bbox_enabled = rule.fall_bbox.enabled;
|
|
const bool pose_enabled = rule.fall_pose.enabled;
|
|
const bool bbox_trigger = drop >= rule.fall_bbox.min_drop_pixels &&
|
|
aspect_delta >= rule.fall_bbox.min_aspect_ratio_delta;
|
|
bool pose_trigger = false;
|
|
if (pose_enabled &&
|
|
first.pose.matched && it->second.back().pose.matched) {
|
|
const float torso_drop = it->second.back().pose.torso_center_y - first.pose.torso_center_y;
|
|
pose_trigger = torso_drop >= rule.fall_pose.min_torso_drop_pixels &&
|
|
it->second.back().pose.upright_ratio >= rule.fall_pose.max_upright_ratio;
|
|
}
|
|
if (!EvaluateSignalMode(rule.signal_mode, bbox_trigger, pose_trigger, bbox_enabled, pose_enabled)) continue;
|
|
if (duration < rule.activate_duration_ms) continue;
|
|
|
|
BehaviorEventItem event;
|
|
event.type = ToBehaviorEventType(rule.kind);
|
|
event.status = BehaviorEventStatus::Active;
|
|
event.score = 1.0f;
|
|
event.bbox = bbox;
|
|
event.track_ids.push_back(track_id);
|
|
event.start_pts = first.pts;
|
|
event.last_pts = frame->pts;
|
|
event.duration_ms = duration;
|
|
event.source = "action_recog";
|
|
frame->behavior_events->items.push_back(std::move(event));
|
|
}
|
|
} else {
|
|
std::set<std::pair<int, int>> emitted_pairs;
|
|
for (auto left = current_tracks.begin(); left != current_tracks.end(); ++left) {
|
|
for (auto right = std::next(left); right != current_tracks.end(); ++right) {
|
|
const float proximity = CenterDistance(left->second, right->second);
|
|
if (proximity > rule.fight_bbox.proximity_pixels) continue;
|
|
|
|
const auto it_left = impl_->history.find(left->first);
|
|
const auto it_right = impl_->history.find(right->first);
|
|
if (it_left == impl_->history.end() || it_right == impl_->history.end()) continue;
|
|
if (it_left->second.empty() || it_right->second.empty()) continue;
|
|
|
|
const float left_motion = MotionDistance(left->second, it_left->second.front().bbox);
|
|
const float right_motion = MotionDistance(right->second, it_right->second.front().bbox);
|
|
const uint64_t duration_left = frame->pts >= it_left->second.front().pts ? (frame->pts - it_left->second.front().pts) : 0;
|
|
const uint64_t duration_right = frame->pts >= it_right->second.front().pts ? (frame->pts - it_right->second.front().pts) : 0;
|
|
const uint64_t duration = std::min(duration_left, duration_right);
|
|
|
|
const float combined_motion = left_motion + right_motion;
|
|
const bool bbox_enabled = rule.fight_bbox.enabled;
|
|
const bool pose_enabled = rule.fight_pose.enabled;
|
|
const bool bbox_trigger = combined_motion >= rule.fight_bbox.min_motion_pixels;
|
|
bool pose_trigger = false;
|
|
const auto& left_pose_now = it_left->second.back().pose;
|
|
const auto& right_pose_now = it_right->second.back().pose;
|
|
if (pose_enabled &&
|
|
it_left->second.front().pose.matched && it_right->second.front().pose.matched &&
|
|
left_pose_now.matched && right_pose_now.matched) {
|
|
const float pose_motion = WristMotion(left_pose_now, it_left->second.front().pose) +
|
|
WristMotion(right_pose_now, it_right->second.front().pose);
|
|
const float wrist_distance = MinimumWristDistance(left_pose_now, right_pose_now);
|
|
pose_trigger = pose_motion >= rule.fight_pose.min_wrist_motion_pixels &&
|
|
wrist_distance <= rule.fight_pose.max_wrist_distance_pixels;
|
|
}
|
|
if (!EvaluateSignalMode(rule.signal_mode, bbox_trigger, pose_trigger, bbox_enabled, pose_enabled)) continue;
|
|
if (duration < rule.activate_duration_ms) continue;
|
|
|
|
const auto pair_key = std::make_pair(left->first, right->first);
|
|
if (!emitted_pairs.insert(pair_key).second) continue;
|
|
|
|
BehaviorEventItem event;
|
|
event.type = ToBehaviorEventType(rule.kind);
|
|
event.status = BehaviorEventStatus::Active;
|
|
event.score = 1.0f;
|
|
event.bbox = Rect{
|
|
std::min(left->second.x, right->second.x),
|
|
std::min(left->second.y, right->second.y),
|
|
std::max(left->second.x + left->second.w, right->second.x + right->second.w) - std::min(left->second.x, right->second.x),
|
|
std::max(left->second.y + left->second.h, right->second.y + right->second.h) - std::min(left->second.y, right->second.y)
|
|
};
|
|
event.track_ids.push_back(left->first);
|
|
event.track_ids.push_back(right->first);
|
|
event.start_pts = frame->pts - duration;
|
|
event.last_pts = frame->pts;
|
|
event.duration_ms = duration;
|
|
event.source = "action_recog";
|
|
frame->behavior_events->items.push_back(std::move(event));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
PushToDownstream(frame);
|
|
return NodeStatus::OK;
|
|
}
|
|
|
|
void ActionRecogNode::EnsureBehaviorEvents(Frame& frame) {
|
|
if (!frame.behavior_events) {
|
|
frame.behavior_events = std::make_shared<BehaviorEventResult>();
|
|
}
|
|
}
|
|
|
|
void ActionRecogNode::PushToDownstream(const FramePtr& frame) {
|
|
for (auto& q : output_queues_) {
|
|
if (q) q->Push(frame);
|
|
}
|
|
}
|
|
|
|
#ifndef RK3588_TEST_BUILD
|
|
REGISTER_NODE(ActionRecogNode, "action_recog");
|
|
#endif
|
|
|
|
} // namespace rk3588
|