Skip to content

Commit 012940e

Browse files
authored
Merge pull request #2434 from SCIInstitute/python_pca
Python PCA API
2 parents 47345be + 5ef5d1b commit 012940e

File tree

3 files changed

+39
-11
lines changed

3 files changed

+39
-11
lines changed

Libs/Particles/ParticleShapeStatistics.cpp

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -476,7 +476,10 @@ int ParticleShapeStatistics::read_point_files(const std::string& s) {
476476
}
477477

478478
//---------------------------------------------------------------------------
479-
ParticleShapeStatistics::ParticleShapeStatistics(std::shared_ptr<Project> project) {
479+
ParticleShapeStatistics::ParticleShapeStatistics(std::shared_ptr<Project> project) { load_from_project(project); }
480+
481+
//---------------------------------------------------------------------------
482+
void ParticleShapeStatistics::load_from_project(std::shared_ptr<Project> project) {
480483
std::vector<Eigen::VectorXd> points;
481484
std::vector<int> groups;
482485
for (auto& s : project->get_subjects()) {
@@ -497,7 +500,7 @@ ParticleShapeStatistics::ParticleShapeStatistics(std::shared_ptr<Project> projec
497500

498501
//---------------------------------------------------------------------------
499502
int ParticleShapeStatistics::do_pca(std::vector<std::vector<Point>> global_pts, int domainsPerShape) {
500-
this->domains_per_shape_ = domainsPerShape;
503+
domains_per_shape_ = domainsPerShape;
501504

502505
// Assumes all the same size.
503506
num_samples_ = global_pts.size() / domains_per_shape_;
@@ -508,11 +511,6 @@ int ParticleShapeStatistics::do_pca(std::vector<std::vector<Point>> global_pts,
508511
mean_.resize(num_dimensions_);
509512
mean_.fill(0);
510513

511-
std::cout << "VDimension = " << values_per_particle_ << "-------------\n";
512-
std::cout << "m_numSamples = " << num_samples_ << "-------------\n";
513-
std::cout << "m_domainsPerShape = " << domains_per_shape_ << "-------------\n";
514-
std::cout << "global_pts.size() = " << global_pts.size() << "-------------\n";
515-
516514
// Compile the "meta shapes"
517515
for (unsigned int i = 0; i < num_samples_; i++) {
518516
for (unsigned int k = 0; k < domains_per_shape_; k++) {
@@ -571,6 +569,13 @@ int ParticleShapeStatistics::do_pca(ParticleSystemEvaluation ParticleSystemEvalu
571569
return do_pca(particlePoints, domainsPerShape);
572570
}
573571

572+
//---------------------------------------------------------------------------
573+
int ParticleShapeStatistics::do_pca(std::shared_ptr<Project> project) {
574+
load_from_project(project);
575+
compute_modes();
576+
return 0;
577+
}
578+
574579
//---------------------------------------------------------------------------
575580
int ParticleShapeStatistics::compute_modes() {
576581
SW_DEBUG("computing PCA modes");
@@ -638,6 +643,13 @@ int ParticleShapeStatistics::principal_component_projections() {
638643
return 0;
639644
}
640645

646+
//---------------------------------------------------------------------------
647+
Eigen::VectorXd ParticleShapeStatistics::project_new_sample(const Eigen::VectorXd& new_sample) {
648+
// Subtract mean and project onto principal components
649+
Eigen::VectorXd centered_sample = new_sample - mean_;
650+
return eigenvectors_.transpose() * centered_sample;
651+
}
652+
641653
//---------------------------------------------------------------------------
642654
int ParticleShapeStatistics::write_csv_file(const std::string& s) {
643655
// Write csv file

Libs/Particles/ParticleShapeStatistics.h

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,13 +19,15 @@ class Project;
1919
class ParticleShapeStatistics {
2020
public:
2121
ParticleShapeStatistics(){};
22-
ParticleShapeStatistics(std::shared_ptr<Project> project);
22+
explicit ParticleShapeStatistics(std::shared_ptr<Project> project);
2323
~ParticleShapeStatistics(){};
2424

2525
int do_pca(std::vector<std::vector<Point>> global_pts, int domainsPerShape = 1);
2626

2727
int do_pca(ParticleSystemEvaluation particleSystem, int domainsPerShape = 1);
2828

29+
int do_pca(std::shared_ptr<Project> project);
30+
2931
//! Loads a set of point files and pre-computes some statistics.
3032
int import_points(std::vector<Eigen::VectorXd> points, std::vector<int> group_ids);
3133

@@ -57,6 +59,9 @@ class ParticleShapeStatistics {
5759
//! principal componenent axes for each of the samples. ComputeModes must be called first.
5860
int principal_component_projections();
5961

62+
//! Projects a new sample into the PCA space defined by the original samples.
63+
Eigen::VectorXd project_new_sample(const Eigen::VectorXd& new_sample);
64+
6065
//! Returns the sample size
6166
int get_num_samples() const { return num_samples_; }
6267
int get_group1_num_samples() const { return num_samples_group1_; }
@@ -135,6 +140,8 @@ class ParticleShapeStatistics {
135140
//! Set the meshes for each sample (used for some evaluation metrics)
136141
void set_meshes(const std::vector<Mesh>& meshes) { meshes_ = meshes; }
137142

143+
void load_from_project(std::shared_ptr<Project> project);
144+
138145
private:
139146
unsigned int num_samples_group1_;
140147
unsigned int num_samples_group2_;

Libs/Python/ShapeworksPython.cpp

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -559,8 +559,7 @@ PYBIND11_MODULE(shapeworks_py, m) {
559559
[](Image& image, std::vector<double>& pt) -> decltype(auto) {
560560
return image.evaluate(Point({pt[0], pt[1], pt[2]}));
561561
},
562-
"evaluate the image at any given point in space", "pt"_a)
563-
;
562+
"evaluate the image at any given point in space", "pt"_a);
564563

565564
// PhysicalRegion
566565
py::class_<PhysicalRegion>(m, "PhysicalRegion")
@@ -1281,9 +1280,14 @@ PYBIND11_MODULE(shapeworks_py, m) {
12811280

12821281
.def(py::init<>())
12831282

1283+
// int do_pca(ParticleSystemEvaluation particleSystem, int domainsPerShape = 1);
12841284
.def("PCA", py::overload_cast<ParticleSystemEvaluation, int>(&ParticleShapeStatistics::do_pca),
12851285
"calculates the eigen values and eigen vectors of the data", "particleSystem"_a, "domainsPerShape"_a = 1)
12861286

1287+
// int do_pca(std::shared_ptr<Project> project);
1288+
.def("PCA", py::overload_cast<std::shared_ptr<Project>>(&ParticleShapeStatistics::do_pca),
1289+
"calculates the eigen values and eigen vectors of the data from a project", "project"_a)
1290+
12871291
.def("principalComponentProjections", &ParticleShapeStatistics::principal_component_projections,
12881292
"projects the original data on the calculated principal components")
12891293

@@ -1302,7 +1306,12 @@ PYBIND11_MODULE(shapeworks_py, m) {
13021306
components are constructed")
13031307

13041308
.def("percentVarByMode", &ParticleShapeStatistics::get_percent_variance_by_mode,
1305-
"return the variance accounted for by the principal components");
1309+
"return the variance accounted for by the principal components")
1310+
1311+
.def("projectNewSample", &ParticleShapeStatistics::project_new_sample, "project a new sample into the PCA space",
1312+
"newSample"_a)
1313+
1314+
.def("getMean", &ParticleShapeStatistics::get_mean, "returns the mean shape particles");
13061315

13071316
define_python_analyze(m);
13081317
define_python_groom(m);

0 commit comments

Comments
 (0)