///|
/// Aggregate track-level visibility and pixel motion statistics.
pub(all) struct TrackAnalytics {
track_count : Int
observation_count : Int
mean_views : Double
mean_span : Double
maximum_span : Double
duplicate_observations : Int
} derive(Debug, Eq)
///|
pub fn track_analytics(
observations : ArrayView[TrackObservation],
) -> TrackAnalytics {
let ids : Array[Int] = []
for observation in observations {
if !ids.contains(observation.point_id) {
ids.push(observation.point_id)
}
}
let mut total_views = 0
let mut total_span = 0.0
let mut maximum_span = 0.0
let mut duplicates = 0
for id in ids {
let group = track_observations_for(observations, id)
total_views += track_view_ids(group).length()
let mut min_x = 0.0
let mut max_x = 0.0
let mut min_y = 0.0
let mut max_y = 0.0
for i in 0.. Double {
if observations.length() == 0 {
0.0
} else {
let mut min_x = observations[0].pixel.x
let mut max_x = observations[0].pixel.x
let mut min_y = observations[0].pixel.y
let mut max_y = observations[0].pixel.y
for observation in observations {
min_x = min_x.min(observation.pixel.x)
max_x = max_x.max(observation.pixel.x)
min_y = min_y.min(observation.pixel.y)
max_y = max_y.max(observation.pixel.y)
}
@core.Vec2::new(x=max_x - min_x, y=max_y - min_y).norm()
}
}
///|
pub fn track_pixel_motion(
observations : ArrayView[TrackObservation],
) -> Array[Double] {
let result : Array[Double] = []
if observations.length() < 2 {
return result
}
for i in 0..<(observations.length() - 1) {
result.push(observations[i + 1].pixel.minus(observations[i].pixel).norm())
}
result
}
///|
pub fn track_mean_motion(observations : ArrayView[TrackObservation]) -> Double {
let values = track_pixel_motion(observations)
@core.mean(values) catch {
_ => 0.0
}
}
///|
pub fn track_max_motion(observations : ArrayView[TrackObservation]) -> Double {
let mut result = 0.0
for value in track_pixel_motion(observations) {
result = result.max(value)
}
result
}
///|
pub fn track_motion_is_smooth(
observations : ArrayView[TrackObservation],
threshold : Double,
) -> Bool {
for value in track_pixel_motion(observations) {
if value > threshold {
return false
}
}
true
}
///|
pub fn track_view_gap_count(observations : ArrayView[TrackObservation]) -> Int {
let sorted = track_view_ids(observations)
if sorted.length() < 2 {
0
} else {
sorted.sort()
let mut gaps = 0
for i in 0..<(sorted.length() - 1) {
gaps += (sorted[i + 1] - sorted[i] - 1).max(0)
}
gaps
}
}
///|
pub fn track_view_density(
observations : ArrayView[TrackObservation],
total_views : Int,
) -> Double {
if total_views <= 0 {
0.0
} else {
track_view_ids(observations).length().to_double() / total_views.to_double()
}
}
///|
pub fn track_is_repeated(observations : ArrayView[TrackObservation]) -> Bool {
track_view_ids(observations).length() < observations.length()
}
///|
pub fn track_quality_score2(
observations : ArrayView[TrackObservation],
minimum_views : Int,
minimum_span : Double,
) -> Double {
let support = if minimum_views <= 0 {
1.0
} else {
(track_view_ids(observations).length().to_double() /
minimum_views.to_double()).min(1.0)
}
let spread = if minimum_span <= 0.0 {
1.0
} else {
(track_pixel_span(observations) / minimum_span).min(1.0)
}
support * spread
}
///|
pub fn track_filter_quality(
observations : ArrayView[TrackObservation],
minimum_views : Int,
minimum_span : Double,
) -> Array[Int] {
let ids : Array[Int] = []
for observation in observations {
if !ids.contains(observation.point_id) {
let group = track_observations_for(observations, observation.point_id)
if track_quality_score2(group, minimum_views, minimum_span) >= 1.0 {
ids.push(observation.point_id)
}
}
}
ids
}
///|
pub fn track_observation_matrix(
observations : ArrayView[TrackObservation],
) -> ObservationMatrix {
let ids : Array[Int] = []
let views : Array[Int] = []
for observation in observations {
if !ids.contains(observation.point_id) {
ids.push(observation.point_id)
}
if !views.contains(observation.view_id) {
views.push(observation.view_id)
}
}
let matrix = ObservationMatrix::new(views=views.length(), tracks=ids.length())
for observation in observations {
let mut track_index = 0
let mut view_index = 0
for i in 0..