From 94760a5f2b53501b2d28491e0fe0c13874f5df81 Mon Sep 17 00:00:00 2001 From: Kirill Kornyakov Date: Sun, 15 May 2011 19:25:00 +0000 Subject: [PATCH] command line parser added. Leonid Beynenson is original contributor of the class. Class will be used in samples for convenient (and unified) work with command arguments. --- modules/core/include/opencv2/core/core.hpp | 126 +++++++++++++++ modules/core/src/CommandLineParser.cpp | 172 +++++++++++++++++++++ samples/c/facedetect.cpp | 80 +++------- 3 files changed, 322 insertions(+), 56 deletions(-) create mode 100755 modules/core/src/CommandLineParser.cpp diff --git a/modules/core/include/opencv2/core/core.hpp b/modules/core/include/opencv2/core/core.hpp index 3065b2fbff..59b98642b0 100644 --- a/modules/core/include/opencv2/core/core.hpp +++ b/modules/core/include/opencv2/core/core.hpp @@ -4166,6 +4166,132 @@ protected: }; #endif +/*! + Command Line Parser + + The class is used for reading command arguments. + Supports the following syntax: + //-k 10 1 2 --x 0.001 --size 640 480 --inputFile lena.jpg + int k = parser.get("--k | -k", -1); + vector kValues=parser.getVec("--k | -k"); + double x = parser.get("--x"); + cv::Size size = parser.get("--size"); + string inputFile = parser.get("--inputFile"); +*/ +class CommandLineParser +{ +public: + //! the default constructor + CommandLineParser(int argc, const char* argv[]); + + //! allows to check if parameter is given + bool has(const std::string& keys) const; + + //! get parameter + template + _Tp get(const std::string& name) + { + return fromStringsVec<_Tp>(getVec(name)); + } + + //! get parameter with default value + template + _Tp get(const std::string& name, const _Tp& default_value) + { + if (!has(name)) + return default_value; + + return get<_Tp>(name); + } + + //! get a vector of values for specified key + template + std::vector<_Tp> getVec(const std::string& keys); + +protected: + std::map > data; + + template + static _Tp fromStringSimple(const std::string& str)//the default conversion function + { + _Tp res; + std::stringstream s1(str); + s1 >> res; + return res; + } + + template + static _Tp fromString(const std::string& str) + { + return fromStringSimple<_Tp>(str); + } + + template + static _Tp fromStringNumber(const std::string& str)//the default conversion function for numbers + { + _Tp dummy_val=0; dummy_val+=1; + + if (str.empty()) + CV_Error(CV_StsParseError, "Empty string cannot be converted to a number"); + + const char* c_str=str.c_str(); + if((!isdigit(c_str[0])) + && + ( + (c_str[0]!='-') || (strlen(c_str) <= 1) || ( !isdigit(c_str[1]) ) + ) + ) + { + CV_Error(CV_StsParseError, "The string '"+ str +"' cannot be converted to a number"); + } + + return fromStringSimple<_Tp>(str); + } + + template + static _Tp fromStringsVec(const std::vector& vec_str) + { + if (vec_str.empty()) + CV_Error(CV_StsParseError, "Cannot convert from an empty vector"); + return fromString<_Tp>(vec_str[0]); + } +}; + +template<> +std::vector CommandLineParser::getVec(const std::string& keys); + +template +std::vector<_Tp> CommandLineParser::getVec(const std::string& keys) +{ + if (!has(keys)) + return std::vector<_Tp>(); + + std::vector v=getVec(keys); + + std::vector<_Tp> res; + for(size_t i=0; i < v.size(); i++) + { + _Tp val=fromString<_Tp>(v[i]); + res.push_back(val); + } + return res; +} + +template<> +std::string CommandLineParser::fromString(const std::string& str); + +template<> +int CommandLineParser::fromString(const std::string& str); + +template<> +unsigned int CommandLineParser::fromString(const std::string& str); + +template<> +double CommandLineParser::fromString(const std::string& str); + +template<> +cv::Size CommandLineParser::fromStringsVec(const std::vector& str); + } #endif // __cplusplus diff --git a/modules/core/src/CommandLineParser.cpp b/modules/core/src/CommandLineParser.cpp new file mode 100755 index 0000000000..16bd46a4d5 --- /dev/null +++ b/modules/core/src/CommandLineParser.cpp @@ -0,0 +1,172 @@ +#include "precomp.hpp" + +using namespace std; +using namespace cv; + + +vector split_string(const string& str, const string& delimiters) +{ + vector res; + string::size_type lastPos = str.find_first_not_of(delimiters, 0); + string::size_type pos = str.find_first_of(delimiters, lastPos); + while (string::npos != pos || string::npos != lastPos) + { + res.push_back(str.substr(lastPos, pos - lastPos)); + lastPos = str.find_first_not_of(delimiters, pos); + pos = str.find_first_of(delimiters, lastPos); + } + + return res; +} + +void PreprocessArgs(int _argc, const char* _argv[], int& argc, char**& argv) +{ + std::vector buffer_vector; + std::string buffer_string; + std::string buffer2_string; + int find_symbol; + + for (int i = 0; i < _argc; i++) + { + buffer_string = _argv[i]; + find_symbol = buffer_string.find('='); + if (find_symbol == -1) + buffer_vector.push_back(buffer_string); + else if (find_symbol == 0 || find_symbol == (buffer_string.length() - 1)) + { + buffer_string.erase(find_symbol, (find_symbol + 1)); + buffer_vector.push_back(buffer_string); + } + else + { + buffer2_string = buffer_string; + buffer_string.erase(find_symbol); + buffer_vector.push_back(buffer_string); + buffer2_string.erase(0, find_symbol + 1); + buffer_vector.push_back(buffer2_string); + } + } + + argc = buffer_vector.size(); + argv = new char* [argc]; + for (int i=0; i < argc; i++) + { + argv[i] = new char[buffer_vector[i].length() + 1]; + memcpy(argv[i], buffer_vector[i].c_str(), buffer_vector[i].length() + 1); + } +} + +CommandLineParser::CommandLineParser(int _argc, const char* _argv[]) +{ + std::string cur_name; + bool was_pushed=false; + int argc; + char** argv; + + PreprocessArgs(_argc, _argv, argc, argv); + + for(int i=1; i < argc; i++) + { + if(!argv[i]) + break; + + if( (argv[i][0]== '-') && (strlen(argv[i]) > 1) && + ((argv[i][1] < '0') || (argv[i][1] > '9')) ) + { + if (!cur_name.empty() && !was_pushed) + { + data[cur_name].push_back(""); + } + cur_name=argv[i]; + was_pushed=false; + + if (data.find(cur_name) != data.end()) + { + string str_exception = "dublicating parameters for name='" + cur_name + "'"; + CV_Error(CV_StsParseError, str_exception); + } + continue; + } + + data[cur_name].push_back(argv[i]); + was_pushed=true; + } + if (!cur_name.empty() && !was_pushed) + data[cur_name].push_back(""); +} + +bool CommandLineParser::has(const std::string& keys) const +{ + vector names=split_string(keys, " |"); + for(size_t j=0; j < names.size(); j++) + { + if (data.find(names[j])!=data.end()) + return true; + } + return false; +} + +template<> +std::vector CommandLineParser::getVec(const std::string& keys) +{ + vector names=split_string(keys, " |"); + + int found_index=-1; + for(size_t j=0; j < names.size(); j++) + { + const string& cur_name=names[j]; + bool is_cur_found=has(cur_name); + + if (is_cur_found && (found_index >= 0)) + { + string str_exception = "dublicating parameters for " + "name='" + names[found_index] + "' and name='"+cur_name+"'"; + CV_Error(CV_StsParseError, str_exception); + } + + if (is_cur_found) + found_index=j; + } + + if (found_index<0) + return vector(); + + return data.find(names[found_index])->second; +} + +template<> +std::string CommandLineParser::fromString(const std::string& str) +{ + return str; +} + +template<> +int CommandLineParser::fromString(const std::string& str) +{ + return fromStringNumber(str); +} + +template<> +unsigned int CommandLineParser::fromString(const std::string& str) +{ + return fromStringNumber(str); +} + +template<> +double CommandLineParser::fromString(const std::string& str) +{ + return fromStringNumber(str); +} + +template<> +cv::Size CommandLineParser::fromStringsVec(const std::vector& vec_str) +{ + if (vec_str.size() < 2) + CV_Error(CV_StsParseError, "Cannot convert vector of string to cv::Size : less than two strings"); + + cv::Size res; + res.width=fromString(vec_str[0]); + res.height=fromString(vec_str[1]); + + return res; +} diff --git a/samples/c/facedetect.cpp b/samples/c/facedetect.cpp index e2e169e1d5..188e6ee686 100644 --- a/samples/c/facedetect.cpp +++ b/samples/c/facedetect.cpp @@ -1,3 +1,4 @@ +#include #include #include #include @@ -10,13 +11,13 @@ using namespace cv; void help() { - cout << "\nThis program demonstrates the cascade recognizer. Now you can use Haar or LBP features.\n" + cout << "\nThis program demonstrates the cascade classifier. Now you can use Haar or LBP features.\n" "This classifier can recognize many ~rigid objects, it's most known use is for faces.\n" "Usage:\n" "./facedetect [--cascade= this is the primary trained classifier such as frontal face]\n" " [--nested-cascade[=nested_cascade_path this an optional secondary classifier such as eyes]]\n" " [--scale=\n" - " [filename|camera_index]\n\n" + " [--input=filename|camera_index]\n\n" "see facedetect.cmd for one call:\n" "./facedetect --cascade=\"../../data/haarcascades/haarcascade_frontalface_alt.xml\" --nested-cascade=\"../../data/haarcascades/haarcascade_eye.xml\" --scale=1.3 \n" "Hit any key to quit.\n" @@ -27,70 +28,42 @@ void detectAndDraw( Mat& img, CascadeClassifier& cascade, CascadeClassifier& nestedCascade, double scale); -String cascadeName = "../../data/haarcascades/haarcascade_frontalface_alt.xml"; -String nestedCascadeName = "../../data/haarcascades/haarcascade_eye_tree_eyeglasses.xml"; - int main( int argc, const char** argv ) { - CvCapture* capture = 0; - Mat frame, frameCopy, image; - const String scaleOpt = "--scale="; - size_t scaleOptLen = scaleOpt.length(); - const String cascadeOpt = "--cascade="; - size_t cascadeOptLen = cascadeOpt.length(); - const String nestedCascadeOpt = "--nested-cascade"; - size_t nestedCascadeOptLen = nestedCascadeOpt.length(); - String inputName; - help(); - CascadeClassifier cascade, nestedCascade; - double scale = 1; + CommandLineParser parser(argc, argv); - for( int i = 1; i < argc; i++ ) - { - cout << "Processing " << i << " " << argv[i] << endl; - if( cascadeOpt.compare( 0, cascadeOptLen, argv[i], cascadeOptLen ) == 0 ) - { - cascadeName.assign( argv[i] + cascadeOptLen ); - cout << " from which we have cascadeName= " << cascadeName << endl; - } - else if( nestedCascadeOpt.compare( 0, nestedCascadeOptLen, argv[i], nestedCascadeOptLen ) == 0 ) - { - if( argv[i][nestedCascadeOpt.length()] == '=' ) - nestedCascadeName.assign( argv[i] + nestedCascadeOpt.length() + 1 ); - if( !nestedCascade.load( nestedCascadeName ) ) - cerr << "WARNING: Could not load classifier cascade for nested objects" << endl; - } - else if( scaleOpt.compare( 0, scaleOptLen, argv[i], scaleOptLen ) == 0 ) - { - if( !sscanf( argv[i] + scaleOpt.length(), "%lf", &scale ) || scale < 1 ) - scale = 1; - cout << " from which we read scale = " << scale << endl; - } - else if( argv[i][0] == '-' ) - { - cerr << "WARNING: Unknown option %s" << argv[i] << endl; - } - else - inputName.assign( argv[i] ); - } + string cascadeName = parser.get("--cascade", "../../data/haarcascades/haarcascade_frontalface_alt.xml"); + if (!cascadeName.empty()) + cout << " from which we have cascadeName= " << cascadeName << endl; + + string nestedCascadeName = parser.get("--nested-cascade", "../../data/haarcascades/haarcascade_eye_tree_eyeglasses.xml"); + if (!nestedCascadeName.empty()) + cout << " from which we have nestedCascadeName= " << nestedCascadeName << endl; + + double scale = parser.get("--scale", 1.0); + + string inputName = parser.get("--input", "0"); //read from camera by default + + CvCapture* capture = 0; + Mat frame, frameCopy, image; + CascadeClassifier cascade, nestedCascade; if( !cascade.load( cascadeName ) ) { cerr << "ERROR: Could not load classifier cascade" << endl; - cerr << "Usage: facedetect [--cascade=]\n" - " [--nested-cascade[=nested_cascade_path]]\n" - " [--scale[=\n" - " [filename|camera_index]\n" << endl ; return -1; } + if( !nestedCascade.load( nestedCascadeName ) ) + cerr << "WARNING: Could not load classifier cascade for nested objects" << endl; + if( inputName.empty() || (isdigit(inputName.c_str()[0]) && inputName.c_str()[1] == '\0') ) { capture = cvCaptureFromCAM( inputName.empty() ? 0 : inputName.c_str()[0] - '0' ); int c = inputName.empty() ? 0 : inputName.c_str()[0] - '0' ; - if(!capture) cout << "Capture from CAM " << c << " didn't work" << endl; + if( !capture) cout << "Capture from CAM " << c << " didn't work" << endl; } else if( inputName.size() ) { @@ -98,14 +71,9 @@ int main( int argc, const char** argv ) if( image.empty() ) { capture = cvCaptureFromAVI( inputName.c_str() ); - if(!capture) cout << "Capture from AVI didn't work" << endl; + if( !capture ) cout << "Capture from AVI didn't work" << endl; } } - else - { - image = imread( "lena.jpg", 1 ); - if(image.empty()) cout << "Couldn't read lena.jpg" << endl; - } cvNamedWindow( "result", 1 );