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 
26 
27 #include <numeric> // std::iota
28 
29 using namespace armarx;
30 
31 
32 Keypoint::Keypoint(const Keypoint& p) : _id(p.getId()), _name(p.getName())
33 {
34  if (p.isStereo())
35  {
36  auto pair = p.getStereo2D();
37  setStereo2D(pair.first, pair.second);
38  }
39  else
40  {
41  set2D(p.get2D());
42  }
43  if (p.is3DSet())
44  {
45  set3D(p.get3D());
46  }
48 }
49 
50 Keypoint::Keypoint(float x, float y, unsigned int id, const std::string& name, float confidence) : Keypoint(Point2D(x, y), id, name, confidence)
51 {
52 }
53 
54 Keypoint::Keypoint(const Point2D& point, unsigned int id, const std::string& name, float confidence) : _id(id), _name(name), _dominantColor(DrawColor24Bit {0, 0, 0})
55 {
56  set2D(point);
57  setConfidence(confidence);
58 }
59 
60 Keypoint::Keypoint(const Point2D& left, const Point2D& right, unsigned int id, const std::string& name, float confidence) : _id(id), _name(name), _dominantColor(DrawColor24Bit {0, 0, 0})
61 {
62  setStereo2D(left, right);
63  setConfidence(confidence);
64 }
65 
66 bool Keypoint::is3DSet() const
67 {
68  return _pos3D ? true : false;
69 }
70 
71 bool Keypoint::isStereo() const
72 {
73  return !_pointR.isnan();
74 }
75 
76 void Keypoint::set2D(const Point2D& point)
77 {
79  _pointL = point;
80 }
81 
82 void Keypoint::setStereo2D(const Point2D& left, const Point2D& right)
83 {
84  set2D(left);
86  _pointR = right;
87 }
88 
90 {
91  _pos3D = pos;
92 }
93 
94 void Keypoint::setConfidence(float confidence)
95 {
96  _confidence = confidence;
97 }
98 
99 void Keypoint::setDominantColor(const DrawColor24Bit& color)
100 {
101  _dominantColor = color;
102 }
103 
105 {
106  return _pointL;
107 }
108 
109 std::pair<Point2D, Point2D> Keypoint::getStereo2D() const
110 {
112  return std::make_pair(_pointL, _pointR);
113 }
114 
116 {
117  return _pos3D;
118 }
119 
121 {
122  return _confidence;
123 }
124 
125 DrawColor24Bit Keypoint::getDominantColor() const
126 {
127  return _dominantColor;
128 }
129 
130 unsigned int Keypoint::getId() const
131 {
132  return _id;
133 }
134 
135 std::string Keypoint::getName() const
136 {
137  return _name;
138 }
139 
140 std::string Keypoint::toString2D() const
141 {
142  std::stringstream ss;
143  ss << "Id: " << std::to_string(_id) << ", ";
144  ss << "Name: " << _name << ", ";
145  ss << "x: " << _pointL._x << ", ";
146  ss << "y: " << _pointL._y << ", ";
147  ss << "Confindence: " << _confidence;
148  return ss.str();
149 }
150 
151 
152 
153 
154 
155 KeypointSetById::const_iterator KeypointObject::begin() const
156 {
157  return _keypoints.get<0>().begin();
158 }
159 
160 KeypointSetById::const_iterator KeypointObject::end() const
161 {
162  return _keypoints.get<0>().end();
163 }
164 
165 
166 KeypointPtr KeypointObject::getNode(const std::string& nodeName) const
167 {
168  auto itFound = _keypoints.get<1>().find(nodeName);
169  if (itFound != _keypoints.get<1>().end())
170  {
171  return *itFound;
172  }
173  else
174  {
175  return KeypointPtr();
176  }
177 }
178 
179 KeypointPtr KeypointObject::getNode(unsigned int id) const
180 {
181  auto itFound = _keypoints.get<0>().find(id);
182  if (itFound != _keypoints.get<0>().end())
183  {
184  return *itFound;
185  }
186  else
187  {
188  return KeypointPtr();
189  }
190 }
191 
193 {
194  if (point)
195  {
196  _keypoints.insert(point);
197  }
198 }
199 
200 void KeypointObject::removeNode(const std::string& nodeName)
201 {
202  _keypoints.get<1>().erase(nodeName);
203 }
204 
205 void KeypointObject::removeNode(unsigned int id)
206 {
207  _keypoints.get<0>().erase(id);
208 }
209 
210 size_t KeypointObject::size() const
211 {
212  return _keypoints.get<0>().size();
213 }
214 
216 {
217  if (size() == 0)
218  {
219  return std::numeric_limits<float>::quiet_NaN();
220  }
221  float result = 0.0f;
222  int amountDepths = 0;
223  for (KeypointPtr point : _keypoints.get<0>())
224  {
225  ARMARX_CHECK_EXPRESSION(point->is3DSet()); // if any point in this object has no 3D-information, something went wrong.
226  result += point->get3D()->z;
227  amountDepths++;
228  }
229  return result / static_cast<float>(amountDepths);
230 }
231 
232 Keypoint2DMap KeypointObject::toIce2D(IdNameMap idNameMap) const
233 {
234  Keypoint2DMap iceMap;
235  for (auto [id, label] : idNameMap)
236  {
237  KeypointPtr keypoint = this->getNode(id);
238  if (keypoint)
239  {
240  Keypoint2D iceKeypoint;
241  iceKeypoint.x = keypoint->get2D()._x;
242  iceKeypoint.y = keypoint->get2D()._y;
243  iceKeypoint.label = keypoint->getName();
244  iceKeypoint.confidence = keypoint->getConfidence();
245  iceKeypoint.dominantColor = keypoint->getDominantColor();
246  iceMap.insert(std::make_pair(iceKeypoint.label, iceKeypoint));
247  }
248  else
249  {
250  Keypoint2D iceKeypoint;
251  iceKeypoint.x = 0.0f;
252  iceKeypoint.y = 0.0f;
253  iceKeypoint.label = label;
254  iceKeypoint.confidence = 0.0f;
255  iceKeypoint.dominantColor = DrawColor24Bit {0, 0, 0}; // Black
256  iceMap.insert(std::make_pair(iceKeypoint.label, iceKeypoint));
257  }
258  }
259  return iceMap;
260 }
261 
262 Keypoint3DMap KeypointObject::toIce3D(IdNameMap idNameMap, const VirtualRobot::RobotPtr& local_robot) const
263 {
264  Keypoint3DMap iceMap;
265  for (auto [id, label] : idNameMap)
266  {
267  KeypointPtr keypoint = this->getNode(id);
268  if (keypoint && keypoint->is3DSet())
269  {
270  Keypoint3D iceKeypoint;
271  iceKeypoint.x = keypoint->get3D()->x;
272  iceKeypoint.y = keypoint->get3D()->y;
273  iceKeypoint.z = keypoint->get3D()->z;
274 
275  Eigen::Vector3f globalPose = keypoint->get3D()->toGlobalEigen(local_robot);
276 
277  iceKeypoint.globalX = globalPose(0);
278  iceKeypoint.globalY = globalPose(1);
279  iceKeypoint.globalZ = globalPose(2);
280 
281  iceKeypoint.label = keypoint->getName();
282  iceKeypoint.confidence = keypoint->getConfidence();
283  iceKeypoint.dominantColor = keypoint->getDominantColor();
284  iceMap.insert(std::make_pair(keypoint->getName(), iceKeypoint));
285  }
286  else
287  {
288  Keypoint3D iceKeypoint;
289  iceKeypoint.x = 0.0f;
290  iceKeypoint.y = 0.0f;
291  iceKeypoint.z = 0.0f;
292  iceKeypoint.globalX = 0.0f;
293  iceKeypoint.globalY = 0.0f;
294  iceKeypoint.globalZ = 0.0f;
295  iceKeypoint.label = label;
296  iceKeypoint.confidence = 0.0f;
297  iceKeypoint.dominantColor = DrawColor24Bit {0, 0, 0}; // Black
298  iceMap.insert(std::make_pair(iceKeypoint.label, iceKeypoint));
299  }
300  }
301  return iceMap;
302 }
303 
305 {
306  if (object)
307  {
308  for (KeypointPtr keypoint : _keypoints.get<0>())
309  {
310  KeypointPtr otherPoint = object->getNode(keypoint->getId());
311  if (otherPoint)
312  {
313  ARMARX_CHECK_EXPRESSION(keypoint->getName() == otherPoint->getName());
314  keypoint->setStereo2D(keypoint->get2D(), otherPoint->get2D());
315  // The combined confindence is defined as the average of both confidences
316  keypoint->setConfidence((keypoint->getConfidence() + otherPoint->getConfidence()) / 2.0f);
317  }
318  }
319  }
320 }
321 
322 std::string KeypointObject::toString2D() const
323 {
324  std::stringstream ss;
325  for (KeypointPtr keypoint : _keypoints.get<0>())
326  {
327  ss << keypoint->toString2D() << "\n";
328  }
329  return ss.str();
330 }
331 
332 KeypointManager::KeypointManager(IdNameMap idNameMap) : _idNameMap(idNameMap)
333 {
334 }
335 
337 {
338 
339 }
340 
341 Keypoint2DMapList KeypointManager::toIce2D() const
342 {
343  Keypoint2DMapList list;
344  for (KeypointObjectPtr o : _objects)
345  {
346  list.push_back(o->toIce2D(_idNameMap));
347  }
348  return list;
349 }
350 
351 Keypoint2DMapList KeypointManager::toIce2D_normalized(int width, int height) const
352 {
353  Keypoint2DMapList list = this->toIce2D();
354  for (Keypoint2DMap map : list)
355  {
356  for (auto pair : map)
357  {
358  Keypoint2D point = pair.second;
359  if (point.x < 0.0f || point.x > width || point.y < 0.0f || point.y > height)
360  {
361  point.x = 0.0;
362  point.y = 0.0;
363  }
364  else
365  {
366  point.x = point.x / static_cast<float>(width);
367  point.y = point.y / static_cast<float>(height);
368  }
369  }
370  }
371  return list;
372 }
373 
374 Keypoint3DMapList KeypointManager::toIce3D(const VirtualRobot::RobotPtr& local_robot) const
375 {
376  Keypoint3DMapList list;
377  for (KeypointObjectPtr o : _objects)
378  {
379  list.push_back(o->toIce3D(_idNameMap, local_robot));
380  }
381  return list;
382 }
383 
385 {
386  // 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
387  // This results in a sum for each possible object-pair, which is the distance between two objects
388  // 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.
389 
390  // It is assumed, that this manager contains the objects of the left image and the other manager conatains the objects of the right image
391  // 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
392 
393  size_t leftSize = getData().size();
394  size_t rightSize = other->getData().size();
395 
396  ARMARX_DEBUG << VAROUT(leftSize) << " " << VAROUT(rightSize);
397 
398  if (leftSize == 0 || rightSize == 0)
399  {
400  return;
401  }
402 
403  std::vector<KeypointObjectPtr> moreObjects;
404  std::vector<KeypointObjectPtr> lessObjects;
405  if (leftSize > rightSize)
406  {
407  moreObjects = this->getData();
408  lessObjects = other->getData();
409  }
410  else
411  {
412  moreObjects = other->getData();
413  lessObjects = this->getData();
414  }
415 
416  // With only one object in every manager we can simply match these two
417  if (leftSize == 1 && rightSize == 1)
418  {
419  KeypointObjectPtr o = getData().at(0);
420  KeypointObjectPtr o1 = other->getData().at(0);
421  o->matchWith(o1);
422  return;
423  }
424 
425  Eigen::MatrixXf personDistances(lessObjects.size(), moreObjects.size()); // rows = lessPersons; cols = morePersons
426  Eigen::MatrixXf validKeypointsRatio(lessObjects.size(), moreObjects.size()); // to prevent, that artifacts of the openpose algorithm are used in the pairs...
427 
428  for (unsigned int l = 0; l < lessObjects.size(); l++)
429  {
430  KeypointObjectPtr pl = lessObjects.at(l);
431  for (unsigned int r = 0; r < moreObjects.size(); r++)
432  {
433  KeypointObjectPtr pr = moreObjects.at(r);
434  double sum = 0.0;
435  int addedDistances = 0;
436  for (KeypointPtr kpl : *pl)
437  {
438  KeypointPtr kpr = pr->getNode(kpl->getId());
439  if (kpr)
440  {
441  sum += std::pow(kpl->get2D()._x - kpr->get2D()._x, 2) + std::pow(kpr->get2D()._y - kpr->get2D()._y, 2); // squared euclidean distance
442  addedDistances++;
443  }
444  }
445 
446  personDistances(l, r) = (addedDistances == 0) ? std::numeric_limits<float>::max() : static_cast<float>(sum) / addedDistances;
447 
448  // Calculate ratio of valid keypoints
449  int validKeypointsPL = static_cast<int>(pl->size());
450  int validKeypointsPR = static_cast<int>(pr->size());
451  if (validKeypointsPL == 0 || validKeypointsPR == 0)
452  {
453  validKeypointsRatio(l, r) = 0.000000001f; // simply a very small number for a very bad ratio (ratio cannot be 0, because it is needed for a division later)
454  }
455  else if (validKeypointsPL > validKeypointsPR)
456  {
457  validKeypointsRatio(l, r) = static_cast<float>(validKeypointsPR) / static_cast<float>(validKeypointsPL);
458  }
459  else
460  {
461  validKeypointsRatio(l, r) = static_cast<float>(validKeypointsPR) / static_cast<float>(validKeypointsPL);
462  }
463  }
464  }
465 
466  // Finding best pairs by generating permutations of the list with more persons and comparing the resulting sum of distances.
467  std::vector<unsigned int> numbers(moreObjects.size());
468  std::iota(numbers.begin(), numbers.end(), 0);
469 
470  std::vector<unsigned int> bestPermutation;
471  float currentMin = std::numeric_limits<float>::max();
472  do
473  {
474  float sum = 0.0;
475  for (unsigned int i = 0; i < lessObjects.size(); i++)
476  {
477  sum += personDistances(i, numbers.at(i)) / static_cast<float>(std::pow(validKeypointsRatio(i, numbers.at(i)), 3)); // distances with bad ratios will be bigger than distances with good ratios
478  }
479  if (sum < currentMin)
480  {
481  currentMin = sum;
482  bestPermutation = numbers;
483  }
484  std::reverse(numbers.begin() + static_cast<long>(lessObjects.size()), numbers.end()); // we only need permutations of the form (morePersons choose lessPersons)
485  }
486  while (std::next_permutation(numbers.begin(), numbers.end()));
487 
488  // Matching
489  if (!bestPermutation.empty())
490  {
491  if (leftSize <= rightSize)
492  {
493  for (unsigned int i = 0; i < this->getData().size(); i++)
494  {
495  KeypointObjectPtr left = this->getData().at(i);
496  KeypointObjectPtr right = other->getData().at(bestPermutation.at(i));
497  left->matchWith(right);
498  }
499  }
500  else
501  {
502  for (unsigned int i = 0; i < other->getData().size(); i++)
503  {
504  KeypointObjectPtr right = other->getData().at(i);
505  KeypointObjectPtr left = this->getData().at(bestPermutation.at(i));
506  left->matchWith(right);
507  }
508  }
509  }
510 }
511 
513 {
514  if (n > _objects.size())
515  {
516  return;
517  }
518  std::sort(_objects.begin(), _objects.end(), [](KeypointObjectPtr o1, KeypointObjectPtr o2)
519  {
520  return o1->getAverageDepth() < o2->getAverageDepth();
521  });
522  _objects.resize(n);
523 }
armarx::Keypoint::setConfidence
void setConfidence(float confidence)
Definition: KeypointManager.cpp:94
armarx::KeypointObject::begin
KeypointSetById::const_iterator begin() const
Definition: KeypointManager.cpp:155
armarx::KeypointManager::matchWith
void matchWith(const KeypointManagerPtr other)
Definition: KeypointManager.cpp:384
armarx::KeypointObject::toString2D
std::string toString2D() const
Definition: KeypointManager.cpp:322
armarx::KeypointManager::filterToNearestN
void filterToNearestN(unsigned int n)
Definition: KeypointManager.cpp:512
armarx::Point2D::_y
float _y
Definition: KeypointManager.h:59
armarx::KeypointObject::insertNode
void insertNode(const KeypointPtr point)
Definition: KeypointManager.cpp:192
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:66
armarx::KeypointManagerPtr
std::shared_ptr< KeypointManager > KeypointManagerPtr
Definition: KeypointManager.h:159
armarx::KeypointObjectPtr
std::shared_ptr< KeypointObject > KeypointObjectPtr
Definition: KeypointManager.h:124
armarx::Point2D::_x
float _x
Definition: KeypointManager.h:58
armarx::KeypointManager::KeypointManager
KeypointManager()=delete
KeypointManager Constructor.
armarx::Keypoint::getStereo2D
std::pair< Point2D, Point2D > getStereo2D() const
Definition: KeypointManager.cpp:109
armarx::KeypointObject::removeNode
void removeNode(const std::string &nodeName)
Definition: KeypointManager.cpp:200
armarx::KeypointObject::matchWith
void matchWith(const KeypointObjectPtr object)
Definition: KeypointManager.cpp:304
armarx::Keypoint::get3D
FramedPositionPtr get3D() const
Definition: KeypointManager.cpp:115
armarx::Keypoint::Keypoint
Keypoint()=delete
IceInternal::Handle< FramedPosition >
armarx::reverse
T reverse(const T &o)
Definition: container.h:32
armarx::KeypointObject::size
size_t size() const
Definition: KeypointManager.cpp:210
armarx::KeypointManager::toIce2D_normalized
Keypoint2DMapList toIce2D_normalized(int width, int height) const
Definition: KeypointManager.cpp:351
armarx::Point2D::isnan
bool isnan() const
Definition: KeypointManager.h:61
armarx::Keypoint::get2D
Point2D get2D() const
Definition: KeypointManager.cpp:104
armarx::KeypointManager::toIce3D
Keypoint3DMapList toIce3D(const VirtualRobot::RobotPtr &) const
Definition: KeypointManager.cpp:374
ARMARX_DEBUG
#define ARMARX_DEBUG
Definition: Logging.h:177
armarx::KeypointPtr
std::shared_ptr< Keypoint > KeypointPtr
Definition: KeypointManager.h:104
armarx::Keypoint::setDominantColor
void setDominantColor(const DrawColor24Bit &color)
Definition: KeypointManager.cpp:99
max
T max(T t1, T t2)
Definition: gdiam.h:48
armarx::Keypoint
Definition: KeypointManager.h:67
armarx::KeypointObject::getNode
KeypointPtr getNode(const std::string &nodeName) const
Definition: KeypointManager.cpp:166
armarx::to_string
const std::string & to_string(const std::string &s)
Definition: StringHelpers.h:40
armarx::Keypoint::set3D
void set3D(FramedPositionPtr pose)
Definition: KeypointManager.cpp:89
armarx::Keypoint::getConfidence
float getConfidence() const
Definition: KeypointManager.cpp:120
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:76
armarx::IdNameMap
const std::map< unsigned int, std::string > & IdNameMap
Definition: KeypointManager.h:121
VAROUT
#define VAROUT(x)
Definition: StringHelpers.h:182
KeypointManager.h
armarx::Keypoint::getId
unsigned int getId() const
Definition: KeypointManager.cpp:130
armarx::KeypointManager::~KeypointManager
~KeypointManager()
KeypointManager Destructor.
Definition: KeypointManager.cpp:336
armarx::Keypoint::getName
std::string getName() const
Definition: KeypointManager.cpp:135
armarx::Keypoint::getDominantColor
DrawColor24Bit getDominantColor() const
Definition: KeypointManager.cpp:125
armarx::KeypointObject::end
KeypointSetById::const_iterator end() const
Definition: KeypointManager.cpp:160
armarx::Point2D
Definition: KeypointManager.h:43
armarx::Keypoint::setStereo2D
void setStereo2D(const Point2D &left, const Point2D &right)
Definition: KeypointManager.cpp:82
armarx::KeypointObject::toIce3D
Keypoint3DMap toIce3D(IdNameMap idNameMap, const VirtualRobot::RobotPtr &) const
Definition: KeypointManager.cpp:262
Logging.h
armarx::Keypoint::toString2D
std::string toString2D() const
Definition: KeypointManager.cpp:140
armarx::KeypointObject::toIce2D
Keypoint2DMap toIce2D(IdNameMap idNameMap) const
Definition: KeypointManager.cpp:232
armarx::KeypointObject::getAverageDepth
float getAverageDepth() const
Definition: KeypointManager.cpp:215
armarx::KeypointManager::getData
std::vector< KeypointObjectPtr > & getData()
Definition: KeypointManager.h:180
armarx
This file offers overloads of toIce() and fromIce() functions for STL container types.
Definition: ArmarXTimeserver.cpp:28
armarx::KeypointManager::toIce2D
Keypoint2DMapList toIce2D() const
Definition: KeypointManager.cpp:341
VirtualRobot::RobotPtr
std::shared_ptr< class Robot > RobotPtr
Definition: Bus.h:18
armarx::Keypoint::isStereo
bool isStereo() const
Definition: KeypointManager.cpp:71