7#include <Eigen/Geometry>
28#include <VisionX/libraries/armem_human/aron/FaceRecognition.aron.generated.h>
29#include <VisionX/libraries/armem_human/aron/HumanPose.aron.generated.h>
30#include <VisionX/libraries/armem_human/aron/Person.aron.generated.h>
31#include <VisionX/libraries/armem_human/aron/PersonInstance.aron.generated.h>
44 faceRecognitionReader(mns.useReader(
armarx::human::FaceRecognitionCoreSegmentID)),
45 personInstanceWriter(mns.useWriter(
armarx::human::PersonInstanceCoreSegmentID)),
46 personInstanceReader(mns.useReader(
armarx::human::PersonInstanceCoreSegmentID)),
47 poseReader(mns.useReader(
armarx::human::PoseCoreSegmentID)),
48 properties_(properties)
50 personInstanceReaderV2.connect(mns);
62 const auto result = personInstanceReaderV2.queryResolved(query);
65 for (
const auto& personInstance : result.personInstances)
67 std::string name =
"~unknown~";
68 if (personInstance.profile.has_value())
70 name = personInstance.profile->id.firstName +
" " +
71 personInstance.profile->id.lastName;
74 std::string trackingId =
"~none~";
75 if (personInstance.humanPose.has_value() and
76 personInstance.humanPose->humanTrackingId.has_value())
78 trackingId = personInstance.humanPose->humanTrackingId.value();
104 std::lock_guard g{consumeMtx};
106 const bool verbose =
false;
108 ARMARX_DEBUG <<
"Consuming face recognition update for "
109 << (faceRecognition.
profileID.has_value()
115 { logFaceRecognitionUpdateDuration(duration); });
122 if (not faceRecognition.
profileID.has_value())
125 <<
"Face recognition " + faceRecognitionID.
entityName +
126 " does not provide valid profileID, finding matching "
127 "personInstance is not possible.";
133 ARMARX_DEBUG <<
"Trying to match face recognition with profile ID: "
136 if (queryResult.success)
141 std::optional<PersonInstanceWithID> personInstanceMatched =
142 findMatchingPersonInstance(queryResult, profileID);
145 if (personInstanceMatched.has_value())
147 ARMARX_DEBUG <<
"Found existing PersonInstance, updating it.";
149 << personInstanceMatched->memoryId.entityName;
152 toAron(personInstanceMatched->personInstance.faceRecognitionID, faceRecognitionID);
156 fromAron(personInstanceMatched->personInstance.poseID, poseID);
159 << (isMemoryIdFullySpecified(poseID) ? poseID.
entityName :
"~none~");
162 const std::optional<::armarx::armem::human::HumanPose> humanPose =
163 humanPoseFromMemId(poseID);
164 const std::optional<armarx::armem::human::FaceRecognition> faceRecognition =
165 faceRecognitionFromMemId(faceRecognitionID);
171 bool poseIdWasCleared =
false;
172 if (humanPose.has_value())
175 if (not checkHumanPoseForPlausability(humanPose.value(),
176 faceRecognition.value()))
179 <<
"The face recognition and the human pose are not "
180 "consistent. Dropping the human pose track.";
181 clearPoseIdFromPersonInstance(personInstanceMatched.value());
182 poseIdWasCleared =
true;
185 else if (isMemoryIdFullySpecified(poseID))
189 <<
"The human pose is likely outdated and meaningless. We remove "
191 clearPoseIdFromPersonInstance(personInstanceMatched.value());
192 poseIdWasCleared =
true;
195 ARMARX_DEBUG <<
"Human Pose exists and memory ID is fully specified";
199 if (poseIdWasCleared or not humanPose.has_value())
201 Eigen::Isometry3f globalPose = Eigen::Isometry3f::Identity();
202 globalPose.translation() = faceRecognition.value().position3DGlobal;
203 personInstanceMatched->personInstance.pose = globalPose.matrix();
207 if (not poseIdWasCleared and isMemoryIdFullySpecified(poseID))
209 ARMARX_DEBUG <<
"A valid pose id for the person instance exists";
218 .withEntityName(faceRecognition.value().profileID.value().entityName);
220 update.instancesData = {personInstanceMatched.value().personInstance.toAron()};
221 personInstanceWriter.commit(update);
227 <<
"The person instance does not have an assigned pose id. Will try to "
228 "match existing ones.";
233 const std::optional<HumanPoseWithID> closestPoseID =
234 getClosestPoseID(faceRecognition->position3DGlobal);
236 if (closestPoseID.has_value())
240 <<
"Found matching human pose with tracking id "
241 <<
QUOTED(closestPoseID->humanPose.humanTrackingId.value_or(
""))
243 << personInstanceMatched->personInstance.profileID.entityName;
244 setPoseIdForPersonInstance(personInstanceMatched.value(),
245 closestPoseID->memoryId);
248 fromAron(personInstanceMatched->personInstance.profileID, profileId);
251 ensureTrackingIdUniqueness(closestPoseID->humanPose.humanTrackingId.value(),
257 <<
"No human pose could be found close to the given face position";
266 armarx::human::arondto::PersonInstance personInstance;
269 toAron(personInstance.faceRecognitionID, faceRecognitionID);
270 toAron(personInstance.profileID, profileID);
273 const std::optional<HumanPoseWithID> closestPoseID =
275 if (closestPoseID.has_value())
277 std::string name =
"";
278 if (faceRecognition.
profileID.has_value())
280 name = faceRecognition.
profileID->entityName;
284 <<
QUOTED(closestPoseID->humanPose.humanTrackingId.value_or(
""))
285 <<
") for face recognition of " <<
QUOTED(name)
286 <<
" due to spatial proximity.";
287 toAron(personInstance.poseID, closestPoseID->memoryId);
290 ensureTrackingIdUniqueness(closestPoseID->humanPose.humanTrackingId.value(),
294 if (faceRecognition.
profileID.value().entityName.empty())
301 ARMARX_VERBOSE <<
"update pose based on latest face recognition result ("
303 Eigen::Isometry3f globalPose = Eigen::Isometry3f::Identity();
306 personInstance.pose = globalPose.matrix();
312 .withEntityName(faceRecognition.
profileID.value().entityName);
314 update.instancesData = {personInstance.toAron()};
315 personInstanceWriter.commit(update);
334 std::lock_guard g{consumeMtx};
358 std::optional<::armarx::armem::human::PersonInstance> matchingInstance = std::nullopt;
359 std::optional<::armarx::armem::MemoryID> matchingInstanceId = std::nullopt;
360 float closestDistance = std::numeric_limits<float>::max();
364 armarx::human::arondto::PersonInstance>& instance)
366 ARMARX_DEBUG <<
"In iteration: " << instance.id().entityName;
368 const auto& personInstance = instance.data();
371 fromAron(personInstance.poseID, currentPoseID);
372 auto pose = humanPoseFromMemId(currentPoseID);
377 fromAron(personInstance.faceRecognitionID, currentRecognitionId);
378 auto recognition = faceRecognitionFromMemId(currentRecognitionId);
379 if (recognition.has_value())
382 auto headPos = getHeadPos(humanPose);
383 if (headPos.has_value())
386 getDistance(recognition->position3DGlobal, headPos.value());
387 ARMARX_VERBOSE <<
"Distance from pose to " << instance.id().entityName
392 distance <= properties_.maxFaceHeadDistance)
395 fromAron(personInstance, bo);
396 matchingInstance = bo;
397 matchingInstanceId = instance.id();
400 <<
" is closer (distance: " <<
distance <<
"mm)";
407 if (pose.has_value())
410 << instance.id().entityName <<
" with tracking id "
411 <<
QUOTED(pose->humanTrackingId.value_or(
""));
413 if (pose->humanTrackingId.has_value() and
419 << instance.id().entityName;
421 fromAron(personInstance, bo);
422 matchingInstance = bo;
423 matchingInstanceId = instance.id();
424 closestDistance = -1.0f;
432 if (matchingInstance.has_value())
440 ensureTrackingIdUniqueness(humanPose.
humanTrackingId.value(), profileId);
444 updateInstanceWithNewPoseId(
445 matchingInstanceId.value(), matchingInstance.value(), poseID, humanPose);
450 ARMARX_DEBUG <<
"No matching instance found, creating new...";
451 createNewInstanceBasedOnPose(poseID, humanPose);
465 std::lock_guard g{consumeMtx};
473 armarx::human::arondto::PersonInstance dto;
476 .instancesData = {dto.toAron()},
479 <<
"Removing pose from PersonInstance: " << personInstanceId.
entityName;
480 personInstanceWriter.
commit(update);
489 UpdateConsumer::createNewInstanceBasedOnPose(
const armarx::armem::MemoryID& poseId,
490 const armarx::armem::human::HumanPose& pose)
496 std::optional<armarx::FramedPosition> headPos = getHeadPos(pose);
497 if (headPos.has_value())
500 std::optional<armarx::armem::MemoryID> faceRecognitionID =
501 getClosestFaceID(headPos.value());
502 if (faceRecognitionID.has_value())
504 armarx::human::arondto::PersonInstance personInstance;
505 toAron(personInstance.faceRecognitionID, faceRecognitionID.value());
509 auto faceRecognition = faceRecognitionFromMemId(faceRecognitionID.value());
510 if (not faceRecognition.has_value())
516 const auto profileId = faceRecognition->profileID;
517 if (not profileId.has_value())
520 <<
"While creating a new instance based on pose: closest face "
521 "recognition has no attached profile. Giving up.";
525 const std::string& entityName = profileId->entityName;
528 toAron(personInstance.profileID, profileId.value());
541 ARMARX_VERBOSE <<
"update pose based on latest human pose result";
542 Eigen::Isometry3f globalPose = Eigen::Isometry3f::Identity();
544 if (pose.
keypoints.at(headKp).orientationGlobal)
547 pose.
keypoints.at(headKp).orientationGlobal->getFrame(),
549 globalPose.linear() =
550 pose.
keypoints.at(headKp).orientationGlobal->toEigen();
553 if (pose.
keypoints.at(headKp).positionGlobal)
557 globalPose.translation() =
558 pose.
keypoints.at(headKp).positionGlobal->toEigen();
561 personInstance.pose = globalPose.matrix();
567 toAron(personInstance.poseID, poseId);
572 ensureTrackingIdUniqueness(pose.
humanTrackingId.value(), profileId.value());
575 armarx::armem::EntityUpdate
update;
580 update.instancesData = {personInstance.toAron()};
581 personInstanceWriter.commit(update);
591 UpdateConsumer::updateInstanceWithNewPoseId(
const armarx::armem::MemoryID& instanceId,
592 armarx::armem::human::PersonInstance oldInstance,
593 const armarx::armem::MemoryID& newPoseId,
594 const ::armarx::armem::human::HumanPose& pose)
603 if (pose.keypoints.at(headKp).orientationGlobal)
608 pose.keypoints.at(headKp).orientationGlobal->toEigen();
611 if (pose.keypoints.at(headKp).positionGlobal)
616 pose.keypoints.at(headKp).positionGlobal->toEigen();
619 ARMARX_DEBUG <<
"update pose based on latest human pose result: "
620 << oldInstance.
globalPose.translation().transpose();
625 oldInstance.
poseID = newPoseId;
626 armarx::human::arondto::PersonInstance dto;
628 armarx::armem::EntityUpdate
update{
630 .instancesData = {dto.toAron()},
635 ARMARX_DEBUG <<
"Committing new person instance with updated pose for entity: "
637 personInstanceWriter.commit(update);
641 UpdateConsumer::checkHumanPoseForPlausability(
642 const ::armarx::armem::human::HumanPose& pose,
643 const ::armarx::armem::human::FaceRecognition& face)
649 auto facePosFromPose = getHeadPos(pose);
650 if (not facePosFromPose.has_value())
655 auto facePosFromFaceRecognition = face.position3DGlobal;
656 float dist = getDistance(facePosFromFaceRecognition, facePosFromPose.value());
658 return dist <= properties_.maxFaceHeadDistance;
662 UpdateConsumer::checkForPlausability(const ::armarx::armem::human::HumanPose& pose,
663 const ::armarx::armem::human::FaceRecognition& face)
666 auto facePosFromPose = getHeadPos(pose);
667 if (not facePosFromPose.has_value())
672 auto facePosFromFaceRecognition = face.position3DGlobal;
673 float dist = getDistance(facePosFromFaceRecognition, facePosFromPose.value());
675 return dist <= properties_.maxFaceHeadDistance;
688 std::optional<UpdateConsumer::HumanPoseWithID>
689 UpdateConsumer::getClosestPoseID(
const Eigen::Vector3f& facePos)
692 armarx::armem::client::QueryResult queryResult =
695 std::optional<UpdateConsumer::HumanPoseWithID> closestPoseID;
696 float closestDistance =
697 properties_.maxFaceHeadDistance;
703 [&facePos, &closestPoseID, &closestDistance](
707 armarx::armem::human::HumanPose pose;
711 std::optional<armarx::FramedPosition> headPos_opt = getHeadPos(pose);
712 if (headPos_opt.has_value())
714 const armarx::FramedPosition& headPos = headPos_opt.value();
715 float distance = getDistance(facePos, headPos);
724 HumanPoseWithID{.humanPose = pose, .memoryId = instance.
id()};
730 return closestPoseID;
733 std::optional<armarx::armem::MemoryID>
734 UpdateConsumer::getClosestFaceID(
const armarx::FramedPosition& headPos)
736 armarx::armem::client::QueryResult queryResult =
739 std::optional<armarx::armem::MemoryID> closestFaceID = std::nullopt;
740 float closestDistance = properties_.maxFaceHeadDistance;
745 [&headPos, &closestFaceID, &closestDistance](
747 armarx::human::arondto::FaceRecognition>& instance)
749 armarx::armem::human::FaceRecognition faceRecognition;
761 closestFaceID = instance.
id();
766 return closestFaceID;
770 UpdateConsumer::addPoseToInstance(
const armarx::armem::MemoryID& memId,
771 const armarx::armem::MemoryID& poseId,
772 armarx::armem::human::PersonInstance currentInstance)
774 currentInstance.
poseID = poseId;
775 armarx::human::arondto::PersonInstance dto;
776 toAron(dto, currentInstance);
777 armarx::armem::EntityUpdate
update{
779 .instancesData = {dto.toAron()},
784 personInstanceWriter.commit(update);
787 std::optional<armarx::armem::human::FaceRecognition>
788 UpdateConsumer::faceRecognitionFromMemId(
const armarx::armem::MemoryID& memId)
801 auto result = faceRecognitionReader.query(qb);
802 if (not result.success)
807 std::optional<armarx::armem::human::FaceRecognition> resFace = std::nullopt;
808 result.memory.forEachInstanceWithDataAs(
810 armarx::human::arondto::FaceRecognition>& instance)
812 armarx::armem::human::FaceRecognition res;
817 if (not resFace.has_value())
824 std::optional<armarx::armem::human::HumanPose>
825 UpdateConsumer::humanPoseFromMemId(
const armarx::armem::MemoryID& memId)
837 auto result = poseReader.query(qb);
838 if (not result.success)
843 std::optional<armarx::armem::human::HumanPose> resPose;
844 result.memory.forEachInstanceWithDataAs(
849 armarx::armem::human::HumanPose res;
855 if (not resPose.has_value())
862 std::optional<armarx::FramedPosition>
863 UpdateConsumer::getHeadPos(armarx::armem::human::HumanPose pose)
880 return pose.
keypoints.at(headKp).positionGlobal;
895 UpdateConsumer::getDistance(
const Eigen::Vector3f& facePos,
896 const armarx::FramedPosition& headPos)
900 const Eigen::Vector3f headPosition = headPos.
toEigen();
902 return (headPosition - facePos).norm();
906 UpdateConsumer::ensureTrackingIdUniqueness(
const std::string& trackingId,
907 const armarx::armem::MemoryID& exceptProfileID)
910 armarx::armem::client::QueryResult queryResult =
920 [
this, &trackingId, &exceptProfileID](
924 armarx::armem::MemoryID thisProfileID;
928 if (thisProfileID == exceptProfileID)
934 armarx::human::arondto::PersonInstance personInstance = instance.
data();
935 armarx::armem::MemoryID humanPoseId;
936 fromAron(personInstance.poseID, humanPoseId);
937 const auto pose = humanPoseFromMemId(humanPoseId);
943 armarx::armem::MemoryID currentPoseID;
944 fromAron(personInstance.poseID, currentPoseID);
950 <<
QUOTED(trackingId) <<
" from PersonInstance "
951 <<
QUOTED(personInstance.profileID.entityName)
952 <<
" because it was reassigned.";
954 toAron(personInstance.poseID, armarx::armem::MemoryID());
956 armarx::armem::EntityUpdate
update;
959 update.instancesData = {personInstance.toAron()};
960 personInstanceWriter.commit(update);
972 armarx::armem::human::client::PersonInstanceReader::QueryResolved query;
981 const auto result = personInstanceReaderV2.queryResolved(query);
984 for (
const auto& personInstance : result.personInstances)
986 std::string name =
"~unknown~";
987 if (personInstance.profile.has_value())
989 name = personInstance.profile->id.firstName +
" " +
990 personInstance.profile->id.lastName;
993 std::string trackingId =
"~none~";
994 if (personInstance.humanPose.has_value() and
995 personInstance.humanPose->humanTrackingId.has_value())
997 trackingId = personInstance.humanPose->humanTrackingId.value();
1004 std::optional<UpdateConsumer::PersonInstanceWithID>
1005 UpdateConsumer::findMatchingPersonInstance(
1006 const armarx::armem::client::QueryResult& queryResult,
1007 const armarx::armem::MemoryID& profileID)
1009 std::optional<PersonInstanceWithID> result;
1012 [
this, &profileID, &result](
1016 armarx::human::arondto::PersonInstance personInstance = instance.
data();
1018 if (profileID.
entityName == personInstance.profileID.entityName)
1025 armarx::armem::human::PersonInstance bo;
1027 result = PersonInstanceWithID{.personInstance = personInstance,
1028 .memoryId = instance.
id()};
1036 UpdateConsumer::clearPoseIdFromPersonInstance(
const PersonInstanceWithID& personInstanceMatched)
1039 << personInstanceMatched.personInstance.profileID.entityName;
1040 auto personInstanceRevised = personInstanceMatched.personInstance;
1041 toAron(personInstanceRevised.poseID, armarx::armem::MemoryID());
1043 armarx::armem::EntityUpdate
update;
1044 update.entityID = personInstanceMatched.memoryId;
1046 update.instancesData = {personInstanceRevised.toAron()};
1047 personInstanceWriter.commit(update);
1051 UpdateConsumer::setPoseIdForPersonInstance(
const PersonInstanceWithID& personInstanceMatched,
1052 const armarx::armem::MemoryID& poseId)
1055 <<
QUOTED(personInstanceMatched.personInstance.profileID.entityName) <<
" to "
1057 auto personInstanceRevised = personInstanceMatched.personInstance;
1058 toAron(personInstanceRevised.poseID, poseId);
1060 armarx::armem::EntityUpdate
update;
1061 update.entityID = personInstanceMatched.memoryId;
1063 update.instancesData = {personInstanceRevised.toAron()};
1064 personInstanceWriter.commit(update);
1068 UpdateConsumer::isMemoryIdFullySpecified(
const armarx::armem::MemoryID& memoryId)
1078 if (consumeMtx.try_lock())
1080 consumeMtx.unlock();
SpamFilterDataPtr deactivateSpam(SpamFilterDataPtr const &spamFilter, float deactivationDurationSec, const std::string &identifier, bool deactivate)
static const std::string provider_name
bool isBusy()
Check if the consumer is currently processing an update.
void consumePoseUpdate(const armarx::armem::human::HumanPose &humanPose, const armarx::armem::MemoryID &poseID)
Process a new human pose update.
UpdateConsumer(armarx::armem::client::MemoryNameSystem &mns, const Properties &properties)
void consumeFaceRecognitionUpdate(const armarx::armem::human::FaceRecognition &faceRecognition, const armarx::armem::MemoryID &faceRecognitionID)
Process a new face recognition result.
void consumeProfileUpdate(const armarx::human::arondto::Person &profile, const armarx::armem::MemoryID &profileID)
Process a profile update (not yet implemented).
static DateTime Now()
Current time on the virtual clock.
static Duration Seconds(std::int64_t seconds)
Constructs a duration in seconds.
virtual Eigen::Vector3f toEigen() const
MemoryID withProviderSegmentName(const std::string &name) const
bool hasProviderSegmentName() const
bool hasEntityName() const
bool hasInstanceIndex() const
bool hasMemoryName() const
MemoryID withEntityName(const std::string &name) const
bool hasCoreSegmentName() const
bool hasTimestamp() const
MemoryID getEntityID() const
const DataT & data() const
The memory name system (MNS) client.
CommitResult commit(const Commit &commit) const
Writes a Commit to the memory.
CoreSegmentSelector & coreSegments()
Start specifying core segments.
CoreSegmentSelector & withID(const MemoryID &id) override
ProviderSegmentSelector & providerSegments()
Start specifying provider segments.
EntitySelector & all() override
SnapshotSelector & snapshots()
Start specifying entity snapshots.
ProviderSegmentSelector & withID(const MemoryID &id) override
EntitySelector & entities()
Start specifying entities.
SnapshotSelector & latest()
std::int64_t toMilliSeconds() const
Returns the amount of milliseconds.
Measures the time this stop watch was inside the current scope.
#define ARMARX_CHECK(expression)
Shortcut for ARMARX_CHECK_EXPRESSION.
#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.
#define ARMARX_IMPORTANT
The logging level for always important information, but expected behaviour (in contrast to ARMARX_WAR...
#define ARMARX_DEBUG
The logging level for output that is only interesting while debugging.
#define ARMARX_WARNING
The logging level for unexpected behaviour, but not a serious problem.
#define ARMARX_VERBOSE
The logging level for verbose information.
std::string const GlobalFrame
Variable of the global coordinate system.
query::Builder QueryBuilder
void fromAron(const armarx::human::arondto::HumanPose &dto, HumanPose &bo)
bool update(mongocxx::collection &coll, const nlohmann::json &query, const nlohmann::json &update)
base::EntityInstanceBase< AronDtoT, EntityInstanceMetadata > EntityInstanceBase
Entity instance with a concrete ARON DTO type as data.
armarx::core::time::Duration Duration
const std::string ModelId
const simox::meta::EnumNames< Joints > JointNames
Names of the joints as defined in the body model.
const std::string ModelId
const std::string ModelId
const armem::MemoryID FaceRecognitionCoreSegmentID
const armem::MemoryID PersonInstanceCoreSegmentID
const armem::MemoryID PoseCoreSegmentID
This file offers overloads of toIce() and fromIce() functions for STL container types.
void toAron(arondto::PackagePath &dto, const PackageFileLocation &bo)
void fromAron(const arondto::PackagePath &dto, PackageFileLocation &bo)
double distance(const Point &a, const Point &b)
An update of an entity for a specific point in time.
bool forEachInstanceWithDataAs(EntityInstanceBaseAronDtoFunctionT &&func) const
Call func on each instance with its data converted to Aron DTO class.
wm::Memory memory
The slice of the memory that matched the query.
Eigen::Vector3f position3DGlobal
std::optional< armarx::armem::MemoryID > profileID
std::optional< std::string > humanTrackingId
Eigen::Isometry3f globalPose
armarx::armem::MemoryID poseID
bool resolveFaceDetection