// Copyright (c) 2012-2017 VideoStitch SAS // Copyright (c) 2018 stitchEm #include #include #include #include #include "version.hpp" #include #include #include #include namespace { #ifdef _MSC_VER #include #include #include const char dirSep = '\\'; #else #include const char dirSep = '/'; #endif void extractBasename(const std::string& infn, std::string& outfn) { for (int i = (int)infn.size() - 1; i > 0; --i) { if (infn[i] == dirSep) { // Something like: "toto.tata/titi" (no extension) outfn = infn; break; } else if (infn[i] == '.') { outfn = infn.substr(0, i); break; } } } void extractPath(const std::string& infn, std::string& outfn) { for (int i = (int)infn.size() - 1; i > 0; --i) { if (infn[i] == dirSep) { outfn = infn.substr(0, i + 1); return; } } outfn = ""; } void split(const char* str, char delim, std::vector* res) { const char* lastP = str; for (const char* p = str; *p != '\0'; ++p) { if (*p == delim) { res->push_back(std::string(lastP, p - lastP)); lastP = p + 1; } } res->push_back(std::string(lastP)); } std::string detectExtractImagesPattern(const char* filename) { std::string fn(filename); if (fn.find('-') == std::string::npos) // at least one dash return ""; std::vector lFn; split(filename, '.', &lFn); //"source/A", "mp4-10", "jpg" if (lFn.size() < 3) return ""; if (lFn[lFn.size() - 2].find('-') == std::string::npos) // at least one dash return ""; std::vector lFnExt; split(lFn[lFn.size() - 2].c_str(), '-', &lFnExt); //"source/A", {"mp4", "10"}, "jpg" if (lFnExt.size() != 2) return ""; std::string frmNum = lFnExt[lFnExt.size() - 1]; //"10" int res; if (!(std::stringstream(frmNum) >> res)) // sanity check: is a number return ""; lFn.pop_back(); // remove "jpg" lFn.pop_back(); // remove "mp4-10" lFn.push_back(lFnExt[0]); // add "mp4" std::string srcFn; //"source/A.mp4" for (size_t i = 0; i < lFn.size(); ++i) { srcFn += (i + 1 < lFn.size()) ? lFn[i] + "." : lFn[i]; } return srcFn; } void printUsage(const char* execName, VideoStitch::ThreadSafeOstream& os) { os << "Usage: " << execName << " [options] -i [output.ptv]" << std::endl; os << "Options are:" << std::endl; os << " -t : Set the output type to (default 'mp4')." << std::endl; os << " -v : Log level: quiet, error, warning, info (default), verbose, debug." << std::endl; os << "If output is not specified, the same name as input is used, with a 'ptv' extension." << std::endl; } } // namespace using VideoStitch::Logger; int main(int argc, char** argv) { if (argc == 2 && !strcmp(argv[1], "--version")) { std::cout << "VideoStitch calibration import tool. Copyright (c) 2018 stitchEm" << std::endl; std::cout << "library version: " << LIB_VIDEOSTITCH_VERSION << std::endl; return 0; } Logger::readLevelFromArgv(argc, argv); std::string infn; std::string outfn; for (int i = 1; i < argc; ++i) { if (argv[i][0] != '\0' && argv[i][1] != '\0' && argv[i][0] == '-') { switch (argv[i][1]) { case 'i': if (i >= argc - 1 || argv[i + 1][0] == '-') { Logger::get(Logger::Error) << "The -i option takes at least one parameter." << std::endl; printUsage(argv[0], Logger::get(Logger::Error)); return 1; } if (!infn.empty()) { Logger::get(Logger::Error) << "Several input files: \"" << infn << "\" and \"" << argv[i + 1] << "\"" << std::endl; printUsage(argv[0], Logger::get(Logger::Error)); return 1; } infn = argv[++i]; break; case 't': if (i >= argc - 1 || argv[i + 1][0] == '-') { Logger::get(Logger::Error) << "The -t option takes a parameter." << std::endl; printUsage(argv[0], Logger::get(Logger::Error)); return 1; } break; default: printUsage(argv[0], Logger::get(Logger::Error)); return 1; } } else if (outfn.empty()) { outfn = argv[i]; } else { printUsage(argv[0], Logger::get(Logger::Error)); return 1; } } if (infn.empty()) { Logger::get(Logger::Error) << "Error: no input file." << std::endl; printUsage(argv[0], Logger::get(Logger::Error)); return 1; } if (outfn.empty()) { extractBasename(infn, outfn); outfn.append(".ptv"); } VideoStitch::Potential pano(VideoStitch::Core::PanoDefinition::parseFromPto(infn)); if (!pano.ok()) { return 1; } // find if the input file pattern matches with an 'extractimages' extracted file name. // pattern: source/A.mp4, 10th frame -> source/A.mp4-10.jpg for (videoreaderid_t i = 0; i < pano->numVideoInputs(); ++i) { VideoStitch::Core::InputDefinition& iDef = pano->getVideoInput(i); std::string srcBaseFn = ""; if (iDef.getReaderConfig().getType() == VideoStitch::Ptv::Value::STRING) { srcBaseFn = detectExtractImagesPattern(iDef.getReaderConfig().asString().c_str()); } std::string srcFullFn; extractPath(outfn, srcFullFn); srcFullFn.append(srcBaseFn); if (std::ifstream(srcFullFn)) { iDef.setFilename(srcBaseFn); } } std::stringstream error; if (!pano->validate(error)) { Logger::get(Logger::Error) << error.str(); return 1; } std::ofstream ofs(outfn.c_str(), std::ios_base::out); if (!ofs.is_open()) { Logger::get(Logger::Error) << "Error: cannot open '" << outfn << "' for writing." << std::endl; return 1; } Logger::get(Logger::Info) << "Boostrapping..." << std::endl; VideoStitch::Ptv::Value* root = pano.release()->serialize(); root->printJson(ofs); delete root; }