///|
pub fn calibrate_anisotropic(
pixel_x : Double,
pixel_y : Double,
physical_x : Double,
physical_y : Double,
) -> AffineTransform {
match try_calibrate_anisotropic(pixel_x, pixel_y, physical_x, physical_y) {
Some(transform) => transform
None => AffineTransform::identity()
}
}
///|
pub fn try_calibrate_anisotropic(
pixel_x : Double,
pixel_y : Double,
physical_x : Double,
physical_y : Double,
) -> AffineTransform? {
if pixel_x > 0.0 &&
pixel_y > 0.0 &&
is_finite(pixel_x) &&
is_finite(pixel_y) &&
is_finite(physical_x) &&
is_finite(physical_y) {
Some(
AffineTransform::scaling(sx=physical_x / pixel_x, sy=physical_y / pixel_y),
)
} else {
None
}
}
///|
pub fn calibration_fit(
source : Array[Point],
target : Array[Point],
) -> CalibrationFit {
fit_affine(source, target)
}
///|
pub fn calibration_scale(fit : CalibrationFit) -> Vector2 {
{
x: (fit.transform.a * fit.transform.a + fit.transform.c * fit.transform.c).sqrt(),
y: (fit.transform.b * fit.transform.b + fit.transform.d * fit.transform.d).sqrt(),
}
}
///|
pub fn calibration_is_usable(
fit : CalibrationFit,
maximum_rms_error : Double,
) -> Bool {
is_finite(fit.rms_error) && fit.rms_error <= maximum_rms_error.max(0.0)
}
///|
pub fn calibrated_distance(
fit : AffineTransform,
first : Point,
second : Point,
) -> Double {
distance(fit.apply(first), fit.apply(second))
}
///|
pub fn calibrated_area(fit : AffineTransform, pixel_area : Double) -> Double {
fit.determinant().abs() * pixel_area
}
///|
pub fn calibrated_circle(fit : AffineTransform, circle : Circle) -> Circle {
let center = fit.apply(circle.center)
let scale = calibration_scale({
transform: fit,
residuals: [],
rms_error: 0.0,
max_error: 0.0,
})
let radius = circle.radius * (scale.x + scale.y) / 2.0
{ center, radius, rms_error: circle.rms_error * (scale.x + scale.y) / 2.0 }
}
///|
pub fn residual_summary(fit : CalibrationFit) -> StatsSummary {
describe_samples(fit.residuals)
}
///|
pub fn calibration_report(fit : CalibrationFit) -> String {
let out = StringBuilder()
out.write_string("rms_error=")
out.write_string(fit.rms_error.to_string())
out.write_string("; max_error=")
out.write_string(fit.max_error.to_string())
out.write_string("; samples=")
out.write_string(fit.residuals.length().to_string())
out.to_string()
}
///|
pub fn map_reference_grid(
fit : AffineTransform,
columns : Int,
rows : Int,
spacing_x : Double,
spacing_y : Double,
) -> Array[Point] {
let result : Array[Point] = []
for y in 0.. Point {
if references.is_empty() {
point
} else {
let mut result = references[0]
let mut best = distance(point, result)
for reference in references[1:] {
let current = distance(point, reference)
if current < best {
best = current
result = reference
}
}
result
}
}
///|
pub fn calibration_quality_score(
fit : CalibrationFit,
sample_count : Int,
) -> Double {
let count_factor = (1.0 - 1.0 / Double::from_int(sample_count.max(1))).max(
0.0,
)
count_factor / (1.0 + fit.rms_error.abs())
}