日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

分类器是如何做检测的?(1)——CascadeClassifier中的detectMultiScale函数解读

發布時間:2025/3/21 编程问答 29 豆豆
生活随笔 收集整理的這篇文章主要介紹了 分类器是如何做检测的?(1)——CascadeClassifier中的detectMultiScale函数解读 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

在進入detectMultiScal函數之前,首先需要對CascadeClassifier做初始化。

1.???? 初始化——read函數

CascadeClassifier的初始化很簡單:

cv::CascadeClassifier classifier;

classifier.load(“cascade.xml”); //這里的xml是訓練得到的分類器xml

?

CascadeClassifier類中既有load也有read函數,二者是相同的,load將引用read函數。

1.1?? xml的結構

訓練得到的分類器以xml形式保存,整體上它包括stageType、featureType、height、width、stageParams、featureParams、stages、features幾個節點。


圖1. 分類器的Xml文件整體結構

除stages和features外,其他主要是一些分類器的參數。

Stages中包含15個stage(訓練程序設定),每個stage中包含多個weakClassifiers,而每個weakClassifier中又包含一個internalNodes和一個leafValues。internalNodes中四個變量代表一個node,分別為node中的left/right標記、特征池中的ID和threshold。leafValues中兩個變量代表一個node,分別為left leaf的值和right leaf的值。


圖2. 分類器的Xml文件具體結構

而features是分類器的特征池,每個特征包含一個矩形和要提取的特征序號(0~35)。


圖3. features的具體結構

1.2?? read的過程

下面是read代碼,主要包括從xml中獲取兩部分內容:data和featureEvaluator的讀取。

bool?CascadeClassifier::read(constFileNode&root)

{

????if( !data.read(root) )//data成員變量的讀取

????????return?false;

?

????// load features---特征的讀取

????featureEvaluator=?FeatureEvaluator::create(data.featureType);

????FileNodefn?=root[CC_FEATURES];

????if(?fn.empty() )

????????return?false;

?

????return?featureEvaluator->read(fn);

}

1.2.1?????????data成員變量的讀取

data的讀取中同樣可以分為兩部分:分類器參數讀取和stage分類樹的建立。

首先是參數部分的獲取。

static?constfloatTHRESHOLD_EPS= 1e-5f;

?

// load stage params

// stageTypeBOOST類型

????string?stageTypeStr?= (string)root[CC_STAGE_TYPE];

????if(?stageTypeStr?==?CC_BOOST)

????????stageType=?BOOST;

????else

????????return?false;

????//?這里以HOG特征分類器為例,featureType=2HOG

????string?featureTypeStr?= (string)root[CC_FEATURE_TYPE];

????if(?featureTypeStr?==?CC_HAAR)

????????featureType=?FeatureEvaluator::HAAR;

????else?if(?featureTypeStr==?CC_LBP?)

????????featureType=?FeatureEvaluator::LBP;

????else?if(?featureTypeStr==?CC_HOG?)

????????featureType=?FeatureEvaluator::HOG;

?

????else

????????return?false;

????//檢測窗口的最小size,也就是正樣本的size

????origWinSize.width?= (int)root[CC_WIDTH];

????origWinSize.height?= (int)root[CC_HEIGHT];

????CV_Assert(origWinSize.height> 0 &&origWinSize.width?> 0 );

????//我訓練得到的HOG分類器為true,還不清楚這里的意思

????isStumpBased= (int)(root[CC_STAGE_PARAMS][CC_MAX_DEPTH])== 1 ?true?:?false;

?

// load feature params

//?載入特征參數,HOG分類器下包括兩個參數:maxCatCountfeatSizefeatSize很透明,就是特征的種類數,這里為36,是指每個block4cell、每個cell9個梯度方向的直方圖。例如特征號為3時,計算的是當前窗口中劃分為4cell后第一個cell中所有點在120°方向(可能是,這要視起始角度而定)上分量的和,然后經過歸一化后的值。對于第二個參數maxCatCount,這里為0,尚不清楚(這是指代表一個弱分類器的樹的類別數量,用來計算一棵樹的節點大小也就是nodeStep

? ??FileNode?fn?=?root[CC_FEATURE_PARAMS];

????if(?fn.empty() )

????????return?false;

?

????ncategories=?fn[CC_MAX_CAT_COUNT];

????int?subsetSize?= (ncategories+ 31)/32,

???????nodeStep?= 3 + (?ncategories>0 ??subsetSize: 1 );

至此分類器參數讀取完畢。

?

接下來是建立分類樹,也就是stage部分的載入。

// load stages

????fn?=?root[CC_STAGES];

????if(?fn.empty() )

????????return?false;

?

????stages.reserve(fn.size());//stages包含15個節點,fn.size()==15

????classifiers.clear();

????nodes.clear();

?

????FileNodeIteratorit?=fn.begin(),it_end=fn.end();

?

????for(?int?si?= 0;?it?!=?it_end;?si++, ++it?)//遍歷stages

??? {

????????FileNodefns?= *it;

????????Stagestage;//stage結構中包含thresholdntreesfirst三個變量

????????stage.threshold?= (float)fns[CC_STAGE_THRESHOLD]-THRESHOLD_EPS;

????????fns=?fns[CC_WEAK_CLASSIFIERS];

????????if(fns.empty())

????????????returnfalse;

????????stage.ntrees?= (int)fns.size();

????????stage.first?= (int)classifiers.size();//ntreesfirst指出該stage中包含的樹的數目和起始位置

????????stages.push_back(stage);//stage被保存在stagevector(也就是stages)中

????????classifiers.reserve(stages[si].first?+stages[si].ntrees);//相應地擴展classifiers的空間,它存儲的是這些stage中的weak classifiers,也就是weak trees

?

????????FileNodeIteratorit1?=fns.begin(),it1_end=fns.end();//遍歷weak classifier

????????for( ;?it1?!=?it1_end;++it1?)// weaktrees

??????? {

????????????FileNodefnw?= *it1;

????????????FileNodeinternalNodes?=fnw[CC_INTERNAL_NODES];

????????????FileNodeleafValues?=fnw[CC_LEAF_VALUES];

????????????if(internalNodes.empty()||leafValues.empty())

????????????????returnfalse;

?

????????????DTreetree;

????????????tree.nodeCount?= (int)internalNodes.size()/nodeStep;

????????????classifiers.push_back(tree);//一個弱分類器或者說一個weak tree中只包含一個int變量,用它在classifiers中的位置和自身來指出它所包含的node個數

?

????????????nodes.reserve(nodes.size() +tree.nodeCount);

????????????leaves.reserve(leaves.size() +leafValues.size());//擴展存儲nodeleavesvector結構空間

????????????if(subsetSize?> 0 )

????????????????subsets.reserve(subsets.size() +tree.nodeCount*subsetSize);

?

????????????FileNodeIteratorinternalNodesIter?=internalNodes.begin(),internalNodesEnd=internalNodes.end();

//遍歷nodes

????????????for(;?internalNodesIter?!=?internalNodesEnd; )//nodes

??????????? {

????????????????DTreeNodenode;//一個node中包含leftrightthresholdfeatureIdx四個變量。其中leftright是其對應的代號,left=0right=-1featureIdx指的是整個分類器中使用的特征池中某個特征的ID,比如共有108個特征,那么featureIdx就在0~107之間;thresholdnode的。同時可以看到這里的HOG分類器中每個弱分類器僅包含一個node,也就是僅對某一個特征做判斷,而不是多個特征的集合

????????????????node.left?= (int)*internalNodesIter; ++internalNodesIter;

????????????????node.right?= (int)*internalNodesIter; ++internalNodesIter;

????????????????node.featureIdx?= (int)*internalNodesIter; ++internalNodesIter;

????????????????if(subsetSize?> 0 )

??????????????? {

????????????????????for(intj?= 0;j?<subsetSize;j++, ++internalNodesIter)

? ??????????????????????subsets.push_back((int)*internalNodesIter);

????????????????????node.threshold?= 0.f;

??????????????? }

????????????????else

??????????????? {

????????????????????node.threshold?= (float)*internalNodesIter; ++internalNodesIter;

??????????????? }

????????????????nodes.push_back(node);//得到的node將保存在它的vector結構nodes

??????????? }

?

????????????internalNodesIter=leafValues.begin(),internalNodesEnd?=leafValues.end();

?

????????????for(;?internalNodesIter?!=?internalNodesEnd; ++internalNodesIter)// leaves

????????????????leaves.push_back((float)*internalNodesIter);//leaves中保存相應每個nodeleft leafright leaf的值,因為每個weak tree只有一個node也就分別只有一個left leafright leaf,這些將保存在leaves

??????? }

??? }

通過stage樹的建立可以看出最終是獲取stages、classifiers、nodes和leaves四個vector變量。其中的nodes和leaves共同組成一系列有序節點,而classifiers中的變量則是在這些節點中查詢來構成一個由弱分類器組,它僅僅是把這些弱分類器組合在一起,最后stages中每一個stage也就是一個強分類器,它在classifiers中查詢得到自己所屬的弱分類器都有哪些,從而構成一個強分類器的基礎。

1.2.2?????????featureEvaluator的讀取

完成data部分的載入后,接下來就是特征計算器(featureEvaluator)的載入了。上面每一個node中都會計算特征池中的某一個特征,這個特征以featureIdx出現在node中。現在來看看這些featureIdx背后的內容。

首先要創建某種特征類型的特征計算器,這里支持的是Haar、LBP和HOG三種。

featureEvaluator =FeatureEvaluator::create(data.featureType);

create中生成一個HaarEvaluator/LBPEvaluator/HOGEvaluator對象并返回指針而已。那HOGEvaluators中包含什么內容呢?

這里暫不提其他成員,先介紹一個vector<Feature>的指針 features,也就是存儲了一系列Feature對象:

struct?Feature

??? {

????????Feature();

????????float?calc(?int?offset?)const;

????????void?updatePtrs(?const?vector<Mat>&_hist,constMat?&_normSum);

????????bool?read(?const?FileNode&node);?

?

????????enum?{?CELL_NUM?= 4,?BIN_NUM= 9 };

?

????????Rectrect[CELL_NUM];

????????int?featComponent;?//componentindex from 0 to 35

????????const?float*?pF[4];?//for feature calculation

????????const?float*?pN[4];?//for normalization calculation

};

這里的vector<Feature>將是計算特征的核心,并且featureEvaluator的讀入部分主要就是對這個vector變量的內容作初始化,因此在此展示一下。

?

?featureEvaluator創建之后在xml中的features節點下開始讀入。

bool?HOGEvaluator::read(?const?FileNode&?node)

{

????features->resize(node.size());//node.size()為整個分類器中使用到的特征數量,以我訓練的HOG分類器為例包含108個特征

????featuresPtr= &(*features)[0];

????FileNodeIteratorit?=node.begin(),it_end=node.end();

????for(inti?= 0;it?!=it_end;++it,i++)

??? {

????????if(!featuresPtr[i].read(*it))//遍歷所有features并讀入到featureEvaluatorfeatures

????????????returnfalse;

??? }

????return?true;

}

Feature的讀入程序:

bool?HOGEvaluator::Feature?::?read(const?FileNode&node?)

{

????FileNodernode?=node[CC_RECT];//rect節點下包括一個矩形和一個特征類型號featComponent

????FileNodeIteratorit?=rnode.begin();

????it>>?rect[0].x>>?rect[0].y>>?rect[0].width>>?rect[0].height>>?featComponent;//featComponent范圍在[0,35]36類特征中的一個

????rect[1].x?=rect[0].x?+rect[0].width;

????rect[1].y?=rect[0].y;

????rect[2].x?=rect[0].x;

????rect[2].y?=rect[0].y?+rect[0].height;

????rect[3].x?=rect[0].x?+rect[0].width;

????rect[3].y?=rect[0].y?+rect[0].height;

????rect[1].width?=rect[2].width?=rect[3].width?=rect[0].width;

rect[1].height=rect[2].height=rect[3].height=rect[0].height;

//xml中的rect存儲的矩形信息與4個矩形之間的關系如下圖4所示

?

????return?true;

}


圖4. Rect數組與xml中矩形的關系

這樣經過特征讀取這一步后,獲得了一個特征池,池中每一個特征表示在圖中某個矩形位置提取ID為0到35的某個特征量。

?

1.3?? read的結果

read的結果一是初始化了分類器的特征類型、最小檢測窗口size等參數;二是建立級聯的分類器樹;三是提取了xml中的特征池。

2.???? detectMultiscale函數

在load分類器之后,可以調用該函數對一幅圖像做多尺度檢測。

2.1?? 函數自身

//輸入參數:imageMat類型的圖像

???????????objects—檢測得到的矩形

???????????rejectLevels—如果不符合特征的矩形,返回級聯分類器中符合的強分類器數

???????????levelWeights

??????????scaleFactor—圖像縮放因子

???????????minNeighbors

??????????flags

???????? ??minObjectSize—最小檢測窗口大小

???????????maxObjectSize—最大檢測窗口大小

???????????outputRejectLevels—是否輸出rejectLevelslevelWeights,默認為false

voidCascadeClassifier::detectMultiScale(constMat&image,vector<Rect>&objects,vector<int>&rejectLevels,vector<double>&levelWeights,doublescaleFactor,intminNeighbors,intflags,SizeminObjectSize,SizemaxObjectSize,booloutputRejectLevels)

{

????const?double?GROUP_EPS?=0.2;

?

????CV_Assert(scaleFactor?> 1 &&image.depth()==CV_8U?);//256灰度級且當前縮放因子大于1

?

????if(?empty() )//沒有載入

????????return;

?

????if(?isOldFormatCascade() )//這里是指haarTraining得到的分類器或者老版本的OpenCV,我不確定,但是這里可以跳過,因為訓練與檢測所使用的OpenCV版本是一致的

??? {

????????MemStoragestorage(cvCreateMemStorage(0));

????????CvMat_image?=image;

????????CvSeq*_objects?=cvHaarDetectObjectsForROC(&_image,oldCascade,storage,rejectLevels,levelWeights,scaleFactor,

??????????????????????????????????????????????minNeighbors,?flags,minObjectSize,maxObjectSize,outputRejectLevels?);

????????vector<CvAvgComp>vecAvgComp;

????????Seq<CvAvgComp>(_objects).copyTo(vecAvgComp);

????????objects.resize(vecAvgComp.size());

????????std::transform(vecAvgComp.begin(),vecAvgComp.end(),objects.begin(),getRect());

????????return;

??? }

?

????objects.clear();

//mask的應用尚不清楚

????if?(!maskGenerator.empty()){

????????maskGenerator->initializeMask(image);

??? }

?

?

????if(?maxObjectSize.height== 0 ||?maxObjectSize.width?== 0 )//很明顯不能為0

????????maxObjectSize=?image.size();//默認最大檢測size為圖像size

?

????Mat?grayImage?=?image;

????if(?grayImage.channels()> 1 )//如果是三通道轉換為灰度圖

??? {

????????Mat?temp;

????????cvtColor(grayImage,temp,CV_BGR2GRAY);

????????grayImage=?temp;

??? }

?

????Mat?imageBuffer(image.rows?+ 1,image.cols?+ 1,CV_8U);

????vector<Rect>candidates;//每個尺度下的圖像的檢測結果裝在該vector

?

????for(?double?factor?= 1;;?factor?*=?scaleFactor)//對每個尺度下圖像檢測

??? {

????????SizeoriginalWindowSize?=getOriginalWindowSize();//最小檢測窗口size

?

????????SizewindowSize(cvRound(originalWindowSize.width*factor),cvRound(originalWindowSize.height*factor) );//當前檢測窗口size

????????SizescaledImageSize(cvRound(grayImage.cols/factor?),cvRound(grayImage.rows/factor?) );//縮放后圖像size

????????SizeprocessingRectSize(scaledImageSize.width?-originalWindowSize.width?+ 1,scaledImageSize.height?-originalWindowSize.height?+ 1 );//滑動窗口在寬和高上的滑動距離

?

????????if(?processingRectSize.width<= 0 ||?processingRectSize.height?<= 0 )

????????????break;

????????if(?windowSize.width>?maxObjectSize.width||?windowSize.height>?maxObjectSize.height)

????????????break;

????????if(?windowSize.width<?minObjectSize.width||?windowSize.height<?minObjectSize.height)

????????????continue;

?

????????Mat?scaledImage(?scaledImageSize,CV_8U,imageBuffer.data?);

????????resize(grayImage,scaledImage,scaledImageSize, 0, 0,CV_INTER_LINEAR?);//將灰度圖resizescaledImage中,size為當前尺度下的縮放圖像

?

????????int?yStep;//滑動窗口的滑動步長,xy方向上相同

????????if(?getFeatureType() ==?cv::FeatureEvaluator::HOG)

??????? {

????????????yStep= 4;

??????? }

????????else

??????? {

????????????yStep=?factor?> 2. ? 1 : 2;//當縮放比例比較大時,滑動步長減小

??????? }

?

????????int?stripCount,?stripSize;

?

????#ifdef?HAVE_TBB

??????? const intPTS_PER_THREAD = 1000;

??????? stripCount =((processingRectSize.width/yStep)*(processingRectSize.height + yStep-1)/yStep +PTS_PER_THREAD/2)/PTS_PER_THREAD;

??????? stripCount =std::min(std::max(stripCount, 1), 100);

??????? stripSize =(((processingRectSize.height + stripCount - 1)/stripCount +yStep-1)/yStep)*yStep;

????#else

????????stripCount= 1;

????????stripSize=?processingRectSize.height;//y方向上的滑動距離

????#endif

?

????????if( !detectSingleScale(scaledImage,stripCount,processingRectSize,stripSize,yStep,factor,candidates,

????????????rejectLevels,levelWeights,outputRejectLevels) )//對單尺度圖像做檢測

????????????break;

??? }

?

?

????objects.resize(candidates.size());

????std::copy(candidates.begin(),candidates.end(),objects.begin());//將每個尺度下的檢測結果copy到輸出vector

?

????if(?outputRejectLevels?)//默認為false,不輸出rejectLevels

??? {

????????groupRectangles(objects,rejectLevels,levelWeights,minNeighbors,GROUP_EPS?);

??? }

????else

??? {

????????groupRectangles(objects,minNeighbors,GROUP_EPS?);//尚未去看

??? }

}

?

可以看到detectMultiscale只是對detectSingleScale做了一次多尺度的封裝。在單一尺度的圖像中detectSingleScale是如何檢測的呢?

2.2??detectSingleScale函數

//函數參數設置可以參見detectMultiScale函數

boolCascadeClassifier::detectSingleScale(constMat&image,intstripCount,SizeprocessingRectSize,intstripSize,intyStep,doublefactor,vector<Rect>&candidates,vector<int>&levels,vector<double>&weights,booloutputRejectLevels)

{

????if( !featureEvaluator->setImage(image,data.origWinSize?) )//setImage函數為特征計算做準備,

????????return?false;

?

????Mat?currentMask;

????if?(!maskGenerator.empty()){

????????currentMask=maskGenerator->generateMask(image);

??? }//仍然不解mask的應用,好像沒用到?

?

????ConcurrentRectVectorconcurrentCandidates;//在每個平行粒子中訪問的檢測輸出空間

????vector<int>rejectLevels;

????vector<double>levelWeights;

????if(?outputRejectLevels?)//這里選擇的默認false,不返回

??? {

????????parallel_for(BlockedRange(0,stripCount),CascadeClassifierInvoker(*this,processingRectSize,stripSize,yStep,factor,

????????????concurrentCandidates,rejectLevels,levelWeights,true,currentMask));

????????levels.insert(levels.end(),rejectLevels.begin(),rejectLevels.end() );

????????weights.insert(weights.end(),levelWeights.begin(),levelWeights.end() );

??? }

????else

??? {

????????parallel_for(BlockedRange(0,stripCount),CascadeClassifierInvoker(*this,processingRectSize,stripSize,yStep,factor,concurrentCandidates,rejectLevels,levelWeights,false,currentMask));//這里是檢測過程中的關鍵,使用parallel_for是為了TBB加速中使用,生成stripCount個平行線程(每個線程生成一個CascadeClassifierInvoker),在每個CascadeClassifierInvoker中對當前圖像做一次檢測,這是TBB利用多線程做的加速計算

??? }

????candidates.insert(candidates.end(),concurrentCandidates.begin(),concurrentCandidates.end() );//將檢測結果加入到輸出中

?

????return?true;

}

2.2.1?????????featureEvaluators的setImage函數

?? 此處仍以HOG為例,其他兩個特征的計算可能與之有所不同。

bool?HOGEvaluator::setImage(?const?Mat&?image,Size?winSize)

{

????int?rows?=?image.rows?+ 1;

????int?cols?=?image.cols?+ 1;

????origWinSize=?winSize;//最小檢測窗口size

????if(?image.cols?<origWinSize.width||image.rows<origWinSize.height)

????????return?false;

????hist.clear();//hist為存儲Mat類型的vector

????for(?int?bin?= 0;?bin?<?Feature::BIN_NUM;bin++)//BIN_NUM=9,梯度方向分為9個,所以統計得到的Mat個數應當為9

??? {

????????hist.push_back(Mat(rows,cols,CV_32FC1) );

??? }

????normSum.create(rows,cols,CV_32FC1);//歸一化的norm存儲空間

?

????integralHistogram(image,hist,normSum,Feature::BIN_NUM?);//計算歸一化后的直方圖

?

????size_t?featIdx,?featCount=?features->size();

//遍歷更新特征池中每個特征的HOG特征計算所需要的矩形四個頂點上對應積分圖的指針

????for(?featIdx?= 0;?featIdx<?featCount;?featIdx++)

??? {

????????featuresPtr[featIdx].updatePtrs(hist,normSum);

??? }

????return?true;

}

這里的updatePtrs函數是要根據梯度直方圖和歸一圖來更新每個Feature中保存的四個指針,例如某Feature在xml中的形式為0 0 8 8 13,那么它所在的矩形就是cvRect(0,0,16,16),同時featComponent=13,binIdx=featComponent%9=4,cellIdx=featComponent/9=1.那么這個特征就是要計算矩形(8,0,8,8)中梯度方向160°方向上的分量總和。要計算這個特征我們只需要在hist中的第4個Mat中查找出矩形四個頂點上的值就可以了。而Feature中的四個float型指針正是指向hist中這四個值的指針。UpdatePtrs的作用就是要更新這四個指針。具體程序如下:

inline?voidHOGEvaluator::Feature?::updatePtrs(constvector<Mat> &_hist,constMat&_normSum?)

{

????int?binIdx?=?featComponent%?BIN_NUM;//計算要更新的角度

????int?cellIdx?=?featComponent/?BIN_NUM;//計算要更新的cell是哪一個

????Rect?normRect?=?Rect(rect[0].x,rect[0].y,2*rect[0].width,2*rect[0].height);

?

????const?float*?featBuf?= (constfloat*)_hist[binIdx].data;

????size_t?featStep?=?_hist[0].step?/sizeof(featBuf[0]);

?

????const?float*?normBuf?= (constfloat*)_normSum.data;

????size_t?normStep?=?_normSum.step?/sizeof(normBuf[0]);

?

????CV_SUM_PTRS(pF[0],pF[1],pF[2],pF[3],featBuf,rect[cellIdx],featStep);//更新四個直方積分圖中的指針

????CV_SUM_PTRS(pN[0],pN[1],pN[2],pN[3],normBuf,normRect,normStep?);//更新四個歸一圖中的指針

}

2.2.2?????????CascadeClassifierInvoker類的實例化

每個線程中會生成該類的一個對象,但是這里沒有做TBB加速,因而是單線程。該對象的operator中對當前縮放尺度下的圖像以滑窗形式掃描,在每個點上做分類器級聯檢測;如果有TBB加速,每個對象僅檢測一行,通過多行一起掃描來加速。

void?operator()(constBlockedRange&range)const

??? {

????????Ptr<FeatureEvaluator>evaluator=classifier->featureEvaluator->clone();//復制featureEvaluator的指針

?

????????SizewinSize(cvRound(classifier->data.origWinSize.width*scalingFactor),cvRound(classifier->data.origWinSize.height*scalingFactor));//當前檢測窗口的size,其實這里是通過縮放圖像來做的,而不是窗口大小的改變

?

????????int?y1?=?range.begin() *stripSize;//range的變化范圍為[0,1)

????????int?y2?=?min(range.end() *stripSize,processingRectSize.height);//y方向上的行數不可能超過滑動距離

????????for(?int?y?=?y1;y?<y2;y?+=yStep?)//遍歷所有行

??????? {

????????????for(intx?= 0;x?<processingRectSize.width;x?+=yStep?)//遍歷一行

??????????? {

//依然是尚未搞懂的mask

????????????????if( (!mask.empty())&& (mask.at<uchar>(Point(x,y))==0)) {

????????????????????continue;

??????????????? }

?

????????????????doublegypWeight;

????????????????intresult?=classifier->runAt(evaluator,Point(x,y),gypWeight);//在當前點提取每個stage中的特征并檢驗是否滿足分類器,result是通過的stage個數的相反數,如果全部通過則為1

????????????????if(rejectLevels?)//默認為false

??????????????? {

????????????????????if(result?== 1 )

????????????????????????result?=? -(int)classifier->data.stages.size();

????????????????????if(classifier->data.stages.size() +result?< 4 )

??????????????????? {

????????????????????????rectangles->push_back(Rect(cvRound(x*scalingFactor),cvRound(y*scalingFactor),winSize.width,winSize.height));

????????????????????????rejectLevels->push_back(-result);

????????????????????????levelWeights->push_back(gypWeight);

??????????????????? }

??????????????? }

????????????????elseif(result> 0 )

????????????????????rectangles->push_back(Rect(cvRound(x*scalingFactor),cvRound(y*scalingFactor),winSize.width,winSize.height));

????????????????if(result?== 0 )//保存當前的窗口

????????????????????x+=?yStep;

??????????? }

??????? ??}

? }

這個程序中唯一需要解釋的是CascadeClassifier::runAt函數。對于isStumpBased=true的HOG分類器,返回的結果是predictOrderedStump<HOGEvaluator>(*this, evaluator, weight )this指針是當前CascadeClassifier的指針,evaluator是featureEvaluator的指針,weight為double類型。predictOrderedStump函數如下:

?

template<classFEval>

inline?intpredictOrderedStump(CascadeClassifier&cascade,Ptr<FeatureEvaluator> &_featureEvaluator,double&sum)

{

????int?nodeOfs?= 0,?leafOfs= 0;//nodeleaf的整體序號

????FEval&featureEvaluator?= (FEval&)*_featureEvaluator;

????float*?cascadeLeaves?= &cascade.data.leaves[0];//定義指向leaves首地址的指針

????CascadeClassifier::Data::DTreeNode*cascadeNodes?= &cascade.data.nodes[0];//定義指向nodes首地址的指針

????CascadeClassifier::Data::Stage*cascadeStages?= &cascade.data.stages[0];//定義指向stages首地址的指針

?

????int?nstages?= (int)cascade.data.stages.size();

????for(?int?stageIdx?= 0;?stageIdx?<?nstages;stageIdx++ )

??? {

????????CascadeClassifier::Data::Stage&stage?=cascadeStages[stageIdx];//遍歷每個stage

????????sum= 0.0;//stage中的葉節點的和

?

????????int?ntrees?=?stage.ntrees;

????????for(?int?i?= 0;?i?<?ntrees;?i++,?nodeOfs++,leafOfs+= 2 )

??????? {

????????????CascadeClassifier::Data::DTreeNode&node?=cascadeNodes[nodeOfs];//獲取當前stage的各個node

????????????doublevalue?=featureEvaluator(node.featureIdx);//這里nodefeatureIdx指出要計算的是哪一個特征,也就是xml中的哪一個rect,在生成一個HOGEvaluator時就會在operator中根據傳入的featureIdx計算特征值,引用到HOGEvaluator中的calc函數

????????????sum+=?cascadeLeaves[?value<?node.threshold??leafOfs?:?leafOfs+ 1 ];//根據node中的threshold得到左葉子或者右葉子的值,加到該stage中的總和

??????? }

?

????????if(?sum?<?stage.threshold?)//如果總和大于stagethreshold則通過,小于則退出,并返回當前stage的相反數

????????????return-stageIdx;

??? }

????return?1;

}

Feature中的calc很簡單,因為前面已經更新了四個對應于矩形頂點處積分圖的指針已經被更新,歸一圖中的指針也已經被更新。

這里表達的計算如下圖所示:


圖5. 積分圖計算示意

要計算D中的值,在積分圖中四個頂點的指針所指向的內容分別為A,A+B,A+C和A+B+C+D。因此中間兩項與其余兩項的差就是要求的D區域了。其中的offset變量是根據滑動窗口的位置確定的,代表上圖中D矩形的左上頂點在全圖中的位置。程序如下:

首先由如下定義

#define?CALC_SUM_(p0,p1,p2,p3,offset)

??? ((p0)[offset] - (p1)[offset] - (p2)[offset] + (p3)[offset])??

???

#define?CALC_SUM(rect,offset)CALC_SUM_((rect)[0], (rect)[1],(rect)[2], (rect)[3],offset)

然后是Feature中的calc函數

inline?floatHOGEvaluator::Feature?::calc(intoffset?)const

{

????float?res?=?CALC_SUM(pF,offset);

????float?normFactor?=?CALC_SUM(pN,offset);

????res?= (res?> 0.001f) ? (res/ (normFactor?+ 0.001f) ) : 0.f;

????return?res;

}


總結

以上是生活随笔為你收集整理的分类器是如何做检测的?(1)——CascadeClassifier中的detectMultiScale函数解读的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。

主站蜘蛛池模板: 日韩欧美一区二区在线观看 | 亚洲91网| 亚洲一区二区三区高清在线 | 国产传媒一区二区三区 | 国产污视频在线看 | 嫩草在线看 | 欧美一区二区三区大屁股撅起来 | 国产精品久久777777换脸 | 亚洲视频色图 | 高潮毛片无遮挡高清免费 | 亚洲成人中文字幕在线 | 激情九月婷婷 | 国产精品九九视频 | 国产chinesehd精品 | 亚洲社区一区二区 | 国内偷拍久久 | 免费一级淫片aaa片毛片a级 | 成年人在线免费观看网站 | 国产伦乱 | 少妇高潮毛片色欲ava片 | 久久精品福利视频 | 精品国产一二三区 | 亚洲综合影视 | 最近免费中文字幕 | 亚洲依依| 亚洲精品国产精品乱码视色 | av自拍| 国产女人水真多18毛片18精品 | 国产成人综合精品 | 射一射| 亚洲激情一区 | 免费黄色看片网站 | 久久精品视频免费看 | 国产第一精品视频 | 久久牛牛 | 36d大奶| 五月婷婷综合在线观看 | 女同一区二区 | 美女扒开让男人桶爽 | 日本色综合网 | 激情文学88| 老女人性视频 | 欧美色一区二区三区在线观看 | 国产素人自拍 | 精品视频第一页 | 美女色呦呦 | 在线视频中文字幕 | 国产ts在线| 久久久久久久无码 | 爱插网| 欧美xxxx×黑人性爽 | 蜜色av | 最新在线视频 | 国产伦精品一区二区三区在线观看 | 黄色一级在线 | 国产亚洲av在线 | 国产乱码精品一区二区三区中文 | 成人黄色激情网 | 成年人看的免费视频 | 欧美一区免费观看 | 国产日b视频 | 日本黄色aaa | 一二三四区在线 | 中文字幕欧美色图 | 国产精品高潮呻吟久久久 | 最近的中文字幕在线看视频 | 久久com| 天天干天天插天天操 | 国产毛片毛片毛片毛片 | 亚洲精品视频网址 | 精品人妻伦一二三区免费 | 精品免费在线视频 | 91成人福利在线 | 久久无码高潮喷水 | 波多一区 | 韩国精品一区二区 | 久久久久无码国产精品一区 | 国产原创中文av | 蜜桃av鲁一鲁一鲁一鲁俄罗斯的 | 领导揉我胸亲奶揉下面 | 久久成人综合 | 永久免费不卡在线观看黄网站 | 欧美日韩高清在线 | 天海翼一区 | 天天操婷婷 | 欧美精品在线视频观看 | 精品国产视频在线 | 成人午夜视频网站 | 午夜少妇久久久久久久久 | 久久久少妇 | 欧美在线xxxx | 鲁鲁在线 | 西方裸体在线观看 | 韩国黄色网址 | 茄子视频懂你更多在线观看 | 日本免费黄网站 | 51av视频| av视屏 | 中文资源在线播放 |