///|
pub(all) struct ThermalRoi {
origin : ThermalPoint
width : Int
height : Int
} derive(Debug, Eq, ToJson)
///|
pub fn ThermalRoi::new(
origin~ : ThermalPoint,
width~ : Int,
height~ : Int,
) -> ThermalRoi raise ThermalError {
if width <= 0 || height <= 0 {
raise InvalidDimensions(width~, height~, values=width * height)
}
{ origin, width, height }
}
///|
pub fn ThermalRoi::contains(roi : ThermalRoi, point : ThermalPoint) -> Bool {
point.x >= roi.origin.x &&
point.y >= roi.origin.y &&
point.x < roi.origin.x + roi.width &&
point.y < roi.origin.y + roi.height
}
///|
pub fn ThermalRoi::area(roi : ThermalRoi) -> Int {
roi.width * roi.height
}
///|
pub fn ThermalRoi::end(roi : ThermalRoi) -> ThermalPoint {
ThermalPoint::new(
x=roi.origin.x + roi.width - 1,
y=roi.origin.y + roi.height - 1,
)
}
///|
pub fn ThermalRoi::intersection(a : ThermalRoi, b : ThermalRoi) -> ThermalRoi? {
let x0 = Int::max(a.origin.x, b.origin.x)
let y0 = Int::max(a.origin.y, b.origin.y)
let x1 = Int::min(a.end().x, b.end().x)
let y1 = Int::min(a.end().y, b.end().y)
if x0 > x1 || y0 > y1 {
None
} else {
Some({
origin: ThermalPoint::new(x=x0, y=y0),
width: x1 - x0 + 1,
height: y1 - y0 + 1,
})
}
}
///|
pub fn ThermalRoi::union(a : ThermalRoi, b : ThermalRoi) -> ThermalRoi {
let x0 = Int::min(a.origin.x, b.origin.x)
let y0 = Int::min(a.origin.y, b.origin.y)
let x1 = Int::max(a.end().x, b.end().x)
let y1 = Int::max(a.end().y, b.end().y)
{
origin: ThermalPoint::new(x=x0, y=y0),
width: x1 - x0 + 1,
height: y1 - y0 + 1,
}
}
///|
pub fn ThermalMatrix::roi(
matrix : ThermalMatrix,
roi : ThermalRoi,
) -> ThermalMatrix raise ThermalError {
matrix.crop(origin=roi.origin, width=roi.width, height=roi.height)
}
///|
pub fn ThermalMatrix::roi_stats(
matrix : ThermalMatrix,
roi : ThermalRoi,
) -> ThermalStats raise ThermalError {
matrix.roi(roi).stats()
}
///|
pub(all) struct ProfileSample {
distance : Int
temperature : Double
} derive(Debug, Eq, ToJson)
///|
pub fn ThermalMatrix::line_profile(
matrix : ThermalMatrix,
start~ : ThermalPoint,
end~ : ThermalPoint,
) -> Array[ProfileSample] raise ThermalError {
ignore(matrix.index(start))
ignore(matrix.index(end))
let dx = (end.x - start.x).abs()
let dy = (end.y - start.y).abs()
let steps = Int::max(dx, dy)
let samples : Array[ProfileSample] = []
if steps == 0 {
samples.push({
distance: 0,
temperature: matrix.unsafe_get(x=start.x, y=start.y),
})
} else {
for i in 0..<=steps {
let x = start.x + (end.x - start.x) * i / steps
let y = start.y + (end.y - start.y) * i / steps
samples.push({ distance: i, temperature: matrix.unsafe_get(x~, y~) })
}
}
samples
}
///|
pub fn ThermalMatrix::roi_mean_profile(
matrix : ThermalMatrix,
roi : ThermalRoi,
) -> Array[Double] raise ThermalError {
let frame = matrix.roi(roi)
let result : Array[Double] = []
for y in 0.. Array[Double] raise ThermalError {
let frame = matrix.roi(roi)
let result : Array[Double] = []
for x in 0.. ThermalPoint {
{ x: point.x + dx, y: point.y + dy }
}
///|
pub fn ThermalPoint::manhattan(a : ThermalPoint, b : ThermalPoint) -> Int {
(a.x - b.x).abs() + (a.y - b.y).abs()
}
///|
pub fn ThermalPoint::chebyshev(a : ThermalPoint, b : ThermalPoint) -> Int {
Int::max((a.x - b.x).abs(), (a.y - b.y).abs())
}
///|
pub fn ThermalPoint::neighbors(
point : ThermalPoint,
connectivity? : Int = 4,
) -> Array[ThermalPoint] {
let result : Array[ThermalPoint] = [
point.translate(dx=-1, dy=0),
point.translate(dx=1, dy=0),
point.translate(dx=0, dy=-1),
point.translate(dx=0, dy=1),
]
if connectivity >= 8 {
result.append([
point.translate(dx=-1, dy=-1),
point.translate(dx=1, dy=-1),
point.translate(dx=-1, dy=1),
point.translate(dx=1, dy=1),
])
}
result
}
///|
pub fn ThermalMatrix::weighted_mean(
matrix : ThermalMatrix,
weights : ThermalMatrix,
) -> Double raise ThermalError {
if matrix.width != weights.width || matrix.height != weights.height {
raise InvalidDimensions(
width=matrix.width,
height=matrix.height,
values=weights.values.length(),
)
}
let mut numerator = 0.0
let mut denominator = 0.0
for i, value in matrix.values {
let weight = weights.values[i]
numerator += value * weight
denominator += weight
}
if denominator == 0.0 {
0.0
} else {
numerator / denominator
}
}
///|
pub fn ThermalMatrix::masked_values(
matrix : ThermalMatrix,
mask : ThermalMask,
) -> Array[Double] raise ThermalError {
if matrix.width != mask.width || matrix.height != mask.height {
raise InvalidDimensions(
width=matrix.width,
height=matrix.height,
values=mask.cells.length(),
)
}
let values : Array[Double] = []
for i, value in matrix.values {
if mask.cells[i] {
values.push(value)
}
}
values
}