diff --git a/doc/Doxyfile.in b/doc/Doxyfile.in index 79af5ac359..93ccafaae8 100644 --- a/doc/Doxyfile.in +++ b/doc/Doxyfile.in @@ -244,7 +244,10 @@ PREDEFINED = __cplusplus=1 \ CV_DEFAULT(x)=" = x" \ CV_NEON=1 \ FLANN_DEPRECATED= \ - "CV_PURE_PROPERTY(type, name)= /**\@{*/ virtual type get##name() const = 0; virtual void set##name(type _##name) = 0; /**\@}*/" + "CV_PURE_PROPERTY(type, name)= /** \@see set##name */ virtual type get##name() const = 0; /** \@copybrief get##name \@see get##name */ virtual void set##name(type val) = 0;" \ + "CV_IMPL_PROPERTY(type, name, x)= /** \@see set##name */ virtual type get##name() const = 0; /** \@copybrief get##name \@see get##name */ virtual void set##name(type val) = 0;" \ + "CV_IMPL_PROPERTY_S(type, name, x)= /** \@see set##name */ virtual type get##name() const = 0; /** \@copybrief get##name \@see get##name */ virtual void set##name(const type & val);" \ + "CV_IMPL_PROPERTY_RO(type, name, x)= virtual type get##name() const;" EXPAND_AS_DEFINED = SKIP_FUNCTION_MACROS = YES TAGFILES = diff --git a/doc/tutorials/ml/introduction_to_svm/introduction_to_svm.markdown b/doc/tutorials/ml/introduction_to_svm/introduction_to_svm.markdown index 9b2de2c1e3..50f19b6fd2 100644 --- a/doc/tutorials/ml/introduction_to_svm/introduction_to_svm.markdown +++ b/doc/tutorials/ml/introduction_to_svm/introduction_to_svm.markdown @@ -1,8 +1,6 @@ Introduction to Support Vector Machines {#tutorial_introduction_to_svm} ======================================= -@todo update this tutorial - Goal ---- @@ -31,13 +29,11 @@ understand that this is done only because our intuition is better built from exa to imagine. However, the same concepts apply to tasks where the examples to classify lie in a space whose dimension is higher than two. -In the above picture you can see that there exists multiple -lines that offer a solution to the problem. Is any of them better than the others? We can -intuitively define a criterion to estimate the worth of the lines: - -- A line is bad if it passes too close to the points because it will be noise sensitive and it will - not generalize correctly. Therefore, our goal should be to find the line passing as far as - possible from all points. +In the above picture you can see that there exists multiple lines that offer a solution to the +problem. Is any of them better than the others? We can intuitively define a criterion to estimate +the worth of the lines: A line is bad if it passes too close to the points because it will be +noise sensitive and it will not generalize correctly. Therefore, our goal should be to find +the line passing as far as possible from all points. Then, the operation of the SVM algorithm is based on finding the hyperplane that gives the largest minimum distance to the training examples. Twice, this distance receives the important name of @@ -57,7 +53,7 @@ where \f$\beta\f$ is known as the *weight vector* and \f$\beta_{0}\f$ as the *bi @sa A more in depth description of this and hyperplanes you can find in the section 4.5 (*Seperating Hyperplanes*) of the book: *Elements of Statistical Learning* by T. Hastie, R. Tibshirani and J. H. -Friedman. +Friedman (@cite HTF01). The optimal hyperplane can be represented in an infinite number of different ways by scaling of \f$\beta\f$ and \f$\beta_{0}\f$. As a matter of convention, among all the possible @@ -107,17 +103,14 @@ Explanation The training data of this exercise is formed by a set of labeled 2D-points that belong to one of two different classes; one of the classes consists of one point and the other of three points. - @code{.cpp} - float labels[4] = {1.0, -1.0, -1.0, -1.0}; - float trainingData[4][2] = {{501, 10}, {255, 10}, {501, 255}, {10, 501}}; - @endcode + + @snippet cpp/tutorial_code/ml/introduction_to_svm/introduction_to_svm.cpp setup1 + The function @ref cv::ml::SVM::train that will be used afterwards requires the training data to be stored as @ref cv::Mat objects of floats. Therefore, we create these objects from the arrays defined above: - @code{.cpp} - Mat trainingDataMat(4, 2, CV_32FC1, trainingData); - Mat labelsMat (4, 1, CV_32FC1, labels); - @endcode + + @snippet cpp/tutorial_code/ml/introduction_to_svm/introduction_to_svm.cpp setup2 -# **Set up SVM's parameters** @@ -126,42 +119,35 @@ Explanation used in a wide variety of problems (e.g. problems with non-linearly separable data, a SVM using a kernel function to raise the dimensionality of the examples, etc). As a consequence of this, we have to define some parameters before training the SVM. These parameters are stored in an - object of the class @ref cv::ml::SVM::Params . - @code{.cpp} - ml::SVM::Params params; - params.svmType = ml::SVM::C_SVC; - params.kernelType = ml::SVM::LINEAR; - params.termCrit = TermCriteria(TermCriteria::MAX_ITER, 100, 1e-6); - @endcode - - *Type of SVM*. We choose here the type **ml::SVM::C_SVC** that can be used for n-class - classification (n \f$\geq\f$ 2). This parameter is defined in the attribute - *ml::SVM::Params.svmType*. - - The important feature of the type of SVM **CvSVM::C_SVC** deals with imperfect separation of classes (i.e. when the training data is non-linearly separable). This feature is not important here since the data is linearly separable and we chose this SVM type only for being the most commonly used. + object of the class @ref cv::ml::SVM. + + @snippet cpp/tutorial_code/ml/introduction_to_svm/introduction_to_svm.cpp init + + Here: + - *Type of SVM*. We choose here the type @ref cv::ml::SVM::C_SVC "C_SVC" that can be used for + n-class classification (n \f$\geq\f$ 2). The important feature of this type is that it deals + with imperfect separation of classes (i.e. when the training data is non-linearly separable). + This feature is not important here since the data is linearly separable and we chose this SVM + type only for being the most commonly used. - *Type of SVM kernel*. We have not talked about kernel functions since they are not - interesting for the training data we are dealing with. Nevertheless, let's explain briefly - now the main idea behind a kernel function. It is a mapping done to the training data to - improve its resemblance to a linearly separable set of data. This mapping consists of - increasing the dimensionality of the data and is done efficiently using a kernel function. - We choose here the type **ml::SVM::LINEAR** which means that no mapping is done. This - parameter is defined in the attribute *ml::SVMParams.kernel_type*. + interesting for the training data we are dealing with. Nevertheless, let's explain briefly now + the main idea behind a kernel function. It is a mapping done to the training data to improve + its resemblance to a linearly separable set of data. This mapping consists of increasing the + dimensionality of the data and is done efficiently using a kernel function. We choose here the + type @ref cv::ml::SVM::LINEAR "LINEAR" which means that no mapping is done. This parameter is + defined using cv::ml::SVM::setKernel. - *Termination criteria of the algorithm*. The SVM training procedure is implemented solving a constrained quadratic optimization problem in an **iterative** fashion. Here we specify a maximum number of iterations and a tolerance error so we allow the algorithm to finish in less number of steps even if the optimal hyperplane has not been computed yet. This - parameter is defined in a structure @ref cv::cvTermCriteria . + parameter is defined in a structure @ref cv::TermCriteria . -# **Train the SVM** + We call the method @ref cv::ml::SVM::train to build the SVM model. - We call the method - [CvSVM::train](http://docs.opencv.org/modules/ml/doc/support_vector_machines.html#cvsvm-train) - to build the SVM model. - @code{.cpp} - CvSVM SVM; - SVM.train(trainingDataMat, labelsMat, Mat(), Mat(), params); - @endcode + @snippet cpp/tutorial_code/ml/introduction_to_svm/introduction_to_svm.cpp train -# **Regions classified by the SVM** @@ -170,22 +156,8 @@ Explanation by the SVM. In other words, an image is traversed interpreting its pixels as points of the Cartesian plane. Each of the points is colored depending on the class predicted by the SVM; in green if it is the class with label 1 and in blue if it is the class with label -1. - @code{.cpp} - Vec3b green(0,255,0), blue (255,0,0); - - for (int i = 0; i < image.rows; ++i) - for (int j = 0; j < image.cols; ++j) - { - Mat sampleMat = (Mat_(1,2) << i,j); - float response = SVM.predict(sampleMat); - - if (response == 1) - image.at(j, i) = green; - else - if (response == -1) - image.at(j, i) = blue; - } - @endcode + + @snippet cpp/tutorial_code/ml/introduction_to_svm/introduction_to_svm.cpp show -# **Support vectors** @@ -193,15 +165,8 @@ Explanation The method @ref cv::ml::SVM::getSupportVectors obtain all of the support vectors. We have used this methods here to find the training examples that are support vectors and highlight them. - @code{.cpp} - int c = SVM.get_support_vector_count(); - - for (int i = 0; i < c; ++i) - { - const float* v = SVM.get_support_vector(i); // get and then highlight with grayscale - circle( image, Point( (int) v[0], (int) v[1]), 6, Scalar(128, 128, 128), thickness, lineType); - } - @endcode + + @snippet cpp/tutorial_code/ml/introduction_to_svm/introduction_to_svm.cpp show_vectors Results ------- diff --git a/doc/tutorials/ml/non_linear_svms/non_linear_svms.markdown b/doc/tutorials/ml/non_linear_svms/non_linear_svms.markdown index 5c36e425b3..eb171b94a3 100644 --- a/doc/tutorials/ml/non_linear_svms/non_linear_svms.markdown +++ b/doc/tutorials/ml/non_linear_svms/non_linear_svms.markdown @@ -1,8 +1,6 @@ Support Vector Machines for Non-Linearly Separable Data {#tutorial_non_linear_svms} ======================================================= -@todo update this tutorial - Goal ---- @@ -10,21 +8,20 @@ In this tutorial you will learn how to: - Define the optimization problem for SVMs when it is not possible to separate linearly the training data. -- How to configure the parameters in @ref cv::ml::SVM::Params to adapt your SVM for this class of - problems. +- How to configure the parameters to adapt your SVM for this class of problems. Motivation ---------- Why is it interesting to extend the SVM optimation problem in order to handle non-linearly separable training data? Most of the applications in which SVMs are used in computer vision require a more -powerful tool than a simple linear classifier. This stems from the fact that in these tasks **the -training data can be rarely separated using an hyperplane**. +powerful tool than a simple linear classifier. This stems from the fact that in these tasks __the +training data can be rarely separated using an hyperplane__. Consider one of these tasks, for example, face detection. The training data in this case is composed -by a set of images that are faces and another set of images that are non-faces (*every other thing -in the world except from faces*). This training data is too complex so as to find a representation -of each sample (*feature vector*) that could make the whole set of faces linearly separable from the +by a set of images that are faces and another set of images that are non-faces (_every other thing +in the world except from faces_). This training data is too complex so as to find a representation +of each sample (_feature vector_) that could make the whole set of faces linearly separable from the whole set of non-faces. Extension of the Optimization Problem @@ -32,13 +29,13 @@ Extension of the Optimization Problem Remember that using SVMs we obtain a separating hyperplane. Therefore, since the training data is now non-linearly separable, we must admit that the hyperplane found will misclassify some of the -samples. This *misclassification* is a new variable in the optimization that must be taken into +samples. This _misclassification_ is a new variable in the optimization that must be taken into account. The new model has to include both the old requirement of finding the hyperplane that gives the biggest margin and the new one of generalizing the training data correctly by not allowing too many classification errors. We start here from the formulation of the optimization problem of finding the hyperplane which -maximizes the **margin** (this is explained in the previous tutorial (@ref tutorial_introduction_to_svm): +maximizes the __margin__ (this is explained in the previous tutorial (@ref tutorial_introduction_to_svm): \f[\min_{\beta, \beta_{0}} L(\beta) = \frac{1}{2}||\beta||^{2} \text{ subject to } y_{i}(\beta^{T} x_{i} + \beta_{0}) \geq 1 \text{ } \forall i\f] @@ -50,8 +47,8 @@ constant times the number of misclassification errors in the training data, i.e. However, this one is not a very good solution since, among some other reasons, we do not distinguish between samples that are misclassified with a small distance to their appropriate decision region or -samples that are not. Therefore, a better solution will take into account the *distance of the -misclassified samples to their correct decision regions*, i.e.: +samples that are not. Therefore, a better solution will take into account the _distance of the +misclassified samples to their correct decision regions_, i.e.: \f[\min ||\beta||^{2} + C \text{(distance of misclassified samples to their correct regions)}\f] @@ -68,7 +65,7 @@ distances of the rest of the samples are zero since they lay already in their co region. The red and blue lines that appear on the picture are the margins to each one of the -decision regions. It is very **important** to realize that each of the \f$\xi_{i}\f$ goes from a +decision regions. It is very __important__ to realize that each of the \f$\xi_{i}\f$ goes from a misclassified training sample to the margin of its appropriate region. Finally, the new formulation for the optimization problem is: @@ -79,26 +76,25 @@ How should the parameter C be chosen? It is obvious that the answer to this ques the training data is distributed. Although there is no general answer, it is useful to take into account these rules: -- Large values of C give solutions with *less misclassification errors* but a *smaller margin*. +- Large values of C give solutions with _less misclassification errors_ but a _smaller margin_. Consider that in this case it is expensive to make misclassification errors. Since the aim of the optimization is to minimize the argument, few misclassifications errors are allowed. -- Small values of C give solutions with *bigger margin* and *more classification errors*. In this +- Small values of C give solutions with _bigger margin_ and _more classification errors_. In this case the minimization does not consider that much the term of the sum so it focuses more on finding a hyperplane with big margin. Source Code ----------- -You may also find the source code and these video file in the -`samples/cpp/tutorial_code/gpu/non_linear_svms/non_linear_svms` folder of the OpenCV source library -or [download it from here ](https://github.com/Itseez/opencv/tree/master/samples/cpp/tutorial_code/ml/non_linear_svms/non_linear_svms.cpp). +You may also find the source code in `samples/cpp/tutorial_code/ml/non_linear_svms` folder of the OpenCV source library or +[download it from here](https://github.com/Itseez/opencv/tree/master/samples/cpp/tutorial_code/ml/non_linear_svms/non_linear_svms.cpp). @includelineno cpp/tutorial_code/ml/non_linear_svms/non_linear_svms.cpp Explanation ----------- --# **Set up the training data** +-# __Set up the training data__ The training data of this exercise is formed by a set of labeled 2D-points that belong to one of two different classes. To make the exercise more appealing, the training data is generated @@ -107,136 +103,67 @@ Explanation We have divided the generation of the training data into two main parts. In the first part we generate data for both classes that is linearly separable. - @code{.cpp} - // Generate random points for the class 1 - Mat trainClass = trainData.rowRange(0, nLinearSamples); - // The x coordinate of the points is in [0, 0.4) - Mat c = trainClass.colRange(0, 1); - rng.fill(c, RNG::UNIFORM, Scalar(1), Scalar(0.4 * WIDTH)); - // The y coordinate of the points is in [0, 1) - c = trainClass.colRange(1,2); - rng.fill(c, RNG::UNIFORM, Scalar(1), Scalar(HEIGHT)); - - // Generate random points for the class 2 - trainClass = trainData.rowRange(2*NTRAINING_SAMPLES-nLinearSamples, 2*NTRAINING_SAMPLES); - // The x coordinate of the points is in [0.6, 1] - c = trainClass.colRange(0 , 1); - rng.fill(c, RNG::UNIFORM, Scalar(0.6*WIDTH), Scalar(WIDTH)); - // The y coordinate of the points is in [0, 1) - c = trainClass.colRange(1,2); - rng.fill(c, RNG::UNIFORM, Scalar(1), Scalar(HEIGHT)); - @endcode + @snippet cpp/tutorial_code/ml/non_linear_svms/non_linear_svms.cpp setup1 + In the second part we create data for both classes that is non-linearly separable, data that overlaps. - @code{.cpp} - // Generate random points for the classes 1 and 2 - trainClass = trainData.rowRange( nLinearSamples, 2*NTRAINING_SAMPLES-nLinearSamples); - // The x coordinate of the points is in [0.4, 0.6) - c = trainClass.colRange(0,1); - rng.fill(c, RNG::UNIFORM, Scalar(0.4*WIDTH), Scalar(0.6*WIDTH)); - // The y coordinate of the points is in [0, 1) - c = trainClass.colRange(1,2); - rng.fill(c, RNG::UNIFORM, Scalar(1), Scalar(HEIGHT)); - @endcode - --# **Set up SVM's parameters** - - @sa - In the previous tutorial @ref tutorial_introduction_to_svm there is an explanation of the atributes of the - class @ref cv::ml::SVM::Params that we configure here before training the SVM. - - @code{.cpp} - CvSVMParams params; - params.svm_type = SVM::C_SVC; - params.C = 0.1; - params.kernel_type = SVM::LINEAR; - params.term_crit = TermCriteria(TermCriteria::ITER, (int)1e7, 1e-6); - @endcode + @snippet cpp/tutorial_code/ml/non_linear_svms/non_linear_svms.cpp setup2 + +-# __Set up SVM's parameters__ + + @note In the previous tutorial @ref tutorial_introduction_to_svm there is an explanation of the + atributes of the class @ref cv::ml::SVM that we configure here before training the SVM. + + @snippet cpp/tutorial_code/ml/non_linear_svms/non_linear_svms.cpp init + There are just two differences between the configuration we do here and the one that was done in - the previous tutorial (tutorial_introduction_to_svm) that we use as reference. + the previous tutorial (@ref tutorial_introduction_to_svm) that we use as reference. - - *CvSVM::C_SVC*. We chose here a small value of this parameter in order not to punish too much - the misclassification errors in the optimization. The idea of doing this stems from the will - of obtaining a solution close to the one intuitively expected. However, we recommend to get a + - _C_. We chose here a small value of this parameter in order not to punish too much the + misclassification errors in the optimization. The idea of doing this stems from the will of + obtaining a solution close to the one intuitively expected. However, we recommend to get a better insight of the problem by making adjustments to this parameter. - @note Here there are just very few points in the overlapping region between classes, giving a smaller value to **FRAC_LINEAR_SEP** the density of points can be incremented and the impact of the parameter **CvSVM::C_SVC** explored deeply. + @note In this case there are just very few points in the overlapping region between classes. + By giving a smaller value to __FRAC_LINEAR_SEP__ the density of points can be incremented and the + impact of the parameter _C_ explored deeply. - - *Termination Criteria of the algorithm*. The maximum number of iterations has to be + - _Termination Criteria of the algorithm_. The maximum number of iterations has to be increased considerably in order to solve correctly a problem with non-linearly separable training data. In particular, we have increased in five orders of magnitude this value. --# **Train the SVM** +-# __Train the SVM__ We call the method @ref cv::ml::SVM::train to build the SVM model. Watch out that the training process may take a quite long time. Have patiance when your run the program. - @code{.cpp} - CvSVM svm; - svm.train(trainData, labels, Mat(), Mat(), params); - @endcode --# **Show the Decision Regions** + @snippet cpp/tutorial_code/ml/non_linear_svms/non_linear_svms.cpp train + +-# __Show the Decision Regions__ The method @ref cv::ml::SVM::predict is used to classify an input sample using a trained SVM. In this example we have used this method in order to color the space depending on the prediction done by the SVM. In other words, an image is traversed interpreting its pixels as points of the Cartesian plane. Each of the points is colored depending on the class predicted by the SVM; in dark green if it is the class with label 1 and in dark blue if it is the class with label 2. - @code{.cpp} - Vec3b green(0,100,0), blue (100,0,0); - for (int i = 0; i < I.rows; ++i) - for (int j = 0; j < I.cols; ++j) - { - Mat sampleMat = (Mat_(1,2) << i, j); - float response = svm.predict(sampleMat); - if (response == 1) I.at(j, i) = green; - else if (response == 2) I.at(j, i) = blue; - } - @endcode + @snippet cpp/tutorial_code/ml/non_linear_svms/non_linear_svms.cpp show --# **Show the training data** +-# __Show the training data__ The method @ref cv::circle is used to show the samples that compose the training data. The samples of the class labeled with 1 are shown in light green and in light blue the samples of the class labeled with 2. - @code{.cpp} - int thick = -1; - int lineType = 8; - float px, py; - // Class 1 - for (int i = 0; i < NTRAINING_SAMPLES; ++i) - { - px = trainData.at(i,0); - py = trainData.at(i,1); - circle(I, Point( (int) px, (int) py ), 3, Scalar(0, 255, 0), thick, lineType); - } - // Class 2 - for (int i = NTRAINING_SAMPLES; i <2*NTRAINING_SAMPLES; ++i) - { - px = trainData.at(i,0); - py = trainData.at(i,1); - circle(I, Point( (int) px, (int) py ), 3, Scalar(255, 0, 0), thick, lineType); - } - @endcode - --# **Support vectors** + + @snippet cpp/tutorial_code/ml/non_linear_svms/non_linear_svms.cpp show_data + +-# __Support vectors__ We use here a couple of methods to obtain information about the support vectors. The method - @ref cv::ml::SVM::getSupportVectors obtain all support vectors. - We have used this methods here to find the training examples that are - support vectors and highlight them. - @code{.cpp} - thick = 2; - lineType = 8; - int x = svm.get_support_vector_count(); - - for (int i = 0; i < x; ++i) - { - const float* v = svm.get_support_vector(i); - circle( I, Point( (int) v[0], (int) v[1]), 6, Scalar(128, 128, 128), thick, lineType); - } - @endcode + @ref cv::ml::SVM::getSupportVectors obtain all support vectors. We have used this methods here + to find the training examples that are support vectors and highlight them. + + @snippet cpp/tutorial_code/ml/non_linear_svms/non_linear_svms.cpp show_vectors Results ------- diff --git a/modules/core/include/opencv2/core.hpp b/modules/core/include/opencv2/core.hpp index 701c0e3a07..77a8c503e7 100644 --- a/modules/core/include/opencv2/core.hpp +++ b/modules/core/include/opencv2/core.hpp @@ -2802,43 +2802,36 @@ public: #define CV_PURE_PROPERTY(type, name) \ CV_WRAP virtual type get##name() const = 0; \ - CV_WRAP virtual void set##name(type _##name) = 0; + CV_WRAP virtual void set##name(type val) = 0; #define CV_PURE_PROPERTY_S(type, name) \ CV_WRAP virtual type get##name() const = 0; \ - CV_WRAP virtual void set##name(const type & _##name) = 0; + CV_WRAP virtual void set##name(const type & val) = 0; #define CV_PURE_PROPERTY_RO(type, name) \ CV_WRAP virtual type get##name() const = 0; // basic property implementation -#define CV_IMPL_PROPERTY(type, name, member) \ - type get##name() const \ - { \ - return member; \ - } \ - void set##name(type val) \ - { \ - member = val; \ - } +#define CV_IMPL_PROPERTY_RO(type, name, member) \ + inline type get##name() const { return member; } -#define CV_IMPL_PROPERTY_S(type, name, member) \ - type get##name() const \ - { \ - return member; \ - } \ - void set##name(const type &val) \ - { \ - member = val; \ - } +#define CV_HELP_IMPL_PROPERTY(r_type, w_type, name, member) \ + CV_IMPL_PROPERTY_RO(r_type, name, member) \ + inline void set##name(w_type val) { member = val; } -#define CV_IMPL_PROPERTY_RO(type, name, member) \ - type get##name() const \ - { \ - return member; \ - } +#define CV_HELP_WRAP_PROPERTY(r_type, w_type, name, internal_name, internal_obj) \ + r_type get##name() const { return internal_obj.get##internal_name(); } \ + void set##name(w_type val) { internal_obj.set##internal_name(val); } + +#define CV_IMPL_PROPERTY(type, name, member) CV_HELP_IMPL_PROPERTY(type, type, name, member) +#define CV_IMPL_PROPERTY_S(type, name, member) CV_HELP_IMPL_PROPERTY(type, const type &, name, member) + +#define CV_WRAP_PROPERTY(type, name, internal_name, internal_obj) CV_HELP_WRAP_PROPERTY(type, type, name, internal_name, internal_obj) +#define CV_WRAP_PROPERTY_S(type, name, internal_name, internal_obj) CV_HELP_WRAP_PROPERTY(type, const type &, name, internal_name, internal_obj) +#define CV_WRAP_SAME_PROPERTY(type, name, internal_obj) CV_WRAP_PROPERTY(type, name, name, internal_obj) +#define CV_WRAP_SAME_PROPERTY_S(type, name, internal_obj) CV_WRAP_PROPERTY_S(type, name, name, internal_obj) struct Param { enum { INT=0, BOOLEAN=1, REAL=2, STRING=3, MAT=4, MAT_VECTOR=5, ALGORITHM=6, FLOAT=7, diff --git a/modules/ml/doc/ml_intro.markdown b/modules/ml/doc/ml_intro.markdown index 5e3c3d2cf8..b7a3d4059f 100644 --- a/modules/ml/doc/ml_intro.markdown +++ b/modules/ml/doc/ml_intro.markdown @@ -449,40 +449,33 @@ classes 0 and 1, one can determine that the given data instance belongs to class \geq 0.5\f$ or class 0 if \f$h_\theta(x) < 0.5\f$ . In Logistic Regression, choosing the right parameters is of utmost importance for reducing the -training error and ensuring high training accuracy. cv::ml::LogisticRegression::Params is the -structure that defines parameters that are required to train a Logistic Regression classifier. - -The learning rate is determined by cv::ml::LogisticRegression::Params.alpha. It determines how fast -we approach the solution. It is a positive real number. - -Optimization algorithms like Batch Gradient Descent and Mini-Batch Gradient Descent are supported in -LogisticRegression. It is important that we mention the number of iterations these optimization -algorithms have to run. The number of iterations are mentioned by -cv::ml::LogisticRegression::Params.num_iters. The number of iterations can be thought as number of -steps taken and learning rate specifies if it is a long step or a short step. These two parameters -define how fast we arrive at a possible solution. - -In order to compensate for overfitting regularization is performed, which can be enabled by setting -cv::ml::LogisticRegression::Params.regularized to a positive integer (greater than zero). One can -specify what kind of regularization has to be performed by setting -cv::ml::LogisticRegression::Params.norm to REG_L1 or REG_L2 values. - -LogisticRegression provides a choice of 2 training methods with Batch Gradient Descent or the Mini- -Batch Gradient Descent. To specify this, set cv::ml::LogisticRegression::Params::train_method to -either BATCH or MINI_BATCH. If training method is set to MINI_BATCH, the size of the mini batch has -to be to a postive integer using cv::ml::LogisticRegression::Params::mini_batch_size. - -A sample set of training parameters for the Logistic Regression classifier can be initialized as -follows: -@code{.cpp} -using namespace cv::ml; -LogisticRegression::Params params; -params.alpha = 0.5; -params.num_iters = 10000; -params.norm = LogisticRegression::REG_L2; -params.regularized = 1; -params.train_method = LogisticRegression::MINI_BATCH; -params.mini_batch_size = 10; -@endcode +training error and ensuring high training accuracy: + +- The learning rate can be set with @ref cv::ml::LogisticRegression::setLearningRate "setLearningRate" + method. It determines how fast we approach the solution. It is a positive real number. + +- Optimization algorithms like Batch Gradient Descent and Mini-Batch Gradient Descent are supported + in LogisticRegression. It is important that we mention the number of iterations these optimization + algorithms have to run. The number of iterations can be set with @ref + cv::ml::LogisticRegression::setIterations "setIterations". This parameter can be thought + as number of steps taken and learning rate specifies if it is a long step or a short step. This + and previous parameter define how fast we arrive at a possible solution. + +- In order to compensate for overfitting regularization is performed, which can be enabled with + @ref cv::ml::LogisticRegression::setRegularization "setRegularization". One can specify what + kind of regularization has to be performed by passing one of @ref + cv::ml::LogisticRegression::RegKinds "regularization kinds" to this method. + +- Logistic regression implementation provides a choice of 2 training methods with Batch Gradient + Descent or the MiniBatch Gradient Descent. To specify this, call @ref + cv::ml::LogisticRegression::setTrainMethod "setTrainMethod" with either @ref + cv::ml::LogisticRegression::BATCH "LogisticRegression::BATCH" or @ref + cv::ml::LogisticRegression::MINI_BATCH "LogisticRegression::MINI_BATCH". If training method is + set to @ref cv::ml::LogisticRegression::MINI_BATCH "MINI_BATCH", the size of the mini batch has + to be to a postive integer set with @ref cv::ml::LogisticRegression::setMiniBatchSize + "setMiniBatchSize". + +A sample set of training parameters for the Logistic Regression classifier can be initialized as follows: +@snippet samples/cpp/logistic_regression.cpp init @sa cv::ml::LogisticRegression diff --git a/modules/ml/include/opencv2/ml.hpp b/modules/ml/include/opencv2/ml.hpp index 9dca486af4..c7559aea52 100644 --- a/modules/ml/include/opencv2/ml.hpp +++ b/modules/ml/include/opencv2/ml.hpp @@ -381,43 +381,22 @@ public: return model->isTrained() ? model : Ptr<_Tp>(); } + /** @brief Create and train model with default parameters - /** @brief Creates new statistical model and trains it - - @param data training data that can be loaded from file using TrainData::loadFromCSV or - created with TrainData::create. - @param p model parameters - @param flags optional flags, depending on the model. Some of the models can be updated with the - new training samples, not completely overwritten (such as NormalBayesClassifier or ANN_MLP). - */ - template static Ptr<_Tp> train(const Ptr& data, const typename _Tp::Params& p, int flags=0) - { - Ptr<_Tp> model = _Tp::create(p); - return !model.empty() && model->train(data, flags) ? model : Ptr<_Tp>(); - } - - /** @brief Creates new statistical model and trains it - - @param samples training samples - @param layout See ml::SampleTypes. - @param responses vector of responses associated with the training samples. - @param p model parameters - @param flags optional flags, depending on the model. Some of the models can be updated with the - new training samples, not completely overwritten (such as NormalBayesClassifier or ANN_MLP). + The class must implement static `create()` method with no parameters or with all default parameter values */ - template static Ptr<_Tp> train(InputArray samples, int layout, InputArray responses, - const typename _Tp::Params& p, int flags=0) + template static Ptr<_Tp> train(const Ptr& data, int flags=0) { - Ptr<_Tp> model = _Tp::create(p); - return !model.empty() && model->train(TrainData::create(samples, layout, responses), flags) ? model : Ptr<_Tp>(); + Ptr<_Tp> model = _Tp::create(); + return !model.empty() && model->train(data, flags) ? model : Ptr<_Tp>(); } - /** @brief Saves the model to a file. - - In order to make this method work, the derived class must overwrite - Algorithm::write(FileStorage& fs). - */ + /** Saves the model to a file. + In order to make this method work, the derived class must implement Algorithm::write(FileStorage& fs). */ virtual void save(const String& filename) const; + + /** Returns model string identifier. + This string is used as top level xml/yml node tag when model is saved to a file or string. */ virtual String getDefaultModelName() const = 0; }; @@ -432,11 +411,6 @@ public: class CV_EXPORTS_W NormalBayesClassifier : public StatModel { public: - class CV_EXPORTS_W Params - { - public: - Params(); - }; /** @brief Predicts the response for sample(s). The method estimates the most probable classes for input vectors. Input vectors (one or more) @@ -447,21 +421,10 @@ public: */ virtual float predictProb( InputArray inputs, OutputArray outputs, OutputArray outputProbs, int flags=0 ) const = 0; - virtual void setParams(const Params& params) = 0; - virtual Params getParams() const = 0; - /** @brief Creates empty model - - @param params The model parameters. There is none so far, the structure is used as a placeholder - for possible extensions. - - Use StatModel::train to train the model: - @code - StatModel::train(traindata, params); // to create and train the model - StatModel::load(filename); // load the pre-trained model - @endcode - */ - static Ptr create(const Params& params=Params()); + /** Creates empty model + Use StatModel::train to train the model after creation. */ + static Ptr create(); }; /****************************************************************************************\ @@ -475,19 +438,18 @@ public: class CV_EXPORTS_W KNearest : public StatModel { public: - class CV_EXPORTS_W_MAP Params - { - public: - /** @brief Constructor with parameters */ - Params(int defaultK=10, bool isclassifier_=true, int Emax_=INT_MAX, int algorithmType_=BRUTE_FORCE); - CV_PROP_RW int defaultK; //!< default number of neighbors to use in predict method - CV_PROP_RW bool isclassifier; //!< whether classification or regression model should be trained - CV_PROP_RW int Emax; //!< for implementation with KDTree - CV_PROP_RW int algorithmType; //!< See KNearest::Types - }; - virtual void setParams(const Params& p) = 0; - virtual Params getParams() const = 0; + /** Default number of neighbors to use in predict method. */ + CV_PURE_PROPERTY(int, DefaultK) + + /** Whether classification or regression model should be trained. */ + CV_PURE_PROPERTY(bool, IsClassifier) + + /** Parameter for KDTree implementation. */ + CV_PURE_PROPERTY(int, Emax) + + /** %Algorithm type, one of KNearest::Types. */ + CV_PURE_PROPERTY(int, AlgorithmType) /** @brief Finds the neighbors and predicts responses for input vectors. @@ -520,17 +482,19 @@ public: OutputArray neighborResponses=noArray(), OutputArray dist=noArray() ) const = 0; - enum Types { BRUTE_FORCE=1, KDTREE=2 }; + /** @brief Implementations of KNearest algorithm + */ + enum Types + { + BRUTE_FORCE=1, + KDTREE=2 + }; /** @brief Creates the empty model - @param params The model parameters - - The static method creates empty %KNearest classifier. It should be then trained using train - method (see StatModel::train). Alternatively, you can load boost model from file using: - `StatModel::load(filename)` + The static method creates empty %KNearest classifier. It should be then trained using StatModel::train method. */ - static Ptr create(const Params& params=Params()); + static Ptr create(); }; /****************************************************************************************\ @@ -544,54 +508,6 @@ public: class CV_EXPORTS_W SVM : public StatModel { public: - /** @brief %SVM training parameters. - - The structure must be initialized and passed to the training method of %SVM. - */ - class CV_EXPORTS_W_MAP Params - { - public: - /** @brief Default constructor */ - Params(); - /** @brief Constructor with parameters */ - Params( int svm_type, int kernel_type, - double degree, double gamma, double coef0, - double Cvalue, double nu, double p, - const Mat& classWeights, TermCriteria termCrit ); - - /** Type of a %SVM formulation. See SVM::Types. Default value is SVM::C_SVC. */ - CV_PROP_RW int svmType; - /** Type of a %SVM kernel. See SVM::KernelTypes. Default value is SVM::RBF. */ - CV_PROP_RW int kernelType; - /** Parameter \f$\gamma\f$ of a kernel function (SVM::POLY / SVM::RBF / SVM::SIGMOID / - SVM::CHI2). Default value is 1. */ - CV_PROP_RW double gamma; - /** Parameter coef0 of a kernel function (SVM::POLY / SVM::SIGMOID). Default value is 0. */ - CV_PROP_RW double coef0; - /** Parameter degree of a kernel function (SVM::POLY). Default value is 0. */ - CV_PROP_RW double degree; - - /** Parameter C of a %SVM optimization problem (SVM::C_SVC / SVM::EPS_SVR / SVM::NU_SVR). - Default value is 0. */ - CV_PROP_RW double C; - /** Parameter \f$\nu\f$ of a %SVM optimization problem (SVM::NU_SVC / SVM::ONE_CLASS / - SVM::NU_SVR). Default value is 0. */ - CV_PROP_RW double nu; - /** Parameter \f$\epsilon\f$ of a %SVM optimization problem (SVM::EPS_SVR). Default value is 0. */ - CV_PROP_RW double p; - - /** Optional weights in the SVM::C_SVC problem , assigned to particular classes. They are - multiplied by C so the parameter C of class \#i becomes classWeights(i) \* C. Thus these - weights affect the misclassification penalty for different classes. The larger weight, the - larger penalty on misclassification of data from the corresponding class. Default value is - empty Mat.*/ - CV_PROP_RW Mat classWeights; - /** Termination criteria of the iterative %SVM training procedure which solves a partial - case of constrained quadratic optimization problem. You can specify tolerance and/or the - maximum number of iterations. Default value is TermCriteria( - TermCriteria::MAX_ITER + TermCriteria::EPS, 1000, FLT_EPSILON );*/ - CV_PROP_RW TermCriteria termCrit; - }; class CV_EXPORTS Kernel : public Algorithm { @@ -600,6 +516,59 @@ public: virtual void calc( int vcount, int n, const float* vecs, const float* another, float* results ) = 0; }; + /** Type of a %SVM formulation. + See SVM::Types. Default value is SVM::C_SVC. */ + CV_PURE_PROPERTY(int, Type) + + /** Parameter \f$\gamma\f$ of a kernel function. + For SVM::POLY, SVM::RBF, SVM::SIGMOID or SVM::CHI2. Default value is 1. */ + CV_PURE_PROPERTY(double, Gamma) + + /** Parameter _coef0_ of a kernel function. + For SVM::POLY or SVM::SIGMOID. Default value is 0.*/ + CV_PURE_PROPERTY(double, Coef0) + + /** Parameter _degree_ of a kernel function. + For SVM::POLY. Default value is 0. */ + CV_PURE_PROPERTY(double, Degree) + + /** Parameter _C_ of a %SVM optimization problem. + For SVM::C_SVC, SVM::EPS_SVR or SVM::NU_SVR. Default value is 0. */ + CV_PURE_PROPERTY(double, C) + + /** Parameter \f$\nu\f$ of a %SVM optimization problem. + For SVM::NU_SVC, SVM::ONE_CLASS or SVM::NU_SVR. Default value is 0. */ + CV_PURE_PROPERTY(double, Nu) + + /** Parameter \f$\epsilon\f$ of a %SVM optimization problem. + For SVM::EPS_SVR. Default value is 0. */ + CV_PURE_PROPERTY(double, P) + + /** Optional weights in the SVM::C_SVC problem, assigned to particular classes. + They are multiplied by _C_ so the parameter _C_ of class _i_ becomes `classWeights(i) * C`. Thus + these weights affect the misclassification penalty for different classes. The larger weight, + the larger penalty on misclassification of data from the corresponding class. Default value is + empty Mat. */ + CV_PURE_PROPERTY_S(cv::Mat, ClassWeights) + + /** Termination criteria of the iterative %SVM training procedure which solves a partial + case of constrained quadratic optimization problem. + You can specify tolerance and/or the maximum number of iterations. Default value is + `TermCriteria( TermCriteria::MAX_ITER + TermCriteria::EPS, 1000, FLT_EPSILON )`; */ + CV_PURE_PROPERTY_S(cv::TermCriteria, TermCriteria) + + /** Type of a %SVM kernel. + See SVM::KernelTypes. Default value is SVM::RBF. */ + virtual int getKernelType() const = 0; + + /** Initialize with one of predefined kernels. + See SVM::KernelTypes. */ + virtual void setKernel(int kernelType) = 0; + + /** Initialize with custom kernel. + See SVM::Kernel class for implementation details */ + virtual void setCustomKernel(const Ptr &_kernel) = 0; + //! %SVM type enum Types { /** C-Support Vector Classification. n-class classification (n \f$\geq\f$ 2), allows @@ -631,6 +600,7 @@ public: ![image](pics/SVM_Comparison.png) */ enum KernelTypes { + /** Returned by SVM::getKernelType in case when custom kernel has been set */ CUSTOM=-1, /** Linear kernel. No mapping is done, linear discrimination (or regression) is done in the original feature space. It is the fastest option. \f$K(x_i, x_j) = x_i^T x_j\f$. */ @@ -678,13 +648,13 @@ public: to such proportion in the whole train dataset. The method trains the %SVM model automatically by choosing the optimal parameters C, gamma, p, - nu, coef0, degree from SVM::Params. Parameters are considered optimal when the cross-validation + nu, coef0, degree. Parameters are considered optimal when the cross-validation estimate of the test set error is minimal. If there is no need to optimize a parameter, the corresponding grid step should be set to any value less than or equal to 1. For example, to avoid optimization in gamma, set `gammaGrid.step = 0`, `gammaGrid.minVal`, `gamma_grid.maxVal` as arbitrary numbers. In this case, the value - `params.gamma` is taken for gamma. + `Gamma` is taken for gamma. And, finally, if the optimization in a parameter is required but the corresponding grid is unknown, you may call the function SVM::getDefaultGrid. To generate a grid, for example, for @@ -710,16 +680,6 @@ public: */ CV_WRAP virtual Mat getSupportVectors() const = 0; - virtual void setParams(const Params& p, const Ptr& customKernel=Ptr()) = 0; - - /** @brief Returns the current %SVM parameters. - - This function may be used to get the optimal parameters obtained while automatically training - SVM::trainAuto. - */ - virtual Params getParams() const = 0; - virtual Ptr getKernel() const = 0; - /** @brief Retrieves the decision function @param i the index of the decision function. If the problem solved is regression, 1-class or @@ -740,28 +700,17 @@ public: /** @brief Generates a grid for %SVM parameters. @param param_id %SVM parameters IDs that must be one of the SVM::ParamTypes. The grid is - generated for the parameter with this ID. + generated for the parameter with this ID. The function generates a grid for the specified parameter of the %SVM algorithm. The grid may be passed to the function SVM::trainAuto. */ static ParamGrid getDefaultGrid( int param_id ); - /** @brief Creates empty model - - @param p %SVM parameters - @param customKernel the optional custom kernel to use. It must implement SVM::Kernel interface. - - Use StatModel::train to train the model: - @code - StatModel::train(traindata, params); // to create and train the model - // or - StatModel::load(filename); // to load the pre-trained model. - @endcode - Since %SVM has several parameters, you may want to find the best parameters for your problem. It - can be done with SVM::trainAuto. - */ - static Ptr create(const Params& p=Params(), const Ptr& customKernel=Ptr()); + /** Creates empty model. + Use StatModel::train to train the model. Since %SVM has several parameters, you may want to + find the best parameters for your problem, it can be done with SVM::trainAuto. */ + static Ptr create(); }; /****************************************************************************************\ @@ -802,34 +751,22 @@ public: //! The initial step enum {START_E_STEP=1, START_M_STEP=2, START_AUTO_STEP=0}; - /** @brief The class describes %EM training parameters. - */ - class CV_EXPORTS_W_MAP Params - { - public: - /** @brief The constructor - - @param nclusters The number of mixture components in the Gaussian mixture model. Default - value of the parameter is EM::DEFAULT_NCLUSTERS=5. Some of %EM implementation could - determine the optimal number of mixtures within a specified value range, but that is not - the case in ML yet. - @param covMatType Constraint on covariance matrices which defines type of matrices. See - EM::Types. - @param termCrit The termination criteria of the %EM algorithm. The %EM algorithm can be - terminated by the number of iterations termCrit.maxCount (number of M-steps) or when - relative change of likelihood logarithm is less than termCrit.epsilon. Default maximum - number of iterations is EM::DEFAULT_MAX_ITERS=100. - */ - explicit Params(int nclusters=DEFAULT_NCLUSTERS, int covMatType=EM::COV_MAT_DIAGONAL, - const TermCriteria& termCrit=TermCriteria(TermCriteria::COUNT+TermCriteria::EPS, - EM::DEFAULT_MAX_ITERS, 1e-6)); - CV_PROP_RW int nclusters; - CV_PROP_RW int covMatType; - CV_PROP_RW TermCriteria termCrit; - }; + /** The number of mixture components in the Gaussian mixture model. + Default value of the parameter is EM::DEFAULT_NCLUSTERS=5. Some of %EM implementation could + determine the optimal number of mixtures within a specified value range, but that is not the + case in ML yet. */ + CV_PURE_PROPERTY(int, ClustersNumber) + + /** Constraint on covariance matrices which defines type of matrices. + See EM::Types. */ + CV_PURE_PROPERTY(int, CovarianceMatrixType) + + /** The termination criteria of the %EM algorithm. + The %EM algorithm can be terminated by the number of iterations termCrit.maxCount (number of + M-steps) or when relative change of likelihood logarithm is less than termCrit.epsilon. Default + maximum number of iterations is EM::DEFAULT_MAX_ITERS=100. */ + CV_PURE_PROPERTY_S(TermCriteria, TermCriteria) - virtual void setParams(const Params& p) = 0; - virtual Params getParams() const = 0; /** @brief Returns weights of the mixtures Returns vector with the number of elements equal to the number of mixtures. @@ -862,9 +799,7 @@ public: */ CV_WRAP virtual Vec2d predict2(InputArray sample, OutputArray probs) const = 0; - virtual bool train( const Ptr& trainData, int flags=0 ) = 0; - - /** @brief Static method that estimate the Gaussian mixture parameters from a samples set + /** @brief Estimate the Gaussian mixture parameters from a samples set. This variation starts with Expectation step. Initial values of the model parameters will be estimated by the k-means algorithm. @@ -891,15 +826,13 @@ public: @param probs The optional output matrix that contains posterior probabilities of each Gaussian mixture component given the each sample. It has \f$nsamples \times nclusters\f$ size and CV_64FC1 type. - @param params The Gaussian mixture params, see EM::Params description */ - static Ptr train(InputArray samples, - OutputArray logLikelihoods=noArray(), - OutputArray labels=noArray(), - OutputArray probs=noArray(), - const Params& params=Params()); + virtual bool trainEM(InputArray samples, + OutputArray logLikelihoods=noArray(), + OutputArray labels=noArray(), + OutputArray probs=noArray()) = 0; - /** @brief Static method that estimate the Gaussian mixture parameters from a samples set + /** @brief Estimate the Gaussian mixture parameters from a samples set. This variation starts with Expectation step. You need to provide initial means \f$a_k\f$ of mixture components. Optionally you can pass initial weights \f$\pi_k\f$ and covariance matrices @@ -925,17 +858,15 @@ public: @param probs The optional output matrix that contains posterior probabilities of each Gaussian mixture component given the each sample. It has \f$nsamples \times nclusters\f$ size and CV_64FC1 type. - @param params The Gaussian mixture params, see EM::Params description */ - static Ptr train_startWithE(InputArray samples, InputArray means0, - InputArray covs0=noArray(), - InputArray weights0=noArray(), - OutputArray logLikelihoods=noArray(), - OutputArray labels=noArray(), - OutputArray probs=noArray(), - const Params& params=Params()); + virtual bool trainE(InputArray samples, InputArray means0, + InputArray covs0=noArray(), + InputArray weights0=noArray(), + OutputArray logLikelihoods=noArray(), + OutputArray labels=noArray(), + OutputArray probs=noArray()) = 0; - /** @brief Static method that estimate the Gaussian mixture parameters from a samples set + /** @brief Estimate the Gaussian mixture parameters from a samples set. This variation starts with Maximization step. You need to provide initial probabilities \f$p_{i,k}\f$ to use this option. @@ -952,22 +883,17 @@ public: @param probs The optional output matrix that contains posterior probabilities of each Gaussian mixture component given the each sample. It has \f$nsamples \times nclusters\f$ size and CV_64FC1 type. - @param params The Gaussian mixture params, see EM::Params description */ - static Ptr train_startWithM(InputArray samples, InputArray probs0, - OutputArray logLikelihoods=noArray(), - OutputArray labels=noArray(), - OutputArray probs=noArray(), - const Params& params=Params()); - - /** @brief Creates empty %EM model - - @param params %EM parameters + virtual bool trainM(InputArray samples, InputArray probs0, + OutputArray logLikelihoods=noArray(), + OutputArray labels=noArray(), + OutputArray probs=noArray()) = 0; + /** Creates empty %EM model. The model should be trained then using StatModel::train(traindata, flags) method. Alternatively, you can use one of the EM::train\* methods or load it from file using StatModel::load\(filename). */ - static Ptr create(const Params& params=Params()); + static Ptr create(); }; /****************************************************************************************\ @@ -989,85 +915,74 @@ public: /** Predict options */ enum Flags { PREDICT_AUTO=0, PREDICT_SUM=(1<<8), PREDICT_MAX_VOTE=(2<<8), PREDICT_MASK=(3<<8) }; - /** @brief The structure contains all the decision tree training parameters. - - You can initialize it by default constructor and then override any parameters directly before - training, or the structure may be fully initialized using the advanced variant of the - constructor. - */ - class CV_EXPORTS_W_MAP Params - { - public: - /** @brief Default constructor. */ - Params(); - /** @brief Constructor with parameters */ - Params( int maxDepth, int minSampleCount, - double regressionAccuracy, bool useSurrogates, - int maxCategories, int CVFolds, - bool use1SERule, bool truncatePrunedTree, - const Mat& priors ); - - /** @brief Cluster possible values of a categorical variable into K\<=maxCategories clusters - to find a suboptimal split. - - If a discrete variable, on which the training procedure tries to make a split, takes more - than maxCategories values, the precise best subset estimation may take a very long time - because the algorithm is exponential. Instead, many decision trees engines (including our - implementation) try to find sub-optimal split in this case by clustering all the samples - into maxCategories clusters that is some categories are merged together. The clustering is - applied only in n \> 2-class classification problems for categorical variables with N \> - max_categories possible values. In case of regression and 2-class classification the optimal - split can be found efficiently without employing clustering, thus the parameter is not used - in these cases. Default value is 10.*/ - CV_PROP_RW int maxCategories; - /** @brief The maximum possible depth of the tree. - - That is the training algorithms attempts to split a node while its depth is less than - maxDepth. The root node has zero depth. The actual depth may be smaller if the other - termination criteria are met (see the outline of the training procedure @ref ml_intro_trees - "here"), and/or if the tree is pruned. Default value is INT_MAX.*/ - CV_PROP_RW int maxDepth; - /** If the number of samples in a node is less than this parameter then the node will not be - split. Default value is 10.*/ - CV_PROP_RW int minSampleCount; - /** If CVFolds \> 1 then algorithms prunes the built decision tree using K-fold - cross-validation procedure where K is equal to CVFolds. Default value is 10.*/ - CV_PROP_RW int CVFolds; - /** @brief If true then surrogate splits will be built. - - These splits allow to work with missing data and compute variable importance correctly. - @note currently it's not implemented. Default value is false.*/ - CV_PROP_RW bool useSurrogates; - /** If true then a pruning will be harsher. This will make a tree more compact and more - resistant to the training data noise but a bit less accurate. Default value is true.*/ - CV_PROP_RW bool use1SERule; - /** If true then pruned branches are physically removed from the tree. Otherwise they are - retained and it is possible to get results from the original unpruned (or pruned less - aggressively) tree. Default value is true.*/ - CV_PROP_RW bool truncatePrunedTree; - /** @brief Termination criteria for regression trees. - - If all absolute differences between an estimated value in a node and values of train samples - in this node are less than this parameter then the node will not be split further. Default - value is 0.01f*/ - CV_PROP_RW float regressionAccuracy; - /** @brief The array of a priori class probabilities, sorted by the class label value. - - The parameter can be used to tune the decision tree preferences toward a certain class. For - example, if you want to detect some rare anomaly occurrence, the training base will likely - contain much more normal cases than anomalies, so a very good classification performance - will be achieved just by considering every case as normal. To avoid this, the priors can be - specified, where the anomaly probability is artificially increased (up to 0.5 or even - greater), so the weight of the misclassified anomalies becomes much bigger, and the tree is - adjusted properly. - - You can also think about this parameter as weights of prediction categories which determine - relative weights that you give to misclassification. That is, if the weight of the first - category is 1 and the weight of the second category is 10, then each mistake in predicting - the second category is equivalent to making 10 mistakes in predicting the first category. - Default value is empty Mat.*/ - CV_PROP_RW Mat priors; - }; + /** Cluster possible values of a categorical variable into K\<=maxCategories clusters to + find a suboptimal split. + If a discrete variable, on which the training procedure tries to make a split, takes more than + maxCategories values, the precise best subset estimation may take a very long time because the + algorithm is exponential. Instead, many decision trees engines (including our implementation) + try to find sub-optimal split in this case by clustering all the samples into maxCategories + clusters that is some categories are merged together. The clustering is applied only in n \> + 2-class classification problems for categorical variables with N \> max_categories possible + values. In case of regression and 2-class classification the optimal split can be found + efficiently without employing clustering, thus the parameter is not used in these cases. + Default value is 10.*/ + CV_PURE_PROPERTY(int, MaxCategories) + + /** The maximum possible depth of the tree. + That is the training algorithms attempts to split a node while its depth is less than maxDepth. + The root node has zero depth. The actual depth may be smaller if the other termination criteria + are met (see the outline of the training procedure @ref ml_intro_trees "here"), and/or if the + tree is pruned. Default value is INT_MAX.*/ + CV_PURE_PROPERTY(int, MaxDepth) + + /** If the number of samples in a node is less than this parameter then the node will not be split. + + Default value is 10.*/ + CV_PURE_PROPERTY(int, MinSampleCount) + + /** If CVFolds \> 1 then algorithms prunes the built decision tree using K-fold + cross-validation procedure where K is equal to CVFolds. + Default value is 10.*/ + CV_PURE_PROPERTY(int, CVFolds) + + /** If true then surrogate splits will be built. + These splits allow to work with missing data and compute variable importance correctly. + Default value is false. + @note currently it's not implemented.*/ + CV_PURE_PROPERTY(bool, UseSurrogates) + + /** If true then a pruning will be harsher. + This will make a tree more compact and more resistant to the training data noise but a bit less + accurate. Default value is true.*/ + CV_PURE_PROPERTY(bool, Use1SERule) + + /** If true then pruned branches are physically removed from the tree. + Otherwise they are retained and it is possible to get results from the original unpruned (or + pruned less aggressively) tree. Default value is true.*/ + CV_PURE_PROPERTY(bool, TruncatePrunedTree) + + /** Termination criteria for regression trees. + If all absolute differences between an estimated value in a node and values of train samples + in this node are less than this parameter then the node will not be split further. Default + value is 0.01f*/ + CV_PURE_PROPERTY(float, RegressionAccuracy) + + /** @brief The array of a priori class probabilities, sorted by the class label value. + + The parameter can be used to tune the decision tree preferences toward a certain class. For + example, if you want to detect some rare anomaly occurrence, the training base will likely + contain much more normal cases than anomalies, so a very good classification performance + will be achieved just by considering every case as normal. To avoid this, the priors can be + specified, where the anomaly probability is artificially increased (up to 0.5 or even + greater), so the weight of the misclassified anomalies becomes much bigger, and the tree is + adjusted properly. + + You can also think about this parameter as weights of prediction categories which determine + relative weights that you give to misclassification. That is, if the weight of the first + category is 1 and the weight of the second category is 10, then each mistake in predicting + the second category is equivalent to making 10 mistakes in predicting the first category. + Default value is empty Mat.*/ + CV_PURE_PROPERTY_S(cv::Mat, Priors) /** @brief The class represents a decision tree node. */ @@ -1114,13 +1029,6 @@ public: @endcode */ }; - /** @brief Sets the training parameters - */ - virtual void setDParams(const Params& p); - /** @brief Returns the training parameters - */ - virtual Params getDParams() const; - /** @brief Returns indices of root nodes */ virtual const std::vector& getRoots() const = 0; @@ -1146,7 +1054,7 @@ public: trained using train method (see StatModel::train). Alternatively, you can load the model from file using StatModel::load\(filename). */ - static Ptr create(const Params& params=Params()); + static Ptr create(); }; /****************************************************************************************\ @@ -1160,58 +1068,38 @@ public: class CV_EXPORTS_W RTrees : public DTrees { public: - /** @brief The set of training parameters for the forest is a superset of the training - parameters for a single tree. - - However, random trees do not need all the functionality/features of decision trees. Most - noticeably, the trees are not pruned, so the cross-validation parameters are not used. - */ - class CV_EXPORTS_W_MAP Params : public DTrees::Params - { - public: - /** @brief Default constructor. */ - Params(); - /** @brief Constructor with parameters. */ - Params( int maxDepth, int minSampleCount, - double regressionAccuracy, bool useSurrogates, - int maxCategories, const Mat& priors, - bool calcVarImportance, int nactiveVars, - TermCriteria termCrit ); - - /** If true then variable importance will be calculated and then it can be retrieved by - RTrees::getVarImportance. Default value is false.*/ - CV_PROP_RW bool calcVarImportance; - /** The size of the randomly selected subset of features at each tree node and that are used - to find the best split(s). If you set it to 0 then the size will be set to the square root - of the total number of features. Default value is 0.*/ - CV_PROP_RW int nactiveVars; - /** The termination criteria that specifies when the training algorithm stops - either when - the specified number of trees is trained and added to the ensemble or when sufficient - accuracy (measured as OOB error) is achieved. Typically the more trees you have the better - the accuracy. However, the improvement in accuracy generally diminishes and asymptotes pass - a certain number of trees. Also to keep in mind, the number of tree increases the prediction - time linearly. Default value is TermCriteria(TermCriteria::MAX_ITERS + TermCriteria::EPS, - 50, 0.1)*/ - CV_PROP_RW TermCriteria termCrit; - }; - - virtual void setRParams(const Params& p) = 0; - virtual Params getRParams() const = 0; - - /** @brief Returns the variable importance array. + /** If true then variable importance will be calculated and then it can be retrieved by RTrees::getVarImportance. + Default value is false.*/ + CV_PURE_PROPERTY(bool, CalculateVarImportance) + + /** The size of the randomly selected subset of features at each tree node and that are used + to find the best split(s). + If you set it to 0 then the size will be set to the square root of the total number of + features. Default value is 0.*/ + CV_PURE_PROPERTY(int, ActiveVarCount) + + /** The termination criteria that specifies when the training algorithm stops. + Either when the specified number of trees is trained and added to the ensemble or when + sufficient accuracy (measured as OOB error) is achieved. Typically the more trees you have the + better the accuracy. However, the improvement in accuracy generally diminishes and asymptotes + pass a certain number of trees. Also to keep in mind, the number of tree increases the + prediction time linearly. Default value is TermCriteria(TermCriteria::MAX_ITERS + + TermCriteria::EPS, 50, 0.1)*/ + CV_PURE_PROPERTY_S(TermCriteria, TermCriteria) + + /** Returns the variable importance array. The method returns the variable importance vector, computed at the training stage when - Params::calcVarImportance is set to true. If this flag was set to false, the empty matrix is + CalculateVarImportance is set to true. If this flag was set to false, the empty matrix is returned. */ virtual Mat getVarImportance() const = 0; - /** @brief Creates the empty model - + /** Creates the empty model. Use StatModel::train to train the model, StatModel::train to create and train the model, StatModel::load to load the pre-trained model. */ - static Ptr create(const Params& params=Params()); + static Ptr create(); }; /****************************************************************************************\ @@ -1225,36 +1113,21 @@ public: class CV_EXPORTS_W Boost : public DTrees { public: - /** @brief Parameters of Boost trees. + /** Type of the boosting algorithm. + See Boost::Types. Default value is Boost::REAL. */ + CV_PURE_PROPERTY(int, BoostType) - The structure is derived from DTrees::Params but not all of the decision tree parameters are - supported. In particular, cross-validation is not supported. + /** The number of weak classifiers. + Default value is 100. */ + CV_PURE_PROPERTY(int, WeakCount) - All parameters are public. You can initialize them by a constructor and then override some of - them directly if you want. - */ - class CV_EXPORTS_W_MAP Params : public DTrees::Params - { - public: - CV_PROP_RW int boostType; //!< Type of the boosting algorithm. See Boost::Types. - //!< Default value is Boost::REAL. - CV_PROP_RW int weakCount; //!< The number of weak classifiers. Default value is 100. - /** A threshold between 0 and 1 used to save computational time. Samples with summary weight - \f$\leq 1 - weight_trim_rate\f$ do not participate in the *next* iteration of training. Set - this parameter to 0 to turn off this functionality. Default value is 0.95.*/ - CV_PROP_RW double weightTrimRate; - - /** @brief Default constructor */ - Params(); - /** @brief Constructor with parameters */ - Params( int boostType, int weakCount, double weightTrimRate, - int maxDepth, bool useSurrogates, const Mat& priors ); - }; - - /** @brief Boosting type + /** A threshold between 0 and 1 used to save computational time. + Samples with summary weight \f$\leq 1 - weight_trim_rate\f$ do not participate in the *next* + iteration of training. Set this parameter to 0 to turn off this functionality. Default value is 0.95.*/ + CV_PURE_PROPERTY(double, WeightTrimRate) - Gentle AdaBoost and Real AdaBoost are often the preferable choices. - */ + /** Boosting type. + Gentle AdaBoost and Real AdaBoost are often the preferable choices. */ enum Types { DISCRETE=0, //!< Discrete AdaBoost. REAL=1, //!< Real AdaBoost. It is a technique that utilizes confidence-rated predictions @@ -1264,17 +1137,9 @@ public: //!(traindata, params) to create - and train the model, StatModel::load\(filename) to load the pre-trained model. - */ - static Ptr create(const Params& params=Params()); + /** Creates the empty model. + Use StatModel::train to train the model, StatModel::load\(filename) to load the pre-trained model. */ + static Ptr create(); }; /****************************************************************************************\ @@ -1327,68 +1192,78 @@ Additional flags for StatModel::train are available: ANN_MLP::TrainFlags. class CV_EXPORTS_W ANN_MLP : public StatModel { public: - /** @brief Parameters of the MLP and of the training algorithm. - */ - struct CV_EXPORTS_W_MAP Params - { - /** @brief Default constructor */ - Params(); - /** @brief Constructor with parameters - @note param1 sets Params::rp_dw0 for RPROP and Paramss::bp_dw_scale for BACKPROP. - @note param2 sets Params::rp_dw_min for RPROP and Params::bp_moment_scale for BACKPROP. - */ - Params( const Mat& layerSizes, int activateFunc, double fparam1, double fparam2, - TermCriteria termCrit, int trainMethod, double param1, double param2=0 ); - - /** Available training methods */ - enum TrainingMethods { - BACKPROP=0, //!< The back-propagation algorithm. - RPROP=1 //!< The RPROP algorithm. See @cite RPROP93 for details. - }; - - /** Integer vector specifying the number of neurons in each layer including the input and - output layers. The very first element specifies the number of elements in the input layer. - The last element - number of elements in the output layer. Default value is empty Mat.*/ - CV_PROP_RW Mat layerSizes; - /** The activation function for each neuron. Currently the default and the only fully - supported activation function is ANN_MLP::SIGMOID_SYM. See ANN_MLP::ActivationFunctions.*/ - CV_PROP_RW int activateFunc; - /** The first parameter of the activation function, \f$\alpha\f$. Default value is 0. */ - CV_PROP_RW double fparam1; - /** The second parameter of the activation function, \f$\beta\f$. Default value is 0. */ - CV_PROP_RW double fparam2; - - /** Termination criteria of the training algorithm. You can specify the maximum number of - iterations (maxCount) and/or how much the error could change between the iterations to make - the algorithm continue (epsilon). Default value is TermCriteria(TermCriteria::MAX_ITER + - TermCriteria::EPS, 1000, 0.01).*/ - CV_PROP_RW TermCriteria termCrit; - /** Training method. Default value is Params::RPROP. See ANN_MLP::Params::TrainingMethods.*/ - CV_PROP_RW int trainMethod; - - // backpropagation parameters - /** BPROP: Strength of the weight gradient term. The recommended value is about 0.1. Default - value is 0.1.*/ - CV_PROP_RW double bpDWScale; - /** BPROP: Strength of the momentum term (the difference between weights on the 2 previous - iterations). This parameter provides some inertia to smooth the random fluctuations of the - weights. It can vary from 0 (the feature is disabled) to 1 and beyond. The value 0.1 or so - is good enough. Default value is 0.1.*/ - CV_PROP_RW double bpMomentScale; - - // rprop parameters - /** RPROP: Initial value \f$\Delta_0\f$ of update-values \f$\Delta_{ij}\f$. Default value is 0.1.*/ - CV_PROP_RW double rpDW0; - /** RPROP: Increase factor \f$\eta^+\f$. It must be \>1. Default value is 1.2.*/ - CV_PROP_RW double rpDWPlus; - /** RPROP: Decrease factor \f$\eta^-\f$. It must be \<1. Default value is 0.5.*/ - CV_PROP_RW double rpDWMinus; - /** RPROP: Update-values lower limit \f$\Delta_{min}\f$. It must be positive. Default value is FLT_EPSILON.*/ - CV_PROP_RW double rpDWMin; - /** RPROP: Update-values upper limit \f$\Delta_{max}\f$. It must be \>1. Default value is 50.*/ - CV_PROP_RW double rpDWMax; + /** Available training methods */ + enum TrainingMethods { + BACKPROP=0, //!< The back-propagation algorithm. + RPROP=1 //!< The RPROP algorithm. See @cite RPROP93 for details. }; + /** Sets training method and common parameters. + @param method Default value is ANN_MLP::RPROP. See ANN_MLP::TrainingMethods. + @param param1 passed to setRpropDW0 for ANN_MLP::RPROP and to setBackpropWeightScale for ANN_MLP::BACKPROP + @param param2 passed to setRpropDWMin for ANN_MLP::RPROP and to setBackpropMomentumScale for ANN_MLP::BACKPROP. + */ + virtual void setTrainMethod(int method, double param1 = 0, double param2 = 0) = 0; + + /** Returns current training method */ + virtual int getTrainMethod() const = 0; + + /** Initialize the activation function for each neuron. + Currently the default and the only fully supported activation function is ANN_MLP::SIGMOID_SYM. + @param type The type of activation function. See ANN_MLP::ActivationFunctions. + @param param1 The first parameter of the activation function, \f$\alpha\f$. Default value is 0. + @param param2 The second parameter of the activation function, \f$\beta\f$. Default value is 0. + */ + virtual void setActivationFunction(int type, double param1 = 0, double param2 = 0) = 0; + + /** Integer vector specifying the number of neurons in each layer including the input and output layers. + The very first element specifies the number of elements in the input layer. + The last element - number of elements in the output layer. Default value is empty Mat. + @sa getLayerSizes */ + virtual void setLayerSizes(InputArray _layer_sizes) = 0; + + /** Integer vector specifying the number of neurons in each layer including the input and output layers. + The very first element specifies the number of elements in the input layer. + The last element - number of elements in the output layer. + @sa setLayerSizes */ + virtual cv::Mat getLayerSizes() const = 0; + + /** Termination criteria of the training algorithm. + You can specify the maximum number of iterations (maxCount) and/or how much the error could + change between the iterations to make the algorithm continue (epsilon). Default value is + TermCriteria(TermCriteria::MAX_ITER + TermCriteria::EPS, 1000, 0.01).*/ + CV_PURE_PROPERTY(TermCriteria, TermCriteria) + + /** BPROP: Strength of the weight gradient term. + The recommended value is about 0.1. Default value is 0.1.*/ + CV_PURE_PROPERTY(double, BackpropWeightScale) + + /** BPROP: Strength of the momentum term (the difference between weights on the 2 previous iterations). + This parameter provides some inertia to smooth the random fluctuations of the weights. It can + vary from 0 (the feature is disabled) to 1 and beyond. The value 0.1 or so is good enough. + Default value is 0.1.*/ + CV_PURE_PROPERTY(double, BackpropMomentumScale) + + /** RPROP: Initial value \f$\Delta_0\f$ of update-values \f$\Delta_{ij}\f$. + Default value is 0.1.*/ + CV_PURE_PROPERTY(double, RpropDW0) + + /** RPROP: Increase factor \f$\eta^+\f$. + It must be \>1. Default value is 1.2.*/ + CV_PURE_PROPERTY(double, RpropDWPlus) + + /** RPROP: Decrease factor \f$\eta^-\f$. + It must be \<1. Default value is 0.5.*/ + CV_PURE_PROPERTY(double, RpropDWMinus) + + /** RPROP: Update-values lower limit \f$\Delta_{min}\f$. + It must be positive. Default value is FLT_EPSILON.*/ + CV_PURE_PROPERTY(double, RpropDWMin) + + /** RPROP: Update-values upper limit \f$\Delta_{max}\f$. + It must be \>1. Default value is 50.*/ + CV_PURE_PROPERTY(double, RpropDWMax) + /** possible activation functions */ enum ActivationFunctions { /** Identity function: \f$f(x)=x\f$ */ @@ -1422,19 +1297,12 @@ public: virtual Mat getWeights(int layerIdx) const = 0; - /** @brief Sets the new network parameters */ - virtual void setParams(const Params& p) = 0; - - /** @brief Retrieves the current network parameters */ - virtual Params getParams() const = 0; - /** @brief Creates empty model - Use StatModel::train to train the model, StatModel::train\(traindata, params) to - create and train the model, StatModel::load\(filename) to load the pre-trained model. + Use StatModel::train to train the model, StatModel::load\(filename) to load the pre-trained model. Note that the train method has optional flags: ANN_MLP::TrainFlags. */ - static Ptr create(const Params& params=Params()); + static Ptr create(); }; /****************************************************************************************\ @@ -1448,43 +1316,38 @@ public: class CV_EXPORTS LogisticRegression : public StatModel { public: - class CV_EXPORTS Params - { - public: - /** @brief Constructor */ - Params(double learning_rate = 0.001, - int iters = 1000, - int method = LogisticRegression::BATCH, - int normalization = LogisticRegression::REG_L2, - int reg = 1, - int batch_size = 1); - double alpha; //!< learning rate. - int num_iters; //!< number of iterations. - /** Kind of regularization to be applied. See LogisticRegression::RegKinds. */ - int norm; - /** Enable or disable regularization. Set to positive integer (greater than zero) to enable - and to 0 to disable. */ - int regularized; - /** Kind of training method used. See LogisticRegression::Methods. */ - int train_method; - /** Specifies the number of training samples taken in each step of Mini-Batch Gradient - Descent. Will only be used if using LogisticRegression::MINI_BATCH training algorithm. It - has to take values less than the total number of training samples. */ - int mini_batch_size; - /** Termination criteria of the algorithm */ - TermCriteria term_crit; - }; + + /** Learning rate. */ + CV_PURE_PROPERTY(double, LearningRate) + + /** Number of iterations. */ + CV_PURE_PROPERTY(int, Iterations) + + /** Kind of regularization to be applied. See LogisticRegression::RegKinds. */ + CV_PURE_PROPERTY(int, Regularization) + + /** Kind of training method used. See LogisticRegression::Methods. */ + CV_PURE_PROPERTY(int, TrainMethod) + + /** Specifies the number of training samples taken in each step of Mini-Batch Gradient + Descent. Will only be used if using LogisticRegression::MINI_BATCH training algorithm. It + has to take values less than the total number of training samples. */ + CV_PURE_PROPERTY(int, MiniBatchSize) + + /** Termination criteria of the algorithm. */ + CV_PURE_PROPERTY(TermCriteria, TermCriteria) //! Regularization kinds enum RegKinds { + REG_NONE = -1, //!< Regularization disabled REG_L1 = 0, //!< %L1 norm - REG_L2 = 1 //!< %L2 norm. Set Params::regularized \> 0 when using this kind + REG_L2 = 1 //!< %L2 norm }; //! Training methods enum Methods { BATCH = 0, - MINI_BATCH = 1 //!< Set Params::mini_batch_size to a positive integer when using this method. + MINI_BATCH = 1 //!< Set MiniBatchSize to a positive integer when using this method. }; /** @brief Predicts responses for input samples and returns a float type. @@ -1505,11 +1368,9 @@ public: /** @brief Creates empty model. - @param params The training parameters for the classifier of type LogisticRegression::Params. - Creates Logistic Regression model with parameters given. */ - static Ptr create( const Params& params = Params() ); + static Ptr create(); }; /****************************************************************************************\ diff --git a/modules/ml/src/ann_mlp.cpp b/modules/ml/src/ann_mlp.cpp index 3bc173513b..2b29519cef 100644 --- a/modules/ml/src/ann_mlp.cpp +++ b/modules/ml/src/ann_mlp.cpp @@ -42,84 +42,57 @@ namespace cv { namespace ml { -ANN_MLP::Params::Params() +struct AnnParams { - layerSizes = Mat(); - activateFunc = SIGMOID_SYM; - fparam1 = fparam2 = 0; - termCrit = TermCriteria( TermCriteria::COUNT + TermCriteria::EPS, 1000, 0.01 ); - trainMethod = RPROP; - bpDWScale = bpMomentScale = 0.1; - rpDW0 = 0.1; rpDWPlus = 1.2; rpDWMinus = 0.5; - rpDWMin = FLT_EPSILON; rpDWMax = 50.; -} + AnnParams() + { + termCrit = TermCriteria( TermCriteria::COUNT + TermCriteria::EPS, 1000, 0.01 ); + trainMethod = ANN_MLP::RPROP; + bpDWScale = bpMomentScale = 0.1; + rpDW0 = 0.1; rpDWPlus = 1.2; rpDWMinus = 0.5; + rpDWMin = FLT_EPSILON; rpDWMax = 50.; + } + + TermCriteria termCrit; + int trainMethod; + + double bpDWScale; + double bpMomentScale; + double rpDW0; + double rpDWPlus; + double rpDWMinus; + double rpDWMin; + double rpDWMax; +}; -ANN_MLP::Params::Params( const Mat& _layerSizes, int _activateFunc, double _fparam1, double _fparam2, - TermCriteria _termCrit, int _trainMethod, double _param1, double _param2 ) +template +inline T inBounds(T val, T min_val, T max_val) { - layerSizes = _layerSizes; - activateFunc = _activateFunc; - fparam1 = _fparam1; - fparam2 = _fparam2; - termCrit = _termCrit; - trainMethod = _trainMethod; - bpDWScale = bpMomentScale = 0.1; - rpDW0 = 1.; rpDWPlus = 1.2; rpDWMinus = 0.5; - rpDWMin = FLT_EPSILON; rpDWMax = 50.; - - if( trainMethod == RPROP ) - { - rpDW0 = _param1; - if( rpDW0 < FLT_EPSILON ) - rpDW0 = 1.; - rpDWMin = _param2; - rpDWMin = std::max( rpDWMin, 0. ); - } - else if( trainMethod == BACKPROP ) - { - bpDWScale = _param1; - if( bpDWScale <= 0 ) - bpDWScale = 0.1; - bpDWScale = std::max( bpDWScale, 1e-3 ); - bpDWScale = std::min( bpDWScale, 1. ); - bpMomentScale = _param2; - if( bpMomentScale < 0 ) - bpMomentScale = 0.1; - bpMomentScale = std::min( bpMomentScale, 1. ); - } - else - trainMethod = RPROP; + return std::min(std::max(val, min_val), max_val); } - class ANN_MLPImpl : public ANN_MLP { public: ANN_MLPImpl() { clear(); - } - - ANN_MLPImpl( const Params& p ) - { - clear(); - setParams(p); + setActivationFunction( SIGMOID_SYM, 0, 0 ); + setLayerSizes(Mat()); + setTrainMethod(ANN_MLP::RPROP, 0.1, FLT_EPSILON); } virtual ~ANN_MLPImpl() {} - void setParams(const Params& p) - { - params = p; - create( params.layerSizes ); - set_activ_func( params.activateFunc, params.fparam1, params.fparam2 ); - } - - Params getParams() const - { - return params; - } + CV_IMPL_PROPERTY(TermCriteria, TermCriteria, params.termCrit) + CV_IMPL_PROPERTY(double, BackpropWeightScale, params.bpDWScale) + CV_IMPL_PROPERTY(double, BackpropMomentumScale, params.bpMomentScale) + CV_IMPL_PROPERTY(double, RpropDW0, params.rpDW0) + CV_IMPL_PROPERTY(double, RpropDWPlus, params.rpDWPlus) + CV_IMPL_PROPERTY(double, RpropDWMinus, params.rpDWMinus) + CV_IMPL_PROPERTY(double, RpropDWMin, params.rpDWMin) + CV_IMPL_PROPERTY(double, RpropDWMax, params.rpDWMax) void clear() { @@ -132,7 +105,35 @@ public: int layer_count() const { return (int)layer_sizes.size(); } - void set_activ_func( int _activ_func, double _f_param1, double _f_param2 ) + void setTrainMethod(int method, double param1, double param2) + { + if (method != ANN_MLP::RPROP && method != ANN_MLP::BACKPROP) + method = ANN_MLP::RPROP; + params.trainMethod = method; + if(method == ANN_MLP::RPROP ) + { + if( param1 < FLT_EPSILON ) + param1 = 1.; + params.rpDW0 = param1; + params.rpDWMin = std::max( param2, 0. ); + } + else if(method == ANN_MLP::BACKPROP ) + { + if( param1 <= 0 ) + param1 = 0.1; + params.bpDWScale = inBounds(param1, 1e-3, 1.); + if( param2 < 0 ) + param2 = 0.1; + params.bpMomentScale = std::min( param2, 1. ); + } + } + + int getTrainMethod() const + { + return params.trainMethod; + } + + void setActivationFunction(int _activ_func, double _f_param1, double _f_param2 ) { if( _activ_func < 0 || _activ_func > GAUSSIAN ) CV_Error( CV_StsOutOfRange, "Unknown activation function" ); @@ -201,7 +202,12 @@ public: } } - void create( InputArray _layer_sizes ) + Mat getLayerSizes() const + { + return Mat_(layer_sizes, true); + } + + void setLayerSizes( InputArray _layer_sizes ) { clear(); @@ -700,7 +706,7 @@ public: termcrit.maxCount = std::max((params.termCrit.type & CV_TERMCRIT_ITER ? params.termCrit.maxCount : MAX_ITER), 1); termcrit.epsilon = std::max((params.termCrit.type & CV_TERMCRIT_EPS ? params.termCrit.epsilon : DEFAULT_EPSILON), DBL_EPSILON); - int iter = params.trainMethod == Params::BACKPROP ? + int iter = params.trainMethod == ANN_MLP::BACKPROP ? train_backprop( inputs, outputs, sw, termcrit ) : train_rprop( inputs, outputs, sw, termcrit ); @@ -1113,13 +1119,13 @@ public: fs << "min_val" << min_val << "max_val" << max_val << "min_val1" << min_val1 << "max_val1" << max_val1; fs << "training_params" << "{"; - if( params.trainMethod == Params::BACKPROP ) + if( params.trainMethod == ANN_MLP::BACKPROP ) { fs << "train_method" << "BACKPROP"; fs << "dw_scale" << params.bpDWScale; fs << "moment_scale" << params.bpMomentScale; } - else if( params.trainMethod == Params::RPROP ) + else if( params.trainMethod == ANN_MLP::RPROP ) { fs << "train_method" << "RPROP"; fs << "dw0" << params.rpDW0; @@ -1186,7 +1192,7 @@ public: f_param1 = (double)fn["f_param1"]; f_param2 = (double)fn["f_param2"]; - set_activ_func( activ_func, f_param1, f_param2 ); + setActivationFunction( activ_func, f_param1, f_param2 ); min_val = (double)fn["min_val"]; max_val = (double)fn["max_val"]; @@ -1194,7 +1200,7 @@ public: max_val1 = (double)fn["max_val1"]; FileNode tpn = fn["training_params"]; - params = Params(); + params = AnnParams(); if( !tpn.empty() ) { @@ -1202,13 +1208,13 @@ public: if( tmethod_name == "BACKPROP" ) { - params.trainMethod = Params::BACKPROP; + params.trainMethod = ANN_MLP::BACKPROP; params.bpDWScale = (double)tpn["dw_scale"]; params.bpMomentScale = (double)tpn["moment_scale"]; } else if( tmethod_name == "RPROP" ) { - params.trainMethod = Params::RPROP; + params.trainMethod = ANN_MLP::RPROP; params.rpDW0 = (double)tpn["dw0"]; params.rpDWPlus = (double)tpn["dw_plus"]; params.rpDWMinus = (double)tpn["dw_minus"]; @@ -1244,7 +1250,7 @@ public: vector _layer_sizes; readVectorOrMat(fn["layer_sizes"], _layer_sizes); - create( _layer_sizes ); + setLayerSizes( _layer_sizes ); int i, l_count = layer_count(); read_params(fn); @@ -1267,11 +1273,6 @@ public: trained = true; } - Mat getLayerSizes() const - { - return Mat_(layer_sizes, true); - } - Mat getWeights(int layerIdx) const { CV_Assert( 0 <= layerIdx && layerIdx < (int)weights.size() ); @@ -1304,17 +1305,16 @@ public: double min_val, max_val, min_val1, max_val1; int activ_func; int max_lsize, max_buf_sz; - Params params; + AnnParams params; RNG rng; Mutex mtx; bool trained; }; -Ptr ANN_MLP::create(const ANN_MLP::Params& params) +Ptr ANN_MLP::create() { - Ptr ann = makePtr(params); - return ann; + return makePtr(); } }} diff --git a/modules/ml/src/boost.cpp b/modules/ml/src/boost.cpp index 236cd97a2d..5694ff1051 100644 --- a/modules/ml/src/boost.cpp +++ b/modules/ml/src/boost.cpp @@ -54,47 +54,32 @@ log_ratio( double val ) } -Boost::Params::Params() +BoostTreeParams::BoostTreeParams() { boostType = Boost::REAL; weakCount = 100; weightTrimRate = 0.95; - CVFolds = 0; - maxDepth = 1; } - -Boost::Params::Params( int _boostType, int _weak_count, - double _weightTrimRate, int _maxDepth, - bool _use_surrogates, const Mat& _priors ) +BoostTreeParams::BoostTreeParams( int _boostType, int _weak_count, + double _weightTrimRate) { boostType = _boostType; weakCount = _weak_count; weightTrimRate = _weightTrimRate; - CVFolds = 0; - maxDepth = _maxDepth; - useSurrogates = _use_surrogates; - priors = _priors; } - class DTreesImplForBoost : public DTreesImpl { public: - DTreesImplForBoost() {} - virtual ~DTreesImplForBoost() {} - - bool isClassifier() const { return true; } - - void setBParams(const Boost::Params& p) + DTreesImplForBoost() { - bparams = p; + params.setCVFolds(0); + params.setMaxDepth(1); } + virtual ~DTreesImplForBoost() {} - Boost::Params getBParams() const - { - return bparams; - } + bool isClassifier() const { return true; } void clear() { @@ -199,10 +184,6 @@ public: bool train( const Ptr& trainData, int flags ) { - Params dp(bparams.maxDepth, bparams.minSampleCount, bparams.regressionAccuracy, - bparams.useSurrogates, bparams.maxCategories, 0, - false, false, bparams.priors); - setDParams(dp); startTraining(trainData, flags); int treeidx, ntrees = bparams.weakCount >= 0 ? bparams.weakCount : 10000; vector sidx = w->sidx; @@ -426,12 +407,6 @@ public: void readParams( const FileNode& fn ) { DTreesImpl::readParams(fn); - bparams.maxDepth = params0.maxDepth; - bparams.minSampleCount = params0.minSampleCount; - bparams.regressionAccuracy = params0.regressionAccuracy; - bparams.useSurrogates = params0.useSurrogates; - bparams.maxCategories = params0.maxCategories; - bparams.priors = params0.priors; FileNode tparams_node = fn["training_params"]; // check for old layout @@ -465,7 +440,7 @@ public: } } - Boost::Params bparams; + BoostTreeParams bparams; vector sumResult; }; @@ -476,6 +451,20 @@ public: BoostImpl() {} virtual ~BoostImpl() {} + CV_IMPL_PROPERTY(int, BoostType, impl.bparams.boostType) + CV_IMPL_PROPERTY(int, WeakCount, impl.bparams.weakCount) + CV_IMPL_PROPERTY(double, WeightTrimRate, impl.bparams.weightTrimRate) + + CV_WRAP_SAME_PROPERTY(int, MaxCategories, impl.params) + CV_WRAP_SAME_PROPERTY(int, MaxDepth, impl.params) + CV_WRAP_SAME_PROPERTY(int, MinSampleCount, impl.params) + CV_WRAP_SAME_PROPERTY(int, CVFolds, impl.params) + CV_WRAP_SAME_PROPERTY(bool, UseSurrogates, impl.params) + CV_WRAP_SAME_PROPERTY(bool, Use1SERule, impl.params) + CV_WRAP_SAME_PROPERTY(bool, TruncatePrunedTree, impl.params) + CV_WRAP_SAME_PROPERTY(float, RegressionAccuracy, impl.params) + CV_WRAP_SAME_PROPERTY_S(cv::Mat, Priors, impl.params) + String getDefaultModelName() const { return "opencv_ml_boost"; } bool train( const Ptr& trainData, int flags ) @@ -498,9 +487,6 @@ public: impl.read(fn); } - void setBParams(const Params& p) { impl.setBParams(p); } - Params getBParams() const { return impl.getBParams(); } - int getVarCount() const { return impl.getVarCount(); } bool isTrained() const { return impl.isTrained(); } @@ -515,11 +501,9 @@ public: }; -Ptr Boost::create(const Params& params) +Ptr Boost::create() { - Ptr p = makePtr(); - p->setBParams(params); - return p; + return makePtr(); } }} diff --git a/modules/ml/src/em.cpp b/modules/ml/src/em.cpp index 351ca39fc7..c84be84b9c 100644 --- a/modules/ml/src/em.cpp +++ b/modules/ml/src/em.cpp @@ -48,37 +48,49 @@ namespace ml const double minEigenValue = DBL_EPSILON; -EM::Params::Params(int _nclusters, int _covMatType, const TermCriteria& _termCrit) -{ - nclusters = _nclusters; - covMatType = _covMatType; - termCrit = _termCrit; -} - class CV_EXPORTS EMImpl : public EM { public: - EMImpl(const Params& _params) + + int nclusters; + int covMatType; + TermCriteria termCrit; + + CV_IMPL_PROPERTY_S(TermCriteria, TermCriteria, termCrit) + + void setClustersNumber(int val) { - setParams(_params); + nclusters = val; + CV_Assert(nclusters > 1); } - virtual ~EMImpl() {} + int getClustersNumber() const + { + return nclusters; + } - void setParams(const Params& _params) + void setCovarianceMatrixType(int val) { - params = _params; - CV_Assert(params.nclusters > 1); - CV_Assert(params.covMatType == COV_MAT_SPHERICAL || - params.covMatType == COV_MAT_DIAGONAL || - params.covMatType == COV_MAT_GENERIC); + covMatType = val; + CV_Assert(covMatType == COV_MAT_SPHERICAL || + covMatType == COV_MAT_DIAGONAL || + covMatType == COV_MAT_GENERIC); } - Params getParams() const + int getCovarianceMatrixType() const { - return params; + return covMatType; } + EMImpl() + { + nclusters = DEFAULT_NCLUSTERS; + covMatType=EM::COV_MAT_DIAGONAL; + termCrit = TermCriteria(TermCriteria::COUNT+TermCriteria::EPS, EM::DEFAULT_MAX_ITERS, 1e-6); + } + + virtual ~EMImpl() {} + void clear() { trainSamples.release(); @@ -100,10 +112,10 @@ public: bool train(const Ptr& data, int) { Mat samples = data->getTrainSamples(), labels; - return train_(samples, labels, noArray(), noArray()); + return trainEM(samples, labels, noArray(), noArray()); } - bool train_(InputArray samples, + bool trainEM(InputArray samples, OutputArray logLikelihoods, OutputArray labels, OutputArray probs) @@ -157,7 +169,7 @@ public: { if( _outputs.fixedType() ) ptype = _outputs.type(); - _outputs.create(samples.rows, params.nclusters, ptype); + _outputs.create(samples.rows, nclusters, ptype); } else nsamples = std::min(nsamples, 1); @@ -193,7 +205,7 @@ public: { if( _probs.fixedType() ) ptype = _probs.type(); - _probs.create(1, params.nclusters, ptype); + _probs.create(1, nclusters, ptype); probs = _probs.getMat(); } @@ -311,7 +323,6 @@ public: const std::vector* covs0, const Mat* weights0) { - int nclusters = params.nclusters, covMatType = params.covMatType; clear(); checkTrainData(startStep, samples, nclusters, covMatType, probs0, means0, covs0, weights0); @@ -350,7 +361,6 @@ public: void decomposeCovs() { - int nclusters = params.nclusters, covMatType = params.covMatType; CV_Assert(!covs.empty()); covsEigenValues.resize(nclusters); if(covMatType == COV_MAT_GENERIC) @@ -383,7 +393,6 @@ public: void clusterTrainSamples() { - int nclusters = params.nclusters; int nsamples = trainSamples.rows; // Cluster samples, compute/update means @@ -443,7 +452,6 @@ public: void computeLogWeightDivDet() { - int nclusters = params.nclusters; CV_Assert(!covsEigenValues.empty()); Mat logWeights; @@ -458,7 +466,7 @@ public: double logDetCov = 0.; const int evalCount = static_cast(covsEigenValues[clusterIndex].total()); for(int di = 0; di < evalCount; di++) - logDetCov += std::log(covsEigenValues[clusterIndex].at(params.covMatType != COV_MAT_SPHERICAL ? di : 0)); + logDetCov += std::log(covsEigenValues[clusterIndex].at(covMatType != COV_MAT_SPHERICAL ? di : 0)); logWeightDivDet.at(clusterIndex) = logWeights.at(clusterIndex) - 0.5 * logDetCov; } @@ -466,7 +474,6 @@ public: bool doTrain(int startStep, OutputArray logLikelihoods, OutputArray labels, OutputArray probs) { - int nclusters = params.nclusters; int dim = trainSamples.cols; // Precompute the empty initial train data in the cases of START_E_STEP and START_AUTO_STEP if(startStep != START_M_STEP) @@ -488,9 +495,9 @@ public: mStep(); double trainLogLikelihood, prevTrainLogLikelihood = 0.; - int maxIters = (params.termCrit.type & TermCriteria::MAX_ITER) ? - params.termCrit.maxCount : DEFAULT_MAX_ITERS; - double epsilon = (params.termCrit.type & TermCriteria::EPS) ? params.termCrit.epsilon : 0.; + int maxIters = (termCrit.type & TermCriteria::MAX_ITER) ? + termCrit.maxCount : DEFAULT_MAX_ITERS; + double epsilon = (termCrit.type & TermCriteria::EPS) ? termCrit.epsilon : 0.; for(int iter = 0; ; iter++) { @@ -521,12 +528,12 @@ public: covs.resize(nclusters); for(int clusterIndex = 0; clusterIndex < nclusters; clusterIndex++) { - if(params.covMatType == COV_MAT_SPHERICAL) + if(covMatType == COV_MAT_SPHERICAL) { covs[clusterIndex].create(dim, dim, CV_64FC1); setIdentity(covs[clusterIndex], Scalar(covsEigenValues[clusterIndex].at(0))); } - else if(params.covMatType == COV_MAT_DIAGONAL) + else if(covMatType == COV_MAT_DIAGONAL) { covs[clusterIndex] = Mat::diag(covsEigenValues[clusterIndex]); } @@ -555,7 +562,6 @@ public: // see Alex Smola's blog http://blog.smola.org/page/2 for // details on the log-sum-exp trick - int nclusters = params.nclusters, covMatType = params.covMatType; int stype = sample.type(); CV_Assert(!means.empty()); CV_Assert((stype == CV_32F || stype == CV_64F) && (ptype == CV_32F || ptype == CV_64F)); @@ -621,7 +627,7 @@ public: void eStep() { // Compute probs_ik from means_k, covs_k and weights_k. - trainProbs.create(trainSamples.rows, params.nclusters, CV_64FC1); + trainProbs.create(trainSamples.rows, nclusters, CV_64FC1); trainLabels.create(trainSamples.rows, 1, CV_32SC1); trainLogLikelihoods.create(trainSamples.rows, 1, CV_64FC1); @@ -642,8 +648,6 @@ public: void mStep() { // Update means_k, covs_k and weights_k from probs_ik - int nclusters = params.nclusters; - int covMatType = params.covMatType; int dim = trainSamples.cols; // Update weights @@ -755,12 +759,12 @@ public: void write_params(FileStorage& fs) const { - fs << "nclusters" << params.nclusters; - fs << "cov_mat_type" << (params.covMatType == COV_MAT_SPHERICAL ? String("spherical") : - params.covMatType == COV_MAT_DIAGONAL ? String("diagonal") : - params.covMatType == COV_MAT_GENERIC ? String("generic") : - format("unknown_%d", params.covMatType)); - writeTermCrit(fs, params.termCrit); + fs << "nclusters" << nclusters; + fs << "cov_mat_type" << (covMatType == COV_MAT_SPHERICAL ? String("spherical") : + covMatType == COV_MAT_DIAGONAL ? String("diagonal") : + covMatType == COV_MAT_GENERIC ? String("generic") : + format("unknown_%d", covMatType)); + writeTermCrit(fs, termCrit); } void write(FileStorage& fs) const @@ -781,15 +785,13 @@ public: void read_params(const FileNode& fn) { - Params _params; - _params.nclusters = (int)fn["nclusters"]; + nclusters = (int)fn["nclusters"]; String s = (String)fn["cov_mat_type"]; - _params.covMatType = s == "spherical" ? COV_MAT_SPHERICAL : + covMatType = s == "spherical" ? COV_MAT_SPHERICAL : s == "diagonal" ? COV_MAT_DIAGONAL : s == "generic" ? COV_MAT_GENERIC : -1; - CV_Assert(_params.covMatType >= 0); - _params.termCrit = readTermCrit(fn); - setParams(_params); + CV_Assert(covMatType >= 0); + termCrit = readTermCrit(fn); } void read(const FileNode& fn) @@ -820,8 +822,6 @@ public: std::copy(covs.begin(), covs.end(), _covs.begin()); } - Params params; - // all inner matrices have type CV_64FC1 Mat trainSamples; Mat trainProbs; @@ -838,41 +838,9 @@ public: Mat logWeightDivDet; }; - -Ptr EM::train(InputArray samples, OutputArray logLikelihoods, - OutputArray labels, OutputArray probs, - const EM::Params& params) -{ - Ptr em = makePtr(params); - if(!em->train_(samples, logLikelihoods, labels, probs)) - em.release(); - return em; -} - -Ptr EM::train_startWithE(InputArray samples, InputArray means0, - InputArray covs0, InputArray weights0, - OutputArray logLikelihoods, OutputArray labels, - OutputArray probs, const EM::Params& params) -{ - Ptr em = makePtr(params); - if(!em->trainE(samples, means0, covs0, weights0, logLikelihoods, labels, probs)) - em.release(); - return em; -} - -Ptr EM::train_startWithM(InputArray samples, InputArray probs0, - OutputArray logLikelihoods, OutputArray labels, - OutputArray probs, const EM::Params& params) -{ - Ptr em = makePtr(params); - if(!em->trainM(samples, probs0, logLikelihoods, labels, probs)) - em.release(); - return em; -} - -Ptr EM::create(const Params& params) +Ptr EM::create() { - return makePtr(params); + return makePtr(); } } diff --git a/modules/ml/src/knearest.cpp b/modules/ml/src/knearest.cpp index 4bf40758f2..70e178e6e2 100644 --- a/modules/ml/src/knearest.cpp +++ b/modules/ml/src/knearest.cpp @@ -50,46 +50,33 @@ namespace cv { namespace ml { -KNearest::Params::Params(int k, bool isclassifier_, int Emax_, int algorithmType_) : - defaultK(k), - isclassifier(isclassifier_), - Emax(Emax_), - algorithmType(algorithmType_) -{ -} +const String NAME_BRUTE_FORCE = "opencv_ml_knn"; +const String NAME_KDTREE = "opencv_ml_knn_kd"; -class KNearestImpl : public KNearest +class Impl { public: - KNearestImpl(const Params& p) - { - params = p; - } - - virtual ~KNearestImpl() {} - - Params getParams() const { return params; } - void setParams(const Params& p) { params = p; } - - bool isClassifier() const { return params.isclassifier; } - bool isTrained() const { return !samples.empty(); } - - String getDefaultModelName() const { return "opencv_ml_knn"; } - - void clear() + Impl() { - samples.release(); - responses.release(); + defaultK = 10; + isclassifier = true; + Emax = INT_MAX; } - int getVarCount() const { return samples.cols; } + virtual ~Impl() {} + virtual String getModelName() const = 0; + virtual int getType() const = 0; + virtual float findNearest( InputArray _samples, int k, + OutputArray _results, + OutputArray _neighborResponses, + OutputArray _dists ) const = 0; bool train( const Ptr& data, int flags ) { Mat new_samples = data->getTrainSamples(ROW_SAMPLE); Mat new_responses; data->getTrainResponses().convertTo(new_responses, CV_32F); - bool update = (flags & UPDATE_MODEL) != 0 && !samples.empty(); + bool update = (flags & ml::KNearest::UPDATE_MODEL) != 0 && !samples.empty(); CV_Assert( new_samples.type() == CV_32F ); @@ -106,9 +93,53 @@ public: samples.push_back(new_samples); responses.push_back(new_responses); + doTrain(samples); + return true; } + virtual void doTrain(InputArray points) { (void)points; } + + void clear() + { + samples.release(); + responses.release(); + } + + void read( const FileNode& fn ) + { + clear(); + isclassifier = (int)fn["is_classifier"] != 0; + defaultK = (int)fn["default_k"]; + + fn["samples"] >> samples; + fn["responses"] >> responses; + } + + void write( FileStorage& fs ) const + { + fs << "is_classifier" << (int)isclassifier; + fs << "default_k" << defaultK; + + fs << "samples" << samples; + fs << "responses" << responses; + } + +public: + int defaultK; + bool isclassifier; + int Emax; + + Mat samples; + Mat responses; +}; + +class BruteForceImpl : public Impl +{ +public: + String getModelName() const { return NAME_BRUTE_FORCE; } + int getType() const { return ml::KNearest::BRUTE_FORCE; } + void findNearestCore( const Mat& _samples, int k0, const Range& range, Mat* results, Mat* neighbor_responses, Mat* dists, float* presult ) const @@ -199,7 +230,7 @@ public: if( results || testidx+range.start == 0 ) { - if( !params.isclassifier || k == 1 ) + if( !isclassifier || k == 1 ) { float s = 0.f; for( j = 0; j < k; j++ ) @@ -251,7 +282,7 @@ public: struct findKNearestInvoker : public ParallelLoopBody { - findKNearestInvoker(const KNearestImpl* _p, int _k, const Mat& __samples, + findKNearestInvoker(const BruteForceImpl* _p, int _k, const Mat& __samples, Mat* __results, Mat* __neighbor_responses, Mat* __dists, float* _presult) { p = _p; @@ -273,7 +304,7 @@ public: } } - const KNearestImpl* p; + const BruteForceImpl* p; int k; const Mat* _samples; Mat* _results; @@ -324,88 +355,18 @@ public: //invoker(Range(0, testcount)); return result; } - - float predict(InputArray inputs, OutputArray outputs, int) const - { - return findNearest( inputs, params.defaultK, outputs, noArray(), noArray() ); - } - - void write( FileStorage& fs ) const - { - fs << "is_classifier" << (int)params.isclassifier; - fs << "default_k" << params.defaultK; - - fs << "samples" << samples; - fs << "responses" << responses; - } - - void read( const FileNode& fn ) - { - clear(); - params.isclassifier = (int)fn["is_classifier"] != 0; - params.defaultK = (int)fn["default_k"]; - - fn["samples"] >> samples; - fn["responses"] >> responses; - } - - Mat samples; - Mat responses; - Params params; }; -class KNearestKDTreeImpl : public KNearest +class KDTreeImpl : public Impl { public: - KNearestKDTreeImpl(const Params& p) - { - params = p; - } - - virtual ~KNearestKDTreeImpl() {} - - Params getParams() const { return params; } - void setParams(const Params& p) { params = p; } - - bool isClassifier() const { return params.isclassifier; } - bool isTrained() const { return !samples.empty(); } + String getModelName() const { return NAME_KDTREE; } + int getType() const { return ml::KNearest::KDTREE; } - String getDefaultModelName() const { return "opencv_ml_knn_kd"; } - - void clear() - { - samples.release(); - responses.release(); - } - - int getVarCount() const { return samples.cols; } - - bool train( const Ptr& data, int flags ) + void doTrain(InputArray points) { - Mat new_samples = data->getTrainSamples(ROW_SAMPLE); - Mat new_responses; - data->getTrainResponses().convertTo(new_responses, CV_32F); - bool update = (flags & UPDATE_MODEL) != 0 && !samples.empty(); - - CV_Assert( new_samples.type() == CV_32F ); - - if( !update ) - { - clear(); - } - else - { - CV_Assert( new_samples.cols == samples.cols && - new_responses.cols == responses.cols ); - } - - samples.push_back(new_samples); - responses.push_back(new_responses); - - tr.build(samples); - - return true; + tr.build(points); } float findNearest( InputArray _samples, int k, @@ -460,51 +421,97 @@ public: { _d = d.row(i); } - tr.findNearest(test_samples.row(i), k, params.Emax, _res, _nr, _d, noArray()); + tr.findNearest(test_samples.row(i), k, Emax, _res, _nr, _d, noArray()); } return result; // currently always 0 } - float predict(InputArray inputs, OutputArray outputs, int) const + KDTree tr; +}; + +//================================================================ + +class KNearestImpl : public KNearest +{ + CV_IMPL_PROPERTY(int, DefaultK, impl->defaultK) + CV_IMPL_PROPERTY(bool, IsClassifier, impl->isclassifier) + CV_IMPL_PROPERTY(int, Emax, impl->Emax) + +public: + int getAlgorithmType() const + { + return impl->getType(); + } + void setAlgorithmType(int val) { - return findNearest( inputs, params.defaultK, outputs, noArray(), noArray() ); + if (val != BRUTE_FORCE && val != KDTREE) + val = BRUTE_FORCE; + initImpl(val); } - void write( FileStorage& fs ) const +public: + KNearestImpl() + { + initImpl(BRUTE_FORCE); + } + ~KNearestImpl() { - fs << "is_classifier" << (int)params.isclassifier; - fs << "default_k" << params.defaultK; + } - fs << "samples" << samples; - fs << "responses" << responses; + bool isClassifier() const { return impl->isclassifier; } + bool isTrained() const { return !impl->samples.empty(); } + + int getVarCount() const { return impl->samples.cols; } + + void write( FileStorage& fs ) const + { + impl->write(fs); } void read( const FileNode& fn ) { - clear(); - params.isclassifier = (int)fn["is_classifier"] != 0; - params.defaultK = (int)fn["default_k"]; + int algorithmType = BRUTE_FORCE; + if (fn.name() == NAME_KDTREE) + algorithmType = KDTREE; + initImpl(algorithmType); + impl->read(fn); + } - fn["samples"] >> samples; - fn["responses"] >> responses; + float findNearest( InputArray samples, int k, + OutputArray results, + OutputArray neighborResponses=noArray(), + OutputArray dist=noArray() ) const + { + return impl->findNearest(samples, k, results, neighborResponses, dist); } - KDTree tr; + float predict(InputArray inputs, OutputArray outputs, int) const + { + return impl->findNearest( inputs, impl->defaultK, outputs, noArray(), noArray() ); + } - Mat samples; - Mat responses; - Params params; -}; + bool train( const Ptr& data, int flags ) + { + return impl->train(data, flags); + } -Ptr KNearest::create(const Params& p) -{ - if (KDTREE==p.algorithmType) + String getDefaultModelName() const { return impl->getModelName(); } + +protected: + void initImpl(int algorithmType) { - return makePtr(p); + if (algorithmType != KDTREE) + impl = makePtr(); + else + impl = makePtr(); } + Ptr impl; +}; - return makePtr(p); +Ptr KNearest::create() +{ + return makePtr(); } } diff --git a/modules/ml/src/lr.cpp b/modules/ml/src/lr.cpp index 2cff9003c4..e621009981 100644 --- a/modules/ml/src/lr.cpp +++ b/modules/ml/src/lr.cpp @@ -60,31 +60,41 @@ using namespace std; namespace cv { namespace ml { -LogisticRegression::Params::Params(double learning_rate, - int iters, - int method, - int normlization, - int reg, - int batch_size) +class LrParams { - alpha = learning_rate; - num_iters = iters; - norm = normlization; - regularized = reg; - train_method = method; - mini_batch_size = batch_size; - term_crit = TermCriteria(TermCriteria::COUNT + TermCriteria::EPS, num_iters, alpha); -} +public: + LrParams() + { + alpha = 0.001; + num_iters = 1000; + norm = LogisticRegression::REG_L2; + train_method = LogisticRegression::BATCH; + mini_batch_size = 1; + term_crit = TermCriteria(TermCriteria::COUNT + TermCriteria::EPS, num_iters, alpha); + } + + double alpha; //!< learning rate. + int num_iters; //!< number of iterations. + int norm; + int train_method; + int mini_batch_size; + TermCriteria term_crit; +}; class LogisticRegressionImpl : public LogisticRegression { public: - LogisticRegressionImpl(const Params& pms) - : params(pms) - { - } + + LogisticRegressionImpl() { } virtual ~LogisticRegressionImpl() {} + CV_IMPL_PROPERTY(double, LearningRate, params.alpha) + CV_IMPL_PROPERTY(int, Iterations, params.num_iters) + CV_IMPL_PROPERTY(int, Regularization, params.norm) + CV_IMPL_PROPERTY(int, TrainMethod, params.train_method) + CV_IMPL_PROPERTY(int, MiniBatchSize, params.mini_batch_size) + CV_IMPL_PROPERTY(TermCriteria, TermCriteria, params.term_crit) + virtual bool train( const Ptr& trainData, int=0 ); virtual float predict(InputArray samples, OutputArray results, int) const; virtual void clear(); @@ -103,7 +113,7 @@ protected: bool set_label_map(const Mat& _labels_i); Mat remap_labels(const Mat& _labels_i, const map& lmap) const; protected: - Params params; + LrParams params; Mat learnt_thetas; map forward_mapper; map reverse_mapper; @@ -111,9 +121,9 @@ protected: Mat labels_n; }; -Ptr LogisticRegression::create(const Params& params) +Ptr LogisticRegression::create() { - return makePtr(params); + return makePtr(); } bool LogisticRegressionImpl::train(const Ptr& trainData, int) @@ -312,7 +322,7 @@ double LogisticRegressionImpl::compute_cost(const Mat& _data, const Mat& _labels theta_b = _init_theta(Range(1, n), Range::all()); multiply(theta_b, theta_b, theta_c, 1); - if(this->params.regularized > 0) + if(params.norm != REG_NONE) { llambda = 1; } @@ -367,7 +377,7 @@ Mat LogisticRegressionImpl::compute_batch_gradient(const Mat& _data, const Mat& m = _data.rows; n = _data.cols; - if(this->params.regularized > 0) + if(params.norm != REG_NONE) { llambda = 1; } @@ -439,7 +449,7 @@ Mat LogisticRegressionImpl::compute_mini_batch_gradient(const Mat& _data, const Mat data_d; Mat labels_l; - if(this->params.regularized > 0) + if(params.norm != REG_NONE) { lambda_l = 1; } @@ -570,7 +580,6 @@ void LogisticRegressionImpl::write(FileStorage& fs) const fs<<"alpha"<params.alpha; fs<<"iterations"<params.num_iters; fs<<"norm"<params.norm; - fs<<"regularized"<params.regularized; fs<<"train_method"<params.train_method; if(this->params.train_method == LogisticRegression::MINI_BATCH) { @@ -592,7 +601,6 @@ void LogisticRegressionImpl::read(const FileNode& fn) this->params.alpha = (double)fn["alpha"]; this->params.num_iters = (int)fn["iterations"]; this->params.norm = (int)fn["norm"]; - this->params.regularized = (int)fn["regularized"]; this->params.train_method = (int)fn["train_method"]; if(this->params.train_method == LogisticRegression::MINI_BATCH) diff --git a/modules/ml/src/nbayes.cpp b/modules/ml/src/nbayes.cpp index 425e337398..9fc0d833ba 100644 --- a/modules/ml/src/nbayes.cpp +++ b/modules/ml/src/nbayes.cpp @@ -43,7 +43,6 @@ namespace cv { namespace ml { -NormalBayesClassifier::Params::Params() {} class NormalBayesClassifierImpl : public NormalBayesClassifier { @@ -53,9 +52,6 @@ public: nallvars = 0; } - void setParams(const Params&) {} - Params getParams() const { return Params(); } - bool train( const Ptr& trainData, int flags ) { const float min_variation = FLT_EPSILON; @@ -455,7 +451,7 @@ public: }; -Ptr NormalBayesClassifier::create(const Params&) +Ptr NormalBayesClassifier::create() { Ptr p = makePtr(); return p; diff --git a/modules/ml/src/precomp.hpp b/modules/ml/src/precomp.hpp index 69ff03047e..77700a05a2 100644 --- a/modules/ml/src/precomp.hpp +++ b/modules/ml/src/precomp.hpp @@ -120,6 +120,91 @@ namespace ml return termCrit; } + struct TreeParams + { + TreeParams(); + TreeParams( int maxDepth, int minSampleCount, + double regressionAccuracy, bool useSurrogates, + int maxCategories, int CVFolds, + bool use1SERule, bool truncatePrunedTree, + const Mat& priors ); + + inline void setMaxCategories(int val) + { + if( val < 2 ) + CV_Error( CV_StsOutOfRange, "max_categories should be >= 2" ); + maxCategories = std::min(val, 15 ); + } + inline void setMaxDepth(int val) + { + if( val < 0 ) + CV_Error( CV_StsOutOfRange, "max_depth should be >= 0" ); + maxDepth = std::min( val, 25 ); + } + inline void setMinSampleCount(int val) + { + minSampleCount = std::max(val, 1); + } + inline void setCVFolds(int val) + { + if( val < 0 ) + CV_Error( CV_StsOutOfRange, + "params.CVFolds should be =0 (the tree is not pruned) " + "or n>0 (tree is pruned using n-fold cross-validation)" ); + if( val == 1 ) + val = 0; + CVFolds = val; + } + inline void setRegressionAccuracy(float val) + { + if( val < 0 ) + CV_Error( CV_StsOutOfRange, "params.regression_accuracy should be >= 0" ); + regressionAccuracy = val; + } + + inline int getMaxCategories() const { return maxCategories; } + inline int getMaxDepth() const { return maxDepth; } + inline int getMinSampleCount() const { return minSampleCount; } + inline int getCVFolds() const { return CVFolds; } + inline float getRegressionAccuracy() const { return regressionAccuracy; } + + CV_IMPL_PROPERTY(bool, UseSurrogates, useSurrogates) + CV_IMPL_PROPERTY(bool, Use1SERule, use1SERule) + CV_IMPL_PROPERTY(bool, TruncatePrunedTree, truncatePrunedTree) + CV_IMPL_PROPERTY_S(cv::Mat, Priors, priors) + + public: + bool useSurrogates; + bool use1SERule; + bool truncatePrunedTree; + Mat priors; + + protected: + int maxCategories; + int maxDepth; + int minSampleCount; + int CVFolds; + float regressionAccuracy; + }; + + struct RTreeParams + { + RTreeParams(); + RTreeParams(bool calcVarImportance, int nactiveVars, TermCriteria termCrit ); + bool calcVarImportance; + int nactiveVars; + TermCriteria termCrit; + }; + + struct BoostTreeParams + { + BoostTreeParams(); + BoostTreeParams(int boostType, int weakCount, double weightTrimRate); + int boostType; + int weakCount; + double weightTrimRate; + }; + class DTreesImpl : public DTrees { public: @@ -191,6 +276,16 @@ namespace ml int maxSubsetSize; }; + CV_WRAP_SAME_PROPERTY(int, MaxCategories, params) + CV_WRAP_SAME_PROPERTY(int, MaxDepth, params) + CV_WRAP_SAME_PROPERTY(int, MinSampleCount, params) + CV_WRAP_SAME_PROPERTY(int, CVFolds, params) + CV_WRAP_SAME_PROPERTY(bool, UseSurrogates, params) + CV_WRAP_SAME_PROPERTY(bool, Use1SERule, params) + CV_WRAP_SAME_PROPERTY(bool, TruncatePrunedTree, params) + CV_WRAP_SAME_PROPERTY(float, RegressionAccuracy, params) + CV_WRAP_SAME_PROPERTY_S(cv::Mat, Priors, params) + DTreesImpl(); virtual ~DTreesImpl(); virtual void clear(); @@ -202,8 +297,7 @@ namespace ml int getCatCount(int vi) const { return catOfs[vi][1] - catOfs[vi][0]; } int getSubsetSize(int vi) const { return (getCatCount(vi) + 31)/32; } - virtual void setDParams(const Params& _params); - virtual Params getDParams() const; + virtual void setDParams(const TreeParams& _params); virtual void startTraining( const Ptr& trainData, int flags ); virtual void endTraining(); virtual void initCompVarIdx(); @@ -250,7 +344,7 @@ namespace ml virtual const std::vector& getSplits() const { return splits; } virtual const std::vector& getSubsets() const { return subsets; } - Params params0, params; + TreeParams params; vector varIdx; vector compVarIdx; diff --git a/modules/ml/src/rtrees.cpp b/modules/ml/src/rtrees.cpp index 7441faac17..f5e2b21bdb 100644 --- a/modules/ml/src/rtrees.cpp +++ b/modules/ml/src/rtrees.cpp @@ -48,21 +48,16 @@ namespace ml { ////////////////////////////////////////////////////////////////////////////////////////// // Random trees // ////////////////////////////////////////////////////////////////////////////////////////// -RTrees::Params::Params() - : DTrees::Params(5, 10, 0.f, false, 10, 0, false, false, Mat()) +RTreeParams::RTreeParams() { calcVarImportance = false; nactiveVars = 0; termCrit = TermCriteria(TermCriteria::EPS + TermCriteria::COUNT, 50, 0.1); } -RTrees::Params::Params( int _maxDepth, int _minSampleCount, - double _regressionAccuracy, bool _useSurrogates, - int _maxCategories, const Mat& _priors, - bool _calcVarImportance, int _nactiveVars, - TermCriteria _termCrit ) - : DTrees::Params(_maxDepth, _minSampleCount, _regressionAccuracy, _useSurrogates, - _maxCategories, 0, false, false, _priors) +RTreeParams::RTreeParams(bool _calcVarImportance, + int _nactiveVars, + TermCriteria _termCrit ) { calcVarImportance = _calcVarImportance; nactiveVars = _nactiveVars; @@ -73,18 +68,19 @@ RTrees::Params::Params( int _maxDepth, int _minSampleCount, class DTreesImplForRTrees : public DTreesImpl { public: - DTreesImplForRTrees() {} - virtual ~DTreesImplForRTrees() {} - - void setRParams(const RTrees::Params& p) - { - rparams = p; - } - - RTrees::Params getRParams() const + DTreesImplForRTrees() { - return rparams; + params.setMaxDepth(5); + params.setMinSampleCount(10); + params.setRegressionAccuracy(0.f); + params.useSurrogates = false; + params.setMaxCategories(10); + params.setCVFolds(0); + params.use1SERule = false; + params.truncatePrunedTree = false; + params.priors = Mat(); } + virtual ~DTreesImplForRTrees() {} void clear() { @@ -129,10 +125,6 @@ public: bool train( const Ptr& trainData, int flags ) { - Params dp(rparams.maxDepth, rparams.minSampleCount, rparams.regressionAccuracy, - rparams.useSurrogates, rparams.maxCategories, rparams.CVFolds, - rparams.use1SERule, rparams.truncatePrunedTree, rparams.priors); - setDParams(dp); startTraining(trainData, flags); int treeidx, ntrees = (rparams.termCrit.type & TermCriteria::COUNT) != 0 ? rparams.termCrit.maxCount : 10000; @@ -326,12 +318,6 @@ public: void readParams( const FileNode& fn ) { DTreesImpl::readParams(fn); - rparams.maxDepth = params0.maxDepth; - rparams.minSampleCount = params0.minSampleCount; - rparams.regressionAccuracy = params0.regressionAccuracy; - rparams.useSurrogates = params0.useSurrogates; - rparams.maxCategories = params0.maxCategories; - rparams.priors = params0.priors; FileNode tparams_node = fn["training_params"]; rparams.nactiveVars = (int)tparams_node["nactive_vars"]; @@ -361,7 +347,7 @@ public: } } - RTrees::Params rparams; + RTreeParams rparams; double oobError; vector varImportance; vector allVars, activeVars; @@ -372,6 +358,20 @@ public: class RTreesImpl : public RTrees { public: + CV_IMPL_PROPERTY(bool, CalculateVarImportance, impl.rparams.calcVarImportance) + CV_IMPL_PROPERTY(int, ActiveVarCount, impl.rparams.nactiveVars) + CV_IMPL_PROPERTY_S(TermCriteria, TermCriteria, impl.rparams.termCrit) + + CV_WRAP_SAME_PROPERTY(int, MaxCategories, impl.params) + CV_WRAP_SAME_PROPERTY(int, MaxDepth, impl.params) + CV_WRAP_SAME_PROPERTY(int, MinSampleCount, impl.params) + CV_WRAP_SAME_PROPERTY(int, CVFolds, impl.params) + CV_WRAP_SAME_PROPERTY(bool, UseSurrogates, impl.params) + CV_WRAP_SAME_PROPERTY(bool, Use1SERule, impl.params) + CV_WRAP_SAME_PROPERTY(bool, TruncatePrunedTree, impl.params) + CV_WRAP_SAME_PROPERTY(float, RegressionAccuracy, impl.params) + CV_WRAP_SAME_PROPERTY_S(cv::Mat, Priors, impl.params) + RTreesImpl() {} virtual ~RTreesImpl() {} @@ -397,9 +397,6 @@ public: impl.read(fn); } - void setRParams(const Params& p) { impl.setRParams(p); } - Params getRParams() const { return impl.getRParams(); } - Mat getVarImportance() const { return Mat_(impl.varImportance, true); } int getVarCount() const { return impl.getVarCount(); } @@ -415,11 +412,9 @@ public: }; -Ptr RTrees::create(const Params& params) +Ptr RTrees::create() { - Ptr p = makePtr(); - p->setRParams(params); - return p; + return makePtr(); } }} diff --git a/modules/ml/src/svm.cpp b/modules/ml/src/svm.cpp index a0df44f78b..8bed117639 100644 --- a/modules/ml/src/svm.cpp +++ b/modules/ml/src/svm.cpp @@ -103,54 +103,60 @@ static void checkParamGrid(const ParamGrid& pg) } // SVM training parameters -SVM::Params::Params() +struct SvmParams { - svmType = SVM::C_SVC; - kernelType = SVM::RBF; - degree = 0; - gamma = 1; - coef0 = 0; - C = 1; - nu = 0; - p = 0; - termCrit = TermCriteria( CV_TERMCRIT_ITER+CV_TERMCRIT_EPS, 1000, FLT_EPSILON ); -} + int svmType; + int kernelType; + double gamma; + double coef0; + double degree; + double C; + double nu; + double p; + Mat classWeights; + TermCriteria termCrit; + SvmParams() + { + svmType = SVM::C_SVC; + kernelType = SVM::RBF; + degree = 0; + gamma = 1; + coef0 = 0; + C = 1; + nu = 0; + p = 0; + termCrit = TermCriteria( CV_TERMCRIT_ITER+CV_TERMCRIT_EPS, 1000, FLT_EPSILON ); + } -SVM::Params::Params( int _svmType, int _kernelType, - double _degree, double _gamma, double _coef0, - double _Con, double _nu, double _p, - const Mat& _classWeights, TermCriteria _termCrit ) -{ - svmType = _svmType; - kernelType = _kernelType; - degree = _degree; - gamma = _gamma; - coef0 = _coef0; - C = _Con; - nu = _nu; - p = _p; - classWeights = _classWeights; - termCrit = _termCrit; -} + SvmParams( int _svmType, int _kernelType, + double _degree, double _gamma, double _coef0, + double _Con, double _nu, double _p, + const Mat& _classWeights, TermCriteria _termCrit ) + { + svmType = _svmType; + kernelType = _kernelType; + degree = _degree; + gamma = _gamma; + coef0 = _coef0; + C = _Con; + nu = _nu; + p = _p; + classWeights = _classWeights; + termCrit = _termCrit; + } + +}; /////////////////////////////////////// SVM kernel /////////////////////////////////////// class SVMKernelImpl : public SVM::Kernel { public: - SVMKernelImpl() - { - } - - SVMKernelImpl( const SVM::Params& _params ) + SVMKernelImpl( const SvmParams& _params = SvmParams() ) { params = _params; } - virtual ~SVMKernelImpl() - { - } - int getType() const { return params.kernelType; @@ -327,7 +333,7 @@ public: } } - SVM::Params params; + SvmParams params; }; @@ -1185,7 +1191,7 @@ public: int cache_size; int max_cache_size; Mat samples; - SVM::Params params; + SvmParams params; vector lru_cache; int lru_first; int lru_last; @@ -1215,6 +1221,7 @@ public: SVMImpl() { clear(); + checkParams(); } ~SVMImpl() @@ -1235,32 +1242,68 @@ public: return sv; } - void setParams( const Params& _params, const Ptr& _kernel ) + CV_IMPL_PROPERTY(int, Type, params.svmType) + CV_IMPL_PROPERTY(double, Gamma, params.gamma) + CV_IMPL_PROPERTY(double, Coef0, params.coef0) + CV_IMPL_PROPERTY(double, Degree, params.degree) + CV_IMPL_PROPERTY(double, C, params.C) + CV_IMPL_PROPERTY(double, Nu, params.nu) + CV_IMPL_PROPERTY(double, P, params.p) + CV_IMPL_PROPERTY_S(cv::Mat, ClassWeights, params.classWeights) + CV_IMPL_PROPERTY_S(cv::TermCriteria, TermCriteria, params.termCrit) + + int getKernelType() const { - params = _params; + return params.kernelType; + } - int kernelType = params.kernelType; - int svmType = params.svmType; + void setKernel(int kernelType) + { + params.kernelType = kernelType; + if (kernelType != CUSTOM) + kernel = makePtr(params); + } - if( kernelType != LINEAR && kernelType != POLY && - kernelType != SIGMOID && kernelType != RBF && - kernelType != INTER && kernelType != CHI2) - CV_Error( CV_StsBadArg, "Unknown/unsupported kernel type" ); + void setCustomKernel(const Ptr &_kernel) + { + params.kernelType = CUSTOM; + kernel = _kernel; + } - if( kernelType == LINEAR ) - params.gamma = 1; - else if( params.gamma <= 0 ) - CV_Error( CV_StsOutOfRange, "gamma parameter of the kernel must be positive" ); + void checkParams() + { + int kernelType = params.kernelType; + if (kernelType != CUSTOM) + { + if( kernelType != LINEAR && kernelType != POLY && + kernelType != SIGMOID && kernelType != RBF && + kernelType != INTER && kernelType != CHI2) + CV_Error( CV_StsBadArg, "Unknown/unsupported kernel type" ); + + if( kernelType == LINEAR ) + params.gamma = 1; + else if( params.gamma <= 0 ) + CV_Error( CV_StsOutOfRange, "gamma parameter of the kernel must be positive" ); + + if( kernelType != SIGMOID && kernelType != POLY ) + params.coef0 = 0; + else if( params.coef0 < 0 ) + CV_Error( CV_StsOutOfRange, "The kernel parameter must be positive or zero" ); + + if( kernelType != POLY ) + params.degree = 0; + else if( params.degree <= 0 ) + CV_Error( CV_StsOutOfRange, "The kernel parameter must be positive" ); - if( kernelType != SIGMOID && kernelType != POLY ) - params.coef0 = 0; - else if( params.coef0 < 0 ) - CV_Error( CV_StsOutOfRange, "The kernel parameter must be positive or zero" ); + kernel = makePtr(params); + } + else + { + if (!kernel) + CV_Error( CV_StsBadArg, "Custom kernel is not set" ); + } - if( kernelType != POLY ) - params.degree = 0; - else if( params.degree <= 0 ) - CV_Error( CV_StsOutOfRange, "The kernel parameter must be positive" ); + int svmType = params.svmType; if( svmType != C_SVC && svmType != NU_SVC && svmType != ONE_CLASS && svmType != EPS_SVR && @@ -1285,28 +1328,18 @@ public: if( svmType != C_SVC ) params.classWeights.release(); - termCrit = params.termCrit; - if( !(termCrit.type & TermCriteria::EPS) ) - termCrit.epsilon = DBL_EPSILON; - termCrit.epsilon = std::max(termCrit.epsilon, DBL_EPSILON); - if( !(termCrit.type & TermCriteria::COUNT) ) - termCrit.maxCount = INT_MAX; - termCrit.maxCount = std::max(termCrit.maxCount, 1); - - if( _kernel ) - kernel = _kernel; - else - kernel = makePtr(params); + if( !(params.termCrit.type & TermCriteria::EPS) ) + params.termCrit.epsilon = DBL_EPSILON; + params.termCrit.epsilon = std::max(params.termCrit.epsilon, DBL_EPSILON); + if( !(params.termCrit.type & TermCriteria::COUNT) ) + params.termCrit.maxCount = INT_MAX; + params.termCrit.maxCount = std::max(params.termCrit.maxCount, 1); } - Params getParams() const + void setParams( const SvmParams& _params) { - return params; - } - - Ptr getKernel() const - { - return kernel; + params = _params; + checkParams(); } int getSVCount(int i) const @@ -1335,9 +1368,9 @@ public: _responses.convertTo(_yf, CV_32F); bool ok = - svmType == ONE_CLASS ? Solver::solve_one_class( _samples, params.nu, kernel, _alpha, sinfo, termCrit ) : - svmType == EPS_SVR ? Solver::solve_eps_svr( _samples, _yf, params.p, params.C, kernel, _alpha, sinfo, termCrit ) : - svmType == NU_SVR ? Solver::solve_nu_svr( _samples, _yf, params.nu, params.C, kernel, _alpha, sinfo, termCrit ) : false; + svmType == ONE_CLASS ? Solver::solve_one_class( _samples, params.nu, kernel, _alpha, sinfo, params.termCrit ) : + svmType == EPS_SVR ? Solver::solve_eps_svr( _samples, _yf, params.p, params.C, kernel, _alpha, sinfo, params.termCrit ) : + svmType == NU_SVR ? Solver::solve_nu_svr( _samples, _yf, params.nu, params.C, kernel, _alpha, sinfo, params.termCrit ) : false; if( !ok ) return false; @@ -1397,7 +1430,7 @@ public: //check that while cross-validation there were the samples from all the classes if( class_ranges[class_count] <= 0 ) CV_Error( CV_StsBadArg, "While cross-validation one or more of the classes have " - "been fell out of the sample. Try to enlarge " ); + "been fell out of the sample. Try to enlarge " ); if( svmType == NU_SVC ) { @@ -1448,10 +1481,10 @@ public: DecisionFunc df; bool ok = params.svmType == C_SVC ? Solver::solve_c_svc( temp_samples, temp_y, Cp, Cn, - kernel, _alpha, sinfo, termCrit ) : + kernel, _alpha, sinfo, params.termCrit ) : params.svmType == NU_SVC ? Solver::solve_nu_svc( temp_samples, temp_y, params.nu, - kernel, _alpha, sinfo, termCrit ) : + kernel, _alpha, sinfo, params.termCrit ) : false; if( !ok ) return false; @@ -1557,6 +1590,8 @@ public: { clear(); + checkParams(); + int svmType = params.svmType; Mat samples = data->getTrainSamples(); Mat responses; @@ -1586,6 +1621,8 @@ public: ParamGrid nu_grid, ParamGrid coef_grid, ParamGrid degree_grid, bool balanced ) { + checkParams(); + int svmType = params.svmType; RNG rng((uint64)-1); @@ -1708,7 +1745,7 @@ public: int test_sample_count = (sample_count + k_fold/2)/k_fold; int train_sample_count = sample_count - test_sample_count; - Params best_params = params; + SvmParams best_params = params; double min_error = FLT_MAX; int rtype = responses.type(); @@ -1729,7 +1766,7 @@ public: FOR_IN_GRID(degree, degree_grid) { // make sure we updated the kernel and other parameters - setParams(params, Ptr() ); + setParams(params); double error = 0; for( k = 0; k < k_fold; k++ ) @@ -1919,7 +1956,9 @@ public: kernelType == LINEAR ? "LINEAR" : kernelType == POLY ? "POLY" : kernelType == RBF ? "RBF" : - kernelType == SIGMOID ? "SIGMOID" : format("Unknown_%d", kernelType); + kernelType == SIGMOID ? "SIGMOID" : + kernelType == CHI2 ? "CHI2" : + kernelType == INTER ? "INTER" : format("Unknown_%d", kernelType); fs << "svmType" << svm_type_str; @@ -2036,7 +2075,7 @@ public: void read_params( const FileNode& fn ) { - Params _params; + SvmParams _params; // check for old naming String svm_type_str = (String)(fn["svm_type"].empty() ? fn["svmType"] : fn["svm_type"]); @@ -2059,10 +2098,12 @@ public: kernel_type_str == "LINEAR" ? LINEAR : kernel_type_str == "POLY" ? POLY : kernel_type_str == "RBF" ? RBF : - kernel_type_str == "SIGMOID" ? SIGMOID : -1; + kernel_type_str == "SIGMOID" ? SIGMOID : + kernel_type_str == "CHI2" ? CHI2 : + kernel_type_str == "INTER" ? INTER : CUSTOM; - if( kernelType < 0 ) - CV_Error( CV_StsParseError, "Missing of invalid SVM kernel type" ); + if( kernelType == CUSTOM ) + CV_Error( CV_StsParseError, "Invalid SVM kernel type (or custom kernel)" ); _params.svmType = svmType; _params.kernelType = kernelType; @@ -2086,7 +2127,7 @@ public: else _params.termCrit = TermCriteria( TermCriteria::EPS + TermCriteria::COUNT, 1000, FLT_EPSILON ); - setParams( _params, Ptr() ); + setParams( _params ); } void read( const FileNode& fn ) @@ -2154,8 +2195,7 @@ public: optimize_linear_svm(); } - Params params; - TermCriteria termCrit; + SvmParams params; Mat class_labels; int var_count; Mat sv; @@ -2167,11 +2207,9 @@ public: }; -Ptr SVM::create(const Params& params, const Ptr& kernel) +Ptr SVM::create() { - Ptr p = makePtr(); - p->setParams(params, kernel); - return p; + return makePtr(); } } diff --git a/modules/ml/src/tree.cpp b/modules/ml/src/tree.cpp index 64f66169b0..537728336d 100644 --- a/modules/ml/src/tree.cpp +++ b/modules/ml/src/tree.cpp @@ -48,18 +48,7 @@ namespace ml { using std::vector; -void DTrees::setDParams(const DTrees::Params&) -{ - CV_Error(CV_StsNotImplemented, ""); -} - -DTrees::Params DTrees::getDParams() const -{ - CV_Error(CV_StsNotImplemented, ""); - return DTrees::Params(); -} - -DTrees::Params::Params() +TreeParams::TreeParams() { maxDepth = INT_MAX; minSampleCount = 10; @@ -72,11 +61,11 @@ DTrees::Params::Params() priors = Mat(); } -DTrees::Params::Params( int _maxDepth, int _minSampleCount, - double _regressionAccuracy, bool _useSurrogates, - int _maxCategories, int _CVFolds, - bool _use1SERule, bool _truncatePrunedTree, - const Mat& _priors ) +TreeParams::TreeParams(int _maxDepth, int _minSampleCount, + double _regressionAccuracy, bool _useSurrogates, + int _maxCategories, int _CVFolds, + bool _use1SERule, bool _truncatePrunedTree, + const Mat& _priors) { maxDepth = _maxDepth; minSampleCount = _minSampleCount; @@ -248,7 +237,7 @@ const vector& DTreesImpl::getActiveVars() int DTreesImpl::addTree(const vector& sidx ) { - size_t n = (params.maxDepth > 0 ? (1 << params.maxDepth) : 1024) + w->wnodes.size(); + size_t n = (params.getMaxDepth() > 0 ? (1 << params.getMaxDepth()) : 1024) + w->wnodes.size(); w->wnodes.reserve(n); w->wsplits.reserve(n); @@ -257,7 +246,7 @@ int DTreesImpl::addTree(const vector& sidx ) w->wsplits.clear(); w->wsubsets.clear(); - int cv_n = params.CVFolds; + int cv_n = params.getCVFolds(); if( cv_n > 0 ) { @@ -347,34 +336,9 @@ int DTreesImpl::addTree(const vector& sidx ) return root; } -DTrees::Params DTreesImpl::getDParams() const -{ - return params0; -} - -void DTreesImpl::setDParams(const Params& _params) +void DTreesImpl::setDParams(const TreeParams& _params) { - params0 = params = _params; - if( params.maxCategories < 2 ) - CV_Error( CV_StsOutOfRange, "params.max_categories should be >= 2" ); - params.maxCategories = std::min( params.maxCategories, 15 ); - - if( params.maxDepth < 0 ) - CV_Error( CV_StsOutOfRange, "params.max_depth should be >= 0" ); - params.maxDepth = std::min( params.maxDepth, 25 ); - - params.minSampleCount = std::max(params.minSampleCount, 1); - - if( params.CVFolds < 0 ) - CV_Error( CV_StsOutOfRange, - "params.CVFolds should be =0 (the tree is not pruned) " - "or n>0 (tree is pruned using n-fold cross-validation)" ); - - if( params.CVFolds == 1 ) - params.CVFolds = 0; - - if( params.regressionAccuracy < 0 ) - CV_Error( CV_StsOutOfRange, "params.regression_accuracy should be >= 0" ); + params = _params; } int DTreesImpl::addNodeAndTrySplit( int parent, const vector& sidx ) @@ -385,7 +349,7 @@ int DTreesImpl::addNodeAndTrySplit( int parent, const vector& sidx ) node.parent = parent; node.depth = parent >= 0 ? w->wnodes[parent].depth + 1 : 0; - int nfolds = params.CVFolds; + int nfolds = params.getCVFolds(); if( nfolds > 0 ) { @@ -400,7 +364,7 @@ int DTreesImpl::addNodeAndTrySplit( int parent, const vector& sidx ) calcValue( nidx, sidx ); - if( n <= params.minSampleCount || node.depth >= params.maxDepth ) + if( n <= params.getMinSampleCount() || node.depth >= params.getMaxDepth() ) can_split = false; else if( _isClassifier ) { @@ -415,7 +379,7 @@ int DTreesImpl::addNodeAndTrySplit( int parent, const vector& sidx ) } else { - if( sqrt(node.node_risk) < params.regressionAccuracy ) + if( sqrt(node.node_risk) < params.getRegressionAccuracy() ) can_split = false; } @@ -493,7 +457,7 @@ int DTreesImpl::findBestSplit( const vector& _sidx ) void DTreesImpl::calcValue( int nidx, const vector& _sidx ) { WNode* node = &w->wnodes[nidx]; - int i, j, k, n = (int)_sidx.size(), cv_n = params.CVFolds; + int i, j, k, n = (int)_sidx.size(), cv_n = params.getCVFolds(); int m = (int)classLabels.size(); cv::AutoBuffer buf(std::max(m, 3)*(cv_n+1)); @@ -841,8 +805,8 @@ DTreesImpl::WSplit DTreesImpl::findSplitCatClass( int vi, const vector& _si int m = (int)classLabels.size(); int base_size = m*(3 + mi) + mi + 1; - if( m > 2 && mi > params.maxCategories ) - base_size += m*std::min(params.maxCategories, n) + mi; + if( m > 2 && mi > params.getMaxCategories() ) + base_size += m*std::min(params.getMaxCategories(), n) + mi; else base_size += mi; AutoBuffer buf(base_size + n); @@ -880,9 +844,9 @@ DTreesImpl::WSplit DTreesImpl::findSplitCatClass( int vi, const vector& _si if( m > 2 ) { - if( mi > params.maxCategories ) + if( mi > params.getMaxCategories() ) { - mi = std::min(params.maxCategories, n); + mi = std::min(params.getMaxCategories(), n); cjk = c_weights + _mi; cluster_labels = (int*)(cjk + m*mi); clusterCategories( _cjk, _mi, m, cjk, mi, cluster_labels ); @@ -1228,7 +1192,7 @@ int DTreesImpl::pruneCV( int root ) // 2. choose the best tree index (if need, apply 1SE rule). // 3. store the best index and cut the branches. - int ti, tree_count = 0, j, cv_n = params.CVFolds, n = w->wnodes[root].sample_count; + int ti, tree_count = 0, j, cv_n = params.getCVFolds(), n = w->wnodes[root].sample_count; // currently, 1SE for regression is not implemented bool use_1se = params.use1SERule != 0 && _isClassifier; double min_err = 0, min_err_se = 0; @@ -1294,7 +1258,7 @@ int DTreesImpl::pruneCV( int root ) double DTreesImpl::updateTreeRNC( int root, double T, int fold ) { - int nidx = root, pidx = -1, cv_n = params.CVFolds; + int nidx = root, pidx = -1, cv_n = params.getCVFolds(); double min_alpha = DBL_MAX; for(;;) @@ -1350,7 +1314,7 @@ double DTreesImpl::updateTreeRNC( int root, double T, int fold ) bool DTreesImpl::cutTree( int root, double T, int fold, double min_alpha ) { - int cv_n = params.CVFolds, nidx = root, pidx = -1; + int cv_n = params.getCVFolds(), nidx = root, pidx = -1; WNode* node = &w->wnodes[root]; if( node->left < 0 ) return true; @@ -1560,19 +1524,19 @@ float DTreesImpl::predict( InputArray _samples, OutputArray _results, int flags void DTreesImpl::writeTrainingParams(FileStorage& fs) const { - fs << "use_surrogates" << (params0.useSurrogates ? 1 : 0); - fs << "max_categories" << params0.maxCategories; - fs << "regression_accuracy" << params0.regressionAccuracy; + fs << "use_surrogates" << (params.useSurrogates ? 1 : 0); + fs << "max_categories" << params.getMaxCategories(); + fs << "regression_accuracy" << params.getRegressionAccuracy(); - fs << "max_depth" << params0.maxDepth; - fs << "min_sample_count" << params0.minSampleCount; - fs << "cross_validation_folds" << params0.CVFolds; + fs << "max_depth" << params.getMaxDepth(); + fs << "min_sample_count" << params.getMinSampleCount(); + fs << "cross_validation_folds" << params.getCVFolds(); - if( params0.CVFolds > 1 ) - fs << "use_1se_rule" << (params0.use1SERule ? 1 : 0); + if( params.getCVFolds() > 1 ) + fs << "use_1se_rule" << (params.use1SERule ? 1 : 0); - if( !params0.priors.empty() ) - fs << "priors" << params0.priors; + if( !params.priors.empty() ) + fs << "priors" << params.priors; } void DTreesImpl::writeParams(FileStorage& fs) const @@ -1724,18 +1688,18 @@ void DTreesImpl::readParams( const FileNode& fn ) FileNode tparams_node = fn["training_params"]; - params0 = Params(); + TreeParams params0 = TreeParams(); if( !tparams_node.empty() ) // training parameters are not necessary { params0.useSurrogates = (int)tparams_node["use_surrogates"] != 0; - params0.maxCategories = (int)(tparams_node["max_categories"].empty() ? 16 : tparams_node["max_categories"]); - params0.regressionAccuracy = (float)tparams_node["regression_accuracy"]; - params0.maxDepth = (int)tparams_node["max_depth"]; - params0.minSampleCount = (int)tparams_node["min_sample_count"]; - params0.CVFolds = (int)tparams_node["cross_validation_folds"]; + params0.setMaxCategories((int)(tparams_node["max_categories"].empty() ? 16 : tparams_node["max_categories"])); + params0.setRegressionAccuracy((float)tparams_node["regression_accuracy"]); + params0.setMaxDepth((int)tparams_node["max_depth"]); + params0.setMinSampleCount((int)tparams_node["min_sample_count"]); + params0.setCVFolds((int)tparams_node["cross_validation_folds"]); - if( params0.CVFolds > 1 ) + if( params0.getCVFolds() > 1 ) { params.use1SERule = (int)tparams_node["use_1se_rule"] != 0; } @@ -1964,11 +1928,9 @@ void DTreesImpl::read( const FileNode& fn ) readTree(fnodes); } -Ptr DTrees::create(const DTrees::Params& params) +Ptr DTrees::create() { - Ptr p = makePtr(); - p->setDParams(params); - return p; + return makePtr(); } } diff --git a/modules/ml/test/test_emknearestkmeans.cpp b/modules/ml/test/test_emknearestkmeans.cpp index 121b34d184..a079be22f2 100644 --- a/modules/ml/test/test_emknearestkmeans.cpp +++ b/modules/ml/test/test_emknearestkmeans.cpp @@ -330,7 +330,8 @@ void CV_KNearestTest::run( int /*start_from*/ ) } // KNearest KDTree implementation - Ptr knearestKdt = KNearest::create(ml::KNearest::Params(10, true, INT_MAX, ml::KNearest::KDTREE)); + Ptr knearestKdt = KNearest::create(); + knearestKdt->setAlgorithmType(KNearest::KDTREE); knearestKdt->train(trainData, ml::ROW_SAMPLE, trainLabels); knearestKdt->findNearest(testData, 4, bestLabels); if( !calcErr( bestLabels, testLabels, sizes, err, true ) ) @@ -394,16 +395,18 @@ int CV_EMTest::runCase( int caseIndex, const EM_Params& params, cv::Mat labels; float err; - Ptr em; - EM::Params emp(params.nclusters, params.covMatType, params.termCrit); + Ptr em = EM::create(); + em->setClustersNumber(params.nclusters); + em->setCovarianceMatrixType(params.covMatType); + em->setTermCriteria(params.termCrit); if( params.startStep == EM::START_AUTO_STEP ) - em = EM::train( trainData, noArray(), labels, noArray(), emp ); + em->trainEM( trainData, noArray(), labels, noArray() ); else if( params.startStep == EM::START_E_STEP ) - em = EM::train_startWithE( trainData, *params.means, *params.covs, - *params.weights, noArray(), labels, noArray(), emp ); + em->trainE( trainData, *params.means, *params.covs, + *params.weights, noArray(), labels, noArray() ); else if( params.startStep == EM::START_M_STEP ) - em = EM::train_startWithM( trainData, *params.probs, - noArray(), labels, noArray(), emp ); + em->trainM( trainData, *params.probs, + noArray(), labels, noArray() ); // check train error if( !calcErr( labels, trainLabels, sizes, err , false, false ) ) @@ -543,7 +546,9 @@ protected: Mat labels; - Ptr em = EM::train(samples, noArray(), labels, noArray(), EM::Params(nclusters)); + Ptr em = EM::create(); + em->setClustersNumber(nclusters); + em->trainEM(samples, noArray(), labels, noArray()); Mat firstResult(samples.rows, 1, CV_32SC1); for( int i = 0; i < samples.rows; i++) @@ -644,8 +649,13 @@ protected: samples1.push_back(sample); } } - Ptr model0 = EM::train(samples0, noArray(), noArray(), noArray(), EM::Params(3)); - Ptr model1 = EM::train(samples1, noArray(), noArray(), noArray(), EM::Params(3)); + Ptr model0 = EM::create(); + model0->setClustersNumber(3); + model0->trainEM(samples0, noArray(), noArray(), noArray()); + + Ptr model1 = EM::create(); + model1->setClustersNumber(3); + model1->trainEM(samples1, noArray(), noArray(), noArray()); Mat trainConfusionMat(2, 2, CV_32SC1, Scalar(0)), testConfusionMat(2, 2, CV_32SC1, Scalar(0)); diff --git a/modules/ml/test/test_lr.cpp b/modules/ml/test/test_lr.cpp index 18de0825dc..e0da01cfb9 100644 --- a/modules/ml/test/test_lr.cpp +++ b/modules/ml/test/test_lr.cpp @@ -95,16 +95,13 @@ void CV_LRTest::run( int /*start_from*/ ) string dataFileName = ts->get_data_path() + "iris.data"; Ptr tdata = TrainData::loadFromCSV(dataFileName, 0); - LogisticRegression::Params params = LogisticRegression::Params(); - params.alpha = 1.0; - params.num_iters = 10001; - params.norm = LogisticRegression::REG_L2; - params.regularized = 1; - params.train_method = LogisticRegression::BATCH; - params.mini_batch_size = 10; - // run LR classifier train classifier - Ptr p = LogisticRegression::create(params); + Ptr p = LogisticRegression::create(); + p->setLearningRate(1.0); + p->setIterations(10001); + p->setRegularization(LogisticRegression::REG_L2); + p->setTrainMethod(LogisticRegression::BATCH); + p->setMiniBatchSize(10); p->train(tdata); // predict using the same data @@ -157,20 +154,17 @@ void CV_LRTest_SaveLoad::run( int /*start_from*/ ) Mat responses1, responses2; Mat learnt_mat1, learnt_mat2; - LogisticRegression::Params params1 = LogisticRegression::Params(); - params1.alpha = 1.0; - params1.num_iters = 10001; - params1.norm = LogisticRegression::REG_L2; - params1.regularized = 1; - params1.train_method = LogisticRegression::BATCH; - params1.mini_batch_size = 10; - // train and save the classifier String filename = tempfile(".xml"); try { // run LR classifier train classifier - Ptr lr1 = LogisticRegression::create(params1); + Ptr lr1 = LogisticRegression::create(); + lr1->setLearningRate(1.0); + lr1->setIterations(10001); + lr1->setRegularization(LogisticRegression::REG_L2); + lr1->setTrainMethod(LogisticRegression::BATCH); + lr1->setMiniBatchSize(10); lr1->train(tdata); lr1->predict(tdata->getSamples(), responses1); learnt_mat1 = lr1->get_learnt_thetas(); diff --git a/modules/ml/test/test_mltests2.cpp b/modules/ml/test/test_mltests2.cpp index b7c5f46c6e..cfaf0f2491 100644 --- a/modules/ml/test/test_mltests2.cpp +++ b/modules/ml/test/test_mltests2.cpp @@ -73,30 +73,14 @@ int str_to_svm_kernel_type( String& str ) return -1; } -Ptr svm_train_auto( Ptr _data, SVM::Params _params, - int k_fold, ParamGrid C_grid, ParamGrid gamma_grid, - ParamGrid p_grid, ParamGrid nu_grid, ParamGrid coef_grid, - ParamGrid degree_grid ) -{ - Mat _train_data = _data->getSamples(); - Mat _responses = _data->getResponses(); - Mat _var_idx = _data->getVarIdx(); - Mat _sample_idx = _data->getTrainSampleIdx(); - - Ptr svm = SVM::create(_params); - if( svm->trainAuto( _data, k_fold, C_grid, gamma_grid, p_grid, nu_grid, coef_grid, degree_grid ) ) - return svm; - return Ptr(); -} - // 4. em // 5. ann int str_to_ann_train_method( String& str ) { if( !str.compare("BACKPROP") ) - return ANN_MLP::Params::BACKPROP; + return ANN_MLP::BACKPROP; if( !str.compare("RPROP") ) - return ANN_MLP::Params::RPROP; + return ANN_MLP::RPROP; CV_Error( CV_StsBadArg, "incorrect ann train method string" ); return -1; } @@ -343,16 +327,16 @@ int CV_MLBaseTest::train( int testCaseIdx ) String svm_type_str, kernel_type_str; modelParamsNode["svm_type"] >> svm_type_str; modelParamsNode["kernel_type"] >> kernel_type_str; - SVM::Params params; - params.svmType = str_to_svm_type( svm_type_str ); - params.kernelType = str_to_svm_kernel_type( kernel_type_str ); - modelParamsNode["degree"] >> params.degree; - modelParamsNode["gamma"] >> params.gamma; - modelParamsNode["coef0"] >> params.coef0; - modelParamsNode["C"] >> params.C; - modelParamsNode["nu"] >> params.nu; - modelParamsNode["p"] >> params.p; - model = SVM::create(params); + Ptr m = SVM::create(); + m->setType(str_to_svm_type( svm_type_str )); + m->setKernel(str_to_svm_kernel_type( kernel_type_str )); + m->setDegree(modelParamsNode["degree"]); + m->setGamma(modelParamsNode["gamma"]); + m->setCoef0(modelParamsNode["coef0"]); + m->setC(modelParamsNode["C"]); + m->setNu(modelParamsNode["nu"]); + m->setP(modelParamsNode["p"]); + model = m; } else if( modelName == CV_EM ) { @@ -371,9 +355,13 @@ int CV_MLBaseTest::train( int testCaseIdx ) data->getVarIdx(), data->getTrainSampleIdx()); int layer_sz[] = { data->getNAllVars(), 100, 100, (int)cls_map.size() }; Mat layer_sizes( 1, (int)(sizeof(layer_sz)/sizeof(layer_sz[0])), CV_32S, layer_sz ); - model = ANN_MLP::create(ANN_MLP::Params(layer_sizes, ANN_MLP::SIGMOID_SYM, 0, 0, - TermCriteria(TermCriteria::COUNT,300,0.01), - str_to_ann_train_method(train_method_str), param1, param2)); + Ptr m = ANN_MLP::create(); + m->setLayerSizes(layer_sizes); + m->setActivationFunction(ANN_MLP::SIGMOID_SYM, 0, 0); + m->setTermCriteria(TermCriteria(TermCriteria::COUNT,300,0.01)); + m->setTrainMethod(str_to_ann_train_method(train_method_str), param1, param2); + model = m; + } else if( modelName == CV_DTREE ) { @@ -386,8 +374,18 @@ int CV_MLBaseTest::train( int testCaseIdx ) modelParamsNode["max_categories"] >> MAX_CATEGORIES; modelParamsNode["cv_folds"] >> CV_FOLDS; modelParamsNode["is_pruned"] >> IS_PRUNED; - model = DTrees::create(DTrees::Params(MAX_DEPTH, MIN_SAMPLE_COUNT, REG_ACCURACY, USE_SURROGATE, - MAX_CATEGORIES, CV_FOLDS, false, IS_PRUNED, Mat() )); + + Ptr m = DTrees::create(); + m->setMaxDepth(MAX_DEPTH); + m->setMinSampleCount(MIN_SAMPLE_COUNT); + m->setRegressionAccuracy(REG_ACCURACY); + m->setUseSurrogates(USE_SURROGATE); + m->setMaxCategories(MAX_CATEGORIES); + m->setCVFolds(CV_FOLDS); + m->setUse1SERule(false); + m->setTruncatePrunedTree(IS_PRUNED); + m->setPriors(Mat()); + model = m; } else if( modelName == CV_BOOST ) { @@ -401,7 +399,15 @@ int CV_MLBaseTest::train( int testCaseIdx ) modelParamsNode["weight_trim_rate"] >> WEIGHT_TRIM_RATE; modelParamsNode["max_depth"] >> MAX_DEPTH; //modelParamsNode["use_surrogate"] >> USE_SURROGATE; - model = Boost::create( Boost::Params(BOOST_TYPE, WEAK_COUNT, WEIGHT_TRIM_RATE, MAX_DEPTH, USE_SURROGATE, Mat()) ); + + Ptr m = Boost::create(); + m->setBoostType(BOOST_TYPE); + m->setWeakCount(WEAK_COUNT); + m->setWeightTrimRate(WEIGHT_TRIM_RATE); + m->setMaxDepth(MAX_DEPTH); + m->setUseSurrogates(USE_SURROGATE); + m->setPriors(Mat()); + model = m; } else if( modelName == CV_RTREES ) { @@ -416,9 +422,18 @@ int CV_MLBaseTest::train( int testCaseIdx ) modelParamsNode["is_pruned"] >> IS_PRUNED; modelParamsNode["nactive_vars"] >> NACTIVE_VARS; modelParamsNode["max_trees_num"] >> MAX_TREES_NUM; - model = RTrees::create(RTrees::Params( MAX_DEPTH, MIN_SAMPLE_COUNT, REG_ACCURACY, - USE_SURROGATE, MAX_CATEGORIES, Mat(), true, // (calc_var_importance == true) <=> RF processes variable importance - NACTIVE_VARS, TermCriteria(TermCriteria::COUNT, MAX_TREES_NUM, OOB_EPS))); + + Ptr m = RTrees::create(); + m->setMaxDepth(MAX_DEPTH); + m->setMinSampleCount(MIN_SAMPLE_COUNT); + m->setRegressionAccuracy(REG_ACCURACY); + m->setUseSurrogates(USE_SURROGATE); + m->setMaxCategories(MAX_CATEGORIES); + m->setPriors(Mat()); + m->setCalculateVarImportance(true); + m->setActiveVarCount(NACTIVE_VARS); + m->setTermCriteria(TermCriteria(TermCriteria::COUNT, MAX_TREES_NUM, OOB_EPS)); + model = m; } if( !model.empty() ) diff --git a/modules/ml/test/test_save_load.cpp b/modules/ml/test/test_save_load.cpp index 74e8eef0df..606079b818 100644 --- a/modules/ml/test/test_save_load.cpp +++ b/modules/ml/test/test_save_load.cpp @@ -149,9 +149,8 @@ int CV_SLMLTest::validate_test_results( int testCaseIdx ) } TEST(ML_NaiveBayes, save_load) { CV_SLMLTest test( CV_NBAYES ); test.safe_run(); } -//CV_SLMLTest lsmlknearest( CV_KNEAREST, "slknearest" ); // does not support save! +TEST(ML_KNearest, save_load) { CV_SLMLTest test( CV_KNEAREST ); test.safe_run(); } TEST(ML_SVM, save_load) { CV_SLMLTest test( CV_SVM ); test.safe_run(); } -//CV_SLMLTest lsmlem( CV_EM, "slem" ); // does not support save! TEST(ML_ANN, save_load) { CV_SLMLTest test( CV_ANN ); test.safe_run(); } TEST(ML_DTree, save_load) { CV_SLMLTest test( CV_DTREE ); test.safe_run(); } TEST(ML_Boost, save_load) { CV_SLMLTest test( CV_BOOST ); test.safe_run(); } diff --git a/modules/superres/include/opencv2/superres.hpp b/modules/superres/include/opencv2/superres.hpp index 0639b10422..acc067302a 100644 --- a/modules/superres/include/opencv2/superres.hpp +++ b/modules/superres/include/opencv2/superres.hpp @@ -104,34 +104,34 @@ namespace cv */ virtual void collectGarbage(); - //! @name Scale factor + //! @brief Scale factor CV_PURE_PROPERTY(int, Scale) - //! @name Iterations count + //! @brief Iterations count CV_PURE_PROPERTY(int, Iterations) - //! @name Asymptotic value of steepest descent method + //! @brief Asymptotic value of steepest descent method CV_PURE_PROPERTY(double, Tau) - //! @name Weight parameter to balance data term and smoothness term + //! @brief Weight parameter to balance data term and smoothness term CV_PURE_PROPERTY(double, Labmda) - //! @name Parameter of spacial distribution in Bilateral-TV + //! @brief Parameter of spacial distribution in Bilateral-TV CV_PURE_PROPERTY(double, Alpha) - //! @name Kernel size of Bilateral-TV filter + //! @brief Kernel size of Bilateral-TV filter CV_PURE_PROPERTY(int, KernelSize) - //! @name Gaussian blur kernel size + //! @brief Gaussian blur kernel size CV_PURE_PROPERTY(int, BlurKernelSize) - //! @name Gaussian blur sigma + //! @brief Gaussian blur sigma CV_PURE_PROPERTY(double, BlurSigma) - //! @name Radius of the temporal search area + //! @brief Radius of the temporal search area CV_PURE_PROPERTY(int, TemporalAreaRadius) - //! @name Dense optical flow algorithm + //! @brief Dense optical flow algorithm CV_PURE_PROPERTY_S(Ptr, OpticalFlow) protected: diff --git a/modules/superres/include/opencv2/superres/optical_flow.hpp b/modules/superres/include/opencv2/superres/optical_flow.hpp index 7bc64782cb..add606c02b 100644 --- a/modules/superres/include/opencv2/superres/optical_flow.hpp +++ b/modules/superres/include/opencv2/superres/optical_flow.hpp @@ -98,17 +98,17 @@ namespace cv class CV_EXPORTS BroxOpticalFlow : public virtual DenseOpticalFlowExt { public: - //! @name Flow smoothness + //! @brief Flow smoothness CV_PURE_PROPERTY(double, Alpha) - //! @name Gradient constancy importance + //! @brief Gradient constancy importance CV_PURE_PROPERTY(double, Gamma) - //! @name Pyramid scale factor + //! @brief Pyramid scale factor CV_PURE_PROPERTY(double, ScaleFactor) - //! @name Number of lagged non-linearity iterations (inner loop) + //! @brief Number of lagged non-linearity iterations (inner loop) CV_PURE_PROPERTY(int, InnerIterations) - //! @name Number of warping iterations (number of pyramid levels) + //! @brief Number of warping iterations (number of pyramid levels) CV_PURE_PROPERTY(int, OuterIterations) - //! @name Number of linear system solver iterations + //! @brief Number of linear system solver iterations CV_PURE_PROPERTY(int, SolverIterations) }; CV_EXPORTS Ptr createOptFlow_Brox_CUDA(); diff --git a/modules/superres/src/optical_flow.cpp b/modules/superres/src/optical_flow.cpp index a08a58bd9e..df6725b72b 100644 --- a/modules/superres/src/optical_flow.cpp +++ b/modules/superres/src/optical_flow.cpp @@ -328,18 +328,6 @@ Ptr cv::superres::createOptFlow_Simple() namespace { - #define CV_WRAP_PROPERTY(type, name, internal_name, internal_obj) \ - type get##name() const \ - { \ - return internal_obj->get##internal_name(); \ - } \ - void set##name(type _name) \ - { \ - internal_obj->set##internal_name(_name); \ - } - - #define CV_WRAP_SAME_PROPERTY(type, name, internal_obj) CV_WRAP_PROPERTY(type, name, name, internal_obj) - class DualTVL1 : public CpuOpticalFlow, public virtual cv::superres::DualTVL1OpticalFlow { public: @@ -347,14 +335,14 @@ namespace void calc(InputArray frame0, InputArray frame1, OutputArray flow1, OutputArray flow2); void collectGarbage(); - CV_WRAP_SAME_PROPERTY(double, Tau, alg_) - CV_WRAP_SAME_PROPERTY(double, Lambda, alg_) - CV_WRAP_SAME_PROPERTY(double, Theta, alg_) - CV_WRAP_SAME_PROPERTY(int, ScalesNumber, alg_) - CV_WRAP_SAME_PROPERTY(int, WarpingsNumber, alg_) - CV_WRAP_SAME_PROPERTY(double, Epsilon, alg_) - CV_WRAP_PROPERTY(int, Iterations, OuterIterations, alg_) - CV_WRAP_SAME_PROPERTY(bool, UseInitialFlow, alg_) + CV_WRAP_SAME_PROPERTY(double, Tau, (*alg_)) + CV_WRAP_SAME_PROPERTY(double, Lambda, (*alg_)) + CV_WRAP_SAME_PROPERTY(double, Theta, (*alg_)) + CV_WRAP_SAME_PROPERTY(int, ScalesNumber, (*alg_)) + CV_WRAP_SAME_PROPERTY(int, WarpingsNumber, (*alg_)) + CV_WRAP_SAME_PROPERTY(double, Epsilon, (*alg_)) + CV_WRAP_PROPERTY(int, Iterations, OuterIterations, (*alg_)) + CV_WRAP_SAME_PROPERTY(bool, UseInitialFlow, (*alg_)) protected: void impl(InputArray input0, InputArray input1, OutputArray dst); diff --git a/modules/video/include/opencv2/video/tracking.hpp b/modules/video/include/opencv2/video/tracking.hpp index 40e9ffab88..90be72ea2d 100644 --- a/modules/video/include/opencv2/video/tracking.hpp +++ b/modules/video/include/opencv2/video/tracking.hpp @@ -440,29 +440,29 @@ Javier Sanchez, Enric Meinhardt-Llopis and Gabriele Facciolo. "TV-L1 Optical Flo class CV_EXPORTS_W DualTVL1OpticalFlow : public DenseOpticalFlow { public: - //! @name Time step of the numerical scheme + //! @brief Time step of the numerical scheme CV_PURE_PROPERTY(double, Tau) - //! @name Weight parameter for the data term, attachment parameter + //! @brief Weight parameter for the data term, attachment parameter CV_PURE_PROPERTY(double, Lambda) - //! @name Weight parameter for (u - v)^2, tightness parameter + //! @brief Weight parameter for (u - v)^2, tightness parameter CV_PURE_PROPERTY(double, Theta) - //! @name coefficient for additional illumination variation term + //! @brief coefficient for additional illumination variation term CV_PURE_PROPERTY(double, Gamma) - //! @name Number of scales used to create the pyramid of images + //! @brief Number of scales used to create the pyramid of images CV_PURE_PROPERTY(int, ScalesNumber) - //! @name Number of warpings per scale + //! @brief Number of warpings per scale CV_PURE_PROPERTY(int, WarpingsNumber) - //! @name Stopping criterion threshold used in the numerical scheme, which is a trade-off between precision and running time + //! @brief Stopping criterion threshold used in the numerical scheme, which is a trade-off between precision and running time CV_PURE_PROPERTY(double, Epsilon) - //! @name Inner iterations (between outlier filtering) used in the numerical scheme + //! @brief Inner iterations (between outlier filtering) used in the numerical scheme CV_PURE_PROPERTY(int, InnerIterations) - //! @name Outer iterations (number of inner loops) used in the numerical scheme + //! @brief Outer iterations (number of inner loops) used in the numerical scheme CV_PURE_PROPERTY(int, OuterIterations) - //! @name Use initial flow + //! @brief Use initial flow CV_PURE_PROPERTY(bool, UseInitialFlow) - //! @name Step between scales (<1) + //! @brief Step between scales (<1) CV_PURE_PROPERTY(double, ScaleStep) - //! @name Median filter kernel size (1 = no filter) (3 or 5) + //! @brief Median filter kernel size (1 = no filter) (3 or 5) CV_PURE_PROPERTY(int, MedianFiltering) }; diff --git a/samples/cpp/em.cpp b/samples/cpp/em.cpp index bb777fcc86..f5310740f4 100644 --- a/samples/cpp/em.cpp +++ b/samples/cpp/em.cpp @@ -36,9 +36,11 @@ int main( int /*argc*/, char** /*argv*/ ) samples = samples.reshape(1, 0); // cluster the data - Ptr em_model = EM::train( samples, noArray(), labels, noArray(), - EM::Params(N, EM::COV_MAT_SPHERICAL, - TermCriteria(TermCriteria::COUNT+TermCriteria::EPS, 300, 0.1))); + Ptr em_model = EM::create(); + em_model->setClustersNumber(N); + em_model->setCovarianceMatrixType(EM::COV_MAT_SPHERICAL); + em_model->setTermCriteria(TermCriteria(TermCriteria::COUNT+TermCriteria::EPS, 300, 0.1)); + em_model->trainEM( samples, noArray(), labels, noArray() ); // classify every image pixel for( i = 0; i < img.rows; i++ ) diff --git a/samples/cpp/letter_recog.cpp b/samples/cpp/letter_recog.cpp index 66a59318cc..174e7f9839 100644 --- a/samples/cpp/letter_recog.cpp +++ b/samples/cpp/letter_recog.cpp @@ -178,8 +178,23 @@ build_rtrees_classifier( const string& data_filename, { // create classifier by using and cout << "Training the classifier ...\n"; +// Params( int maxDepth, int minSampleCount, +// double regressionAccuracy, bool useSurrogates, +// int maxCategories, const Mat& priors, +// bool calcVarImportance, int nactiveVars, +// TermCriteria termCrit ); Ptr tdata = prepare_train_data(data, responses, ntrain_samples); - model = StatModel::train(tdata, RTrees::Params(10,10,0,false,15,Mat(),true,4,TC(100,0.01f))); + model = RTrees::create(); + model->setMaxDepth(10); + model->setMinSampleCount(10); + model->setRegressionAccuracy(0); + model->setUseSurrogates(false); + model->setMaxCategories(15); + model->setPriors(Mat()); + model->setCalculateVarImportance(true); + model->setActiveVarCount(4); + model->setTermCriteria(TC(100,0.01f)); + model->train(tdata); cout << endl; } @@ -269,7 +284,14 @@ build_boost_classifier( const string& data_filename, priors[1] = 26; cout << "Training the classifier (may take a few minutes)...\n"; - model = StatModel::train(tdata, Boost::Params(Boost::GENTLE, 100, 0.95, 5, false, Mat(priors) )); + model = Boost::create(); + model->setBoostType(Boost::GENTLE); + model->setWeakCount(100); + model->setWeightTrimRate(0.95); + model->setMaxDepth(5); + model->setUseSurrogates(false); + model->setPriors(Mat(priors)); + model->train(tdata); cout << endl; } @@ -374,11 +396,11 @@ build_mlp_classifier( const string& data_filename, Mat layer_sizes( 1, nlayers, CV_32S, layer_sz ); #if 1 - int method = ANN_MLP::Params::BACKPROP; + int method = ANN_MLP::BACKPROP; double method_param = 0.001; int max_iter = 300; #else - int method = ANN_MLP::Params::RPROP; + int method = ANN_MLP::RPROP; double method_param = 0.1; int max_iter = 1000; #endif @@ -386,7 +408,12 @@ build_mlp_classifier( const string& data_filename, Ptr tdata = TrainData::create(train_data, ROW_SAMPLE, train_responses); cout << "Training the classifier (may take a few minutes)...\n"; - model = StatModel::train(tdata, ANN_MLP::Params(layer_sizes, ANN_MLP::SIGMOID_SYM, 0, 0, TC(max_iter,0), method, method_param)); + model = ANN_MLP::create(); + model->setLayerSizes(layer_sizes); + model->setActivationFunction(ANN_MLP::SIGMOID_SYM, 0, 0); + model->setTermCriteria(TC(max_iter,0)); + model->setTrainMethod(method, method_param); + model->train(tdata); cout << endl; } @@ -403,7 +430,6 @@ build_knearest_classifier( const string& data_filename, int K ) if( !ok ) return ok; - Ptr model; int nsamples_all = data.rows; int ntrain_samples = (int)(nsamples_all*0.8); @@ -411,7 +437,10 @@ build_knearest_classifier( const string& data_filename, int K ) // create classifier by using and cout << "Training the classifier ...\n"; Ptr tdata = prepare_train_data(data, responses, ntrain_samples); - model = StatModel::train(tdata, KNearest::Params(K, true)); + Ptr model = KNearest::create(); + model->setDefaultK(K); + model->setIsClassifier(true); + model->train(tdata); cout << endl; test_and_save_classifier(model, data, responses, ntrain_samples, 0, string()); @@ -435,7 +464,8 @@ build_nbayes_classifier( const string& data_filename ) // create classifier by using and cout << "Training the classifier ...\n"; Ptr tdata = prepare_train_data(data, responses, ntrain_samples); - model = StatModel::train(tdata, NormalBayesClassifier::Params()); + model = NormalBayesClassifier::create(); + model->train(tdata); cout << endl; test_and_save_classifier(model, data, responses, ntrain_samples, 0, string()); @@ -471,13 +501,11 @@ build_svm_classifier( const string& data_filename, // create classifier by using and cout << "Training the classifier ...\n"; Ptr tdata = prepare_train_data(data, responses, ntrain_samples); - - SVM::Params params; - params.svmType = SVM::C_SVC; - params.kernelType = SVM::LINEAR; - params.C = 1; - - model = StatModel::train(tdata, params); + model = SVM::create(); + model->setType(SVM::C_SVC); + model->setKernel(SVM::LINEAR); + model->setC(1); + model->train(tdata); cout << endl; } diff --git a/samples/cpp/logistic_regression.cpp b/samples/cpp/logistic_regression.cpp index 1aeb42d925..b567dd2d25 100644 --- a/samples/cpp/logistic_regression.cpp +++ b/samples/cpp/logistic_regression.cpp @@ -132,20 +132,16 @@ int main() showImage(data_train, 28, "train data"); showImage(data_test, 28, "test data"); - // simple case with batch gradient - LogisticRegression::Params params = LogisticRegression::Params( - 0.001, 10, LogisticRegression::BATCH, LogisticRegression::REG_L2, 1, 1); - // simple case with mini-batch gradient - // LogisticRegression::Params params = LogisticRegression::Params( - // 0.001, 10, LogisticRegression::MINI_BATCH, LogisticRegression::REG_L2, 1, 1); - - // mini-batch gradient with higher accuracy - // LogisticRegression::Params params = LogisticRegression::Params( - // 0.000001, 10, LogisticRegression::MINI_BATCH, LogisticRegression::REG_L2, 1, 1); - cout << "training..."; - Ptr lr1 = LogisticRegression::create(params); + //! [init] + Ptr lr1 = LogisticRegression::create(); + lr1->setLearningRate(0.001); + lr1->setIterations(10); + lr1->setRegularization(LogisticRegression::REG_L2); + lr1->setTrainMethod(LogisticRegression::BATCH); + lr1->setMiniBatchSize(1); + //! [init] lr1->train(data_train, ROW_SAMPLE, labels_train); cout << "done!" << endl; diff --git a/samples/cpp/points_classifier.cpp b/samples/cpp/points_classifier.cpp index 9b274bac1a..c0270d084c 100644 --- a/samples/cpp/points_classifier.cpp +++ b/samples/cpp/points_classifier.cpp @@ -102,7 +102,7 @@ static void predict_and_paint(const Ptr& model, Mat& dst) static void find_decision_boundary_NBC() { // learn classifier - Ptr normalBayesClassifier = StatModel::train(prepare_train_data(), NormalBayesClassifier::Params()); + Ptr normalBayesClassifier = StatModel::train(prepare_train_data()); predict_and_paint(normalBayesClassifier, imgDst); } @@ -112,15 +112,29 @@ static void find_decision_boundary_NBC() #if _KNN_ static void find_decision_boundary_KNN( int K ) { - Ptr knn = StatModel::train(prepare_train_data(), KNearest::Params(K, true)); + + Ptr knn = KNearest::create(); + knn->setDefaultK(K); + knn->setIsClassifier(true); + knn->train(prepare_train_data()); predict_and_paint(knn, imgDst); } #endif #if _SVM_ -static void find_decision_boundary_SVM( SVM::Params params ) +static void find_decision_boundary_SVM( double C ) { - Ptr svm = StatModel::train(prepare_train_data(), params); + Ptr svm = SVM::create(); + svm->setType(SVM::C_SVC); + svm->setKernel(SVM::POLY); //SVM::LINEAR; + svm->setDegree(0.5); + svm->setGamma(1); + svm->setCoef0(1); + svm->setNu(0.5); + svm->setP(0); + svm->setTermCriteria(TermCriteria(TermCriteria::MAX_ITER+TermCriteria::EPS, 1000, 0.01)); + svm->setC(C); + svm->train(prepare_train_data()); predict_and_paint(svm, imgDst); Mat sv = svm->getSupportVectors(); @@ -135,16 +149,14 @@ static void find_decision_boundary_SVM( SVM::Params params ) #if _DT_ static void find_decision_boundary_DT() { - DTrees::Params params; - params.maxDepth = 8; - params.minSampleCount = 2; - params.useSurrogates = false; - params.CVFolds = 0; // the number of cross-validation folds - params.use1SERule = false; - params.truncatePrunedTree = false; - - Ptr dtree = StatModel::train(prepare_train_data(), params); - + Ptr dtree = DTrees::create(); + dtree->setMaxDepth(8); + dtree->setMinSampleCount(2); + dtree->setUseSurrogates(false); + dtree->setCVFolds(0); // the number of cross-validation folds + dtree->setUse1SERule(false); + dtree->setTruncatePrunedTree(false); + dtree->train(prepare_train_data()); predict_and_paint(dtree, imgDst); } #endif @@ -152,15 +164,14 @@ static void find_decision_boundary_DT() #if _BT_ static void find_decision_boundary_BT() { - Boost::Params params( Boost::DISCRETE, // boost_type - 100, // weak_count - 0.95, // weight_trim_rate - 2, // max_depth - false, //use_surrogates - Mat() // priors - ); - - Ptr boost = StatModel::train(prepare_train_data(), params); + Ptr boost = Boost::create(); + boost->setBoostType(Boost::DISCRETE); + boost->setWeakCount(100); + boost->setWeightTrimRate(0.95); + boost->setMaxDepth(2); + boost->setUseSurrogates(false); + boost->setPriors(Mat()); + boost->train(prepare_train_data()); predict_and_paint(boost, imgDst); } @@ -185,18 +196,17 @@ static void find_decision_boundary_GBT() #if _RF_ static void find_decision_boundary_RF() { - RTrees::Params params( 4, // max_depth, - 2, // min_sample_count, - 0.f, // regression_accuracy, - false, // use_surrogates, - 16, // max_categories, - Mat(), // priors, - false, // calc_var_importance, - 1, // nactive_vars, - TermCriteria(TermCriteria::MAX_ITER, 5, 0) // max_num_of_trees_in_the_forest, - ); - - Ptr rtrees = StatModel::train(prepare_train_data(), params); + Ptr rtrees = RTrees::create(); + rtrees->setMaxDepth(4); + rtrees->setMinSampleCount(2); + rtrees->setRegressionAccuracy(0.f); + rtrees->setUseSurrogates(false); + rtrees->setMaxCategories(16); + rtrees->setPriors(Mat()); + rtrees->setCalculateVarImportance(false); + rtrees->setActiveVarCount(1); + rtrees->setTermCriteria(TermCriteria(TermCriteria::MAX_ITER, 5, 0)); + rtrees->train(prepare_train_data()); predict_and_paint(rtrees, imgDst); } @@ -205,9 +215,6 @@ static void find_decision_boundary_RF() #if _ANN_ static void find_decision_boundary_ANN( const Mat& layer_sizes ) { - ANN_MLP::Params params(layer_sizes, ANN_MLP::SIGMOID_SYM, 1, 1, TermCriteria(TermCriteria::MAX_ITER+TermCriteria::EPS, 300, FLT_EPSILON), - ANN_MLP::Params::BACKPROP, 0.001); - Mat trainClasses = Mat::zeros( (int)trainedPoints.size(), (int)classColors.size(), CV_32FC1 ); for( int i = 0; i < trainClasses.rows; i++ ) { @@ -217,7 +224,12 @@ static void find_decision_boundary_ANN( const Mat& layer_sizes ) Mat samples = prepare_train_samples(trainedPoints); Ptr tdata = TrainData::create(samples, ROW_SAMPLE, trainClasses); - Ptr ann = StatModel::train(tdata, params); + Ptr ann = ANN_MLP::create(); + ann->setLayerSizes(layer_sizes); + ann->setActivationFunction(ANN_MLP::SIGMOID_SYM, 1, 1); + ann->setTermCriteria(TermCriteria(TermCriteria::MAX_ITER+TermCriteria::EPS, 300, FLT_EPSILON)); + ann->setTrainMethod(ANN_MLP::BACKPROP, 0.001); + ann->train(tdata); predict_and_paint(ann, imgDst); } #endif @@ -247,8 +259,11 @@ static void find_decision_boundary_EM() // learn models if( !modelSamples.empty() ) { - em_models[i] = EM::train(modelSamples, noArray(), noArray(), noArray(), - EM::Params(componentCount, EM::COV_MAT_DIAGONAL)); + Ptr em = EM::create(); + em->setClustersNumber(componentCount); + em->setCovarianceMatrixType(EM::COV_MAT_DIAGONAL); + em->trainEM(modelSamples, noArray(), noArray(), noArray()); + em_models[i] = em; } } @@ -332,33 +347,20 @@ int main() imshow( "NormalBayesClassifier", imgDst ); #endif #if _KNN_ - int K = 3; - find_decision_boundary_KNN( K ); + find_decision_boundary_KNN( 3 ); imshow( "kNN", imgDst ); - K = 15; - find_decision_boundary_KNN( K ); + find_decision_boundary_KNN( 15 ); imshow( "kNN2", imgDst ); #endif #if _SVM_ //(1)-(2)separable and not sets - SVM::Params params; - params.svmType = SVM::C_SVC; - params.kernelType = SVM::POLY; //CvSVM::LINEAR; - params.degree = 0.5; - params.gamma = 1; - params.coef0 = 1; - params.C = 1; - params.nu = 0.5; - params.p = 0; - params.termCrit = TermCriteria(TermCriteria::MAX_ITER+TermCriteria::EPS, 1000, 0.01); - - find_decision_boundary_SVM( params ); + + find_decision_boundary_SVM( 1 ); imshow( "classificationSVM1", imgDst ); - params.C = 10; - find_decision_boundary_SVM( params ); + find_decision_boundary_SVM( 10 ); imshow( "classificationSVM2", imgDst ); #endif diff --git a/samples/cpp/train_HOG.cpp b/samples/cpp/train_HOG.cpp index dfecb0e03f..68c7f56aae 100644 --- a/samples/cpp/train_HOG.cpp +++ b/samples/cpp/train_HOG.cpp @@ -141,7 +141,7 @@ Mat get_hogdescriptor_visu(const Mat& color_origImg, vector& descriptorVa int cellSize = 8; int gradientBinSize = 9; - float radRangeForOneBin = (float)(CV_PI/(float)gradientBinSize); // dividing 180° into 9 bins, how large (in rad) is one bin? + float radRangeForOneBin = (float)(CV_PI/(float)gradientBinSize); // dividing 180 into 9 bins, how large (in rad) is one bin? // prepare data structure: 9 orientation / gradient strenghts for each cell int cells_in_x_dir = DIMX / cellSize; @@ -313,23 +313,23 @@ void compute_hog( const vector< Mat > & img_lst, vector< Mat > & gradient_lst, c void train_svm( const vector< Mat > & gradient_lst, const vector< int > & labels ) { - /* Default values to train SVM */ - SVM::Params params; - params.coef0 = 0.0; - params.degree = 3; - params.termCrit.epsilon = 1e-3; - params.gamma = 0; - params.kernelType = SVM::LINEAR; - params.nu = 0.5; - params.p = 0.1; // for EPSILON_SVR, epsilon in loss function? - params.C = 0.01; // From paper, soft classifier - params.svmType = SVM::EPS_SVR; // C_SVC; // EPSILON_SVR; // may be also NU_SVR; // do regression task Mat train_data; convert_to_ml( gradient_lst, train_data ); clog << "Start training..."; - Ptr svm = StatModel::train(train_data, ROW_SAMPLE, Mat(labels), params); + Ptr svm = SVM::create(); + /* Default values to train SVM */ + svm->setCoef0(0.0); + svm->setDegree(3); + svm->setTermCriteria(TermCriteria( CV_TERMCRIT_ITER+CV_TERMCRIT_EPS, 1000, 1e-3 )); + svm->setGamma(0); + svm->setKernel(SVM::LINEAR); + svm->setNu(0.5); + svm->setP(0.1); // for EPSILON_SVR, epsilon in loss function? + svm->setC(0.01); // From paper, soft classifier + svm->setType(SVM::EPS_SVR); // C_SVC; // EPSILON_SVR; // may be also NU_SVR; // do regression task + svm->train(train_data, ROW_SAMPLE, Mat(labels)); clog << "...[done]" << endl; svm->save( "my_people_detector.yml" ); diff --git a/samples/cpp/tree_engine.cpp b/samples/cpp/tree_engine.cpp index 6defc31c50..2d6824d24d 100644 --- a/samples/cpp/tree_engine.cpp +++ b/samples/cpp/tree_engine.cpp @@ -73,18 +73,42 @@ int main(int argc, char** argv) data->setTrainTestSplitRatio(train_test_split_ratio); printf("======DTREE=====\n"); - Ptr dtree = DTrees::create(DTrees::Params( 10, 2, 0, false, 16, 0, false, false, Mat() )); + Ptr dtree = DTrees::create(); + dtree->setMaxDepth(10); + dtree->setMinSampleCount(2); + dtree->setRegressionAccuracy(0); + dtree->setUseSurrogates(false); + dtree->setMaxCategories(16); + dtree->setCVFolds(0); + dtree->setUse1SERule(false); + dtree->setTruncatePrunedTree(false); + dtree->setPriors(Mat()); train_and_print_errs(dtree, data); if( (int)data->getClassLabels().total() <= 2 ) // regression or 2-class classification problem { printf("======BOOST=====\n"); - Ptr boost = Boost::create(Boost::Params(Boost::GENTLE, 100, 0.95, 2, false, Mat())); + Ptr boost = Boost::create(); + boost->setBoostType(Boost::GENTLE); + boost->setWeakCount(100); + boost->setWeightTrimRate(0.95); + boost->setMaxDepth(2); + boost->setUseSurrogates(false); + boost->setPriors(Mat()); train_and_print_errs(boost, data); } printf("======RTREES=====\n"); - Ptr rtrees = RTrees::create(RTrees::Params(10, 2, 0, false, 16, Mat(), false, 0, TermCriteria(TermCriteria::MAX_ITER, 100, 0))); + Ptr rtrees = RTrees::create(); + rtrees->setMaxDepth(10); + rtrees->setMinSampleCount(2); + rtrees->setRegressionAccuracy(0); + rtrees->setUseSurrogates(false); + rtrees->setMaxCategories(16); + rtrees->setPriors(Mat()); + rtrees->setCalculateVarImportance(false); + rtrees->setActiveVarCount(0); + rtrees->setTermCriteria(TermCriteria(TermCriteria::MAX_ITER, 100, 0)); train_and_print_errs(rtrees, data); return 0; diff --git a/samples/cpp/tutorial_code/ml/introduction_to_svm/introduction_to_svm.cpp b/samples/cpp/tutorial_code/ml/introduction_to_svm/introduction_to_svm.cpp index e90882b67a..0513e367d6 100644 --- a/samples/cpp/tutorial_code/ml/introduction_to_svm/introduction_to_svm.cpp +++ b/samples/cpp/tutorial_code/ml/introduction_to_svm/introduction_to_svm.cpp @@ -14,23 +14,30 @@ int main(int, char**) Mat image = Mat::zeros(height, width, CV_8UC3); // Set up training data + //! [setup1] int labels[4] = {1, -1, -1, -1}; - Mat labelsMat(4, 1, CV_32SC1, labels); - float trainingData[4][2] = { {501, 10}, {255, 10}, {501, 255}, {10, 501} }; + //! [setup1] + //! [setup2] Mat trainingDataMat(4, 2, CV_32FC1, trainingData); + Mat labelsMat(4, 1, CV_32SC1, labels); + //! [setup2] - // Set up SVM's parameters - SVM::Params params; - params.svmType = SVM::C_SVC; - params.kernelType = SVM::LINEAR; - params.termCrit = TermCriteria(TermCriteria::MAX_ITER, 100, 1e-6); // Train the SVM - Ptr svm = StatModel::train(trainingDataMat, ROW_SAMPLE, labelsMat, params); + //! [init] + Ptr svm = SVM::create(); + svm->setType(SVM::C_SVC); + svm->setKernel(SVM::LINEAR); + svm->setTermCriteria(TermCriteria(TermCriteria::MAX_ITER, 100, 1e-6)); + //! [init] + //! [train] + svm->train(trainingDataMat, ROW_SAMPLE, labelsMat); + //! [train] - Vec3b green(0,255,0), blue (255,0,0); // Show the decision regions given by the SVM + //! [show] + Vec3b green(0,255,0), blue (255,0,0); for (int i = 0; i < image.rows; ++i) for (int j = 0; j < image.cols; ++j) { @@ -42,16 +49,20 @@ int main(int, char**) else if (response == -1) image.at(i,j) = blue; } + //! [show] // Show the training data + //! [show_data] int thickness = -1; int lineType = 8; circle( image, Point(501, 10), 5, Scalar( 0, 0, 0), thickness, lineType ); circle( image, Point(255, 10), 5, Scalar(255, 255, 255), thickness, lineType ); circle( image, Point(501, 255), 5, Scalar(255, 255, 255), thickness, lineType ); circle( image, Point( 10, 501), 5, Scalar(255, 255, 255), thickness, lineType ); + //! [show_data] // Show support vectors + //! [show_vectors] thickness = 2; lineType = 8; Mat sv = svm->getSupportVectors(); @@ -61,6 +72,7 @@ int main(int, char**) const float* v = sv.ptr(i); circle( image, Point( (int) v[0], (int) v[1]), 6, Scalar(128, 128, 128), thickness, lineType); } + //! [show_vectors] imwrite("result.png", image); // save the image diff --git a/samples/cpp/tutorial_code/ml/non_linear_svms/non_linear_svms.cpp b/samples/cpp/tutorial_code/ml/non_linear_svms/non_linear_svms.cpp index c40a17e9aa..b221ab5f18 100644 --- a/samples/cpp/tutorial_code/ml/non_linear_svms/non_linear_svms.cpp +++ b/samples/cpp/tutorial_code/ml/non_linear_svms/non_linear_svms.cpp @@ -39,6 +39,7 @@ int main() // Set up the linearly separable part of the training data int nLinearSamples = (int) (FRAC_LINEAR_SEP * NTRAINING_SAMPLES); + //! [setup1] // Generate random points for the class 1 Mat trainClass = trainData.rowRange(0, nLinearSamples); // The x coordinate of the points is in [0, 0.4) @@ -56,9 +57,10 @@ int main() // The y coordinate of the points is in [0, 1) c = trainClass.colRange(1,2); rng.fill(c, RNG::UNIFORM, Scalar(1), Scalar(HEIGHT)); + //! [setup1] //------------------ Set up the non-linearly separable part of the training data --------------- - + //! [setup2] // Generate random points for the classes 1 and 2 trainClass = trainData.rowRange( nLinearSamples, 2*NTRAINING_SAMPLES-nLinearSamples); // The x coordinate of the points is in [0.4, 0.6) @@ -67,24 +69,28 @@ int main() // The y coordinate of the points is in [0, 1) c = trainClass.colRange(1,2); rng.fill(c, RNG::UNIFORM, Scalar(1), Scalar(HEIGHT)); - + //! [setup2] //------------------------- Set up the labels for the classes --------------------------------- labels.rowRange( 0, NTRAINING_SAMPLES).setTo(1); // Class 1 labels.rowRange(NTRAINING_SAMPLES, 2*NTRAINING_SAMPLES).setTo(2); // Class 2 //------------------------ 2. Set up the support vector machines parameters -------------------- - SVM::Params params; - params.svmType = SVM::C_SVC; - params.C = 0.1; - params.kernelType = SVM::LINEAR; - params.termCrit = TermCriteria(TermCriteria::MAX_ITER, (int)1e7, 1e-6); - //------------------------ 3. Train the svm ---------------------------------------------------- cout << "Starting training process" << endl; - Ptr svm = StatModel::train(trainData, ROW_SAMPLE, labels, params); + //! [init] + Ptr svm = SVM::create(); + svm->setType(SVM::C_SVC); + svm->setC(0.1); + svm->setKernel(SVM::LINEAR); + svm->setTermCriteria(TermCriteria(TermCriteria::MAX_ITER, (int)1e7, 1e-6)); + //! [init] + //! [train] + svm->train(trainData, ROW_SAMPLE, labels); + //! [train] cout << "Finished training process" << endl; //------------------------ 4. Show the decision regions ---------------------------------------- + //! [show] Vec3b green(0,100,0), blue (100,0,0); for (int i = 0; i < I.rows; ++i) for (int j = 0; j < I.cols; ++j) @@ -95,8 +101,10 @@ int main() if (response == 1) I.at(j, i) = green; else if (response == 2) I.at(j, i) = blue; } + //! [show] //----------------------- 5. Show the training data -------------------------------------------- + //! [show_data] int thick = -1; int lineType = 8; float px, py; @@ -114,8 +122,10 @@ int main() py = trainData.at(i,1); circle(I, Point( (int) px, (int) py ), 3, Scalar(255, 0, 0), thick, lineType); } + //! [show_data] //------------------------- 6. Show support vectors -------------------------------------------- + //! [show_vectors] thick = 2; lineType = 8; Mat sv = svm->getSupportVectors(); @@ -125,6 +135,7 @@ int main() const float* v = sv.ptr(i); circle( I, Point( (int) v[0], (int) v[1]), 6, Scalar(128, 128, 128), thick, lineType); } + //! [show_vectors] imwrite("result.png", I); // save the Image imshow("SVM for Non-Linear Training Data", I); // show it to the user