opynsim
Unofficial C++ Documentation
Loading...
Searching...
No Matches
thin_plate_spline_scaling_step.h
Go to the documentation of this file.
1#pragma once
2
9
11#include <OpenSim/Simulation/Model/Frame.h>
12#include <OpenSim/Simulation/Model/Model.h>
13
14#include <filesystem>
15#include <functional>
16#include <optional>
17#include <sstream>
18#include <string>
19#include <string_view>
20#include <utility>
21#include <vector>
22
23namespace opyn
24{
25 // An abstract base class for a `ScalingStep` that uses the Thin-Plate Spline (TPS)
26 // algorithm to perform some kind of (concrete-implementation-defined) scaling to
27 // the model.
29 OpenSim_DECLARE_ABSTRACT_OBJECT(ThinPlateSplineScalingStep, ScalingStep)
30
31 public:
32 // These parameters are common for all TPS `ScalingStep`s.
33 OpenSim_DECLARE_PROPERTY(source_landmarks_file, std::string, "Filesystem path, relative to the model's filesystem path, where a CSV containing the source landmarks can be loaded from (e.g. `Geometry/torso.landmarks.csv`)");
34 OpenSim_DECLARE_PROPERTY(destination_landmarks_file, std::string, "Filesystem path, relative to the model's filesystem path, where a CSV containing the destination landmarks can be loaded from (e.g. `DestinationGeometry/torso.landmarks.csv`)");
35 OpenSim_DECLARE_PROPERTY(landmarks_frame, std::string, "Component path (e.g. `/bodyset/somebody`) to the frame that the landmarks defined in both `source_landmarks_file` and `destination_landmarks_file` are expressed in.\n\nThe engine uses this to figure out how to transform the input to/from the coordinate system of the warp transform.");
36 OpenSim_DECLARE_PROPERTY(compensate_for_frame_changes, bool, "If `landmarks_frame` is different from the source data's frame, and previous scaling steps have caused the spatial transform between those two frames to change, compensate for it by inverse-applying the difference between the frames to the result, so that the effect of those frame changes is compensated for. This can be necessary to stop double-warping for occurring (e.g. when separately warping a frame followed by warping data within that frame)");
37 OpenSim_DECLARE_PROPERTY(source_landmarks_prescale, double, "Scaling factor that each source landmark point should be multiplied by before computing the TPS warp. This is sometimes necessary if (e.g.) the mesh is in different units (OpenSim works in meters).");
38 OpenSim_DECLARE_PROPERTY(destination_landmarks_prescale, double, "Scaling factor that each destination landmark point should be multiplied by before computing the TPS warp. This is sometimes necessary if (e.g.) the mesh is in different units (OpenSim works in meters).");
39 OpenSim_DECLARE_PROPERTY(warping_penalty, double, "A warping penalty that smooths out the warp. Explained in OPynSim's Thin-Plate Spline documentation.");
40
41 struct CommonParameters final {
43 const OpenSim::Frame* sourceLandmarksFrame = nullptr;
44 const OpenSim::Frame* resultLandmarksFrame = nullptr;
45 bool compensateForFrameChanges = false;
46 };
47
48 protected:
49
50 // Constructs a `ThinPlateSplineScalingStep` with defaulted parameters.
51 explicit ThinPlateSplineScalingStep(std::string_view label) :
53 {
54 constructProperty_source_landmarks_file({});
55 constructProperty_destination_landmarks_file({});
56 constructProperty_landmarks_frame("/ground");
57 constructProperty_compensate_for_frame_changes(false);
58 constructProperty_source_landmarks_prescale(1.0);
59 constructProperty_destination_landmarks_prescale(1.0);
60 constructProperty_warping_penalty(0.0);
61 }
62
63 // Overriders should still call this base method.
64 void implForEachScalingParameterDeclaration(const std::function<void(const ScalingParameterDeclaration&)>& callback) const override
65 {
66 callback(ScalingParameterDeclaration{"blending_factor", 1.0});
67 }
68
69 // Performs validation steps that are common for all TPS scaling steps, so overriders
70 // should call this base method.
71 std::vector<ScalingStepValidationMessage> implValidate(
73 const ScalingParameters&,
74 const OpenSim::Model& sourceModel) const override
75 {
76 std::vector<ScalingStepValidationMessage> messages;
77
78 // Ensure the model has a filesystem location (prerequisite).
79 const auto modelFilesystemLocation = opyn::TryFindInputFile(sourceModel);
80 if (not modelFilesystemLocation) {
81 messages.emplace_back(ScalingStepValidationState::Error, "The source model has no filesystem location.");
82 return messages;
83 }
84
85 // Ensure the `source_landmarks_file` can be found (relative to the model osim).
86 if (get_source_landmarks_file().empty()) {
87 messages.emplace_back(ScalingStepValidationState::Error, "`source_landmarks_file` is empty.");
88 }
89 else if (const auto sourceLandmarksPath = modelFilesystemLocation->parent_path() / get_source_landmarks_file();
90 not std::filesystem::exists(sourceLandmarksPath)) {
91
92 std::stringstream msg;
93 msg << sourceLandmarksPath.string() << ": Cannot find source landmarks file on filesystem";
94 messages.emplace_back(ScalingStepValidationState::Error, std::move(msg).str());
95 }
96
97 // Ensure the `destination_landmarks_file` can be found (relative to the model osim).
98 if (get_destination_landmarks_file().empty()) {
99 messages.emplace_back(ScalingStepValidationState::Error, "`destination_landmarks_file` is empty.");
100 }
101 else if (const auto destinationLandmarksPath = modelFilesystemLocation->parent_path() / get_destination_landmarks_file();
102 not std::filesystem::exists(destinationLandmarksPath)) {
103
104 std::stringstream msg;
105 msg << destinationLandmarksPath.string() << ": Cannot find destination landmarks file on filesystem";
106 messages.emplace_back(ScalingStepValidationState::Error, std::move(msg).str());
107 }
108
109 // Ensure `landmarks_frame` exists in the model
110 const auto* landmarksFrame = FindComponent<OpenSim::Frame>(sourceModel, get_landmarks_frame());
111 if (not landmarksFrame) {
112 std::stringstream msg;
113 msg << get_landmarks_frame() << ": Cannot find this frame in the source model (or it isn't a Frame).";
114 messages.emplace_back(ScalingStepValidationState::Error, std::move(msg).str());
115 }
116
117 return messages;
118 }
119
120 // Returns TPS `ScalingStep` parameters that are common for all uses of the TPS
121 // algorithm.
123 const ScalingParameters& parameters,
124 const OpenSim::Model& sourceModel,
125 const OpenSim::Model& resultModel) const
126 {
127 // Lookup/validate warping inputs.
128 const std::optional<std::filesystem::path> modelFilesystemLocation = TryFindInputFile(resultModel);
129 OSC_ASSERT_ALWAYS(modelFilesystemLocation && "The source model has no filesystem location");
130
131 OSC_ASSERT_ALWAYS(not get_source_landmarks_file().empty());
132 const std::filesystem::path sourceLandmarksPath = modelFilesystemLocation->parent_path() / get_source_landmarks_file();
133
134 OSC_ASSERT_ALWAYS(not get_destination_landmarks_file().empty());
135 const std::filesystem::path destinationLandmarksPath = modelFilesystemLocation->parent_path() / get_destination_landmarks_file();
136
137 OSC_ASSERT_ALWAYS(not get_landmarks_frame().empty());
138
139 const auto* sourceLandmarksFrame = FindComponent<OpenSim::Frame>(sourceModel, get_landmarks_frame());
140 OSC_ASSERT_ALWAYS(sourceLandmarksFrame && "could not find the landmarks frame in the source model");
141
142 const auto* resultLandmarksFrame = FindComponent<OpenSim::Frame>(resultModel, get_landmarks_frame());
143 OSC_ASSERT_ALWAYS(resultLandmarksFrame && "could not find the landmarks frame in the model");
144
145 const std::optional<double> blendingFactor = parameters.lookup<double>("blending_factor");
146 OSC_ASSERT_ALWAYS(blendingFactor && "blending_factor was not set by the warping engine");
147
148 return CommonParameters{
149 .tpsInputs = ThinPlateSplineCommonInputs{
150 sourceLandmarksPath,
151 destinationLandmarksPath,
152 get_source_landmarks_prescale(),
153 get_destination_landmarks_prescale(),
154 *blendingFactor,
155 get_warping_penalty(),
156 },
157 .sourceLandmarksFrame = sourceLandmarksFrame,
158 .resultLandmarksFrame = resultLandmarksFrame,
159 .compensateForFrameChanges = get_compensate_for_frame_changes(),
160 };
161 }
162 };
163}
#define OSC_ASSERT_ALWAYS(expr)
Definition assertions.h:20
Definition scaling_cache.h:30
Definition scaling_parameter_declaration.h:16
Definition scaling_parameters.h:14
std::optional< T > lookup(const std::string &key) const
Definition scaling_parameters.h:17
Definition scaling_step.h:25
osc::CStringView label() const
Definition scaling_step.h:41
Definition thin_plate_spline_scaling_step.h:28
std::vector< ScalingStepValidationMessage > implValidate(ScalingCache &, const ScalingParameters &, const OpenSim::Model &sourceModel) const override
Definition thin_plate_spline_scaling_step.h:71
CommonParameters calcTPSScalingStepCommonParams(const ScalingParameters &parameters, const OpenSim::Model &sourceModel, const OpenSim::Model &resultModel) const
Definition thin_plate_spline_scaling_step.h:122
void implForEachScalingParameterDeclaration(const std::function< void(const ScalingParameterDeclaration &)> &callback) const override
Definition thin_plate_spline_scaling_step.h:64
ThinPlateSplineScalingStep(std::string_view label)
Definition thin_plate_spline_scaling_step.h:51
Definition component_registry.h:14
std::optional< std::filesystem::path > TryFindInputFile(const OpenSim::Model &)
bool empty(const OpenSim::Set< T, C > &s)
Definition open_sim_helpers.h:149
Definition thin_plate_spline_common_inputs.h:10