Skip to main content

LULC Input Dataset

LulcInputDataset, LulcInputWindowedDataset, and MBTilesLulcInputMaskWindowedDataset feed multi-source LULC classification rasters as extra input channels alongside the RGB image.

Each LULC raster is one-hot encoded and concatenated with the RGB bands, producing a (3 + N × C, H, W) tensor where N is the number of LULC sources and C is num_classes.

Motivation​

LULC products (MapBiomas, Dynamic World, ESRI LULC) provide complementary land-cover signals at different resolutions and with different biases. Rather than picking one, this dataset lets the model learn which source to trust per-pixel by exposing all of them as extra input channels.

Output format​

KeyShapedtypeDescription
"image"(3 + N*C, H, W)float32RGB (normalised) + one-hot LULC blocks
"mask"(H, W)int64Hard integer class labels (training target)
"path"scalarstrPath of the source image file

The LULC blocks follow the lulc_keys order: channels [3, 3+C) for the first source, [3+C, 3+2C) for the second, and so on.

Pixels where the LULC value equals ignore_lulc_index (default 255) or falls outside [0, num_classes) produce all-zero one-hot vectors — no class signal, no gradient.

CSV format​

Tile-based (LulcInputDataset)​

Each row is one pre-cut tile:

image_path,mask_path,mapbiomas_path,esri_path,dw_path
/data/tiles/img_001.tif,/data/tiles/mask_001.tif,/data/mb/001.tif,/data/esri/001.tif,/data/dw/001.tif

Windowed (LulcInputWindowedDataset)​

Each row is a patch specification over full-scene rasters:

image_path,mask_path,mapbiomas_path,esri_path,dw_path,row_off,col_off,patch_size
/data/scenes/img.tif,/data/scenes/mask.tif,...,0,0,256
/data/scenes/img.tif,/data/scenes/mask.tif,...,256,256,256

The same scene file can appear in multiple rows (one row per patch).

MBTiles + mask windows (MBTilesLulcInputMaskWindowedDataset)​

Use this variant when RGB is stored in an MBTiles source and the mask GeoTIFFs define the training grid. The window CSV follows the same format accepted by MBTilesMaskWindowedDataset:

mask_path,row_off,col_off,patch_size
/data/masks/76.tif,4,4,256

LULC sources are passed as raster/VRT paths in the YAML, not as CSV columns.

Configuration​

Tile-based​

train_dataset:
_target_: pytorch_segmentation_models_trainer.dataset_loader.lulc_input_dataset.LulcInputDataset
input_csv_path: /data/splits/train.csv
image_key: image_path
mask_key: mask_path
lulc_keys:
- mapbiomas_path
- esri_path
- dw_path
num_classes: 6
ignore_lulc_index: 255
augmentation_list:
- _target_: albumentations.HorizontalFlip
p: 0.5
- _target_: albumentations.VerticalFlip
p: 0.5
- _target_: albumentations.RandomRotate90
p: 0.5
- _target_: albumentations.Normalize
mean: [0.485, 0.456, 0.406]
std: [0.229, 0.224, 0.225]
- _target_: albumentations.pytorch.ToTensorV2
data_loader:
batch_size: 16
num_workers: 8
shuffle: true
pin_memory: true

Windowed​

train_dataset:
_target_: pytorch_segmentation_models_trainer.dataset_loader.lulc_input_dataset.LulcInputWindowedDataset
input_csv_path: /data/splits/train_windows.csv
image_key: image_path
mask_key: mask_path
lulc_keys:
- mapbiomas_path
- esri_path
- dw_path
num_classes: 6
row_off_key: row_off
col_off_key: col_off
patch_size_key: patch_size
augmentation_list:
- _target_: albumentations.HorizontalFlip
p: 0.5
- _target_: albumentations.Normalize
mean: [0.485, 0.456, 0.406]
std: [0.229, 0.224, 0.225]
- _target_: albumentations.pytorch.ToTensorV2
data_loader:
batch_size: 8
num_workers: 8
shuffle: true
pin_memory: true

MBTiles + LULC Windowed​

train_dataset:
_target_: pytorch_segmentation_models_trainer.dataset_loader.lulc_input_dataset.MBTilesLulcInputMaskWindowedDataset
mbtiles_path: /data/tiles.mbtiles
mask_dir: /data/masks
window_index_cache: /data/coreset_windows.csv
patch_size: 256
selected_bands: [1, 2, 3]
lulc_paths:
- /data/mapbiomas_edgv_2_5m.vrt
- /data/esri_edgv_2_5m.vrt
- /data/dynamic_world_edgv_2_5m.vrt
num_classes: 6
ignore_lulc_index: 255
lulc_resampling: nearest
augmentation_list:
- _target_: albumentations.HorizontalFlip
p: 0.5
- _target_: albumentations.Normalize
mean: [0.485, 0.456, 0.406]
std: [0.229, 0.224, 0.225]
- _target_: albumentations.pytorch.ToTensorV2

Parameters​

Shared (LulcInputDataset)​

ParameterTypeDefaultDescription
input_csv_pathstrNonePath to the CSV file
lulc_keyslist[str][]CSV columns for LULC raster paths
num_classesint6Number of LULC classes (same for all sources)
ignore_lulc_indexint255Value treated as "no valid class"
image_keystr"image_path"CSV column for RGB image paths
mask_keystr"mask_path"CSV column for training target mask
root_dirstrNonePrepended to relative CSV paths
augmentation_listlist[]Albumentations transform list

Extra (LulcInputWindowedDataset)​

ParameterTypeDefaultDescription
row_off_keystr"row_off"CSV column for patch top-left row offset
col_off_keystr"col_off"CSV column for patch top-left col offset
patch_size_keystr"patch_size"CSV column for patch size (square)
mask_class_mappingDict[int, int] | NoneNoneRemaps mask classes on the fly ({5: 3} merges 5 into 3), before augmentations. Also accepted by MBTilesLulcInputMaskWindowedDataset. See Mask Class Mapping.

Extra (MBTilesLulcInputMaskWindowedDataset)​

ParameterTypeDefaultDescription
mbtiles_pathstrrequiredRGB MBTiles or rasterio-readable source
lulc_pathslist[str]requiredLULC raster/VRT paths, one source per entry
window_index_cachestrNoneCSV/Parquet with mask_path,row_off,col_off,width,height or patch_size
lulc_resamplingstr"nearest"Resampling for categorical LULC sources

Python API​

from pytorch_segmentation_models_trainer.dataset_loader.lulc_input_dataset import (
LulcInputDataset,
LulcInputWindowedDataset,
)
import pandas as pd

# Tile-based
ds = LulcInputDataset(
input_csv_path="/data/splits/train.csv",
lulc_keys=["mapbiomas_path", "esri_path", "dw_path"],
num_classes=6,
)
sample = ds[0]
print(sample["image"].shape) # torch.Size([21, H, W]) — 3 + 3*6
print(sample["mask"].shape) # torch.Size([H, W])

# Windowed — from a DataFrame
import pandas as pd
df = pd.DataFrame([
{"image_path": "/data/scene.tif", "mask_path": "/data/mask.tif",
"mapbiomas_path": "/data/mb.tif", "esri_path": "/data/esri.tif",
"dw_path": "/data/dw.tif",
"row_off": 0, "col_off": 0, "patch_size": 256},
])
ds_win = LulcInputWindowedDataset(df=df, lulc_keys=["mapbiomas_path", "esri_path", "dw_path"])

Adding a new LULC source​

Add the raster path column to the CSV and append the column name to lulc_keys:

lulc_keys:
- mapbiomas_path
- esri_path
- dw_path
- new_source_path # new source — no other changes required

Output channels increase by num_classes automatically.

Augmentation notes​

  • Geometric transforms (flip, rotate, …) are applied consistently to the RGB image, the training mask, and all LULC rasters via albumentations additional_targets.
  • Normalize in the pipeline affects only the image target; the one-hot LULC channels remain in {0, 1} after augmentation.
  • ToTensorV2 handles the image; LULC tensors are built from one-hot encoded numpy arrays after augmentation.