1. 程式人生 > >OpenCV函式解讀之groupRectangles

OpenCV函式解讀之groupRectangles

不管新版本的CascadeClassifier,還是老版本的HAAR檢測函式cvHaarDetectObjects,都使用了groupRectangles函式進行視窗的組合,其函式原型有以下幾個: CV_EXPORTS void groupRectangles(CV_OUT CV_IN_OUT vector<Rect>& rectList, int groupThreshold, double eps=0.2); CV_EXPORTS_W void groupRectangles(CV_OUT CV_IN_OUT vector<Rect>& rectList, CV_OUT vector<int>& weights, int groupThreshold, double eps=0.2); CV_EXPORTS void groupRectangles( vector<Rect>& rectList, int groupThreshold, double eps, vector<int>* weights, vector<double>* levelWeights ); CV_EXPORTS void groupRectangles(vector<Rect>& rectList, vector<int>& rejectLevels,                                 vector<double>& levelWeights, int groupThreshold, double eps=0.2); CV_EXPORTS void groupRectangles_meanshift(vector<Rect>& rectList, vector<double>& foundWeights, vector<double>& foundScales,                                           double detectThreshold = 0.0, Size winDetSize = Size(64, 128)); 最後一個函式新增mean shift進行組合聚類,下面針對groupRectangles函式進行說明(前三個函式都呼叫了引數最多的第四個函式實現): rectList:帶組合的視窗,即作為輸入又作為輸出 rejectLevels:通過分類器的stage數,一般不小於stage總數-4,也就是weights levelWeights:通過上述stage數的輸出權重,也就是通過的stage數的所有node之和,裡面即包含left_val又right_val,同一個node只包含其中的一個 groupThreshold:組合閾值,當沒有輸入rejectLevels的時候,當待合併的視窗數大於該閾值的時候才可能進行合併,否則放棄;當輸入rejectLevels的時候,當前組合下通過檢測的stage最大值數大於該閾值的時候才可能進行合併,否則放棄
eps:待合併的兩個視窗的相關性,從矩形所在位置的畫素差值考慮,當eps為0的時候不進行合併,直接返回 該函式的內部執行流程 1) 當組合閾值groupThreshold小於等於0的時候,如果輸出weights,則weights中返回與rectList同樣個數個1,函式直接返回,不進行合併操作 2) 呼叫partition函式對rectList中的矩形進行分類     vector<int> labels;     int nclasses = partition(rectList, labels, SimilarRects(eps)); 其中nclasses表示組合類別,labels表示每個rect屬於哪個類別的,相似度計算使用SimilarRects類 值得一提的是,該函式的呼叫必須輸入不相交的計算方法,在groupRectangles函式中使用SimilarRects計算相似度,輸入引數為eps,相似的矩形是要被分為同一類的     SimilarRect中計算相似度的方法:     inline bool operator()(const Rect& r1, const Rect& r2) const     {         // delta為最小長寬的eps倍         double delta = eps*(std::min(r1.width, r2.width) + std::min(r1.height, r2.height))*0.5;         // 如果矩形的四個頂點的位置差別都小於delta,則表示相似的矩形         return std::abs(r1.x - r2.x) <= delta &&                std::abs(r1.y - r2.y) <= delta &&                std::abs(r1.x + r1.width - r2.x - r2.width) <= delta &&                std::abs(r1.y + r1.height - r2.y - r2.height) <= delta;     } 3) 組合分到同一類別的矩形並儲存當前類別下通過stage的最大值以及最大的權重     for( i = 0; i < nlabels; i++ )     {         int cls = labels[i];         rrects[cls].x += rectList[i].x;         rrects[cls].y += rectList[i].y;         rrects[cls].width += rectList[i].width;         rrects[cls].height += rectList[i].height;         rweights[cls]++;     }     for( i = 0; i < nclasses; i++ )     {         Rect r = rrects[i];         float s = 1.f/rweights[i];         rrects[i] = Rect(saturate_cast<int>(r.x*s),              saturate_cast<int>(r.y*s),              saturate_cast<int>(r.width*s),              saturate_cast<int>(r.height*s));     }     for( i = 0; i < nlabels; i++ )     {         int cls = labels[i];         if( (*weights)[i] > rejectLevels[cls] )         {             rejectLevels[cls] = (*weights)[i];             rejectWeights[cls] = (*levelWeights)[i];          }          else if( ( (*weights)[i] == rejectLevels[cls] ) && ( (*levelWeights)[i] > rejectWeights[cls] ) )             rejectWeights[cls] = (*levelWeights)[i];      } 4) 按照groupThreshold合併規則,以及是否存在包含關係輸出合併後的矩形     for( i = 0; i < nclasses; i++ )     {         Rect r1 = rrects[i];         int n1 = levelWeights ? rejectLevels[i] : rweights[i];         double w1 = rejectWeights[i];         // 合併的矩形數小於等於組合閾值不進行輸出         if( n1 <= groupThreshold )             continue;         // filter out small face rectangles inside large rectangles         for( j = 0; j < nclasses; j++ )         {             int n2 = rweights[j];             if( j == i || n2 <= groupThreshold )                 continue;             Rect r2 = rrects[j];             int dx = saturate_cast<int>( r2.width * eps );             int dy = saturate_cast<int>( r2.height * eps );             // 當r1在r2的內部的時候,停止             if( i != j &&                 r1.x >= r2.x - dx &&                 r1.y >= r2.y - dy &&                 r1.x + r1.width <= r2.x + r2.width + dx &&                 r1.y + r1.height <= r2.y + r2.height + dy &&                 (n2 > std::max(3, n1) || n1 < 3) )                 break;         }         // r1不在r2的內部時j才可能等於nclasses         if( j == nclasses )         {             rectList.push_back(r1);             if( weights )                 weights->push_back(n1);             if( levelWeights )                 levelWeights->push_back(w1);         }     }