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 
29 using namespace armarx;
30 
31 Keypoint::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 
49 Keypoint::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 
75 bool
77 {
78  return _pos3D ? true : false;
79 }
80 
81 bool
83 {
84  return !_pointR.isnan();
85 }
86 
87 void
88 Keypoint::set2D(const Point2D& point)
89 {
91  _pointL = point;
92 }
93 
94 void
95 Keypoint::setStereo2D(const Point2D& left, const Point2D& right)
96 {
97  set2D(left);
99  _pointR = right;
100 }
101 
102 void
104 {
105  _pos3D = pos;
106 }
107 
108 void
109 Keypoint::setConfidence(float confidence)
110 {
111  _confidence = confidence;
112 }
113 
114 void
115 Keypoint::setDominantColor(const DrawColor24Bit& color)
116 {
117  _dominantColor = color;
118 }
119 
120 Point2D
122 {
123  return _pointL;
124 }
125 
126 std::pair<Point2D, Point2D>
128 {
130  return std::make_pair(_pointL, _pointR);
131 }
132 
135 {
136  return _pos3D;
137 }
138 
139 float
141 {
142  return _confidence;
143 }
144 
145 DrawColor24Bit
147 {
148  return _dominantColor;
149 }
150 
151 unsigned int
153 {
154  return _id;
155 }
156 
157 std::string
159 {
160  return _name;
161 }
162 
163 std::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 
175 KeypointSetById::const_iterator
177 {
178  return _keypoints.get<0>().begin();
179 }
180 
181 KeypointSetById::const_iterator
183 {
184  return _keypoints.get<0>().end();
185 }
186 
188 KeypointObject::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 
202 KeypointObject::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 
215 void
217 {
218  if (point)
219  {
220  _keypoints.insert(point);
221  }
222 }
223 
224 void
225 KeypointObject::removeNode(const std::string& nodeName)
226 {
227  _keypoints.get<1>().erase(nodeName);
228 }
229 
230 void
232 {
233  _keypoints.get<0>().erase(id);
234 }
235 
236 size_t
238 {
239  return _keypoints.get<0>().size();
240 }
241 
242 float
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 
262 Keypoint2DMap
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 
293 Keypoint3DMap
294 KeypointObject::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 
336 void
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 
356 std::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 
367 KeypointManager::KeypointManager(IdNameMap idNameMap) : _idNameMap(idNameMap)
368 {
369 }
370 
372 {
373 }
374 
375 Keypoint2DMapList
377 {
378  Keypoint2DMapList list;
379  for (KeypointObjectPtr o : _objects)
380  {
381  list.push_back(o->toIce2D(_idNameMap));
382  }
383  return list;
384 }
385 
386 Keypoint2DMapList
387 KeypointManager::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 
410 Keypoint3DMapList
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 
421 void
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)
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 
567 void
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 }
armarx::Keypoint::setConfidence
void setConfidence(float confidence)
Definition: KeypointManager.cpp:109
armarx::KeypointObject::begin
KeypointSetById::const_iterator begin() const
Definition: KeypointManager.cpp:176
armarx::KeypointManager::matchWith
void matchWith(const KeypointManagerPtr other)
Definition: KeypointManager.cpp:422
armarx::KeypointObject::toString2D
std::string toString2D() const
Definition: KeypointManager.cpp:357
armarx::KeypointManager::filterToNearestN
void filterToNearestN(unsigned int n)
Definition: KeypointManager.cpp:568
armarx::Point2D::_y
float _y
Definition: KeypointManager.h:65
armarx::KeypointObject::insertNode
void insertNode(const KeypointPtr point)
Definition: KeypointManager.cpp:216
list
list(APPEND SOURCES ${QT_RESOURCES}) set(COMPONENT_LIBS ArmarXGui ArmarXCoreObservers ArmarXCoreEigen3Variants PlotterController $
Definition: CMakeLists.txt:49
armarx::Keypoint::is3DSet
bool is3DSet() const
Definition: KeypointManager.cpp:76
armarx::KeypointManagerPtr
std::shared_ptr< KeypointManager > KeypointManagerPtr
Definition: KeypointManager.h:184
armarx::KeypointObjectPtr
std::shared_ptr< KeypointObject > KeypointObjectPtr
Definition: KeypointManager.h:143
armarx::Point2D::_x
float _x
Definition: KeypointManager.h:64
armarx::KeypointManager::KeypointManager
KeypointManager()=delete
KeypointManager Constructor.
armarx::Keypoint::getStereo2D
std::pair< Point2D, Point2D > getStereo2D() const
Definition: KeypointManager.cpp:127
armarx::KeypointObject::removeNode
void removeNode(const std::string &nodeName)
Definition: KeypointManager.cpp:225
armarx::KeypointObject::matchWith
void matchWith(const KeypointObjectPtr object)
Definition: KeypointManager.cpp:337
armarx::Keypoint::get3D
FramedPositionPtr get3D() const
Definition: KeypointManager.cpp:134
armarx::Keypoint::Keypoint
Keypoint()=delete
IceInternal::Handle< FramedPosition >
armarx::reverse
T reverse(const T &o)
Definition: container.h:33
armarx::KeypointObject::size
size_t size() const
Definition: KeypointManager.cpp:237
armarx::KeypointManager::toIce2D_normalized
Keypoint2DMapList toIce2D_normalized(int width, int height) const
Definition: KeypointManager.cpp:387
armarx::Point2D::isnan
bool isnan() const
Definition: KeypointManager.h:68
armarx::Keypoint::get2D
Point2D get2D() const
Definition: KeypointManager.cpp:121
armarx::KeypointManager::toIce3D
Keypoint3DMapList toIce3D(const VirtualRobot::RobotPtr &) const
Definition: KeypointManager.cpp:411
ARMARX_DEBUG
#define ARMARX_DEBUG
Definition: Logging.h:184
armarx::KeypointPtr
std::shared_ptr< Keypoint > KeypointPtr
Definition: KeypointManager.h:126
armarx::Keypoint::setDominantColor
void setDominantColor(const DrawColor24Bit &color)
Definition: KeypointManager.cpp:115
max
T max(T t1, T t2)
Definition: gdiam.h:51
armarx::Keypoint
Definition: KeypointManager.h:74
armarx::KeypointObject::getNode
KeypointPtr getNode(const std::string &nodeName) const
Definition: KeypointManager.cpp:188
armarx::to_string
const std::string & to_string(const std::string &s)
Definition: StringHelpers.h:41
armarx::Keypoint::set3D
void set3D(FramedPositionPtr pose)
Definition: KeypointManager.cpp:103
armarx::Keypoint::getConfidence
float getConfidence() const
Definition: KeypointManager.cpp:140
ARMARX_CHECK_EXPRESSION
#define ARMARX_CHECK_EXPRESSION(expression)
This macro evaluates the expression and if it turns out to be false it will throw an ExpressionExcept...
Definition: ExpressionException.h:73
armarx::Keypoint::set2D
void set2D(const Point2D &point)
Definition: KeypointManager.cpp:88
armarx::IdNameMap
const std::map< unsigned int, std::string > & IdNameMap
Definition: KeypointManager.h:140
VAROUT
#define VAROUT(x)
Definition: StringHelpers.h:198
KeypointManager.h
armarx::Keypoint::getId
unsigned int getId() const
Definition: KeypointManager.cpp:152
armarx::KeypointManager::~KeypointManager
~KeypointManager()
KeypointManager Destructor.
Definition: KeypointManager.cpp:371
armarx::Keypoint::getName
std::string getName() const
Definition: KeypointManager.cpp:158
armarx::Keypoint::getDominantColor
DrawColor24Bit getDominantColor() const
Definition: KeypointManager.cpp:146
armarx::KeypointObject::end
KeypointSetById::const_iterator end() const
Definition: KeypointManager.cpp:182
armarx::Point2D
Definition: KeypointManager.h:49
armarx::Keypoint::setStereo2D
void setStereo2D(const Point2D &left, const Point2D &right)
Definition: KeypointManager.cpp:95
armarx::KeypointObject::toIce3D
Keypoint3DMap toIce3D(IdNameMap idNameMap, const VirtualRobot::RobotPtr &) const
Definition: KeypointManager.cpp:294
Logging.h
armarx::Keypoint::toString2D
std::string toString2D() const
Definition: KeypointManager.cpp:164
armarx::KeypointObject::toIce2D
Keypoint2DMap toIce2D(IdNameMap idNameMap) const
Definition: KeypointManager.cpp:263
armarx::KeypointObject::getAverageDepth
float getAverageDepth() const
Definition: KeypointManager.cpp:243
armarx::KeypointManager::getData
std::vector< KeypointObjectPtr > & getData()
Definition: KeypointManager.h:207
armarx
This file offers overloads of toIce() and fromIce() functions for STL container types.
Definition: ArmarXTimeserver.cpp:27
armarx::KeypointManager::toIce2D
Keypoint2DMapList toIce2D() const
Definition: KeypointManager.cpp:376
VirtualRobot::RobotPtr
std::shared_ptr< class Robot > RobotPtr
Definition: Bus.h:19
armarx::Keypoint::isStereo
bool isStereo() const
Definition: KeypointManager.cpp:82