SAM Label Correction
build-sam-corrected-masks uses the Segment Anything Model (SAM, AMG mode) to correct noisy
segmentation masks produced from cartographic sources. Instead of training with hard or soft labels
derived from imperfect map databases, this tool replaces them with spatially coherent labels
obtained by majority vote within each SAM segment.
How it works
- For each tile in the coreset CSV, the tool loads the original GeoTIFF mask and optional auxiliary LULC rasters (e.g. MapBiomas, ESRI, Dynamic World).
- SAM AMG generates segments for the RGB image chip read from an MBTiles file.
- For each SAM segment that contains at least one pixel from the target class set, a majority vote is computed across all sources (original mask + LULC rasters).
- The winning class label is written to every pixel in the segment.
- Pixels belonging to non-target classes are never modified.
- SAM masks are processed ascending by
(predicted_iou, area)so high-confidence, larger segments overwrite smaller ones in overlapping regions.
An NPZ cache can be configured to store SAM segments on disk, avoiding redundant SAM inference across multiple experiments with different target class sets.
Installation
SAM is not a dependency of the framework itself. Install it separately:
pip install git+https://github.com/facebookresearch/segment-anything.git
Download a SAM checkpoint (ViT-B recommended for speed):
wget https://dl.fbaipublicfiles.com/segment_anything/sam_vit_b_01ec64.pth
CLI usage
pytorch-smt-tools build-sam-corrected-masks path/to/config.yaml
Configuration
# Required
coreset_csv: /data/coreset.csv # CSV with mask_path, row_off, col_off, patch_size columns
masks_dir: /data/masks # Directory containing original GeoTIFF masks
sam_checkpoint: /models/sam_vit_b_01ec64.pth
mbtiles_path: /data/imagery/tiles.mbtiles
# One or more output targets (different class sets)
targets:
- classes: [3, 5] # grassland + cropland
output_dir: /data/masks_sam_gc
- classes: [1, 3, 5] # forest + grassland + cropland
output_dir: /data/masks_sam_gcf
- classes: [0, 1, 2, 3, 4, 5] # all classes
output_dir: /data/masks_sam_all
# Optional auxiliary LULC rasters included in the majority vote
lulc_paths:
- /data/lulc/mapbiomas.vrt
- /data/lulc/esri.vrt
- /data/lulc/dynamic_world.vrt
include_bags: true # include original mask as one vote source (default: true)
# SAM AMG parameters
sam_model_type: vit_b
device: cuda:0
points_per_side: 32
pred_iou_thresh: 0.80
stability_score_thresh: 0.90
min_mask_region_area: 200
# Processing
num_classes: 6
nodata_val: 255
chunk_size: 1024 # tile processing chunk size in pixels
# NPZ segment cache (set to "" to disable)
cache_dir: /data/sam_cache
# Multi-GPU splits (process tiles [start_idx, end_idx) on each GPU)
start_idx: 0
end_idx: 999999
Multi-GPU parallelism
Run multiple processes with non-overlapping start_idx/end_idx slices and point each to
a shared cache_dir. The first process to process a chunk writes the cache; subsequent
processes with overlapping class sets load from cache instead of running SAM again.
# GPU 0 — tiles 0..9999
pytorch-smt-tools build-sam-corrected-masks config_gpu0.yaml &
# GPU 1 — tiles 10000..19999
pytorch-smt-tools build-sam-corrected-masks config_gpu1.yaml &
Output
For each target, a copy of every tile is written to output_dir with SAM-corrected pixels. The
original files in masks_dir are never modified.
The run() method returns a summary dict:
{
"n_tiles": 1234,
"elapsed_s": 5432.1,
"tiles": [
{
"tile": "tile_001.tif",
"n_chunks": 4,
"n_skipped": 1,
"per_target": [
{
"output_dir": "/data/masks_sam_gc",
"classes": [3, 5],
"n_target": 89432,
"n_changed": 12340,
"pct_changed": 13.8
}
]
}
]
}
Python API
from pytorch_segmentation_models_trainer.tools.sam_correction import (
SAMLabelCorrectionConfig,
SamLabelCorrector,
apply_sam_correction,
)
config = SAMLabelCorrectionConfig(
coreset_csv="/data/coreset.csv",
masks_dir="/data/masks",
targets=[{"classes": [3, 5], "output_dir": "/data/out"}],
sam_checkpoint="/models/sam_vit_b_01ec64.pth",
mbtiles_path="/data/tiles.mbtiles",
lulc_paths=["/data/lulc/mapbiomas.vrt"],
cache_dir="/data/sam_cache",
)
stats = SamLabelCorrector(config).run()
print(f"Processed {stats['n_tiles']} tiles in {stats['elapsed_s']}s")
The apply_sam_correction function is also available as a pure, dependency-free utility
(no SAM or rasterio needed) for unit testing or custom integration:
import numpy as np
from pytorch_segmentation_models_trainer.tools.sam_correction import apply_sam_correction
corrected = apply_sam_correction(
bags_raw=original_mask, # (H, W) uint8
sam_masks=sam_output, # list of SAM mask dicts
lulc_maps=[lulc_array], # list of (H, W) uint8 arrays
classes_to_correct=frozenset([3, 5]),
num_classes=6,
include_bags=True,
)