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
26
27
28// ArmarX
29#include <filesystem>
30#include <functional>
31#include <string>
32#include <tuple>
33#include <vector>
34
35#include <SimoxUtility/algorithm/string/string_tools.h>
36
40
44
45using namespace armarx;
46using namespace visionx;
47using namespace visionx::components;
51
56
61
62namespace fs = std::filesystem;
63
64void
66{
67 getProperty(m_stereo_reference_frame, "stereo.reference_frame");
68 getProperty(m_stereo_calibration_file, "stereo.calibration_file");
69
70 fs::path basePath = getProperty<std::string>("base_path").getValue();
71 std::string recordingFilesStr = getProperty<std::string>("recording_files").getValue();
72 std::vector<std::string> recordingFiles = simox::alg::split(recordingFilesStr, ";");
73
75 {
76 if (recordingFiles.size() != 2)
77 {
78 throw LocalException() << "Stereo calibration file and/or reference frame were set, "
79 << "but there are " << recordingFiles.size() << " recording "
80 << "files (expected 2).";
81 }
82
83 init_stereo_calibration_file();
84 init_stereo_calibration();
85 }
86
87 auto parse_fps = [](const std::string& setting, double source_fps) -> std::tuple<double, double>
88 {
89 // This lambda assumes that all operations are safe to call, as fps_setting was checked
90 // against a RegEx while configuration, and does therefore not perform any sanity checks
91
92 const unsigned long int prefix_length = std::string("sourceX").size();
93 std::string setting_value;
94 std::function<double(double, double)> operation;
95
96 // Source FPS
97 if (setting == "source")
98 {
99 return std::make_tuple(source_fps, 1.);
100 }
101 // Source FPS multiplied by value
102 else if (simox::alg::starts_with(setting, "source*"))
103 {
104 // Multiplication
105 operation = [](double x, double y) -> double { return x * y; };
106 setting_value = setting.substr(prefix_length);
107 }
108 // Source FPS divided by value
109 else if (simox::alg::starts_with(setting, "source/"))
110 {
111 // Division
112 operation = [](double x, double y) -> double { return x / y; };
113 setting_value = setting.substr(prefix_length);
114 }
115 // Source FPS is ignored and the value is assumed to be source FPS
116 else if (simox::alg::starts_with(setting, "source="))
117 {
118 return std::make_tuple(std::stod(setting.substr(prefix_length)), 1.);
119 }
120 // Set FPS to a specific value
121 else
122 {
123 // Identity to keep the user defined setting regardless of source FPS
124 operation = [](double, double fps) -> double { return fps; };
125 setting_value = setting;
126 }
127
128 const double derived_fps = operation(source_fps, std::stod(setting_value));
129 const double playback_speed_normalisation = source_fps / derived_fps;
130
131 return std::make_tuple(derived_fps, playback_speed_normalisation);
132 };
133
134 unsigned int refFrameHeight = 0;
135 unsigned int refFrameWidth = 0;
136 unsigned int actual_source_fps = 0;
137
138 // Instantiate playbacks
139 for (std::string const& recordingFile : recordingFiles)
140 {
141 fs::path fullPath = basePath != "" ? basePath / recordingFile : fs::path(recordingFile);
143 m_playbacks.push_back(playback);
144
145 if (not playback)
146 {
147 ARMARX_ERROR << "Could not find a playback strategy for '" << fullPath.string() << "'";
148 continue;
149 }
150
151 // Sanity checks for image heights and widths
152 {
153 // If uninitialised, use first playback as reference
154 if (refFrameHeight == 0 and refFrameWidth == 0)
155 {
156 refFrameHeight = playback->getFrameHeight();
157 refFrameWidth = playback->getFrameWidth();
158 }
159
160 // Check that neither hight nor width are 0 pixels
161 ARMARX_CHECK_GREATER(playback->getFrameHeight(), 0)
162 << "Image source frame height cannot be 0 pixel";
163 ARMARX_CHECK_GREATER(playback->getFrameWidth(), 0)
164 << "Image source frame width cannot be 0 pixel";
165
166 // Check that the frame heights and widths from all image sources are equal
167 ARMARX_CHECK_EQUAL(playback->getFrameHeight(), refFrameHeight)
168 << "Image source frames must have the same dimensions";
169 ARMARX_CHECK_EQUAL(playback->getFrameWidth(), refFrameWidth)
170 << "Image source frames must have the same dimensions";
171 }
172
173 // Determine playback with the highest FPS
174 if (playback->getFps() > actual_source_fps)
175 {
176 actual_source_fps = playback->getFps();
177 }
178 }
179
180 // Figure out FPS related variables base FPS, normalisations, multiplier
181 double playback_speed_normalisation;
182 std::tie(frameRate, playback_speed_normalisation) =
183 parse_fps(getProperty<std::string>("fps"), actual_source_fps);
185 playback_speed_normalisation * getProperty<double>("playback_speed_multiplier");
186
187 ARMARX_INFO << "Playback at " << frameRate << " FPS, advancing " << m_frame_advance
188 << " per loop";
189
190 // Set up required properties for the image capturer
191 setNumberImages(static_cast<int>(m_playbacks.size()));
193 ImageDimension(static_cast<int>(refFrameWidth), static_cast<int>(refFrameHeight)),
194 eRgb,
195 eBayerPatternGr);
196 setImageSyncMode(eFpsSynchronization);
197}
198
199void
201{
203 {
204 playback->stopPlayback();
205 }
206
207 m_playbacks.clear();
208}
209
210void
212{
213 // pass
214}
215
216void
221
222bool
224{
225 const bool loop = getProperty<bool>("loop");
226 bool success = false; // Result is true if at least one of the playbacks provided a picture
227
228 for (unsigned int i = 0; i < static_cast<unsigned int>(getNumberImages()); ++i)
229 {
231
232 // If the last frame is reached and loop is enabled, rewind
233 if (not playback->hasNextFrame() and loop)
234 {
235 m_current_frame = 0;
236 }
237
238 const unsigned int current_frame = static_cast<unsigned int>(std::round(m_current_frame));
239 const unsigned int min_frame = 0;
240 const unsigned int max_frame = playback->getFrameCount() - 1;
241 unsigned int frame_index = std::clamp(current_frame, min_frame, max_frame);
242 playback->setCurrentFrame(frame_index);
243 success |= playback->getNextFrame(imageBuffer[i]);
244 }
245
247
249 if (success)
250 {
251 updateTimestamp(IceUtil::Time::microSeconds(time.toMicroSecondsSinceEpoch()));
252 }
253
254 // Update working memory.
255 const bool memoryEnabled = getProperty<bool>("memory.enable");
256 if (memoryEnabled and imageBuffer != nullptr)
257 {
258 getImagesServerPlugin().commitImages(imageBuffer, time);
259 }
260
261 return success;
262}
263
264StereoCalibration
266{
267 assert_stereo_initialized();
269}
270
271bool
273{
274 assert_stereo_initialized();
275 return false;
276}
277
278std::string
280{
281 assert_stereo_initialized();
283}
284
285std::string
287{
288 return "PlaybackImageProvider";
289}
290
293{
295
296 defs->defineRequiredProperty<std::string>(
297 "recording_files",
298 "List of recording files, separated by semicolons `;` for each channel. For video files, "
299 "use the filename, for image sequences, use the folder name where the image sequence is "
300 "located or any frame as pattern.\n"
301 "Wildcards (e.g. `frame_left_*.jpg;frame_right_*.jpg`) are supported as well.\n"
302 "Files will be interpreted as absolute paths, or relative paths to the current working "
303 "directory if base_path is not set. If base_path is set, all paths in recording_files are "
304 "interpreted relative to base_path");
305 defs->defineOptionalProperty<std::string>(
306 "base_path",
307 "",
308 "Common base path of recording_files (will be prepened if set). To unset, use \"\"");
309 defs->defineOptionalProperty<double>("playback_speed_multiplier",
310 1.0,
311 "Adjust the playback speed with this multiplier, e.g. `2` "
312 "= double playback speed, `0.5` = "
313 "half playback speed")
314 .setMin(0);
315 defs->defineOptionalProperty<std::string>(
316 "fps",
317 "source",
318 "Lock the FPS to an absolute value, or a value relative to source FPS. Valid inputs:\n"
319 " 1) `source` => Derive FPS from source\n"
320 " 2) `source*<X>`, with <X> being a positive decimal or integer => Playback with FPS "
321 "= "
322 "source FPS multiplied by <X>\n"
323 " 3) `source/<X>`, with <X> being a positive decimal or integer => Playback with FPS "
324 "= "
325 "source FPS devided by <X>\n"
326 " 4) `<X>`, with <X> being a positive decimal or integer => Playback with FPS at <X>\n"
327 " 5) `source=<X>`, with <X> being a positive decimal or integer => Playback with FPS "
328 "at "
329 "<X>, ignoring source FPS completely (Assume that <X> is source FPS)\n"
330 "With the exception of 5), all settings only have direct effect of the FPS the image "
331 "provider delivers the frames, but not on the playback speed. "
332 "Use `playback_speed_multiplier` to adjust that.\n"
333 "5) is only useful if the metadata of the recording is incomplete or incorrect (for "
334 "example when replaying generic image sequences where the FPS cannot be derived)")
335 .setMatchRegex(
336 R"(source(\/|\*|=)(\d+(\.\d*)?|\.\d+)|(\d+(\.\d*)?|\.\d+)|(\d+(\.\d*)?|\.\d+)|source)");
337 defs->defineOptionalProperty<bool>(
338 "loop", true, "Whether the playback should restart after the last frame was provided");
339
340 // Stereo functionality
341 defs->defineOptionalProperty<std::string>(
342 "stereo.calibration_file",
343 "",
344 "Path to a stereo calibration file that should additionally be provided.");
345 defs->defineOptionalProperty<std::string>(
346 "stereo.reference_frame",
347 "",
348 "Path to a stereo calibration file that should additionally be provided.");
349
350
351 // Working Memory
352 defs->defineOptionalProperty<bool>(
353 "memory.enable", true, "If true, also provide the images as a memory server.");
354
355 return defs;
356}
357
358void
359PlaybackImageProvider::init_stereo_calibration_file()
360{
363
364 if (not success)
365 {
367 }
368}
369
370void
371PlaybackImageProvider::init_stereo_calibration()
372{
373 const bool transform_left_camera_to_identity = true;
374 const bool loading_calibration_sucessful = m_stereo_calibration_ivt.LoadCameraParameters(
375 m_stereo_calibration_file.c_str(), transform_left_camera_to_identity);
376
377 if (not loading_calibration_sucessful)
378 {
380 }
381
384}
385
386void
387PlaybackImageProvider::assert_stereo_initialized()
388{
389 if (not m_stereo_initialized)
390 {
391 throw LocalException() << "Stereo calibration API was called, but the calibration of this "
392 << "PlaybackProvider (instance name '" << getName() << "') was "
393 << "never initialized.\n"
394 << "Provide this PlaybackProvider instance with a calibration file "
395 << "using the 'stereo.calibration_file' property.";
396 }
397}
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.