PlaybackImageProvider.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::components
17 * @author Christian R. G. Dreher <c.dreher@kit.edu>
18 * @date 2018
19 * @copyright http://www.gnu.org/licenses/gpl-2.0.txt
20 * GNU General Public License
21 */
22
23
25
28
29
30// ArmarX
31#include <filesystem>
32#include <functional>
33#include <string>
34#include <tuple>
35#include <vector>
36
37#include <SimoxUtility/algorithm/string/string_tools.h>
38
42
46
47using namespace armarx;
48using namespace visionx;
49using namespace visionx::components;
53
58
63
64namespace fs = std::filesystem;
65
66void
68{
69 getProperty(m_stereo_reference_frame, "stereo.reference_frame");
70 getProperty(m_stereo_calibration_file, "stereo.calibration_file");
71
72 fs::path basePath = getProperty<std::string>("base_path").getValue();
73 std::string recordingFilesStr = getProperty<std::string>("recording_files").getValue();
74 std::vector<std::string> recordingFiles = simox::alg::split(recordingFilesStr, ";");
75
77 {
78 if (recordingFiles.size() != 2)
79 {
80 throw LocalException() << "Stereo calibration file and/or reference frame were set, "
81 << "but there are " << recordingFiles.size() << " recording "
82 << "files (expected 2).";
83 }
84
85 init_stereo_calibration_file();
86 init_stereo_calibration();
87 }
88
89 auto parse_fps = [](const std::string& setting, double source_fps) -> std::tuple<double, double>
90 {
91 // This lambda assumes that all operations are safe to call, as fps_setting was checked
92 // against a RegEx while configuration, and does therefore not perform any sanity checks
93
94 const unsigned long int prefix_length = std::string("sourceX").size();
95 std::string setting_value;
96 std::function<double(double, double)> operation;
97
98 // Source FPS
99 if (setting == "source")
100 {
101 return std::make_tuple(source_fps, 1.);
102 }
103 // Source FPS multiplied by value
104 else if (simox::alg::starts_with(setting, "source*"))
105 {
106 // Multiplication
107 operation = [](double x, double y) -> double { return x * y; };
108 setting_value = setting.substr(prefix_length);
109 }
110 // Source FPS divided by value
111 else if (simox::alg::starts_with(setting, "source/"))
112 {
113 // Division
114 operation = [](double x, double y) -> double { return x / y; };
115 setting_value = setting.substr(prefix_length);
116 }
117 // Source FPS is ignored and the value is assumed to be source FPS
118 else if (simox::alg::starts_with(setting, "source="))
119 {
120 return std::make_tuple(std::stod(setting.substr(prefix_length)), 1.);
121 }
122 // Set FPS to a specific value
123 else
124 {
125 // Identity to keep the user defined setting regardless of source FPS
126 operation = [](double, double fps) -> double { return fps; };
127 setting_value = setting;
128 }
129
130 const double derived_fps = operation(source_fps, std::stod(setting_value));
131 const double playback_speed_normalisation = source_fps / derived_fps;
132
133 return std::make_tuple(derived_fps, playback_speed_normalisation);
134 };
135
136 unsigned int refFrameHeight = 0;
137 unsigned int refFrameWidth = 0;
138 unsigned int actual_source_fps = 0;
139
140 // Instantiate playbacks
141 for (std::string const& recordingFile : recordingFiles)
142 {
143 fs::path fullPath = basePath != "" ? basePath / recordingFile : fs::path(recordingFile);
145 m_playbacks.push_back(playback);
146
147 if (not playback)
148 {
149 ARMARX_ERROR << "Could not find a playback strategy for '" << fullPath.string() << "'";
150 continue;
151 }
152
153 // Sanity checks for image heights and widths
154 {
155 // If uninitialised, use first playback as reference
156 if (refFrameHeight == 0 and refFrameWidth == 0)
157 {
158 refFrameHeight = playback->getFrameHeight();
159 refFrameWidth = playback->getFrameWidth();
160 }
161
162 // Check that neither hight nor width are 0 pixels
163 ARMARX_CHECK_GREATER(playback->getFrameHeight(), 0)
164 << "Image source frame height cannot be 0 pixel";
165 ARMARX_CHECK_GREATER(playback->getFrameWidth(), 0)
166 << "Image source frame width cannot be 0 pixel";
167
168 // Check that the frame heights and widths from all image sources are equal
169 ARMARX_CHECK_EQUAL(playback->getFrameHeight(), refFrameHeight)
170 << "Image source frames must have the same dimensions";
171 ARMARX_CHECK_EQUAL(playback->getFrameWidth(), refFrameWidth)
172 << "Image source frames must have the same dimensions";
173 }
174
175 // Determine playback with the highest FPS
176 if (playback->getFps() > actual_source_fps)
177 {
178 actual_source_fps = playback->getFps();
179 }
180 }
181
182 // Figure out FPS related variables base FPS, normalisations, multiplier
183 double playback_speed_normalisation;
184 std::tie(frameRate, playback_speed_normalisation) =
185 parse_fps(getProperty<std::string>("fps"), actual_source_fps);
187 playback_speed_normalisation * getProperty<double>("playback_speed_multiplier");
188
189 ARMARX_INFO << "Playback at " << frameRate << " FPS, advancing " << m_frame_advance
190 << " per loop";
191
192 // Set up required properties for the image capturer
193 setNumberImages(static_cast<int>(m_playbacks.size()));
195 ImageDimension(static_cast<int>(refFrameWidth), static_cast<int>(refFrameHeight)),
196 eRgb,
197 eBayerPatternGr);
198 setImageSyncMode(eFpsSynchronization);
199}
200
201void
203{
205 {
206 playback->stopPlayback();
207 }
208
209 m_playbacks.clear();
210}
211
212void
214{
215 // pass
216}
217
218void
223
224bool
226{
227 const bool loop = getProperty<bool>("loop");
228 bool success = false; // Result is true if at least one of the playbacks provided a picture
229
230 for (unsigned int i = 0; i < static_cast<unsigned int>(getNumberImages()); ++i)
231 {
233
234 // If the last frame is reached and loop is enabled, rewind
235 if (not playback->hasNextFrame() and loop)
236 {
237 m_current_frame = 0;
238 }
239
240 const unsigned int current_frame = static_cast<unsigned int>(std::round(m_current_frame));
241 const unsigned int min_frame = 0;
242 const unsigned int max_frame = playback->getFrameCount() - 1;
243 unsigned int frame_index = std::clamp(current_frame, min_frame, max_frame);
244 playback->setCurrentFrame(frame_index);
245 success |= playback->getNextFrame(imageBuffer[i]);
246 }
247
249
251 if (success)
252 {
253 updateTimestamp(IceUtil::Time::microSeconds(time.toMicroSecondsSinceEpoch()));
254 }
255
256 // Update working memory.
257 const bool memoryEnabled = getProperty<bool>("memory.enable");
258 if (memoryEnabled and imageBuffer != nullptr)
259 {
260 getImagesServerPlugin().commitImages(imageBuffer, time);
261 }
262
263 return success;
264}
265
266StereoCalibration
268{
269 assert_stereo_initialized();
271}
272
273bool
275{
276 assert_stereo_initialized();
277 return false;
278}
279
280std::string
282{
283 assert_stereo_initialized();
285}
286
287std::string
289{
290 return "PlaybackImageProvider";
291}
292
293std::string
298
301{
303
304 defs->defineRequiredProperty<std::string>(
305 "recording_files",
306 "List of recording files, separated by semicolons `;` for each channel. For video files, "
307 "use the filename, for image sequences, use the folder name where the image sequence is "
308 "located or any frame as pattern.\n"
309 "Wildcards (e.g. `frame_left_*.jpg;frame_right_*.jpg`) are supported as well.\n"
310 "Files will be interpreted as absolute paths, or relative paths to the current working "
311 "directory if base_path is not set. If base_path is set, all paths in recording_files are "
312 "interpreted relative to base_path");
313 defs->defineOptionalProperty<std::string>(
314 "base_path",
315 "",
316 "Common base path of recording_files (will be prepened if set). To unset, use \"\"");
317 defs->defineOptionalProperty<double>("playback_speed_multiplier",
318 1.0,
319 "Adjust the playback speed with this multiplier, e.g. `2` "
320 "= double playback speed, `0.5` = "
321 "half playback speed")
322 .setMin(0);
323 defs->defineOptionalProperty<std::string>(
324 "fps",
325 "source",
326 "Lock the FPS to an absolute value, or a value relative to source FPS. Valid inputs:\n"
327 " 1) `source` => Derive FPS from source\n"
328 " 2) `source*<X>`, with <X> being a positive decimal or integer => Playback with FPS "
329 "= "
330 "source FPS multiplied by <X>\n"
331 " 3) `source/<X>`, with <X> being a positive decimal or integer => Playback with FPS "
332 "= "
333 "source FPS devided by <X>\n"
334 " 4) `<X>`, with <X> being a positive decimal or integer => Playback with FPS at <X>\n"
335 " 5) `source=<X>`, with <X> being a positive decimal or integer => Playback with FPS "
336 "at "
337 "<X>, ignoring source FPS completely (Assume that <X> is source FPS)\n"
338 "With the exception of 5), all settings only have direct effect of the FPS the image "
339 "provider delivers the frames, but not on the playback speed. "
340 "Use `playback_speed_multiplier` to adjust that.\n"
341 "5) is only useful if the metadata of the recording is incomplete or incorrect (for "
342 "example when replaying generic image sequences where the FPS cannot be derived)")
343 .setMatchRegex(
344 R"(source(\/|\*|=)(\d+(\.\d*)?|\.\d+)|(\d+(\.\d*)?|\.\d+)|(\d+(\.\d*)?|\.\d+)|source)");
345 defs->defineOptionalProperty<bool>(
346 "loop", true, "Whether the playback should restart after the last frame was provided");
347
348 // Stereo functionality
349 defs->defineOptionalProperty<std::string>(
350 "stereo.calibration_file",
351 "",
352 "Path to a stereo calibration file that should additionally be provided.");
353 defs->defineOptionalProperty<std::string>(
354 "stereo.reference_frame",
355 "",
356 "Path to a stereo calibration file that should additionally be provided.");
357
358
359 // Working Memory
360 defs->defineOptionalProperty<bool>(
361 "memory.enable", true, "If true, also provide the images as a memory server.");
362
363 return defs;
364}
365
366void
367PlaybackImageProvider::init_stereo_calibration_file()
368{
371
372 if (not success)
373 {
375 }
376}
377
378void
379PlaybackImageProvider::init_stereo_calibration()
380{
381 const bool transform_left_camera_to_identity = true;
382 const bool loading_calibration_sucessful = m_stereo_calibration_ivt.LoadCameraParameters(
383 m_stereo_calibration_file.c_str(), transform_left_camera_to_identity);
384
385 if (not loading_calibration_sucessful)
386 {
388 }
389
392}
393
394void
395PlaybackImageProvider::assert_stereo_initialized()
396{
397 if (not m_stereo_initialized)
398 {
399 throw LocalException() << "Stereo calibration API was called, but the calibration of this "
400 << "PlaybackProvider (instance name '" << getName() << "') was "
401 << "never initialized.\n"
402 << "Provide this PlaybackProvider instance with a calibration file "
403 << "using the 'stereo.calibration_file' property.";
404 }
405}
406
#define ARMARX_REGISTER_COMPONENT_EXECUTABLE(ComponentT, applicationName)
Definition Decoupled.h:29
static bool getAbsolutePath(const std::string &relativeFilename, std::string &storeAbsoluteFilename, const std::vector< std::string > &additionalSearchPaths={}, bool verbose=true)
Default component property definition container.
Definition Component.h:70
std::string getConfigIdentifier()
Retrieve config identifier for this component as set in constructor.
Definition Component.cpp:90
Property< PropertyType > getProperty(const std::string &name)
static DateTime Now()
Definition DateTime.cpp:51
std::string getName() const
Retrieve name of object.
Represents a point in time.
Definition DateTime.h:25
std::int64_t toMicroSecondsSinceEpoch() const
Definition DateTime.cpp:87
void setImageSyncMode(ImageSyncMode imageSyncMode)
Sets the image synchronization mode.
void updateTimestamp(Ice::Long timestamp, bool threadSafe=true)
Updates the timestamp of the currently captured image.
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 setNumberImages(int numberImages)
Sets the number of images on each capture.
virtual std::string getReferenceFrame(const Ice::Current &c=Ice::emptyCurrent) override
virtual bool getImagesAreUndistorted(const Ice::Current &c=Ice::emptyCurrent) override
virtual visionx::StereoCalibration getStereoCalibration(const Ice::Current &c=Ice::emptyCurrent) override
virtual void onStopCapture() override
This is called when the image provider capturing has been stopped.
virtual armarx::PropertyDefinitionsPtr createPropertyDefinitions() override
virtual void onExitCapturingImageProvider() override
This is called when the Component::onExitComponent() setup is called.
std::vector< visionx::imrec::Playback > m_playbacks
virtual void onStartCapture(float) override
This is called when the image provider capturing has been started.
virtual void onInitCapturingImageProvider() override
This is called when the Component::onInitComponent() is called.
virtual std::string getDefaultName() const override
Retrieve default name of component.
#define ARMARX_CHECK_GREATER(lhs, rhs)
This macro evaluates whether lhs is greater (>) than rhs and if it turns out to be false it will thro...
#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_ERROR
The logging level for unexpected behaviour, that must be fixed.
Definition Logging.h:196
This file offers overloads of toIce() and fromIce() functions for STL container types.
IceUtil::Handle< class PropertyDefinitionContainer > PropertyDefinitionsPtr
PropertyDefinitions smart pointer type.
visionx::imrec::Playback newPlayback(const std::filesystem::path &path)
Instantiates and returns a new playback strategy which is capable of replaying the file or collection...
std::shared_ptr< visionx::imrec::AbstractPlaybackStrategy > Playback
Convenience alias for an instance of any playback strategy.
CByteImage::ImageType convert(const ImageType visionxImageType)
Converts a VisionX image type into an image type of IVT's ByteImage.
ArmarX headers.