1. 程式人生 > >OpenCV中CvSVM部分函式解讀

OpenCV中CvSVM部分函式解讀

CvSVM::predict函式解析:不管是Mat介面還是CvMat介面最終都是通過指標的形式呼叫的,也就是最終都是呼叫的以下函式實現的 float CvSVM::predict( const float* row_sample, int row_len, bool returnDFVal ) const { // 首先確保建立了核函式輸入了樣本     assert( kernel );     assert( row_sample ); // 樣本的長度,也就是特徵的維數必須匹配     int var_count = get_var_count();     assert( row_len == var_count );
    (void)row_len; // 不知道幹啥的 // 計算類別數目     int class_count = class_labels ? class_labels->cols :                   params.svm_type == ONE_CLASS ? 1 : 0;     float result = 0;     cv::AutoBuffer<float> _buffer(sv_total + (class_count+1)*2);     float* buffer = _buffer; // 對於迴歸或者一類使用以下函式     if( params.svm_type == EPS_SVR ||
        params.svm_type == NU_SVR ||         params.svm_type == ONE_CLASS )     {         CvSVMDecisionFunc* df = (CvSVMDecisionFunc*)decision_func;         int i, sv_count = df->sv_count;         double sum = -df->rho; // 計算最優平面與當前樣本點之間的距離 kernel->calc( sv_count, var_count, (const float**)sv, row_sample, buffer );
        for( i = 0; i < sv_count; i++ )             sum += buffer[i]*df->alpha[i];         result = params.svm_type == ONE_CLASS ? (float)(sum > 0) : (float)sum;     } // 對於分類問題使用以下方法     else if( params.svm_type == C_SVC ||              params.svm_type == NU_SVC )     {         CvSVMDecisionFunc* df = (CvSVMDecisionFunc*)decision_func;         int* vote = (int*)(buffer + sv_total);         int i, j, k;         memset( vote, 0, class_count*sizeof(vote[0])); // 計算樣本點到所有支援向量之間的距離,sum(xi-yi),其中xi表示樣本點,yi表示支援向量,i=1:N,N表示一個樣本的特徵維度 kernel->calc( sv_total, var_count, (const float**)sv, row_sample, buffer );         double sum = 0.; // 對於二分類問題以下函式只執行一遍         for( i = 0; i < class_count; i++ )         {             for( j = i+1; j < class_count; j++, df++ )             { // 獲取樣本點到最優分介面的距離:buffer中存放測試樣本與每個支援向量距離度量值                 sum = -df->rho;                 int sv_count = df->sv_count;                 for( k = 0; k < sv_count; k++ )                     sum += df->alpha[k]*buffer[df->sv_index[k]]; // sv_index儲存的是支援向量index,對於2分類實際sv_index[k]=k // sum大於0表示屬於第一個類別,也就是類別標記較小的那個類別,對於二分類問題也就是負樣本                 vote[sum > 0 ? i : j]++;             }         }         // 統計每個類別的投票次數,注意大於0投票給了0也就是0為負樣本,如果一次也沒有投票的話,預設輸出的也是負樣本         for( i = 1, k = 0; i < class_count; i++ )         {             if( vote[i] > vote[k] )                 k = i;         } // returnDFVal只對於2分類問題有效,sum大於0表示屬於類別標記較小的那個類別也就是-1,sum小於0表示屬於類別標記較大的那個類別也就是+1         result = returnDFVal && class_count == 2 ? (float)sum : (float)(class_labels->data.i[k]);     }     else         CV_Error( CV_StsBadArg, "INTERNAL ERROR: Unknown SVM type, "                                 "the SVM structure is probably corrupted" );     return result; } kernel->calc( sv_total, var_count, (const float**)sv, row_sample, buffer ); 
計算樣本點到支援向量點間的距離,結果儲存到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

相關推薦

OpenCVCvSVM部分函式解讀

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 基礎   今天的低價單孔攝像機(照相機)會給影

最近鄰插值和雙線性插值的基本原理 以及OpenCVresize函式的用法改變影象的大小

最近鄰插值和雙線性插值的基本原理 影象的縮放很好理解,就是影象的放大和縮小。傳統的繪畫工具中,有一種叫做“放大尺”的繪畫工具,畫家常用它來放大圖畫。當然,在計算機上,我們不再需要用放大尺去放大或縮小影象了,把這個工作交給程式來完成就可以了。下面就來講講計算機怎麼來放大縮小圖象;在本文中,

OpenCV的cvRound()、cvFloor()、 cvCeil()函式講解

功能:cvRound(), cvFloor(), cvCeil()函式講解。 函式cvRound,cvFloor,cvCeil 都是用一種舍入的方法將輸入浮點數轉換成整數: cvRound():返回跟引數最接近的整數值,即四捨五入; cvFl

OpencvcopyTo()函式的使用方法

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