Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Save and apply jigsaw adjustments #5419

Merged
merged 17 commits into from
Sep 20, 2024
Merged
Show file tree
Hide file tree
Changes from 10 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ release.
- Fixed gllssi2isis to support V1.1 data [#5396](https://github.com/DOI-USGS/ISIS3/issues/5396)

### Added
- Added option to save and apply bundle adjustment values in `jigsaw` [#4474](https://github.com/DOI-USGS/ISIS3/issues/4474)
- 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
Expand Down
1 change: 1 addition & 0 deletions environment.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ dependencies:
- graphviz
- conda-forge::gsl >=2.6, <2.7
- hdf5
- highfive
- icu
- inja
- jama
Expand Down
58 changes: 58 additions & 0 deletions isis/src/base/objs/Table/Table.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ find files of those names at the top level of this repository. **/
#include "Table.h"

#include <fstream>
#include <sstream>
#include <string>

#include "Blob.h"
Expand Down Expand Up @@ -137,6 +138,63 @@ namespace Isis {
}
}

/**
* This constructor takes in a string to create a Table object.
*
* @param tableName The name of the Table to be read
* @param tableStr The table string
* @param fieldDelimiter The delimiter to separate fields with
*/
Table::Table(const QString &tableName, const std::string &tableString, const char &fieldDelimiter) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

I think if we are reading in Jigsaw results we should be able to use the ISIS CSVReader class instead of implementing the reader in Table. I could be missing something about our CSV Reader though.

Something to try when we get back to this

Copy link
Collaborator

Choose a reason for hiding this comment

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

CSVReader was kind of a mess. I dont mind re-visiting trying it, originally, we wanted to just write out the blobs using with something like Table -> toBlob -> write(stringstream) -> write to HDF5. But that also was a mess. If we revisit I think trying the blob route would be best. She was able to get it work with a quick ghetto method than I was trying to de-tangle Blob writing with streams. Only spent a day on it, but I was getting a bunch of segfaults and stream errors for whatever reason.

p_name = tableName;

std::stringstream tableStream;
tableStream << tableString;

std::vector<std::string> tableLinesStringList;
std::string line;
while(std::getline(tableStream, line, '\n')) {
tableLinesStringList.push_back(line);
}

int numOfFieldValues = tableLinesStringList.size() - 1; // minus the header line

std::string fieldNamesLineString = tableLinesStringList.front();
std::stringstream fieldNamesStringStream;
fieldNamesStringStream << fieldNamesLineString;

std::vector<QString> fieldNames;
std::string fieldNameString;
while(std::getline(fieldNamesStringStream, fieldNameString, fieldDelimiter)) {
fieldNames.push_back(QString::fromStdString(fieldNameString));
}

// Clear error flags and set pointer back to beginning
tableStream.clear();
tableStream.seekg(0, ios::beg);

// Add records to table
std::string recordString;
int index = 0;
while(std::getline(tableStream, recordString, '\n')) {
// skip first line bc that's the header line
if (index == 0) {
index++;
continue;
}

TableRecord tableRecord(recordString, fieldDelimiter, fieldNames, numOfFieldValues);
p_record = tableRecord;
this->operator+=(tableRecord);
index++;
}

// Add fields
for (int f = 0; f < p_record.Fields(); f++) {
p_label.addGroup(p_record[f].pvlGroup());
}
}


/**
* Initialize a Table from a Blob that has been read from a file.
Expand Down
1 change: 1 addition & 0 deletions isis/src/base/objs/Table/Table.h
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ namespace Isis {
Table(const QString &tableName, const QString &file,
const Pvl &fileHeader);
Table(const Table &other);
Table(const QString &tableName, const std::string &tableString, const char &fieldDelimiter);
Table &operator=(const Isis::Table &other);

~Table();
Expand Down
26 changes: 25 additions & 1 deletion isis/src/base/objs/TableRecord/TableRecord.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ find files of those names at the top level of this repository. **/
#include "TableRecord.h"

#include <iostream>
#include <sstream>
#include <string>
#include <vector>

Expand All @@ -21,6 +22,29 @@ namespace Isis {
TableRecord::TableRecord(){
}

/**
* TableRecord constructor
*
* @param tableRecordStr Table record string
* @param fieldDelimiter The delimiter to separate fields with
* @param fieldNames Table header names
* @param numOfFieldValues Number of fields (rows)
*/
TableRecord::TableRecord(std::string tableRecordStr, char fieldDelimiter,
std::vector<QString> fieldNames, int numOfFieldValues) {
std::stringstream tableRecordStream;
tableRecordStream << tableRecordStr;

std::string fieldStr;
int i = 0;
while(std::getline(tableRecordStream, fieldStr, fieldDelimiter)) {
TableField tableField(fieldNames[i], TableField::Double);
tableField = std::stod(fieldStr); // convert string to double
this->operator+=(tableField);
i++;
}
}

//! Destroys the TableRecord object
TableRecord::~TableRecord() {
}
Expand Down Expand Up @@ -155,7 +179,7 @@ namespace Isis {
Isis::TableField &field = p_fields[f];
field = (void *)&buf[sbyte];
sbyte += field.bytes();
}
}
}

/**
Expand Down
2 changes: 2 additions & 0 deletions isis/src/base/objs/TableRecord/TableRecord.h
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ namespace Isis {
class TableRecord {
public:
TableRecord();
TableRecord(std::string tableRecordStr, char fieldDelimiter,
std::vector<QString> fieldNames, int numOfFieldValues);
~TableRecord();


Expand Down
119 changes: 103 additions & 16 deletions isis/src/control/apps/jigsaw/jigsaw.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,19 @@ find files of those names at the top level of this repository. **/
/* SPDX-License-Identifier: CC0-1.0 */

#include <iostream>
#include <highfive/H5File.hpp>
#include <highfive/H5DataType.hpp>
#include <highfive/H5DataSet.hpp>
#include <highfive/H5Group.hpp>
#include <vector>

#include <QDir>
#include <QList>
#include <QObject>
#include <QSharedPointer>
#include <QString>


#include "Blob.h"
#include "BundleAdjust.h"
#include "BundleObservationSolveSettings.h"
Expand All @@ -35,6 +41,8 @@ find files of those names at the top level of this repository. **/
#include "jigsaw.h"

using namespace std;
using namespace HighFive;


namespace Isis {

Expand Down Expand Up @@ -130,23 +138,23 @@ namespace Isis {
bundleSolution->outputResiduals();
}

// write lidar csv output file
if (ui.GetBoolean("LIDAR_CSV")) {
bundleSolution->outputLidarCSV();
}
// write lidar csv output file
if (ui.GetBoolean("LIDAR_CSV")) {
bundleSolution->outputLidarCSV();
}

// write updated control net
bundleAdjustment->controlNet()->Write(ui.GetFileName("ONET"));

// write updated lidar data file
if (ui.WasEntered("LIDARDATA")) {
if (ui.GetString("OLIDARFORMAT") == "JSON") {
bundleAdjustment->lidarData()->write(ui.GetFileName("OLIDARDATA"),LidarData::Format::Json);
}
else {
bundleAdjustment->lidarData()->write(ui.GetFileName("OLIDARDATA"),LidarData::Format::Binary);
// write updated lidar data file
if (ui.WasEntered("LIDARDATA")) {
if (ui.GetString("OLIDARFORMAT") == "JSON") {
bundleAdjustment->lidarData()->write(ui.GetFileName("OLIDARDATA"),LidarData::Format::Json);
}
else {
bundleAdjustment->lidarData()->write(ui.GetFileName("OLIDARDATA"),LidarData::Format::Binary);
}
}
}
PvlGroup gp("JigsawResults");
// Update the cube pointing if requested but ONLY if bundle has converged
if (ui.GetBoolean("UPDATE") ) {
Expand All @@ -156,6 +164,29 @@ namespace Isis {
throw IException(IException::Unknown, msg, _FILEINFO_);
}
else {

// Update the image parameters
QString jigComment = "Jigged = " + Isis::iTime::CurrentLocalTime();

// Need to prep the adjustment file first
QString adjustmentType;
unsigned adjustmentFileEnum = 0;
QString adjustmentFilename;
bool placeholderAdjustmentFileExists = false;
if (ui.WasEntered("ADJUSTMENT_OUTPUT")) {
adjustmentFilename = ui.GetFileName("ADJUSTMENT_OUTPUT");
adjustmentFileEnum = File::Truncate;
} else if (ui.WasEntered("ADJUSTMENT_INPUT")) {
adjustmentFilename = ui.GetFileName("ADJUSTMENT_INPUT");
adjustmentFileEnum = File::ReadOnly;
} else {
placeholderAdjustmentFileExists = true;
adjustmentFilename = "/tmp/placeholder_adj.tmp";
adjustmentFileEnum = File::Create;
}
File file(adjustmentFilename.toStdString(), adjustmentFileEnum);

// Loop through images
for (int i = 0; i < bundleAdjustment->numberOfImages(); i++) {
Process p;
CubeAttributeInput inAtt;
Expand All @@ -174,8 +205,6 @@ namespace Isis {
break;
}

// Update the image parameters
QString jigComment = "Jigged = " + Isis::iTime::CurrentLocalTime();
if (c->hasBlob("CSMState", "String")) {
Blob csmStateBlob("CSMState", "String");
// Read the BLOB from the cube to propagate things like the model
Expand All @@ -185,17 +214,75 @@ namespace Isis {
csmStateBlob.setData(modelState.c_str(), modelState.size());
csmStateBlob.Label().addComment(jigComment);
c->write(csmStateBlob);
}
else {
} else if (ui.WasEntered("ADJUSTMENT_OUTPUT")) {
// Write bundle adjustment values to cube
Table cmatrix = bundleAdjustment->cMatrix(i);
cmatrix.Label().addComment(jigComment);
Table spvector = bundleAdjustment->spVector(i);
spvector.Label().addComment(jigComment);
c->write(cmatrix);
c->write(spvector);

QString serialNumber = bundleAdjustment->serialNumberList()->serialNumber(i);
QString cmatrixName = cmatrix.Name();
QString spvectorName = spvector.Name();

std::string cmatrixKey = serialNumber.toStdString() + "/" + cmatrixName.toStdString();
std::string spvectorKey = serialNumber.toStdString() + "/" + spvectorName.toStdString();

// Save bundle adjustment values to HDF5 file
std::string cmatrixTableStr = Table::toString(cmatrix).toStdString();
DataSet dataset = file.createDataSet<std::string>(cmatrixKey, cmatrixTableStr);
std::string spvectorTableStr = Table::toString(spvector).toStdString();
dataset = file.createDataSet<std::string>(spvectorKey, spvectorTableStr);
} else if (ui.WasEntered("ADJUSTMENT_INPUT")) {
Table cmatrix = bundleAdjustment->cMatrix(i);
Table spvector = bundleAdjustment->spVector(i);

QString serialNumber = bundleAdjustment->serialNumberList()->serialNumber(i);
QString cmatrixName = cmatrix.Name();
QString spvectorName = spvector.Name();

std::string cmatrixKey = serialNumber.toStdString() + "/" + cmatrixName.toStdString();
std::string spvectorKey = serialNumber.toStdString() + "/" + spvectorName.toStdString();

// Read h5 into table
DataSet datasetRead = file.getDataSet(cmatrixKey);
auto cmatrixData = datasetRead.read<std::string>();
Table cmatrixTable(cmatrixName, cmatrixData, ',');

datasetRead = file.getDataSet(spvectorKey);
auto spvectorData = datasetRead.read<std::string>();
Table spvectorTable(spvectorName, spvectorData, ',');

// Write bundle adjustment values out
cmatrixTable.Label().addComment(jigComment);
c->write(cmatrixTable);
spvectorTable.Label().addComment(jigComment);
c->write(spvectorTable);
} else {
std::cout << "ADJUSTMENT ERROR????" << std::endl;
gp += PvlKeyword("Status","If UPDATE=True then must specify either ADJUSTMENT_OUTPUT or ADJUSTMENT_INPUT or must be csminit'd.");
QString msg = "If UPDATE=True, must run csminit on cubes or specify either ADJUSTMENT_OUTPUT or ADJUSTMENT_INPUT.";
throw IException(IException::Unknown, msg, _FILEINFO_);
}
p.WriteHistory(*c);
}

// Check if temp adjustment file was created
// Delete if so
if (placeholderAdjustmentFileExists) {
int status = remove(adjustmentFilename.toStdString().c_str());
if (status != 0) {
PvlGroup tempAdjustmentFileWarning("TemporaryAdjustmentFileWarning");
tempAdjustmentFileWarning.addKeyword(PvlKeyword("Warning", "The placeholder adjustment file \
" + adjustmentFilename + "was \
not removed properly."));
if(log) {
log->addLogGroup(tempAdjustmentFileWarning);
}
}
}
gp += PvlKeyword("Status", "Camera pointing updated");
}
}
Expand Down
34 changes: 33 additions & 1 deletion isis/src/control/apps/jigsaw/jigsaw.xml
Original file line number Diff line number Diff line change
Expand Up @@ -885,7 +885,9 @@
from the solution if the adjustment converges. The results are written
to the <def>SPICE</def> blobs attached to the cube, overwriting the previous
values. If this option is not selected, the <def>cube</def> files are not
changed. All other output files are still created.
changed. All other output files are still created. Unless the cubes have a
CSMState blob attached, must either define ADJUSTMENT_OUTPUT or ADJUSTMENT_INPUT
if UPDATE is true.
</description>
<type>boolean</type>
<default>
Expand Down Expand Up @@ -1835,6 +1837,36 @@
<item>no</item>
</default>
</parameter>

<parameter name="ADJUSTMENT_OUTPUT">
<type>filename</type>
<internalDefault>none</internalDefault>
<fileMode>input</fileMode>
<brief>
Outputs adjustment values
</brief>
<description>
"Outputs adjustment values"
</description>
<filter>
*.h5
</filter>
</parameter>

<parameter name="ADJUSTMENT_INPUT">
<type>filename</type>
<internalDefault>none</internalDefault>
<fileMode>input</fileMode>
<brief>
Reads in adjustment values
</brief>
<description>
"Reads in adjustment values"
</description>
<filter>
*.h5
</filter>
</parameter>
</group>

</groups>
Expand Down
Loading