command line parser added. Leonid Beynenson is original contributor of the class...
authorKirill Kornyakov <no@email>
Sun, 15 May 2011 19:25:00 +0000 (19:25 +0000)
committerKirill Kornyakov <no@email>
Sun, 15 May 2011 19:25:00 +0000 (19:25 +0000)
modules/core/include/opencv2/core/core.hpp
modules/core/src/CommandLineParser.cpp [new file with mode: 0755]
samples/c/facedetect.cpp

index 3065b2f..59b9864 100644 (file)
@@ -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<int>("--k | -k", -1);
+   vector<int> kValues=parser.getVec<int>("--k | -k");
+   double x = parser.get<double>("--x");
+   cv::Size size = parser.get<cv::Size>("--size");
+   string inputFile = parser.get<string>("--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<typename _Tp>
+    _Tp get(const std::string& name)
+    {
+        return fromStringsVec<_Tp>(getVec<std::string>(name));
+    }
+
+    //! get parameter with default value
+    template<typename _Tp>
+    _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<typename _Tp>
+    std::vector<_Tp> getVec(const std::string& keys);
+
+protected:
+    std::map<std::string, std::vector<std::string> > data;
+
+    template<typename _Tp>
+    static _Tp fromStringSimple(const std::string& str)//the default conversion function
+    {
+        _Tp res;
+        std::stringstream s1(str);
+        s1 >> res;
+        return res;
+    }
+
+    template<typename _Tp>
+    static _Tp fromString(const std::string& str)
+    {
+        return fromStringSimple<_Tp>(str);
+    }
+
+    template<typename _Tp>
+    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<typename _Tp>
+    static _Tp fromStringsVec(const std::vector<std::string>& 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<std::string> CommandLineParser::getVec<std::string>(const std::string& keys);
+
+template<typename _Tp>
+std::vector<_Tp> CommandLineParser::getVec(const std::string& keys)
+{
+    if (!has(keys))
+        return std::vector<_Tp>();
+
+    std::vector<std::string> v=getVec<std::string>(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<std::string>(const std::string& str);
+
+template<>
+int CommandLineParser::fromString<int>(const std::string& str);
+
+template<>
+unsigned int CommandLineParser::fromString<unsigned int>(const std::string& str);
+
+template<>
+double CommandLineParser::fromString<double>(const std::string& str);
+
+template<>
+cv::Size CommandLineParser::fromStringsVec<cv::Size>(const std::vector<std::string>& str);
+
 }
 
 #endif // __cplusplus
diff --git a/modules/core/src/CommandLineParser.cpp b/modules/core/src/CommandLineParser.cpp
new file mode 100755 (executable)
index 0000000..16bd46a
--- /dev/null
@@ -0,0 +1,172 @@
+#include "precomp.hpp"\r
+\r
+using namespace std;\r
+using namespace cv;\r
+\r
+\r
+vector<string> split_string(const string& str, const string& delimiters)\r
+{\r
+    vector<string> res;\r
+    string::size_type lastPos = str.find_first_not_of(delimiters, 0);\r
+    string::size_type pos     = str.find_first_of(delimiters, lastPos);\r
+    while (string::npos != pos || string::npos != lastPos)\r
+    {\r
+        res.push_back(str.substr(lastPos, pos - lastPos));\r
+        lastPos = str.find_first_not_of(delimiters, pos);\r
+        pos = str.find_first_of(delimiters, lastPos);\r
+    }\r
+\r
+    return res;\r
+}\r
+\r
+void PreprocessArgs(int _argc, const char* _argv[], int& argc, char**& argv)\r
+{\r
+    std::vector<std::string> buffer_vector;\r
+    std::string buffer_string;\r
+    std::string buffer2_string;\r
+    int find_symbol;\r
+\r
+    for (int i = 0; i < _argc; i++)\r
+    {\r
+        buffer_string = _argv[i];\r
+        find_symbol = buffer_string.find('=');\r
+        if (find_symbol == -1)\r
+            buffer_vector.push_back(buffer_string);\r
+        else if (find_symbol == 0 || find_symbol == (buffer_string.length() - 1))\r
+        {\r
+            buffer_string.erase(find_symbol, (find_symbol + 1));\r
+            buffer_vector.push_back(buffer_string);\r
+        }\r
+        else\r
+        {\r
+            buffer2_string = buffer_string;\r
+            buffer_string.erase(find_symbol);\r
+            buffer_vector.push_back(buffer_string);\r
+            buffer2_string.erase(0, find_symbol + 1);\r
+            buffer_vector.push_back(buffer2_string);\r
+        }\r
+    }\r
+\r
+    argc = buffer_vector.size();\r
+    argv = new char* [argc];\r
+    for (int i=0; i < argc; i++)\r
+    {\r
+        argv[i] = new char[buffer_vector[i].length() + 1];\r
+        memcpy(argv[i], buffer_vector[i].c_str(), buffer_vector[i].length() + 1);\r
+    }\r
+}\r
+\r
+CommandLineParser::CommandLineParser(int _argc, const char* _argv[])\r
+{\r
+    std::string cur_name;\r
+    bool was_pushed=false;\r
+    int argc;\r
+    char** argv;\r
+\r
+    PreprocessArgs(_argc, _argv, argc, argv);\r
+\r
+    for(int i=1; i < argc; i++)\r
+    {\r
+        if(!argv[i])\r
+            break;\r
+\r
+        if( (argv[i][0]== '-') && (strlen(argv[i]) > 1) &&\r
+            ((argv[i][1] < '0') || (argv[i][1] > '9'))   )\r
+        {\r
+            if (!cur_name.empty() && !was_pushed)\r
+            {\r
+                data[cur_name].push_back("");\r
+            }\r
+            cur_name=argv[i];\r
+            was_pushed=false;\r
+\r
+            if (data.find(cur_name) != data.end())\r
+            {\r
+                string str_exception = "dublicating parameters for name='" + cur_name + "'";\r
+                CV_Error(CV_StsParseError, str_exception);\r
+            }\r
+            continue;\r
+        }\r
+\r
+        data[cur_name].push_back(argv[i]);\r
+        was_pushed=true;\r
+    }\r
+    if (!cur_name.empty() && !was_pushed)\r
+        data[cur_name].push_back("");\r
+}\r
+\r
+bool CommandLineParser::has(const std::string& keys) const\r
+{\r
+    vector<string> names=split_string(keys, " |");\r
+    for(size_t j=0; j < names.size(); j++)\r
+    {\r
+        if (data.find(names[j])!=data.end())\r
+            return true;\r
+    }\r
+    return false;\r
+}\r
+\r
+template<>\r
+std::vector<std::string> CommandLineParser::getVec<std::string>(const std::string& keys)\r
+{\r
+    vector<string> names=split_string(keys, " |");\r
+\r
+    int found_index=-1;\r
+    for(size_t j=0; j < names.size(); j++)\r
+    {\r
+        const string& cur_name=names[j];\r
+        bool is_cur_found=has(cur_name);\r
+\r
+        if (is_cur_found && (found_index >= 0))\r
+        {\r
+            string str_exception = "dublicating parameters for "\r
+                                   "name='" + names[found_index] + "' and name='"+cur_name+"'";\r
+            CV_Error(CV_StsParseError, str_exception);\r
+        }\r
+\r
+        if (is_cur_found)\r
+            found_index=j;\r
+    }\r
+\r
+    if (found_index<0)\r
+        return vector<string>();\r
+\r
+    return data.find(names[found_index])->second;\r
+}\r
+\r
+template<>\r
+std::string CommandLineParser::fromString<std::string>(const std::string& str)\r
+{\r
+    return str;\r
+}\r
+\r
+template<>\r
+int CommandLineParser::fromString<int>(const std::string& str)\r
+{\r
+    return fromStringNumber<int>(str);\r
+}\r
+\r
+template<>\r
+unsigned int CommandLineParser::fromString<unsigned int>(const std::string& str)\r
+{\r
+    return fromStringNumber<unsigned int>(str);\r
+}\r
+\r
+template<>\r
+double CommandLineParser::fromString<double>(const std::string& str)\r
+{\r
+    return fromStringNumber<double>(str);\r
+}\r
+\r
+template<>\r
+cv::Size CommandLineParser::fromStringsVec<cv::Size>(const std::vector<std::string>& vec_str)\r
+{\r
+    if (vec_str.size() < 2)\r
+        CV_Error(CV_StsParseError, "Cannot convert vector of string to cv::Size : less than two strings");\r
+\r
+    cv::Size res;\r
+    res.width=fromString<int>(vec_str[0]);\r
+    res.height=fromString<int>(vec_str[1]);\r
+\r
+    return res;\r
+}\r
index e2e169e..188e6ee 100644 (file)
@@ -1,3 +1,4 @@
+#include <opencv2/core/core.hpp>
 #include <opencv2/objdetect/objdetect.hpp>
 #include <opencv2/highgui/highgui.hpp>
 #include <opencv2/imgproc/imgproc.hpp>
@@ -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=<cascade_path> 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=<image scale greater or equal to 1, try 1.3 for example>\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<string>("--cascade", "../../data/haarcascades/haarcascade_frontalface_alt.xml");
+    if (!cascadeName.empty())
+        cout << "  from which we have cascadeName= " << cascadeName << endl;
+
+    string nestedCascadeName = parser.get<string>("--nested-cascade", "../../data/haarcascades/haarcascade_eye_tree_eyeglasses.xml");
+    if (!nestedCascadeName.empty())
+        cout << "  from which we have nestedCascadeName= " << nestedCascadeName << endl;
+
+    double scale = parser.get<double>("--scale", 1.0);
+
+    string inputName = parser.get<string>("--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=<cascade_path>]\n"
-            "   [--nested-cascade[=nested_cascade_path]]\n"
-            "   [--scale[=<image 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 );