Lunar Reconnaissance Orbiter (LRO) Narrow Angle Camera (NAC)#
Stereo example based on the ASP “Lightning Fast” lunar tutorial, but processing the full 5000×5000 px crop in the LRONAC_example.tar bundle instead of the small 900×973 sub-window the readme uses.
Why the larger crop:
A bigger DEM (~5 km × 5 km on the lunar surface vs ~1 km × 1 km) brings out spacecraft jitter in the disparity / hillshade panels.
The query area pulls thousands of LOLA points (vs ~19 in the tutorial crop), enough for a meaningful
pc_alignagainst LOLA.This mirrors the WorldView notebooks in this repo, which also process full-frame stereo rather than tiny sub-crops.
We use ASP’s CSM camera models (the .json files in the bundle) so no SPICE / ISIS spice setup is needed.
Data download#
wget https://github.com/NeoGeographyToolkit/StereoPipelineSolvedExamples/releases/download/LRONAC/LRONAC_example.tar
tar xf LRONAC_example.tar
Stereo + DEM (diverges from the ASP docs)#
The ASP tutorial uses --left-image-crop-win 2259 1196 900 973 to keep the demo tiny. We drop both crop-win flags so stereo runs on the full extent of the cubes that the tar already contains. Threading kept conservative (--processes 2 --threads 4, total 8 cores) to match this repo’s WorldView examples and avoid memory pressure on a laptop:
parallel_stereo \
M181058717LE_crop.cub M181073012LE_crop.cub \
M181058717LE.json M181073012LE.json \
--alignment-method local_epipolar \
--stereo-algorithm asp_mgm --subpixel-mode 9 \
--processes 2 --threads 4 \
out_stereo/output
point2dem --auto-proj-center --errorimage --orthoimage \
out_stereo/output-PC.tif out_stereo/output-L.tif
LOLA altimetry data#
Request via request_planetary_altimetry:
request_planetary_altimetry \
--dem out_stereo/output-DEM.tif \
--email user@example.com \
--channels ttttt # all five LOLA detectors (default tffff = channel 1)
When the email arrives, download and unzip. For LOLA either CSV format works — the loader auto-detects:
*_topo_simple_csv.csv(results=u): Pt_Longitude / Pt_Latitude / Topography (meters)*_pts_csv.csv(results=p): Pt_Longitude / Pt_Latitude / Pt_Radius (kilometers, auto-converted to meters)
The Moon is essentially spherical (~1.4 km equatorial-vs-polar variation), so LOLA topography ≈ height above the IAU 1737.4 km lunar sphere to ~1 m — both formats give the same dh against an ASP lunar DEM. (Mars is different — see the MOC NA notebook.)
directory = "~/Desktop/asp-plot-examples/lunar_lro_nac/"
stereo_directory = "out_stereo/"
lola_csv = f"{directory}/lola/LolaRDR_-10N-10N_15E15E_20260428T183313426_pts_csv.csv"
Full report (with LOLA + automatic pc_align)#
--pc_align True (default) runs pc_align against LOLA after the unaligned dh plots, applies the translation, and appends a pre/post comparison.
!asp_plot \
--directory $directory \
--stereo_directory $stereo_directory \
--subset_km 0.5 \
--add_basemap False \
--plot_geometry False \
--altimetry_csv $lola_csv \
--report_filename ../../reports/LRO_NAC-asp-plot-report.pdf
Individual plots#
Modular API examples.
Processing Parameters#
%load_ext autoreload
%autoreload 2
from asp_plot.processing_parameters import ProcessingParameters
processing_parameters = ProcessingParameters(
processing_directory=directory,
stereo_directory=stereo_directory,
)
processing_parameters_dict = processing_parameters.from_log_files()
print(f"Processed on: {processing_parameters_dict['processing_timestamp']}\n")
print(f"Reference DEM: {processing_parameters_dict['reference_dem']}\n")
print(f"Bundle adjustment ({processing_parameters_dict['bundle_adjust_run_time']}):\n")
print(processing_parameters_dict["bundle_adjust"])
print(f"\nStereo ({processing_parameters_dict['stereo_run_time']}):\n")
print(processing_parameters_dict["stereo"])
print(f"\nPoint2dem ({processing_parameters_dict['point2dem_run_time']}):\n")
print(processing_parameters_dict["point2dem"])
Scene plots#
from asp_plot.scenes import ScenePlotter
ScenePlotter(directory, stereo_directory, title="Scenes").plot_scenes()
Stereo plots#
from asp_plot.stereo import StereoPlotter
plotter = StereoPlotter(directory, stereo_directory)
plotter.title = "Hillshade with details"
plotter.plot_detailed_hillshade(subset_km=0.5)
plotter.title = "Stereo Match Points"
plotter.plot_match_points()
plotter.title = "Disparity (pixels)"
plotter.plot_disparity(unit="pixels", quiver=True)
plotter.title = "Stereo DEM Results"
plotter.plot_dem_results()
LOLA altimetry comparison + pc_align#
from asp_plot.altimetry import Altimetry
dem_fn = f"{directory}/{stereo_directory}/output-DEM.tif"
alt = Altimetry(directory=directory, dem_fn=dem_fn)
alt.load_planetary_csv(lola_csv)
alt.planetary_to_dem_dh()
alt.planetary_points[["lon", "lat", "height", "dem_height", "altimetry_minus_dem"]].head(10)
alt.mapview_plot_planetary_to_dem()
alt.histogram_planetary_to_dem()
result = alt.align_and_evaluate_planetary()
print(f"Status: {result.status}")
print(f"Improvement: {result.improvement_pct:.1f}%" if result.improvement_pct else "Improvement: n/a")
print(f"Aligned DEM: {result.aligned_dem_fn}")
print(f"Message: {result.message}")
result.alignment_report_df
alt.mapview_plot_planetary_to_dem(plot_aligned=True)
alt.histogram_planetary_to_dem(plot_aligned=True)