added new filtration and fixed processing parameters
This commit is contained in:
+62
-14
@@ -21,7 +21,6 @@ constexpr double kSmoothSigma = 1.5;
|
||||
// same default 'reflect' (half-sample symmetric) extension — see reflect_index.
|
||||
constexpr double kGaussianTruncate = 4.0;
|
||||
constexpr std::size_t kMaxObjects = 10U;
|
||||
constexpr double kObjectMinFrac = 0.7;
|
||||
constexpr double kRegionThresholdFrac = 0.75;
|
||||
constexpr double kSuppressThresholdFrac = 0.20;
|
||||
constexpr double kSuppressRadiusXM = 0.80;
|
||||
@@ -1405,7 +1404,8 @@ void apply_depth_gate(
|
||||
|
||||
[[nodiscard]] auto find_bp_objects(
|
||||
const std::vector<double>& bp_image,
|
||||
const GridDefinition& grid
|
||||
const GridDefinition& grid,
|
||||
double min_frac
|
||||
) -> std::vector<ObjectRecord> {
|
||||
std::vector<ObjectRecord> objects{};
|
||||
if (bp_image.empty() || grid.x_grid.size() < 2U || grid.z_grid.size() < 2U) {
|
||||
@@ -1414,7 +1414,7 @@ void apply_depth_gate(
|
||||
|
||||
std::vector<double> work = bp_image;
|
||||
const double global_peak = max_value(work);
|
||||
const double stop_level = kObjectMinFrac * global_peak;
|
||||
const double stop_level = min_frac * global_peak;
|
||||
if (!(global_peak > 0.0)) {
|
||||
return objects;
|
||||
}
|
||||
@@ -1740,7 +1740,15 @@ void add_bp_score_metrics(
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] auto output_objects_sorted(
|
||||
// Select the FINAL visible objects exactly as Horns_motion_3libre.py does, so the
|
||||
// processor is the single source of truth: the GUI plot and the locator socket both
|
||||
// consume this set verbatim (no second, duplicated filter). Steps, in order:
|
||||
// 1. drop sidelobe candidates (when enabled), then sort by score (peak tie-break);
|
||||
// 2. clip to the visible X/Z window (display window doubles as an object gate);
|
||||
// 3. apply the N/M draw rule (BP_MAX_DETECTED_OBJECTS_TO_DRAW / BP_DRAW_TOP_M_OBJECTS):
|
||||
// if more than N survive, show none; otherwise keep the top M.
|
||||
// There is deliberately NO score threshold (the Python reference has none).
|
||||
[[nodiscard]] auto select_visible_objects(
|
||||
const std::vector<ObjectRecord>& objects,
|
||||
const ProcessingLiveConfig& live_config
|
||||
) -> std::vector<const ObjectRecord*> {
|
||||
@@ -1750,6 +1758,12 @@ void add_bp_score_metrics(
|
||||
if (live_config.gpr_remove_sidelobe_objects_enabled && object.sidelobe_candidate) {
|
||||
continue;
|
||||
}
|
||||
if (object.x_m < live_config.gpr_visible_x_min_m
|
||||
|| object.x_m > live_config.gpr_visible_x_max_m
|
||||
|| object.z_m < live_config.gpr_visible_z_min_m
|
||||
|| object.z_m > live_config.gpr_visible_z_max_m) {
|
||||
continue;
|
||||
}
|
||||
visible.push_back(&object);
|
||||
}
|
||||
|
||||
@@ -1759,6 +1773,17 @@ void add_bp_score_metrics(
|
||||
}
|
||||
return left->selected_score > right->selected_score;
|
||||
});
|
||||
|
||||
// N/M draw rule (0 on either disables limiting, mirroring the GUI/locator default).
|
||||
const auto max_detected = live_config.gpr_max_detected_objects_to_draw;
|
||||
const auto draw_top = live_config.gpr_draw_top_m_objects;
|
||||
if (max_detected > 0U && draw_top > 0U) {
|
||||
if (visible.size() > max_detected) {
|
||||
visible.clear();
|
||||
} else if (visible.size() > draw_top) {
|
||||
visible.resize(draw_top);
|
||||
}
|
||||
}
|
||||
return visible;
|
||||
}
|
||||
|
||||
@@ -1815,7 +1840,8 @@ void add_bp_score_metrics(
|
||||
const config::RunConfig& run_config,
|
||||
const ipc::PreprocessedCollection& collection,
|
||||
std::span<const ipc::PreprocessedCollection> previous_collections,
|
||||
const ProcessingLiveConfig& live_config
|
||||
const ProcessingLiveConfig& live_config,
|
||||
ObjectApproachFilter& approach_filter
|
||||
) -> ipc::ResultCollection {
|
||||
ipc::ResultCollection results{};
|
||||
results.collection_id = collection.collection_id;
|
||||
@@ -1833,8 +1859,10 @@ void add_bp_score_metrics(
|
||||
return results;
|
||||
}
|
||||
|
||||
const double velocity_mps =
|
||||
kSpeedOfLightMetersPerSec / std::sqrt(std::max(1e-6, static_cast<double>(run_config.gpr.relative_permittivity)));
|
||||
// Coherent BP fixes the medium to vacuum/air (eps_r = 1), matching the Python
|
||||
// reference Horns_motion_3libre.py (its 0.3 block hardcodes eps_r = 1.0). The
|
||||
// configurable relative_permittivity stays a legacy-GPR-only knob.
|
||||
const double velocity_mps = kSpeedOfLightMetersPerSec;
|
||||
const double start_hz = static_cast<double>(live_config.gpr_start_freq_mhz) * 1'000'000.0;
|
||||
const double stop_hz = static_cast<double>(live_config.gpr_stop_freq_mhz) * 1'000'000.0;
|
||||
const double min_depth_m = static_cast<double>(live_config.gpr_min_depth_m);
|
||||
@@ -1920,7 +1948,7 @@ void add_bp_score_metrics(
|
||||
min_depth_m,
|
||||
max_depth_m,
|
||||
std::max(0.0, static_cast<double>(live_config.gpr_range_comp_power)),
|
||||
std::max(0.0, static_cast<double>(live_config.gpr_angle_comp_power))
|
||||
0.0 // angle compensation fixed off (Python Horns_motion_3libre.py COMP_ANGLE_POWER = 0.0)
|
||||
);
|
||||
if (bp.image.empty()) {
|
||||
return results;
|
||||
@@ -1930,7 +1958,7 @@ void add_bp_score_metrics(
|
||||
const auto incoherent_display_map =
|
||||
normalize_bp_map(bp.incoherent, grid, min_depth_m, max_depth_m, kSmoothSigma);
|
||||
|
||||
auto objects = find_bp_objects(display_map, grid);
|
||||
auto objects = find_bp_objects(display_map, grid, static_cast<double>(live_config.gpr_object_min_frac));
|
||||
add_local_prominence_metrics(objects, display_map, grid, min_depth_m, max_depth_m);
|
||||
add_incoherent_support_metrics(objects, incoherent_display_map, bp.coherence_factor);
|
||||
mark_sidelobe_candidates(objects, selected_traces, selection, imaging_plane_y_m);
|
||||
@@ -1951,14 +1979,34 @@ void add_bp_score_metrics(
|
||||
|
||||
results.collection_payloads.push_back(build_image_payload("gpr_accumulator", grid.x_grid, grid.z_grid, display_map));
|
||||
|
||||
// Final visible set (window + N/M), then the cross-frame approach filter: keep only
|
||||
// objects confirmed as a >= min_frames motion-consistent track (Horns_motion notebook).
|
||||
const auto visible = select_visible_objects(objects, live_config);
|
||||
std::vector<ObjectApproachFilter::Point> visible_points{};
|
||||
visible_points.reserve(visible.size());
|
||||
for (const auto* object : visible) {
|
||||
visible_points.push_back({object->x_m, object->z_m});
|
||||
}
|
||||
const auto confirmed = approach_filter.confirm(
|
||||
visible_points,
|
||||
collection.collection_id,
|
||||
static_cast<double>(collection.monotonic_ns) * 1e-9,
|
||||
static_cast<double>(live_config.gpr_speed_m_s),
|
||||
static_cast<double>(live_config.gpr_look_angle_deg),
|
||||
static_cast<std::size_t>(live_config.gpr_object_approach_min_frames)
|
||||
);
|
||||
|
||||
std::vector<std::vector<float>> point_rows{};
|
||||
point_rows.reserve(objects.size());
|
||||
for (const auto* object : output_objects_sorted(objects, live_config)) {
|
||||
point_rows.reserve(visible.size());
|
||||
for (std::size_t index = 0U; index < visible.size(); ++index) {
|
||||
if (!confirmed[index]) {
|
||||
continue;
|
||||
}
|
||||
point_rows.push_back(
|
||||
{
|
||||
static_cast<float>(object->x_m),
|
||||
static_cast<float>(object->z_m),
|
||||
static_cast<float>(object->selected_score),
|
||||
static_cast<float>(visible[index]->x_m),
|
||||
static_cast<float>(visible[index]->z_m),
|
||||
static_cast<float>(visible[index]->selected_score),
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@@ -36,7 +36,7 @@ auto GprProcessor::process_collection(
|
||||
std::span<const ipc::PreprocessedCollection> previous_collections,
|
||||
const ProcessingLiveConfig& live_config
|
||||
) -> ipc::ResultCollection {
|
||||
return process_backprojection_gpr(run_config, collection, previous_collections, live_config);
|
||||
return process_backprojection_gpr(run_config, collection, previous_collections, live_config, approach_filter_);
|
||||
}
|
||||
|
||||
auto LegacyGprProcessor::name() const -> std::string {
|
||||
|
||||
Reference in New Issue
Block a user