///|
fn neighborhood_average(
matrix : ThermalMatrix,
center : ThermalPoint,
radius : Int,
) -> Double {
let mut sum = 0.0
let mut count = 0
let y0 = Int::max(0, center.y - radius)
let y1 = Int::min(matrix.height - 1, center.y + radius)
let x0 = Int::max(0, center.x - radius)
let x1 = Int::min(matrix.width - 1, center.x + radius)
for y in y0..<=y1 {
for x in x0..<=x1 {
if !(x == center.x && y == center.y) {
sum = sum + matrix.unsafe_get(x~, y~)
count = count + 1
}
}
}
if count == 0 {
matrix.unsafe_get(x=center.x, y=center.y)
} else {
sum / count.to_double()
}
}
///|
fn is_local_peak(
matrix : ThermalMatrix,
point : ThermalPoint,
radius : Int,
) -> Bool {
let center = matrix.unsafe_get(x=point.x, y=point.y)
let y0 = Int::max(0, point.y - radius)
let y1 = Int::min(matrix.height - 1, point.y + radius)
let x0 = Int::max(0, point.x - radius)
let x1 = Int::min(matrix.width - 1, point.x + radius)
for y in y0..<=y1 {
for x in x0..<=x1 {
if !(x == point.x && y == point.y) && matrix.unsafe_get(x~, y~) > center {
return false
}
}
}
true
}
///|
fn compare_hotspot_desc(a : Hotspot, b : Hotspot) -> Int {
if a.temperature > b.temperature {
-1
} else if a.temperature < b.temperature {
1
} else {
a.point.y.compare(b.point.y)
|> fn(order) {
if order != 0 {
order
} else {
a.point.x.compare(b.point.x)
}
}
}
}
///|
pub fn ThermalMatrix::detect_hotspots(
matrix : ThermalMatrix,
min_temp~ : Double,
radius? : Int = 1,
min_contrast? : Double = 0.0,
limit? : Int = 16,
) -> Array[Hotspot] {
let search_radius = Int::max(1, radius)
let hotspots : Array[Hotspot] = []
for y in 0..= min_temp && is_local_peak(matrix, point, search_radius) {
let ambient = neighborhood_average(matrix, point, search_radius)
let contrast = temp - ambient
if contrast >= min_contrast {
hotspots.push({ point, temperature: temp, contrast })
}
}
}
}
hotspots.sort_by(compare_hotspot_desc)
if hotspots.length() > limit {
hotspots.truncate(limit)
}
hotspots
}