Skip to content

Repository files navigation

FIRE-TRACE

FIRE-TRACE is a Python workflow for automatic wildfire perimeter extraction from airborne LWIR and NIR imagery. The workflow uses an LWIR image and the NIR band from an RGBN image to compute the Normalized Burn Thermal Ratio (NBTR), a spectral index developed for this methodology that combines thermal and near-infrared information. It then automatically selects the appropriate processing mode and writes one georeferenced perimeter shapefile per input case.

FIRE-TRACE has two processing modes: burning, used when the wildfire is active, and burned, used when the wildfire is no longer active. The same script determines which type of case is being processed and applies the corresponding workflow automatically.

Both processing modes are based on deterministic image-processing algorithms. The core perimeter-detection step is based on a modified Canny edge-detection procedure, adapted to work with normalized airborne wildfire imagery. In the original Canny algorithm, two thresholds (vmin and vmax) are required to classify image-gradient pixels as strong, weak, or irrelevant edges. FIRE-TRACE keeps this idea, but applies the thresholds to the gradient magnitude. Instead of using fixed threshold values, FIRE-TRACE defines vmin and vmax as percentiles of the gradient magnitude distribution of each image. This makes the method more adaptive to differences between wildfire scenes, sensors, and image-normalization conditions.

The repository supports two independent workflows:

  1. Extraction with calibrated parameters: use an existing calibrated configuration (vmin and vmax) to generate perimeter shapefiles.
  2. Coarse calibration: test different percentile-based combinations of vmin and vmax, compute the evaluation metrics, and select the best configuration for the cases available in Input/.

To run the calibration workflow, a wildfire dataset must be available in Input/. Each input case must include the corresponding LWIR image, NIR image, and ground-truth perimeter, all referring to the same acquisition date and time. Without this reference perimeter, the calibration script cannot compute the evaluation metrics or select the best parameter configuration.

This repository implements a coarse calibration stage, which tests a broad set of percentile values to identify the general region where good configurations are found. After reviewing the coarse calibration outputs, users can optionally perform two additional finer searches: refinement and micro-adjustment. Refinement consists of testing a smaller set of values around the best coarse configuration. Micro-adjustment consists of testing an even narrower and higher-resolution set of values around the best refinement configuration. These finer searches are not automated here because the appropriate local search ranges depend on the dataset, sensor, image-normalization workflow, and visual quality of the coarse results.

Repository structure

fire-trace/
├── Input/
│   ├── CasteloBranco_12092025/
│   └── Montalegre_26092025/
├── Output/
├── Results/
├── Calibration/
├── config/
│   └── best_parameters.json
├── scripts/
│   ├── run_extraction.py
│   └── calibrate.py
└── src/fire_trace/

Input/ contains one subfolder per wildfire case. If a wildfire has more than one perimeter, each perimeter should be placed in a separate case folder inside Input/. Output/ is used by the extraction script. Calibration/ is used by the calibration script to store per-configuration outputs, metrics, rankings, and the best calibrated parameters obtained from the available cases.

There is only one README, this file.

Method overview

For each input case, FIRE-TRACE performs the following steps:

  1. Loads the LWIR image and the NIR band from the RGBN image.
  2. Aligns both products on the LWIR grid and builds a valid-data mask.
  3. Computes NBTR from LWIR and NIR.
  4. Selects the processing mode (burning or burned) from the proportion of high-value LWIR pixels.
  5. Crops the region of interest.
  6. Applies Canny edge detection using calibrated percentile thresholds.
  7. Adds additional NBTR-based interior points inside a convex hull.
  8. Reduces point density and reconstructs the final perimeter using an alpha shape.

Source code organization

The main implementation is located in src/fire_trace/. The command-line scripts in scripts/ are thin entry points; most of the algorithm logic is implemented in src/fire_trace/.

File Purpose
config.py Defines fixed method settings, processing-mode thresholds, and coarse calibration search ranges. It does not contain the final best parameters, because those are calibration outputs.
io_handler.py Loads LWIR and RGBN input rasters, extracts the NIR band, aligns the data, builds the valid-data mask, and writes geospatial outputs.
data_processing.py Contains image-processing utilities such as processing-mode selection, NBTR computation, region-of-interest cropping, trimming near invalid areas, mask-to-point conversion, and point-density reduction.
finding_edges.py Computes image gradients and applies Canny edge detection using percentile-based thresholds.
fire_extraction.py Contains the main burning and burned perimeter-extraction workflows. It combines preprocessing, edge detection, convex-hull interior-point addition, point reduction, and final polygon reconstruction.
shape_reconstruction.py Builds convex hulls and alpha-shape polygons from georeferenced points.
evaluation.py Computes the calibration/evaluation metrics by comparing extracted perimeters with the ground-truth perimeter.
calibration.py Runs the coarse calibration grid search, evaluates each parameter candidate, ranks configurations, and writes the selected best parameters.

Input format

Each case must be a direct subfolder inside Input/. The code is generic: every valid case folder under Input/ is processed automatically.

Example case structure:

Input/CasteloBranco_12092025/
├── LWIR_QuickMosaic_Hotspot_Highlight_1522.tiff
├── RGBN_QuickMosaic_8-bit_Linear_1522.tiff
├── perimeter.shp
├── perimeter.shx
├── perimeter.dbf
├── perimeter.prj
└── perimeter.cpg

The image filenames must start with:

  • LWIR for the thermal image.
  • RGBN for the multispectral RGBN image. The RGBN image is expected to contain the NIR band as band 4. If only a single-band NIR image is available instead of an RGBN image, the input-reading step must be adapted in src/fire_trace/io_handler.py, specifically in the load_data() function, so that the script reads the NIR file directly instead of extracting band 4 from the RGBN raster.

If the single-band NIR file is named using a different prefix, such as NIR*.tif, the input-discovery step in src/fire_trace/pipeline.py should also be updated, specifically in the find_case_inputs() function, so that the workflow searches for the NIR file instead of an RGBN*.tif file.

Both images must be normalized to the range [0, 255]. For calibration and metric computation, each case must also include the reference perimeter as perimeter.shp with its auxiliary shapefile files. For extraction-only runs, the reference perimeter is optional. If extraction is run without a new calibration, the default calibrated configuration will be used, but it may not be suitable for every dataset, sensor, or image-normalization workflow.

Data assumptions and limitations

The method assumes that LWIR and RGBN images correspond to the same acquisition time and cover the same fire situation. Images should be normalized to [0, 255]. The method was designed for well-defined wildfire perimeters; highly fragmented fires, partial fire coverage, strong data gaps, or cases with large internal unburned islands may require additional preprocessing or parameter adjustment.

Geospatial requirements

Input rasters and the reference perimeter must use compatible coordinate reference systems. The output perimeter is written using the LWIR image CRS. If the reference perimeter is used for calibration, it should be in the same CRS as the raster data or be readable and reprojectable by the geospatial libraries used by the workflow.

Installation

Using conda:

conda env create -f environment.yml
conda activate fire-trace
pip install -e .

Using a Python virtual environment:

python -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
pip install -e .

On Windows, activate the virtual environment with:

.venv\Scripts\activate

Workflow A: extract perimeters with calibrated parameters

Use this when the parameter configuration is already known and you only want to generate perimeter shapefiles.

python scripts/run_extraction.py --input Input --output Output

By default, the script reads:

config/best_parameters.json

This file is outside src/ because it is not part of the algorithm implementation. It is an example calibrated configuration. If you recalibrate the method with another dataset, the best parameters can be different. best_parameters.json should be understood as a calibrated result, not as a universal setting. Users should generate a new one when applying the method to a different sensor, flight setup, or normalization workflow.

To use another calibrated parameter file:

python scripts/run_extraction.py --input Input --output Output --parameters path/to/parameters.json

The output for each case is written to:

Output/<case_name>/perimeter.shp

For example:

Output/CasteloBranco_12092025/perimeter.shp
Output/Montalegre_26092025/perimeter.shp

If more case folders are added under Input/, the same command processes them as well.

Workflow B: run coarse calibration

Use this when you want to evaluate coarse percentile combinations on the cases available in Input/ and select the best configuration from those cases.

This repository implements the coarse calibration stage. The refinement and micro-adjustment stages described in the method are not executed automatically here; they should be defined after inspecting the coarse results.

python scripts/calibrate.py --input Input --calibration-dir Calibration --mode both

The calibration script:

  1. Reads all valid case folders in Input/.
  2. Classifies each case as burning or burned.
  3. Runs only the percentile combinations for the corresponding processing mode.
  4. Extracts a perimeter for each tested configuration.
  5. Compares the extracted perimeter with the ground truth perimeter.shp.
  6. Writes per-case metrics, ranked configurations, and a best-parameter JSON file.

Main outputs:

Calibration/burning/<configuration_name>/metrics.txt
Calibration/burned/<configuration_name>/metrics.txt
Calibration/burning/summary_best_configuration.txt
Calibration/burned/summary_best_configuration.txt
Calibration/calibration_summary.json
Calibration/best_parameters.json

candidate_id is the sequential identifier of a tested parameter candidate during calibration. It is only used to make the calibration ranking easier to trace.

configuration_name is the folder-safe name generated from the tested percentile values. Its generic format is:

<input1>_vmin<value>_vmax<value>__<input2>_vmin<value>_vmax<value>

For example, in burning mode, where both LWIR and NBTR are used, a configuration name can look like:

lwir_vmin10_vmax90__nbtr_vmin10_vmax90

In burned mode, where only NBTR is used, a configuration name can look like:

nbtr_vmin35_vmax99.99

Calibration/best_parameters.json can be passed directly to the extraction script:

python scripts/run_extraction.py --input Input --output Output --parameters Calibration/best_parameters.json

To calibrate only one processing mode:

python scripts/calibrate.py --input Input --calibration-dir Calibration --mode burning
python scripts/calibrate.py --input Input --calibration-dir Calibration --mode burned

Outputs

Extraction produces one shapefile per case:

Output/<case_name>/perimeter.shp

Calibration produces:

  • extracted perimeters for each tested configuration,
  • per-case metric files,
  • ranking summaries for each processing mode,
  • Calibration/calibration_summary.json,
  • Calibration/best_parameters.json.

Burning example: Montalegre, 26/09/2025 Burned example: Castelo Branco, 12/09/2025

Processing-mode rule

The workflow automatically decides whether a case is burning or burned using the LWIR image. A case is classified as burning if more than 0.01% of valid LWIR pixels have a value greater than 100. In the code this is expressed as a fraction: 0.0001. Otherwise, the case is classified as burned.

These constants are defined in:

src/fire_trace/config.py

NBTR

The code uses the name nbtr consistently for the Normalized Burn Thermal Ratio:

NBTR = (LWIR - NIR) / (LWIR + NIR + 1e-6)

The small value 1e-6 is added to the denominator to avoid division by zero when LWIR + NIR is zero or very close to zero. After computing NBTR, the image is smoothed with a Gaussian filter and then normalized to [0, 255] using the minimum and maximum NBTR values of that image. This allows NBTR to be processed on the same scale as the input imagery.

Calibrated percentile parameters

The extraction script uses these percentile values by default through config/best_parameters.json:

Processing mode Input image vmin percentile vmax percentile
burning LWIR 10 93.50
burning NBTR 90 99.992
burned NBTR 35 99.990

The parameter file only contains the values that come from calibration:

{
  "burning": {
    "lwir": {"vmin_percentile": 10, "vmax_percentile": 93.5},
    "nbtr": {"vmin_percentile": 90, "vmax_percentile": 99.992}
  },
  "burned": {
    "nbtr": {"vmin_percentile": 35, "vmax_percentile": 99.99}
  }
}

Fixed parameters used by every configuration

Fixed method parameters are separated from calibrated percentile parameters. They are defined in src/fire_trace/config.py and are applied internally to every tested or extracted configuration.

Parameter Burning mode Burned mode Meaning
gamma 4 4 Gamma correction applied before edge detection.
ksize 7 7 Shared Sobel kernel size and Canny aperture size.
NBTR sigma 2 3 Gaussian smoothing applied to NBTR before edge detection.
ROI padding 250 20 Margin added around the detected region of interest.
trimming distance 10 10 Distance used to remove edge pixels near invalid image areas.
hull std factor 0.25 1.0 Factor used in the convex-hull NBTR interior-point criterion.
point reduction distance 2 2 Minimum spacing used when reducing point density.
alpha shape value 60 60 Alpha value used for final concave perimeter reconstruction.

The same ksize value controls both the Sobel gradient kernel and the Canny aperture size.

Coarse calibration search ranges

The coarse calibration grid is defined in src/fire_trace/config.py. Only vmin_percentile and vmax_percentile are searched.

For burning, both LWIR and NBTR are tested with:

vmin_percentile = [10, 20, 30, 40, 50, 60, 70, 80, 90]
vmax_percentile = [90, 95, 97, 98, 99, 99.5, 99.8, 99.9]

For burned, NBTR is tested with:

vmin_percentile = [10, 20, 30, 40, 50, 60, 70, 80, 90]
vmax_percentile = [80, 90, 95, 97, 98, 99, 99.5, 99.8, 99.9]

Only combinations where vmin_percentile < vmax_percentile are evaluated.

Metrics used for calibration

Only the four metrics used by the method are computed:

  • HDnorm: normalized Hausdorff Distance similarity.
  • ASSDnorm: normalized Average Symmetric Surface Distance similarity.
  • F2: spatial F2-score.
  • SC: Sørensen-Dice coefficient.

For each configuration, the script computes the mean value of these four metrics across the valid cases of the corresponding processing mode. The winning configuration is selected using the radar-area score built from mean HDnorm, mean ASSDnorm, mean F2, and mean SC.

Typical commands

Generate perimeters using the provided calibrated configuration:

python scripts/run_extraction.py --input Input --output Output

Run coarse calibration on all available cases:

python scripts/calibrate.py --input Input --calibration-dir Calibration --mode both

Generate perimeters using the best parameters obtained from a new calibration:

python scripts/run_extraction.py --input Input --output Output --parameters Calibration/best_parameters.json

Troubleshooting

  • If no output is produced, check that each case folder contains one LWIR*.tif or LWIR*.tiff file and one RGBN*.tif or RGBN*.tiff file.
  • If calibration skips a case, check that perimeter.shp and its auxiliary files are present.
  • If geospatial errors occur, check that rasters and shapefiles have valid CRS information.
  • If the extracted perimeter is poor, run calibration on representative cases from the same sensor and normalization workflow.

Example airborne images

The example RGBN and LWIR airborne images included in this repository (located in the Input/ directory) are ANEPC airborne images. They are included with authorization from ANEPC for the purpose of allowing users to test the code on real example images. Please identify these files as ANEPC airborne images when referring to them.

Citation

If you use this code, please cite the associated paper. Citation details will be added after the review process.

Funding

Funding information will be added after the review process.

License

This repository is distributed under the GNU General Public License v3.0. See LICENSE for details.

About

FIRE-TRACE is a Python workflow for automatic wildfire perimeter extraction from airborne LWIR and NIR imagery.

Topics

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages