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 
45 using namespace armarx;
46 using namespace visionx;
47 using namespace visionx::components;
51 
52 PlaybackImageProvider::PlaybackImageProvider()
53 {
54  m_stereo_initialized = false;
55 }
56 
57 PlaybackImageProvider::~PlaybackImageProvider()
58 {
59  // pass
60 }
61 
62 namespace fs = std::filesystem;
63 
64 void
65 PlaybackImageProvider::onInitCapturingImageProvider()
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 
74  if (m_stereo_calibration_file != "" or m_stereo_reference_frame != "")
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);
142  Playback playback = imrec::newPlayback(fullPath);
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);
184  m_frame_advance =
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()));
192  setImageFormat(
193  ImageDimension(static_cast<int>(refFrameWidth), static_cast<int>(refFrameHeight)),
194  eRgb,
195  eBayerPatternGr);
196  setImageSyncMode(eFpsSynchronization);
197 }
198 
199 void
200 PlaybackImageProvider::onExitCapturingImageProvider()
201 {
202  for (Playback& playback : m_playbacks)
203  {
204  playback->stopPlayback();
205  }
206 
207  m_playbacks.clear();
208 }
209 
210 void
211 PlaybackImageProvider::onStartCapture(float)
212 {
213  // pass
214 }
215 
216 void
217 PlaybackImageProvider::onStopCapture()
218 {
219  // pass
220 }
221 
222 bool
223 PlaybackImageProvider::capture(void** imageBuffer)
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  {
230  Playback playback = m_playbacks[i];
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 
246  m_current_frame += m_frame_advance;
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 
264 StereoCalibration
265 PlaybackImageProvider::getStereoCalibration(const Ice::Current&)
266 {
267  assert_stereo_initialized();
268  return m_stereo_calibration;
269 }
270 
271 bool
272 PlaybackImageProvider::getImagesAreUndistorted(const Ice::Current&)
273 {
274  assert_stereo_initialized();
275  return false;
276 }
277 
278 std::string
279 PlaybackImageProvider::getReferenceFrame(const Ice::Current&)
280 {
281  assert_stereo_initialized();
282  return m_stereo_reference_frame;
283 }
284 
285 std::string
286 PlaybackImageProvider::getDefaultName() const
287 {
288  return "PlaybackImageProvider";
289 }
290 
292 PlaybackImageProvider::createPropertyDefinitions()
293 {
294  PropertyDefinitionsPtr defs{new ComponentPropertyDefinitions(getConfigIdentifier())};
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 
358 void
359 PlaybackImageProvider::init_stereo_calibration_file()
360 {
361  const bool success = armarx::ArmarXDataPath::getAbsolutePath(m_stereo_calibration_file,
362  m_stereo_calibration_file);
363 
364  if (not success)
365  {
366  throw LoadingCalibrationFileFailedException(m_stereo_calibration_file.c_str());
367  }
368 }
369 
370 void
371 PlaybackImageProvider::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  {
379  throw LoadingCalibrationFileFailedException(m_stereo_calibration_file.c_str());
380  }
381 
382  m_stereo_calibration = visionx::tools::convert(m_stereo_calibration_ivt);
383  m_stereo_initialized = true;
384 }
385 
386 void
387 PlaybackImageProvider::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 }
visionx::exceptions::local::LoadingCalibrationFileFailedException
Definition: LoadingCalibrationFileFailedException.h:33
armarx::PropertyDefinitionContainer::defineRequiredProperty
PropertyDefinition< PropertyType > & defineRequiredProperty(const std::string &name, const std::string &description="", PropertyDefinitionBase::PropertyConstness constness=PropertyDefinitionBase::eConstant)
Definition: PropertyDefinitionContainer.h:516
visionx
ArmarX headers.
Definition: OpenPoseStressTest.h:38
LoadingCalibrationFileFailedException.h
armarx::core::time::DateTime::Now
static DateTime Now()
Definition: DateTime.cpp:55
ARMARX_CHECK_GREATER
#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...
Definition: ExpressionException.h:116
StereoCameraSystemRequiredException.h
armarx::starts_with
bool starts_with(const std::string &haystack, const std::string &needle)
Definition: StringHelpers.cpp:43
clamp
double clamp(double x, double a, double b)
Definition: point.hpp:125
plugins.h
visionx::imrec::Playback
std::shared_ptr< visionx::imrec::AbstractPlaybackStrategy > Playback
Convenience alias for an instance of any playback strategy.
Definition: AbstractPlaybackStrategy.h:48
visionx::tools::convert
CByteImage::ImageType convert(const ImageType visionxImageType)
Converts a VisionX image type into an image type of IVT's ByteImage.
Definition: TypeMapping.cpp:95
ARMARX_ERROR
#define ARMARX_ERROR
Definition: Logging.h:189
armarx::core::time::DateTime::toMicroSecondsSinceEpoch
std::int64_t toMicroSecondsSinceEpoch() const
Definition: DateTime.cpp:95
ExpressionException.h
armarx::core::time::DateTime
Represents a point in time.
Definition: DateTime.h:24
armarx::ComponentPropertyDefinitions
Default component property definition container.
Definition: Component.h:70
TimeUtil.h
ARMARX_INFO
#define ARMARX_INFO
Definition: Logging.h:174
visionx::imrec::newPlayback
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...
Definition: public_api.cpp:205
IceUtil::Handle< class PropertyDefinitionContainer >
visionx::components
Definition: PlaybackImageProvider.h:40
TypeMapping.h
armarx::ArmarXDataPath::getAbsolutePath
static bool getAbsolutePath(const std::string &relativeFilename, std::string &storeAbsoluteFilename, const std::vector< std::string > &additionalSearchPaths={}, bool verbose=true)
Definition: ArmarXDataPath.cpp:111
ARMARX_CHECK_EQUAL
#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...
Definition: ExpressionException.h:130
PlaybackImageProvider.h
visionx::exceptions::local::StereoCameraSystemRequiredException
Definition: StereoCameraSystemRequiredException.h:34
ArmarXDataPath.h
armarx
This file offers overloads of toIce() and fromIce() functions for STL container types.
Definition: ArmarXTimeserver.cpp:28
armarx::status::success
@ success
armarx::split
std::vector< std::string > split(const std::string &source, const std::string &splitBy, bool trimElements=false, bool removeEmptyElements=false)
Definition: StringHelpers.cpp:36