Skip to content

Commit 96a9a64

Browse files
authored
Merge pull request #2126 from SCIInstitute/2081-mesh_based_attributes
Mesh field based correspondence attributes
2 parents 86e32ef + 912768f commit 96a9a64

23 files changed

Lines changed: 1028 additions & 772 deletions

Applications/shapeworks/Commands.cpp

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,13 @@ bool OptimizeCommand::execute(const optparse::Values& options, SharedCommandData
126126
try {
127127
// load spreadsheet project
128128
ProjectHandle project = std::make_shared<Project>();
129-
project->load(projectFile);
129+
try {
130+
project->load(projectFile);
131+
} catch (std::exception& e) {
132+
SW_ERROR("Project failed to load: {}", e.what());
133+
return false;
134+
}
135+
130136

131137
const auto oldBasePath = boost::filesystem::current_path();
132138
auto base = StringUtils::getPath(projectFile);

Libs/Mesh/Mesh.cpp

Lines changed: 147 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818

1919
// vtk
2020
#include <vtkAppendPolyData.h>
21+
#include <vtkGradientFilter.h>
2122
#include <vtkButterflySubdivisionFilter.h>
2223
#include <vtkCenterOfMass.h>
2324
#include <vtkCleanPolyData.h>
@@ -863,6 +864,150 @@ Field Mesh::curvature(const CurvatureType type) const {
863864
return curv;
864865
}
865866

867+
868+
void computeGradient(vtkDataSet* inputDataSet, const char* scalarFieldName, const char* gradientFieldName) {
869+
vtkSmartPointer<vtkGradientFilter> gradientFilter = vtkSmartPointer<vtkGradientFilter>::New();
870+
gradientFilter->SetInputData(inputDataSet);
871+
gradientFilter->SetInputScalars(vtkDataSet::FIELD_ASSOCIATION_POINTS, scalarFieldName);
872+
gradientFilter->SetResultArrayName(gradientFieldName);
873+
gradientFilter->Update();
874+
/*
875+
876+
vtkSmartPointer<vtkDoubleArray> gradientData = vtkSmartPointer<vtkDoubleArray>::New();
877+
gradientData->SetNumberOfComponents(3);
878+
gradientData->SetNumberOfTuples(inputDataSet->GetNumberOfPoints());
879+
gradientData->SetName(gradientFieldName);
880+
*/
881+
vtkDoubleArray* gradPointArray = vtkArrayDownCast<vtkDoubleArray>(
882+
vtkDataSet::SafeDownCast(gradientFilter->GetOutput())->GetPointData()->GetArray(gradientFieldName));
883+
884+
885+
/*
886+
for (vtkIdType pointId = 0; pointId < inputDataSet->GetNumberOfPoints(); ++pointId) {
887+
double gradient[3];
888+
gradientFilter->GetOutput()->GetPointData()->GetTuple(pointId, gradient);
889+
gradientData->SetTuple(pointId, gradient);
890+
}
891+
*/
892+
893+
// inputDataSet->GetPointData()->AddArray(gradientData);
894+
inputDataSet->GetPointData()->AddArray(gradPointArray);
895+
}
896+
897+
898+
void Mesh::computeFieldGradient(const std::string& field) const {
899+
computeGradient(poly_data_, field.c_str(), (std::string("gradient_") + field).c_str());
900+
return;
901+
auto arr = poly_data_->GetPointData()->GetArray(field.c_str());
902+
903+
// for each vertex, compute the gradient of the field and store x,y,z
904+
// components in a vector
905+
auto gradient = vtkSmartPointer<vtkDoubleArray>::New();
906+
gradient->SetNumberOfComponents(3);
907+
gradient->SetNumberOfTuples(numPoints());
908+
gradient->SetName((std::string("gradient_") + field).c_str());
909+
910+
for (int i = 0; i < numPoints(); i++) {
911+
// get the field value at this vertex
912+
double value = arr->GetTuple1(i);
913+
914+
// collect all neighboring vertices using vtk
915+
auto cell = vtkSmartPointer<vtkGenericCell>::New();
916+
auto neighbors = vtkSmartPointer<vtkIdList>::New();
917+
poly_data_->GetPointCells(i, neighbors);
918+
int num_neighbors = neighbors->GetNumberOfIds();
919+
920+
// for each neighbor vertex
921+
double x = 0.0;
922+
double y = 0.0;
923+
double z = 0.0;
924+
925+
for (int j = 0; j < num_neighbors; j++) {
926+
// get the neighbor vertex id
927+
int neighbor_id = neighbors->GetId(j);
928+
929+
// get the neighbor vertex
930+
auto neighbor = poly_data_->GetPoint(neighbor_id);
931+
932+
// get the neighbor vertex value
933+
double neighbor_value = arr->GetTuple1(neighbor_id);
934+
935+
// compute the gradient
936+
x += (neighbor[0] - getPoint(i)[0]) * (neighbor_value - value);
937+
y += (neighbor[1] - getPoint(i)[1]) * (neighbor_value - value);
938+
z += (neighbor[2] - getPoint(i)[2]) * (neighbor_value - value);
939+
}
940+
941+
gradient->SetTuple3(i, x, y, z);
942+
}
943+
944+
poly_data_->GetPointData()->AddArray(gradient);
945+
}
946+
947+
Eigen::Vector3d Mesh::computeFieldGradientAtPoint(const std::string& field, const Point3& query) const {
948+
this->updateCellLocator();
949+
950+
// compute gradient if not already computed
951+
if (poly_data_->GetPointData()->GetArray((std::string("gradient_") + field).c_str()) == nullptr) {
952+
computeFieldGradient(field);
953+
}
954+
955+
double closestPoint[3];
956+
vtkIdType cellId;
957+
int subId;
958+
double dist;
959+
cellLocator->FindClosestPoint(query.data(), closestPoint, cellId, subId, dist);
960+
961+
auto cell = poly_data_->GetCell(cellId);
962+
963+
size_t v1 = cell->GetPointId(0);
964+
size_t v2 = cell->GetPointId(1);
965+
size_t v3 = cell->GetPointId(2);
966+
967+
auto gradient = poly_data_->GetPointData()->GetArray((std::string("gradient_") + field).c_str());
968+
969+
Eigen::Vector3d grad1(gradient->GetTuple3(v1)[0], gradient->GetTuple3(v1)[1], gradient->GetTuple3(v1)[2]);
970+
Eigen::Vector3d grad2(gradient->GetTuple3(v2)[0], gradient->GetTuple3(v2)[1], gradient->GetTuple3(v2)[2]);
971+
Eigen::Vector3d grad3(gradient->GetTuple3(v3)[0], gradient->GetTuple3(v3)[1], gradient->GetTuple3(v3)[2]);
972+
973+
// Compute barycentric distances
974+
Eigen::Vector3d cp(closestPoint[0], closestPoint[1], closestPoint[2]);
975+
Eigen::Vector3d bary = computeBarycentricCoordinates(cp, cellId);
976+
977+
bary = bary / bary.sum();
978+
979+
Eigen::Vector3d result;
980+
result = bary[0] * grad1 + bary[1] * grad2 + bary[2] * grad3;
981+
return result;
982+
}
983+
984+
double Mesh::interpolateFieldAtPoint(const std::string& field, const Point3& query) const {
985+
this->updateCellLocator();
986+
987+
double closestPoint[3];
988+
vtkIdType cellId;
989+
int subId;
990+
double dist;
991+
this->cellLocator->FindClosestPoint(query.data(), closestPoint, cellId, subId, dist);
992+
993+
auto cell = this->poly_data_->GetCell(cellId);
994+
995+
size_t v1 = cell->GetPointId(0);
996+
size_t v2 = cell->GetPointId(1);
997+
size_t v3 = cell->GetPointId(2);
998+
999+
// Compute barycentric distances
1000+
Eigen::Vector3d cp(closestPoint[0], closestPoint[1], closestPoint[2]);
1001+
Eigen::Vector3d bary = computeBarycentricCoordinates(cp, cellId);
1002+
1003+
bary = bary / bary.sum();
1004+
1005+
Eigen::Vector3d values(this->getFieldValue(field, v1), this->getFieldValue(field, v2),
1006+
this->getFieldValue(field, v3));
1007+
1008+
return (bary * values.transpose()).mean();
1009+
}
1010+
8661011
Mesh& Mesh::applySubdivisionFilter(const SubdivisionType type, int subdivision) {
8671012
if (type == Mesh::SubdivisionType::Loop) {
8681013
auto filter = vtkSmartPointer<vtkLoopSubdivisionFilter>::New();
@@ -1466,30 +1611,8 @@ Eigen::Vector3d Mesh::computeBarycentricCoordinates(const Eigen::Vector3d& pt, i
14661611
}
14671612

14681613
double Mesh::getFFCValue(Eigen::Vector3d query) const {
1469-
this->updateCellLocator();
1470-
1471-
double closestPoint[3];
1472-
vtkIdType cellId;
1473-
int subId;
1474-
double dist;
1475-
this->cellLocator->FindClosestPoint(query.data(), closestPoint, cellId, subId, dist);
1476-
1477-
auto cell = this->poly_data_->GetCell(cellId);
1478-
1479-
size_t v1 = cell->GetPointId(0);
1480-
size_t v2 = cell->GetPointId(1);
1481-
size_t v3 = cell->GetPointId(2);
1482-
1483-
// Compute barycentric distances
1484-
Eigen::Vector3d cp(closestPoint[0], closestPoint[1], closestPoint[2]);
1485-
Eigen::Vector3d bary = computeBarycentricCoordinates(cp, cellId);
1486-
1487-
bary = bary / bary.sum();
1488-
1489-
Eigen::Vector3d values(this->getFieldValue("value", v1), this->getFieldValue("value", v2),
1490-
this->getFieldValue("value", v3));
1491-
1492-
return (bary * values.transpose()).mean();
1614+
Point3 point(query.data());
1615+
return interpolateFieldAtPoint("value", point);
14931616
}
14941617

14951618
Eigen::Vector3d Mesh::getFFCGradient(Eigen::Vector3d query) const {

Libs/Mesh/Mesh.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,15 @@ class Mesh {
162162
/// computes curvature using principal (default) or gaussian or mean algorithms
163163
Field curvature(const CurvatureType type = Principal) const;
164164

165+
/// compute the gradient of a scalar field for all vertices
166+
void computeFieldGradient(const std::string& field) const;
167+
168+
/// compute the gradient of a scalar field at a point
169+
Eigen::Vector3d computeFieldGradientAtPoint(const std::string& field, const Point3& query) const;
170+
171+
/// interpolate a scalar field at a given point
172+
double interpolateFieldAtPoint(const std::string& field, const Point3& query) const;
173+
165174
/// applies subdivision filter (butterfly (default) or loop)
166175
Mesh& applySubdivisionFilter(const SubdivisionType type = Butterfly, int subdivision = 1);
167176

Libs/Optimize/Domain/ImplicitSurfaceDomain.h

Lines changed: 2 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,6 @@
11
#pragma once
22

33
#include "ImageDomainWithCurvature.h"
4-
#include "Libs/Mesh/meshFIM.h"
5-
#include "TriMesh.h"
6-
#include "TriMesh_algo.h"
74
#include "vnl/vnl_cross.h"
85
#include "vnl/vnl_inverse.h"
96
#include "vnl/vnl_math.h"
@@ -105,34 +102,6 @@ class ImplicitSurfaceDomain : public ImageDomainWithCurvature<T> {
105102
return newpoint;
106103
}
107104

108-
void SetMesh(TriMesh* mesh) {
109-
m_mesh = new meshFIM();
110-
m_mesh->SetMesh(mesh);
111-
};
112-
113-
void SetFeaMesh(const char* feaFile) { m_mesh->ReadFeatureFromFile(feaFile); };
114-
void SetFeaGrad(const char* feaGradFile) { m_mesh->ReadFeatureGradientFromFile(feaGradFile); };
115-
void SetFids(const char* fidsFile) {
116-
m_mesh->ReadFaceIndexMap(fidsFile);
117-
const typename ImageType::PointType orgn = this->GetOrigin();
118-
m_mesh->imageOrigin[0] = orgn[0];
119-
m_mesh->imageOrigin[1] = orgn[1];
120-
m_mesh->imageOrigin[2] = orgn[2];
121-
typename ImageType::RegionType::SizeType sz = this->GetSize();
122-
m_mesh->imageSize[0] = sz[0];
123-
m_mesh->imageSize[1] = sz[1];
124-
m_mesh->imageSize[2] = sz[2];
125-
typename ImageType::SpacingType sp = this->GetSpacing();
126-
m_mesh->imageSpacing[0] = sp[0];
127-
m_mesh->imageSpacing[1] = sp[1];
128-
m_mesh->imageSpacing[2] = sp[2];
129-
typename ImageType::RegionType::IndexType idx = this->GetIndex();
130-
m_mesh->imageIndex[0] = idx[0];
131-
m_mesh->imageIndex[1] = idx[1];
132-
m_mesh->imageIndex[2] = idx[2];
133-
};
134-
meshFIM* GetMesh() { return m_mesh; }
135-
meshFIM* GetMesh() const { return m_mesh; }
136105

137106
/** Get any valid point on the domain. This is used to place the first particle. */
138107
PointType GetZeroCrossingPoint() const override {
@@ -142,7 +111,7 @@ class ImplicitSurfaceDomain : public ImageDomainWithCurvature<T> {
142111
return p;
143112
}
144113

145-
ImplicitSurfaceDomain() : m_Tolerance(1.0e-4) { m_mesh = NULL; }
114+
ImplicitSurfaceDomain() : m_Tolerance(1.0e-4) { }
146115
void PrintSelf(std::ostream& os, itk::Indent indent) const {
147116
Superclass::PrintSelf(os, indent);
148117
os << indent << "m_Tolerance = " << m_Tolerance << std::endl;
@@ -152,7 +121,7 @@ class ImplicitSurfaceDomain : public ImageDomainWithCurvature<T> {
152121
private:
153122
T m_Tolerance;
154123

155-
meshFIM* m_mesh;
124+
156125
};
157126

158127
} // end namespace shapeworks

Libs/Optimize/Domain/MeshDomain.cpp

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,10 @@
1212
namespace shapeworks {
1313

1414
bool MeshDomain::ApplyConstraints(PointType &p, int idx, bool dbg) const {
15-
if (!meshWrapper) {
15+
if (!mesh_wrapper_) {
1616
return true;
1717
}
18-
p = meshWrapper->SnapToMesh(p, idx);
18+
p = mesh_wrapper_->SnapToMesh(p, idx);
1919
return true;
2020
}
2121

@@ -27,7 +27,7 @@ bool MeshDomain::ApplyVectorConstraints(vnl_vector_fixed<double, DIMENSION> &gra
2727

2828
vnl_vector_fixed<double, DIMENSION> MeshDomain::ProjectVectorToSurfaceTangent(
2929
vnl_vector_fixed<double, DIMENSION> &gradE, const PointType &pos, int idx) const {
30-
return meshWrapper->ProjectVectorToSurfaceTangent(pos, idx, gradE);
30+
return mesh_wrapper_->ProjectVectorToSurfaceTangent(pos, idx, gradE);
3131
}
3232

3333
MeshDomain::PointType MeshDomain::UpdateParticlePosition(const PointType &point, int idx,
@@ -36,20 +36,20 @@ MeshDomain::PointType MeshDomain::UpdateParticlePosition(const PointType &point,
3636
for (unsigned int i = 0; i < DIMENSION; i++) {
3737
negativeUpdate[i] = -update[i];
3838
}
39-
PointType newPoint = meshWrapper->GeodesicWalk(point, idx, negativeUpdate);
39+
PointType newPoint = mesh_wrapper_->GeodesicWalk(point, idx, negativeUpdate);
4040
return newPoint;
4141
}
4242

4343
double MeshDomain::GetMaxDiameter() const {
4444
// todo should this not be the length of the bounding box diagonal?
45-
PointType boundingBoxSize = meshWrapper->GetMeshUpperBound() - meshWrapper->GetMeshLowerBound();
45+
PointType boundingBoxSize = mesh_wrapper_->GetMeshUpperBound() - mesh_wrapper_->GetMeshLowerBound();
4646
double max = 0;
4747
for (int d = 0; d < 3; d++) {
4848
max = max > boundingBoxSize[d] ? max : boundingBoxSize[d];
4949
}
5050
return max;
5151
}
5252

53-
void MeshDomain::InvalidateParticlePosition(int idx) const { this->meshWrapper->InvalidateParticle(idx); }
53+
void MeshDomain::InvalidateParticlePosition(int idx) const { this->mesh_wrapper_->InvalidateParticle(idx); }
5454

5555
} // namespace shapeworks

0 commit comments

Comments
 (0)