93 static_assert(N == 2 || N == 3);
95 = std::conditional_t<N == 2, halp::xy_type<float>, halp::xyz_type<float>>;
96 using tracker_type = ossia::point_tracker<N>;
97 using detection_type = ossia::point_detection<N>;
107 double creation_time = 0.;
109 float time_since_seen = 0.f;
110 position_type position{};
111 position_type position_raw{};
112 position_type velocity{};
113 float confidence = 0.f;
114 bool provisional =
false;
115 bool reacquired =
false;
118 id, slot, state, creation_time, age, time_since_seen, position,
119 position_raw, velocity, confidence, provisional, reacquired);
124 struct : halp::val_port<
"Points", std::vector<ossia::value>>
131 ?
"Detections for this frame. Accepts a list of vec2f, a list of "
132 "vec3f read as (x, y, confidence), sub-lists [x, y] or "
133 "[x, y, confidence], {position, confidence} maps as emitted by "
134 "Blob stats, or one flat list of numbers [x, y, x, y, ...]."
135 :
"Detections for this frame. Accepts a list of vec3f, a list of "
136 "vec4f read as (x, y, z, confidence), sub-lists [x, y, z] or "
137 "[x, y, z, confidence], {position, confidence} maps, or one "
138 "flat list of numbers [x, y, z, x, y, z, ...].")
139 void update(
auto& self) { self.points_dirty =
true; }
143 struct : halp::spinbox_f32<
"Max Speed", halp::range{0.01, 100., 2.}>
147 "Fastest plausible object speed, in coordinate units per second - the "
148 "primary association gate. 2 m/s suits human-scale motion in metric "
149 "spaces; in [0,1] camera space 2.0 means crossing the frame in 0.5 s.")
154 struct : halp::combobox_t<
"Motion Gate", TrackerMotionGate>
158 "How to reject implausible jumps: Max Speed (analytic budget "
159 "max_speed*dt*(1 + lost/coast)), Mahalanobis (chi-square on the "
160 "Kalman innovation), or Off.")
163 std::string_view values[3]{
"Max speed",
"Mahalanobis",
"Off"};
164 TrackerMotionGate init{TrackerMotionGate::MaxSpeed};
167 struct : halp::toggle<
"Two-Stage Association", halp::default_on_toggle>
171 "ByteTrack: after matching high-confidence detections, let "
172 "low-confidence ones sustain still-unmatched recent tracks (they "
173 "never birth or revive). Worth 1-10 IDF1 points on crowded scenes.")
175 struct : halp::knob_f32<
"High Confidence", halp::range{0., 1., 0.5}>
179 "Detections at or above this confidence are matched first and can "
180 "sustain any track. Set it around the confidence your detector gives "
181 "clearly-visible objects.")
183 struct : halp::knob_f32<
"Low Confidence", halp::range{0., 1., 0.1}>
187 "Detections between this and High Confidence only run in the "
188 "second-stage recovery: they can sustain an existing track through an "
189 "occlusion dip but never start or revive one. Below this they are "
192 struct : halp::knob_f32<
"New Track Confidence", halp::range{0., 1., 0.6}>
194 halp_meta(description,
"Minimum confidence for a detection to start a new track.")
196 struct : halp::knob_f32<
"Direction Consistency", halp::range{0., 1., 0.2}>
200 "OC-SORT: penalize associations that reverse the track's direction of "
201 "motion, averaged over 1-3 frame baselines so one noisy frame does "
202 "not fake a turn. Helps crossings; 0 = off.")
204 struct : halp::knob_f32<
"Confidence Modeling", halp::range{0., 1.5, 1.}>
208 "Hybrid-SORT: prefer the detection whose confidence continues the "
209 "track's confidence trend (an occluded object's confidence sinks "
210 "smoothly, so the trend tells crossing objects apart when position "
211 "cannot). Needs a detector with meaningful per-detection confidence; "
216 struct : halp::spinbox_f32<
"Motion Noise", halp::range{0.001, 50., 0.67}>
220 "Kalman process noise: expected acceleration (units/s^2). Human "
221 "motion peaks near 2 m/s^2; 0.67 fits that as 3 sigma. Raise for "
222 "erratic motion, lower for very smooth coasting.")
224 struct : halp::spinbox_f32<
"Position Noise", halp::range{0.0001, 1., 0.005}>
229 ?
"Detector jitter (standard deviation, coordinate units). Match it "
230 "to your detector's actual noise: the association gates widen by "
231 "this amount. Understating it makes a track reject its own noisy "
232 "detections and be reborn under a new id; overstating it widens "
233 "the gates until neighbouring entities compete for the same "
234 "detection and swap."
235 :
"Detector jitter (standard deviation, coordinate units). Match it "
236 "to your detector's actual noise: the association gates widen by "
237 "this amount. Understating it makes a track reject its own noisy "
238 "detections and be reborn under a new id; overstating it widens "
239 "the gates until neighbouring entities compete for the same "
240 "detection and swap. This matters more in 3D than in 2D: noise "
241 "on three axes gives a radial error of sigma*sqrt(3) against a "
242 "gate that does not grow with the dimension.")
246 struct : halp::spinbox_f32<
"Confirm Time", halp::range{0., 2000., 100.}>
250 "Minimum age (milliseconds, not frames) before a track can be "
251 "confirmed - stable across frame rates.")
253 struct : halp::spinbox_i32<
"Confirm Hits", halp::range{1, 31, 3}>
257 "A track is confirmed once it was detected in this many of the last "
258 "Confirm Window detection frames (and is older than Confirm Time). "
259 "Raise to demand more evidence before a track joins the stable set.")
261 struct : halp::spinbox_i32<
"Confirm Window", halp::range{1, 31, 5}>
265 "Size of the recent-frames window that Confirm Hits is counted over. "
266 "A wider window tolerates more dropped frames on the way to "
269 struct : halp::knob_f32<
"Instant Confirm Above", halp::range{0., 1.001, 0.9}>
273 "A detection at or above this confidence confirms its track "
274 "immediately, skipping M-of-N. Set above 1 to disable.")
276 struct : halp::toggle<
"Emit Unconfirmed", halp::default_on_toggle>
280 "Emit provisional tracks immediately (flagged) instead of sitting out "
281 "the ~100 ms confirmation latency. For musical onsets, keep this on "
282 "and filter on the `provisional` field where it matters.")
286 struct : halp::spinbox_f32<
"Coast Time", halp::range{0., 5000., 500.}>
290 "After a miss, keep emitting the Kalman-predicted position for this "
291 "long (ms) before the track goes lost.")
293 struct : halp::toggle<
"Revive", halp::default_on_toggle>
297 "Keep lost tracks in memory after coasting ends, so an object that "
298 "reappears nearby gets its old id back instead of a new one.")
300 struct : halp::spinbox_f32<
"Revive Time", halp::range{0., 10000., 2000.}>
304 "After coasting ends, keep the lost track (unemitted) for this long "
305 "(ms); a matching detection revives it with its id intact.")
307 struct : halp::toggle<
"Re-update on Revive", halp::default_on_toggle>
311 "OC-SORT ORU: on revival, re-run the filter along a virtual "
312 "trajectory across the gap, killing the post-occlusion lurch.")
316 struct : halp::toggle<
"Smooth", halp::default_on_toggle>
320 "One-Euro filter on the output positions (per track, per axis): "
321 "removes detector jitter at rest with minimal lag during motion. "
322 "Tune with Min Cutoff and Beta.")
324 struct : halp::log_hslider_f32<
"Min Cutoff", halp::range{0.005, 10., 1.}>
327 description,
"One-Euro cutoff at rest, Hz. Lower = smoother = laggier.")
329 struct : halp::hslider_f32<
"Beta", halp::range{0., 100., 1.}>
333 "One-Euro speed coefficient: how much motion raises the cutoff. "
334 "MediaPipe ships 10-80 on landmarks; raise it if fast motion lags.")
336 struct : halp::spinbox_f32<
"Prediction Lead", halp::range{0., 100., 0.}>
340 "Output position = estimate + velocity * lead (ms): compensates "
341 "downstream latency at the price of overshoot.")
343 struct : halp::spinbox_f32<
"Output Deadband", halp::range{0., 0.1, 0.0005}>
347 "Suppress output changes smaller than this distance (coordinate "
348 "units; ~half a pixel in normalized camera space). 0 = off.")
352 struct : halp::spinbox_i32<
"Slot Count", halp::range{0, 64, 8}>
356 "Size of the dense reusable slot bank (for mapping to a fixed set of "
357 "voices/parameters). 0 disables slots.")
359 struct : halp::combobox_t<
"Allocation", TrackerAllocation>
363 "How a newly confirmed track picks a slot: Lowest Free (deterministic, "
364 "cv.jit-style), Round Robin (spread reuse over the bank, so a dead "
365 "voice gets time to fade), or Nearest Vacated (an object re-entering "
366 "where one just left inherits that slot).")
369 std::string_view values[3]{
"Lowest free",
"Round robin",
"Nearest vacated"};
370 TrackerAllocation init{TrackerAllocation::LowestFree};
373 struct : halp::combobox_t<
"Steal Policy", TrackerSteal>
377 "When every slot is taken and a new track confirms: Never (it stays "
378 "unslotted until one frees up), or steal from the Stalest (unseen the "
379 "longest) or Lowest Confidence track.")
382 std::string_view values[3]{
"Never",
"Stalest",
"Lowest confidence"};
383 TrackerSteal init{TrackerSteal::Never};
386 struct : halp::spinbox_f32<
"Hold Time", halp::range{0., 5000., 250.}>
390 "Quarantine (ms): a vacated slot is not handed to a new track for "
391 "this long, so a brief dropout does not shuffle the whole bank.")
395 struct : halp::combobox_t<
"Order By", TrackerOrder>
397 halp_meta(description,
"Ordering of the compact outputs.")
400 std::string_view values[5]{
"Id",
"Slot",
"Age",
"Confidence",
"Distance to anchor"};
401 TrackerOrder init{TrackerOrder::Id};
405 : std::conditional_t<
406 N == 2, halp::xy_spinboxes_f32<
"Anchor", halp::range{-1000., 1000., 0.}>,
407 halp::xyz_spinboxes_f32<
"Anchor", halp::range{-1000., 1000., 0.}>>
410 description,
"Reference point for the Distance To Anchor ordering.")
412 struct : halp::combobox_t<
"Data Format", TrackerFormat>
416 "Positions/Ids layout: Compact = live tracks back-to-back in Order By "
417 "order; Slots = Slot Count fixed positions indexed by slot, "
418 "zero-padded (GPU/voice-bank friendly).")
421 std::string_view values[2]{
"Compact",
"Slots"};
422 TrackerFormat init{TrackerFormat::Compact};
426 struct : halp::impulse_button<
"Reset IDs">
428 halp_meta(description,
"Forget every track and restart ids from 1.")
429 void update(
auto& self) { self.reset_requested =
true; }
437 halp_meta(name,
"Tracks")
440 "Every emitted track as a record:
id, slot, state, creation_time, "
441 "age, time_since_seen, position, position_raw, velocity, confidence, "
442 "provisional, reacquired.")
448 halp_meta(name,
"Count")
451 "Number of currently emitted tracks (confirmed + coasting + revived, "
452 "plus provisional ones when Emit Unconfirmed is on).")
458 halp_meta(name,
"Positions")
461 "Flat [x,y,...]
float list of the smoothed track positions, in the "
462 "chosen Data Format. Cables directly into Point2D View, Array to "
463 "Mesh, and other renderers.")
464 std::vector<
float> value;
469 halp_meta(name,
"Ids")
472 "Track
id per entry of Positions (-1 for an empty slot in Slots "
474 std::vector<
int> value;
477 struct : halp::callback<"Entered",
int>
481 "Fires with the track id the instant a detection births a new track "
482 "(before confirmation) - the lowest-latency onset signal.")
484 struct : halp::callback<
"Confirmed",
int>
488 "Fires with the track id when a track passes confirmation and joins "
491 struct : halp::callback<
"Exited",
int>
495 "Fires with the track id when a track is removed for good (coast and "
496 "revival windows exhausted). Use it to release voices/mappings.")
498 struct : halp::callback<
"Revived",
int>
502 "Fires with the track id when a lost track is re-acquired with its "
514 halp_meta(name,
"Point Tracker")
515 halp_meta(layout, halp::layouts::tabs)
516 halp_meta(background, halp::colors::background_mid)
520 halp_meta(name,
"Association")
521 halp_meta(layout, halp::layouts::hbox)
525 halp_meta(name,
"Gating")
526 halp_meta(layout, halp::layouts::vbox)
527 halp::item<&ins::motion_gate> motion_gate;
528 halp::item<&ins::max_speed> max_speed;
529 halp::item<&ins::accel_sigma> accel_sigma;
530 halp::item<&ins::meas_noise> meas_noise;
533 halp::spacing sp1{.width = 12, .height = 1};
537 halp_meta(name,
"Confidence")
538 halp_meta(layout, halp::layouts::vbox)
539 halp::item<&ins::two_stage> two_stage;
540 halp::item<&ins::high_conf> high_conf;
541 halp::item<&ins::low_conf> low_conf;
542 halp::item<&ins::new_conf> new_conf;
545 halp::spacing sp2{.width = 12, .height = 1};
549 halp_meta(name,
"Extra cues")
550 halp_meta(layout, halp::layouts::vbox)
551 halp::item<&ins::dir_weight> dir_weight;
552 halp::item<&ins::conf_weight> conf_weight;
558 halp_meta(name,
"Lifecycle")
559 halp_meta(layout, halp::layouts::hbox)
563 halp_meta(name,
"Confirmation")
564 halp_meta(layout, halp::layouts::vbox)
565 halp::item<&ins::confirm_time> confirm_time;
566 halp::item<&ins::confirm_hits> confirm_hits;
567 halp::item<&ins::confirm_window> confirm_window;
568 halp::item<&ins::instant_confirm> instant_confirm;
569 halp::item<&ins::emit_unconfirmed> emit_unconfirmed;
572 halp::spacing sp1{.width = 12, .height = 1};
576 halp_meta(name,
"Coast & revive")
577 halp_meta(layout, halp::layouts::vbox)
578 halp::item<&ins::coast_time> coast_time;
579 halp::item<&ins::revive> revive;
580 halp::item<&ins::revive_time> revive_time;
581 halp::item<&ins::revive_reupdate> revive_reupdate;
587 halp_meta(name,
"Smoothing")
588 halp_meta(layout, halp::layouts::hbox)
592 halp_meta(name,
"Filter")
593 halp_meta(layout, halp::layouts::vbox)
594 halp::item<&ins::smooth> smooth;
595 halp::item<&ins::min_cutoff> min_cutoff;
596 halp::item<&ins::beta> beta;
599 halp::spacing sp1{.width = 12, .height = 1};
603 halp_meta(name,
"Latency")
604 halp_meta(layout, halp::layouts::vbox)
605 halp::item<&ins::lead> lead;
606 halp::item<&ins::deadband> deadband;
612 halp_meta(name,
"Slots & output")
613 halp_meta(layout, halp::layouts::hbox)
617 halp_meta(name,
"Bank")
618 halp_meta(layout, halp::layouts::vbox)
619 halp::item<&ins::slot_count> slot_count;
620 halp::item<&ins::allocation> allocation;
623 halp::spacing sp1{.width = 12, .height = 1};
627 halp_meta(name,
"Reuse")
628 halp_meta(layout, halp::layouts::vbox)
629 halp::item<&ins::steal> steal;
630 halp::item<&ins::hold_time> hold_time;
631 halp::item<&ins::reset_ids> reset_ids;
634 halp::spacing sp2{.width = 12, .height = 1};
638 halp_meta(name,
"Output")
639 halp_meta(layout, halp::layouts::vbox)
640 halp::item<&ins::format> format;
641 halp::item<&ins::order_by> order_by;
642 halp::item<&ins::anchor> anchor;
647 using tick = halp::tick_musical;
649 void prepare(halp::setup s)
655 void operator()(
const halp::tick_musical& tk)
659 if(tk.position_in_nanoseconds > 0 && m_last_ns > 0
660 && tk.position_in_nanoseconds > m_last_ns)
661 dt = (tk.position_in_nanoseconds - m_last_ns) * 1e-9;
662 else if(tk.frames > 0)
663 dt = tk.frames / m_rate;
664 if(tk.position_in_nanoseconds > 0)
665 m_last_ns = tk.position_in_nanoseconds;
666 dt = std::clamp(dt, 0., 2.);
670 reset_requested =
false;
677 bool recompute =
false;
680 points_dirty =
false;
682 m_tracker.update(m_dets.data(), m_dets.size(),
float(dt));
687 m_tracker.advance(
float(dt));
691 if(m_silent_acc >= m_tracker.estimated_period() && has_emitted_tracks())
695 const auto& ev = m_tracker.events();
696 if(!ev.entered.empty() || !ev.confirmed.empty() || !ev.exited.empty()
697 || !ev.revived.empty())
700 for(
auto id : ev.entered)
702 for(
auto id : ev.confirmed)
703 outputs.confirmed(id);
704 for(
auto id : ev.revived)
706 for(
auto id : ev.exited)
708 m_last_out.erase(
id);
719 bool points_dirty =
false;
720 bool reset_requested =
false;
723 using track_t =
typename tracker_type::track;
728 c.max_speed = std::max(inputs.max_speed.value, 1e-4f);
729 switch(inputs.motion_gate.value)
731 case TrackerMotionGate::MaxSpeed:
732 c.gate = ossia::track_motion_gate::max_speed;
734 case TrackerMotionGate::Mahalanobis:
735 c.gate = ossia::track_motion_gate::mahalanobis;
737 case TrackerMotionGate::Off:
738 c.gate = ossia::track_motion_gate::off;
741 c.two_stage = inputs.two_stage.value;
742 c.high_conf = inputs.high_conf.value;
743 c.low_conf = inputs.low_conf.value;
744 c.new_conf = inputs.new_conf.value;
745 c.dir_weight = inputs.dir_weight.value;
746 c.conf_weight = inputs.conf_weight.value;
747 c.accel_sigma = inputs.accel_sigma.value;
748 c.meas_std = inputs.meas_noise.value;
749 c.confirm_time = inputs.confirm_time.value * 1e-3f;
750 c.confirm_hits = std::uint32_t(std::max(inputs.confirm_hits.value, 1));
751 c.confirm_window = std::uint32_t(std::clamp(inputs.confirm_window.value, 1, 31));
752 c.instant_confirm = inputs.instant_confirm.value;
753 c.coast_time = inputs.coast_time.value * 1e-3f;
754 c.revive = inputs.revive.value;
755 c.revive_time = inputs.revive_time.value * 1e-3f;
756 c.revive_reupdate = inputs.revive_reupdate.value;
757 c.smooth = inputs.smooth.value;
758 c.min_cutoff = std::max(inputs.min_cutoff.value, 0.001f);
759 c.beta = inputs.beta.value;
760 c.slot_count = std::uint32_t(std::max(inputs.slot_count.value, 0));
761 switch(inputs.allocation.value)
763 case TrackerAllocation::LowestFree:
764 c.allocation = ossia::track_slot_allocation::lowest_free;
766 case TrackerAllocation::RoundRobin:
767 c.allocation = ossia::track_slot_allocation::round_robin;
769 case TrackerAllocation::NearestVacated:
770 c.allocation = ossia::track_slot_allocation::nearest_vacated;
773 switch(inputs.steal.value)
775 case TrackerSteal::Never:
776 c.steal = ossia::track_slot_steal::never;
778 case TrackerSteal::Stalest:
779 c.steal = ossia::track_slot_steal::stalest;
781 case TrackerSteal::LowestConfidence:
782 c.steal = ossia::track_slot_steal::lowest_confidence;
785 c.slot_hold_time = inputs.hold_time.value * 1e-3f;
786 m_tracker.configure(c);
789 static bool number_like(
const ossia::value& v)
noexcept
791 const auto t = v.get_type();
792 return t == ossia::val_type::FLOAT || t == ossia::val_type::INT
793 || t == ossia::val_type::BOOL;
796 static float to_float(
const ossia::value& v)
noexcept
800 case ossia::val_type::FLOAT:
801 return *v.target<
float>();
802 case ossia::val_type::INT:
803 return float(*v.target<
int>());
804 case ossia::val_type::BOOL:
805 return *v.target<
bool>() ? 1.f : 0.f;
813 bool parse_element(
const ossia::value& v, detection_type& out)
noexcept
817 case ossia::val_type::VEC2F: {
818 const auto& a = *v.target<ossia::vec2f>();
821 out.position = {a[0], a[1]};
822 out.confidence = 1.f;
827 case ossia::val_type::VEC3F: {
828 const auto& a = *v.target<ossia::vec3f>();
831 out.position = {a[0], a[1]};
832 out.confidence = a[2];
836 out.position = {a[0], a[1], a[2]};
837 out.confidence = 1.f;
841 case ossia::val_type::VEC4F: {
842 const auto& a = *v.target<ossia::vec4f>();
845 out.position = {a[0], a[1]};
846 out.confidence = a[2];
850 out.position = {a[0], a[1], a[2]};
851 out.confidence = a[3];
855 case ossia::val_type::LIST: {
856 const auto& l = *v.target<std::vector<ossia::value>>();
859 for(std::size_t i = 0; i < N; i++)
861 if(!number_like(l[i]))
863 out.position[i] = to_float(l[i]);
865 out.confidence = (l.size() > N && number_like(l[N])) ? to_float(l[N]) : 1.f;
868 case ossia::val_type::MAP: {
869 const auto& m = *v.target<ossia::value_map_type>();
870 bool has_pos =
false;
871 out.confidence = 1.f;
872 for(
const auto& [k, val] : m)
874 if(k ==
"position" || k ==
"pos" || k ==
"centroid" || k ==
"point")
877 if(parse_element(val, sub))
879 out.position = sub.position;
883 else if(k ==
"confidence" || k ==
"score" || k ==
"conf")
886 out.confidence = to_float(val);
896 void parse_detections()
899 const auto& in = inputs.points.value;
904 if(number_like(in[0]))
906 for(std::size_t i = 0; i + N <= in.size(); i += N)
910 for(std::size_t k = 0; k < N; k++)
912 if(!number_like(in[i + k]))
917 d.position[k] = to_float(in[i + k]);
922 if(std::isfinite(d.position[0]))
924 if(m_dets.size() >= 1024)
930 for(
const auto& v : in)
933 if(parse_element(v, d) && std::isfinite(d.position[0])
934 && std::isfinite(d.position[1]))
936 if(m_dets.size() >= 1024)
941 bool has_emitted_tracks() const noexcept
943 for(
const auto& t : m_tracker.tracks())
944 if(t.emitted(inputs.emit_unconfirmed.value))
949 static const char* state_name(ossia::track_state s)
noexcept
953 case ossia::track_state::provisional:
954 return "provisional";
955 case ossia::track_state::confirmed:
957 case ossia::track_state::coasting:
959 case ossia::track_state::revived:
961 case ossia::track_state::lost:
963 case ossia::track_state::expired:
970 std::array<float, N> output_position(
const track_t& t)
972 std::array<float, N> p = t.filtered;
973 const float lead_s = inputs.lead.value * 1e-3f;
976 const auto v = t.velocity();
977 for(std::size_t i = 0; i < N; i++)
978 p[i] += v[i] * lead_s;
981 const float db = inputs.deadband.value;
984 auto it = m_last_out.find(t.id);
985 if(it != m_last_out.end())
988 for(std::size_t i = 0; i < N; i++)
990 const float d = p[i] - it->second[i];
999 m_last_out.emplace(t.id, p);
1005 static position_type to_position(
const std::array<float, N>& a)
noexcept
1007 if constexpr(N == 2)
1008 return {a[0], a[1]};
1010 return {a[0], a[1], a[2]};
1017 for(
const auto& t : m_tracker.tracks())
1018 if(t.emitted(inputs.emit_unconfirmed.value))
1019 m_order.push_back(&t);
1022 const auto key_less = [
this](
const track_t* a,
const track_t* b) {
1023 switch(inputs.order_by.value)
1025 case TrackerOrder::Id:
1026 return a->id < b->id;
1027 case TrackerOrder::Slot: {
1029 const auto sa = a->slot < 0 ? INT32_MAX : a->slot;
1030 const auto sb = b->slot < 0 ? INT32_MAX : b->slot;
1031 return sa != sb ? sa < sb : a->id < b->id;
1033 case TrackerOrder::Age:
1034 return a->age != b->age ? a->age > b->age : a->id < b->id;
1035 case TrackerOrder::Confidence:
1036 return a->confidence != b->confidence ? a->confidence > b->confidence
1038 case TrackerOrder::DistanceToAnchor: {
1039 const float da = anchor_distance2(*a), db_ = anchor_distance2(*b);
1040 return da != db_ ? da < db_ : a->id < b->id;
1043 return a->id < b->id;
1045 std::sort(m_order.begin(), m_order.end(), key_less);
1048 auto& recs = outputs.tracks.value;
1050 recs.reserve(m_order.size());
1051 for(
const track_t* t : m_order)
1056 r.state = state_name(t->state);
1057 r.creation_time = t->creation_time;
1059 r.time_since_seen = t->time_since_seen;
1060 r.position = to_position(output_position(*t));
1061 r.position_raw = to_position(t->last_meas);
1062 r.velocity = to_position(t->velocity());
1063 r.confidence = t->confidence;
1064 r.provisional = t->state == ossia::track_state::provisional;
1065 r.reacquired = t->reacquired;
1066 recs.push_back(std::move(r));
1068 outputs.count.value = int(m_order.size());
1071 auto& pos = outputs.positions.value;
1072 auto& ids = outputs.ids.value;
1075 if(inputs.format.value == TrackerFormat::Slots)
1077 const int slots = std::max(inputs.slot_count.value, 0);
1078 pos.assign(std::size_t(slots) * N, 0.f);
1079 ids.assign(std::size_t(slots), -1);
1080 for(
const track_t* t : m_order)
1082 if(t->slot < 0 || t->slot >= slots)
1084 const auto p = output_position(*t);
1085 for(std::size_t i = 0; i < N; i++)
1086 pos[std::size_t(t->slot) * N + i] = p[i];
1087 ids[std::size_t(t->slot)] = t->id;
1092 pos.reserve(m_order.size() * N);
1093 ids.reserve(m_order.size());
1094 for(
const track_t* t : m_order)
1096 const auto p = output_position(*t);
1097 for(std::size_t i = 0; i < N; i++)
1098 pos.push_back(p[i]);
1099 ids.push_back(t->id);
1104 float anchor_distance2(
const track_t& t)
const noexcept
1106 const auto& a = inputs.anchor.value;
1107 const auto p = t.filtered;
1108 float d2 = (p[0] - a.x) * (p[0] - a.x) + (p[1] - a.y) * (p[1] - a.y);
1109 if constexpr(N == 3)
1110 d2 += (p[2] - a.z) * (p[2] - a.z);
1115 tracker_type m_tracker;
1116 typename tracker_type::config m_cfg;
1117 std::vector<detection_type> m_dets;
1118 std::vector<const track_t*> m_order;
1119 ossia::flat_map<std::int32_t, std::array<float, N>> m_last_out;
1120 double m_rate = 48000.;
1121 std::int64_t m_last_ns = 0;
1122 double m_silent_acc = 0.;