Skip to content

Add ShadowCam ISIS#6011

Merged
Kelvinrr merged 51 commits intoDOI-USGS:devfrom
cordellmichaud:dev-shadowcam-isis
Apr 27, 2026
Merged

Add ShadowCam ISIS#6011
Kelvinrr merged 51 commits intoDOI-USGS:devfrom
cordellmichaud:dev-shadowcam-isis

Conversation

@cordellmichaud
Copy link
Copy Markdown
Contributor

@cordellmichaud cordellmichaud commented Apr 7, 2026

Description

This PR adds the ShadowCamCamera and ShadowCamDistortionMap implementations, as well as the shadowcam2isis and shadowcamcal apps. They are in a working state in ISIS 8.3.0, but may need updates to become compatible with the latest ISIS. The apps also need ISIS tests.

Additionally, shadowcam2isis + shadowcamcal together are slower than our standalone calibration tool, and have room for significant performance improvement. I believe the major reason for this slowdown is the way that shadowcamcal currently writes each calibration step result to a separate output cube in a temporary location, allowing for the WriteOutSteps option to enable writing those temporary step output cubes to non-temporary output cubes. This WriteOutSteps option is an unnecessary lingering debug feature, and by coupling the removal of the WriteOutSteps option with some refactoring of the calibration steps, I believe we can apply calibration steps more directly, skip temporary cube I/O, and significantly improve performance.

Related Issue

How Has This Been Validated?

  • The ShadowCam team has internally validated the output of the shadowcam2isis and shadowcamcal apps. (Thank you @rvwagner!)
    • Calibration works correctly, with no difference in output from our standalone calibration tool.
    • Serial number generation works correctly, and matches the current values used by our controlled mosaic team.
  • There remains a minor ShadowCam image first-pixel timing difference from expected (measured by campt line=1 sample=1).
    • Image first-pixel time is ahead by about (0.6 ± 0.1) microseconds.
    • We are not concerned about this fractional microseconds difference, but it may be worth investigating.

Types of changes

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Documentation change (update to the documentation; no code change)
  • Breaking change (fix or feature that would cause existing functionality to change)
  • Infrastructure change (changes to things like CI or the build system that do not impact users)

Checklist:

  • I have read and agree to abide by the Code of Conduct
  • I have read the CONTRIBUTING document.
  • My change requires a change to the documentation and I have updated the documentation accordingly.
  • I have added tests to cover my changes.
  • I have added myself to the .zenodo.json document.
  • I have added my user impacting change to the CHANGELOG.md document.

Licensing

This project is mostly composed of free and unencumbered software released into the public domain, and we are unlikely to accept contributions that are not also released into the public domain. Somewhere near the top of each file should have these words:

This work is free and unencumbered software released into the public domain. In jurisdictions that recognize copyright laws, the author or authors of this software dedicate any and all copyright interest in the software to the public domain.

  • I dedicate any and all copyright interest in this software to the public domain. I make this dedication for the benefit of the public at large and to the detriment of my heirs and successors. I intend this dedication to be an overt act of relinquishment in perpetuity of all present and future rights to this software under copyright law.

Victor Silva and others added 30 commits April 7, 2026 12:01
- Implement shadowcamcal
- Implement shadowcam2isis
- Implement ShadowCamCamera
- Move all functions into a ShadowCam sub-namespace
- Move function docstrings into the header and correct them
- Add missing includes, remove unused includes, and add include guard
- Remove using namespace std declaration and scope any standard library
  members
- Modify GetTdiFactor to throw an error if an invalid TDIDirection value
  is encountered, rather than ignoring it and defaulting to 0
- Modify ToLower to leverage the functionality already available in QString
- Remove unnecessary log parameter from shadowcamcal
- Add shadowcamcal overload accepting an input cube and ui
- Correct typos and make variable names more consistent
- Simplify code where possible
- Remove unnecessary using namespace std
- Add missing includes and remove unused ones
- Move shadowcamcal's WriteCube helper function into a ShadowCam
  sub-namespace within shadowcamcal.cpp and shadowcamcal.h
- Improve variable naming consistency and simplify where possible
- Update references to WriteCube accordingly
- Rename DarkSubtraction to SubtractDark and move it to a
  sub-namespace within shadowcamcal
- Update references to DarkSubtraction
- Add docstring to dark subtraction function
- Simplify dark subtraction function where possible and make style and
  formatting consistent
- Add missing scope prefixes and headers
- Remove unnecessary headers
- Remove old dark subtraction function
- Rename FlatFieldCorrection to CorrectFlatfield, move it to a
  sub-namespace in shadowcamcal, and refactor it
- Improve code styling, readability, and complexity where possible
- Update references to old function where applicable
- Rename GainCorrection to CorrectGain, move it to a sub-namespace in
  shadowcamcal, and refactor it
- Improve style, consistency, and complexity where possible
- Rename radiance correction from RadianceCoefficients to
  CorrectRadiance and move it into a sub-namespace in shadowcamcal
- Refactor radiance correction to improve readability, consistency, and
  complexity where possible
@github-actions
Copy link
Copy Markdown

github-actions Bot commented Apr 7, 2026

The build and test suite have started for your pull request.

To view your build log, please reference the build with source version: "PR_6011".

Additionally, check the latest "dev" source version to identify existing test failures. Please note that you are not responsible for the test failures that exist on both your PR and the dev branch.

@Kelvinrr
Copy link
Copy Markdown
Collaborator

Kelvinrr commented Apr 7, 2026

Thanks for getting this in!

Some followup questions: any additional supporting files that are needed for working shadowcam? Looking at calibration I dont see an obvious need for an additional calibration file. But where do we get SPICE kernels? Is it somewhere in rsync.im-ldi.com? We found some on the KPDS website, but I am hoping for a storage we can rsync/rclone from, also IIRC there may be a custom IAK. We would need to host them on USGS S3 buckets.

@rvwagner
Copy link
Copy Markdown

rvwagner commented Apr 7, 2026

A quick comment about the remaining time offset: I believe it is due to a limit in the precision of data reported in the PDS file headers for ShadowCam. The difference is in the 7th decimal place, and the various time values in the PDS header are "only" reported to 6 decimal places. In the prototype code, meanwhile, the various adjustments go out to ~15-20 decimal places.

And yes, the KPLO kernels are available on the same rsync server as LROC. Just change the /lro/ to /kplo/ (rsync://rsync.im-ldi.com/kplo), and it's the same ISIS kernel directory structure. The default IAK is in that directory tree; like for LROC, highest-accuracy usage is to customize it per-image to add a fixed offset for light time, but it works perfectly well with just the defaul file, and honestly the extra meter or so of accuracy from the light time correction isn't really needed given the ~60m baseline uncertainty.

@Kelvinrr
Copy link
Copy Markdown
Collaborator

Kelvinrr commented Apr 9, 2026

Any objections to pushing directly to this to add tests? @cordellmichaud

@cordellmichaud
Copy link
Copy Markdown
Contributor Author

Any objections to pushing directly to this to add tests? @cordellmichaud

I have no objections. Feel free to push to it directly.

@acpaquette acpaquette mentioned this pull request Apr 10, 2026
12 tasks
@acpaquette
Copy link
Copy Markdown
Collaborator

@cordellmichaud or @rvwagner small question regarding calibrated vs uncalibrated data and ingestion. The shadowcam2isis XML recommends "...images in the CDR or RDR format should be ingested using pds2isis". I tired processing a calibrated image, specificaly M013049982SC.cub, with pds2isis and it failed to process the file. I don't think the pds2isis program was updated to read pds ISIS3 cubes since they're already cubes. Can the calibrated data just be used in ISIS without any sort of ingest or processing, or does it need some level of processing before use? Or or, were there updates made to pds2isis/ProcessImportPds to read in the cubes?

I just want to clarify as it seems like the only things this PR needs is a few error checks and documentation tweeks

@rvwagner
Copy link
Copy Markdown

Yeah, I suspect that is left over from the NAC documentation that was probably adapted (possibly before we decided on the final PDS format?). Probably should simply mention that CDR and RDRs can be used directly, because they can.

shadowcam2isis itself is kind of a make-work program, honestly. It exists because ISIS traditionally uses an xxx2isis program, and so we decided to stick the decompanding step of calibration in there to give it something to do. Our standalone tool goes straight from EDR to CDR in one program (which, to be clear, shadowcamcal cannot do, it needs shadowcam2isis first).

@acpaquette
Copy link
Copy Markdown
Collaborator

Hi all, I put in a PR into @cordellmichaud's branch.

cordellmichaud#1

It's enough of a change to the core apps that I wanted to give folks a chance to look over it

@github-actions
Copy link
Copy Markdown

The build and test suite have started for your pull request.

To view your build log, please reference the build with source version: "PR_6011".

Additionally, check the latest "dev" source version to identify existing test failures. Please note that you are not responsible for the test failures that exist on both your PR and the dev branch.

@acpaquette
Copy link
Copy Markdown
Collaborator

acpaquette commented Apr 23, 2026

@cordellmichaud Tests for shadow cam are here:
cordellmichaud#2

There is a small change to one of the apps, so I thought it would be best to be transparent about it

Pushed directly

@github-actions
Copy link
Copy Markdown

The build and test suite have started for your pull request.

To view your build log, please reference the build with source version: "PR_6011".

Additionally, check the latest "dev" source version to identify existing test failures. Please note that you are not responsible for the test failures that exist on both your PR and the dev branch.

Comment on lines +158 to +173
/*
if (ShadowCam::IsSpecialPixelSHC(in[bufferIndex])) {
if (keepSpecial)
out[bufferIndex] = ShadowCam::Set8bitMaxMintoSpecialPixelsHIS4LIS4(in[bufferIndex]);
else {
// Special pixel handling
cout << "WARNING: Special pixels set to 0 or 255." << endl;
out[bufferIndex] = static_cast<float>(ShadowCam::Set_LIS_HIS_SpecialPixelsTo_0_255(in[bufferIndex]));
}
}
else {
// Decompanding non-special pixels
out[bufferIndex] = static_cast<float>(decompanding_table[(uint16_t) in[bufferIndex]]);
#if (out[bufferIndex] < 0){
# cout << "Value is less than zero for line: " << std::to_string(in.Line()) << ", pixel: " << std::to_string(bufferIndex) << "at(i): " << std::to_string(in.at(bufferIndex)) << endl;
}*/
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.

What is this for?

@Kelvinrr Kelvinrr merged commit 3363028 into DOI-USGS:dev Apr 27, 2026
5 of 7 checks passed
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.

4 participants