Next-day fire-probability mapping and multi-hour fire-spread simulation at 30 m resolution, built on real NASA FIRMS VIIRS detections, a Random Forest classifier, and a vectorised Cellular Automata engine.
Auto-generated dashboard: probability map, binary map, spread snapshots and feature importances in one figure.
Wildfires in the Kumaon and Garhwal Himalaya region of Uttarakhand cause recurring ecological and economic damage. This project implements a six-stage pipeline, orchestrated end-to-end by main.py, that:
- Rasterises real VIIRS NOAA-20 / Suomi-NPP active-fire detections onto a 30 m grid
- Generates a physically-informed synthetic terrain and weather dataset aligned to the real fire-occurrence dates
- Trains a balanced Random Forest classifier on an 11-channel per-pixel feature stack
- Predicts next-day fire probability across the full study tile
- Simulates hourly fire spread with a vectorised Cellular Automata model driven by wind, slope, fuel and moisture
- Exports every result as georeferenced GeoTIFF plus publication-ready PNGs and an animated GIF
The study area is a 512 Γ 512 pixel tile (β15.36 km Γ 15.36 km) in UTM Zone 44N (EPSG:32644).
| Feature | Detail |
|---|---|
| π°οΈ Real fire data | NASA FIRMS VIIRS archive rasterised directly into training labels |
| πΊοΈ Georeferenced outputs | All rasters written as GeoTIFF in EPSG:32644, readable in QGIS/ArcGIS/GDAL |
| π§ 11-channel feature stack | Elevation, slope, aspect (sin/cos), temperature, humidity, wind (speed + sin/cos), rainfall, LULC |
| π³ Balanced Random Forest | class_weight='balanced', OOB-validated, single-process for Windows memory safety |
| π₯ Cellular Automata spread | 8-neighbourhood, fully vectorised with numpy.roll, no per-pixel Python loops |
| π¨ 5-class risk visualisation | Discrete probability-to-risk colour ramp across all output maps |
| π¬ Animated + dashboard exports | One-command GIF and 6-panel summary figure |
| βοΈ Config-driven | Every geographic, model and CA parameter lives in a single config.py |
flowchart TD
A["Stage 1 - data_generator.py: Real VIIRS dates + synthetic terrain/weather"] --> B["Stage 2 - preprocessing.py: 11-channel normalised feature stack (.npy)"]
B --> C["Stage 3 - sklearn_model.py train(): Balanced Random Forest -> rf_model.pkl"]
C --> D["Stage 4 - sklearn_model.py predict(): fire_prob_nextday.tif + fire_binary.tif"]
D --> E["Stage 5 - cellular_automata.py: CA spread seeded from high-risk pixels"]
E --> F["Stage 6 - visualization.py: PNGs + animated GIF + dashboard"]
Each stage is an independent, callable function (stage_data_gen, stage_preprocess, stage_train, stage_predict, stage_simulate, stage_visualise in main.py), so any stage can be re-run in isolation via CLI flags β see Quick Start.
| Stage | Module | Produces |
|---|---|---|
| 1 | src/data_generator.py |
Per-day synthetic DEM/weather GeoTIFFs + VIIRS-derived fire labels |
| 2 | src/preprocessing.py |
11-channel normalised .npy feature stacks, chronological train/val split |
| 3 | src/sklearn_model.py |
models/rf_model.pkl (trained Random Forest) |
| 4 | src/sklearn_model.py |
fire_prob_nextday.tif, fire_binary_nextday.tif |
| 5 | src/cellular_automata.py |
spread_XXh.tif snapshots (1/2/3/6/12 h) |
| 6 | src/visualization.py |
PNG maps, GIF animation, feature-importance chart, dashboard |
The images below are generated artefacts committed to
outputs/in this repository β re-run the pipeline to regenerate them for your own data.
![]() Next-day fire probability (5-class risk) |
![]() Binary fire / no-fire classification |
![]() Spread simulation β Hour 3 |
![]() Spread simulation β Hour 12 |
Quantitative metrics (validation ROC-AUC, OOB score, per-class precision/recall) are printed to console during stage_train and stored in models/history.npy β run the pipeline and check your own console output/history.npy for current numbers, since these depend on the VIIRS data window used.
forest-fire-detection/
βββ main.py # CLI entry point β orchestrates all 6 stages
βββ config.py # Single source of truth: paths, grid, RF/CA params
βββ generate_arch_diagram.py # Regenerates outputs/architecture.png
βββ requirements.txt
β
βββ src/
β βββ data_generator.py # Stage 1 β synthetic terrain/weather + VIIRS rasterisation
β βββ preprocessing.py # Stage 2 β feature stacking & normalisation
β βββ sklearn_model.py # Stage 3β4 β ACTIVE model: RF train() + predict()
β βββ cellular_automata.py # Stage 5 β vectorised fire-spread engine
β βββ visualization.py # Stage 6 β maps, GIF, dashboard
β βββ unet.py / train.py / predict.py / dataset.py
β # Reference PyTorch U-Net implementation
β # (defined for future use, not called by main.py)
β
βββ outputs/
β βββ architecture.png
β βββ dashboard.png
β βββ feature_importance.png
β βββ prediction_maps/
β βββ spread_maps/
β βββ animations/
β
βββ .github/workflows/ # CI: pylint.yml, bandit.yml
data/, models/, and the raw VIIRS CSV are created/populated at runtime under the paths defined in config.py (DATA_DIR, SYNTHETIC_DIR, PROCESSED_DIR, MODEL_DIR) and are not committed to the repository.
Prerequisites: Python 3.10+, β₯8 GB RAM recommended. No GPU is required β the active model is a CPU-trained Random Forest.
git clone https://github.com/Jaideep193/forest-fire-detection.git
cd forest-fire-detection
python -m venv venv
venv\Scripts\activate # Windows
source venv/bin/activate # Linux / macOS
pip install -r requirements.txtPlace your NASA FIRMS VIIRS CSV in the project root at the path referenced by config.RAW_FIRE_CSV.
| Package | Version | Role |
|---|---|---|
| numpy | β₯1.24.0 | Array ops, vectorised CA |
| scipy | β₯1.10.0 | Terrain/noise generation |
| scikit-learn | β₯1.3.0 | RandomForestClassifier |
| rasterio | β₯1.3.0 | GeoTIFF I/O |
| matplotlib | β₯3.7.0 | Static plots & animation |
| Pillow | β₯9.0.0 | GIF export |
| pandas | β₯2.0.0 | VIIRS CSV parsing |
| tqdm | β₯4.65.0 | Progress bars |
| torch / torchvision | β₯2.0.0 / β₯0.15.0 | Reference U-Net code path (not used by the active pipeline) |
All commands below map directly to the argparse options defined in main.py.
# Run the full 6-stage pipeline
python main.py
# Generate a custom number of synthetic days
python main.py --days 90
# Skip stages that are already complete
python main.py --skip data_gen preprocess
# Jump straight to one stage (loads prerequisite artefacts from disk)
python main.py --only train
python main.py --only predict
python main.py --only simulate
python main.py --only visualise| Group | Key parameters |
|---|---|
STUDY_AREA |
Bounding box: 29.481β29.619Β°N, 79.470β79.630Β°E |
GEO_CONFIG |
EPSG:32644, 30 m pixels, 512Γ512 grid |
FEATURE_NAMES |
11 channels: elevation, slope, aspect (sin/cos), temperature, humidity, wind speed, wind direction (sin/cos), rainfall, LULC |
FUEL_WEIGHTS |
Per-LULC-class fuel weight, from 0.0 (water/snow) to 0.95 (dense forest) |
CA_CONFIG |
Ignition threshold, seed fraction, wind/slope/moisture weights, target hours [1, 2, 3, 6, 12], steps per simulated hour |
src/sklearn_model.py trains a sklearn.ensemble.RandomForestClassifier on a per-day sampled, class-balanced pixel dataset (all fire pixels retained, no-fire pixels undersampled) built by build_dataset().
Key configuration, read directly from the training call:
n_estimators=100max_features='sqrt'class_weight='balanced'β compensates for fire pixels typically being a small minority of the tileoob_score=Trueβ out-of-bag validation without a held-out setn_jobs=1β intentionally single-process to avoid Windows shared-memory/paging errors during trainingrandom_stateβ fixed seed for reproducibility
At inference, predict() flattens the latest 11-channel feature stack, calls predict_proba(), reshapes back to the 512Γ512 grid, and writes both the continuous probability GeoTIFF and a thresholded binary GeoTIFF (default threshold 0.55, configurable via CA_CONFIG['fire_prob_threshold']).
Why Random Forest instead of the reference U-Net? The repository also ships a complete PyTorch U-Net implementation (
src/unet.py,train.py,predict.py,dataset.py) which is fully defined but not invoked bymain.py. The active pipeline uses scikit-learn to avoid Windows virtual-memory/shared-memory failures encountered when training the U-Net.
src/cellular_automata.py implements an 8-neighbourhood spread model that is fully vectorised with numpy.roll β there is no per-pixel Python loop.
Cell states: 0 = unburned, 1 = burning, 2 = burned.
Per step, for each of the 8 neighbour directions, ignition probability into a candidate (unburned) cell is a product of:
- the target cell's fuel weight (from LULC class),
- a wind-alignment factor, derived from the angle between the local wind destination direction and the spread direction, scaled by wind speed,
- a slope factor, computed from the elevation drop between source and target (uphill spread is favoured), clipped to a sane multiplier range,
- a moisture-suppression factor, which reduces spread probability as local humidity increases.
Each step, every currently-burning cell transitions to burned, and every candidate cell whose stochastic ignition draw succeeds becomes burning β so the fire front advances outward one 8-connected ring per CA step. The simulation runs CA_CONFIG['ca_steps_per_hour'] steps per simulated hour and writes a GeoTIFF snapshot at each configured target hour ([1, 2, 3, 6, 12] by default). Initial ignition points are sampled from the highest-probability pixels of the Stage 4 prediction map.
See src/cellular_automata.py for the exact implementation.
All rasters share the same spatial reference: EPSG:32644 (WGS 84 / UTM Zone 44N), 30 m Γ 30 m pixels, 512 Γ 512 grid.
| File | Dtype | Description |
|---|---|---|
fire_prob_nextday.tif |
float32 | Per-pixel fire probability, [0.0, 1.0] |
fire_binary_nextday.tif |
uint8 | Binary fire/no-fire at the configured threshold |
spread_01h.tif β¦ spread_12h.tif |
uint8 | CA state (0/1/2) at each snapshot hour |
Verify any output with GDAL:
gdalinfo outputs/prediction_maps/fire_prob_nextday.tifThis repository runs automated checks on every push via GitHub Actions:
pylint.ymlβ static code-quality lintingbandit.ymlβ static security analysis for common Python vulnerabilities
No LICENSE file is currently included in this repository. Until one is added, default copyright applies and reuse terms are not formally defined β consider adding an OSI-approved license (e.g., MIT or Apache-2.0) if you intend this project to be reused by others. The VIIRS fire data itself is sourced from NASA FIRMS and is subject to NASA's open-data policy.






