util.cpp
Go to the documentation of this file.
1#include "util.h"
2
3#include <algorithm>
4#include <cmath>
5#include <limits>
6#include <optional>
7#include <set>
8#include <string>
9#include <vector>
10
11#include <range/v3/range/conversion.hpp>
12#include <range/v3/view/transform.hpp>
13
14#include <SimoxUtility/algorithm/string.h>
15
21
23
26
28{
29
30 std::optional<Eigen::Vector3f>
32 {
33 Eigen::Vector3f sumPosition = Eigen::Vector3f::Zero();
34 int sumWeights = 0;
35 for (const auto& [id, keypoint] : humanPose.keypoints)
36 {
37 switch (coordSystem)
38 {
39 case Camera:
40 {
41 sumPosition += keypoint.positionCamera.toEigen();
42 sumWeights += 1;
43 break;
44 }
45 case Global:
46 [[fallthrough]];
48 {
49 if (keypoint.positionGlobal)
50 {
51 sumPosition += keypoint.positionGlobal.value().toEigen();
52 sumWeights += 1;
53 }
54 break;
55 }
56 case Robot:
57 {
58 if (keypoint.positionRobot)
59 {
60 sumPosition += keypoint.positionRobot.value().toEigen();
61 sumWeights += 1;
62 }
63 break;
64 }
65 default:
66 break;
67 }
68 }
69 if (sumWeights == 0)
70 {
71 return std::nullopt;
72 }
73
75 {
76 sumPosition.z() = 0.0;
77 }
78
79 return sumPosition / sumWeights;
80 }
81
82 std::optional<HumanPose>
83 getNearestHuman(const std::vector<HumanPose>& humanPoses,
84 const NearestHumanParameters& parameters)
85 {
86 std::optional<HumanPose> nearestHumanPose = std::nullopt;
87 double minDistance = std::numeric_limits<double>::infinity();
88 for (const HumanPose& humanPose : humanPoses)
89 {
90 if (parameters.modelIDs.empty() or parameters.modelIDs.count(humanPose.poseModelId))
91 {
92 const std::optional<Eigen::Vector3f> meanPosition =
93 computeMeanPosition(humanPose, parameters.coordSystem);
94 if (not meanPosition)
95 {
96 continue;
97 }
98 const double distance = (meanPosition.value() - parameters.positionMM).norm();
99 if (distance < minDistance)
100 {
101 minDistance = distance;
102 nearestHumanPose = humanPose;
103 }
104 }
105 }
106 if (parameters.distanceThresholdMM and minDistance > parameters.distanceThresholdMM)
107 {
108 return std::nullopt;
109 }
110 return nearestHumanPose;
111 }
112
113 void
115 const std::string& prefix)
116 {
117 const std::string prefix_ = prefix + "nearestHuman.";
118 def->optional(
119 positionMM.x(), prefix_ + "position.x", "Anchor position (x-coordinate) in mm.");
120 def->optional(
121 positionMM.y(), prefix_ + "position.y", "Anchor position (y-coordinate) in mm.");
122 def->optional(
123 positionMM.z(), prefix_ + "position.z", "Anchor position (z-coordinate) in mm.");
124 def->optional(
125 coordSystem, prefix_ + "coordSystem", "Coordinate system of the anchor position.")
128 .map("GlobalFloorProjection", KeyPointCoordinateSystem::GlobalFloorProjection)
130 .setCaseInsensitive(true);
131 def->optional(distanceThresholdMM,
132 prefix_ + "distanceThreshold",
133 "Optional minimum distance threshold in mm.");
134 def->optional(
135 modelIDs, prefix_ + "allowedModelIDs", "Allowed pose model ids, seperated by commas.");
136 }
137
138 void
140 {
141 positionMM = properties.positionMM;
142 coordSystem = properties.coordSystem;
143 if (properties.distanceThresholdMM > 0.0)
144 {
145 distanceThresholdMM = properties.distanceThresholdMM;
146 }
147 const std::vector<std::string> ids = simox::alg::split(properties.modelIDs, ",");
148 modelIDs = std::set<std::string>(ids.begin(), ids.end());
149
150 ARMARX_INFO << "Setting nearest human position to " << positionMM.transpose()
151 << " in frame " << coordSystem;
152 }
153
154 std::optional<Eigen::Vector3f>
156 {
157 // We can either use both shoulders, or both ears, or ...
158 // Possible candidates are listed here. We check if they are suitable
159 // iteratively
160
162 {
163 ARMARX_WARNING << "NYI";
164 return std::nullopt;
165 }
166
168
169 const std::vector<std::vector<Joints>> jointCandidates{
170 {Joints::ShoulderLeft, Joints::ShoulderRight},
171 {Joints::HipLeft, Joints::HipRight},
172 {Joints::EarLeft, Joints::EarRight},
173 {Joints::KneeLeft, Joints::KneeRight}};
174
175 const auto toString = [](const std::vector<Joints>& joints)
176 {
177 const auto toString = [](const Joints& joint)
179
180 return joints | ranges::views::transform(toString) | ranges::to_vector;
181 };
182
183 const std::vector<std::vector<std::string>> candidates =
184 jointCandidates | ranges::views::transform(toString) | ranges::to_vector;
185
186
187 std::vector<std::string> suitableKeypoints;
188 for (const auto& candidate : candidates)
189 {
190 if (checkVisibility(pose, candidate, 0.3))
191 {
192 ARMARX_INFO << "Chosen candidates for pose direction: " << candidate;
193 suitableKeypoints = candidate;
194 break;
195 }
196 }
197
198 // did we find anything usable?
199 if (suitableKeypoints.empty())
200 {
201 return std::nullopt;
202 }
203
204 // idea: take vector between both points, rotate by 90 degrees, and project to
205 // x/y direction
206
207 ARMARX_CHECK_EQUAL(suitableKeypoints.size(), 2);
208 const auto& kpLeft = pose.keypoints.at(suitableKeypoints.at(0));
209 const auto& kpRight = pose.keypoints.at(suitableKeypoints.at(1));
210
211 ARMARX_CHECK_NOT_NULL(kpLeft.positionGlobal);
212 ARMARX_CHECK_NOT_NULL(kpRight.positionGlobal);
213
214 ARMARX_CHECK_EQUAL(kpLeft.positionGlobal->frame, armarx::GlobalFrame);
215 ARMARX_CHECK_EQUAL(kpRight.positionGlobal->frame, armarx::GlobalFrame);
216
217 const Eigen::Vector3f posLeftGlobal = kpLeft.positionGlobal.value().toEigen();
218 const Eigen::Vector3f posRightGlobal = kpRight.positionGlobal.value().toEigen();
219
220 const Eigen::Vector3f leftToRightGlobal = posRightGlobal - posLeftGlobal;
221
222 // from shoulder line to forward direction
223 Eigen::Vector3f forwardDirectionOfHumanGlobal =
224 Eigen::AngleAxisf(M_PI_2, Eigen::Vector3f::UnitZ()) * leftToRightGlobal;
225
226 // project to x/y plane
227 forwardDirectionOfHumanGlobal.z() = 0.0f;
228
229 // ensure unit length
230 forwardDirectionOfHumanGlobal.normalize();
231
232 return forwardDirectionOfHumanGlobal;
233 }
234
235 bool
236 checkVisibility(const HumanPose& pose, std::vector<std::string> labels, const float threshold)
237 {
238 // for every keypoint...
239 for (const auto& [u, v] : pose.keypoints)
240 {
241 //... if it exists ...
242 if (v.positionGlobal.has_value())
243 {
244 // ... find corresponding entry in the vector
245 auto elem = std::find(labels.begin(), labels.end(), u);
246
247 // check if keypoint is in label vector
248 if (elem == labels.end())
249 {
250 // this label is not interesting
251 continue;
252 }
253
254 // is the provided confidence sufficient?
255 if (v.confidence < threshold)
256 {
257 // we found a keypoint, but the confidence is not sufficient
258 return false;
259 }
260 else
261 {
262 // if all is well, remove entry from vector
263 labels.erase(elem);
264 }
265 }
266 }
267
268 // did we miss a keypoint?
269 return labels.empty();
270 }
271
272 std::optional<Eigen::Isometry2f>
273 calculatePose(const HumanPose& humanPose)
274 {
275 const std::optional<Eigen::Vector3f> meanPositionGlobal =
277 if (not meanPositionGlobal)
278 {
279 return std::nullopt;
280 }
281
282 Eigen::Isometry2f global_T_human = Eigen::Isometry2f::Identity();
283 global_T_human.translation() = meanPositionGlobal->head<2>();
284 const std::optional<Eigen::Vector3f> directionOfHuman =
285 calculateDirectionOfHuman(humanPose);
286 if (not directionOfHuman)
287 {
288 return std::nullopt;
289 }
290
291 const float yaw = std::atan2(directionOfHuman->y(), directionOfHuman->x());
292 global_T_human.linear() = Eigen::Rotation2Df(yaw).toRotationMatrix();
293
294 return global_T_human;
295 }
296
297
298} // namespace armarx::armem::human
#define ARMARX_CHECK_NOT_NULL(ptr)
This macro evaluates whether ptr is not null and if it turns out to be false it will throw an Express...
#define ARMARX_CHECK_EQUAL(lhs, rhs)
This macro evaluates whether lhs is equal (==) rhs and if it turns out to be false it will throw an E...
#define ARMARX_INFO
The normal logging level.
Definition Logging.h:181
#define ARMARX_WARNING
The logging level for unexpected behaviour, but not a serious problem.
Definition Logging.h:193
std::string const GlobalFrame
Variable of the global coordinate system.
Definition FramedPose.h:65
std::optional< Eigen::Vector3f > computeMeanPosition(const HumanPose &humanPose, KeyPointCoordinateSystem coordSystem)
Definition util.cpp:31
std::optional< Eigen::Isometry2f > calculatePose(const HumanPose &humanPose)
Definition util.cpp:273
std::optional< Eigen::Vector3f > calculateDirectionOfHuman(const HumanPose &pose)
Definition util.cpp:155
std::optional< HumanPose > getNearestHuman(const std::vector< HumanPose > &humanPoses, const NearestHumanParameters &parameters)
Definition util.cpp:83
bool checkVisibility(const HumanPose &pose, std::vector< std::string > labels, const float threshold)
Definition util.cpp:236
const simox::meta::EnumNames< Joints > JointNames
Names of the joints as defined in the body model.
Joints
Joints with index as defined in the body model.
IceUtil::Handle< class PropertyDefinitionContainer > PropertyDefinitionsPtr
PropertyDefinitions smart pointer type.
double distance(const Point &a, const Point &b)
Definition point.hpp:95
void addTo(armarx::PropertyDefinitionsPtr &def, const std::string &prefix=std::string())
Definition util.cpp:114
KeyPointCoordinateSystem coordSystem
Definition util.h:44
std::optional< double > distanceThresholdMM
Definition util.h:45
struct armarx::armem::human::NearestHumanParameters::Properties properties
std::set< std::string > modelIDs
Definition util.h:46