Skip to content

Commit

Permalink
Merge pull request #2220 from SCIInstitute/2218-scalar-evaluation
Browse files Browse the repository at this point in the history
#2218 scalar evaluation
  • Loading branch information
akenmorris committed Mar 28, 2024
2 parents 15e5f4c + 852496d commit 6b25879
Show file tree
Hide file tree
Showing 14 changed files with 216 additions and 118 deletions.
2 changes: 1 addition & 1 deletion Libs/Analyze/StudioMesh.cpp
Expand Up @@ -98,7 +98,7 @@ void StudioMesh::interpolate_scalars_to_mesh(std::string name, Eigen::VectorXd p

if (num_points != scalar_values.size()) {
std::cerr << "Warning, mismatch of points and scalar values (num_points = " << num_points
<< ", scalar_values.size() = " << scalar_values.size() << std::endl;
<< ", scalar_values.size() = " << scalar_values.size() << ")\n";
return;
}

Expand Down
42 changes: 22 additions & 20 deletions Libs/Particles/ParticleSystemEvaluation.cpp
Expand Up @@ -7,57 +7,56 @@

namespace shapeworks {

ParticleSystemEvaluation::ParticleSystemEvaluation(const std::vector<std::string>& _paths) {
if (_paths.empty()) {
//---------------------------------------------------------------------------
ParticleSystemEvaluation::ParticleSystemEvaluation(const std::vector<std::string>& paths) {
if (paths.empty()) {
throw std::runtime_error("No filenames passed to readParticleSystemEvaluation");
}

this->paths = _paths;
const int N = paths.size();
paths_ = paths;
const int N = paths_.size();
const int VDimension = 3;
assert(N > 0);

// TODO: We're using the existing particle file reader here. This is not ideal
// since this particle reader loads into a std::vector, which subsequently
// is copied to Eigen. Refactor it to load directly to Eigen. (This is not a
// huge problem for now because the particle files are quite small)

// Read the first file to find dimensions
auto points0 = particles::read_particles_as_vector(paths[0]);
auto points0 = particles::read_particles_as_vector(paths_[0]);
const int D = points0.size() * VDimension;

P.resize(D, N);
P.col(0) = Eigen::Map<const Eigen::VectorXd>((double*)points0.data(), D);
matrix_.resize(D, N);
matrix_.col(0) = Eigen::Map<const Eigen::VectorXd>((double*)points0.data(), D);

for (int i = 1; i < N; i++) {
auto points = particles::read_particles_as_vector(paths[i]);
auto points = particles::read_particles_as_vector(paths_[i]);
int count = points.size() * VDimension;
if (count != D) {
throw std::runtime_error("ParticleSystemEvaluation files must have the same number of particles");
}
P.col(i) = Eigen::Map<const Eigen::VectorXd>((double*)points.data(), D);
matrix_.col(i) = Eigen::Map<const Eigen::VectorXd>((double*)points.data(), D);
}
}

ParticleSystemEvaluation::ParticleSystemEvaluation(const Eigen::MatrixXd& matrix) { this->P = matrix; }
//---------------------------------------------------------------------------
ParticleSystemEvaluation::ParticleSystemEvaluation(const Eigen::MatrixXd& matrix) { matrix_ = matrix; }

//---------------------------------------------------------------------------
bool ParticleSystemEvaluation::ExactCompare(const ParticleSystemEvaluation& other) const {
if (P.rows() != other.P.rows() || P.cols() != other.P.cols()) {
if (matrix_.rows() != other.matrix_.rows() || matrix_.cols() != other.matrix_.cols()) {
std::cerr << "Rows/Columns mismatch\n";
return false;
}
bool same = true;
for (int r = 0; r < P.rows(); r++) {
for (int c = 0; c < P.cols(); c++) {
if (!epsEqual(P(r, c), other.P(r, c), 0.001)) {
std::cerr << "(" << r << "," << c << "): " << P(r, c) << " vs " << other.P(r, c) << "\n";
for (int r = 0; r < matrix_.rows(); r++) {
for (int c = 0; c < matrix_.cols(); c++) {
if (!epsEqual(matrix_(r, c), other.matrix_(r, c), 0.001)) {
std::cerr << "(" << r << "," << c << "): " << matrix_(r, c) << " vs " << other.matrix_(r, c) << "\n";
same = false;
}
}
}
return same;
}

//---------------------------------------------------------------------------
bool ParticleSystemEvaluation::EvaluationCompare(const ParticleSystemEvaluation& other) const {
auto compactness1 = ShapeEvaluation::ComputeFullCompactness(*this);
auto compactness2 = ShapeEvaluation::ComputeFullCompactness(other);
Expand Down Expand Up @@ -113,9 +112,12 @@ bool ParticleSystemEvaluation::EvaluationCompare(const ParticleSystemEvaluation&
return good;
}

//---------------------------------------------------------------------------
bool ParticleSystemEvaluation::ReadParticleFile(std::string filename, Eigen::VectorXd& points) {
points = particles::read_particles(filename);
return true;
}

//---------------------------------------------------------------------------

} // namespace shapeworks
12 changes: 6 additions & 6 deletions Libs/Particles/ParticleSystemEvaluation.h
Expand Up @@ -12,15 +12,15 @@ class ParticleSystemEvaluation {
// Initialize particle system from eigen matrix (rows=dimensions, cols=num_samples)
ParticleSystemEvaluation(const Eigen::MatrixXd& matrix);

const Eigen::MatrixXd& Particles() const { return P; };
const Eigen::MatrixXd& Particles() const { return matrix_; };

const std::vector<std::string>& Paths() const { return paths; }
const std::vector<std::string>& Paths() const { return paths_; }

//! Number of samples
int N() const { return P.cols(); }
int N() const { return matrix_.cols(); }

//! Dimensions (e.g. x/y/z * number of particles)
int D() const { return P.rows(); }
int D() const { return matrix_.rows(); }

bool ExactCompare(const ParticleSystemEvaluation& other) const;

Expand All @@ -34,7 +34,7 @@ class ParticleSystemEvaluation {
ParticleSystemEvaluation() {
} // only for use by SharedCommandData since a ParticleSystem should always be valid, never "empty"

Eigen::MatrixXd P;
std::vector<std::string> paths;
Eigen::MatrixXd matrix_;
std::vector<std::string> paths_;
};
} // namespace shapeworks
3 changes: 1 addition & 2 deletions Libs/Particles/ShapeEvaluation.cpp
Expand Up @@ -2,8 +2,7 @@

#include <Eigen/Core>
#include <Eigen/SVD>
#include <iostream>

#include <fstream>
#include "EvaluationUtil.h"

typedef Eigen::Matrix<double, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor> RowMajorMatrix;
Expand Down
1 change: 0 additions & 1 deletion Libs/Particles/ShapeEvaluation.h
Expand Up @@ -3,7 +3,6 @@
#include <Eigen/Core>
#include <string>

#include "ParticleShapeStatistics.h"
#include "ParticleSystemEvaluation.h"

namespace shapeworks {
Expand Down
63 changes: 63 additions & 0 deletions Studio/Analysis/AnalysisTool.cpp
Expand Up @@ -161,6 +161,23 @@ AnalysisTool::AnalysisTool(Preferences& prefs) : preferences_(prefs) {

connect(ui_->group_analysis_combo, qOverload<int>(&QComboBox::currentIndexChanged), this,
&AnalysisTool::group_analysis_combo_changed);

// when one is checked, turn the other off, connect these first
connect(ui_->show_difference_to_predicted_scalar, &QPushButton::clicked, this, [this]() {
if (ui_->show_difference_to_predicted_scalar->isChecked()) {
ui_->show_predicted_scalar->setChecked(false);
}
});
connect(ui_->show_predicted_scalar, &QPushButton::clicked, this, [this]() {
if (ui_->show_predicted_scalar->isChecked()) {
ui_->show_difference_to_predicted_scalar->setChecked(false);
}
});

connect(ui_->show_difference_to_predicted_scalar, &QPushButton::clicked, this,
&AnalysisTool::handle_samples_predicted_scalar_options);
connect(ui_->show_predicted_scalar, &QPushButton::clicked, this,
&AnalysisTool::handle_samples_predicted_scalar_options);
}

//---------------------------------------------------------------------------
Expand Down Expand Up @@ -280,6 +297,9 @@ void AnalysisTool::set_session(QSharedPointer<Session> session) {
ui_->group2_button->setChecked(false);
update_difference_particles();

ui_->show_predicted_scalar->setChecked(false);
ui_->show_difference_to_predicted_scalar->setChecked(false);

connect(ui_->show_good_bad, &QCheckBox::toggled, session_.data(), &Session::set_show_good_bad_particles);
}

Expand Down Expand Up @@ -1091,6 +1111,11 @@ void AnalysisTool::reset_stats() {
ui_->pca_scalar_combo->addItem(QString::fromStdString(feature));
}
}
bool has_scalars = ui_->pca_scalar_combo->count() > 0;
if (!has_scalars) {
ui_->show_difference_to_predicted_scalar->setChecked(false);
}
ui_->show_difference_to_predicted_scalar->setEnabled(has_scalars);

ui_->shape_scalar_groupbox->setVisible(ui_->pca_scalar_combo->count() > 0);
}
Expand Down Expand Up @@ -1303,6 +1328,15 @@ std::string AnalysisTool::get_display_feature_map() {
}
}

if (get_analysis_mode() == AnalysisTool::MODE_ALL_SAMPLES_C &&
ui_->show_difference_to_predicted_scalar->isChecked()) {
return "predicted_scalar_diff";
}

if (get_analysis_mode() == AnalysisTool::MODE_ALL_SAMPLES_C && ui_->show_predicted_scalar->isChecked()) {
return "predicted_scalar";
}

return feature_map_;
}

Expand Down Expand Up @@ -1763,6 +1797,11 @@ void AnalysisTool::change_pca_analysis_type() {
ui_->pca_predict_scalar->setEnabled(ui_->pca_scalar_shape_only->isChecked());
ui_->pca_predict_shape->setEnabled(ui_->pca_scalar_only->isChecked());

if (ui_->pca_predict_scalar->isChecked()) {
// set the feature map to the target feature
session_->set_feature_map(ui_->pca_scalar_combo->currentText().toStdString());
}

compute_stats();
Q_EMIT pca_update();
}
Expand All @@ -1785,6 +1824,30 @@ Eigen::VectorXd AnalysisTool::construct_mean_shape() {
return mean_shape;
}

//---------------------------------------------------------------------------
void AnalysisTool::handle_samples_predicted_scalar_options() {
if (ui_->show_difference_to_predicted_scalar->isChecked() || ui_->show_predicted_scalar->isChecked()) {
// iterate over all samples, predict scalars, compute the difference and store as a new scalar field
auto shapes = session_->get_non_excluded_shapes();
for (auto& shape : shapes) {
auto particles = shape->get_particles();
auto target_feature = ui_->pca_scalar_combo->currentText();
auto predicted =
ShapeScalarJob::predict_scalars(session_, target_feature, particles.get_combined_global_particles());

// load the mesh and feature
shape->get_meshes(session_->get_display_mode(), true);
shape->load_feature(session_->get_display_mode(), target_feature.toStdString());
auto scalars = shape->get_point_features(target_feature.toStdString());
// compute difference
auto diff = predicted - scalars;
shape->set_point_features("predicted_scalar_diff", diff);
shape->set_point_features("predicted_scalar", predicted);
}
}
Q_EMIT update_view();
}

//---------------------------------------------------------------------------
void AnalysisTool::reconstruction_method_changed() {
ui_->reconstruction_options->setVisible(ui_->distance_transform_radio_button->isChecked());
Expand Down
2 changes: 2 additions & 0 deletions Studio/Analysis/AnalysisTool.h
Expand Up @@ -197,6 +197,8 @@ class AnalysisTool : public QWidget {
//! Compute the mean shape outside of the PCA in case we are using scalars only
Eigen::VectorXd construct_mean_shape();

void handle_samples_predicted_scalar_options();

Q_SIGNALS:

void update_view();
Expand Down
80 changes: 63 additions & 17 deletions Studio/Analysis/AnalysisTool.ui
Expand Up @@ -535,7 +535,7 @@ QWidget#particles_panel {
<item row="3" column="0" colspan="2">
<widget class="QTabWidget" name="tabWidget">
<property name="currentIndex">
<number>2</number>
<number>1</number>
</property>
<widget class="QWidget" name="mean_tab">
<attribute name="title">
Expand Down Expand Up @@ -1089,16 +1089,6 @@ QWidget#particles_panel {
<string>Samples</string>
</attribute>
<layout class="QGridLayout" name="gridLayout_4">
<item row="1" column="1">
<widget class="QSpinBox" name="sampleSpinBox">
<property name="enabled">
<bool>false</bool>
</property>
<property name="toolTip">
<string>Sample selection</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QRadioButton" name="singleSamplesRadio">
<property name="toolTip">
Expand All @@ -1109,6 +1099,19 @@ QWidget#particles_panel {
</property>
</widget>
</item>
<item row="2" column="0" colspan="2">
<widget class="QPushButton" name="medianButton">
<property name="enabled">
<bool>false</bool>
</property>
<property name="toolTip">
<string>Show median sample</string>
</property>
<property name="text">
<string>Median</string>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QRadioButton" name="allSamplesRadio">
<property name="toolTip">
Expand All @@ -1122,19 +1125,62 @@ QWidget#particles_panel {
</property>
</widget>
</item>
<item row="2" column="0" colspan="2">
<widget class="QPushButton" name="medianButton">
<item row="3" column="0" colspan="2">
<widget class="QWidget" name="predict_diff_widget" native="true">
<layout class="QGridLayout" name="gridLayout_28">
<item row="0" column="0">
<widget class="QCheckBox" name="show_difference_to_predicted_scalar">
<property name="text">
<string>Show difference to predicted scalar</string>
</property>
</widget>
</item>
<item row="0" column="1">
<spacer name="horizontalSpacer_9">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item row="1" column="0">
<widget class="QCheckBox" name="show_predicted_scalar">
<property name="text">
<string>Show predicted scalar</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item row="1" column="1">
<widget class="QSpinBox" name="sampleSpinBox">
<property name="enabled">
<bool>false</bool>
</property>
<property name="toolTip">
<string>Show median sample</string>
</property>
<property name="text">
<string>Median</string>
<string>Sample selection</string>
</property>
</widget>
</item>
<item row="4" column="0">
<spacer name="verticalSpacer_3">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
<widget class="QWidget" name="pca_tab">
Expand Down

0 comments on commit 6b25879

Please sign in to comment.