OpenCV中CvSVM部分函式解讀
計算樣本點到支援向量點間的距離,結果儲存到buffer中,以線性核函式為例 從上面的分析可以看出,如果不更改rho或者alpha的符號,計算的是-rho+w*x的結果,大於0表示負樣本,小於0表示正樣本;因此在HOGDescriptor中需要將alpha更改符號,直接儲存rho即可,這樣給出的結果rho-alpha*x,大於0表示正樣本! w=alpha*sv; alpha中存放每個支援向量的權重,sv表示每個支援向量,w為加權後的支援向量 void CvSVMKernel::calc( int vcount, int var_count, const float** vecs, const float* another, Qfloat* results ) { const Qfloat max_val = (Qfloat)(FLT_MAX*1e-3); int j; // 對於核函式為Liner時呼叫線性核函式進行計算 (this->*calc_func)( vcount, var_count, vecs, another, results ); // 檢查是否越界 for( j = 0; j < vcount; j++ ) { if( results[j] > max_val ) results[j] = max_val; } } 線性核函式又呼叫non_rbf_base核函式進行計算 void CvSVMKernel::calc_linear( int vcount, int var_count, const float** vecs, const float* another, Qfloat* results ) { calc_non_rbf_base( vcount, var_count, vecs, another, results, 1, 0 ); } vcount表示支援向量的個數,也就是位於正負樣本分介面上的正負樣本總數 var_count表示樣本的特徵維度 vecs表示支援向量,也就是位於正負樣本分解面上的正負樣本 another表示測試樣本 result表示對於每個支援向量的累加結果值 void CvSVMKernel::calc_non_rbf_base( int vcount, int var_count, const float** vecs, const float* another, Qfloat* results, double alpha, double beta ) { int j, k; // 對於每一個支援向量,或者每一個位於分介面的訓練樣本 for( j = 0; j < vcount; j++ ) { // 獲取第j個支援向量或者說第j個樣本 const float* sample = vecs[j]; double s = 0; // 特徵維度大於4的時候進行優化 for( k = 0; k <= var_count - 4; k += 4 ) s += sample[k]*another[k] + sample[k+1]*another[k+1] + sample[k+2]*another[k+2] + sample[k+3]*another[k+3]; // 計算其餘的維度 for( ; k < var_count; k++ ) s += sample[k]*another[k]; // 計算測試樣本與第j個樣本之間的距離 results[j] = (Qfloat)(s*alpha + beta); // 注意這裡的alpha和beta為每個支援向量的權重,對於線性核函式而言alpha=1,beta=0可以從calc_linear函式看出 } } // RBF核函式,遵循RBF核函式公式 void CvSVMKernel::calc_rbf( 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; // vcount=sv_count for( j = 0; j < vcount; j++ ) { const float* sample = vecs[j]; double s = 0; // var_count=feature_num for( k = 0; k <= var_count - 4; k += 4 ) { double t0 = sample[k] - another[k]; double t1 = sample[k+1] - another[k+1]; s += t0*t0 + t1*t1; t0 = sample[k+2] - another[k+2]; t1 = sample[k+3] - another[k+3]; s += t0*t0 + t1*t1; } for( ; k < var_count; k++ ) { double t0 = sample[k] - another[k]; s += t0*t0; } results[j] = (Qfloat)(s*gamma); } if( vcount > 0 ) cvExp( &R, &R ); } // 根據交叉次數自動訓練函式,CvParamGrid為訓練引數建構函式,其包含三個引數(min_val,max_val,step) // Param Grid的檢查函式,最大值不小於最小值,最小值不小於DBL_EPSILON,step不小於1(應該是不小於等於1) bool CvParamGrid::check() const { bool ok = false; CV_FUNCNAME( "CvParamGrid::check" ); __BEGIN__; if( min_val > max_val ) CV_ERROR( CV_StsBadArg, "Lower bound of the grid must be less then the upper one" ); if( min_val < DBL_EPSILON ) CV_ERROR( CV_StsBadArg, "Lower bound of the grid must be positive" ); if( step < 1. + FLT_EPSILON ) CV_ERROR( CV_StsBadArg, "Grid step must greater then 1" ); ok = true; __END__; return ok; } 如果不確定引數的值的範圍可以通過以下函式獲得,當然也可以通過一次次實驗不斷調整,最後得到一個大概的範圍後採用train_auto函式進行訓練 CvParamGrid CvSVM::get_default_grid( int param_id ) { CvParamGrid grid; if( param_id == CvSVM::C ) { grid.min_val = 0.1; grid.max_val = 500; grid.step = 5; // total iterations = 5 } else if( param_id == CvSVM::GAMMA ) { grid.min_val = 1e-5; grid.max_val = 0.6; grid.step = 15; // total iterations = 4 } else if( param_id == CvSVM::P ) { grid.min_val = 0.01; grid.max_val = 100; grid.step = 7; // total iterations = 4 } else if( param_id == CvSVM::NU ) { grid.min_val = 0.01; grid.max_val = 0.2; grid.step = 3; // total iterations = 3 } else if( param_id == CvSVM::COEF ) { grid.min_val = 0.1; grid.max_val = 300; grid.step = 14; // total iterations = 3 } else if( param_id == CvSVM::DEGREE ) { grid.min_val = 0.01; grid.max_val = 4; grid.step = 7; // total iterations = 3 } else cvError( CV_StsBadArg, "CvSVM::get_default_grid", "Invalid type of parameter " "(use one of CvSVM::C, CvSVM::GAMMA et al.)", __FILE__, __LINE__ ); return grid; } bool CvSVM::train_auto( const CvMat* _train_data, const CvMat* _responses, const CvMat* _var_idx, const CvMat* _sample_idx, CvSVMParams _params, int k_fold, CvParamGrid C_grid, CvParamGrid gamma_grid, CvParamGrid p_grid, CvParamGrid nu_grid, CvParamGrid coef_grid, CvParamGrid degree_grid, bool balanced) { bool ok = false; CvMat* responses = 0; CvMat* responses_local = 0; CvMemStorage* temp_storage = 0; const float** samples = 0; const float** samples_local = 0; CV_FUNCNAME( "CvSVM::train_auto" ); __BEGIN__; int svm_type, sample_count, var_count, sample_size; int block_size = 1 << 16; double* alpha; RNG* rng = &theRNG(); // all steps are logarithmic and must be > 1 // 步長都必須大於1,因為等於1的時候會造成死迴圈!,預設step=10 double degree_step = 10, g_step = 10, coef_step = 10, C_step = 10, nu_step = 10, p_step = 10; double gamma = 0, curr_c = 0, degree = 0, coef = 0, p = 0, nu = 0; double best_degree = 0, best_gamma = 0, best_coef = 0, best_C = 0, best_nu = 0, best_p = 0; float min_error = FLT_MAX, error; // SVMType為ONE_CLASS的時候不能進行自動訓練 if( _params.svm_type == CvSVM::ONE_CLASS ) { if(!train( _train_data, _responses, _var_idx, _sample_idx, _params )) EXIT; return true; } clear(); // 選擇的交叉驗證層數必須大於等於2,等於2的時候就是使用一個訓練另外一個測試,訓練兩次,測試兩次 // 等於10表示訓練10次,測試10次,選擇一組測試其餘9組訓練 if( k_fold < 2 ) CV_ERROR( CV_StsBadArg, "Parameter <k_fold> must be > 1" ); CV_CALL(set_params( _params )); svm_type = _params.svm_type; // All the parameters except, possibly, <coef0> are positive. // <coef0> is nonnegative // 檢查各個引數的值是否滿足要求,這是第一步檢查(步長不能小於1),後面根據SVM型別與Kernel型別還會進行二次檢查 if( C_grid.step <= 1 ) { C_grid.min_val = C_grid.max_val = params.C; C_grid.step = 10; } else CV_CALL(C_grid.check()); if( gamma_grid.step <= 1 ) { gamma_grid.min_val = gamma_grid.max_val = params.gamma; gamma_grid.step = 10; } else CV_CALL(gamma_grid.check()); if( p_grid.step <= 1 ) { p_grid.min_val = p_grid.max_val = params.p; p_grid.step = 10; } else CV_CALL(p_grid.check()); if( nu_grid.step <= 1 ) { nu_grid.min_val = nu_grid.max_val = params.nu; nu_grid.step = 10; } else CV_CALL(nu_grid.check()); if( coef_grid.step <= 1 ) { coef_grid.min_val = coef_grid.max_val = params.coef0; coef_grid.step = 10; } else CV_CALL(coef_grid.check()); if( degree_grid.step <= 1 ) { degree_grid.min_val = degree_grid.max_val = params.degree; degree_grid.step = 10; } else CV_CALL(degree_grid.check()); // these parameters are not used: // 二次檢查引數的值,根據核函式型別與SVM型別優化引數 if( params.kernel_type != CvSVM::POLY ) degree_grid.min_val = degree_grid.max_val = params.degree; if( params.kernel_type == CvSVM::LINEAR ) gamma_grid.min_val = gamma_grid.max_val = params.gamma; if( params.kernel_type != CvSVM::POLY && params.kernel_type != CvSVM::SIGMOID ) coef_grid.min_val = coef_grid.max_val = params.coef0; if( svm_type == CvSVM::NU_SVC || svm_type == CvSVM::ONE_CLASS ) C_grid.min_val = C_grid.max_val = params.C; if( svm_type == CvSVM::C_SVC || svm_type == CvSVM::EPS_SVR ) nu_grid.min_val = nu_grid.max_val = params.nu; if( svm_type != CvSVM::EPS_SVR ) p_grid.min_val = p_grid.max_val = params.p; CV_ASSERT( g_step > 1 && degree_step > 1 && coef_step > 1); CV_ASSERT( p_step > 1 && C_step > 1 && nu_step > 1 ); /* Prepare training data and related parameters */ // 實現資料的轉存,放到指標中 CV_CALL(cvPrepareTrainData( "CvSVM::train_auto", _train_data, CV_ROW_SAMPLE, svm_type != CvSVM::ONE_CLASS ? _responses : 0, svm_type == CvSVM::C_SVC || svm_type == CvSVM::NU_SVC ? CV_VAR_CATEGORICAL : CV_VAR_ORDERED, _var_idx, _sample_idx, false, &samples, &sample_count, &var_count, &var_all, &responses, &class_labels, &var_idx )); sample_size = var_count*sizeof(samples[0][0]); // make the storage block size large enough to fit all // the temporary vectors and output support vectors. block_size = MAX( block_size, sample_count*(int)sizeof(CvSVMKernelRow)); block_size = MAX( block_size, sample_count*2*(int)sizeof(double) + 1024 ); block_size = MAX( block_size, sample_size*2 + 1024 ); CV_CALL( storage = cvCreateMemStorage(block_size + sizeof(CvMemBlock) + sizeof(CvSeqBlock))); CV_CALL(temp_storage = cvCreateChildMemStorage(storage)); CV_CALL(alpha = (double*)cvMemStorageAlloc(temp_storage, sample_count*sizeof(double))); create_kernel(); create_solver(); { const int testset_size = sample_count/k_fold; // 每組測試集合大小 const int trainset_size = sample_count - testset_size; // 每組訓練集合大小 const int last_testset_size = sample_count - testset_size*(k_fold-1); // 最後一組測試集合大小,實際為testset_size const int last_trainset_size = sample_count - last_testset_size; // 最後一組訓練集合大小,實際trainset_size const bool is_regression = (svm_type == EPS_SVR) || (svm_type == NU_SVR); size_t resp_elem_size = CV_ELEM_SIZE(responses->type); size_t size = 2*last_trainset_size*sizeof(samples[0]); samples_local = (const float**) cvAlloc( size ); memset( samples_local, 0, size ); responses_local = cvCreateMat( 1, trainset_size, CV_MAT_TYPE(responses->type) ); cvZero( responses_local ); // randomly permute samples and responses // 隨機變更樣本和標籤的順序為了獲取分組 for(int i = 0; i < sample_count; i++ ) { int i1 = (*rng)(sample_count); int i2 = (*rng)(sample_count); const float* temp; float t; int y; CV_SWAP( samples[i1], samples[i2], temp ); if( is_regression ) CV_SWAP( responses->data.fl[i1], responses->data.fl[i2], t ); else CV_SWAP( responses->data.i[i1], responses->data.i[i2], y ); } // 如果是分類問題,並且是2分類,並且需要均衡化分組 if (!is_regression && class_labels->cols==2 && balanced) { // count class samples // responses中存放0和1,class_label中存放為0和1就對了,因此注意負樣本的標籤為0,正樣本的標籤為1!! int num_0=0,num_1=0; for (int i=0; i<sample_count; ++i) { if (responses->data.i[i]==class_labels->data.i[0]) ++num_0; else ++num_1; } // 哪個類別是較大的 int label_smallest_class; int label_biggest_class; if (num_0 < num_1) { label_biggest_class = class_labels->data.i[1]; label_smallest_class = class_labels->data.i[0]; } else { label_biggest_class = class_labels->data.i[0]; label_smallest_class = class_labels->data.i[1]; int y; CV_SWAP(num_0,num_1,y); } const double class_ratio = (double) num_0/sample_count; // calculate class ratio of each fold indexedratio *ratios=0; ratios = (indexedratio*) cvAlloc(k_fold*sizeof(*ratios)); for (int k=0, i_begin=0; k<k_fold; ++k, i_begin+=testset_size) { int count0=0; int count1=0; int i_end = i_begin + (k<k_fold-1 ? testset_size : last_testset_size); for (int i=i_begin; i<i_end; ++i) { if (responses->data.i[i]==label_smallest_class) ++count0; else ++count1; } ratios[k].ind = k; ratios[k].count_smallest = count0; ratios[k].count_biggest = count1; ratios[k].eval(); } // initial distance qsort(ratios, k_fold, sizeof(ratios[0]), icvCmpIndexedratio); double old_dist = 0.0; for (int k=0; k<k_fold; ++k) old_dist += abs(ratios[k].val-class_ratio); double new_dist = 1.0; // iterate to make the folds more balanced while (new_dist > 0.0) { if (ratios[0].count_biggest==0 || ratios[k_fold-1].count_smallest==0) break; // we are not able to swap samples anymore // what if we swap the samples, calculate the new distance ratios[0].count_smallest++; ratios[0].count_biggest--; ratios[0].eval(); ratios[k_fold-1].count_smallest--; ratios[k_fold-1].count_biggest++; ratios[k_fold-1].eval(); qsort(ratios, k_fold, sizeof(ratios[0]), icvCmpIndexedratio); new_dist = 0.0; for (int k=0; k<k_fold; ++k) new_dist += abs(ratios[k].val-class_ratio); if (new_dist < old_dist) { // swapping really improves, so swap the samples // index of the biggest_class sample from the minimum ratio fold int i1 = ratios[0].ind * testset_size; for ( ; i1<sample_count; ++i1) { if (responses->data.i[i1]==label_biggest_class) break; } // index of the smallest_class sample from the maximum ratio fold int i2 = ratios[k_fold-1].ind * testset_size; for ( ; i2<sample_count; ++i2) { if (responses->data.i[i2]==label_smallest_class) break; } // swap const float* temp; int y; CV_SWAP( samples[i1], samples[i2], temp ); CV_SWAP( responses->data.i[i1], responses->data.i[i2], y ); old_dist = new_dist; } else break; // does not improve, so break the loop } cvFree(&ratios); } // 遍歷每個引數進行測試,將最小錯誤率的檢測結果儲存下來 // 在自動話訓練時是按照如下方式進行的,當然針對不同的核函式與不同的SVM型別進行了優化 while(cur_val=min_val;cur_val<=max_val; cur_val*=step) int* cls_lbls = class_labels ? class_labels->data.i : 0; curr_c = C_grid.min_val; do { params.C = curr_c; gamma = gamma_grid.min_val; do { params.gamma = gamma; p = p_grid.min_val; do { params.p = p; nu = nu_grid.min_val; do { params.nu = nu; coef = coef_grid.min_val; do { params.coef0 = coef; degree = degree_grid.min_val; do
相關推薦
OpenCV中CvSVM部分函式解讀
CvSVM::predict函式解析:不管是Mat介面還是CvMat介面最終都是通過指標的形式呼叫的,也就是最終都是呼叫的以下函式實現的 float CvSVM::predict( const float* row_sample, int row_len, bool ret
OpenCV中的meanStdDev函式
OpenCV中的meanStdDev函式 2017年10月26日 20:34:31 liuxiangxxl 閱讀數:2812 版權宣告:本文為博主原創文章,未經博主允許不得轉載。 https://blog.csdn.net/liuxiangxxl/article/deta
0003-OpenCV中重對映函式remap的使用,以影象在x和y方向的翻轉為例!
首先介紹一下remap函式! remap就是用來做重對映的,重對映的含義這裡先不講,大家看完這篇文章就知道了! 函式原型如下: C++: void remap(InputArray src, OutputArray dst, InputArray map1, InputArray map2, i
OpenCV 中的split函式和merge函式 及示例
就讓我們來詳細介紹一下這兩個互為冤家的函式。首先是進行通道分離的split函式。 <1>split函式詳解 將一個多通道陣列分離成幾個單通道陣列。ps:這裡的array按語境譯為陣列或者陣列。 這個split函式的C++版本有兩個原型,他們分別是: C++:
OpenCV中的imshow函式深度剖析
imshow函式OpenCV官方註釋文件有一部分如下: Displays an image in the specified window. The function imshow display
openCV中的findHomography函式分析以及RANSAC演算法的詳解(原始碼分析)
本文將openCV中的RANSAC程式碼全部挑選出來,進行分析和講解,以便大家更好的理解RANSAC演算法。程式碼我都試過,可以直接執行。 在計算機視覺和影象處理等很多領域,都需要用到RANSAC演算法。openCV中也有封裝好的RANSAC演算法,以便於人們使用。關於RA
分類器是如何做檢測的?——CascadeClassifier中的detectMultiScale函式解讀
轉載註明出處即可。 在進入detectMultiScal函式之前,首先需要對CascadeClassifier做初始化。 1. 初始化——read函式 CascadeClassifier的初始化很簡單: cv::CascadeClassifier classifie
opencv 中的 cvPutText() 函式的使用
利用OpenCV進行英文字串的書寫是很容易的。只需要呼叫cvPutText()函式即可。該函式的申明如下所示: cvPutText(CvArr* img, const char* text, CvPoint origin, const CvFont* font, CvSca
Opencv 中圖形繪製函式 rectangle函式的使用
函式 rectangle: Opencv 原始碼: /** @brief Draws a simple, thick, or filled up-right rectangle. The function rectangle draws a rectangle out
libmkl_intel_thread.so中對部分函式未定義的引用
問題 在編譯bdf的時候出現瞭如下錯誤 /home/js/Documents/tool/intel2013/mkl/lib/intel64/libmkl_intel_thread.so:對‘omp_get_nested’未定義的引用 /home/js/D
OpenCV中的LUT函式(查表法)
簡單來說就是通過對映關係,將原影象的畫素值進行縮減操作,比如說 table[56]=5,就表示將畫素值為56的點對映為5,這種方法僅有賦值運算,不涉及乘除、加減運算,可以極大降低時間複雜度。程式原始碼:/* LUT查表函式的使用,結合滑動條函式 */ #include
部分opencv中的GPU加速函式(中文翻譯)
由於專案需要,翻譯了一部分可以用於我現在專案的opencv函式,記錄於此,原始英文文件來自於http://blog.csdn.net/mtt_sky/article/details/42607839。 getCudaEnableDeviceCount:返回已安裝CUDA裝置的數量;
OpenCV-Python官方文件三——在OpenCV中繪製函式
在OpenCV中繪製函式 目標 · 學習使用OpenCV繪製不同的幾何形狀 · 您將學習以下函式:cv2.line(),cv2.circle(),cv2.rectangle(),cv2.ellipse(),cv2.putText()等。 程式碼 &nbs
OpenCV-Python] OpenCV 中攝像機標定和 3D 重構 部分 VII
https://www.cnblogs.com/Undo-self-blog/p/8448500.html 42 攝像機標定 目標 • 學習攝像機畸變以及攝像機的內部引數和外部引數 • 學習找到這些引數,對畸變影象進行修復 42.1 基礎 今天的低價單孔攝像機(照相機)會給影
最近鄰插值和雙線性插值的基本原理 以及OpenCV中resize函式的用法改變影象的大小
最近鄰插值和雙線性插值的基本原理 影象的縮放很好理解,就是影象的放大和縮小。傳統的繪畫工具中,有一種叫做“放大尺”的繪畫工具,畫家常用它來放大圖畫。當然,在計算機上,我們不再需要用放大尺去放大或縮小影象了,把這個工作交給程式來完成就可以了。下面就來講講計算機怎麼來放大縮小圖象;在本文中,
OpenCV中的cvRound()、cvFloor()、 cvCeil()函式講解
功能:cvRound(), cvFloor(), cvCeil()函式講解。 函式cvRound,cvFloor,cvCeil 都是用一種舍入的方法將輸入浮點數轉換成整數: cvRound():返回跟引數最接近的整數值,即四捨五入; cvFl
Opencv中copyTo()函式的使用方法
https://www.cnblogs.com/phoenixdsg/p/8420716.html 在Mat矩陣類的成員函式中copyTo(roi , mask)函式是非常有用的一個函式,尤其是後面的mask可以實現蒙版的功能,我們用幾個例項來說明它的作用。我們要注意mask的資料型別,必須是C
OpenCV中響應滑鼠資訊cvSetMouseCallback函式的使用
分享一下我老師大神的人工智慧教程!零基礎,通俗易懂!http://blog.csdn.net/jiangjunshow 也歡迎大家轉載本篇文章。分享知識,造福人民,實現我們中華民族偉大復興!  
opencv中的cv2.cvtColor()函式中將BGR圖轉換為YCrCb及YCR_CB所使用的公式及程式碼驗證
【時間】2018.11.13 【題目】opencv中的cv2.cvtColor()函式中將BGR圖轉換為YCrCb及YCR_CB所使用的公式及程式碼驗證 概述 在opencv中,可以使用cv2.cvtColor()函式將BGR圖轉換為YCrCb及YCR_CB,本文主要講述他們所使用的由B
opencv中歸一化函式cv2.normalize()的原理講解
本篇文章參考部落格:https://blog.csdn.net/kuweicai/article/details/78988886 功能:歸一化函式 引數:Python: cv2.normalize(src[, dst[, alpha[, beta[, norm_type[, dt