opynsim
Unofficial C++ Documentation
Loading...
Searching...
No Matches
scaling_cache.h
Go to the documentation of this file.
1#pragma once
2
9#include <libopynsim/tps3d.h>
10
18#include <OpenSim/Simulation/Model/Model.h>
19#include <SimTKcommon/internal/Transform.h>
20#include <SimTKcommon/internal/Rotation.h>
21#include <SimTKcommon/SmallMatrix.h>
22
23#include <memory>
24#include <unordered_map>
25#include <utility>
26
27namespace opyn
28{
29 // A cache that is (presumed to be) persisted between multiple executions of the
30 // model warping pipeline, in order to improve runtime performance.
31 class ScalingCache final {
32 public:
33 std::unique_ptr<InMemoryMesh> lookupTPSMeshWarp(
34 const OpenSim::Model& sourceModel,
35 const OpenSim::Model& resultModel,
36 const OpenSim::Mesh& sourceMesh,
37 const OpenSim::Mesh& resultMesh,
38 const OpenSim::Frame& sourceLandmarksFrame,
39 const OpenSim::Frame& resultLandmarksFrame,
40 const ThinPlateSplineCommonInputs& tpsInputs,
41 bool compensateForFrameChanges)
42 {
43 // Compile the TPS coefficients from the source+destination landmarks
44 const TPSCoefficients3D<float>& coefficients = lookupTPSCoefficients(tpsInputs);
45
46 // Calculate transforms to use before/after TPS warping
47 const Transforms transforms = calculateTransforms(
48 sourceModel,
49 resultModel,
50 sourceMesh.getFrame(),
51 resultMesh.getFrame(),
52 sourceLandmarksFrame,
53 resultLandmarksFrame,
54 compensateForFrameChanges
55 );
56
57 // Convert the input mesh into an OSC mesh, so that it's suitable for warping.
58 osc::Mesh resultOscMesh = ToOscMesh(
59 resultModel,
60 resultModel.getWorkingState(),
61 resultMesh
62 );
63
64 // Warp the vertices in-place.
65 auto vertices = resultOscMesh.vertices();
66 for (auto& vertex : vertices) {
67 vertex = transform_point(transforms.localToLandmarks, vertex); // put vertex into landmark frame
68 }
69 static_assert(alignof(decltype(vertices.front())) == alignof(SimTK::fVec3));
70 static_assert(sizeof(decltype(vertices.front())) == sizeof(SimTK::fVec3));
71 auto* punned = std::launder(reinterpret_cast<SimTK::fVec3*>(vertices.data()));
72 tps3d_warp_points_in_place(coefficients, {punned, vertices.size()}, static_cast<float>(tpsInputs.blendingFactor));
73 for (auto& vertex : vertices) {
74 vertex = transform_point(transforms.landmarksToLocal, vertex); // put vertex back into mesh frame
75 }
76
77 // Assign the vertices back to the OSC mesh and emit it as an `InMemoryMesh` component
78 resultOscMesh.set_vertices(vertices);
79 resultOscMesh.recalculate_normals(); // maybe should be a runtime param
80 return std::make_unique<InMemoryMesh>(resultOscMesh);
81 }
82
84 const OpenSim::Model& sourceModel,
85 const OpenSim::Model& resultModel,
86 [[maybe_unused]] const SimTK::Vec3& sourceLocation,
87 const SimTK::Vec3& resultLocation,
88 const OpenSim::Frame& sourceParentFrame,
89 const OpenSim::Frame& resultParentFrame,
90 const OpenSim::Frame& sourceLandmarksFrame,
91 const OpenSim::Frame& resultLandmarksFrame,
92 const ThinPlateSplineCommonInputs& tpsInputs,
93 bool compensateForFrameChanges)
94 {
95 // Compile the TPS coefficients from the source+destination landmarks
96 const TPSCoefficients3D<float>& coefficients = lookupTPSCoefficients(tpsInputs);
97
98 // Calculate transforms to use before/after TPS warping
99 const Transforms transforms = calculateTransforms(
100 sourceModel,
101 resultModel,
102 sourceParentFrame,
103 resultParentFrame,
104 sourceLandmarksFrame,
105 resultLandmarksFrame,
106 compensateForFrameChanges
107 );
108
109 const auto resultLocationInLandmarkFrame = transform_point(transforms.localToLandmarks, osc::to<osc::Vector3>(resultLocation));
110 const auto warpedLocationInLandmarkFrame = tps3d_warp_point(coefficients, osc::to<SimTK::fVec3>(resultLocationInLandmarkFrame), static_cast<float>(tpsInputs.blendingFactor));
111 return osc::to<SimTK::Vec3>(osc::transform_point(transforms.landmarksToLocal, osc::to<osc::Vector3>(warpedLocationInLandmarkFrame)));
112 }
113
115 const ThinPlateSplineCommonInputs& tpsInputs)
116 {
117 OSC_ASSERT_ALWAYS(tpsInputs.applyAffineRotation && "affine rotation must be requested in order to figure out the transform");
118 OSC_ASSERT_ALWAYS(tpsInputs.applyAffineTranslation && "affine translation must be requested in order to figure out the transform");
119 const TPSCoefficients3D<float>& coefficients = lookupTPSCoefficients(tpsInputs);
120
121 const SimTK::Vec<3, float> x = coefficients.a2.normalize();
122 const SimTK::Vec<3, float> y = coefficients.a3.normalize();
123 const SimTK::Vec<3, float> z = coefficients.a4.normalize();
124 const SimTK::Mat33 rotationMatrix{
125 x[0], y[0], z[0],
126 x[1], y[1], z[1],
127 x[2], y[2], z[2],
128 };
129 const SimTK::Rotation rotation{rotationMatrix};
130 const auto translation = osc::to<SimTK::Vec3>(coefficients.a1);
131 return SimTK::Transform{rotation, translation};
132 }
133
134 void clear() { m_CoefficientCache.clear(); }
135 private:
136 struct Transforms final {
137 osc::Matrix4x4 localToLandmarks;
138 osc::Matrix4x4 landmarksToLocal;
139 };
140
141 Transforms calculateTransforms(
142 const OpenSim::Model& sourceModel,
143 const OpenSim::Model& resultModel,
144 const OpenSim::Frame& sourceFrame,
145 const OpenSim::Frame& resultFrame,
146 const OpenSim::Frame& sourceLandmarksFrame,
147 const OpenSim::Frame& resultLandmarksFrame,
148 bool compensateForFrameChanges) const
149 {
150 const SimTK::Transform resultTransform = resultFrame.findTransformBetween(resultModel.getWorkingState(), resultLandmarksFrame);
151
152 if (compensateForFrameChanges) {
153 const SimTK::Transform sourceTransform = sourceFrame.findTransformBetween(sourceModel.getWorkingState(), sourceLandmarksFrame);
154 const SimTK::Transform frameWarpTransform = sourceTransform.invert() * resultTransform;
155 return Transforms{
156 .localToLandmarks = osc::to<osc::Matrix4x4>(sourceTransform),
157 .landmarksToLocal = osc::to<osc::Matrix4x4>(frameWarpTransform.invert() * sourceTransform.invert()),
158 };
159 } else {
160 return Transforms{
161 .localToLandmarks = osc::to<osc::Matrix4x4>(resultTransform),
162 .landmarksToLocal = osc::to<osc::Matrix4x4>(SimTK::Transform{resultTransform.invert()}),
163 };
164 }
165 }
166
167 const TPSCoefficients3D<float>& lookupTPSCoefficients(const ThinPlateSplineCommonInputs& tpsInputs)
168 {
169 const auto [cacheEntry, inserted] = m_CoefficientCache.try_emplace(tpsInputs);
170 if (not inserted) {
171 return cacheEntry->second;
172 }
173
174 // Read source+destination landmark files into independent collections
175 const auto sourceLandmarks = ReadLandmarksFromCSVIntoVectorOrThrow(tpsInputs.sourceLandmarksPath);
176 const auto destinationLandmarks = ReadLandmarksFromCSVIntoVectorOrThrow(tpsInputs.destinationLandmarksPath);
177
178 // Pair the source+destination landmarks together into a TPS coefficient solver's inputs
179 TPSCoefficientSolverInputs3D<float> inputs;
180 inputs.landmarks.reserve(osc::max(sourceLandmarks.size(), destinationLandmarks.size()));
181 TryPairingLandmarks(sourceLandmarks, destinationLandmarks, [&inputs, &tpsInputs](const MaybeNamedLandmarkPair& p)
182 {
183 if (auto landmark3d = p.tryGetPairedLocations()) {
184 landmark3d->source = tpsInputs.sourceLandmarksPrescale * landmark3d->source;
185 landmark3d->destination = tpsInputs.destinationLandmarksPrescale * landmark3d->destination;
186 inputs.landmarks.push_back(*landmark3d);
187 }
188 else {
189 osc::log_warn("The landmarks {} could not be paired, might be missing in the source/destination?", p.name());
190 }
191 });
192 inputs.apply_affine_translation = tpsInputs.applyAffineTranslation;
193 inputs.apply_affine_scale = tpsInputs.applyAffineScale;
194 inputs.apply_affine_rotation = tpsInputs.applyAffineRotation;
195 inputs.apply_non_affine_warp = tpsInputs.applyNonAffineWarp;
196 inputs.warping_penalty = static_cast<float>(tpsInputs.warpingPenalty);
197
198 // Solve the coefficients
199 cacheEntry->second = opyn::tps3d_solve_coefficients(inputs);
200
201 return cacheEntry->second;
202 }
203
204 std::unordered_map<ThinPlateSplineCommonInputs, TPSCoefficients3D<float>> m_CoefficientCache;
205 };
206}
#define OSC_ASSERT_ALWAYS(expr)
Definition assertions.h:20
Definition scaling_cache.h:31
SimTK::Transform lookupTPSAffineTransformWithoutScaling(const ThinPlateSplineCommonInputs &tpsInputs)
Definition scaling_cache.h:114
void clear()
Definition scaling_cache.h:134
SimTK::Vec3 lookupTPSWarpedRigidPoint(const OpenSim::Model &sourceModel, const OpenSim::Model &resultModel, const SimTK::Vec3 &sourceLocation, const SimTK::Vec3 &resultLocation, const OpenSim::Frame &sourceParentFrame, const OpenSim::Frame &resultParentFrame, const OpenSim::Frame &sourceLandmarksFrame, const OpenSim::Frame &resultLandmarksFrame, const ThinPlateSplineCommonInputs &tpsInputs, bool compensateForFrameChanges)
Definition scaling_cache.h:83
std::unique_ptr< InMemoryMesh > lookupTPSMeshWarp(const OpenSim::Model &sourceModel, const OpenSim::Model &resultModel, const OpenSim::Mesh &sourceMesh, const OpenSim::Mesh &resultMesh, const OpenSim::Frame &sourceLandmarksFrame, const OpenSim::Frame &resultLandmarksFrame, const ThinPlateSplineCommonInputs &tpsInputs, bool compensateForFrameChanges)
Definition scaling_cache.h:33
Definition mesh.h:33
void set_vertices(std::span< const Vector3 >)
void recalculate_normals()
std::vector< Vector3 > vertices() const
Definition component_registry.h:14
void tps3d_warp_points_in_place(const TPSCoefficients3D< float > &, std::span< SimTK::Vec< 3, float > >, float linear_interpolant)
TPSCoefficients3D< float > tps3d_solve_coefficients(const TPSCoefficientSolverInputs3D< float > &)
SimTK::Vec< 3, float > tps3d_warp_point(const TPSCoefficients3D< float > &, const SimTK::Vec< 3, float > &p)
void TryPairingLandmarks(std::vector< Landmark >, std::vector< Landmark >, const std::function< void(const MaybeNamedLandmarkPair &)> &consumer)
std::vector< Landmark > ReadLandmarksFromCSVIntoVectorOrThrow(const std::filesystem::path &)
osc::Mesh ToOscMesh(osc::SceneCache &, const OpenSim::Model &, const SimTK::State &, const OpenSim::Mesh &, const OpenSimDecorationOptions &, float fixupScaleFactor)
Vector3 transform_point(const Matrix4x4 &mat, const Vector3 &point)
Definition matrix_functions.h:540
constexpr auto max(Angle< Rep1, Units1 > x, Angle< Rep2, Units2 > y) -> std::common_type_t< decltype(x), decltype(y)>
Definition angle.h:300
Definition tps3d.h:131
SimTK::Vec< 3, T > a1
Definition tps3d.h:138
SimTK::Vec< 3, T > a3
Definition tps3d.h:140
SimTK::Vec< 3, T > a4
Definition tps3d.h:141
SimTK::Vec< 3, T > a2
Definition tps3d.h:139
Definition thin_plate_spline_common_inputs.h:13
bool applyAffineTranslation
Definition thin_plate_spline_common_inputs.h:37
double blendingFactor
Definition thin_plate_spline_common_inputs.h:41
bool applyAffineRotation
Definition thin_plate_spline_common_inputs.h:39