///|
pub fn process_frame(
image : GrayImage,
config : CaliperConfig,
tolerance : Tolerance,
) -> PipelineResult {
let validation = validate_image(image)
let config_validation = validate_caliper_config(config)
let result = measure_caliper(image, config, tolerance)
let diagnostics = validation
diagnostics.append(config_validation)
diagnostics.append(result.diagnostics)
{
..result,
diagnostics,
accepted: result.accepted && diagnostics_ok(diagnostics),
}
}
///|
pub fn process_frames(
images : Array[GrayImage],
config : CaliperConfig,
tolerance : Tolerance,
) -> BatchRun {
let batch = BatchRun::new("frames")
for image in images {
let result = process_frame(image, config, tolerance)
for measurement in result.measurements {
batch.add(measurement)
}
}
batch
}
///|
pub fn process_pyramid(
image : GrayImage,
config : CaliperConfig,
tolerance : Tolerance,
levels : Int,
) -> PipelineResult {
let pyramid = build_pyramid(image, levels)
if pyramid.is_empty() {
{
measurements: [],
diagnostics: [diagnostic_error("pyramid", "no pyramid levels")],
accepted: false,
confidence: 0.0,
}
} else {
let level = pyramid[0]
process_frame(level.image, config, tolerance)
}
}
///|
pub fn frame_quality(image : GrayImage) -> QualitySummary {
let profile = summarize_profile(
edge_profile(
image,
Point::Point(x=0.0, y=Double::from_int(image.height / 2)),
Point::Point(
x=Double::from_int((image.width - 1).max(0)),
y=Double::from_int(image.height / 2),
),
),
0.05,
)
profile_quality(profile, 2.0)
}
///|
pub fn frame_diagnostics(
image : GrayImage,
config : CaliperConfig,
tolerance : Tolerance,
) -> Array[DiagnosticEvent] {
let result = diagnostics_for_image(image)
let profile = summarize_profile(
edge_profile(image, config.start, config.finish),
config.threshold,
mode=config.mode,
)
result.append(quality_diagnostics(image, profile))
let tolerance_events = [
diagnostic_event("LOWER", "tolerance", "lower bound", tolerance.lower),
diagnostic_event("UPPER", "tolerance", "upper bound", tolerance.upper),
]
result.append(tolerance_events)
result
}
///|
pub fn sliding_window(
values : Array[Double],
width : Int,
step : Int,
) -> Array[Array[Double]] {
let result : Array[Array[Double]] = []
let safe_width = width.max(1)
let safe_step = step.max(1)
let mut start = 0
while start < values.length() {
let window : Array[Double] = []
for i in start..<(start + safe_width).min(values.length()) {
window.push(values[i])
}
if !window.is_empty() {
result.push(window)
}
start += safe_step
}
result
}
///|
pub fn window_statistics(
values : Array[Double],
width : Int,
step : Int,
) -> Array[StatsSummary] {
sliding_window(values, width, step).map(fn(window) {
describe_samples(window)
})
}
///|
pub fn rolling_decisions(
values : Array[Double],
tolerance : Tolerance,
width : Int,
) -> Array[MeasurementDecision] {
let result : Array[MeasurementDecision] = []
for window in sliding_window(values, width, 1) {
result.push(tolerance.judge(mean_value(window)))
}
result
}
///|
pub fn first_failure(decisions : Array[MeasurementDecision]) -> Int? {
for i in 0.. Int {
let mut best = 0
let mut current = 0
for decision in decisions {
if decision == target {
current += 1
best = best.max(current)
} else {
current = 0
}
}
best
}
///|
pub fn frame_acceptance_rate(results : Array[PipelineResult]) -> Double {
if results.is_empty() {
0.0
} else {
let mut accepted = 0
for result in results {
if result.accepted {
accepted += 1
}
}
Double::from_int(accepted) / Double::from_int(results.length())
}
}
///|
pub fn merge_pipeline_results(
results : Array[PipelineResult],
) -> PipelineResult {
if results.is_empty() {
{ measurements: [], diagnostics: [], accepted: false, confidence: 0.0 }
} else {
let mut result = results[0]
for item in results[1:] {
result = suite_merge(result, item)
}
result
}
}
///|
pub fn pipeline_timeline(results : Array[PipelineResult]) -> Array[Double] {
results.map(fn(result) { result.confidence })
}
///|
pub fn timeline_summary(results : Array[PipelineResult]) -> StatsSummary {
describe_samples(pipeline_timeline(results))
}
///|
pub fn frame_report(results : Array[PipelineResult]) -> String {
let out = StringBuilder()
out.write_string("frame,accepted,confidence,decision\n")
for i in 0..