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
27
28#include <Eigen/Core>
29#include <Eigen/Geometry>
30
31#include <pcl/common/transforms.h>
32
34
36#include <VisionX/interface/components/Calibration.h>
38
39
40using namespace armarx;
41
42void
44{
45 offeringTopicFromProperty("DebugObserverName");
46
47 providerName = getProperty<std::string>("ProviderName").getValue();
48 usingImageProvider(providerName);
49
50 fovH = getProperty<float>("HorizontalViewAngle").getValue();
51 fovV = getProperty<float>("VerticalViewAngle").getValue();
52 if (!getProperty<std::string>("CalibrationProviderName").getValue().empty())
53 {
54 usingProxy(getProperty<std::string>("CalibrationProviderName").getValue());
55 }
56 nanValue = getProperty<float>("NaNValue").getValue();
57 maxDist = getProperty<float>("MaxDist").getValue();
58 minDist = getProperty<float>("MinDist").getValue();
59}
60
61void
63{
64 ARMARX_VERBOSE << "onConnectImageProcessor()";
65 imageProviderInfo = getImageProvider(providerName);
66 ARMARX_VERBOSE << "after getImageProvider('" << providerName << "')";
67
68 ARMARX_CHECK_GREATER_EQUAL(imageProviderInfo.numberImages, 2);
69 numImages = std::min(imageProviderInfo.numberImages, 3);
70
71 images = new CByteImage*[numImages];
72 for (size_t i = 0; i < numImages; i++)
73 {
74 images[i] = visionx::tools::createByteImage(imageProviderInfo);
75 }
76
77 getTopicFromProperty(debugObserver, "DebugObserverName");
78
79
80 auto applyCalibration = [&](visionx::StereoCalibrationInterfacePrx prx)
81 {
82 if (getProperty<bool>("ComputeViewAngleFromCalibration").getValue())
83 {
84 visionx::StereoCalibration stereoCalibration = prx->getStereoCalibration();
85 float fx = stereoCalibration.calibrationRight.cameraParam.focalLength[0];
86 float fy = stereoCalibration.calibrationRight.cameraParam.focalLength[1];
87 fovV = 180.0 / M_PI * 2.0 * std::atan(images[0]->height / (2.0 * fy));
88 fovH = 180.0 / M_PI * 2.0 * std::atan(images[0]->width / (2.0 * fx));
89 ARMARX_INFO << " Using provided HorizontalViewAngle/VerticalViewAngle parameters from "
90 "calibration";
91 }
92 };
93 if (!getProperty<std::string>("CalibrationProviderName").getValue().empty())
94 {
96 getProperty<std::string>("CalibrationProviderName").getValue());
97
98 if (calibrationPrx)
99 {
100 applyCalibration(calibrationPrx);
101 }
102 else
103 {
104 ARMARX_WARNING << "Property CalibrationProviderName was given, but the proxy is not a "
105 "StereoCalibrationInterfacePrx. Calibration not available!";
106 }
107 }
108 else
109 {
110 visionx::StereoCalibrationInterfacePrx calibrationInterface =
111 visionx::StereoCalibrationInterfacePrx::checkedCast(imageProviderInfo.proxy);
112 if (calibrationInterface)
113 {
114 applyCalibration(calibrationInterface);
115 }
116 }
117
118 ARMARX_INFO << "HorizontalViewAngle: " << fovH << " VerticalViewAngle: " << fovV;
119 cloud.reset(new pcl::PointCloud<PointL>());
120 transformedCloud.reset(new pcl::PointCloud<PointL>());
121
122 debugObserver->setDebugDatafield(getName(), "processCount", new Variant(processCounter));
123}
124
125void
127{
130 for (size_t i = 0; i < numImages; i++)
131 {
132 delete images[i];
133 }
134 delete[] images;
135}
136
137void
141
142void
144{
145 std::unique_lock lock(mutex);
146
147 if (!waitForImages(1000))
148 {
149 ARMARX_INFO << deactivateSpam(300) << "Timeout or error while waiting for image data";
150 return;
151 }
152
153 if (getImages(providerName, images, imageMetaInfo) != static_cast<int>(numImages))
154 {
155 ARMARX_WARNING << "Unable to transfer or read images";
156 return;
157 }
158
159
160 cloud->width = images[0]->width;
161 cloud->height = images[0]->height;
162 cloud->header.stamp = imageMetaInfo->timeProvided;
163 ARMARX_CHECK_EQUAL(getProperty<int>("PointCloudWidth").getValue(), images[0]->width);
164 ARMARX_CHECK_EQUAL(getProperty<int>("PointCloudHeight").getValue(), images[0]->height);
165 cloud->points.resize(images[0]->width * images[0]->height);
166 cloud->is_dense = true;
167
168
169 const float scaleX = std::tan(fovH * M_PI / 180.0 / 2.0) * 2.0;
170 const float scaleY = std::tan(fovV * M_PI / 180.0 / 2.0) * 2.0;
171
172 const size_t width = static_cast<size_t>(images[0]->width);
173 const size_t height = static_cast<size_t>(images[0]->height);
174 const float halfWidth = (width / 2.0);
175 const float halfHeight = (height / 2.0);
176 const auto& image0Data = images[0]->pixels;
177 const auto& image1Data = images[1]->pixels;
178
179 for (size_t j = 0; j < height; j++)
180 {
181 for (size_t i = 0; i < width; i++)
182 {
183 auto coord = j * width + i;
184 // unsigned short value = images[1]->pixels[3 * coord + 0]
185 // + (images[1]->pixels[3 * coord + 1] << 8);
187 image1Data[3 * coord + 0], image1Data[3 * coord + 1], image1Data[3 * coord + 2]);
188
189 if (maxDist > 0 && value > maxDist)
190 {
191 value = -1.0f;
192 }
193 else if (value < minDist)
194 {
195 value = -1.0f;
196 }
197
198
199 PointL& p = cloud->points.at(coord);
200 auto index = 3 * (coord);
201 p.r = image0Data[index + 0];
202 p.g = image0Data[index + 1];
203 p.b = image0Data[index + 2];
204
205 if (value < 0)
206 {
207 if (nanValue >= 0)
208 {
209 p.z = static_cast<float>(nanValue);
210 //p.z /= 1000.0;
211 p.x = -1.0 * (i - halfWidth) / width * p.z * scaleX;
212 p.y = (halfHeight - j) / height * p.z * scaleY;
213 }
214 else
215 {
216 p.x = p.y = p.z = std::numeric_limits<float>::quiet_NaN();
217 }
218 }
219 else
220 {
221 p.z = static_cast<float>(value);
222 p.x = -1.0 * (i - halfWidth) / width * p.z * scaleX;
223 p.y = (halfHeight - j) / height * p.z * scaleY;
224 }
225
226 if (numImages > 2)
227 {
228 auto& image2Data = images[2]->pixels;
229
230 if (images[2]->bytesPerPixel == 3)
231 {
232 p.label = static_cast<unsigned int>(image2Data[index + 0] +
233 (image2Data[index + 1] << 8) +
234 (image2Data[index + 2] << 16));
235 }
236 else
237 {
238 p.label = static_cast<unsigned int>(image2Data[coord]);
239 }
240 }
241 else
242 {
243 p.label = 0;
244 }
245 }
246 }
247
248 processCounter++;
249 debugObserver->setDebugDatafield(getName(), "processCount", new Variant(processCounter));
250
251 float angle = getProperty<float>("PointCloudRotationZ").getValue() / 180.f * M_PI;
252 if (angle != 0.0f)
253 {
254 Eigen::Matrix4f transform2 = Eigen::Matrix4f::Identity();
255 Eigen::Matrix3f m;
256 m = Eigen::AngleAxisf(angle, Eigen::Vector3f::UnitZ());
257 transform2.block<3, 3>(0, 0) *= m;
258 pcl::transformPointCloud(*cloud, *transformedCloud, transform2);
259 transformedCloud->header.stamp = imageMetaInfo->timeProvided;
260 providePointCloud(transformedCloud);
261 }
262 else
263 {
264 providePointCloud(cloud);
265 }
266 // pcl::PointCloud<PointL>::Ptr temp(new pcl::PointCloud<PointL>());
267}
268
269visionx::MetaPointCloudFormatPtr
271{
272 visionx::MetaPointCloudFormatPtr info = new visionx::MetaPointCloudFormat();
273 info->size = getProperty<int>("PointCloudWidth").getValue() *
274 getProperty<int>("PointCloudHeight").getValue() * sizeof(PointL);
275 info->capacity = info->size;
276 info->type = visionx::PointContentType::eColoredLabeledPoints;
277 return info;
278}
279
286
287namespace armarx
288{
289 std::string
291 {
292 return "ImageToPointCloud";
293 }
294
297} // namespace armarx
#define ARMARX_REGISTER_COMPONENT_EXECUTABLE(ComponentT, applicationName)
Definition Decoupled.h:29
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)
Brief description of class ImageToPointCloud.
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.
static std::string GetDefaultName()
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