OpenPose3DDepthImageConverter.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 VisionX::ArmarXObjects::OpenPoseEstimation
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 
24 
25 using namespace visionx;
26 using namespace armarx;
27 
28 
30 {
31  OpenPoseEstimation::setupPropertyDefinitions(def);
32 
33  def->optional(radius, "DepthMedianRadius", "Depth Median Radius");
34  def->optional(maxDepth, "MaxDepth", "Pixels with a distance higher than this value are masked out. Only for depth camera mode.", PropertyDefinitionBase::eModifiable);
35  def->optional(maxDepthDifference, "MaxDepthDifference", "Allowed difference of depth value for one keypoint to median of all keypoints.", PropertyDefinitionBase::eModifiable);
36  def->optional(cameraNodeName, "CameraNodeName");
37 }
38 
40 {
41  OpenPoseEstimation::setupLocalVariables();
42 }
43 
45 {
46  OpenPoseEstimation::destroyLocalVariables();
47 }
48 
50 {
51  if (!localRobot || !running3D_is_possible || !running3D)
52  {
53  return;
54  }
55 
56  RemoteRobot::synchronizeLocalCloneToTimestamp(localRobot, robotStateInterface, timestamp_of_update);
57 
58  const int depthThreshold = maxDepthDifference;
59  openposeResult3D.clear();
60 
61  for (const auto& entities_iterator : openposeResult)
62  {
63  const std::string& name = entities_iterator->first;
64  const Entity2D& entity = entities_iterator->second;
65 
66  if (entity.keypointMap.size() == 0)
67  {
68  continue;
69  }
70 
71  std::map<std::string, int> depthStorage; // we do not want to store perspective depth-values in a Keypoint, we transfer them into world-coordiantes (in cameraframe) anyways
72  std::vector<int> depthsCopy;
73 
74  // Get camera depth information from image coordinates
75  for (const auto& entity_iterator : entity.keypointMap)
76  {
77  const std::string& kp_name = entities_iterator->first;
78  const Keypoint2D& point = entities_iterator->second;
79 
80  int depth = getMedianDepthFromImage(static_cast<int>(point.x), static_cast<int>(point.y), radius);
81  depthStorage[kp_name] = depth;
82  depthsCopy.push_back(depth);
83  }
84 
85  // Find outlier in depth values and set them to median of depth values
86  std::sort(depthsCopy.begin(), depthsCopy.end());
87  const int medianDepth = depthsCopy.at(depthsCopy.size() / 2);
88  for (auto& depthStorage_iterator : depthStorage)
89  {
90  const std::string& storage_name = depthStorage_iterator->first;
91  int& depth = depthStorage_iterator->second;
92 
93  if (depth > medianDepth + depthThreshold || depth < medianDepth - depthThreshold)
94  {
95  depth = medianDepth;
96  }
97  }
98 
99  // Transform pixel + depth into world coordinates
100  // and update stored 3d entity
101  openposeResult3D[name] = Entity3D();
102  for (const auto& entity_iterator : entity.keypointMap)
103  {
104  const std::string& kp_name = entity_iterator->first;
105  const Keypoint3D& point = entity_iterator->second;
106 
107  Vec2d imagePoint;
108  imagePoint.x = point.x;
109  imagePoint.y = point.y;
110 
111  Vec3d result;
112  ARMARX_CHECK_EXPRESSION(calibration);
113  calibration->ImageToWorldCoordinates(imagePoint, result, static_cast<float>(depthStorage.at(i)), useDistortionParameters);
114 
115  //FramedPosition pos = FramedPosition(Eigen::Vector3f(result.x, result.y, result.z), cameraNodeName, localRobot->getName());
116  openposeResult3D[name].keypointMap[kp_name].x = result.x;
117  openposeResult3D[name].keypointMap[kp_name].y = result.y;
118  openposeResult3D[name].keypointMap[kp_name].z = result.z;
119  }
120 
121  float average_x = 0;
122  float average_y = 0;
123  float average_z = 0;
124  }
125 }
126 
127 int OpenPose3DDepthImageConverter::getMedianDepthFromImage(int x, int y, int radius) const
128 {
129  std::vector<int> depths;
130  for (int xoffset = -radius; xoffset < radius; xoffset++)
131  {
132  int xo = x + xoffset;
133  if (xo < 0 || xo > depthImageBuffer->width)
134  {
135  continue;
136  }
137  for (int yoffset = -radius; yoffset < radius; yoffset++)
138  {
139  int yo = y + yoffset;
140  if (yo < 0 || yo > depthImageBuffer->height)
141  {
142  continue;
143  }
144 
145  // Check whether (x,y) is in circle:
146  if (xoffset * xoffset + yoffset * yoffset <= radius * radius)
147  {
148  unsigned int pixelPos = static_cast<unsigned int>(3 * (yo * depthImageBuffer->width + xo));
149  int z_value = depthImageBuffer->pixels[pixelPos + 0]
150  + (depthImageBuffer->pixels[pixelPos + 1] << 8)
151  + (depthImageBuffer->pixels[pixelPos + 2] << 16);
152  if (z_value > 0)
153  {
154  depths.push_back(z_value);
155  }
156  }
157  }
158  }
159  std::sort(depths.begin(), depths.end());
160 
161  return depths.empty() ? 0 : depths[depths.size() / 2];
162 }
163 
164 
165 void OpenPose3DDepthImageConverter::maskOutBasedOnDepth(CByteImage& image, int maxDepth)
166 {
167  std::unique_lock lock(depthImageBufferMutex);
168 
169  if (maxDepth <= 0)
170  {
171  return;
172  }
173  int pixelCount = depthImageBuffer->width * depthImageBuffer->height;
174  int depthThresholdmm = maxDepth;
175  CByteImage maskImage(depthImageBuffer->width, depthImageBuffer->height, CByteImage::eGrayScale);
176  for (int i = 0; i < pixelCount; i += 1)
177  {
178  int z_value = depthImageBuffer->pixels[i * 3 + 0]
179  + (depthImageBuffer->pixels[i * 3 + 1] << 8)
180  + (depthImageBuffer->pixels[i * 3 + 2] << 16);
181  maskImage.pixels[i] = z_value > depthThresholdmm || z_value == 0 ? 0 : 255;
182 
183  }
184  ::ImageProcessor::Erode(&maskImage, &maskImage, 5);
185  ::ImageProcessor::Dilate(&maskImage, &maskImage, 20);
186  for (int i = 0; i < pixelCount; i += 1)
187  {
188  if (maskImage.pixels[i] == 0)
189  {
190  image.pixels[i * 3] = 0;
191  image.pixels[i * 3 + 1] = 255;
192  image.pixels[i * 3 + 2] = 0;
193 
194  }
195  }
196 }
visionx::OpenPose3DDepthImageConverter::destroyLocalVariables
virtual void destroyLocalVariables() override
Definition: OpenPose3DDepthImageConverter.cpp:44
visionx
ArmarX headers.
Definition: OpenPoseStressTest.h:38
visionx::OpenPose3DDepthImageConverter::calculate3DEntitiesFrom2DEntitiesInBuffer
void calculate3DEntitiesFrom2DEntitiesInBuffer() const
Definition: OpenPose3DDepthImageConverter.cpp:49
GfxTL::Vec2d
VectorXD< 2, double > Vec2d
Definition: VectorXD.h:694
visionx::OpenPose3DDepthImageConverter::setupPropertyDefinitions
virtual void setupPropertyDefinitions(armarx::PropertyDefinitionsPtr &def) override
Definition: OpenPose3DDepthImageConverter.cpp:29
GfxTL::Vec3d
VectorXD< 3, double > Vec3d
Definition: VectorXD.h:695
OpenPose3DDepthImageConverter.h
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
visionx::OpenPose3DDepthImageConverter::setupLocalVariables
virtual void setupLocalVariables() override
Definition: OpenPose3DDepthImageConverter.cpp:39
IceUtil::Handle< class PropertyDefinitionContainer >
armarx
This file offers overloads of toIce() and fromIce() functions for STL container types.
Definition: ArmarXTimeserver.cpp:28