diff --git a/modules/ml/doc/pics/SVM_Comparison.png b/modules/ml/doc/pics/SVM_Comparison.png new file mode 100644 index 0000000000000000000000000000000000000000..4bb3dababc22b65525dc486593574de011a922b7 Binary files /dev/null and b/modules/ml/doc/pics/SVM_Comparison.png differ diff --git a/modules/ml/doc/support_vector_machines.rst b/modules/ml/doc/support_vector_machines.rst index b0d23ae863abe0f01caf9b5bccaaaa29ce261719..723dda087386f8b2fbc88b9c980a83dd461c0832 100644 --- a/modules/ml/doc/support_vector_machines.rst +++ b/modules/ml/doc/support_vector_machines.rst @@ -115,9 +115,13 @@ The constructors. * **CvSVM::SIGMOID** Sigmoid kernel: :math:`K(x_i, x_j) = \tanh(\gamma x_i^T x_j + coef0)`. + * **CvSVM::CHI2** Exponential Chi2 kernel, similar to the RBF kernel: :math:`K(x_i, x_j) = e^{-\gamma \chi^2(x_i,x_j)}, \chi^2(x_i,x_j) = (x_i-x_j)^2/(x_i+x_j), \gamma > 0`. + + * **CvSVM::INTER** Histogram intersection kernel. A fast kernel. :math:`K(x_i, x_j) = min(x_i,x_j)`. + :param degree: Parameter ``degree`` of a kernel function (POLY). - :param gamma: Parameter :math:`\gamma` of a kernel function (POLY / RBF / SIGMOID). + :param gamma: Parameter :math:`\gamma` of a kernel function (POLY / RBF / SIGMOID / CHI2). :param coef0: Parameter ``coef0`` of a kernel function (POLY / SIGMOID). @@ -142,6 +146,10 @@ The default constructor initialize the structure with following values: term_crit = cvTermCriteria( CV_TERMCRIT_ITER+CV_TERMCRIT_EPS, 1000, FLT_EPSILON ); } +A comparison of different kernels on the following 2D test case with four classes. Four C_SVC SVMs have been trained (one against rest) with auto_train. Evaluation on three different kernels (CHI2, INTER, RBF). The color depicts the class with max score. Bright means max-score > 0, dark means max-score < 0. + +.. image:: pics/SVM_Comparison.png + CvSVM diff --git a/modules/ml/include/opencv2/ml/ml.hpp b/modules/ml/include/opencv2/ml/ml.hpp index 80ad0e790103323d200dab25f7d80ed5870b3c03..700dd19ec7c93bc4d47dedbe0de8ce0984255ef2 100644 --- a/modules/ml/include/opencv2/ml/ml.hpp +++ b/modules/ml/include/opencv2/ml/ml.hpp @@ -297,7 +297,7 @@ struct CV_EXPORTS_W_MAP CvSVMParams CV_PROP_RW int svm_type; CV_PROP_RW int kernel_type; CV_PROP_RW double degree; // for poly - CV_PROP_RW double gamma; // for poly/rbf/sigmoid + CV_PROP_RW double gamma; // for poly/rbf/sigmoid/chi2 CV_PROP_RW double coef0; // for poly/sigmoid CV_PROP_RW double C; // for CV_SVM_C_SVC, CV_SVM_EPS_SVR and CV_SVM_NU_SVR @@ -326,7 +326,10 @@ struct CV_EXPORTS CvSVMKernel virtual void calc_non_rbf_base( int vec_count, int vec_size, const float** vecs, const float* another, float* results, double alpha, double beta ); - + virtual void calc_intersec( int vcount, int var_count, const float** vecs, + const float* another, float* results ); + virtual void calc_chi2( int vec_count, int vec_size, const float** vecs, + const float* another, float* results ); virtual void calc_linear( int vec_count, int vec_size, const float** vecs, const float* another, float* results ); virtual void calc_rbf( int vec_count, int vec_size, const float** vecs, @@ -456,7 +459,7 @@ public: enum { C_SVC=100, NU_SVC=101, ONE_CLASS=102, EPS_SVR=103, NU_SVR=104 }; // SVM kernel type - enum { LINEAR=0, POLY=1, RBF=2, SIGMOID=3 }; + enum { LINEAR=0, POLY=1, RBF=2, SIGMOID=3, CHI2=4, INTER=5 }; // SVM params type enum { C=0, GAMMA=1, P=2, NU=3, COEF=4, DEGREE=5 }; diff --git a/modules/ml/src/svm.cpp b/modules/ml/src/svm.cpp index 9cbc46be845b222e40ea1d5bf9da8825511d9a6f..be662f459399167fe4ac4b0f031bae22b6a4de63 100644 --- a/modules/ml/src/svm.cpp +++ b/modules/ml/src/svm.cpp @@ -220,6 +220,8 @@ bool CvSVMKernel::create( const CvSVMParams* _params, Calc _calc_func ) calc_func = params->kernel_type == CvSVM::RBF ? &CvSVMKernel::calc_rbf : params->kernel_type == CvSVM::POLY ? &CvSVMKernel::calc_poly : params->kernel_type == CvSVM::SIGMOID ? &CvSVMKernel::calc_sigmoid : + params->kernel_type == CvSVM::CHI2 ? &CvSVMKernel::calc_chi2 : + params->kernel_type == CvSVM::INTER ? &CvSVMKernel::calc_intersec : &CvSVMKernel::calc_linear; return true; @@ -318,6 +320,52 @@ void CvSVMKernel::calc_rbf( int vcount, int var_count, const float** vecs, cvExp( &R, &R ); } +/// Histogram intersection kernel +void CvSVMKernel::calc_intersec( int vcount, int var_count, const float** vecs, + const float* another, Qfloat* results ) +{ + int j, k; + for( j = 0; j < vcount; j++ ) + { + const float* sample = vecs[j]; + double s = 0; + for( k = 0; k <= var_count - 4; k += 4 ) + s += min(sample[k],another[k]) + min(sample[k+1],another[k+1]) + + min(sample[k+2],another[k+2]) + min(sample[k+3],another[k+3]); + for( ; k < var_count; k++ ) + s += min(sample[k],another[k]); + results[j] = (Qfloat)(s); + } +} + +/// Exponential chi2 kernel +void CvSVMKernel::calc_chi2( int vcount, int var_count, const float** vecs, + const float* another, Qfloat* results ) +{ + CvMat R = cvMat( 1, vcount, QFLOAT_TYPE, results ); + double gamma = -params->gamma; + int j, k; + for( j = 0; j < vcount; j++ ) + { + const float* sample = vecs[j]; + double chi2 = 0; + for(k = 0 ; k < var_count; k++ ) + { + double d = sample[k]-another[k]; + double devisor = sample[k]+another[k]; + /// if devisor == 0, the Chi2 distance would be zero, but calculation would rise an error because of deviding by zero + if (devisor != 0) + { + chi2 += d*d/devisor; + } + } + results[j] = (Qfloat) (gamma*chi2); + } + if( vcount > 0 ) + cvExp( &R, &R ); + + +} void CvSVMKernel::calc( int vcount, int var_count, const float** vecs, const float* another, Qfloat* results ) @@ -1214,7 +1262,8 @@ bool CvSVM::set_params( const CvSVMParams& _params ) svm_type = params.svm_type; if( kernel_type != LINEAR && kernel_type != POLY && - kernel_type != SIGMOID && kernel_type != RBF ) + kernel_type != SIGMOID && kernel_type != RBF && + kernel_type != INTER && kernel_type != CHI2) CV_ERROR( CV_StsBadArg, "Unknown/unsupported kernel type" ); if( kernel_type == LINEAR )