25#define FALLBACK_FPS 30
37#include <boost/algorithm/string/predicate.hpp>
38#include <boost/lexical_cast.hpp>
39#include <boost/tokenizer.hpp>
42#include <opencv2/core/core.hpp>
43#include <opencv2/highgui/highgui.hpp>
44#include <opencv2/imgproc/imgproc.hpp>
52 this->playingBack =
false;
53 this->currentFrame = 0;
55 this->metadataPath =
"/dev/null";
59 const std::filesystem::path& filePath)
61 this->playingBack =
false;
62 this->currentFrame = 0;
76 return this->playingBack;
88 return this->frameCount;
94 return this->frameHeight;
100 return this->frameWidth;
106 this->currentFrame = frame;
112 return this->currentFrame;
118 return this->currentFrame < this->frameCount;
122getMetadataVersion(std::map<std::string, std::tuple<std::string, std::string>> metadata)
124 if (
auto it = metadata.find(
"metadata_version"); it != metadata.end())
127 return std::get<1>(it->second);
137 const std::filesystem::path& filePath)
140 if (std::filesystem::is_directory(filePath))
142 this->metadataPath = std::filesystem::canonical(filePath);
146 this->metadataPath = std::filesystem::canonical(filePath.parent_path());
150 this->initMetadata();
154 this->initExtension();
155 this->initFramesPerChunk();
156 this->initFrameCount();
157 this->initFrameHeight();
158 this->initFrameWidth();
161 std::stringstream errors;
162 for (
unsigned int currentFrameNumber = 0; currentFrameNumber < this->frameCount;
163 ++currentFrameNumber)
165 const unsigned int currentChunkNumber = currentFrameNumber / framesPerChunk;
166 const std::filesystem::path currentChunk(
"chunk_" + std::to_string(currentChunkNumber));
168 const std::string metadataVersion = getMetadataVersion(metadata);
169 std::filesystem::path frameFile =
"";
170 if (metadataVersion ==
"1")
172 frameFile = std::filesystem::path(
"frame_" + std::to_string(currentFrameNumber));
176 const std::string frameName =
"frame_" + std::to_string(currentFrameNumber);
177 if (
auto it = this->metadata.find(frameName); it != this->metadata.end())
179 frameFile = std::filesystem::path(std::get<1>(it->second));
183 errors <<
"\nFilename for frame " << currentFrameNumber <<
" in chunk "
184 << currentChunkNumber <<
" is missing!";
187 if (not frameFile.empty())
189 frameFile = frameFile.replace_extension(this->extension);
190 const std::filesystem::path currentFramePath =
191 std::filesystem::canonical(this->metadataPath / currentChunk / frameFile);
193 this->framePaths.push_back(currentFramePath);
196 if (not errors.str().empty())
198 ARMARX_ERROR <<
"The following errors occured when starting the playback: " << errors.str();
201 this->playingBack =
true;
208 if (this->currentFrame >= this->frameCount)
218 cv::cvtColor(frame, frame, cv::COLOR_BGR2RGB);
221 this->frameHeight * this->frameWidth *
static_cast<unsigned int>(frame.channels()));
236 const std::string path = this->framePaths[this->currentFrame++].string();
237 buffer = cv::imread(path);
240 if (buffer.data ==
nullptr)
246 if (
static_cast<unsigned int>(buffer.size().height) != this->frameHeight or
247 static_cast<unsigned int>(buffer.size().width) != this->frameWidth)
258 this->playingBack =
false;
262visionx::imrec::strats::ChunkedImageSequencePlaybackStrategy::initMetadata()
265 std::ifstream metadatacsv((this->metadataPath /
"metadata.csv").
string());
267 while (std::getline(metadatacsv, line))
269 boost::tokenizer<boost::escaped_list_separator<char>> tokenizer(line);
270 std::vector<std::string> tokens(tokenizer.begin(), tokenizer.end());
272 const std::string varName = tokens[0];
273 const std::string varType = tokens[1];
274 const std::string varValue = tokens[2];
276 this->metadata[varName] = std::make_tuple(varType, varValue);
281visionx::imrec::strats::ChunkedImageSequencePlaybackStrategy::initFps()
283 unsigned int candidate = 0;
285 if (
auto it = this->metadata.find(
"fps"); it != this->metadata.end())
287 candidate = this->fps = boost::lexical_cast<unsigned int>(std::get<1>(it->second));
296 <<
"FPS cannot be derived from an image sequence and are expected to be "
297 "manifested in the metadata.csv file. "
298 <<
"If the assumed value does not reflect the actual FPS, update the 'fps' "
299 "variable in the metadata.csv";
302 this->updateMetadata(
"fps",
"unsigned int", std::to_string(candidate));
305 this->fps = candidate;
309visionx::imrec::strats::ChunkedImageSequencePlaybackStrategy::initExtension()
311 std::string candidate =
"";
313 if (
auto it = this->metadata.find(
"extension"); it != this->metadata.end())
315 candidate = std::get<1>(it->second);
322 <<
"No 'extension' entry found in the metadata.csv. Trying to derive the value now...";
324 const std::filesystem::path probeChunk = this->metadataPath /
"chunk_0";
326 for (
const std::filesystem::directory_entry& de :
327 boost::make_iterator_range(std::filesystem::directory_iterator(probeChunk), {}))
329 const std::string currentStem = de.path().stem().string();
330 const std::string currentExtension = de.path().extension().string();
331 const std::string frameTemplate =
"frame_";
333 if (boost::algorithm::starts_with(currentStem, frameTemplate) and
334 boost::algorithm::starts_with(currentExtension,
".") and currentExtension !=
".")
336 candidate = currentExtension;
342 <<
"Could not determine the file extension. Update the metadata.csv file manually for "
344 "'extension' (type 'string') with the corresponding value";
346 ARMARX_INFO <<
"Determined 'extension' to be '" << candidate <<
"'";
348 this->updateMetadata(
"extension",
"string", candidate);
351 this->extension = candidate;
355visionx::imrec::strats::ChunkedImageSequencePlaybackStrategy::initFramesPerChunk()
357 unsigned int candidate = 0;
359 if (
auto it = this->metadata.find(
"frames_per_chunk"); it != this->metadata.end())
361 candidate = boost::lexical_cast<unsigned int>(std::get<1>(it->second));
367 ARMARX_WARNING <<
"No 'frames_per_chunk' entry found in the metadata.csv. Trying to derive "
371 std::filesystem::path file =
372 std::filesystem::path(
"frame_0").replace_extension(this->extension);
373 while (std::filesystem::exists(this->metadataPath /
"chunk_0" / file))
375 file = std::filesystem::path(
"frame_" + std::to_string(++candidate))
376 .replace_extension(this->extension);
380 <<
"Could not determine the amount of frames per chunk. Update the metadata.csv file "
381 "manually for the variable "
382 "'frames_per_chunk' (type 'unsigned int') with the corresponding value.";
384 ARMARX_INFO <<
"Determined 'frames_per_chunk' to be '" << candidate <<
"'";
386 this->updateMetadata(
"frames_per_chunk",
"unsigned int", std::to_string(candidate));
389 this->framesPerChunk = candidate;
393visionx::imrec::strats::ChunkedImageSequencePlaybackStrategy::initFrameCount()
395 unsigned int candidate = 0;
397 if (
auto it = this->metadata.find(
"frame_count"); it != this->metadata.end())
399 candidate = boost::lexical_cast<unsigned int>(std::get<1>(it->second));
405 ARMARX_WARNING <<
"No 'frame_count' entry found in the metadata.csv. Trying to derive the "
409 std::filesystem::path chunk =
"chunk_0";
410 unsigned int chunkNumber = 0;
411 std::filesystem::path file =
412 std::filesystem::path(
"frame_0").replace_extension(this->extension);
413 while (std::filesystem::exists(this->metadataPath / chunk))
415 while (std::filesystem::exists(this->metadataPath / chunk / file))
417 file = std::filesystem::path(
"frame_" + std::to_string(++candidate))
418 .replace_extension(this->extension);
421 chunk =
"chunk_" + std::to_string(++chunkNumber);
425 <<
"Could not determine the amount of frames. Update the metadata.csv file manually "
427 "'frame_count' (type 'unsigned int') with the corresponding value";
429 ARMARX_INFO <<
"Determined 'frame_count' to be '" << candidate <<
"'";
431 this->updateMetadata(
"frame_count",
"unsigned int", std::to_string(candidate));
434 this->frameCount = candidate;
438visionx::imrec::strats::ChunkedImageSequencePlaybackStrategy::initFrameHeight()
440 unsigned int candidate = 0;
442 if (
auto it = this->metadata.find(
"frame_height"); it != this->metadata.end())
444 candidate = boost::lexical_cast<unsigned int>(std::get<1>(it->second));
450 ARMARX_WARNING <<
"No 'frame_height' entry found in the metadata.csv. Trying to derive the "
453 const std::filesystem::path chunk =
"chunk_0";
454 const std::filesystem::path frame =
455 std::filesystem::path(
"frame_0").replace_extension(this->extension);
456 const std::filesystem::path probe = this->metadataPath / chunk / frame;
457 cv::Mat frameImage = cv::imread(probe.string());
460 <<
"Failed loading first frame. Image file corrupted?";
462 candidate =
static_cast<unsigned int>(frameImage.size().height);
465 <<
"Could not determine the frame height. Update the metadata.csv file manually for "
467 "'frame_height' (type 'unsigned int') with the corresponding value";
469 ARMARX_INFO <<
"Determined 'frame_height' to be '" << candidate <<
"'";
471 this->updateMetadata(
"frame_height",
"unsigned int", std::to_string(candidate));
474 this->frameHeight = candidate;
478visionx::imrec::strats::ChunkedImageSequencePlaybackStrategy::initFrameWidth()
480 unsigned int candidate = 0;
482 if (
auto it = this->metadata.find(
"frame_width"); it != this->metadata.end())
484 candidate = boost::lexical_cast<unsigned int>(std::get<1>(it->second));
490 ARMARX_WARNING <<
"No 'frame_width' entry found in the metadata.csv. Trying to derive the "
493 const std::filesystem::path chunk =
"chunk_0";
494 const std::filesystem::path frame =
495 std::filesystem::path(
"frame_0").replace_extension(this->extension);
496 const std::filesystem::path probe = this->metadataPath / chunk / frame;
497 cv::Mat frameImage = cv::imread(probe.string());
500 <<
"Failed loading first frame. Image file corrupted?";
502 candidate =
static_cast<unsigned int>(frameImage.size().width);
505 <<
"Could not determine the frame width. Update the metadata.csv file manually for the "
507 "'frame_width' (type 'unsigned int') with the corresponding value";
509 ARMARX_INFO <<
"Determined 'frame_width' to be '" << candidate <<
"'";
511 this->updateMetadata(
"frame_width",
"unsigned int", std::to_string(candidate));
514 this->frameWidth = candidate;
518visionx::imrec::strats::ChunkedImageSequencePlaybackStrategy::updateMetadata(
519 const std::string& varName,
520 const std::string& varType,
521 const std::string& varValue)
523 if (this->metadata.count(varName) == 0)
525 ARMARX_INFO <<
"Updating metadata.csv file with missing variable '" << varName
526 <<
"' (type '" << varType <<
"') with value '" << varValue <<
"'";
528 std::ofstream metadatacsv((this->metadataPath /
"metadata.csv").
string(),
529 std::ios::out | std::ios::app);
530 metadatacsv << varName <<
"," << varType <<
"," << varValue <<
"\n";
virtual void setCurrentFrame(unsigned int frame) override
Sets the frame from there the playback should resume afterwards (seek)
virtual void stopPlayback() override
Stops the playback.
virtual unsigned int getFrameWidth() const override
Gets the width of a frame in pixel.
virtual unsigned int getFps() const override
Gets the amount of frames per second of the recording.
virtual ~ChunkedImageSequencePlaybackStrategy() override
Destructor.
virtual bool hasNextFrame() const override
Indicates whether the recording has a consecutive frame.
virtual unsigned int getFrameCount() const override
Gets the total amout of frames in the recording.
ChunkedImageSequencePlaybackStrategy()
Default constructor to manually setup later.
virtual unsigned int getCurrentFrame() const override
Gets the current frame index of the playback.
virtual bool getNextFrame(void *buffer) override
Writes the next frame into a buffer of any form (RGB)
virtual void startPlayback(const std::filesystem::path &filePath) override
Starts the playback.
virtual bool isPlayingBack() const override
Indicates whether the instance is configured to be able to play back.
virtual unsigned int getFrameHeight() const override
Gets the height of a frame in pixel.
#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_NOT_EQUAL(lhs, rhs)
This macro evaluates whether lhs is inequal (!=) rhs and if it turns out to be false it will throw an...
#define ARMARX_CHECK_NOT_NULL(ptr)
This macro evaluates whether ptr is not null and if it turns out to be false it will throw an Express...
#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.
#define ARMARX_ERROR
The logging level for unexpected behaviour, that must be fixed.
#define ARMARX_WARNING
The logging level for unexpected behaviour, but not a serious problem.