NerianVisionImageProvider.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::NerianVisionImageProvider
17 * @author Julian Tusch <julian.tusch@student.kit.edu>
18 * @date 2025
19 * @copyright http://www.gnu.org/licenses/gpl-2.0.txt
20 * GNU General Public License
21 */
22
24
25#include <cstring>
26#include <exception>
27#include <memory>
28#include <string>
29#include <vector>
30
31#include <opencv2/imgproc.hpp>
32
33#include <Ice/Config.h>
34#include <Ice/Current.h>
35#include <IceUtil/Time.h>
36
37
38// Nerian Vision SDK
39#include <visiontransfer/deviceenumeration.h>
40#include <visiontransfer/deviceinfo.h>
41#include <visiontransfer/deviceparameters.h>
42#include <visiontransfer/imageset.h>
43#include <visiontransfer/imagetransfer.h>
44
45// IVT
46#include <Image/ByteImage.h>
47#include <Image/ImageProcessor.h>
48
49// ArmarX
56
57// VisionX
58#include <VisionX/interface/components/Calibration.h>
59#include <VisionX/interface/core/DataTypes.h>
63
64namespace visionx
65{
66 void
68 {
69 using visiontransfer::DeviceEnumeration;
70 using visiontransfer::DeviceInfo;
71 using visiontransfer::DeviceParameters;
72
73 DeviceEnumeration deviceEnum;
74 DeviceEnumeration::DeviceList devices = deviceEnum.discoverDevices();
75 if (devices.size() == 0)
76 {
77 ARMARX_INFO << "No Nerian Vision devices detected! Shutting down...";
78 getArmarXManager()->asyncShutdown();
79 throw armarx::LocalException("No Nerian Vision devices detected!");
80 }
81
82 device = devices[0];
83 auto deviceModel = device.getModel();
84 setMetaInfo("model", new armarx::Variant(getNameForDeviceModel(deviceModel)));
85 setMetaInfo("serialNumber", new armarx::Variant(device.getSerialNumber()));
86 setMetaInfo("firmwareVersion", new armarx::Variant(device.getFirmwareVersion()));
87
88 ARMARX_INFO << "Using Nerian Vision device: " << device.toString();
89
90 // the Ruby camera has an additional color camera
91 enableColorCamera = getProperty<bool>("enableColorCamera").getValue();
92 int numImages = (deviceModel == DeviceInfo::RUBY && enableColorCamera) ? 3 : 2;
93 setNumberImages(numImages);
94
95 visionx::ImageDimension dimensions;
96
97 std::unique_ptr<DeviceParameters> parameterPtr = nullptr;
98 try
99 {
100 parameterPtr = std::make_unique<DeviceParameters>(device);
101 }
102 catch (const std::exception& e)
103 {
104 ARMARX_INFO << "Failed to open device parameters."
105 << " (" << e.what() << ")";
106 return;
107 }
108
109 try
110 {
111 auto dim = parameterPtr->getParameter("RT_output_image_size").getTensorData();
112 dimensions.width = static_cast<int>(dim[0]);
113 dimensions.height = static_cast<int>(dim[1]);
114 }
115 catch (const std::exception& e)
116 {
117 ARMARX_INFO << "Failed to get dimension from device parameters. Using app property "
118 "`dimensions` as fallback."
119 << " (" << e.what() << ")";
120 dimensions = getProperty<visionx::ImageDimension>("dimensions").getValue();
121 }
122
123 ARMARX_DEBUG << "Using image dimensions: " << dimensions.width << "x" << dimensions.height;
124 setImageFormat(dimensions, visionx::eRgb, visionx::eBayerPatternRg);
125
126 ARMARX_DEBUG << "Configuring device.";
127
128 rectifyImages = getProperty<bool>("rectifyImages").getValue();
129 try
130 {
131 if (deviceModel == DeviceInfo::DeviceModel::RUBY)
132 {
133 parameterPtr->setParameter("output_channel_left_enabled", true);
134 parameterPtr->setParameter("output_channel_right_enabled", true);
135 parameterPtr->setParameter("output_channel_color_enabled", enableColorCamera);
136 parameterPtr->setParameter("output_channel_color_mode", 0);
137 parameterPtr->setParameter("output_channel_disparity_enabled", false);
138 }
139
140 parameterPtr->setOperationMode(rectifyImages
141 ? visiontransfer::DeviceParameters::RECTIFY
142 : DeviceParameters::OperationMode::PASS_THROUGH);
143 }
144 catch (const std::exception& e)
145 {
146 ARMARX_INFO << "Failed to configure device." << " (" << e.what() << ")";
148 << "If the images are not displayed as expected, manual configuration of the "
149 << "device may be necessary. To do this, open the web interface of the device at "
150 << device.getIpAddress() << " and verify/set the following settings: "
151 << (deviceModel == DeviceInfo::DeviceModel::RUBY
152 ? "Output channels [left, right, color] are enabled and the "
153 "disparity output channel is disabled. Optionally, change the "
154 "color output mode to not use projection in order to avoid artifacts."
155 : "")
156 << "Operation mode is set to " << (rectifyImages ? "RECTIFY" : "PASS_THROUGH")
157 << ".";
158 }
159 }
160
161 void
171
172 void
174 {
175 using visiontransfer::DeviceParameters;
176 using visiontransfer::ImageSet;
177 using visiontransfer::ImageTransfer;
178
179 ARMARX_DEBUG << "Started image processing task.";
180
181 std::unique_ptr<ImageTransfer> imageTransferPtr = nullptr;
182 try
183 {
184 imageTransferPtr = std::make_unique<ImageTransfer>(device);
185 }
186 catch (const std::exception& e)
187 {
188 ARMARX_INFO << "Failed to open device."
189 << " (" << e.what() << ")";
190 return;
191 }
192
193 if (imageTransferPtr == nullptr)
194 {
195 ARMARX_INFO << "Failed to connnect to device!";
196 return;
197 }
198
199 while (not imageProcessingTask->isStopped())
200 {
201 ImageSet imageSet;
202 bool status = false;
203 try
204 {
205 // receiveImageSet() has an internal timout of 100ms
206 status = imageTransferPtr->receiveImageSet(imageSet);
207 }
208 catch (const std::exception& e)
209 {
210 ARMARX_INFO << deactivateSpam(30) << "Error receiving images from device."
211 << " (" << e.what() << ")";
212 continue;
213 }
214
215 if (!status)
216 {
217 ARMARX_INFO << deactivateSpam(30) << "Did not get frame until timeout of 100ms.";
218 continue;
219 }
220
221 int numImages = getNumberImages();
222 if (numImages != imageSet.getNumberOfImages())
223 {
224 ARMARX_INFO << deactivateSpam(30) << "Expected " << numImages << " images, but got "
225 << imageSet.getNumberOfImages()
226 << " images. Please verify the device and component configurations.";
227 continue;
228 }
229
230 if (!imageSet.hasImageType(ImageSet::ImageType::IMAGE_LEFT) ||
231 !imageSet.hasImageType(ImageSet::ImageType::IMAGE_RIGHT) ||
232 (numImages == 3 && !imageSet.hasImageType(ImageSet::ImageType::IMAGE_COLOR)))
233 {
235 << "Received image set does not contain the expected image types. "
236 "Please verify the device and component configurations, then "
237 "restart this component.";
238 continue;
239 }
240
241 bool success = true;
242 std::vector<CByteImage*> imageBuffers(numImages);
243
244 for (int i = 0; i < numImages; i++)
245 {
246 auto imageType = imageSet.getImageType(i);
247 auto imageFormat = imageSet.getPixelFormat(imageType);
248
249 if (imageType == ImageSet::ImageType::IMAGE_DISPARITY ||
250 imageType == ImageSet::ImageType::IMAGE_UNDEFINED ||
251 (!enableColorCamera && imageType == ImageSet::ImageType::IMAGE_COLOR))
252 {
253 ARMARX_INFO << deactivateSpam(30) << "Received unexpected image type: "
254 << ImageSet::getNameForImageType(imageType);
255 success = false;
256 break;
257 }
258
259 cv::Mat bgr_image;
260 if (imageFormat == ImageSet::ImageFormat::FORMAT_8_BIT_MONO)
261 {
262 cv::Mat grayscale_image;
263 imageSet.toOpenCVImage(i, grayscale_image);
264 cv::cvtColor(grayscale_image, bgr_image, cv::COLOR_GRAY2BGR);
265 }
266 else if (imageFormat == ImageSet::ImageFormat::FORMAT_8_BIT_RGB)
267 {
268 imageSet.toOpenCVImage(i, bgr_image, true);
269 }
270 else
271 {
272 ARMARX_INFO << deactivateSpam(30) << "Received unexpected image format: "
273 << ImageSet::getNameForImageFormat(imageFormat);
274 success = false;
275 break;
276 }
277
278 imageBuffers[i] = new CByteImage();
280 }
281
282 if (success)
283 {
284 int t_s = 0;
285 int t_us = 0;
286 imageSet.getTimestamp(t_s, t_us);
287 const IceUtil::Time timestamp =
288 IceUtil::Time::seconds(t_s) + IceUtil::Time::microSeconds(t_us);
290 }
291 }
292
293 ARMARX_DEBUG << "Stopped image processing task.";
294 }
295
296 void
298 {
299 ARMARX_DEBUG << "Disconnecting " << getName();
300
301 // Stop task
302 {
303 ARMARX_DEBUG << "Stopping image processing thread...";
304 imageProcessingTask->stop(false);
305 ARMARX_DEBUG << "Waiting for image processing thread to stop...";
306 imageProcessingTask->waitForStop();
307 ARMARX_DEBUG << "Image processing thread stopped";
308 }
309 }
310
311 void
315
316 void
317 NerianVisionImageProvider::startCapture(Ice::Float /*framesPerSecond*/,
318 const Ice::Current& /*c*/)
319 {
320 }
321
322 void
323 NerianVisionImageProvider::stopCapture(const Ice::Current& /*c*/)
324 {
325 }
326
327 std::string
329 {
330 return {};
331 }
332
333 bool
335 {
336 return rectifyImages;
337 }
338
339 StereoCalibration
341 {
342 StereoCalibration calibration;
343
344 // TODO: find out how to retrieve the calibration data from nerian vision devices
345 // calibration.calibrationLeft = calibration.calibrationRight = ...
346
347 calibration.rectificationHomographyLeft = calibration.rectificationHomographyRight =
348 visionx::tools::convertEigenMatToVisionX(Eigen::Matrix3f::Identity());
349
350 return calibration;
351 }
352
358} // namespace visionx
std::string timestamp()
SpamFilterDataPtr deactivateSpam(SpamFilterDataPtr const &spamFilter, float deactivationDurationSec, const std::string &identifier, bool deactivate)
Definition Logging.cpp:75
std::string getConfigIdentifier()
Retrieve config identifier for this component as set in constructor.
Definition Component.cpp:90
Property< PropertyType > getProperty(const std::string &name)
std::string getName() const
Retrieve name of object.
void setMetaInfo(const std::string &id, const VariantBasePtr &value)
Allows to set meta information that can be queried live via Ice interface on the ArmarXManager.
ArmarXManagerPtr getArmarXManager() const
Returns the ArmarX manager used to add and remove components.
IceUtil::Handle< RunningTask< T > > pointer_type
Shared pointer type for convenience.
The Variant class is described here: Variants.
Definition Variant.h:224
void setImageFormat(ImageDimension imageDimension, ImageType imageType, BayerPatternType bayerPatternType=visionx::eBayerPatternRg)
Sets the image basic format data.
int getNumberImages(const Ice::Current &c=Ice::emptyCurrent) override
Retrieve number of images handled by this provider.
void provideImages(void **inputBuffers, const IceUtil::Time &imageTimestamp=IceUtil::Time())
send images raw.
void setNumberImages(int numberImages)
Sets the number of images on each capture.
void ** imageBuffers
Image buffer memory.
bool getImagesAreUndistorted(const Ice::Current &c) override
void stopCapture(const Ice::Current &c) override
armarx::PropertyDefinitionsPtr createPropertyDefinitions() override
void onConnectImageProvider() override
This is called when the Component::onConnectComponent() setup is called.
visionx::StereoCalibration getStereoCalibration(const Ice::Current &c) override
std::string getReferenceFrame(const Ice::Current &c) override
static std::string getNameForDeviceModel(const visiontransfer::DeviceInfo::DeviceModel &model)
void startCapture(Ice::Float framesPerSecond, const Ice::Current &c) override
void onInitImageProvider() override
This is called when the Component::onInitComponent() is called.
void onExitImageProvider() override
This is called when the Component::onExitComponent() setup is called.
#define ARMARX_INFO
The normal logging level.
Definition Logging.h:181
#define ARMARX_DEBUG
The logging level for output that is only interesting while debugging.
Definition Logging.h:184
#define ARMARX_WARNING
The logging level for unexpected behaviour, but not a serious problem.
Definition Logging.h:193
IceUtil::Handle< class PropertyDefinitionContainer > PropertyDefinitionsPtr
PropertyDefinitions smart pointer type.
void convert(const CByteImage &in, cv::Mat &out)
Converts an IVT CByteImage to OpenCV's BGR Mat.
Definition helper.cpp:40
visionx::types::Mat convertEigenMatToVisionX(Eigen::MatrixXf m)
ArmarX headers.