///|
/// Per-track residual statistics for bundle-adjustment diagnostics.
pub(all) struct BundleStatistics {
count : Int
mean : Double
rms : Double
maximum : Double
}
///|
pub fn bundle_statistics(residuals : ArrayView[Double]) -> BundleStatistics {
if residuals.length() == 0 {
return { count: 0, mean: 0.0, rms: 0.0, maximum: 0.0 }
}
let mut sum = 0.0
let mut square = 0.0
let mut maximum = 0.0
for residual in residuals {
let value = @core.abs(residual)
sum += value
square += value * value
maximum = maximum.max(value)
}
{
count: residuals.length(),
mean: sum / residuals.length().to_double(),
rms: (square / residuals.length().to_double()).sqrt(),
maximum,
}
}
///|
pub fn robust_bundle_cost(
residuals : ArrayView[Double],
threshold : Double,
) -> Double {
let mut cost = 0.0
for residual in residuals {
let value = @core.abs(residual)
cost += if value <= threshold {
value * value
} else {
2.0 * threshold * value - threshold * threshold
}
}
cost
}
///|
pub fn huber_weights(
residuals : ArrayView[Double],
threshold : Double,
) -> Array[Double] {
let result : Array[Double] = []
for residual in residuals {
let value = @core.abs(residual)
result.push(
if value <= threshold || value == 0.0 {
1.0
} else {
threshold / value
},
)
}
result
}
///|
pub fn residuals_above(
residuals : ArrayView[Double],
threshold : Double,
) -> Array[Int] {
let result : Array[Int] = []
for i in 0.. threshold {
result.push(i)
}
}
result
}
///|
pub fn weighted_cost(
residuals : ArrayView[Double],
weights : ArrayView[Double],
) -> Double {
let mut result = 0.0
let count = residuals.length().min(weights.length())
for i in 0..