Skip to content

Add MroHiRisePds3LabelNaifSpiceDriver for ISIS-free HiRISE processing#702

Open
michaelaye wants to merge 1 commit intoDOI-USGS:mainfrom
michaelaye:hirise-pds3-driver
Open

Add MroHiRisePds3LabelNaifSpiceDriver for ISIS-free HiRISE processing#702
michaelaye wants to merge 1 commit intoDOI-USGS:mainfrom
michaelaye:hirise-pds3-driver

Conversation

@michaelaye
Copy link
Copy Markdown

Summary

Adds MroHiRisePds3LabelNaifSpiceDriver — a PDS3 label driver for HiRISE that enables ISD generation directly from raw PDS EDR files without requiring an ISIS cube.

CTX already has both ISIS-label and PDS3-label variants (MroCtxPds3LabelNaifSpiceDriver). This PR brings the same capability to HiRISE.

Motivation

I'm building a fully ISIS-free HiRISE calibration and map-projection pipeline (isistools/hirisepipe). Before this driver, the pipeline required hi2isis + spiceinit to create a cube for ALE to generate the ISD. With this driver, the entire chain from PDS EDR download to map-projected GeoTIFF works without any ISIS dependency:

PDS EDR → ale.loads(edr, props={'web': True}) → ISD → usgscsm → GeoTIFF

Implementation

The driver follows the same pattern as the existing MroHiRiseIsisLabelNaifSpiceDriver, adapted for PDS3 label keyword paths:

  • Parses INSTRUMENT_SETTING_PARAMETERS group for CPMM, TDI, binning, delta line timer count
  • Parses TIME_PARAMETERS group for spacecraft clock counts
  • Uses INSTRUMENT_HOST_NAME (PDS3) instead of SPACECRAFT_NAME (not present in HiRISE EDR labels)
  • Computes ephemeris_start_time with TDI/binning offsets matching the ISIS HiRise camera model
  • Uses hirise_ccd_lookup for CPMM-to-CCD NAIF IK ID mapping

Testing

Tested with ESP_021491_0950_RED4_0.IMG (RED4, channel 0, TDI=128, BIN=1):

import ale
isd = ale.loads("ESP_021491_0950_RED4_0.IMG", props={"web": True}, only_naif_spice=True)
# ISD generated in ~17s via SpiceQL web service
# image_lines: 35000, image_samples: 1024
# Camera model produces correct ground coordinates (lat=-85.14, lon=-178.79)

Full pipeline validated: Python-calibrated (hical replacement, 0.04% accuracy vs ISIS) + ALE PDS3 driver + csm2map projection produces a correct map-projected HiRISE RED mosaic. Two CCDs (RED4+RED5) mosaicked with rasterio.merge — zero ISIS commands used.

Resolves

Resolves #701

🤖 Generated with Claude Code

Enables HiRISE ISD generation directly from PDS3 EDR labels without
requiring an ISIS cube (hi2isis + spiceinit).  Uses NaifSpice for
ephemeris data via SpiceQL (local or web service).

The driver parses HiRISE PDS3 label groups:
- INSTRUMENT_SETTING_PARAMETERS for CPMM, TDI, binning, exposure
- TIME_PARAMETERS for spacecraft clock counts and start/stop times
- IMAGE for dimensions
- INSTRUMENT_HOST_NAME for spacecraft identification

Camera geometry matches the ISIS HiRise camera model:
- ephemeris_start_time adjusted for TDI and binning offsets
- un_binned_rate from DeltaLineTimerCount
- CCD NAIF IK ID from CPMM-to-CCD lookup table

Tested with ESP_021491_0950_RED4_0.IMG via web SpiceQL — ISD
generated in ~17s, camera model produces correct ground coordinates.

Resolves DOI-USGS#701.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@michaelaye
Copy link
Copy Markdown
Author

Comment: csm2map is my experimental but so far fully functional and up to 20 times faster cam2map replacement based on the CSM.

michaelaye added a commit to michaelaye/isistools that referenced this pull request Apr 20, 2026
…d feature matching

New subpackage `isistools.hirisepipe`:
- hical.py: 10-module radiometric calibration (0.04% mean accuracy vs ISIS from cube, 0.25% from EDR)
- ingest.py: direct PDS EDR reader (pixel-exact match with hi2isis)
- stitch.py: channel stitching with balance correction
- cubenorm.py: column normalization
- pipeline.py: full pipeline with parallel calibration via ProcessPoolExecutor + rich progress bars

Performance: 10-CCD RED mosaic calibrated in 28s parallel (vs ISIS 631s = 22x speedup).
Fully ISIS-free: reads PDS EDRs directly, uses ALE + web SpiceQL for camera models.
ALE HiRISE PDS3 driver contributed as DOI-USGS/ale#702.

New module `isistools.findfeatures`:
- AKAZE/ORB/SIFT feature matching via OpenCV
- Plio-compatible control network output with body-fixed XYZ coordinates
- Optional CSM camera model for ground coordinate computation
- Drop-in replacement for ISIS findfeatures

Tests: 14 hirisepipe + 8 findfeatures, all passing.
Documentation: docs/hirisepipe.qmd.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown
Collaborator

@acpaquette acpaquette left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Overall looks like a good addition, a few things to clean up. It would also be nice to get a test for the parameters that are being overwritten. Something like the test_ctx_pds_naif test set in test_mro_drivers.py

Comment on lines +723 to +734
def image_samples(self):
"""
Override to return science image width (excluding prefix/suffix).
The PDS3 label LINE_SAMPLES includes prefix and suffix bytes.
The actual science image for a single channel is 1024 / binning.

Returns
-------
: int
Number of science image samples
"""
return int(self.label['IMAGE']['LINE_SAMPLES'])
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment doesn't match code, not sure what's correct here. If the code is correct, this property doesn't need to be overridden as the base class has:

    @property
    def image_samples(self):
        """
        Returns the number of samples in the image.

        Returns
        -------
        : int
          Number of samples in the image
        """
        return self.label['IMAGE']['LINE_SAMPLES']

We could add the int conversion to the base class to be safe

: float
Center detector sample (placeholder for ISIS compatibility)
"""
return 0
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These don't seem correct compared to the ISIS camera:

focalMap->SetDetectorOrigin(1024.5, 0.0);
focalMap->SetDetectorOffset(0.0, ccdLine_c);

I'm not sure about the comment, "(placeholder for ISIS compatibility)" but these values are much more important to the USGSCSM camera than they are to ISIS.

Comment on lines +581 to +582
raw = self.label.get('SPACECRAFT_NAME',
self.label.get('INSTRUMENT_HOST_NAME', ''))
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we are sure that HiRise just uses the INSTRUMENT_HOST_NAME do we have to also try to get the SPACECRAFT_NAME

Comment on lines +816 to +825
@property
def platform_name(self):
"""
Returns
-------
: str
Platform name
"""
return self.label.get('SPACECRAFT_NAME',
self.label.get('INSTRUMENT_HOST_NAME', ''))
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This property seems unnecessary as the default class should provide the same value:

  @property
  def instrument_host_name(self):
      """
      Returns the full name of the instrument host

      Returns
      -------
      : str
        Full name of the instrument host
      """
      return self.label['INSTRUMENT_HOST_NAME']


  @property
  def platform_name(self):
      """
      Returns the name of the platform which the instrument is mounted on

      Returns
      -------
      : str
        platform name
      """
      return self.instrument_host_name

I'm not sure why we are trying both fields again here

@Kelvinrr
Copy link
Copy Markdown
Collaborator

@michaelaye How much of the code is generated? Here is our planned LLM policy: DOI-USGS/ISIS3#6013

At some point in the near future we can leverage Claude to create drivers for everything but this would require a lot of effort to test these drivers for accuracy.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add MroHiRisePds3LabelNaifSpiceDriver for direct EDR processing

3 participants