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;
64 this->startPlayback(filePath);
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;
122 getMetadataVersion(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)
215 this->getNextFrame(frame);
218 cv::cvtColor(frame, frame, cv::COLOR_BGR2RGB);
221 this->frameHeight * this->frameWidth *
static_cast<unsigned int>(frame.channels()));
230 return this->getNextFrame(buffer.pixels);
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;
262 visionx::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);
281 visionx::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;
309 visionx::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_";
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;
355 visionx::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;
393 visionx::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);
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;
438 visionx::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;
478 visionx::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;
518 visionx::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";