9#include <boost/geometry.hpp>
10#include <boost/geometry/algorithms/assign.hpp>
11#include <boost/geometry/algorithms/centroid.hpp>
12#include <boost/geometry/geometries/point_xy.hpp>
13#include <boost/geometry/geometries/polygon.hpp>
26#include <range/v3/range/conversion.hpp>
27#include <range/v3/view/transform.hpp>
38 std::vector<DetectedHuman> newPoses =
40 ranges::views::transform(
43 return convertHumanPoseToDetectedHuman(measurements.
detectionTime, humanPose);
48 associateHumans(newPoses);
51 for (
const auto& detectedHuman : newPoses)
53 if (not detectedHuman.associated)
56 trackedHumans.push_back(
58 detectedHuman.detectionTime,
59 parameters.useKalmanFilter},
60 .trackingId = detectedHuman.trackingId,
61 .associatedCluster =
nullptr,
63 .confidence = parameters.confidenceCamera});
76 std::vector<AdvancedCluster> potentialFootprints;
77 std::vector<Cluster> unusedClusters;
78 for (
const auto& cluster : measurements.
clusters)
80 if(cluster.convexHull.empty())
82 ARMARX_VERBOSE <<
"Skipping cluster because convex hull is not available.";
86 if (getClusterSize(cluster) <= 2 * parameters.maxFootprintSize)
89 .center = getClusterCenter(cluster), .cluster = cluster, .associated =
false});
93 unusedClusters.push_back(cluster);
98 const auto sortedDistances = getSortedDistances(trackedHumans, potentialFootprints);
100 for (
auto& posDistance : sortedDistances)
102 if (posDistance.distance > parameters.maxAssociationDistance)
106 if (posDistance.oldHuman->associated || posDistance.newCluster->associated)
111 if (!posDistance.oldHuman->associatedCluster)
113 posDistance.oldHuman->associatedCluster = posDistance.newCluster;
114 posDistance.newCluster->associated =
true;
120 auto& footprint1 = posDistance.oldHuman->associatedCluster->cluster;
121 auto& footprint2 = posDistance.newCluster->cluster;
126 (getClusterCenter(footprint1) + getClusterCenter(footprint2)) / 2;
127 pose.linear() = posDistance.oldHuman->humanFilter.get().pose.linear();
130 posDistance.oldHuman->humanFilter.update(pose, measurements.
detectionTime);
132 posDistance.oldHuman->associatedCluster =
nullptr;
133 posDistance.oldHuman->associated =
true;
134 posDistance.newCluster->associated =
true;
135 posDistance.oldHuman->confidence += parameters.confidenceLaser;
140 for (
auto&
human : trackedHumans)
142 if (
human.associated)
147 if (
human.associatedCluster)
149 auto& footprint =
human.associatedCluster;
152 pose.translation() = footprint->center;
153 pose.linear() =
human.humanFilter.get().pose.linear();
157 human.associatedCluster =
nullptr;
158 human.associated =
true;
159 human.confidence += parameters.confidenceLaser;
164 for (
auto& cluster : potentialFootprints)
166 if (!cluster.associated)
168 unusedClusters.push_back(cluster.cluster);
171 return unusedClusters;
174 std::vector<human::Human>
177 return trackedHumans |
186 trackedHumans.clear();
190 HumanTracker::convertHumanPoseToDetectedHuman(
const DateTime& time,
193 const std::map<std::string, armem::human::PoseKeypoint>& keypoints = humanPose.
keypoints;
197 Eigen::Vector3f centerPos = Eigen::Vector3f::Zero();
199 for (
const auto& [_, v] : keypoints)
201 if (v.positionGlobal.has_value())
203 centerPos += v.positionGlobal.value().toEigen();
217 Eigen::Vector3f vec(1, 0, 0);
218 Eigen::Vector3f rot3 = qhead._transformVector(vec);
219 Eigen::Vector2f rot2(rot3.x(), rot3.y());
220 if (rot2.norm() != 0)
223 yaw = atan2(rot2.y(), rot2.x());
230 pose.linear() = Eigen::Rotation2Df(yaw).toRotationMatrix();
235 std::vector<HumanTracker::PosHumanDistance>
236 HumanTracker::getSortedDistances(std::vector<HumanTracker::TrackedHuman>& oldHumans,
237 std::vector<HumanTracker::DetectedHuman>& newHumans)
239 std::vector<PosHumanDistance> posDistances;
241 for (
auto& oldHuman : oldHumans)
243 if (oldHuman.associated)
247 for (
auto& newHuman : newHumans)
249 if (newHuman.associated)
255 posDistances.push_back(
258 (newHuman.pose.translation() - oldHuman.humanFilter.get().pose.translation())
264 std::sort(posDistances.begin(),
267 { return a.distance < b.distance; });
272 std::vector<HumanTracker::PosLaserDistance>
273 HumanTracker::getSortedDistances(std::vector<TrackedHuman>& oldHumans,
274 std::vector<AdvancedCluster>& newClusters)
276 std::vector<PosLaserDistance> posDistances;
278 for (
auto& oldHuman : oldHumans)
280 for (
auto& newCluster : newClusters)
283 posDistances.push_back(
286 (newCluster.center - oldHuman.humanFilter.get().pose.translation()).norm()});
291 std::sort(posDistances.begin(),
294 { return a.distance < b.distance; });
300 HumanTracker::associateHumans(std::vector<DetectedHuman>& detectedHumans)
303 for (
auto& oldHuman : trackedHumans)
305 if (oldHuman.associated || not oldHuman.trackingId)
309 for (
auto& newHuman : detectedHumans)
311 if (newHuman.associated || not newHuman.trackingId)
317 if (oldHuman.trackingId.value() == newHuman.trackingId.value())
319 associate(&oldHuman, &newHuman);
325 const auto sortedDistances = getSortedDistances(trackedHumans, detectedHumans);
327 for (
auto& posDistance : sortedDistances)
329 if (posDistance.distance > parameters.maxAssociationDistance)
333 if (posDistance.oldHuman->associated || posDistance.newHuman->associated)
338 associate(posDistance.oldHuman, posDistance.newHuman);
343 HumanTracker::associate(TrackedHuman* trackedHuman, DetectedHuman* detectedHuman)
349 trackedHuman->associated =
true;
350 detectedHuman->associated =
true;
352 trackedHuman->humanFilter.update(detectedHuman->pose, detectedHuman->detectionTime);
353 trackedHuman->trackingId = detectedHuman->trackingId;
355 trackedHuman->confidence = parameters.confidenceCamera;
359 HumanTracker::getClusterSize(
Cluster cluster)
361 return 2 * cluster.circle.radius;
365 HumanTracker::getClusterCenter(
Cluster cluster)
367 typedef boost::geometry::model::d2::point_xy<double> boost_point;
368 typedef boost::geometry::model::polygon<boost_point> boost_polygon;
371 std::vector<boost_point> points;
372 for (
auto& point : cluster.convexHull)
374 points.push_back(boost_point(point.x(), point.y()));
378 boost_point(cluster.convexHull.front().x(), cluster.convexHull.front().y()));
380 boost::geometry::assign_points(poly, points);
383 boost::geometry::centroid(poly, p);
385 return Eigen::Vector2f{p.x(), p.y()};
389 HumanTracker::prepareTrackedHumans(DateTime time)
392 for (
auto it = trackedHumans.begin(); it != trackedHumans.end();)
397 if (human.humanFilter.detectionAge(time) >= parameters.maxTrackingAge ||
398 human.confidence <= 0)
400 it = trackedHumans.erase(it);
405 human.associatedCluster =
nullptr;
406 human.associated =
false;
407 human.humanFilter.propagation(time);
#define ARMARX_CHECK_NOT_EMPTY(c)
Represents a point in time.
The HumanFilter class can be used to track and filter the state of a single human.
const Human & get() const
HumanFilter::get returns the human whose pose is filtered containing the most recent state.
void update(const CameraMeasurement &measurements)
HumanTracker::update Updates the tracked humans with the human measurements from a camera.
void reset()
HumanTracker::reset Resets this instance to the same state as if it just would have been created.
std::vector< human::Human > getTrackedHumans() const
HumanTracker::getTrackedHumans Returns all humans that are currently tracked.
#define ARMARX_CHECK(expression)
Shortcut for ARMARX_CHECK_EXPRESSION.
#define ARMARX_VERBOSE
The logging level for verbose information.
Quaternion< float, 0 > Quaternionf
std::vector< Eigen::Vector2f > to2D(const std::vector< Eigen::Vector3f > &v)
This file is part of ArmarX.
memory::LaserScannerFeature Cluster
std::optional< std::string > humanTrackingId
std::optional< armarx::FramedOrientation > orientationGlobal
std::vector< armem::human::HumanPose > humanPoses
std::vector< Cluster > clusters
const std::string rotationKeypoint