KeypointManager.cpp
Go to the documentation of this file.
1/*
2 * This file is part of ArmarX.
3 *
4 * ArmarX is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 2 as
6 * published by the Free Software Foundation.
7 *
8 * ArmarX is distributed in the hope that it will be useful, but
9 * WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 *
16 * @package <PACKAGE_NAME>::<CATEGORY>::KeypointManager
17 * @author Stefan Reither ( stef dot reither at web dot de )
18 * @date 2018
19 * @copyright http://www.gnu.org/licenses/gpl-2.0.txt
20 * GNU General Public License
21 */
22
23#include "KeypointManager.h"
24
25#include <numeric> // std::iota
26
28
29using namespace armarx;
30
31Keypoint::Keypoint(const Keypoint& p) : _id(p.getId()), _name(p.getName())
32{
33 if (p.isStereo())
34 {
35 auto pair = p.getStereo2D();
36 setStereo2D(pair.first, pair.second);
37 }
38 else
39 {
40 set2D(p.get2D());
41 }
42 if (p.is3DSet())
43 {
44 set3D(p.get3D());
45 }
47}
48
49Keypoint::Keypoint(float x, float y, unsigned int id, const std::string& name, float confidence) :
50 Keypoint(Point2D(x, y), id, name, confidence)
51{
52}
53
55 unsigned int id,
56 const std::string& name,
57 float confidence) :
58 _id(id), _name(name), _dominantColor(DrawColor24Bit{0, 0, 0})
59{
60 set2D(point);
61 setConfidence(confidence);
62}
63
65 const Point2D& right,
66 unsigned int id,
67 const std::string& name,
68 float confidence) :
69 _id(id), _name(name), _dominantColor(DrawColor24Bit{0, 0, 0})
70{
71 setStereo2D(left, right);
72 setConfidence(confidence);
73}
74
75bool
77{
78 return _pos3D ? true : false;
79}
80
81bool
83{
84 return !_pointR.isnan();
85}
86
87void
89{
91 _pointL = point;
92}
93
94void
95Keypoint::setStereo2D(const Point2D& left, const Point2D& right)
96{
97 set2D(left);
98 ARMARX_CHECK_EXPRESSION(!right.isnan());
99 _pointR = right;
100}
101
102void
104{
105 _pos3D = pos;
106}
107
108void
109Keypoint::setConfidence(float confidence)
110{
111 _confidence = confidence;
112}
113
114void
115Keypoint::setDominantColor(const DrawColor24Bit& color)
116{
117 _dominantColor = color;
118}
119
122{
123 return _pointL;
124}
125
126std::pair<Point2D, Point2D>
128{
130 return std::make_pair(_pointL, _pointR);
131}
132
135{
136 return _pos3D;
137}
138
139float
141{
142 return _confidence;
143}
144
145DrawColor24Bit
147{
148 return _dominantColor;
149}
150
151unsigned int
153{
154 return _id;
155}
156
157std::string
159{
160 return _name;
161}
162
163std::string
165{
166 std::stringstream ss;
167 ss << "Id: " << std::to_string(_id) << ", ";
168 ss << "Name: " << _name << ", ";
169 ss << "x: " << _pointL._x << ", ";
170 ss << "y: " << _pointL._y << ", ";
171 ss << "Confindence: " << _confidence;
172 return ss.str();
173}
174
175KeypointSetById::const_iterator
177{
178 return _keypoints.get<0>().begin();
179}
180
181KeypointSetById::const_iterator
183{
184 return _keypoints.get<0>().end();
185}
186
188KeypointObject::getNode(const std::string& nodeName) const
189{
190 auto itFound = _keypoints.get<1>().find(nodeName);
191 if (itFound != _keypoints.get<1>().end())
192 {
193 return *itFound;
194 }
195 else
196 {
197 return KeypointPtr();
198 }
199}
200
202KeypointObject::getNode(unsigned int id) const
203{
204 auto itFound = _keypoints.get<0>().find(id);
205 if (itFound != _keypoints.get<0>().end())
206 {
207 return *itFound;
208 }
209 else
210 {
211 return KeypointPtr();
212 }
213}
214
215void
217{
218 if (point)
219 {
220 _keypoints.insert(point);
221 }
222}
223
224void
225KeypointObject::removeNode(const std::string& nodeName)
226{
227 _keypoints.get<1>().erase(nodeName);
228}
229
230void
232{
233 _keypoints.get<0>().erase(id);
234}
235
236size_t
238{
239 return _keypoints.get<0>().size();
240}
241
242float
244{
245 if (size() == 0)
246 {
247 return std::numeric_limits<float>::quiet_NaN();
248 }
249 float result = 0.0f;
250 int amountDepths = 0;
251 for (KeypointPtr point : _keypoints.get<0>())
252 {
254 point
255 ->is3DSet()); // if any point in this object has no 3D-information, something went wrong.
256 result += point->get3D()->z;
257 amountDepths++;
258 }
259 return result / static_cast<float>(amountDepths);
260}
261
262Keypoint2DMap
264{
265 Keypoint2DMap iceMap;
266 for (auto [id, label] : idNameMap)
267 {
268 KeypointPtr keypoint = this->getNode(id);
269 if (keypoint)
270 {
271 Keypoint2D iceKeypoint;
272 iceKeypoint.x = keypoint->get2D()._x;
273 iceKeypoint.y = keypoint->get2D()._y;
274 iceKeypoint.label = keypoint->getName();
275 iceKeypoint.confidence = keypoint->getConfidence();
276 iceKeypoint.dominantColor = keypoint->getDominantColor();
277 iceMap.insert(std::make_pair(iceKeypoint.label, iceKeypoint));
278 }
279 else
280 {
281 Keypoint2D iceKeypoint;
282 iceKeypoint.x = 0.0f;
283 iceKeypoint.y = 0.0f;
284 iceKeypoint.label = label;
285 iceKeypoint.confidence = 0.0f;
286 iceKeypoint.dominantColor = DrawColor24Bit{0, 0, 0}; // Black
287 iceMap.insert(std::make_pair(iceKeypoint.label, iceKeypoint));
288 }
289 }
290 return iceMap;
291}
292
293Keypoint3DMap
294KeypointObject::toIce3D(IdNameMap idNameMap, const VirtualRobot::RobotPtr& local_robot) const
295{
296 Keypoint3DMap iceMap;
297 for (auto [id, label] : idNameMap)
298 {
299 KeypointPtr keypoint = this->getNode(id);
300 if (keypoint && keypoint->is3DSet())
301 {
302 Keypoint3D iceKeypoint;
303 iceKeypoint.x = keypoint->get3D()->x;
304 iceKeypoint.y = keypoint->get3D()->y;
305 iceKeypoint.z = keypoint->get3D()->z;
306
307 Eigen::Vector3f globalPose = keypoint->get3D()->toGlobalEigen(local_robot);
308
309 iceKeypoint.globalX = globalPose(0);
310 iceKeypoint.globalY = globalPose(1);
311 iceKeypoint.globalZ = globalPose(2);
312
313 iceKeypoint.label = keypoint->getName();
314 iceKeypoint.confidence = keypoint->getConfidence();
315 iceKeypoint.dominantColor = keypoint->getDominantColor();
316 iceMap.insert(std::make_pair(keypoint->getName(), iceKeypoint));
317 }
318 else
319 {
320 Keypoint3D iceKeypoint;
321 iceKeypoint.x = 0.0f;
322 iceKeypoint.y = 0.0f;
323 iceKeypoint.z = 0.0f;
324 iceKeypoint.globalX = 0.0f;
325 iceKeypoint.globalY = 0.0f;
326 iceKeypoint.globalZ = 0.0f;
327 iceKeypoint.label = label;
328 iceKeypoint.confidence = 0.0f;
329 iceKeypoint.dominantColor = DrawColor24Bit{0, 0, 0}; // Black
330 iceMap.insert(std::make_pair(iceKeypoint.label, iceKeypoint));
331 }
332 }
333 return iceMap;
334}
335
336void
338{
339 if (object)
340 {
341 for (KeypointPtr keypoint : _keypoints.get<0>())
342 {
343 KeypointPtr otherPoint = object->getNode(keypoint->getId());
344 if (otherPoint)
345 {
346 ARMARX_CHECK_EXPRESSION(keypoint->getName() == otherPoint->getName());
347 keypoint->setStereo2D(keypoint->get2D(), otherPoint->get2D());
348 // The combined confindence is defined as the average of both confidences
349 keypoint->setConfidence((keypoint->getConfidence() + otherPoint->getConfidence()) /
350 2.0f);
351 }
352 }
353 }
354}
355
356std::string
358{
359 std::stringstream ss;
360 for (KeypointPtr keypoint : _keypoints.get<0>())
361 {
362 ss << keypoint->toString2D() << "\n";
363 }
364 return ss.str();
365}
366
367KeypointManager::KeypointManager(IdNameMap idNameMap) : _idNameMap(idNameMap)
368{
369}
370
374
375Keypoint2DMapList
377{
378 Keypoint2DMapList list;
379 for (KeypointObjectPtr o : _objects)
380 {
381 list.push_back(o->toIce2D(_idNameMap));
382 }
383 return list;
384}
385
386Keypoint2DMapList
387KeypointManager::toIce2D_normalized(int width, int height) const
388{
389 Keypoint2DMapList list = this->toIce2D();
390 for (Keypoint2DMap map : list)
391 {
392 for (auto pair : map)
393 {
394 Keypoint2D point = pair.second;
395 if (point.x < 0.0f || point.x > width || point.y < 0.0f || point.y > height)
396 {
397 point.x = 0.0;
398 point.y = 0.0;
399 }
400 else
401 {
402 point.x = point.x / static_cast<float>(width);
403 point.y = point.y / static_cast<float>(height);
404 }
405 }
406 }
407 return list;
408}
409
410Keypoint3DMapList
412{
413 Keypoint3DMapList list;
414 for (KeypointObjectPtr o : _objects)
415 {
416 list.push_back(o->toIce3D(_idNameMap, local_robot));
417 }
418 return list;
419}
420
421void
423{
424 // For every person in the left image, we need to calculate the distance of all keypoints to their corresponding keypoints of every object in the right image
425 // This results in a sum for each possible object-pair, which is the distance between two objects
426 // The persons in both images then have to be paired in such a way that the sum of distances between the two objects in a pair is minimized.
427
428 // It is assumed, that this manager contains the objects of the left image and the other manager conatains the objects of the right image
429 // Afterwards this manager contains still all objects of the left image, but as much as possible are matched to the objects in the other manager
430
431 size_t leftSize = getData().size();
432 size_t rightSize = other->getData().size();
433
434 ARMARX_DEBUG << VAROUT(leftSize) << " " << VAROUT(rightSize);
435
436 if (leftSize == 0 || rightSize == 0)
437 {
438 return;
439 }
440
441 std::vector<KeypointObjectPtr> moreObjects;
442 std::vector<KeypointObjectPtr> lessObjects;
443 if (leftSize > rightSize)
444 {
445 moreObjects = this->getData();
446 lessObjects = other->getData();
447 }
448 else
449 {
450 moreObjects = other->getData();
451 lessObjects = this->getData();
452 }
453
454 // With only one object in every manager we can simply match these two
455 if (leftSize == 1 && rightSize == 1)
456 {
457 KeypointObjectPtr o = getData().at(0);
458 KeypointObjectPtr o1 = other->getData().at(0);
459 o->matchWith(o1);
460 return;
461 }
462
463 Eigen::MatrixXf personDistances(lessObjects.size(),
464 moreObjects.size()); // rows = lessPersons; cols = morePersons
465 Eigen::MatrixXf validKeypointsRatio(
466 lessObjects.size(),
467 moreObjects
468 .size()); // to prevent, that artifacts of the openpose algorithm are used in the pairs...
469
470 for (unsigned int l = 0; l < lessObjects.size(); l++)
471 {
472 KeypointObjectPtr pl = lessObjects.at(l);
473 for (unsigned int r = 0; r < moreObjects.size(); r++)
474 {
475 KeypointObjectPtr pr = moreObjects.at(r);
476 double sum = 0.0;
477 int addedDistances = 0;
478 for (KeypointPtr kpl : *pl)
479 {
480 KeypointPtr kpr = pr->getNode(kpl->getId());
481 if (kpr)
482 {
483 sum += std::pow(kpl->get2D()._x - kpr->get2D()._x, 2) +
484 std::pow(kpr->get2D()._y - kpr->get2D()._y,
485 2); // squared euclidean distance
486 addedDistances++;
487 }
488 }
489
490 personDistances(l, r) = (addedDistances == 0)
491 ? std::numeric_limits<float>::max()
492 : static_cast<float>(sum) / addedDistances;
493
494 // Calculate ratio of valid keypoints
495 int validKeypointsPL = static_cast<int>(pl->size());
496 int validKeypointsPR = static_cast<int>(pr->size());
497 if (validKeypointsPL == 0 || validKeypointsPR == 0)
498 {
499 validKeypointsRatio(l, r) =
500 0.000000001f; // simply a very small number for a very bad ratio (ratio cannot be 0, because it is needed for a division later)
501 }
502 else if (validKeypointsPL > validKeypointsPR)
503 {
504 validKeypointsRatio(l, r) =
505 static_cast<float>(validKeypointsPR) / static_cast<float>(validKeypointsPL);
506 }
507 else
508 {
509 validKeypointsRatio(l, r) =
510 static_cast<float>(validKeypointsPR) / static_cast<float>(validKeypointsPL);
511 }
512 }
513 }
514
515 // Finding best pairs by generating permutations of the list with more persons and comparing the resulting sum of distances.
516 std::vector<unsigned int> numbers(moreObjects.size());
517 std::iota(numbers.begin(), numbers.end(), 0);
518
519 std::vector<unsigned int> bestPermutation;
520 float currentMin = std::numeric_limits<float>::max();
521 do
522 {
523 float sum = 0.0;
524 for (unsigned int i = 0; i < lessObjects.size(); i++)
525 {
526 sum +=
527 personDistances(i, numbers.at(i)) /
528 static_cast<float>(std::pow(
529 validKeypointsRatio(i, numbers.at(i)),
530 3)); // distances with bad ratios will be bigger than distances with good ratios
531 }
532 if (sum < currentMin)
533 {
534 currentMin = sum;
535 bestPermutation = numbers;
536 }
537 std::reverse(
538 numbers.begin() + static_cast<long>(lessObjects.size()),
539 numbers
540 .end()); // we only need permutations of the form (morePersons choose lessPersons)
541 } while (std::next_permutation(numbers.begin(), numbers.end()));
542
543 // Matching
544 if (!bestPermutation.empty())
545 {
546 if (leftSize <= rightSize)
547 {
548 for (unsigned int i = 0; i < this->getData().size(); i++)
549 {
550 KeypointObjectPtr left = this->getData().at(i);
551 KeypointObjectPtr right = other->getData().at(bestPermutation.at(i));
552 left->matchWith(right);
553 }
554 }
555 else
556 {
557 for (unsigned int i = 0; i < other->getData().size(); i++)
558 {
559 KeypointObjectPtr right = other->getData().at(i);
560 KeypointObjectPtr left = this->getData().at(bestPermutation.at(i));
561 left->matchWith(right);
562 }
563 }
564 }
565}
566
567void
569{
570 if (n > _objects.size())
571 {
572 return;
573 }
574 std::sort(_objects.begin(),
575 _objects.end(),
577 { return o1->getAverageDepth() < o2->getAverageDepth(); });
578 _objects.resize(n);
579}
if(!yyvaluep)
Definition Grammar.cpp:645
#define VAROUT(x)
Keypoint3DMapList toIce3D(const VirtualRobot::RobotPtr &) const
std::vector< KeypointObjectPtr > & getData()
void filterToNearestN(unsigned int n)
~KeypointManager()
KeypointManager Destructor.
Keypoint2DMapList toIce2D_normalized(int width, int height) const
KeypointManager()=delete
KeypointManager Constructor.
void matchWith(const KeypointManagerPtr other)
Keypoint2DMapList toIce2D() const
std::string toString2D() const
KeypointSetById::const_iterator begin() const
KeypointSetById::const_iterator end() const
void matchWith(const KeypointObjectPtr object)
KeypointPtr getNode(const std::string &nodeName) const
Keypoint2DMap toIce2D(IdNameMap idNameMap) const
void removeNode(const std::string &nodeName)
Keypoint3DMap toIce3D(IdNameMap idNameMap, const VirtualRobot::RobotPtr &) const
void insertNode(const KeypointPtr point)
std::string toString2D() const
void set2D(const Point2D &point)
void setDominantColor(const DrawColor24Bit &color)
Point2D get2D() const
void set3D(FramedPositionPtr pose)
void setConfidence(float confidence)
std::pair< Point2D, Point2D > getStereo2D() const
void setStereo2D(const Point2D &left, const Point2D &right)
std::string getName() const
DrawColor24Bit getDominantColor() const
FramedPositionPtr get3D() const
float getConfidence() const
unsigned int getId() const
bool isStereo() const
#define ARMARX_CHECK_EXPRESSION(expression)
This macro evaluates the expression and if it turns out to be false it will throw an ExpressionExcept...
#define ARMARX_DEBUG
The logging level for output that is only interesting while debugging.
Definition Logging.h:184
std::shared_ptr< class Robot > RobotPtr
Definition Bus.h:19
This file offers overloads of toIce() and fromIce() functions for STL container types.
IceInternal::Handle< FramedPosition > FramedPositionPtr
Definition FramedPose.h:149
std::shared_ptr< KeypointManager > KeypointManagerPtr
std::shared_ptr< Keypoint > KeypointPtr
std::shared_ptr< KeypointObject > KeypointObjectPtr
const std::map< unsigned int, std::string > & IdNameMap
bool isnan() const