///|
fn lerp_point(a : Point, b : Point, t : Double) -> Point {
Point(x=a.x + (b.x - a.x) * t, y=a.y + (b.y - a.y) * t)
}
///|
fn matches_mode(mode : EdgeMode, gradient : Double) -> Bool {
match mode {
Rising => gradient > 0.0
Falling => gradient < 0.0
Both => gradient != 0.0
}
}
///|
pub fn edge_profile(
image : GrayImage,
start : Point,
finish : Point,
steps? : Int = 96,
) -> Array[ProfileSample] {
let n = if steps < 2 { 2 } else { steps }
let samples : Array[ProfileSample] = []
for i in 0.. EdgePoint {
let g0 = previous.gradient.abs()
let g1 = current.gradient.abs()
let total = g0 + g1
let t = if total <= 0.0 { 1.0 } else { (threshold - g0) / total }
let clamped = t.clamp(min=0.0, max=1.0)
{
point: lerp_point(previous.point, current.point, clamped),
strength: current.gradient.abs(),
polarity: if current.gradient >= 0.0 {
1
} else {
-1
},
}
}
///|
pub fn ProfileSample::detect_edges(
samples : Array[ProfileSample],
threshold : Double,
mode? : EdgeMode = Both,
) -> Array[EdgePoint] {
let edges : Array[EdgePoint] = []
for i in 1..= threshold &&
matches_mode(mode, current.gradient) {
edges.push(subpixel_edge(samples[i - 1], current, threshold))
}
}
edges
}
///|
pub fn gauge_width(
image : GrayImage,
start : Point,
finish : Point,
threshold? : Double = 0.15,
) -> Double {
match try_gauge_width(image, start, finish, threshold~) {
Some(width) => width
None => 0.0
}
}
///|
pub fn try_gauge_width(
image : GrayImage,
start : Point,
finish : Point,
threshold? : Double = 0.15,
) -> Double? {
let samples = edge_profile(image, start, finish)
let edges = ProfileSample::detect_edges(samples, threshold, mode=Both)
if edges.length() < 2 {
None
} else {
Some(distance(edges[0].point, edges[edges.length() - 1].point))
}
}