Tile Stitching for Multi-Tile Datasets
Seamlessly combine multiple LiDAR tiles into unified training datasets with automatic neighbor detection and consistency management.
π― What is Tile Stitching?β
Tile stitching enables processing multiple adjacent LiDAR tiles as a single cohesive dataset, automatically handling:
- Neighbor tile detection
- Coordinate system consistency
- Cross-tile patch sampling
- Metadata unification
- Dataset-level statistics
π Quick Startβ
Basic Stitchingβ
# Process multiple tiles as unified dataset
ign-lidar-hd process \
input_dir=data/raw/ \
output_dir=output/ \
stitching.enabled=true
With Boundary-Aware Featuresβ
# Combine stitching with boundary-aware processing
ign-lidar-hd process \
input_dir=data/raw/ \
output_dir=output/ \
stitching.enabled=true \
features.boundary_aware=true \
features.buffer_size=5.0
π How It Worksβ
1. Tile Discoveryβ
2. Neighbor Detectionβ
The system automatically detects tile relationships:
Tile Grid (1000m tiles):
βββββββ¬ββββββ¬ββββββ
β A-1 β A-2 β A-3 β Tile naming: X_Y coordinates
βββββββΌββββββΌββββββ€
β B-1 β B-2 β B-3 β Example: 1234_5678.laz
βββββββΌββββββΌββββββ€
β C-1 β C-2 β C-3 β
βββββββ΄ββββββ΄ββββββ
Each tile knows its 8 potential neighbors:
N, S, E, W, NE, NW, SE, SW
3. Unified Processingβ
# Unified dataset creation
1. Load all tiles in directory
2. Compute global statistics (mean, std, etc.)
3. Apply consistent normalization
4. Generate patches respecting tile boundaries
5. Create unified metadata
βοΈ Configurationβ
Key Parametersβ
Parameter | Type | Default | Description |
---|---|---|---|
stitching.enabled | bool | false | Enable tile stitching |
stitching.tile_size | float | 1000 | Expected tile size in meters |
stitching.overlap_tolerance | float | 10 | Allowed overlap in meters |
stitching.min_points | int | 1000 | Minimum points per tile |
stitching.neighbor_search | string | auto | Neighbor detection method |
Neighbor Search Methodsβ
# Automatic detection (recommended)
stitching.neighbor_search=auto
# Grid-based (faster, assumes regular grid)
stitching.neighbor_search=grid
# Distance-based (flexible, handles irregular layouts)
stitching.neighbor_search=distance
π Output Structureβ
Without Stitching (Default)β
output/
βββ patches/
β βββ tile_1234_5678/
β β βββ patch_0000.npy
β β βββ patch_0001.npy
β β βββ ...
β βββ tile_1234_5679/
β β βββ patch_0000.npy
β β βββ ...
β βββ ...
βββ metadata/
βββ tile_1234_5678.json
βββ tile_1234_5679.json
With Stitching (Unified)β
output/
βββ patches/
β βββ patch_0000.npy β All tiles combined
β βββ patch_0001.npy
β βββ ...
β βββ patch_9999.npy
βββ metadata.json β Unified metadata
βββ tile_index.json β Tileβpatch mapping
βββ stitching_info.json β Neighbor relationships
π― Use Casesβ
1. Regional Datasetsβ
Process an entire region as one dataset:
# Download Paris city center
ign-lidar-hd download \
bbox="2.3,48.85,2.4,48.9" \
output_dir=data/paris/
# Process as unified dataset
ign-lidar-hd process \
input_dir=data/paris/ \
output_dir=output/paris_dataset/ \
stitching.enabled=true \
features=full
2. Building-Spanning Datasetsβ
Handle buildings that span multiple tiles:
# Large building dataset
ign-lidar-hd process \
input_dir=data/buildings/ \
output_dir=output/buildings_dataset/ \
stitching.enabled=true \
features.boundary_aware=true \
target_class=building
3. Continuous Landscapesβ
Process forests, coastlines, or other continuous features:
# Forest landscape
ign-lidar-hd process \
input_dir=data/forest/ \
output_dir=output/forest_dataset/ \
stitching.enabled=true \
features.boundary_aware=true \
target_class=vegetation
π§ Advanced Usageβ
Python APIβ
from ign_lidar.core import TileStitcher
from ign_lidar.io import TileManager
# Initialize stitcher
stitcher = TileStitcher(
tile_size=1000.0,
overlap_tolerance=10.0,
neighbor_search="auto"
)
# Load and analyze tiles
tile_manager = TileManager(input_dir="data/raw/")
tiles = tile_manager.load_all()
# Build tile grid
grid = stitcher.build_grid(tiles)
print(f"Grid: {grid.shape}, {len(tiles)} tiles")
# Get neighbors for a tile
tile = tiles[0]
neighbors = stitcher.get_neighbors(tile, grid)
print(f"Tile {tile.name} has {len(neighbors)} neighbors")
# Process with stitching
from ign_lidar.core import LiDARProcessor
processor = LiDARProcessor(
stitching_enabled=True,
boundary_aware=True
)
dataset = processor.process(tiles)
Custom Tile Layoutsβ
Handle non-standard tile arrangements:
from ign_lidar.core import TileStitcher
# Define custom tile positions
tile_positions = {
"tile_A": (0, 0),
"tile_B": (1000, 0),
"tile_C": (0, 1000),
"tile_D": (1200, 500) # Irregular position
}
# Create stitcher with custom layout
stitcher = TileStitcher(
neighbor_search="distance",
max_neighbor_distance=1500
)
grid = stitcher.build_grid_from_positions(tile_positions)
π Performance Considerationsβ
Memory Usageβ
Memory = n_tiles Γ tile_memory + stitching_overhead
Example (10 tiles, 100MB each):
Without stitching: 100 MB (1 tile at a time)
With stitching: 1,200 MB (all tiles + overhead)
Processing Timeβ
Mode | Time | Memory | Output Quality |
---|---|---|---|
No stitching | 1.0x | Low | Per-tile |
Stitching only | 1.1x | Medium | Unified |
+ Boundary-aware | 1.3x | High | Best |
Optimization Strategiesβ
# 1. Process in batches
stitching.batch_size=5 # Process 5 tiles at a time
# 2. Reduce buffer for boundary-aware
features.buffer_size=3.0 # Smaller buffer = less memory
# 3. Use grid-based neighbor search
stitching.neighbor_search=grid # Faster for regular grids
# 4. Filter small tiles
stitching.min_points=5000 # Skip tiles with <5k points
π Stitching Metadataβ
tile_index.jsonβ
Maps patches back to source tiles:
{
"patch_0000.npy": {
"source_tiles": ["tile_1234_5678"],
"bbox": [2.3, 48.85, 2.301, 48.851],
"num_points": 2048
},
"patch_0001.npy": {
"source_tiles": ["tile_1234_5678", "tile_1234_5679"],
"bbox": [2.3005, 48.85, 2.3015, 48.851],
"num_points": 2048,
"crosses_boundary": true
}
}
stitching_info.jsonβ
Documents tile relationships:
{
"tiles": {
"tile_1234_5678": {
"neighbors": {
"north": "tile_1234_5679",
"east": "tile_1235_5678",
"northeast": "tile_1235_5679"
},
"position": [1234000, 5678000],
"num_points": 1234567
}
},
"grid": {
"shape": [3, 3],
"tile_size": 1000.0,
"total_tiles": 9
}
}
β Best Practicesβ
Data Preparationβ
- Consistent naming: Use standard IGN naming (X_Y.laz)
- Complete coverage: Ensure no missing tiles in region
- Same CRS: All tiles must use same coordinate system
- Same format: Consistent LAZ version and point format
Configurationβ
# Recommended for most cases
ign-lidar-hd process \
input_dir=data/ \
output_dir=output/ \
stitching.enabled=true \
stitching.neighbor_search=auto \
features.boundary_aware=true \
features.buffer_size=5.0
Quality Checksβ
# Verify stitching quality
import json
# Load stitching info
with open("output/stitching_info.json") as f:
info = json.load(f)
# Check tile connectivity
for tile, data in info["tiles"].items():
n_neighbors = len(data["neighbors"])
print(f"{tile}: {n_neighbors} neighbors")
# Expected: Interior tiles have 8, edge tiles have 3-5
π Troubleshootingβ
Missing Neighborsβ
# Error: Expected neighbor not found
Solution: Check tile naming and coverage
# List tiles
ls -la data/raw/*.laz
# Expected pattern: tile_XXXX_YYYY.laz
Memory Errorsβ
# Out of memory error
Solution: Process in batches
stitching.batch_size=3 # Smaller batches
Coordinate Mismatchesβ
# Error: Tiles don't align
Solution: Verify CRS consistency
# Check CRS for all tiles
for f in data/raw/*.laz; do
pdal info $f | grep "srs"
done
Slow Processingβ
# Use grid-based search for regular layouts
stitching.neighbor_search=grid
# Or disable boundary-aware processing
features.boundary_aware=false
π Complete Exampleβ
Regional Building Classificationβ
# 1. Download region
ign-lidar-hd download \
bbox="2.35,48.86,2.37,48.88" \
output_dir=data/le_marais/
# 2. Process with full stitching
ign-lidar-hd process \
input_dir=data/le_marais/ \
output_dir=output/le_marais_dataset/ \
stitching.enabled=true \
stitching.neighbor_search=auto \
features.boundary_aware=true \
features.buffer_size=5.0 \
features=full \
target_class=building \
preprocess=aggressive
# 3. Verify output
ls -lh output/le_marais_dataset/patches/ | wc -l
cat output/le_marais_dataset/stitching_info.json | jq '.grid'
# 4. Train model on unified dataset
python train.py \
--data output/le_marais_dataset/ \
--architecture pointnet++ \
--epochs 100
π Related Topicsβ
- Boundary-Aware Processing - Cross-tile feature quality
- Configuration System - Advanced configuration
- Complete Workflows - End-to-end examples
Next Steps:
- Try Multi-Architecture Support
- Explore Python API
- Read Performance Optimization