Skip to content

Commit 6b25879

Browse files
authored
Merge pull request #2220 from SCIInstitute/2218-scalar-evaluation
#2218 scalar evaluation
2 parents 15e5f4c + 852496d commit 6b25879

14 files changed

+216
-118
lines changed

Libs/Analyze/StudioMesh.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@ void StudioMesh::interpolate_scalars_to_mesh(std::string name, Eigen::VectorXd p
9898

9999
if (num_points != scalar_values.size()) {
100100
std::cerr << "Warning, mismatch of points and scalar values (num_points = " << num_points
101-
<< ", scalar_values.size() = " << scalar_values.size() << std::endl;
101+
<< ", scalar_values.size() = " << scalar_values.size() << ")\n";
102102
return;
103103
}
104104

Libs/Particles/ParticleSystemEvaluation.cpp

Lines changed: 22 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -7,57 +7,56 @@
77

88
namespace shapeworks {
99

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

15-
this->paths = _paths;
16-
const int N = paths.size();
16+
paths_ = paths;
17+
const int N = paths_.size();
1718
const int VDimension = 3;
1819
assert(N > 0);
1920

20-
// TODO: We're using the existing particle file reader here. This is not ideal
21-
// since this particle reader loads into a std::vector, which subsequently
22-
// is copied to Eigen. Refactor it to load directly to Eigen. (This is not a
23-
// huge problem for now because the particle files are quite small)
24-
2521
// Read the first file to find dimensions
26-
auto points0 = particles::read_particles_as_vector(paths[0]);
22+
auto points0 = particles::read_particles_as_vector(paths_[0]);
2723
const int D = points0.size() * VDimension;
2824

29-
P.resize(D, N);
30-
P.col(0) = Eigen::Map<const Eigen::VectorXd>((double*)points0.data(), D);
25+
matrix_.resize(D, N);
26+
matrix_.col(0) = Eigen::Map<const Eigen::VectorXd>((double*)points0.data(), D);
3127

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

42-
ParticleSystemEvaluation::ParticleSystemEvaluation(const Eigen::MatrixXd& matrix) { this->P = matrix; }
38+
//---------------------------------------------------------------------------
39+
ParticleSystemEvaluation::ParticleSystemEvaluation(const Eigen::MatrixXd& matrix) { matrix_ = matrix; }
4340

41+
//---------------------------------------------------------------------------
4442
bool ParticleSystemEvaluation::ExactCompare(const ParticleSystemEvaluation& other) const {
45-
if (P.rows() != other.P.rows() || P.cols() != other.P.cols()) {
43+
if (matrix_.rows() != other.matrix_.rows() || matrix_.cols() != other.matrix_.cols()) {
4644
std::cerr << "Rows/Columns mismatch\n";
4745
return false;
4846
}
4947
bool same = true;
50-
for (int r = 0; r < P.rows(); r++) {
51-
for (int c = 0; c < P.cols(); c++) {
52-
if (!epsEqual(P(r, c), other.P(r, c), 0.001)) {
53-
std::cerr << "(" << r << "," << c << "): " << P(r, c) << " vs " << other.P(r, c) << "\n";
48+
for (int r = 0; r < matrix_.rows(); r++) {
49+
for (int c = 0; c < matrix_.cols(); c++) {
50+
if (!epsEqual(matrix_(r, c), other.matrix_(r, c), 0.001)) {
51+
std::cerr << "(" << r << "," << c << "): " << matrix_(r, c) << " vs " << other.matrix_(r, c) << "\n";
5452
same = false;
5553
}
5654
}
5755
}
5856
return same;
5957
}
6058

59+
//---------------------------------------------------------------------------
6160
bool ParticleSystemEvaluation::EvaluationCompare(const ParticleSystemEvaluation& other) const {
6261
auto compactness1 = ShapeEvaluation::ComputeFullCompactness(*this);
6362
auto compactness2 = ShapeEvaluation::ComputeFullCompactness(other);
@@ -113,9 +112,12 @@ bool ParticleSystemEvaluation::EvaluationCompare(const ParticleSystemEvaluation&
113112
return good;
114113
}
115114

115+
//---------------------------------------------------------------------------
116116
bool ParticleSystemEvaluation::ReadParticleFile(std::string filename, Eigen::VectorXd& points) {
117117
points = particles::read_particles(filename);
118118
return true;
119119
}
120120

121+
//---------------------------------------------------------------------------
122+
121123
} // namespace shapeworks

Libs/Particles/ParticleSystemEvaluation.h

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,15 +12,15 @@ class ParticleSystemEvaluation {
1212
// Initialize particle system from eigen matrix (rows=dimensions, cols=num_samples)
1313
ParticleSystemEvaluation(const Eigen::MatrixXd& matrix);
1414

15-
const Eigen::MatrixXd& Particles() const { return P; };
15+
const Eigen::MatrixXd& Particles() const { return matrix_; };
1616

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

1919
//! Number of samples
20-
int N() const { return P.cols(); }
20+
int N() const { return matrix_.cols(); }
2121

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

2525
bool ExactCompare(const ParticleSystemEvaluation& other) const;
2626

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

37-
Eigen::MatrixXd P;
38-
std::vector<std::string> paths;
37+
Eigen::MatrixXd matrix_;
38+
std::vector<std::string> paths_;
3939
};
4040
} // namespace shapeworks

Libs/Particles/ShapeEvaluation.cpp

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,7 @@
22

33
#include <Eigen/Core>
44
#include <Eigen/SVD>
5-
#include <iostream>
6-
5+
#include <fstream>
76
#include "EvaluationUtil.h"
87

98
typedef Eigen::Matrix<double, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor> RowMajorMatrix;

Libs/Particles/ShapeEvaluation.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
#include <Eigen/Core>
44
#include <string>
55

6-
#include "ParticleShapeStatistics.h"
76
#include "ParticleSystemEvaluation.h"
87

98
namespace shapeworks {

Studio/Analysis/AnalysisTool.cpp

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,23 @@ AnalysisTool::AnalysisTool(Preferences& prefs) : preferences_(prefs) {
161161

162162
connect(ui_->group_analysis_combo, qOverload<int>(&QComboBox::currentIndexChanged), this,
163163
&AnalysisTool::group_analysis_combo_changed);
164+
165+
// when one is checked, turn the other off, connect these first
166+
connect(ui_->show_difference_to_predicted_scalar, &QPushButton::clicked, this, [this]() {
167+
if (ui_->show_difference_to_predicted_scalar->isChecked()) {
168+
ui_->show_predicted_scalar->setChecked(false);
169+
}
170+
});
171+
connect(ui_->show_predicted_scalar, &QPushButton::clicked, this, [this]() {
172+
if (ui_->show_predicted_scalar->isChecked()) {
173+
ui_->show_difference_to_predicted_scalar->setChecked(false);
174+
}
175+
});
176+
177+
connect(ui_->show_difference_to_predicted_scalar, &QPushButton::clicked, this,
178+
&AnalysisTool::handle_samples_predicted_scalar_options);
179+
connect(ui_->show_predicted_scalar, &QPushButton::clicked, this,
180+
&AnalysisTool::handle_samples_predicted_scalar_options);
164181
}
165182

166183
//---------------------------------------------------------------------------
@@ -280,6 +297,9 @@ void AnalysisTool::set_session(QSharedPointer<Session> session) {
280297
ui_->group2_button->setChecked(false);
281298
update_difference_particles();
282299

300+
ui_->show_predicted_scalar->setChecked(false);
301+
ui_->show_difference_to_predicted_scalar->setChecked(false);
302+
283303
connect(ui_->show_good_bad, &QCheckBox::toggled, session_.data(), &Session::set_show_good_bad_particles);
284304
}
285305

@@ -1091,6 +1111,11 @@ void AnalysisTool::reset_stats() {
10911111
ui_->pca_scalar_combo->addItem(QString::fromStdString(feature));
10921112
}
10931113
}
1114+
bool has_scalars = ui_->pca_scalar_combo->count() > 0;
1115+
if (!has_scalars) {
1116+
ui_->show_difference_to_predicted_scalar->setChecked(false);
1117+
}
1118+
ui_->show_difference_to_predicted_scalar->setEnabled(has_scalars);
10941119

10951120
ui_->shape_scalar_groupbox->setVisible(ui_->pca_scalar_combo->count() > 0);
10961121
}
@@ -1303,6 +1328,15 @@ std::string AnalysisTool::get_display_feature_map() {
13031328
}
13041329
}
13051330

1331+
if (get_analysis_mode() == AnalysisTool::MODE_ALL_SAMPLES_C &&
1332+
ui_->show_difference_to_predicted_scalar->isChecked()) {
1333+
return "predicted_scalar_diff";
1334+
}
1335+
1336+
if (get_analysis_mode() == AnalysisTool::MODE_ALL_SAMPLES_C && ui_->show_predicted_scalar->isChecked()) {
1337+
return "predicted_scalar";
1338+
}
1339+
13061340
return feature_map_;
13071341
}
13081342

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

1800+
if (ui_->pca_predict_scalar->isChecked()) {
1801+
// set the feature map to the target feature
1802+
session_->set_feature_map(ui_->pca_scalar_combo->currentText().toStdString());
1803+
}
1804+
17661805
compute_stats();
17671806
Q_EMIT pca_update();
17681807
}
@@ -1785,6 +1824,30 @@ Eigen::VectorXd AnalysisTool::construct_mean_shape() {
17851824
return mean_shape;
17861825
}
17871826

1827+
//---------------------------------------------------------------------------
1828+
void AnalysisTool::handle_samples_predicted_scalar_options() {
1829+
if (ui_->show_difference_to_predicted_scalar->isChecked() || ui_->show_predicted_scalar->isChecked()) {
1830+
// iterate over all samples, predict scalars, compute the difference and store as a new scalar field
1831+
auto shapes = session_->get_non_excluded_shapes();
1832+
for (auto& shape : shapes) {
1833+
auto particles = shape->get_particles();
1834+
auto target_feature = ui_->pca_scalar_combo->currentText();
1835+
auto predicted =
1836+
ShapeScalarJob::predict_scalars(session_, target_feature, particles.get_combined_global_particles());
1837+
1838+
// load the mesh and feature
1839+
shape->get_meshes(session_->get_display_mode(), true);
1840+
shape->load_feature(session_->get_display_mode(), target_feature.toStdString());
1841+
auto scalars = shape->get_point_features(target_feature.toStdString());
1842+
// compute difference
1843+
auto diff = predicted - scalars;
1844+
shape->set_point_features("predicted_scalar_diff", diff);
1845+
shape->set_point_features("predicted_scalar", predicted);
1846+
}
1847+
}
1848+
Q_EMIT update_view();
1849+
}
1850+
17881851
//---------------------------------------------------------------------------
17891852
void AnalysisTool::reconstruction_method_changed() {
17901853
ui_->reconstruction_options->setVisible(ui_->distance_transform_radio_button->isChecked());

Studio/Analysis/AnalysisTool.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,8 @@ class AnalysisTool : public QWidget {
197197
//! Compute the mean shape outside of the PCA in case we are using scalars only
198198
Eigen::VectorXd construct_mean_shape();
199199

200+
void handle_samples_predicted_scalar_options();
201+
200202
Q_SIGNALS:
201203

202204
void update_view();

Studio/Analysis/AnalysisTool.ui

Lines changed: 63 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -535,7 +535,7 @@ QWidget#particles_panel {
535535
<item row="3" column="0" colspan="2">
536536
<widget class="QTabWidget" name="tabWidget">
537537
<property name="currentIndex">
538-
<number>2</number>
538+
<number>1</number>
539539
</property>
540540
<widget class="QWidget" name="mean_tab">
541541
<attribute name="title">
@@ -1089,16 +1089,6 @@ QWidget#particles_panel {
10891089
<string>Samples</string>
10901090
</attribute>
10911091
<layout class="QGridLayout" name="gridLayout_4">
1092-
<item row="1" column="1">
1093-
<widget class="QSpinBox" name="sampleSpinBox">
1094-
<property name="enabled">
1095-
<bool>false</bool>
1096-
</property>
1097-
<property name="toolTip">
1098-
<string>Sample selection</string>
1099-
</property>
1100-
</widget>
1101-
</item>
11021092
<item row="1" column="0">
11031093
<widget class="QRadioButton" name="singleSamplesRadio">
11041094
<property name="toolTip">
@@ -1109,6 +1099,19 @@ QWidget#particles_panel {
11091099
</property>
11101100
</widget>
11111101
</item>
1102+
<item row="2" column="0" colspan="2">
1103+
<widget class="QPushButton" name="medianButton">
1104+
<property name="enabled">
1105+
<bool>false</bool>
1106+
</property>
1107+
<property name="toolTip">
1108+
<string>Show median sample</string>
1109+
</property>
1110+
<property name="text">
1111+
<string>Median</string>
1112+
</property>
1113+
</widget>
1114+
</item>
11121115
<item row="0" column="0">
11131116
<widget class="QRadioButton" name="allSamplesRadio">
11141117
<property name="toolTip">
@@ -1122,19 +1125,62 @@ QWidget#particles_panel {
11221125
</property>
11231126
</widget>
11241127
</item>
1125-
<item row="2" column="0" colspan="2">
1126-
<widget class="QPushButton" name="medianButton">
1128+
<item row="3" column="0" colspan="2">
1129+
<widget class="QWidget" name="predict_diff_widget" native="true">
1130+
<layout class="QGridLayout" name="gridLayout_28">
1131+
<item row="0" column="0">
1132+
<widget class="QCheckBox" name="show_difference_to_predicted_scalar">
1133+
<property name="text">
1134+
<string>Show difference to predicted scalar</string>
1135+
</property>
1136+
</widget>
1137+
</item>
1138+
<item row="0" column="1">
1139+
<spacer name="horizontalSpacer_9">
1140+
<property name="orientation">
1141+
<enum>Qt::Horizontal</enum>
1142+
</property>
1143+
<property name="sizeHint" stdset="0">
1144+
<size>
1145+
<width>40</width>
1146+
<height>20</height>
1147+
</size>
1148+
</property>
1149+
</spacer>
1150+
</item>
1151+
<item row="1" column="0">
1152+
<widget class="QCheckBox" name="show_predicted_scalar">
1153+
<property name="text">
1154+
<string>Show predicted scalar</string>
1155+
</property>
1156+
</widget>
1157+
</item>
1158+
</layout>
1159+
</widget>
1160+
</item>
1161+
<item row="1" column="1">
1162+
<widget class="QSpinBox" name="sampleSpinBox">
11271163
<property name="enabled">
11281164
<bool>false</bool>
11291165
</property>
11301166
<property name="toolTip">
1131-
<string>Show median sample</string>
1132-
</property>
1133-
<property name="text">
1134-
<string>Median</string>
1167+
<string>Sample selection</string>
11351168
</property>
11361169
</widget>
11371170
</item>
1171+
<item row="4" column="0">
1172+
<spacer name="verticalSpacer_3">
1173+
<property name="orientation">
1174+
<enum>Qt::Vertical</enum>
1175+
</property>
1176+
<property name="sizeHint" stdset="0">
1177+
<size>
1178+
<width>20</width>
1179+
<height>40</height>
1180+
</size>
1181+
</property>
1182+
</spacer>
1183+
</item>
11381184
</layout>
11391185
</widget>
11401186
<widget class="QWidget" name="pca_tab">

0 commit comments

Comments
 (0)