RCImageProvider.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::RCImageProvider
17 * @author Markus Grotz ( markus dot grotz at kit dot edu )
18 * @date 2017
19 * @copyright http://www.gnu.org/licenses/gpl-2.0.txt
20 * GNU General Public License
21 */
22
23#include "RCImageProvider.h"
24
25
26using namespace armarx;
27
28
29#include <stdlib.h>
30
32
35
36#include <Calibration/Calibration.h>
37#include <Calibration/StereoCalibration.h>
38#include <rc_genicam_api/buffer.h>
39#include <rc_genicam_api/config.h>
40#include <rc_genicam_api/image.h>
41#include <rc_genicam_api/interface.h>
42#include <rc_genicam_api/pixel_formats.h>
43#include <rc_genicam_api/system.h>
44
45
46using namespace armarx;
47
50{
51 defineOptionalProperty<std::string>("DeviceId", "00_14_2d_2c_6e_ed", "");
52 defineOptionalProperty<float>("ScaleFactor", 1.0, "Image down scale factor");
53 defineOptionalProperty<float>("FrameRate", 25.0f, "Frames per second")
54 .setMatchRegex("\\d+(.\\d*)?")
55 .setMin(0.0)
56 .setMax(25.0);
57 defineOptionalProperty<bool>("AutoExposure", false, "Enables auto exposure");
59 "ExposureTimeMs", 12.0f, "Exposure time in ms if auto exposure is disabled");
60 defineOptionalProperty<float>("GainDb", 0.0f, "Gain in db");
61
62 defineOptionalProperty<bool>("EnableColor", true, "Enables colored images");
63
64 defineOptionalProperty<bool>("EnableAutoWhiteBalance", false, "Enable auto white balance");
65 defineOptionalProperty<float>("WhiteBalanceRatioRed", 1.2f, "Red to green balance ratio.")
66 .setMin(0.125)
67 .setMax(8.0);
68 defineOptionalProperty<float>("WhiteBalanceRatioBlue", 2.4f, "Blue to green balance ratio.")
69 .setMin(0.125)
70 .setMax(8.0);
71
73 "ReferenceFrameName", "Roboception_LeftCamera", "Optional reference frame name.");
74
76 "RobotHealthTopicName", "RobotHealthTopic", "Name of the RobotHealth topic");
77}
78
81{
84
85 // This option is disabled, as the depth image cannot be set to Full quality (like RGB image).
86 // The RGB image could be scaled down to the depth image. If this is what you want, feel free to implement it.
87 defs->optional(enableDepth,
88 "EnableDepth",
89 "Enables depth image, i.e. provide RGB (left) + Depth.\n"
90 "Note that the depth image is scaled up by 2 (nearest neighbour) to match the "
91 "resolution of the color image.");
92
93 return defs;
94}
95
96RCImageProvider::RCImageProvider() : intensityBuffer(50), disparityBuffer(25)
97{
98}
99
100void
101RCImageProvider::updateCameraSettings()
102{
103 using namespace visionx::roboception;
104
105 bool autoExposure = getProperty<bool>("AutoExposure");
106 float exposureTimeMs = getProperty<float>("ExposureTimeMs");
107 float gainDb = getProperty<float>("GainDb");
108
109 bool enableColor = getProperty<bool>("EnableColor").getValue();
110
111 std::shared_ptr<GenApi::CNodeMapRef> nodemap = dev->getRemoteNodeMap();
112 rcg::setString(nodemap,
114 "IntensityCombined",
115 true); // selectz for the following actions
116
117 ARMARX_INFO << "updating camera settings...";
118
119 if (enableColor)
120 {
121 if (rcg::setEnum(nodemap, "PixelFormat", "YCbCr411_8", false))
122 {
123 ARMARX_INFO << "Enabling color mode";
124 }
125 else
126 {
127 ARMARX_WARNING << "Falling back to monochrome mode";
128 enableColor = false;
129 }
130 }
131
132 if (enableColor)
133 {
134 if (getProperty<bool>("EnableAutoWhiteBalance").getValue())
135 {
136 if (rcg::setEnum(nodemap, "BalanceWhiteAuto", "Continuous"))
137 {
138 ARMARX_INFO << "enabling auto white balance";
139 }
140 else
141 {
142 ARMARX_WARNING << "unable to enable auto white balance";
143 }
144 }
145 else
146 {
147 if (rcg::setEnum(nodemap, "BalanceWhiteAuto", "Off"))
148 {
149 ARMARX_INFO << "disabling auto white balance";
150
151 rcg::setEnum(nodemap, "BalanceRatioSelector", "Red");
152 if (!rcg::setFloat(nodemap,
153 "BalanceRatio",
154 getProperty<float>("WhiteBalanceRatioRed").getValue()))
155 {
156 ARMARX_WARNING << "unable to set white balance ratio for red-green";
157 }
158
159 rcg::setEnum(nodemap, "BalanceRatioSelector", "Blue");
160 if (!rcg::setFloat(nodemap,
161 "BalanceRatio",
162 getProperty<float>("WhiteBalanceRatioBlue").getValue()))
163 {
164 ARMARX_WARNING << "unable to set white balance ratio for green-blue";
165 }
166 }
167 else
168 {
169 ARMARX_WARNING << "unable to disable auto white balance";
170 }
171 }
172
173 rcg::setEnum(nodemap, "BalanceRatioSelector", "Red");
174 double wbRatioRed = rcg::getFloat(nodemap, "BalanceRatio");
175
176 rcg::setEnum(nodemap, "BalanceRatioSelector", "Blue");
177 double wbRatioBlue = rcg::getFloat(nodemap, "BalanceRatio");
178
179 ARMARX_INFO << "white balance ratio is red: " << wbRatioRed << " blue: " << wbRatioBlue;
180 }
181 else
182 {
183 if (rcg::setEnum(nodemap, "PixelFormat", "Mono8", false))
184 {
185 ARMARX_INFO << "Enabling monochrome mode";
186 }
187 else
188 {
189 ARMARX_ERROR << "Could not set monochrome mode";
190 }
191 }
192
193 if (enableDepth)
194 {
195 try
196 {
197 // rcg::setEnum(nodemap, GC_DEPTH_QUALITY, GC_DEPTH_QUALITY_FULL, true);
198 rcg::setEnum(nodemap, GC_DEPTH_QUALITY, GC_DEPTH_QUALITY_HIGH, true);
199 }
200 catch (std::invalid_argument& e)
201 {
202 ARMARX_INFO << e.what();
203 ARMARX_WARNING << "Depth quality '" << GC_DEPTH_QUALITY_FULL
204 << "' is currently not available for this device. "
205 << "Falling back to '" << GC_DEPTH_QUALITY_HIGH << "' (640x480@3FPS).";
206 rcg::setEnum(nodemap, GC_DEPTH_QUALITY, GC_DEPTH_QUALITY_HIGH, true);
207 }
208 }
209
210 std::string oldAutoExposure = rcg::getEnum(nodemap, GC_EXPOSURE_AUTO, false);
211 ARMARX_INFO << "Setting auto exposure from " << oldAutoExposure << " to " << autoExposure;
212 if (!rcg::setEnum(nodemap, GC_EXPOSURE_AUTO, autoExposure ? "Continuous" : "Off", false))
213 {
214 ARMARX_WARNING << "Could not set auto exposure to: " << autoExposure;
215 }
216 double oldExposureTime = rcg::getFloat(nodemap, GC_EXPOSURE_TIME) / 1000.0;
217 ARMARX_INFO << "Setting exposure time from " << oldExposureTime << " to " << exposureTimeMs;
218 if (!rcg::setFloat(nodemap, GC_EXPOSURE_TIME, exposureTimeMs * 1000.0, false))
219 {
220 ARMARX_WARNING << "Could not set exposure time to: " << exposureTimeMs;
221 }
222
223 double oldGain = rcg::getFloat(nodemap, GC_GAIN);
224 ARMARX_INFO << "Setting gain from " << oldGain << " to " << gainDb;
225 if (!rcg::setFloat(nodemap, GC_GAIN, gainDb, false))
226 {
227 ARMARX_WARNING << "Could not set gain to: " << gainDb;
228 }
229
230 ARMARX_INFO << "updating camera settings...done!";
231}
232
233void
235{
236 // offeringTopic(getProperty<std::string>("RobotHealthTopicName").getValue());
237
238 if (!initDevice(getProperty<std::string>("DeviceId")))
239 {
240 getArmarXManager()->asyncShutdown();
241 return;
242 }
243
244 if (enableDepth)
245 {
246 enableIntensity(true);
248 enableDisparity(true);
249 enableConfidence(false);
250 enableError(false);
251 }
252 else
253 {
254 enableIntensity(false);
256 enableDisparity(false);
257 enableConfidence(false);
258 enableError(false);
259 }
260
261 rcg::setString(nodemap, visionx::roboception::GC_COMPONENT_SELECTOR, "IntensityCombined", true);
262
263
264 updateCameraSettings();
265
267 "/tmp/armar6_rc_color_calibration.txt");
268
269 frameRate = getProperty<float>("FrameRate");
270 rcg::setFloat(nodemap, "AcquisitionFrameRate", double(frameRate));
272
273 scan3dCoordinateScale = rcg::getFloat(nodemap, "Scan3dCoordinateScale", nullptr, nullptr, true);
274
276 setImageFormat(dimensions, visionx::eRgb, visionx::eBayerPatternRg);
277 setImageSyncMode(visionx::eCaptureSynchronization);
278}
279
280void
282{
283 // CapturingImageProvider::onConnectImageProvider();
284 // robotHealthTopic = getTopic<RobotHealthInterfacePrx>(getProperty<std::string>("RobotHealthTopicName").getValue());
285}
286
287void
292
293void
295{
296 (void)framesPerSecond;
297 startRC();
298}
299
300void
305
306bool
307RCImageProvider::capture(void** ppImageBuffers)
308{
309 visionx::ImageFormatInfo imageFormat = getImageFormat();
310
311 IceUtil::Time timeoutTime =
312 IceUtil::Time::now(IceUtil::Time::Monotonic) + IceUtil::Time::secondsDouble(2);
313 while (IceUtil::Time::now(IceUtil::Time::Monotonic) < timeoutTime)
314 {
315 const rcg::Buffer* buffer = stream->grab(
316 (timeoutTime - IceUtil::Time::now(IceUtil::Time::Monotonic)).toMilliSeconds());
317 if (!buffer)
318 {
320 << "buffer is NULL - Unable to get image - restarting streaming";
321 try
322 {
323 stream->stopStreaming();
324 }
325 catch (std::exception const& ex)
326 {
327 ARMARX_WARNING << "stopStreaming failed: " << ex.what();
328 }
329 try
330 {
331 stream->close();
332 }
333 catch (std::exception const& ex)
334 {
335 ARMARX_WARNING << "close failed: " << ex.what();
336 }
337 ARMARX_WARNING << deactivateSpam(10) << "streaming stopped";
338 stream = nullptr;
340 "/tmp/armar6_rc_color_calibration.txt");
341
342 stream->open();
343 stream->startStreaming();
344 ARMARX_WARNING << deactivateSpam(10) << "streaming started again";
345 return false;
346 }
347
348 if (buffer->getIsIncomplete())
349 {
350 continue; // try again
351 }
352 if (buffer->getNumberOfParts() > 1)
353 {
354 ARMARX_WARNING << deactivateSpam(10) << "Buffer contains more than one part!";
355 return false;
356 }
357
358 const int part = 0;
359 if (!buffer->getImagePresent(part))
360 {
361 ARMARX_WARNING << deactivateSpam(10) << "Buffer does not contain an image!";
362 return false;
363 }
364
365 size_t px = buffer->getXPadding(part);
366 uint64_t format = buffer->getPixelFormat(part);
367
368 int width = imageFormat.dimension.width;
369 int height = imageFormat.dimension.height;
370 if (scaleFactor > 1.0f)
371 {
372 width *= scaleFactor;
373 height *= scaleFactor;
374 }
375
376 const int imageSize =
377 imageFormat.dimension.width * imageFormat.dimension.height * imageFormat.bytesPerPixel;
378
379 // ARMARX_LOG << "grabbing image " << imageFormat.dimension.width << "x" << imageFormat.dimension.height << " vs " << buffer->getWidth() << "x" << buffer->getHeight();
380
381
382 if (!enableDepth)
383 {
384 const uint8_t* inputPixels = static_cast<const uint8_t*>(buffer->getBase(0));
385
386 for (size_t n = 0; n < numImages; n++)
387 {
389 n,
390 inputPixels,
391 size_t(width),
392 size_t(height),
393 format,
394 px);
395 }
396 }
397 else
398 {
399 switch (format)
400 {
401 case PfncFormat::Mono8:
402 case PfncFormat::Confidence8:
403 case Error8:
404 case PfncFormat::YCbCr411_8:
405 intensityBuffer.add(buffer, part);
406 break;
407 case Coord3D_C16:
408 disparityBuffer.add(buffer, part);
409 break;
410 default:
411 ARMARX_INFO << "Unexpected pixel format: " << int(format);
412 break;
413 }
414
415 uint64_t timestamp = buffer->getTimestampNS();
416
417 std::shared_ptr<const rcg::Image> intensity = intensityBuffer.find(timestamp);
418 std::shared_ptr<const rcg::Image> disparity = disparityBuffer.find(timestamp);
419
420 if (!intensity || !disparity)
421 {
422 // Waiting for second synchronized image
423 return false;
424 }
425 updateTimestamp(Ice::Long(buffer->getTimestamp()));
426 ARMARX_DEBUG << "Received all synchronized images at timestamp "
427 << buffer->getTimestamp() << ".";
428
429
430 // Build images
431
432 size_t upscale = 1;
433 if (intensity->getWidth() / disparity->getWidth() == 2 &&
434 intensity->getHeight() / disparity->getHeight() == 2)
435 {
436 upscale = 2;
437 static bool reported = false;
438 if (!reported)
439 {
440 ARMARX_INFO << "Scaling up depth image by a factor of " << upscale << ".";
441 reported = true;
442 }
443 }
444
445 // Left eye image.
446 fillCameraImageRGB(cameraImages[0]->pixels, 0, *intensity);
447 // Depth image.
450 baseline,
451 scan3dCoordinateScale,
452 *disparity,
453 upscale);
454 }
455
456
458 updateTimestamp(buffer->getTimestampNS() / 1000, false);
459 if (scaleFactor > 1.0f)
460 {
461 ImageProcessor::Resize(cameraImages[0], smallImages[0]);
462 ImageProcessor::Resize(cameraImages[1], smallImages[1]);
463
464 for (size_t i = 0; i < numImages; i++)
465 {
466 memcpy(ppImageBuffers[i], smallImages[i]->pixels, imageSize);
467 }
468 }
469 else
470 {
471
472 for (size_t i = 0; i < numImages; i++)
473 {
474 memcpy(ppImageBuffers[i], cameraImages[i]->pixels, imageSize);
475 }
476 }
477
478 // RobotHealthHeartbeatArgs rhha;
479 // rhha.maximumCycleTimeWarningMS = 500; // Robotception is really slow.
480 // rhha.maximumCycleTimeErrorMS = 1000;
481 // robotHealthTopic->heartbeat(getName(), rhha);
482
483 return true;
484 }
485
486 ARMARX_INFO << deactivateSpam() << "failed to capture image";
487 return false;
488}
std::string timestamp()
SpamFilterDataPtr deactivateSpam(SpamFilterDataPtr const &spamFilter, float deactivationDurationSec, const std::string &identifier, bool deactivate)
Definition Logging.cpp:75
#define VAROUT(x)
ComponentPropertyDefinitions(std::string prefix, bool hasObjectNameParameter=true)
Definition Component.cpp:46
std::string getConfigIdentifier()
Retrieve config identifier for this component as set in constructor.
Definition Component.cpp:90
Property< PropertyType > getProperty(const std::string &name)
ArmarXManagerPtr getArmarXManager() const
Returns the ArmarX manager used to add and remove components.
std::string prefix
Prefix of the properties such as namespace, domain, component name, etc.
PropertyDefinition< PropertyType > & defineOptionalProperty(const std::string &name, PropertyType defaultValue, const std::string &description="", PropertyDefinitionBase::PropertyConstness constness=PropertyDefinitionBase::eConstant)
Property< PropertyType > getProperty(const std::string &name)
Property creation and retrieval.
void onStartCapture(float frameRate) override
This is called when the image provider capturing has been started.
armarx::PropertyDefinitionsPtr createPropertyDefinitions() override
void onStartCapturingImageProvider() override
This is called when the Component::onConnectComponent() setup is called.
void onStopCapture() override
This is called when the image provider capturing has been stopped.
void onExitCapturingImageProvider() override
This is called when the Component::onExitComponent() setup is called.
void onInitCapturingImageProvider() override
This is called when the Component::onInitComponent() is called.
void setImageSyncMode(ImageSyncMode imageSyncMode)
Sets the image synchronization mode.
armarx::IceSharedMemoryProvider< unsignedchar >::pointer_type sharedMemoryProvider
shared memory provider
void updateTimestamp(Ice::Long timestamp, bool threadSafe=true)
Updates the timestamp of the currently captured image.
ImageFormatInfo getImageFormat(const Ice::Current &c=Ice::emptyCurrent) override
Returns the entire image format info struct via Ice.
void setImageFormat(ImageDimension imageDimension, ImageType imageType, BayerPatternType bayerPatternType=visionx::eBayerPatternRg)
Sets the image basic format data.
void setNumberImages(int numberImages)
Sets the number of images on each capture.
void enableDisparity(bool enabled)
void enableError(bool enabled)
void enableIntensityCombined(bool enabled)
std::shared_ptr< GenApi::CNodeMapRef > nodemap
bool initDevice(const std::string &dname)
void enableConfidence(bool enabled)
void enableIntensity(bool enabled)
void setupStreamAndCalibration(float scalefactor, const std::string &calibSavePath)
visionx::ImageDimension dimensions
static void fillCameraImageDepth(unsigned char *outputPixels, double f, double t, double scale, const rcg::Image &disparity, size_t upscale=1)
static void fillCameraImageRGB(unsigned char *outputPixels, size_t nImage, const uint8_t *inputPixels, size_t width, size_t height, uint64_t pixelFormat, size_t xpadding)
#define ARMARX_INFO
The normal logging level.
Definition Logging.h:181
#define ARMARX_ERROR
The logging level for unexpected behaviour, that must be fixed.
Definition Logging.h:196
#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
T getValue(nlohmann::json &userConfig, nlohmann::json &defaultConfig, const std::string &entryName)
Definition utils.h:80
This file offers overloads of toIce() and fromIce() functions for STL container types.
std::shared_ptr< SharedMemoryScopedWriteLock > SharedMemoryScopedWriteLockPtr
IceUtil::Handle< class PropertyDefinitionContainer > PropertyDefinitionsPtr
PropertyDefinitions smart pointer type.
const char *const GC_DEPTH_QUALITY_HIGH
const char *const GC_COMPONENT_SELECTOR
const char *const GC_DEPTH_QUALITY_FULL