Unverified Commit 625b7f0e authored by Cordell Michaud's avatar Cordell Michaud Committed by GitHub
Browse files

Update lrowacphomap with versioned defaults for PHOALGO and PHOPARCUBE parameters (#5452)



* Update lrowacphomap.xml with PHOALGO and PHOPARCUBE defaults

* Add test cases for lrowacphomap PHOALGO and PHOPARCUBE defaults

* Add handling for lrowacphomap PHOALGO and PHOPARCUBE defaults

* Add CHANGELOG.md entry for lrowacphomap defaults update

* Fix error during parCube setVirtualBands call

* Move lrowacphomap defaults update changelog entry to the Unreleased section

---------

Co-authored-by: default avatarAustin Sanders <arsanders@usgs.gov>
parent dce1406b
Loading
Loading
Loading
Loading
+3 −0
Original line number Diff line number Diff line
@@ -44,6 +44,9 @@ release.
- Fixed a bug in QVIEW's Stretch tool where the default min/max type was not an available option [#5289](https://github.com/DOI-USGS/ISIS3/issues/5289)
- Fixed a bug in QVIEW where images would double load if loaded from the commandline [#5505](https://github.com/DOI-USGS/ISIS3/pull/5505)

### Added
- Added versioned default values to lrowacphomap's PHOALGO and PHOPARCUBE parameters and updated lrowacphomap to handle them properly. [#5452](https://github.com/DOI-USGS/ISIS3/pull/5452)

## [8.2.0] - 2024-04-18

### Changed
+97 −37
Original line number Diff line number Diff line
#include <string>
#include "lrowacphomap.h"

#include <memory>
#include <vector>

#include <QList>
#include <QString>

#include "Buffer.h"
#include "Cube.h"
#include "CubeAttribute.h"
#include "FileName.h"
#include "HapkeLRO.h"
#include "HapkeLROC.h"
#include "IException.h"
#include "PhotometricFunction.h"
#include "ProcessByBrick.h"
#include "Pvl.h"
#include "PvlGroup.h"
#include "SpecialPixel.h"

#include "lrowacphomap.h"

using namespace std;
using namespace Isis;

#include "UserInterface.h"

namespace Isis {
  /**
   * @brief Apply Hapke photometric correction to a WAC cube.
   * 
   * This is the programmatic interface to the lrowacphomap application.
   * 
   * @author 2021-08-02 Cordell Michaud
   * 
   * @internal
   *   @history 2021-08-02 Cordell Michaud - Code adapted from lrowacphomap by Kris Becker
   *   @history 2024-04-02 Cordell Michaud - Changed icube to be a regular Cube object rather than 
   *                           a pointer
   * 
   * @param ui the user interface to parse parameters from
  */
  Pvl lrowacphomap(UserInterface &ui) {
    // Set up the input cube
    Cube *icube = new Cube();
    Cube icube;
    CubeAttributeInput inAtt = ui.GetInputAttribute("FROM");
    if (inAtt.bands().size() != 0) {
      icube->setVirtualBands(inAtt.bands());
      icube.setVirtualBands(inAtt.bands());
    }
    icube->open(ui.GetCubeName("FROM"));
    icube.open(ui.GetCubeName("FROM"));

    Pvl log = lrowacphomap(icube, ui);
    Pvl log = lrowacphomap(&icube, ui);

    delete icube;
    icube = NULL;
    icube.close();

    return log;
  }

  /**
   * @brief Apply Hapke photometric correction to a WAC cube.
   * 
   * This is the programmatic interface to the lrowacphomap application.
   * 
   * @author 2021-08-02 Cordell Michaud
   * 
   * @internal
   *   @history 2021-08-02 Cordell Michaud - Code adapted from lrowacphomap by Kris Becker
   *   @history 2024-04-02 Cordell Michaud - Converted phoLro and phoLroc to smart pointers and 
   *                           added handling for new PHOALGO and PHOPARCUBE defaults
   * 
   * @param icube the input cube to apply Hapke photometric correction to
   * @param ui the user interface to parse parameters from
  */
  Pvl lrowacphomap(Cube *icube, UserInterface &ui) {
    Pvl log;

    HapkeLRO *phoLro = NULL;
    HapkeLROC *phoLroc = NULL;
    std::shared_ptr<HapkeLRO> phoLro;
    std::shared_ptr<HapkeLROC> phoLroc;
    QString algoName;

    bool normalized;
@@ -54,13 +88,14 @@ namespace Isis {

    if (ui.WasEntered("BACKPLANE")) {
      if (icube->bandCount() != 1) {
        string msg = "Invalid Image: The backplane option can only be used with a single image band at a time.";
        QString msg = "Invalid Image: The backplane option can only be used with a single image "
                      "band at a time.";
        throw IException(IException::User, msg, _FILEINFO_);
      }

      CubeAttributeInput backplaneCai = ui.GetInputAttribute("BACKPLANE");

      vector<QString> bands = backplaneCai.bands();
      std::vector<QString> bands = backplaneCai.bands();

      if (bands.size() == 0) {
        bands.clear();
@@ -71,7 +106,7 @@ namespace Isis {
        bands.push_back("5");
      }
      else if (bands.size() != 5) {
        string msg = "Invalid Backplane: The backplane must be exactly 5 bands";
        QString msg = "Invalid Backplane: The backplane must be exactly 5 bands";
        throw IException(IException::User, msg, _FILEINFO_);
      }

@@ -91,13 +126,37 @@ namespace Isis {
    }

    // Get the name of the parameter files
    Pvl par(ui.GetFileName("PHOALGO"));
    Cube *parCube = new Cube();
    CubeAttributeInput parCubeAtt = ui.GetInputAttribute("PHOPARCUBE");
    if (parCubeAtt.bands().size() != 0) {
      parCube->setVirtualBands(parCubeAtt.bands());
    QString algoFile = ui.GetAsString("PHOALGO");
    FileName algoFileName(algoFile);
    if (algoFileName.isVersioned()) {
      algoFileName = algoFileName.highestVersion();
    }
    if (!algoFileName.fileExists()) {
      QString msg = algoFileName.expanded() + " does not exist.";
      throw IException(IException::User, msg, _FILEINFO_);
    }
    Pvl par(algoFileName.expanded());

    QString parCubeFile = ui.GetCubeName("PHOPARCUBE");
    FileName parCubeFileName(parCubeFile);
    if (parCubeFileName.isVersioned()) {
      parCubeFileName = parCubeFileName.highestVersion();
    }
    if (!parCubeFileName.fileExists()) {
      QString msg = parCubeFileName.expanded() + " does not exist.";
      throw IException(IException::User, msg, _FILEINFO_);
    }
    CubeAttributeInput parCubeAtt(parCubeFileName);
    Cube parCube(parCubeFileName);
    std::vector<QString> parCubeBandsVector = parCubeAtt.bands();
    QList<QString> parCubeBands;
    parCubeBands.reserve(parCubeBandsVector.size());
    for (const QString &band : parCubeBandsVector) {
      parCubeBands.push_back(band);
    }
    if (parCubeBands.size() != 0) {
      parCube.setVirtualBands(parCubeBands);
    }
    parCube->open(ui.GetCubeName("PHOPARCUBE"));

    p.SetBrickSize(128, 128, icube->bandCount());

@@ -107,7 +166,7 @@ namespace Isis {
    normalized = ui.GetBoolean("NORMALIZED");

    if (algoName == "HAPKELRO") {
      phoLro = new HapkeLRO(par, *icube, !useBackplane, parCube);
      phoLro = std::make_shared<HapkeLRO>(par, *icube, !useBackplane, &parCube);
      phoLro->setMinimumPhaseAngle(ui.GetDouble("MINPHASE"));
      phoLro->setMaximumPhaseAngle(ui.GetDouble("MAXPHASE"));
      phoLro->setMinimumEmissionAngle(ui.GetDouble("MINEMISSION"));
@@ -118,7 +177,7 @@ namespace Isis {
      phoLro->setNormalized(normalized);
    }
    else if (algoName == "HAPKELROC") {
      phoLroc = new HapkeLROC(par, *icube, !useBackplane, parCube);
      phoLroc = std::make_shared<HapkeLROC>(par, *icube, !useBackplane, &parCube);
      phoLroc->setMinimumPhaseAngle(ui.GetDouble("MINPHASE"));
      phoLroc->setMaximumPhaseAngle(ui.GetDouble("MAXPHASE"));
      phoLroc->setMinimumEmissionAngle(ui.GetDouble("MINEMISSION"));
@@ -147,11 +206,13 @@ namespace Isis {
    * 
    * @internal
    *   @history 2021-07-19 Cordell Michaud - Code adapted from lrowacphomap by Kris Becker
    *   @history 2024-04-02 Cordell Michaud - Changed to capture phoLro and phoLroc by value
    *
    * @param in Buffer containing input data
    * @param out Buffer of photometrically corrected data
    */
    auto phoCal = [&phoLro, &phoLroc, algoName, useDem, photometryOnly](Buffer &in, Buffer &out) -> void {
    auto phoCal = [phoLro, phoLroc, algoName, useDem, photometryOnly](
        Buffer &in, Buffer &out) -> void {
      for (int i = 0; i < in.size(); i++) {
        //  Don't correct special pixels
        if (IsSpecial(in[i])) {
@@ -189,12 +250,13 @@ namespace Isis {
    * 
    * @internal
    *   @history 2021-07-19 Cordell Michaud - Code adapted from lrowacphomap by Kris Becker
    *   @history 2024-04-02 Cordell Michaud - Changed to capture phoLro and phoLroc by value
    *
    * @param in Buffer containing input data
    * @param out Buffer of photometrically corrected data
    */
    auto phoCalWithBackplane = [&phoLro, &phoLroc, algoName, photometryOnly](std::vector<Isis::Buffer *> &in, std::vector<Isis::Buffer *> &out) -> void {

    auto phoCalWithBackplane = [phoLro, phoLroc, algoName, photometryOnly](
        std::vector<Buffer *> &in, std::vector<Buffer *> &out) -> void {
      Buffer &image = *in[0];
      Buffer &phase = *in[1];
      Buffer &emission = *in[2];
@@ -208,17 +270,20 @@ namespace Isis {
        if (IsSpecial(image[i])) {
          calibrated[i] = image[i];
        }
        else if (IsSpecial(phase[i]) || IsSpecial(emission[i]) || IsSpecial(incidence[i]) || IsSpecial(lat[i]) || IsSpecial(lon[i])) {
        else if (IsSpecial(phase[i]) || IsSpecial(emission[i]) || IsSpecial(incidence[i]) 
                 || IsSpecial(lat[i]) || IsSpecial(lon[i])) {
          calibrated[i] = Isis::Null;
        }
        else {
          // Get correction and test for validity
          double ph = Null;
          if (algoName == "HAPKELRO") {
            ph = phoLro->photometry(incidence[i], emission[i], phase[i], lat[i], lon[i], image.Band(i));
            ph = phoLro->photometry(incidence[i], emission[i], phase[i], lat[i], lon[i], 
                                    image.Band(i));
          }
          else if (algoName == "HAPKELROC") {
            ph = phoLroc->photometry(incidence[i], emission[i], phase[i], lat[i], lon[i], image.Band(i));
            ph = phoLroc->photometry(incidence[i], emission[i], phase[i], lat[i], lon[i], 
                                     image.Band(i));
          }

          if (!photometryOnly) {
@@ -253,11 +318,6 @@ namespace Isis {
    ocube->putGroup(photo);
    log.addGroup(photo);

    delete phoLro;
    phoLro = NULL;
    delete phoLroc;
    phoLroc = NULL;

    return log;
  }
}
 No newline at end of file
+7 −0
Original line number Diff line number Diff line
@@ -272,6 +272,10 @@
    <change name="Cordell Michaud" date="2022-05-10">
      Refactored lrowacphomap for testability and added functional tests.
    </change>
    <change name="Cordell Michaud" date="2024-04-02">
      Added new default for PHOALGO.
      Added new default for PHOPARCUBE.
    </change>
  </history>

  <category>
@@ -326,6 +330,8 @@
      <parameter name="PHOALGO">
        <type>filename</type>
        <fileMode>input</fileMode>
        <defaultPath>$lro/calibration</defaultPath>
        <default><item>$lro/calibration/WAC_hapke_full.????.pvl</item></default>
        <brief>
          Pvl file
        </brief>
@@ -353,6 +359,7 @@
      <parameter name="PHOPARCUBE">
        <type>cube</type>
        <fileMode>input</fileMode>
        <default><item>$lro/calibration/WAC_global_7bands_1x1_wbhs70NS_const_each_pole.????.cub</item></default>
        <brief>
          Parameter cube
        </brief>
+94 −6
Original line number Diff line number Diff line
#include <memory>
#include <ostream>

#include <QString>
#include <QTemporaryDir>
#include <QVector>

#include "Cube.h"
#include "FileName.h"
#include "Histogram.h"
#include "Pvl.h"
#include "PvlGroup.h"
#include "IException.h"
#include "TestUtilities.h"

#include "lrowacphomap.h"
#include "UserInterface.h"

#include "gtest/gtest.h"

#include "lrowacphomap.h"

using namespace Isis;

static QString APP_XML = FileName("$ISISROOT/bin/xml/lrowacphomap.xml").expanded();
@@ -34,7 +41,8 @@ TEST(Lrowacphomap, FunctionalTestLrowacphomapWithBack) {
    lrowacphomap(options);
  }
  catch(IException &e) {
    FAIL() << "Call to lrowacphomap failed, unable to apply photometric correction to input cube: " << e.what() << std::endl;
    FAIL() << "Call to lrowacphomap failed, unable to apply photometric correction to input cube: " 
           << e.what() << std::endl;
  }

  double expectedAvg = 58.565850201775;
@@ -76,7 +84,8 @@ TEST(Lrowacphomap, FunctionalTestLrowacphomapNoBack) {
    lrowacphomap(options);
  }
  catch(IException &e) {
    FAIL() << "Call to lrowacphomap failed, unable to apply photometric correction to input cube: " << e.what() << std::endl;
    FAIL() << "Call to lrowacphomap failed, unable to apply photometric correction to input cube: " 
           << e.what() << std::endl;
  }

  double expectedAvg = 58.159470616532;
@@ -97,3 +106,82 @@ TEST(Lrowacphomap, FunctionalTestLrowacphomapNoBack) {
  EXPECT_NEAR(hist->Maximum(), expectedMax, 0.001);
  EXPECT_NEAR(hist->Sum(), expectedSum, 0.001);
}

TEST(Lrowacphomap, FunctionalTestLrowacphomapDefaultAlgoAndParCubeWithBack) {
  QTemporaryDir tempDir;
  ASSERT_TRUE(tempDir.isValid());

  QString outCubeFileName = tempDir.path() + "/outTemp.cub";
  QString testCubeFileName = "data/lrowacphomap/M1181493219CE.vis.odd.reduced.cub";
  QString backplaneFileName = "data/lrowacphomap/back.reduced.cub";

  QVector<QString> args = {"from=" + testCubeFileName + "+1",
                           "to=" + outCubeFileName,
                           "backplane=" + backplaneFileName};
  UserInterface options(APP_XML, args);

  try {
    lrowacphomap(options);
  }
  catch(IException &e) {
    FAIL() << "Call to lrowacphomap failed, unable to apply photometric correction to input cube: " 
           << e.what() << std::endl;
  }

  double expectedAvg = 57.150192172911;
  double expectedStdDev = 19.201699528246;
  double expectedMedian = 55.0376024164;
  double expectedMin = 20.151010513306;
  double expectedMax = 157.08757019043;
  double expectedSum = 192367.54685402;

  Cube outCube(outCubeFileName);

  std::unique_ptr<Histogram> hist(outCube.histogram());

  EXPECT_NEAR(hist->Average(), expectedAvg, 0.001);
  EXPECT_NEAR(hist->StandardDeviation(), expectedStdDev, 0.001);
  EXPECT_NEAR(hist->Median(), expectedMedian, 0.001);
  EXPECT_NEAR(hist->Minimum(), expectedMin, 0.001);
  EXPECT_NEAR(hist->Maximum(), expectedMax, 0.001);
  EXPECT_NEAR(hist->Sum(), expectedSum, 0.001);
}

TEST(Lrowacphomap, FunctionalTestLrowacphomapDefaultAlgoAndParCubeNoBack) {
  QTemporaryDir tempDir;
  ASSERT_TRUE(tempDir.isValid());

  QString outCubeFileName = tempDir.path() + "/outTemp.cub";
  QString testCubeFileName = "data/lrowacphomap/M1181493219CE.vis.odd.reduced.cub";

  QVector<QString> args = {"from=" + testCubeFileName + "+1",
                           "to=" + outCubeFileName,
                           "usedem=true"};
  UserInterface options(APP_XML, args);

  try {
    lrowacphomap(options);
  }
  catch(IException &e) {
    FAIL() << "Call to lrowacphomap failed, unable to apply photometric correction to input cube: " 
           << e.what() << std::endl;
  }

  double expectedAvg = 56.750011832815;
  double expectedStdDev = 18.44290433699;
  double expectedMedian = 55.263128187622;
  double expectedMin = 22.274614334106;
  double expectedMax = 152.65106201172;
  double expectedSum = 191020.53982925;

  Cube outCube(outCubeFileName);

  std::unique_ptr<Histogram> hist(outCube.histogram());

  EXPECT_NEAR(hist->Average(), expectedAvg, 0.001);
  EXPECT_NEAR(hist->StandardDeviation(), expectedStdDev, 0.001);
  EXPECT_NEAR(hist->Median(), expectedMedian, 0.001);
  EXPECT_NEAR(hist->Minimum(), expectedMin, 0.001);
  EXPECT_NEAR(hist->Maximum(), expectedMax, 0.001);
  EXPECT_NEAR(hist->Sum(), expectedSum, 0.001);
}