LunarSight is a modular pipeline for lunar feature detection using YOLO and/or Detectron2. It processes LROC NAC images with SPICE geolocation, converts annotations to COCO format, trains object detection models, and runs inference.
# Install the package (includes all dependencies)
pip install .
# Run the full pipeline (uses ./data directory by default)
lunarsight run 1-5Note: No configuration file required. The pipeline uses sensible defaults with ./data as the base directory.
Complete setup checklist for team members:
# 1. Clone and install
git clone https://github.com/ansidian/LunarSight.git
cd LunarSight
pip install .
# 2. Install system dependencies (GDAL) - see Prerequisites section below
# 3. Verify installation
lunarsight --help
# 4. Run tests to confirm everything works (~60 seconds)
python tests/test_data/download.py # Download test data
pytest # Should show: 395 passed, 2 skippedFor full pipeline demo, you'll also need:
- LROC NAC imagery (
.IMG+.xmlfiles) — uselunarsight acquireto download, or place manually indata/source_images/ - SPICE kernels in
data/kernels/ - Annotation tool: LabelImg for local annotation, or Roboflow for cloud-based annotation (see Roboflow Workflow)
- ML frameworks:
pip install ultralytics(YOLO) orpip install torch detectron2(Detectron2)
- Python 3.11+
- GDAL - For GeoTIFF processing
# macOS brew install gdal # Ubuntu/Debian sudo apt-get install gdal-bin libgdal-dev # Windows: use OSGeo4W installer (recommended) # Download from https://trac.osgeo.org/osgeo4w/ # Or with conda: conda install -c conda-forge gdal
- SPICE kernels - For lunar geolocation (placed in
data/kernels/)
git clone https://github.com/ansidian/LunarSight.git
cd LunarSight
pip install .The installed CLI command is lunarsight; the Python API uses
lunar_pipeline package name.
For development (editable install):
pip install -e .All core dependencies (including imaging libraries like rasterio and Pillow, SPICE support, and test tools) are automatically installed from pyproject.toml.
Training requires framework-specific packages:
# For YOLO training (Phase 3)
pip install ultralytics
# For Detectron2 training (Phase 4)
pip install torch torchvision
pip install 'git+https://github.com/facebookresearch/detectron2.git'Execute all 5 phases in sequence:
lunarsight run 1-5Run specific phases using the run command:
# Phase 1: Image processing
lunarsight run imaging
# Phase 2: Annotation conversion
lunarsight run annotation
# Phase 3: YOLO training
lunarsight run yolo
# Phase 4: Detectron2 training
lunarsight run d2
# Phase 5: Inference
lunarsight run inferenceYou can run a subset of training phases:
# YOLO-only workflow (phases 1-3, skip Detectron2)
lunarsight run 1-3
# Detectron2-only workflow (phases 1,2,4, skip YOLO)
lunarsight run 1,2,4
# Run inference with whichever model(s) were trained
lunarsight run 5This is useful when:
- You only need one model type for your use case
- You want faster iteration during development
- Your hardware doesn't support one of the frameworks
The run command supports multiple selection patterns:
# Run a single phase by number
lunarsight run 3
# Run by name or alias
lunarsight run yolo
lunarsight run train-yolo
# Run a range of phases
lunarsight run 1-3
# Run specific phases (comma-separated)
lunarsight run 2,4,5
lunarsight run ann,yolo,inf
# Interactive menu
lunarsight runPhase aliases:
- Phase 0:
acquire,download - Phase 1:
imaging,img - Phase 2:
annotation,ann - Phase 3:
train-yolo,yolo - Phase 4:
train-detectron2,d2 - Phase 5:
inference,inf
Control log verbosity with CLI flags:
# Verbose mode (DEBUG level)
lunarsight --verbose run 1-5
# Quiet mode (WARNING level only)
lunarsight --quiet run 1-5
# Default: INFO level
lunarsight run 1-5The pipeline automatically detects GPU availability:
- GPU available: Training uses CUDA for acceleration
- No GPU: Training falls back to CPU automatically
No configuration needed. Both YOLO and Detectron2 will use the best available device.
Note: CPU training is significantly slower but works on any machine. For large datasets, GPU is recommended.
The pipeline uses sensible defaults with ./data as the base directory:
| Setting | Default | Description |
|---|---|---|
| Base directory | ./data |
All data paths relative to this |
| Source images | ./data/source_images/ |
LROC NAC .IMG files |
| SPICE kernels | ./data/kernels/ |
Planetary ephemeris data |
| Annotations | ./data/annotated_images/ |
XML annotations from LabelImg |
| Output tiles | ./data/raw_tiles/ |
Generated PNG tiles |
| Training runs | ./data/runs/ |
Model weights and metrics |
| Tile metadata | ./data/tile_metadata/ |
Geolocation JSON files |
Model defaults: 3 classes (crater, pit, rock), 1024px tiles, 80/20 train/test split, 30 YOLO epochs, 300 Detectron2 iterations.
Just place your data in ./data and run — no config file needed.
For Roboflow integration, create a .env file:
# Copy the example file
cp .env.example .env
# Edit with your credentials
ROBOFLOW_API_KEY=your_roboflow_api_key
ROBOFLOW_PROJECT=lunarsightNote: The .env file is gitignored and never committed. Only needed if using the push/pull commands.
| Phase | Name | Description | Input | Output |
|---|---|---|---|---|
| 0 | acquire | Download LROC NAC images from PDS | Volume selection | .IMG + .xml files in structured dirs |
| 1 | imaging | Process LROC NAC images, create tiles | .IMG files, SPICE kernels | PNG tiles, geolocation JSON |
| 2 | annotation | Convert XML to COCO, train/test split | XML annotations, tiles | COCO JSON (train/test), split datasets |
| 3 | train-yolo | Train YOLOv11 model | COCO dataset | best.pt weights, metrics.json |
| 4 | train-detectron2 | Train Faster R-CNN (Detectron2) | COCO dataset | model_final.pth, metrics.json |
| 5 | inference | Run detection on new images | Trained weights, images | Predictions, visualizations |
Phase 0 - Data Acquisition:
- Interactive CLI to browse and download LROC NAC images from the PDS archive
- Supports volume selection (LROLRC_0020 and higher)
- Downloads .IMG files with matching PDS4 .xml labels
- Outputs to structured directories:
source_images/{volume_id}/{esm}/NAC/ - Tracks existing downloads to avoid duplicates
# Interactive volume selection
lunarsight acquire
# List available volumes
lunarsight acquire --list-volumesPhase 1 - Imaging:
- Reads LROC NAC
.IMGfiles using SPICE for geolocation - Generates tiles at configured size (default: 1024×1024)
- Outputs PNG tiles and geolocation metadata (lat/lon/altitude per tile)
Phase 2 - Annotation:
- Converts PASCAL VOC XML annotations (from LabelImg) to COCO format
- Also converts COCO to YOLO format for Phase 3 training
- Performs train/test split (default: 80/20)
- Validates COCO schema and file references
- Reports class distribution per split
Note: After Phase 1 generates tiles, annotate craters, pits, and rocks using either:
- LabelImg — local annotation, save as PASCAL VOC XML to
annotated_images/- Roboflow — cloud-based annotation via
push/pullcommands (see Roboflow Workflow)
Phase 3 - Train YOLO:
- Trains YOLOv11 on COCO dataset
- Saves best model weights to
runs/yolo/train/weights/best.pt - Generates training metrics: mAP@50, mAP@50:95, precision, recall
- Saves metrics to
metrics.jsonalongside weights
Phase 4 - Train Detectron2:
- Trains Faster R-CNN with ResNet-50 backbone
- Saves final model to
runs/detectron2/model_final.pth - Generates evaluation metrics on test set
- Saves metrics to
metrics.json
Phase 5 - Inference:
- Auto-discovers trained weights from runs directory
- Runs both YOLO and Detectron2 models (if available)
- Batch processing for large datasets (configurable batch_size, default 16)
- Generates predictions in COCO format
- Creates visualization images with bounding boxes
The pipeline integrates with Roboflow for cloud-based annotation. This is an alternative to local annotation with LabelImg — use whichever fits your workflow.
Setup: Install the optional dependency and configure credentials:
pip install '.[roboflow]'
cp .env.example .env # then add your ROBOFLOW_API_KEY and ROBOFLOW_PROJECTAfter Phase 1 generates tiles, push them to Roboflow:
# Interactive batch picker
lunarsight push
# Push all batches without prompting
lunarsight push --all
# Use more upload threads (default: 10)
lunarsight push -w 20The push command:
- Discovers batch directories in
raw_tiles/with tiles - Uploads concurrently with retry logic and rate-limit handling
- Cleans up local tile directories after successful upload
After annotating in Roboflow, pull the dataset back:
# Pull latest version
lunarsight pull
# Pull a specific version
lunarsight pull --version 3The pull command:
- Downloads the annotated dataset from Roboflow
- Transforms Roboflow's COCO export into the pipeline's unified format
- Splits data 3-way: 70% train / 20% val / 10% test
- Converts to YOLO format automatically
- Backs up any existing annotations before overwriting
After pulling, you can proceed directly to Phase 3 (YOLO) or Phase 4 (Detectron2) training.
For programmatic usage:
from lunar_pipeline import (
run_imaging,
run_annotation,
run_train_yolo,
run_train_detectron2,
run_inference,
run_all
)
from lunar_pipeline.config import PipelineConfig
# Load configuration (uses ./data defaults)
config = PipelineConfig.from_yaml()
# Run individual phases
run_imaging(config)
run_annotation(config)
weights_yolo = run_train_yolo(config)
weights_d2 = run_train_detectron2(config)
run_inference(config, yolo_weights=weights_yolo, d2_weights=weights_d2, batch_size=16)
# Or run all phases
run_all(config)
# YOLO-only workflow
config = PipelineConfig.from_yaml()
run_imaging(config)
run_annotation(config)
weights = run_train_yolo(config)
run_inference(config, yolo_weights=weights) # D2 weights optionalfrom lunar_pipeline.config import PipelineConfig
# Load default configuration
config = PipelineConfig.from_yaml()
# Access configuration
print(config.paths.base_dir) # ./data
print(config.model.classes) # ['crater', 'pit', 'rock']
print(config.model.yolo_epochs) # 30LunarSight/
├── src/
│ └── lunar_pipeline/ # Main package
│ ├── phases/ # Phase modules (5 phases)
│ │ ├── imaging.py # Phase 1: Tiling & geolocation
│ │ ├── annotation.py # Phase 2: XML to COCO conversion
│ │ ├── train_yolo.py # Phase 3: YOLOv11 training
│ │ ├── train_detectron2.py # Phase 4: Detectron2 training
│ │ └── inference.py # Phase 5: Detection inference
│ ├── acquisition/ # Phase 0: LROC image download
│ │ ├── runner.py # Acquisition orchestrator
│ │ ├── downloader.py # PDS archive download
│ │ ├── volumes.py # Volume/product indexing
│ │ └── lookup.py # Image ID → volume lookup
│ ├── roboflow/ # Roboflow integration
│ │ ├── client.py # API client with lazy validation
│ │ ├── uploader.py # Batch upload with retry logic
│ │ └── puller.py # Pull annotations & COCO transform
│ ├── cli.py # CLI entry point
│ ├── config.py # Configuration management
│ ├── validation.py # Data validation utilities
│ └── __init__.py # Public API exports
├── utils/ # Shared utilities
│ ├── exceptions.py # Exception hierarchy
│ ├── rollback.py # Cleanup on failure (RollbackContext)
│ ├── spice.py # SPICE kernel manager
│ ├── coco.py # COCO format helpers
│ └── paths.py # Path validation
├── tests/ # Test suite
├── data/ # Default data directory (auto-created)
│ ├── source_images/ # Input: LROC NAC images (.IMG files)
│ │ ├── *.IMG # Flat structure (manual placement)
│ │ └── {volume_id}/ # Nested structure (Phase 0 acquisition)
│ │ └── {esm}/NAC/*.IMG
│ ├── kernels/ # Input: SPICE kernels
│ ├── annotated_images/ # Input: XML annotations
│ ├── raw_tiles/ # Output: Generated tiles
│ ├── runs/ # Output: Training results
│ └── tile_metadata/ # Output: Geolocation data
├── .env # Secrets (optional, gitignored)
└── pyproject.toml # Package metadata
Both training phases automatically evaluate on the test set and save metrics:
YOLO metrics (data/runs/detect/train_*/weights/metrics.json):
{
"mAP50": 0.847,
"mAP50-95": 0.623,
"precision": 0.891,
"recall": 0.834
}Detectron2 metrics (data/runs/detectron2_train/metrics.json):
{
"bbox/AP": 0.615,
"bbox/AP50": 0.842,
"bbox/AP75": 0.687
}Metrics are displayed as formatted tables during training and saved for later reference.
Test dependencies (pytest, pytest-mock) are included in the package, so no additional installation is needed.
# Run all tests (~60 seconds)
pytest
# Run with coverage
pytest --cov=lunar_pipeline
# Run specific test file
pytest tests/test_phase1_imaging.py
# Run with verbose output
pytest -v
# Show skip reasons for skipped tests
pytest -rsExpected Results: 395 passed, 2 skipped (with test data)
The test suite provides comprehensive coverage of all pipeline phases. Tests complete in approximately 60 seconds and verify the entire data flow without requiring actual trained model weights.
| Test File | Tests | Purpose |
|---|---|---|
test_phase1_imaging.py |
8 | Tile generation, geolocation accuracy, output structure, PNG/GeoTIFF creation |
test_phase2_annotation.py |
13 | XML parsing, COCO conversion, train/test splitting, YOLO format conversion |
test_phase3_yolo.py |
5 | YOLO config validation (model name, epochs, batch size, image size) |
test_phase4_detectron2.py |
2 | Detectron2 config validation, dataset registration paths |
test_phase5_inference.py |
~20 | SPICE kernel discovery, CPU/CUDA device detection, batch processing, inference output structure |
test_rollback.py |
12 | RollbackContext cleanup on failure, directory cleanup, edge cases |
test_annotation_yolo.py |
13 | COCO → YOLO format conversion, bbox normalization, data.yaml generation |
integration/test_inference_integration.py |
9 | Batch boundary cases, empty/invalid input handling, mocked ML models |
integration/test_annotation_integration.py |
14 | Malformed XML handling, missing image validation, end-to-end conversion |
roboflow/test_client.py |
17 | API client validation, project creation, credential handling |
roboflow/test_uploader.py |
30 | Batch upload, retry logic, concurrent upload, batch discovery |
roboflow/test_puller.py |
18 | Pull workflow, COCO transform, 3-way split, YOLO conversion |
Two integration test classes are intentionally skipped:
-
TestPhase3Integration(test_phase3_yolo.py) — "Requires ultralytics and test data"- Full YOLO training integration requires
ultralyticslibrary and real training data - Unit tests validate config and setup without actual model training
- Full YOLO training integration requires
-
TestPhase4Integration(test_phase4_detectron2.py) — "Requires detectron2 and test data"- Full Detectron2 training integration requires
detectron2library and real training data - Unit tests validate config and setup without actual model training
- Full Detectron2 training integration requires
These skips are intentional to keep the test suite fast and runnable without ML framework dependencies. The skipped tests are for end-to-end training validation, which requires GPU resources and significant time.
Use pytest -rs to see skip reasons in test output.
Phase 1 tests use a 4096-pixel tile size (the maximum allowed) instead of the default 1024 pixels. This generates approximately 13 tiles instead of 153 tiles, dramatically reducing test execution time while still validating the tiling logic, geolocation accuracy, and output structure.
Tests require sample LROC NAC imagery, SPICE kernels, and annotated tiles located in tests/test_data/.
Test data structure:
tests/test_data/
├── source_images/ # LROC NAC .IMG files with PDS4 .xml labels
├── kernels/ # SPICE kernels (.bsp, .tsc, .tf, etc.)
├── annotated_images/ # Annotated tiles (.png) with VOC XML annotations
├── raw_tiles/ # Generated output (created by tests)
├── runs/ # Generated output (created by tests)
└── download.py # Automated test data setup script
Setup test data:
# Automatic download (recommended)
python tests/test_data/download.py
# Check if test data is present
python tests/test_data/download.py --check-onlyThe test fixture automatically downloads test data from GitHub releases if missing. See tests/test_data/README.md for manual setup options.
Without test data: Tests that require real imagery will be skipped with the message "Test data not available. Run tests/test_data/download.py for setup instructions."
# Standard install
pip install .
# Editable install (for development)
pip install -e .
# Verify installation
lunarsight --help
python -c "from lunar_pipeline import run_all; print('OK')""SPICE kernel not found"
- Ensure SPICE kernels are placed in
data/kernels/ - Required kernels: planetary constants, lunar frame, and appropriate time kernels
"GDAL not found"
- Install GDAL system package (see Prerequisites section)
- Verify with:
gdalinfo --version
"No module named 'ultralytics'"
- Training requires ML framework:
pip install ultralytics(for YOLO) - Or:
pip install torch detectron2(for Detectron2)
"Training is very slow"
- No GPU detected — training is running on CPU
- Install CUDA-compatible PyTorch:
pip install torch --index-url https://download.pytorch.org/whl/cu118 - Verify GPU is available:
python -c "import torch; print(torch.cuda.is_available())"
"Invalid COCO format"
- Check annotation files match expected schema
- Validation errors show specific issues with annotation ID, filename, and dimensions
- Bbox bounds validation catches coordinates extending beyond image dimensions
- Review logs with
--verboseflag for detailed error messages
