diff --git a/isis/src/base/apps/stretch/main.cpp b/isis/src/base/apps/stretch/main.cpp index 37dcf08b52..494bd54605 100644 --- a/isis/src/base/apps/stretch/main.cpp +++ b/isis/src/base/apps/stretch/main.cpp @@ -1,84 +1,26 @@ #include "Isis.h" -#include "TextFile.h" -#include "Statistics.h" -#include "ProcessByLine.h" -#include "SpecialPixel.h" -#include "Stretch.h" -#include "PvlGroup.h" -#include "PvlKeyword.h" + +#include "Application.h" +#include "UserInterface.h" + +#include "stretch_app.h" using namespace std; using namespace Isis; -void stretch(Buffer &in, Buffer &out); -Stretch str; -Statistics stats; - void IsisMain() { - ProcessByLine p; - Cube *inCube = p.SetInputCube("FROM"); - UserInterface &ui = Application::GetUserInterface(); - - QString pairs; - - // first just get the pairs from where ever and worry about - // whether they are dn values or %'s later - if(ui.GetBoolean("READFILE")) { - FileName pairsFileName = ui.GetFileName("INPUTFILE"); - TextFile pairsFile; - pairsFile.SetComment("#"); - pairsFile.Open(pairsFileName.expanded()); - - // concat all non-comment lines into one string (pairs) - QString line = ""; - while(pairsFile.GetLine(line, true)) { - pairs += " " + line; - } - pairs += line; + Pvl results; + try{ + stretch(ui, &results); } - else { - if(ui.WasEntered("PAIRS")) - pairs = ui.GetString("PAIRS"); - } - - if(ui.GetBoolean("USEPERCENTAGES")) { - str.Parse(pairs, inCube->histogram()); + catch(...){ + for (int resultIndex = 0; resultIndex < results.groups(); resultIndex++) { + Application::Log(results.group(resultIndex)); + } + throw; } - else - str.Parse(pairs); - - // Setup new mappings for special pixels if necessary - if(ui.WasEntered("NULL")) - str.SetNull(StringToPixel(ui.GetString("NULL"))); - if(ui.WasEntered("LIS")) - str.SetLis(StringToPixel(ui.GetString("LIS"))); - if(ui.WasEntered("LRS")) - str.SetLrs(StringToPixel(ui.GetString("LRS"))); - if(ui.WasEntered("HIS")) - str.SetHis(StringToPixel(ui.GetString("HIS"))); - if(ui.WasEntered("HRS")) - str.SetHrs(StringToPixel(ui.GetString("HRS"))); - - p.SetOutputCube("TO"); - - // Start the processing - p.StartProcess(stretch); - p.EndProcess(); - - PvlKeyword dnPairs = PvlKeyword("StretchPairs"); - dnPairs.addValue(str.Text()); - - PvlGroup results = PvlGroup("Results"); - results.addKeyword(dnPairs); - - Application::Log(results); - -} - -// Line processing routine -void stretch(Buffer &in, Buffer &out) { - for(int i = 0; i < in.size(); i++) { - out[i] = str.Map(in[i]); + for (int resultIndex = 0; resultIndex < results.groups(); resultIndex++) { + Application::Log(results.group(resultIndex)); } } diff --git a/isis/src/base/apps/stretch/stretch_app.cpp b/isis/src/base/apps/stretch/stretch_app.cpp new file mode 100644 index 0000000000..8b8ffd9f3b --- /dev/null +++ b/isis/src/base/apps/stretch/stretch_app.cpp @@ -0,0 +1,94 @@ +#include "TextFile.h" +#include "Statistics.h" +#include "ProcessByLine.h" +#include "SpecialPixel.h" +#include "Stretch.h" +#include "PvlGroup.h" +#include "PvlKeyword.h" + +#include "stretch_app.h" + +namespace Isis { + void stretchProcess(Buffer &in, Buffer &out); + Stretch str; + Statistics stats; + + void stretch(UserInterface &ui, Pvl *log) { + Cube *cubeFile = new Cube(); + CubeAttributeInput inAtt = ui.GetInputAttribute("FROM"); + if (inAtt.bands().size() != 0) { + cubeFile->setVirtualBands(inAtt.bands()); + } + cubeFile->open(ui.GetFileName("FROM"), "r"); + + QString pairs; + + // first just get the pairs from where ever and worry about + // whether they are dn values or %'s later + if(ui.GetBoolean("READFILE")) { + FileName pairsFileName = ui.GetFileName("INPUTFILE"); + TextFile pairsFile; + pairsFile.SetComment("#"); + pairsFile.Open(pairsFileName.expanded()); + + // concat all non-comment lines into one string (pairs) + QString line = ""; + while(pairsFile.GetLine(line, true)) { + pairs += " " + line; + } + pairs += line; + } + else { + if(ui.WasEntered("PAIRS")) + pairs = ui.GetString("PAIRS"); + } + + stretch(cubeFile, pairs, ui, log); + } + + void stretch(Cube *inCube, QString &pairs, UserInterface &ui, Pvl *log) { + ProcessByLine p; + p.SetInputCube(inCube); + + if(ui.GetBoolean("USEPERCENTAGES")) { + str.Parse(pairs, inCube->histogram()); + } + else + str.Parse(pairs); + + // Setup new mappings for special pixels if necessary + if(ui.WasEntered("NULL")) + str.SetNull(StringToPixel(ui.GetString("NULL"))); + if(ui.WasEntered("LIS")) + str.SetLis(StringToPixel(ui.GetString("LIS"))); + if(ui.WasEntered("LRS")) + str.SetLrs(StringToPixel(ui.GetString("LRS"))); + if(ui.WasEntered("HIS")) + str.SetHis(StringToPixel(ui.GetString("HIS"))); + if(ui.WasEntered("HRS")) + str.SetHrs(StringToPixel(ui.GetString("HRS"))); + + p.SetOutputCubeStretch("TO", &ui); + + // Start the processing + p.StartProcess(stretchProcess); + p.EndProcess(); + + PvlKeyword dnPairs = PvlKeyword("StretchPairs"); + dnPairs.addValue(str.Text()); + + PvlGroup results = PvlGroup("Results"); + results.addKeyword(dnPairs); + + if (log){ + log->addGroup(results); + } + } + + // Line processing routine + void stretchProcess(Buffer &in, Buffer &out) { + for(int i = 0; i < in.size(); i++) { + out[i] = str.Map(in[i]); + } + } +} diff --git a/isis/src/base/apps/stretch/stretch_app.h b/isis/src/base/apps/stretch/stretch_app.h new file mode 100644 index 0000000000..59c521e788 --- /dev/null +++ b/isis/src/base/apps/stretch/stretch_app.h @@ -0,0 +1,14 @@ +#ifndef stretch_app_h +#define stretch_app_h + +#include "PvlGroup.h" +#include "PvlKeyword.h" +#include "UserInterface.h" + +namespace Isis { + extern void stretch(UserInterface &ui, Pvl *log=nullptr); + + extern void stretch(Cube *inCube, QString &pairs, UserInterface &ui, Pvl *log=nullptr); +} + +#endif diff --git a/isis/src/base/objs/Process/Process.cpp b/isis/src/base/objs/Process/Process.cpp index 1402e9e843..59303c17ad 100644 --- a/isis/src/base/objs/Process/Process.cpp +++ b/isis/src/base/objs/Process/Process.cpp @@ -277,6 +277,38 @@ namespace Isis { return SetOutputCube(parameter, ns, nl, nb); } + /** + * Allocates a user-specified output cube whose size matches the first input + * cube. + * + * @return Cube* + * + * @param parameter User specified output file. For example, "TO" is a popular + * user parameter. If the user specified TO=output.cub, then + * this routine would allocate the file output.cub with size + * specified by the first opened input cube. The output pixel + * type will be propagated from the first loaded input cube or + * will use the value in the application XML file for + * pixelType. + * + * @param ui A user interface used to get the attributes needed for SetOutputCube. + * + * @throws Isis::iException::Message + */ + Isis::Cube *Process::SetOutputCubeStretch(const QString ¶meter, UserInterface *ui) { + // Make sure we have an input cube to get a default size from + if(InputCubes.size() == 0) { + QString message = "No input images have been selected ... therefore"; + message += "the output image size can not be determined"; + throw IException(IException::Programmer, message, _FILEINFO_); + } + + int nl = InputCubes[0]->lineCount(); + int ns = InputCubes[0]->sampleCount(); + int nb = InputCubes[0]->bandCount(); + return SetOutputCubeStretch(parameter, ns, nl, nb, ui); + } + /** * Allocates a user specified output cube whose size is specified by the * programmer. @@ -308,11 +340,59 @@ namespace Isis { << ",nb=" << nb << "]"; throw IException(IException::Programmer, message.str().c_str(), _FILEINFO_); } - QString fname = Application::GetUserInterface().GetFileName(parameter); - Isis::CubeAttributeOutput &atts = Application::GetUserInterface().GetOutputAttribute(parameter); + QString fname; + Isis::CubeAttributeOutput atts; + fname = Application::GetUserInterface().GetFileName(parameter); + atts = Application::GetUserInterface().GetOutputAttribute(parameter); return SetOutputCube(fname, atts, ns, nl, nb); - } +} +/** + * Allocates a user specified output cube whose size is specified by the + * programmer. + * + * @return Cube* + * + * @param parameter User specified output file. For example, "TO" is a popular + * user parameter. If the user specified TO=output.cub, then + * this routine would allocate the file output.cub with size + * specified by the first opened input cube. The output pixel + * type will be propagated from the first loaded input cube or + * will use the value in the application XML file for + * pixelType. + * + * @param ns Number of samples to allocate + * + * @param nl Number of lines to allocate + * + * @param nb Number of bands to allocate + * + * @param ui A user interface used to get the attributes needed. If null, the + * user interface will be obtained from the application. + * + * @throws Isis::iException::Message + */ +Isis::Cube *Process::SetOutputCubeStretch(const QString ¶meter, const int ns, + const int nl, const int nb, UserInterface *ui) { + // Make sure we have good dimensions + if((ns <= 0) || (nl <= 0) || (nb <= 0)) { + ostringstream message; + message << "Invalid cube size specifications [ns=" << ns << ",nl=" << nl + << ",nb=" << nb << "]"; + throw IException(IException::Programmer, message.str().c_str(), _FILEINFO_); + } + QString fname; + Isis::CubeAttributeOutput atts; + if(ui==nullptr){ + fname = Application::GetUserInterface().GetFileName(parameter); + atts = Application::GetUserInterface().GetOutputAttribute(parameter); + } + else{ + fname = ui->GetFileName(parameter); + atts = ui->GetOutputAttribute(parameter); + } + return SetOutputCube(fname, atts, ns, nl, nb); +} /** * Allocates a output cube whose name and size is specified by the programmer. @@ -408,7 +488,7 @@ namespace Isis { // Allocate the cube cube->create(fname); - + // Transfer labels from the first input cube if((p_propagateLabels) && (InputCubes.size() > 0)) { Isis::PvlObject &incube = diff --git a/isis/src/base/objs/Process/Process.h b/isis/src/base/objs/Process/Process.h index 1f6dfcbc89..0316b69afc 100644 --- a/isis/src/base/objs/Process/Process.h +++ b/isis/src/base/objs/Process/Process.h @@ -241,8 +241,11 @@ namespace Isis { virtual Isis::Cube *SetOutputCube(const QString ¶meter); + virtual Isis::Cube *SetOutputCubeStretch(const QString ¶meter, UserInterface *ui=nullptr); virtual Isis::Cube *SetOutputCube(const QString ¶meter, const int nsamps, const int nlines, const int nbands = 1); + virtual Isis::Cube *SetOutputCubeStretch(const QString ¶meter, const int nsamps, + const int nlines, const int nbands = 1, UserInterface *ui=nullptr); virtual Isis::Cube *SetOutputCube(const QString &fname, const Isis::CubeAttributeOutput &att, const int nsamps, const int nlines, diff --git a/isis/src/base/objs/ProcessMosaic/ProcessMosaic.cpp b/isis/src/base/objs/ProcessMosaic/ProcessMosaic.cpp index 5bf4f09afd..dde83545e9 100644 --- a/isis/src/base/objs/ProcessMosaic/ProcessMosaic.cpp +++ b/isis/src/base/objs/ProcessMosaic/ProcessMosaic.cpp @@ -128,7 +128,7 @@ namespace Isis { bool bTrackExists = false; if (!m_createOutputMosaic) { bTrackExists = GetTrackStatus(); - if (m_trackingEnabled && + if (m_trackingEnabled && !(OutputCubes[0]->hasGroup("Tracking") || OutputCubes[0]->hasTable("InputImages"))) { QString m = "Cannot enable tracking while adding to a mosaic without tracking "; m += "information. Confirm that your mosaic was originally created with tracking enabled."; @@ -274,7 +274,7 @@ namespace Isis { bandPriorityInputBandNumber = GetBandIndex(true); bandPriorityOutputBandNumber = GetBandIndex(false); } - + // Set index of tracking image to the default offset of the Isis::UnsignedByte int iIndex = VALID_MINUI4; // Propogate tracking if adding to mosaic that was previouly tracked. @@ -283,7 +283,7 @@ namespace Isis { } // Create tracking cube if need-be, add bandbin group, and update tracking table. Add tracking - // group to mosaic cube. + // group to mosaic cube. if (m_trackingEnabled) { TrackingTable *trackingTable; @@ -303,7 +303,7 @@ namespace Isis { // The tracking cube file name convention is "output_cube_file_name" + "_tracking.cub" QString trackingBase = FileName(OutputCubes[0]->fileName()).removeExtension().expanded().split("/").last();//toString(); - m_trackingCube->create(FileName(OutputCubes[0]->fileName()).path() + m_trackingCube->create(FileName(OutputCubes[0]->fileName()).path() + "/" + trackingBase + "_tracking.cub"); // Add the tracking group to the mosaic cube label @@ -313,7 +313,7 @@ namespace Isis { trackingFileName.setValue(trackingBase + "_tracking.cub"); trackingFileGroup.addKeyword(trackingFileName); mosaicLabel->findObject("IsisCube").addGroup(trackingFileGroup); - + // Write the bandbin group to the tracking cube label Pvl *trackingLabel = m_trackingCube->label(); PvlGroup bandBin("BandBin"); @@ -325,7 +325,7 @@ namespace Isis { // Initialize an empty TrackingTable object to manage tracking table in tracking cube trackingTable = new TrackingTable(); } - + // An existing mosaic cube is being added to else { // Confirm tracking group exists in mosaic cube to address backwards compatibility @@ -354,11 +354,11 @@ namespace Isis { throw IException(IException::User, msg, _FILEINFO_); } } - + // Add current file to the TrackingTable object - iIndex = trackingTable->fileNameToPixel(InputCubes[0]->fileName(), + iIndex = trackingTable->fileNameToPixel(InputCubes[0]->fileName(), SerialNumber::Compose(*(InputCubes[0]))); - + // Write the tracking table to the tracking cube, overwriting if need-be if (m_trackingCube->hasTable(Isis::trackingTableName)) { m_trackingCube->deleteBlob("Table", Isis::trackingTableName); @@ -1308,7 +1308,7 @@ namespace Isis { bFound = true; } } - + //key name if (!m_bandPriorityBandNumber) { PvlKeyword cKeyName; @@ -1537,4 +1537,3 @@ void ProcessMosaic::BandPriorityWithNoTracking(int iss, int isl, int isb, int in } } - diff --git a/isis/src/base/objs/ProcessMosaic/ProcessMosaic.h b/isis/src/base/objs/ProcessMosaic/ProcessMosaic.h index 5837656874..2d11ab856a 100644 --- a/isis/src/base/objs/ProcessMosaic/ProcessMosaic.h +++ b/isis/src/base/objs/ProcessMosaic/ProcessMosaic.h @@ -192,17 +192,17 @@ namespace Isis { * @history 2017-05-19 Christopher Combs - Modified unitTest.cpp to truncate paths before date * directory. Allows test to pass when not using the default data area. * Fixes #4738. - * @history 2018-07-30 Jesse Mapel & Summer Stapleton - Refactoring of class to create a - * separate tracking cube to keep track of input images for a mosaic - * instead of storing this information within the mosaic cube itself. - * The mosaic cube no longer contains a tracking band or a tracking - * table; it now contains a tracking group containing the name of the - * tracking file. The tracking file is named - * _tracking.cub. This tracking cube will contain - * the tracking table as well as the tracking band; it will always be - * of PixelType::UnsignedInteger regardless of the pixel type of the + * @history 2018-07-30 Jesse Mapel & Summer Stapleton - Refactoring of class to create a + * separate tracking cube to keep track of input images for a mosaic + * instead of storing this information within the mosaic cube itself. + * The mosaic cube no longer contains a tracking band or a tracking + * table; it now contains a tracking group containing the name of the + * tracking file. The tracking file is named + * _tracking.cub. This tracking cube will contain + * the tracking table as well as the tracking band; it will always be + * of PixelType::UnsignedInteger regardless of the pixel type of the * mosaic cube or of the input images. References #971 - * @history 2018-08-13 Summer Stapleton - Error now being thrown with appropriate message if + * @history 2018-08-13 Summer Stapleton - Error now being thrown with appropriate message if * user attempts to add tracking capabilities to a mosaic that already * exists without tracking. Fixes #2052. */ diff --git a/isis/src/base/objs/UserInterface/UserInterface.cpp b/isis/src/base/objs/UserInterface/UserInterface.cpp index fdd7eeb7f7..c9dd938b37 100644 --- a/isis/src/base/objs/UserInterface/UserInterface.cpp +++ b/isis/src/base/objs/UserInterface/UserInterface.cpp @@ -394,7 +394,7 @@ namespace Isis { * This is used to load the command line into p_cmdline and the Aml object * using information contained in argc and argv. * - * @param args QVector of arguments + * @param args QVector of arguments * * @throws Isis::IException::User - Invalid value for reserve parameter * @throws Isis::IException::User - Invalid command line @@ -405,22 +405,22 @@ namespace Isis { */ void UserInterface::loadCommandLine(QVector &args, bool ignoreAppName) { char **c_args; - + if (ignoreAppName) { args.prepend("someapp"); } c_args = (char**)malloc(sizeof(char*)*args.size()); - - for (size_t i = 0; i < args.size(); i++) { + + for (int i = 0; i < args.size(); i++) { c_args[i] = (char*)malloc(sizeof(char)*args[i].size()+1); strcpy(c_args[i], args[i].toLatin1().data()); } loadCommandLine(args.size(), c_args); - } - - + } + + /** * This is used to load the command line into p_cmdline and the Aml object * using information contained in argc and argv. @@ -484,7 +484,7 @@ namespace Isis { vector paramValue; getNextParameter(currArgument, paramName, paramValue); - + // we now have a name,value pair if (paramName[0] == '-') { paramName = paramName.toUpper(); @@ -521,7 +521,7 @@ namespace Isis { } evaluateOption(paramName, realValue); - + continue; } diff --git a/isis/tests/FunctionalTestsStretch.cpp b/isis/tests/FunctionalTestsStretch.cpp new file mode 100644 index 0000000000..269f20e592 --- /dev/null +++ b/isis/tests/FunctionalTestsStretch.cpp @@ -0,0 +1,66 @@ +#include "Fixtures.h" +#include "Pvl.h" +#include "PvlGroup.h" +#include "TestUtilities.h" +#include "Histogram.h" +#include "UserInterface.h" + +#include "stretch_app.h" + +#include "gtest/gtest.h" + +using namespace Isis; + +static QString STRETCH_XML = FileName("$ISISROOT/bin/xml/stretch.xml").expanded(); + +// case 1 +TEST_F(SpecialSmallCube, FunctionalTestStretchDefault) { + QTemporaryDir prefix; + QString outCubeFileName = prefix.path() + "/outTemp.cub"; + QVector args = {"to="+outCubeFileName, "null=500", "lis=700", "lrs=800", "his=900", "hrs=1000"}; + QString pairs = "0:255 255:0"; + + UserInterface options(STRETCH_XML, args); + Pvl log; + try { + stretch(testCube, pairs, options, &log); + } + catch (IException &e) { + FAIL() << "Unable to open image: " << e.what() << std::endl; + } + + Cube oCube(outCubeFileName, "r"); + + Histogram *oCubeStats = oCube.histogram(); + + EXPECT_DOUBLE_EQ(oCubeStats->Average(), 505.25); + EXPECT_DOUBLE_EQ(oCubeStats->Sum(), 50525); + EXPECT_DOUBLE_EQ(oCubeStats->ValidPixels(), 100); + EXPECT_DOUBLE_EQ(oCubeStats->StandardDeviation(), 302.16673352386897); +} + +// case 2, changes other special pixels to other special pixels +TEST_F(SpecialSmallCube, FunctionalTestStretchSwitchSpecial) { + QTemporaryDir prefix; + QString outCubeFileName = prefix.path() + "/outTemp.cub"; + QVector args = {"to="+outCubeFileName, "null=hrs", "lis=NULL", "lrs=Lis", "his=lRs", "hrs=HiS"}; + QString pairs = "0:255 255:0"; + + UserInterface options(STRETCH_XML, args); + Pvl log; + try { + stretch(testCube, pairs, options, &log); + } + catch (IException &e) { + FAIL() << "Unable to open image: " << e.what() << std::endl; + } + + Cube oCube(outCubeFileName, "r"); + + Histogram *oCubeStats = oCube.histogram(); + + EXPECT_DOUBLE_EQ(oCubeStats->Average(), 230.5); + EXPECT_DOUBLE_EQ(oCubeStats->Sum(), 11525); + EXPECT_DOUBLE_EQ(oCubeStats->ValidPixels(), 50); + EXPECT_DOUBLE_EQ(oCubeStats->StandardDeviation(), 14.577379737113251); +}