ImageToPointCloud.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 ActiveVision::ArmarXObjects::ImageToPointCloud
17 * @author Markus Grotz ( markus dot grotz at kit dot edu )
18 * @author Mirko Waechter ( waechter at kit dot edu )
19 * @date 2019
20 * @copyright http://www.gnu.org/licenses/gpl-2.0.txt
21 * GNU General Public License
22 */
23
24#include "ImageToPointCloud.h"
25
26#include <Eigen/Core>
27#include <Eigen/Geometry>
28
29#include <pcl/common/transforms.h>
30
32
34#include <VisionX/interface/components/Calibration.h>
36
37
38using namespace armarx;
39
40void
42{
43 offeringTopicFromProperty("DebugObserverName");
44
45 providerName = getProperty<std::string>("ProviderName").getValue();
46 usingImageProvider(providerName);
47
48 fovH = getProperty<float>("HorizontalViewAngle").getValue();
49 fovV = getProperty<float>("VerticalViewAngle").getValue();
50 if (!getProperty<std::string>("CalibrationProviderName").getValue().empty())
51 {
52 usingProxy(getProperty<std::string>("CalibrationProviderName").getValue());
53 }
54 nanValue = getProperty<float>("NaNValue").getValue();
55 maxDist = getProperty<float>("MaxDist").getValue();
56 minDist = getProperty<float>("MinDist").getValue();
57}
58
59void
61{
62 ARMARX_VERBOSE << "onConnectImageProcessor()";
63 imageProviderInfo = getImageProvider(providerName);
64 ARMARX_VERBOSE << "after getImageProvider('" << providerName << "')";
65
66 ARMARX_CHECK_GREATER_EQUAL(imageProviderInfo.numberImages, 2);
67 numImages = std::min(imageProviderInfo.numberImages, 3);
68
69 images = new CByteImage*[numImages];
70 for (size_t i = 0; i < numImages; i++)
71 {
72 images[i] = visionx::tools::createByteImage(imageProviderInfo);
73 }
74
75 getTopicFromProperty(debugObserver, "DebugObserverName");
76
77
78 auto applyCalibration = [&](visionx::StereoCalibrationInterfacePrx prx)
79 {
80 if (getProperty<bool>("ComputeViewAngleFromCalibration").getValue())
81 {
82 visionx::StereoCalibration stereoCalibration = prx->getStereoCalibration();
83 float fx = stereoCalibration.calibrationRight.cameraParam.focalLength[0];
84 float fy = stereoCalibration.calibrationRight.cameraParam.focalLength[1];
85 fovV = 180.0 / M_PI * 2.0 * std::atan(images[0]->height / (2.0 * fy));
86 fovH = 180.0 / M_PI * 2.0 * std::atan(images[0]->width / (2.0 * fx));
87 ARMARX_INFO << " Using provided HorizontalViewAngle/VerticalViewAngle parameters from "
88 "calibration";
89 }
90 };
91 if (!getProperty<std::string>("CalibrationProviderName").getValue().empty())
92 {
94 getProperty<std::string>("CalibrationProviderName").getValue());
95
96 if (calibrationPrx)
97 {
98 applyCalibration(calibrationPrx);
99 }
100 else
101 {
102 ARMARX_WARNING << "Property CalibrationProviderName was given, but the proxy is not a "
103 "StereoCalibrationInterfacePrx. Calibration not available!";
104 }
105 }
106 else
107 {
108 visionx::StereoCalibrationInterfacePrx calibrationInterface =
109 visionx::StereoCalibrationInterfacePrx::checkedCast(imageProviderInfo.proxy);
110 if (calibrationInterface)
111 {
112 applyCalibration(calibrationInterface);
113 }
114 }
115
116 ARMARX_INFO << "HorizontalViewAngle: " << fovH << " VerticalViewAngle: " << fovV;
117 cloud.reset(new pcl::PointCloud<PointL>());
118 transformedCloud.reset(new pcl::PointCloud<PointL>());
119
120 debugObserver->setDebugDatafield(getName(), "processCount", new Variant(processCounter));
121}
122
123void
125{
128 for (size_t i = 0; i < numImages; i++)
129 {
130 delete images[i];
131 }
132 delete[] images;
133}
134
135void
139
140void
142{
143 std::unique_lock lock(mutex);
144
145 if (!waitForImages(1000))
146 {
147 ARMARX_INFO << deactivateSpam(300) << "Timeout or error while waiting for image data";
148 return;
149 }
150
151 if (getImages(providerName, images, imageMetaInfo) != static_cast<int>(numImages))
152 {
153 ARMARX_WARNING << "Unable to transfer or read images";
154 return;
155 }
156
157
158 cloud->width = images[0]->width;
159 cloud->height = images[0]->height;
160 cloud->header.stamp = imageMetaInfo->timeProvided;
161 ARMARX_CHECK_EQUAL(getProperty<int>("PointCloudWidth").getValue(), images[0]->width);
162 ARMARX_CHECK_EQUAL(getProperty<int>("PointCloudHeight").getValue(), images[0]->height);
163 cloud->points.resize(images[0]->width * images[0]->height);
164 cloud->is_dense = true;
165
166
167 const float scaleX = std::tan(fovH * M_PI / 180.0 / 2.0) * 2.0;
168 const float scaleY = std::tan(fovV * M_PI / 180.0 / 2.0) * 2.0;
169
170 const size_t width = static_cast<size_t>(images[0]->width);
171 const size_t height = static_cast<size_t>(images[0]->height);
172 const float halfWidth = (width / 2.0);
173 const float halfHeight = (height / 2.0);
174 const auto& image0Data = images[0]->pixels;
175 const auto& image1Data = images[1]->pixels;
176
177 for (size_t j = 0; j < height; j++)
178 {
179 for (size_t i = 0; i < width; i++)
180 {
181 auto coord = j * width + i;
182 // unsigned short value = images[1]->pixels[3 * coord + 0]
183 // + (images[1]->pixels[3 * coord + 1] << 8);
185 image1Data[3 * coord + 0], image1Data[3 * coord + 1], image1Data[3 * coord + 2]);
186
187 if (maxDist > 0 && value > maxDist)
188 {
189 value = -1.0f;
190 }
191 else if (value < minDist)
192 {
193 value = -1.0f;
194 }
195
196
197 PointL& p = cloud->points.at(coord);
198 auto index = 3 * (coord);
199 p.r = image0Data[index + 0];
200 p.g = image0Data[index + 1];
201 p.b = image0Data[index + 2];
202
203 if (value < 0)
204 {
205 if (nanValue >= 0)
206 {
207 p.z = static_cast<float>(nanValue);
208 //p.z /= 1000.0;
209 p.x = -1.0 * (i - halfWidth) / width * p.z * scaleX;
210 p.y = (halfHeight - j) / height * p.z * scaleY;
211 }
212 else
213 {
214 p.x = p.y = p.z = std::numeric_limits<float>::quiet_NaN();
215 }
216 }
217 else
218 {
219 p.z = static_cast<float>(value);
220 p.x = -1.0 * (i - halfWidth) / width * p.z * scaleX;
221 p.y = (halfHeight - j) / height * p.z * scaleY;
222 }
223
224 if (numImages > 2)
225 {
226 auto& image2Data = images[2]->pixels;
227
228 if (images[2]->bytesPerPixel == 3)
229 {
230 p.label = static_cast<unsigned int>(image2Data[index + 0] +
231 (image2Data[index + 1] << 8) +
232 (image2Data[index + 2] << 16));
233 }
234 else
235 {
236 p.label = static_cast<unsigned int>(image2Data[coord]);
237 }
238 }
239 else
240 {
241 p.label = 0;
242 }
243 }
244 }
245
246 processCounter++;
247 debugObserver->setDebugDatafield(getName(), "processCount", new Variant(processCounter));
248
249 float angle = getProperty<float>("PointCloudRotationZ").getValue() / 180.f * M_PI;
250 if (angle != 0.0f)
251 {
252 Eigen::Matrix4f transform2 = Eigen::Matrix4f::Identity();
253 Eigen::Matrix3f m;
254 m = Eigen::AngleAxisf(angle, Eigen::Vector3f::UnitZ());
255 transform2.block<3, 3>(0, 0) *= m;
256 pcl::transformPointCloud(*cloud, *transformedCloud, transform2);
257 transformedCloud->header.stamp = imageMetaInfo->timeProvided;
258 providePointCloud(transformedCloud);
259 }
260 else
261 {
262 providePointCloud(cloud);
263 }
264 // pcl::PointCloud<PointL>::Ptr temp(new pcl::PointCloud<PointL>());
265}
266
267visionx::MetaPointCloudFormatPtr
269{
270 visionx::MetaPointCloudFormatPtr info = new visionx::MetaPointCloudFormat();
271 info->size = getProperty<int>("PointCloudWidth").getValue() *
272 getProperty<int>("PointCloudHeight").getValue() * sizeof(PointL);
273 info->capacity = info->size;
274 info->type = visionx::PointContentType::eColoredLabeledPoints;
275 return info;
276}
277
uint8_t index
SpamFilterDataPtr deactivateSpam(SpamFilterDataPtr const &spamFilter, float deactivationDurationSec, const std::string &identifier, bool deactivate)
Definition Logging.cpp:75
#define M_PI
Definition MathTools.h:17
TopicProxyType getTopicFromProperty(const std::string &propertyName)
Get a topic proxy whose name is specified by the given property.
Definition Component.h:221
void offeringTopicFromProperty(const std::string &propertyName)
Offer a topic whose name is specified by the given property.
std::string getConfigIdentifier()
Retrieve config identifier for this component as set in constructor.
Definition Component.cpp:90
Property< PropertyType > getProperty(const std::string &name)
virtual armarx::PropertyDefinitionsPtr createPropertyDefinitions()
virtual visionx::MetaPointCloudFormatPtr getDefaultPointCloudFormat()
default point cloud format used to initialize shared memory
void onConnectImageProcessor() override
Implement this method in the ImageProcessor in order execute parts when the component is fully initia...
void onDisconnectComponent() override
Hook for subclass.
void onExitImageProcessor() override
Exit the ImapeProcessor component.
void process() override
Process the vision component.
void onInitImageProcessor() override
Setup the vision component.
bool usingProxy(const std::string &name, const std::string &endpoints="")
Registers a proxy for retrieval after initialization and adds it to the dependency list.
std::string getName() const
Retrieve name of object.
Ice::ObjectPrx getProxy(long timeoutMs=0, bool waitForScheduler=true) const
Returns the proxy of this object (optionally it waits for the proxy)
The Variant class is described here: Variants.
Definition Variant.h:224
void usingImageProvider(std::string name)
Registers a delayed topic subscription and a delayed provider proxy retrieval which all will be avail...
bool waitForImages(int milliseconds=1000)
Wait for new images.
ImageProviderInfo getImageProvider(std::string name, ImageType destinationImageType=eRgb, bool waitForProxy=false)
Select an ImageProvider.
void onDisconnectComponent() override
int getImages(CByteImage **ppImages)
Poll images from provider.
void onDisconnectComponent() override
Hook for subclass.
void providePointCloud(PointCloudPtrT pointCloudPtr)
offer the new point cloud.
#define ARMARX_CHECK_GREATER_EQUAL(lhs, rhs)
This macro evaluates whether lhs is greater or equal (>=) rhs and if it turns out to be false it will...
#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.
Definition Logging.h:181
#define ARMARX_WARNING
The logging level for unexpected behaviour, but not a serious problem.
Definition Logging.h:193
#define ARMARX_VERBOSE
The logging level for verbose information.
Definition Logging.h:187
This file offers overloads of toIce() and fromIce() functions for STL container types.
IceUtil::Handle< class PropertyDefinitionContainer > PropertyDefinitionsPtr
PropertyDefinitions smart pointer type.
pcl::PointXYZRGBL PointL
CByteImage * createByteImage(const ImageFormatInfo &imageFormat, const ImageType imageType)
Creates a ByteImage for the destination type specified in the given imageProviderInfo.
float rgbToDepthValue(unsigned char r, unsigned char g, unsigned char b, bool noiseResistant=false)
Definition ImageUtil.h:148
double angle(const Point &a, const Point &b, const Point &c)
Definition point.hpp:109