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
10
13#include <OpenSim/Simulation/Model/Frame.h>
14#include <OpenSim/Simulation/Model/Model.h>
15
16#include <filesystem>
17#include <format>
18#include <functional>
19#include <optional>
20#include <string>
21#include <string_view>
22#include <vector>
23
24namespace opyn
25{
26 // An abstract base class for a `ScalingStep` that uses the Thin-Plate Spline (TPS)
27 // algorithm to perform some kind of (concrete-implementation-defined) scaling to
28 // the model.
30 OpenSim_DECLARE_ABSTRACT_OBJECT(ThinPlateSplineScalingStep, ScalingStep)
31
32 public:
33 // These parameters are common for all TPS `ScalingStep`s.
34 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`)");
35 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`)");
36 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.");
37 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)");
38 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).");
39 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).");
40 OpenSim_DECLARE_PROPERTY(warping_penalty, double, "A warping penalty that smooths out the warp. Explained in OPynSim's Thin-Plate Spline documentation.");
41
42 struct CommonParameters final {
44 const OpenSim::Frame* sourceLandmarksFrame = nullptr;
45 const OpenSim::Frame* resultLandmarksFrame = nullptr;
46 bool compensateForFrameChanges = false;
47 };
48
49 protected:
50
51 // Constructs a `ThinPlateSplineScalingStep` with defaulted parameters.
52 explicit ThinPlateSplineScalingStep(std::string_view label) :
54 {
55 constructProperty_source_landmarks_file({});
56 constructProperty_destination_landmarks_file({});
57 constructProperty_landmarks_frame("/ground");
58 constructProperty_compensate_for_frame_changes(false);
59 constructProperty_source_landmarks_prescale(1.0);
60 constructProperty_destination_landmarks_prescale(1.0);
61 constructProperty_warping_penalty(0.0);
62 }
63
64 // Overriders should still call this base method.
65 void implForEachScalingParameterDeclaration(const std::function<void(const ScalingParameterDeclaration&)>& callback) const override
66 {
67 callback(ScalingParameterDeclaration{"blending_factor", 1.0});
68 }
69
70 // Performs validation steps that are common for all TPS scaling steps, so overriders
71 // should call this base method.
72 std::vector<ScalingStepValidationMessage> implValidate(
74 const ScalingParameters&,
75 const OpenSim::Model& sourceModel) const override
76 {
77 std::vector<ScalingStepValidationMessage> messages;
78
79 // Ensure the model has a filesystem location (prerequisite).
80 const auto modelFilesystemLocation = opyn::TryFindInputFile(sourceModel);
81 if (not modelFilesystemLocation) {
82 messages.emplace_back(ScalingStepValidationState::Error, "The source model has no filesystem location.");
83 return messages;
84 }
85
86 // Ensure the `source_landmarks_file` can be found (relative to the model osim).
87 if (get_source_landmarks_file().empty()) {
88 messages.emplace_back(ScalingStepValidationState::Error, "`source_landmarks_file` is empty.");
89 }
90 else if (const auto sourceLandmarksPath = modelFilesystemLocation->parent_path() / osc::read_windows_or_unix_path_string(get_source_landmarks_file());
91 not std::filesystem::exists(sourceLandmarksPath)) {
92
93 messages.emplace_back(
95 std::format("{}: Cannot find source landmarks file on filesystem", sourceLandmarksPath.string())
96 );
97 }
98
99 // Ensure the `destination_landmarks_file` can be found (relative to the model osim).
100 if (get_destination_landmarks_file().empty()) {
101 messages.emplace_back(ScalingStepValidationState::Error, "`destination_landmarks_file` is empty.");
102 }
103 else if (const auto destinationLandmarksPath = modelFilesystemLocation->parent_path() / osc::read_windows_or_unix_path_string(get_destination_landmarks_file());
104 not std::filesystem::exists(destinationLandmarksPath)) {
105
106 messages.emplace_back(
108 std::format("{}: Cannot find destination landmarks file on filesystem", destinationLandmarksPath.string())
109 );
110 }
111
112 // Ensure `landmarks_frame` exists in the model
113 const auto* landmarksFrame = FindComponent<OpenSim::Frame>(sourceModel, get_landmarks_frame());
114 if (not landmarksFrame) {
115 messages.emplace_back(
117 std::format("{}: Cannot find this frame in the source model (or it isn't a Frame).", get_landmarks_frame())
118 );
119 }
120
121 return messages;
122 }
123
124 // Returns TPS `ScalingStep` parameters that are common for all uses of the TPS
125 // algorithm.
127 const ScalingParameters& parameters,
128 const OpenSim::Model& sourceModel,
129 const OpenSim::Model& resultModel) const
130 {
131 // Lookup/validate warping inputs.
132 const std::optional<std::filesystem::path> modelFilesystemLocation = TryFindInputFile(resultModel);
133 OSC_ASSERT_ALWAYS(modelFilesystemLocation && "The source model has no filesystem location");
134
135 OSC_ASSERT_ALWAYS(not get_source_landmarks_file().empty());
136 const std::filesystem::path sourceLandmarksPath = modelFilesystemLocation->parent_path() / osc::read_windows_or_unix_path_string(get_source_landmarks_file());
137
138 OSC_ASSERT_ALWAYS(not get_destination_landmarks_file().empty());
139 const std::filesystem::path destinationLandmarksPath = modelFilesystemLocation->parent_path() / osc::read_windows_or_unix_path_string(get_destination_landmarks_file());
140
141 OSC_ASSERT_ALWAYS(not get_landmarks_frame().empty());
142
143 const auto* sourceLandmarksFrame = FindComponent<OpenSim::Frame>(sourceModel, get_landmarks_frame());
144 OSC_ASSERT_ALWAYS(sourceLandmarksFrame && "could not find the landmarks frame in the source model");
145
146 const auto* resultLandmarksFrame = FindComponent<OpenSim::Frame>(resultModel, get_landmarks_frame());
147 OSC_ASSERT_ALWAYS(resultLandmarksFrame && "could not find the landmarks frame in the model");
148
149 const std::optional<double> blendingFactor = parameters.lookup<double>("blending_factor");
150 OSC_ASSERT_ALWAYS(blendingFactor && "blending_factor was not set by the warping engine");
151
152 return CommonParameters{
153 .tpsInputs = ThinPlateSplineCommonInputs{
154 sourceLandmarksPath,
155 destinationLandmarksPath,
156 get_source_landmarks_prescale(),
157 get_destination_landmarks_prescale(),
158 *blendingFactor,
159 get_warping_penalty(),
160 },
161 .sourceLandmarksFrame = sourceLandmarksFrame,
162 .resultLandmarksFrame = resultLandmarksFrame,
163 .compensateForFrameChanges = get_compensate_for_frame_changes(),
164 };
165 }
166 };
167}
#define OSC_ASSERT_ALWAYS(expr)
Definition assertions.h:20
Definition scaling_cache.h:31
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:29
std::vector< ScalingStepValidationMessage > implValidate(ScalingCache &, const ScalingParameters &, const OpenSim::Model &sourceModel) const override
Definition thin_plate_spline_scaling_step.h:72
CommonParameters calcTPSScalingStepCommonParams(const ScalingParameters &parameters, const OpenSim::Model &sourceModel, const OpenSim::Model &resultModel) const
Definition thin_plate_spline_scaling_step.h:126
void implForEachScalingParameterDeclaration(const std::function< void(const ScalingParameterDeclaration &)> &callback) const override
Definition thin_plate_spline_scaling_step.h:65
ThinPlateSplineScalingStep(std::string_view label)
Definition thin_plate_spline_scaling_step.h:52
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
std::filesystem::path read_windows_or_unix_path_string(const std::string &)
Definition thin_plate_spline_common_inputs.h:13