moonbit-thermal-vision

Thermal infrared matrix analysis toolkit for MoonBit.

thermal
infrared
vision
matrix
inspection
Download zip
Author
Version
0.2.1
License
Apache-2.0
Last updated
yesterday
Downloads
8

#moonbit-thermal-vision

A pure MoonBit toolkit for analyzing calibrated thermal-infrared temperature matrices. It is designed for industrial inspection, equipment monitoring, low-light sensing, and educational pipelines where a camera or upstream adapter provides a numeric temperature grid.

#Core capabilities

  • Validated matrices with safe indexing, rows, crops, rescaling, mapping, and CSV.
  • Mixed-delimiter text parsing for comma, semicolon, space, and tab data.
  • Thermal statistics, percentiles, histograms, z-score anomalies, and gradients.
  • Gray, ironbow, and blue-red palettes with ASCII PPM export.
  • Threshold masks, morphology, connected regions, bounding boxes, and PBM export.
  • Local hotspot detection, frame deltas, row/column trends, and quality reports.
  • Emissivity/gain/offset calibration, mean/median filters, kernels, convolution, Laplacian and sharpening helpers.
  • Deterministic 2048-entry pseudocolor and normalization lookup tables for embedded/native deployments that need stable output without interpolation.

The root package owns the domain model and algorithms. cmd/main is a small demonstration; cmd/benchmark is a reproducible workload entry point.

#Quick start

moon run cmd/main

Library usage:

let matrix = @thermal.parse_temperature_matrix(
#|31.2, 33.5, 35.1
#|30.9, 48.8, 36.2
#|29.8, 45.3, 34.4
)
let report = matrix.inspect_threshold(threshold=40.0)
let frame = matrix.colorize(palette=@thermal.Ironbow)
println(report.to_markdown())
println(frame.to_ppm_ascii())

#CLI and benchmark

The demo prints a Markdown inspection report and a PPM preview header:

moon run cmd/main moon run --target native cmd/benchmark

The benchmark performs 100 real 8x8 frames through mean filtering, threshold region extraction, and statistics. On the development machine (MoonBit 0.1.20260807, Windows, native target), the measured workload completed in approximately 121–126 ms for the complete native command across five local runs (including process startup); the program prints a checksum so a run cannot silently skip the work. Re-run the command on your own target when comparing machines.

#Architecture

root package ├── matrix / types validated data model and safe access ├── parse / export text and portable PPM/PBM/CSV representations ├── palette pseudocolor conversion ├── mask / regions binary morphology and connected components ├── hotspots / analytics local peaks, histograms, anomalies ├── calibration / quality input correction and quality classification ├── filters / spatial smoothing, kernels, gradients, convolution ├── temporal / tracking frame deltas, trends, and region trajectories └── lookup_tables deterministic 2048-entry edge-deployment LUTs

Algorithms operate on ordinary MoonBit arrays and standard-library types. No vendor SDK, generated runtime, or device-specific binary format is required. Private camera adapters can be layered above this package without changing its core data model.

#Testing and CI

The repository contains unit tests, documentation examples, and a broad boundary regression matrix. Run the same checks locally as CI:

moon fmt --check moon check --deny-warn moon info moon test --deny-warn moon build --target native

GitHub Actions tests Ubuntu, macOS, and Windows with the latest stable MoonBit installer, checks generated interfaces, runs all tests, and builds the native target. A separate workflow runs the benchmark smoke test on pushes to main.

#License

Apache-2.0. See LICENSE.

ThermalError

pub(all) suberror ThermalError {
InvalidDimensions(width~ : Int, height~ : Int, values~ : Int)
EmptyMatrix
OutOfBounds(x~ : Int, y~ : Int, width~ : Int, height~ : Int)
RaggedRows(expected~ : Int, actual~ : Int, row~ : Int)
ParseNumber(token~ : String, row~ : Int, col~ : Int)
} derive(Eq, ToJson,
Debug
)

Anomaly

pub(all) struct Anomaly {
point : ThermalPoint
value : Double
z_score : Double
} derive(Eq, ToJson,
Debug
)

BoundaryPixel

pub(all) struct BoundaryPixel {
point : ThermalPoint
value : Double
normal_x : Int
normal_y : Int
} derive(Eq, ToJson,
Debug
)

Calibration

pub(all) struct Calibration {
emissivity : Double
reflected_temperature : Double
ambient_temperature : Double
gain : Double
offset : Double
} derive(Eq, ToJson,
Debug
)

Calibration::apply

fn Calibration::apply(calibration : Calibration, raw : Double) -> Double

Calibration::corrected_emission

fn Calibration::corrected_emission(calibration : Calibration, apparent : Double) -> Double

Calibration::identity

fn Calibration::identity() -> Calibration

Calibration::new

fn Calibration::new(emissivity~ : Double, reflected_temperature~ : Double, ambient_temperature~ : Double, gain? : Double, offset? : Double) -> Calibration

CalibrationReport

pub(all) struct CalibrationReport {
before : ThermalStats
after : ThermalStats
calibration : Calibration
} derive(Eq, ToJson,
Debug
)

ColorFrame

pub(all) struct ColorFrame {
width : Int
height : Int
pixels : Array[Rgb]
} derive(Eq, ToJson,
Debug
)

ColorFrame::pixel

fn ColorFrame::pixel(frame : ColorFrame, point : ThermalPoint) -> Rgb raise ThermalError

ColorFrame::to_ppm_ascii

fn ColorFrame::to_ppm_ascii(frame : ColorFrame) -> String

ColorFrame::to_ppm_ascii_wrapped

fn ColorFrame::to_ppm_ascii_wrapped(frame : ColorFrame, columns~ : Int) -> String

CsvOptions

pub(all) struct CsvOptions {
delimiter : String
decimals : Int
include_header : Bool
} derive(Eq, ToJson,
Debug
)

CsvOptions::default

fn CsvOptions::default() -> CsvOptions

FilterKind

pub(all) enum FilterKind {
Mean
Median
Minimum
Maximum
} derive(Eq, ToJson,
Debug
)

FrameDelta

pub(all) struct FrameDelta {
changed : Int
mean_absolute : Double
maximum_absolute : Double
normalized : Double
} derive(Eq, ToJson,
Debug
)

HistogramBin

pub(all) struct HistogramBin {
lower : Double
upper : Double
count : Int
} derive(Eq, ToJson,
Debug
)

Hotspot

pub(all) struct Hotspot {
point : ThermalPoint
temperature : Double
contrast : Double
} derive(Eq, ToJson,
Debug
)

InspectionReport

pub(all) struct InspectionReport {
frame_width : Int
frame_height : Int
threshold : Double
stats : ThermalStats
hotspots : Array[Hotspot]
regions : Array[ThermalRegion]
} derive(Eq, ToJson,
Debug
)

InspectionReport::to_markdown

fn InspectionReport::to_markdown(report : InspectionReport) -> String

Kernel

pub(all) struct Kernel {
width : Int
height : Int
values : Array[Double]
} derive(Eq, ToJson,
Debug
)

Kernel::box

fn Kernel::box(size~ : Int) -> Kernel raise ThermalError

Kernel::new

fn Kernel::new(width~ : Int, height~ : Int, values~ : Array[Double]) -> Kernel raise ThermalError

MotionSummary

pub(all) struct MotionSummary {
track_count : Int
active_count : Int
total_observations : Int
fastest_track : Int
hottest_track : Int
} derive(Eq, ToJson,
Debug
)

Palette

pub(all) enum Palette {
Gray
Ironbow
BlueRed
} derive(Eq, ToJson,
Debug
)

ProfileSample

pub(all) struct ProfileSample {
distance : Int
temperature : Double
} derive(Eq, ToJson,
Debug
)

QualitySummary

pub(all) struct QualitySummary {
total : Int
valid : Int
missing : Int
non_finite : Int
below_limit : Int
above_limit : Int
min_valid : Double
max_valid : Double
} derive(Eq, ToJson,
Debug
)

QualitySummary::to_markdown

fn QualitySummary::to_markdown(summary : QualitySummary) -> String

Rgb

pub(all) struct Rgb {
r : Int
g : Int
b : Int
} derive(Eq, ToJson,
Debug
)

Rgb::new

fn Rgb::new(r~ : Int, g~ : Int, b~ : Int) -> Rgb

SampleQuality

pub(all) enum SampleQuality {
Valid
Missing
NonFinite
BelowPhysicalLimit
AbovePhysicalLimit
} derive(Eq, ToJson,
Debug
)

SignalPeak

pub(all) struct SignalPeak {
index : Int
value : Double
prominence : Double
width : Int
} derive(Eq, ToJson,
Debug
)

TemperatureLimits

pub(all) struct TemperatureLimits {
minimum : Double
maximum : Double
} derive(Eq, ToJson,
Debug
)

TemperatureLimits::contains

fn TemperatureLimits::contains(limits : TemperatureLimits, value : Double) -> Bool

TemperatureLimits::new

fn TemperatureLimits::new(minimum~ : Double, maximum~ : Double) -> TemperatureLimits

TemperatureRange

pub(all) struct TemperatureRange {
min : Double
max : Double
} derive(Eq, ToJson,
Debug
)

TemperatureRange::contains

fn TemperatureRange::contains(range : TemperatureRange, value : Double) -> Bool

TemperatureRange::span

fn TemperatureRange::span(range : TemperatureRange) -> Double

ThermalMask

pub(all) struct ThermalMask {
width : Int
height : Int
cells : Array[Bool]
} derive(Eq, ToJson,
Debug
)

ThermalMask::bounding_box

fn ThermalMask::bounding_box(mask : ThermalMask) -> ThermalRegion?

ThermalMask::close

fn ThermalMask::close(mask : ThermalMask, radius? : Int) -> ThermalMask

ThermalMask::count

fn ThermalMask::count(mask : ThermalMask) -> Int

ThermalMask::dilate

fn ThermalMask::dilate(mask : ThermalMask, radius? : Int) -> ThermalMask

ThermalMask::erode

fn ThermalMask::erode(mask : ThermalMask, radius? : Int) -> ThermalMask

ThermalMask::fill_holes

fn ThermalMask::fill_holes(mask : ThermalMask) -> ThermalMask

ThermalMask::get

fn ThermalMask::get(mask : ThermalMask, point : ThermalPoint) -> Bool raise ThermalError

ThermalMask::intersection

fn ThermalMask::intersection(a : ThermalMask, b : ThermalMask) -> ThermalMask raise ThermalError

ThermalMask::invert

fn ThermalMask::invert(mask : ThermalMask) -> ThermalMask

ThermalMask::new

fn ThermalMask::new(width~ : Int, height~ : Int, cells~ : Array[Bool]) -> ThermalMask raise ThermalError

ThermalMask::open

fn ThermalMask::open(mask : ThermalMask, radius? : Int) -> ThermalMask

ThermalMask::remove_small_components

fn ThermalMask::remove_small_components(mask : ThermalMask, minimum_area~ : Int) -> ThermalMask

ThermalMask::to_ascii

fn ThermalMask::to_ascii(mask : ThermalMask, hot? : String, cold? : String) -> String

ThermalMask::to_pbm_ascii

fn ThermalMask::to_pbm_ascii(mask : ThermalMask) -> String

ThermalMask::union

ThermalMatrix

pub(all) struct ThermalMatrix {
width : Int
height : Int
values : Array[Double]
} derive(Eq, ToJson,
Debug
)

ThermalMatrix::adaptive_mask

fn ThermalMatrix::adaptive_mask(matrix : ThermalMatrix, mode : ThresholdMode) -> ThermalMask raise ThermalError

ThermalMatrix::anomalies

fn ThermalMatrix::anomalies(matrix : ThermalMatrix, z_limit? : Double) -> Array[Anomaly] raise ThermalError

ThermalMatrix::boundary

fn ThermalMatrix::boundary(matrix : ThermalMatrix, mask : ThermalMask) -> Array[BoundaryPixel] raise ThermalError

ThermalMatrix::calibrate

fn ThermalMatrix::calibrate(matrix : ThermalMatrix, calibration : Calibration) -> ThermalMatrix

ThermalMatrix::calibration_report

fn ThermalMatrix::calibration_report(matrix : ThermalMatrix, calibration : Calibration) -> CalibrationReport raise ThermalError

ThermalMatrix::colorize

fn ThermalMatrix::colorize(matrix : ThermalMatrix, palette? : Palette, range? : TemperatureRange) -> ColorFrame raise ThermalError

ThermalMatrix::colorize_lut

fn ThermalMatrix::colorize_lut(matrix : ThermalMatrix, palette? : Palette, range? : TemperatureRange) -> ColorFrame

ThermalMatrix::column_signal

fn ThermalMatrix::column_signal(matrix : ThermalMatrix, x : Int) -> Array[Double] raise ThermalError

fn ThermalMatrix::column_trends(matrix : ThermalMatrix) -> Array[Trend]

ThermalMatrix::convolve

fn ThermalMatrix::convolve(matrix : ThermalMatrix, kernel : Kernel) -> ThermalMatrix

ThermalMatrix::correct_emissivity

fn ThermalMatrix::correct_emissivity(matrix : ThermalMatrix, calibration : Calibration) -> ThermalMatrix

ThermalMatrix::crop

fn ThermalMatrix::crop(matrix : ThermalMatrix, origin~ : ThermalPoint, width~ : Int, height~ : Int) -> ThermalMatrix raise ThermalError

ThermalMatrix::delta

ThermalMatrix::detect_hotspots

fn ThermalMatrix::detect_hotspots(matrix : ThermalMatrix, min_temp~ : Double, radius? : Int, min_contrast? : Double, limit? : Int) -> Array[Hotspot]

ThermalMatrix::filter

fn ThermalMatrix::filter(matrix : ThermalMatrix, radius? : Int, kind? : FilterKind) -> ThermalMatrix

ThermalMatrix::from_rows

fn ThermalMatrix::from_rows(rows : Array[Array[Double]]) -> ThermalMatrix raise ThermalError

ThermalMatrix::get

fn ThermalMatrix::get(matrix : ThermalMatrix, point : ThermalPoint) -> Double raise ThermalError

ThermalMatrix::gradient

fn ThermalMatrix::gradient(matrix : ThermalMatrix) -> ThermalMatrix

ThermalMatrix::histogram

fn ThermalMatrix::histogram(matrix : ThermalMatrix, bins~ : Int) -> Array[HistogramBin] raise ThermalError

ThermalMatrix::hot_fraction

fn ThermalMatrix::hot_fraction(matrix : ThermalMatrix, threshold~ : Double) -> Double

ThermalMatrix::index

fn ThermalMatrix::index(matrix : ThermalMatrix, point : ThermalPoint) -> Int raise ThermalError

ThermalMatrix::inspect_threshold

fn ThermalMatrix::inspect_threshold(matrix : ThermalMatrix, threshold~ : Double, hotspot_limit? : Int) -> InspectionReport raise ThermalError

ThermalMatrix::laplacian

fn ThermalMatrix::laplacian(matrix : ThermalMatrix) -> ThermalMatrix raise ThermalError

ThermalMatrix::line_profile

fn ThermalMatrix::line_profile(matrix : ThermalMatrix, start~ : ThermalPoint, end~ : ThermalPoint) -> Array[ProfileSample] raise ThermalError

ThermalMatrix::map

fn ThermalMatrix::map(matrix : ThermalMatrix, f : (Double) -> Double) -> ThermalMatrix

ThermalMatrix::masked_values

fn ThermalMatrix::masked_values(matrix : ThermalMatrix, mask : ThermalMask) -> Array[Double] raise ThermalError

ThermalMatrix::mean_filter

fn ThermalMatrix::mean_filter(matrix : ThermalMatrix, radius? : Int) -> ThermalMatrix

ThermalMatrix::median_filter

fn ThermalMatrix::median_filter(matrix : ThermalMatrix, radius? : Int) -> ThermalMatrix

ThermalMatrix::new

fn ThermalMatrix::new(width~ : Int, height~ : Int, values~ : Array[Double]) -> ThermalMatrix raise ThermalError

ThermalMatrix::peak_map

fn ThermalMatrix::peak_map(matrix : ThermalMatrix, radius? : Int) -> ThermalMask

ThermalMatrix::quality

fn ThermalMatrix::quality(matrix : ThermalMatrix, limits : TemperatureLimits) -> QualitySummary

ThermalMatrix::range

ThermalMatrix::region_adjacency

fn ThermalMatrix::region_adjacency(matrix : ThermalMatrix, regions : Array[ThermalRegion]) -> Array[Array[Int]]

ThermalMatrix::rescale

fn ThermalMatrix::rescale(matrix : ThermalMatrix, width~ : Int, height~ : Int) -> ThermalMatrix raise ThermalError

ThermalMatrix::roi

ThermalMatrix::roi_column_profile

fn ThermalMatrix::roi_column_profile(matrix : ThermalMatrix, roi : ThermalRoi) -> Array[Double] raise ThermalError

ThermalMatrix::roi_mean_profile

fn ThermalMatrix::roi_mean_profile(matrix : ThermalMatrix, roi : ThermalRoi) -> Array[Double] raise ThermalError

ThermalMatrix::roi_stats

fn ThermalMatrix::roi_stats(matrix : ThermalMatrix, roi : ThermalRoi) -> ThermalStats raise ThermalError

ThermalMatrix::row

fn ThermalMatrix::row(matrix : ThermalMatrix, y : Int) -> Array[Double] raise ThermalError

ThermalMatrix::row_signal

fn ThermalMatrix::row_signal(matrix : ThermalMatrix, y : Int) -> Array[Double] raise ThermalError

fn ThermalMatrix::row_trends(matrix : ThermalMatrix) -> Array[Trend] raise ThermalError

ThermalMatrix::sharpen

fn ThermalMatrix::sharpen(matrix : ThermalMatrix, amount? : Double) -> ThermalMatrix raise ThermalError

ThermalMatrix::stats

ThermalMatrix::threshold_mask

fn ThermalMatrix::threshold_mask(matrix : ThermalMatrix, min_temp~ : Double, max_temp? : Double) -> ThermalMask

ThermalMatrix::threshold_regions

fn ThermalMatrix::threshold_regions(matrix : ThermalMatrix, min_temp~ : Double, max_temp? : Double) -> Array[ThermalRegion]

ThermalMatrix::threshold_value

fn ThermalMatrix::threshold_value(matrix : ThermalMatrix, mode : ThresholdMode) -> Double raise ThermalError

ThermalMatrix::to_csv

fn ThermalMatrix::to_csv(matrix : ThermalMatrix) -> String

ThermalMatrix::to_csv_with

fn ThermalMatrix::to_csv_with(matrix : ThermalMatrix, options : CsvOptions) -> String

ThermalMatrix::track_frame_sequence

fn ThermalMatrix::track_frame_sequence(frames : Array[ThermalMatrix], threshold~ : Double, config? : TrackingConfig) -> Array[ThermalTrack]

ThermalMatrix::unsafe_get

fn ThermalMatrix::unsafe_get(matrix : ThermalMatrix, x~ : Int, y~ : Int) -> Double

ThermalMatrix::valid_mask

fn ThermalMatrix::valid_mask(matrix : ThermalMatrix, limits : TemperatureLimits) -> ThermalMask

ThermalMatrix::weighted_mean

fn ThermalMatrix::weighted_mean(matrix : ThermalMatrix, weights : ThermalMatrix) -> Double raise ThermalError

ThermalPoint

pub(all) struct ThermalPoint {
x : Int
y : Int
} derive(Eq, ToJson,
Debug
)

ThermalPoint::chebyshev

fn ThermalPoint::chebyshev(a : ThermalPoint, b : ThermalPoint) -> Int

ThermalPoint::manhattan

fn ThermalPoint::manhattan(a : ThermalPoint, b : ThermalPoint) -> Int

ThermalPoint::neighbors

fn ThermalPoint::neighbors(point : ThermalPoint, connectivity? : Int) -> Array[ThermalPoint]

ThermalPoint::new

fn ThermalPoint::new(x~ : Int, y~ : Int) -> ThermalPoint

ThermalPoint::translate

fn ThermalPoint::translate(point : ThermalPoint, dx~ : Int, dy~ : Int) -> ThermalPoint

ThermalRegion

pub(all) struct ThermalRegion {
id : Int
pixels : Array[ThermalPoint]
min_x : Int
min_y : Int
max_x : Int
max_y : Int
min_temp : Double
max_temp : Double
average_temp : Double
peak : ThermalPoint
} derive(Eq, ToJson,
Debug
)

ThermalRegion::area

fn ThermalRegion::area(region : ThermalRegion) -> Int

ThermalRegion::centroid

fn ThermalRegion::centroid(region : ThermalRegion) -> ThermalPoint

ThermalRegion::compactness

fn ThermalRegion::compactness(region : ThermalRegion) -> Double

ThermalRegion::perimeter

fn ThermalRegion::perimeter(region : ThermalRegion) -> Int

ThermalRegion::summary

fn ThermalRegion::summary(region : ThermalRegion) -> String

ThermalRoi

pub(all) struct ThermalRoi {
origin : ThermalPoint
width : Int
height : Int
} derive(Eq, ToJson,
Debug
)

ThermalRoi::area

fn ThermalRoi::area(roi : ThermalRoi) -> Int

ThermalRoi::contains

fn ThermalRoi::contains(roi : ThermalRoi, point : ThermalPoint) -> Bool

ThermalRoi::end

ThermalRoi::intersection

fn ThermalRoi::intersection(a : ThermalRoi, b : ThermalRoi) -> ThermalRoi?

ThermalRoi::new

fn ThermalRoi::new(origin~ : ThermalPoint, width~ : Int, height~ : Int) -> ThermalRoi raise ThermalError

ThermalRoi::union

ThermalStats

pub(all) struct ThermalStats {
count : Int
min : Double
max : Double
mean : Double
stddev : Double
p50 : Double
p90 : Double
p95 : Double
} derive(Eq, ToJson,
Debug
)

ThermalTrack

pub(all) struct ThermalTrack {
id : Int
observations : Array[TrackObservation]
active : Bool
} derive(Eq, ToJson,
Debug
)

ThermalTrack::last

ThermalTrack::length

fn ThermalTrack::length(track : ThermalTrack) -> Int

ThermalTrack::peak

ThermalTrack::to_markdown

fn ThermalTrack::to_markdown(track : ThermalTrack) -> String

ThermalTrack::velocity

fn ThermalTrack::velocity(track : ThermalTrack) -> ThermalPoint

ThresholdMode

pub(all) enum ThresholdMode {
Absolute(Double)
MeanOffset(Double)
MedianOffset(Double)
Percentile(Double)
} derive(Eq, ToJson,
Debug
)

TrackObservation

pub(all) struct TrackObservation {
frame : Int
point : ThermalPoint
temperature : Double
area : Int
} derive(Eq, ToJson,
Debug
)

TrackingConfig

pub(all) struct TrackingConfig {
maximum_distance : Int
maximum_gap : Int
minimum_temperature : Double
} derive(Eq, ToJson,
Debug
)

TrackingConfig::default

Trend

pub(all) struct Trend {
slope : Double
intercept : Double
first : Double
last : Double
direction : Int
} derive(Eq, ToJson,
Debug
)

autocorrelation

fn autocorrelation(values : Array[Double], lag~ : Int) -> Double

auxiliary_lut_resolution

fn auxiliary_lut_resolution() -> Int

blue_red_lut

fn blue_red_lut() -> Array[Rgb]

classify_temperature

fn classify_temperature(value : Double, limits : TemperatureLimits) -> SampleQuality

confidence_lut

fn confidence_lut() -> Array[Double]

confidence_weight

fn confidence_weight(value : Double) -> Double

derivative

fn derivative(values : Array[Double]) -> Array[Double]

edge_response

fn edge_response(value : Double) -> Double

edge_response_lut

fn edge_response_lut() -> Array[Double]

find_peaks

fn find_peaks(values : Array[Double], minimum_prominence? : Double, minimum_distance? : Int) -> Array[SignalPeak]

gamma_correct

fn gamma_correct(value : Double) -> Double

gamma_lut

fn gamma_lut() -> Array[Double]

gray_lut

fn gray_lut() -> Array[Rgb]

integrate

fn integrate(values : Array[Double], step? : Double) -> Double

ironbow_lut

fn ironbow_lut() -> Array[Rgb]

lut_resolution

fn lut_resolution() -> Int

normalized_temperature_lut

fn normalized_temperature_lut() -> Array[Double]

parse_temperature_matrix

fn parse_temperature_matrix(text : String) -> ThermalMatrix raise ThermalError

quantization_lut

fn quantization_lut() -> Array[Int]

quantize_temperature

fn quantize_temperature(value : Double, minimum~ : Double, maximum~ : Double) -> Int

second_derivative

fn second_derivative(values : Array[Double]) -> Array[Double]

smooth_signal

fn smooth_signal(values : Array[Double], radius? : Int) -> Array[Double]

summarize_tracks

fn summarize_tracks(tracks : Array[ThermalTrack]) -> MotionSummary

track_regions

fn track_regions(frames : Array[Array[ThermalRegion]], config? : TrackingConfig) -> Array[ThermalTrack]

trend

fn trend(values : Array[Double]) -> Trend