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>
53 this->playingBack =
false;
54 this->currentFrame = 0;
56 this->metadataPath =
"/dev/null";
62 this->playingBack =
false;
63 this->currentFrame = 0;
65 this->startPlayback(filePath);
78 return this->playingBack;
92 return this->frameCount;
99 return this->frameHeight;
106 return this->frameWidth;
113 this->currentFrame = frame;
120 return this->currentFrame;
127 return this->currentFrame < this->frameCount;
132 getMetadataVersion(std::map<std::string, std::tuple<std::string, std::string>> metadata)
134 if (
auto it = metadata.find(
"metadata_version"); it != metadata.end())
137 return std::get<1>(it->second);
150 if (std::filesystem::is_directory(filePath))
152 this->metadataPath = std::filesystem::canonical(filePath);
156 this->metadataPath = std::filesystem::canonical(filePath.parent_path());
160 this->initMetadata();
164 this->initExtension();
165 this->initFramesPerChunk();
166 this->initFrameCount();
167 this->initFrameHeight();
168 this->initFrameWidth();
171 std::stringstream errors;
172 for (
unsigned int currentFrameNumber = 0; currentFrameNumber < this->frameCount; ++currentFrameNumber)
174 const unsigned int currentChunkNumber = currentFrameNumber / framesPerChunk;
175 const std::filesystem::path currentChunk(
"chunk_" +
std::to_string(currentChunkNumber));
177 const std::string metadataVersion = getMetadataVersion(metadata);
178 std::filesystem::path frameFile =
"";
179 if (metadataVersion ==
"1")
181 frameFile = std::filesystem::path(
"frame_" +
std::to_string(currentFrameNumber));
185 const std::string frameName =
"frame_" +
std::to_string(currentFrameNumber);
186 if (
auto it = this->metadata.find(frameName); it != this->metadata.end())
188 frameFile = std::filesystem::path(std::get<1>(it->second));
192 errors <<
"\nFilename for frame " << currentFrameNumber <<
" in chunk " << currentChunkNumber <<
" is missing!";
195 if (not frameFile.empty())
197 frameFile = frameFile.replace_extension(this->extension);
198 const std::filesystem::path currentFramePath =
199 std::filesystem::canonical(this->metadataPath / currentChunk / frameFile);
201 this->framePaths.push_back(currentFramePath);
204 if (not errors.str().empty())
206 ARMARX_ERROR <<
"The following errors occured when starting the playback: " << errors.str();
209 this->playingBack =
true;
217 if (this->currentFrame >= this->frameCount)
224 this->getNextFrame(frame);
227 cv::cvtColor(frame, frame, cv::COLOR_BGR2RGB);
228 std::memcpy(buffer, frame.data, this->frameHeight * this->frameWidth *
static_cast<unsigned int>(frame.channels()));
238 return this->getNextFrame(buffer.pixels);
245 const std::string path = this->framePaths[this->currentFrame++].string();
246 buffer = cv::imread(path);
249 if (buffer.data ==
nullptr)
255 if (
static_cast<unsigned int>(buffer.size().height) != this->frameHeight
256 or
static_cast<unsigned int>(buffer.size().width) != this->frameWidth)
268 this->playingBack =
false;
273 visionx::imrec::strats::ChunkedImageSequencePlaybackStrategy::initMetadata()
276 std::ifstream metadatacsv((this->metadataPath /
"metadata.csv").
string());
278 while (std::getline(metadatacsv, line))
280 boost::tokenizer<boost::escaped_list_separator<char>> tokenizer(line);
281 std::vector<std::string> tokens(tokenizer.begin(), tokenizer.end());
283 const std::string varName = tokens[0];
284 const std::string varType = tokens[1];
285 const std::string varValue = tokens[2];
287 this->metadata[varName] = std::make_tuple(varType, varValue);
293 visionx::imrec::strats::ChunkedImageSequencePlaybackStrategy::initFps()
295 unsigned int candidate = 0;
297 if (
auto it = this->metadata.find(
"fps"); it != this->metadata.end())
299 candidate = this->fps = boost::lexical_cast<unsigned int>(std::get<1>(it->second));
307 <<
"FPS cannot be derived from an image sequence and are expected to be manifested in the metadata.csv file. "
308 <<
"If the assumed value does not reflect the actual FPS, update the 'fps' variable in the metadata.csv";
311 this->updateMetadata(
"fps",
"unsigned int",
std::to_string(candidate));
314 this->fps = candidate;
319 visionx::imrec::strats::ChunkedImageSequencePlaybackStrategy::initExtension()
321 std::string candidate =
"";
323 if (
auto it = this->metadata.find(
"extension"); it != this->metadata.end())
325 candidate = std::get<1>(it->second);
331 ARMARX_WARNING <<
"No 'extension' entry found in the metadata.csv. Trying to derive the value now...";
333 const std::filesystem::path probeChunk = this->metadataPath /
"chunk_0";
335 for (
const std::filesystem::directory_entry& de : boost::make_iterator_range(std::filesystem::directory_iterator(probeChunk), {}))
337 const std::string currentStem = de.path().stem().string();
338 const std::string currentExtension = de.path().extension().string();
339 const std::string frameTemplate =
"frame_";
343 candidate = currentExtension;
348 ARMARX_CHECK_NOT_EQUAL(candidate,
"") <<
"Could not determine the file extension. Update the metadata.csv file manually for the variable "
349 "'extension' (type 'string') with the corresponding value";
351 ARMARX_INFO <<
"Determined 'extension' to be '" << candidate <<
"'";
353 this->updateMetadata(
"extension",
"string", candidate);
356 this->extension = candidate;
361 visionx::imrec::strats::ChunkedImageSequencePlaybackStrategy::initFramesPerChunk()
363 unsigned int candidate = 0;
365 if (
auto it = this->metadata.find(
"frames_per_chunk"); it != this->metadata.end())
367 candidate = boost::lexical_cast<unsigned int>(std::get<1>(it->second));
373 ARMARX_WARNING <<
"No 'frames_per_chunk' entry found in the metadata.csv. Trying to derive the value now...";
376 std::filesystem::path file = std::filesystem::path(
"frame_0").replace_extension(this->extension);
377 while (std::filesystem::exists(this->metadataPath /
"chunk_0" / file))
379 file = std::filesystem::path(
"frame_" +
std::to_string(++candidate)).replace_extension(this->extension);
382 ARMARX_CHECK_GREATER(candidate, 0) <<
"Could not determine the amount of frames per chunk. Update the metadata.csv file manually for the variable "
383 "'frames_per_chunk' (type 'unsigned int') with the corresponding value.";
385 ARMARX_INFO <<
"Determined 'frames_per_chunk' to be '" << candidate <<
"'";
387 this->updateMetadata(
"frames_per_chunk",
"unsigned int",
std::to_string(candidate));
390 this->framesPerChunk = candidate;
395 visionx::imrec::strats::ChunkedImageSequencePlaybackStrategy::initFrameCount()
397 unsigned int candidate = 0;
399 if (
auto it = this->metadata.find(
"frame_count"); it != this->metadata.end())
401 candidate = boost::lexical_cast<unsigned int>(std::get<1>(it->second));
407 ARMARX_WARNING <<
"No 'frame_count' entry found in the metadata.csv. Trying to derive the value now...";
410 std::filesystem::path chunk =
"chunk_0";
411 unsigned int chunkNumber = 0;
412 std::filesystem::path file = 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)).replace_extension(this->extension);
423 ARMARX_CHECK_GREATER(candidate, 0) <<
"Could not determine the amount of frames. Update the metadata.csv file manually for the variable "
424 "'frame_count' (type 'unsigned int') with the corresponding value";
426 ARMARX_INFO <<
"Determined 'frame_count' to be '" << candidate <<
"'";
428 this->updateMetadata(
"frame_count",
"unsigned int",
std::to_string(candidate));
431 this->frameCount = candidate;
436 visionx::imrec::strats::ChunkedImageSequencePlaybackStrategy::initFrameHeight()
438 unsigned int candidate = 0;
440 if (
auto it = this->metadata.find(
"frame_height"); it != this->metadata.end())
442 candidate = boost::lexical_cast<unsigned int>(std::get<1>(it->second));
448 ARMARX_WARNING <<
"No 'frame_height' entry found in the metadata.csv. Trying to derive the value now...";
450 const std::filesystem::path chunk =
"chunk_0";
451 const std::filesystem::path frame = std::filesystem::path(
"frame_0").replace_extension(this->extension);
452 const std::filesystem::path probe = this->metadataPath / chunk / frame;
453 cv::Mat frameImage = cv::imread(probe.string());
457 candidate =
static_cast<unsigned int>(frameImage.size().height);
459 ARMARX_CHECK_GREATER(candidate, 0) <<
"Could not determine the frame height. Update the metadata.csv file manually for the variable "
460 "'frame_height' (type 'unsigned int') with the corresponding value";
462 ARMARX_INFO <<
"Determined 'frame_height' to be '" << candidate <<
"'";
464 this->updateMetadata(
"frame_height",
"unsigned int",
std::to_string(candidate));
467 this->frameHeight = candidate;
472 visionx::imrec::strats::ChunkedImageSequencePlaybackStrategy::initFrameWidth()
474 unsigned int candidate = 0;
476 if (
auto it = this->metadata.find(
"frame_width"); it != this->metadata.end())
478 candidate = boost::lexical_cast<unsigned int>(std::get<1>(it->second));
484 ARMARX_WARNING <<
"No 'frame_width' entry found in the metadata.csv. Trying to derive the value now...";
486 const std::filesystem::path chunk =
"chunk_0";
487 const std::filesystem::path frame = std::filesystem::path(
"frame_0").replace_extension(this->extension);
488 const std::filesystem::path probe = this->metadataPath / chunk / frame;
489 cv::Mat frameImage = cv::imread(probe.string());
493 candidate =
static_cast<unsigned int>(frameImage.size().width);
495 ARMARX_CHECK_GREATER(candidate, 0) <<
"Could not determine the frame width. Update the metadata.csv file manually for the variable "
496 "'frame_width' (type 'unsigned int') with the corresponding value";
498 ARMARX_INFO <<
"Determined 'frame_width' to be '" << candidate <<
"'";
500 this->updateMetadata(
"frame_width",
"unsigned int",
std::to_string(candidate));
503 this->frameWidth = candidate;
508 visionx::imrec::strats::ChunkedImageSequencePlaybackStrategy::updateMetadata(
509 const std::string& varName,
const std::string& varType,
const std::string& varValue)
511 if (this->metadata.count(varName) == 0)
513 ARMARX_INFO <<
"Updating metadata.csv file with missing variable '" << varName <<
"' (type '" << varType <<
"') with value '" << varValue <<
"'";
515 std::ofstream metadatacsv((this->metadataPath /
"metadata.csv").
string(), std::ios::out | std::ios::app);
516 metadatacsv << varName <<
"," << varType <<
"," << varValue <<
"\n";