LWIR-Align¶
Registration pipeline for aligning High-Altitude (HA) LWIR frames to Low-Altitude (LA) mosaic imagery. This is the geometric backbone of the MFSR project -- without accurate frame-to-mosaic homographies, no training data can be generated.
What It Does¶
LWIR-Align takes 675 HA thermal frames (1024x768, 16-bit, captured at ~1218m ASL from flight 21052) and registers each one to two LA mosaics (flight 21051, ~800m ASL) via 3x3 homography matrices. The output is a per-frame H_<timestamp>_to_mosaic{A|B}.json file that can warp the HA frame into the mosaic's coordinate space.
The pipeline handles the full registration lifecycle:
- Undistortion -- Correct Jenoptik lens distortion using camera calibration parameters extracted from Metashape XML (
scripts/batch_undistort_filtered_ha.py,scripts/undistort_image.py) - Group organization -- 675 frames divided into 8 temporal groups (group1: 85 frames, group2: 144, group3: 103, group4: 96, group5: 65, group6: 4, group7: 11, group8: 22)
- Initial guess generation -- Compose pairwise HA-to-HA homographies with a manually established anchor to get coarse frame-to-mosaic transforms
- Refinement -- Use SuperGlue or RAFT to refine the coarse homographies into sub-pixel accurate alignments
- Dual-mosaic registration -- Register each frame to both Mosaic A and Mosaic B where coverage overlaps, linked by
mosaicB_to_mosaicA_homography.json - Tile metadata generation -- Produce comprehensive metadata for downstream tools like LWIR Tile Validator
Source: lwir-align/README.md, lwir-align/CLAUDE.md
Registration Methods¶
All methods implement AbstractRegistrationMethod (src/methods/base.py) with a common interface: register(reference, template) -> H returning a 3x3 homography.
SuperGlue (src/methods/superglue.py)¶
Primary method. Uses SuperPoint keypoint detection + SuperGlue learned matching. The refinement script (scripts/refine_group_to_mosaic.py) projects HA frame corners into mosaic space, creates a bounding box (expanded 10%), and filters SuperGlue matches to lie within that region. This spatial filtering is critical because the mosaic is far larger than any single HA frame.
Key challenge: SuperGlue fails on group 3 due to 180-degree rotation and LWIR's low contrast. The solution was pre-warping HA images with the initial guess before running SuperGlue, which gave the matcher a fighting chance (commit 380cb35: "superglue is no longer failing now that we are prewarping the images with the initial guess").
RAFT (src/methods/raft.py)¶
Optical flow method used as both a fallback and a refinement stage. Two variants:
- Downsampled RAFT -- Resize both images and compute dense flow
- Tiled RAFT (
scripts/refine_with_raft_tiled.py) -- Divide the overlap region into tiles, compute flow per tile, fit a homography from the tile displacements. This was found to be slightly better than downsampled RAFT (commit6884a4c: "a tiled raft approach seems to work slightly better than a downsampled raft approach").
RAFT was the breakthrough for group 3 (commit 6bad62c: "RAFT is working!!!").
Deep Homography (src/methods/deep_homography.py)¶
Neural homography regression. Implemented but less prominent in the actual processing pipeline than SuperGlue and RAFT.
Other Methods¶
Also implemented: LoFTR (src/methods/loftr.py), UDHN (src/methods/udhn.py), DGC-Net (src/methods/dgc.py), CoTR (src/methods/cotr.py). These appear to be comparison/evaluation implementations rather than production methods.
Source: lwir-align/src/methods/, git log
Metrics¶
Defined in src/metrics/alignment_metrics.py:
| Metric | Implementation | Purpose |
|---|---|---|
| NMI (Normalized Mutual Information) | Joint histogram with 32 bins, entropy-based | Robust to intensity differences between HA and LA |
| NCC (Normalized Cross Correlation) | Zero-mean, normalized dot product | Measures linear relationship between pixel intensities |
| SSIM (Structural Similarity Index) | Via skimage.metrics.structural_similarity |
Perceptual quality of alignment |
| L1 (Normalized L1 Loss) | Mean absolute difference, normalized | Simple pixel-level error |
| Combined | Weighted combination | Composite score for ranking |
All metrics support optional validity masks to exclude black/invalid regions from the computation. The tiled RAFT refinement improved SSIM by over 1.2% compared to standard SuperGlue refinement alone.
Source: lwir-align/src/metrics/alignment_metrics.py
Group-Based Processing Structure¶
The 675 HA frames are organized into 8 temporal groups based on flight path segments. Each group has its own subdirectory under flight_21052/filtered_enhanced_undistorted/:
group{N}/
initial_guess_to_mosaic/ # Coarse homographies from anchor composition
refined_to_mosaic/ # SuperGlue-refined homographies
refined_to_mosaic_raft_tiled/ # RAFT-refined (best quality)
The master orchestration script (scripts/master_process_all_groups.py) automates the multi-stage pipeline:
- Detect missing files/directories
- Generate initial guess homographies where needed
- Pass 1: Refine to best mosaic (SuperGlue)
- Pass 2: Cross-mosaic registration (dual coverage)
- Generate analysis and visualization
Group 3 required special handling -- it had a 180-degree rotation relative to the other groups, causing SuperGlue to fail entirely. The solution involved manual corrections (group3/*_corrected.json) and RAFT-based recovery (new_registration_plan/group3_tiled_raft_recovery_plan.md).
Source: lwir-align/scripts/master_process_all_groups.py, lwir-align/CLAUDE.md
Results Across Groups 1-8¶
| Group | Frames | Registration Method | Notes |
|---|---|---|---|
| 1 | 85 | SuperGlue + RAFT tiled | Standard processing |
| 2 | 144 | SuperGlue + RAFT tiled | Largest group |
| 3 | 103 | Manual anchor + RAFT | 180-degree rotation; SuperGlue failed; required manual corrections |
| 4 | 96 | SuperGlue + RAFT tiled | Standard processing |
| 5 | 65 | SuperGlue + RAFT tiled | Standard processing |
| 6 | 4 | SuperGlue | Very small group |
| 7 | 11 | SuperGlue | Small group |
| 8 | 22 | SuperGlue | Group 8 also had SuperGlue difficulties (commit b37bffd: "group 8 refuses to register with superglue") |
Coverage analysis (scripts/analyze_coverage.py) found that with a minimum of 8 overlapping frames per patch, 294 viable training tiles could be generated.
Source: lwir-align/new_registration_plan/coverage_analysis_and_next_steps.md, git log
Tile Metadata Generation¶
The script scripts/generate_comprehensive_tile_metadata.py is the critical bridge to downstream tools. It:
- Generates a regular tile grid over each mosaic (384x384 or 192x192 pixels, with 32-pixel overlap)
- Uses Shapely spatial queries (STRtree) to find which HA frame footprints overlap each tile
- Produces 4 metadata JSON files:
tile_metadata_mosaic{A|B}_{384x384|192x192}.json - Each tile record includes: tile ID, position, coverage ratios, list of contributing HA images with their homography paths
These metadata files are consumed directly by LWIR Tile Validator for visual QC and by the ProbaV dataset exporter for training data generation.
Source: lwir-align/scripts/generate_comprehensive_tile_metadata.py
Relationship to Downstream Tools¶
To LWIR Tile Validator¶
The tile validator reads lwir-align's output as read-only data:
- Mosaic GeoTIFFs from flight_21051/
- HA PNGs from flight_21052/filtered_enhanced_undistorted/group{1-8}/
- Comprehensive tile metadata from comprehensive_tile_metadata/
- Homography matrices from group{N}/refined_to_mosaic/
To PIUnet Training Data¶
The MFSR dataset creation plan (new_registration_plan/mfsr_dataset_creation_plan.md) describes a three-phase workflow:
1. Geometric indexing -- Generate footprints.json from all refined homographies
2. Tiling strategy -- Determine optimal tile placement based on coverage density
3. Patch extraction -- Extract ProbaV-format training data (HR mosaic patch + multiple LR HA patches per tile)
The recommended refinement chain for best quality: Initial Guess -> SuperGlue Refinement -> Tiled RAFT Refinement.
Interactive Registration Tool¶
When automated methods failed (especially group 3), a custom OpenGL-based registration tool was built (registration_tool/) to replace GIMP for manual matrix adjustment (commit 6b5d0b8: "started work on this registration tool with openGL support because the transforms I was getting from gimp were not right"). This tool later evolved into the Hephaestus project.
Struggle Log¶
SuperGlue Fails on 180-Degree Rotation (Group 3)¶
- Hypothesis: SuperGlue's learned matching would handle arbitrary orientations in LWIR
- Failure Mode: Zero or near-zero valid matches for all group 3 frames
- Root Cause: LWIR imagery lacks the rich texture that SuperGlue was trained on; combined with 180-degree rotation, the matcher had no signal
- Resolution: Pre-warp with initial guess + fall back to RAFT for dense flow
- Anti-Pattern: Do not assume learned feature matchers will generalize to extreme rotations in low-contrast thermal imagery
GIMP Transforms Were Inaccurate¶
- Hypothesis: GIMP's interactive warping could provide usable homography matrices
- Failure Mode: The transforms from GIMP were not geometrically correct for the pipeline
- Root Cause: GIMP's internal transform representation doesn't map cleanly to a standard 3x3 homography
- Resolution: Built custom OpenGL registration tool with direct matrix control
- Anti-Pattern: Do not use general-purpose image editors for precision geometric registration
Key Files¶
| Path | Purpose |
|---|---|
src/methods/base.py |
Abstract registration interface |
src/methods/superglue.py |
SuperGlue implementation |
src/methods/raft.py |
RAFT optical flow implementation |
src/metrics/alignment_metrics.py |
NMI, NCC, SSIM, L1 metrics |
scripts/master_process_all_groups.py |
Master orchestration script |
scripts/refine_group_to_mosaic.py |
SuperGlue refinement with spatial filtering |
scripts/refine_with_raft_tiled.py |
Tiled RAFT refinement (best quality) |
scripts/generate_comprehensive_tile_metadata.py |
Tile metadata for downstream tools |
scripts/generate_footprints.py |
Frame footprint index |
scripts/analyze_coverage.py |
Coverage density analysis |
project_paths.py |
Centralized path configuration |
new_registration_plan/mfsr_dataset_creation_plan.md |
Dataset generation plan |
Environment¶
- Conda environment:
superglue-env(primary),geo_env(geospatial),lwir-registration(GUI tool) - Last active: October 2025
- Location:
/home/geoff/projects/ceres/superrez/lwir-align/