CascadeClassifier多尺度檢測

1.? 概述

CascadeClassifier為OpenCV中cv namespace下用來做目標(biāo)檢測的級聯(lián)分類器的一個(gè)類哩牍。該類中封裝的目標(biāo)檢測機(jī)制,簡而言之是滑動窗口機(jī)制+級聯(lián)分類器的方式儡率。OpenCV的早期版本中僅支持haar特征的目標(biāo)檢測挂据,分別在2.2和2.4.0(包含)之后開始支持LBP和HOG特征的目標(biāo)檢測。

2.? 支持的特征

對于Haar儿普、LBP和HOG崎逃,CascadeClassifier都有自己想對他們說的話:

1)? Haar:因?yàn)橹皬腛penCV1.0以來,一直都是只有用haar特征的級聯(lián)分類器訓(xùn)練和檢測(當(dāng)時(shí)的檢測函數(shù)稱為cvHaarDetectObjects眉孩,訓(xùn)練得到的也是特征和node放在一起的xml)个绍,所以在之后當(dāng)CascadeClassifier出現(xiàn)并統(tǒng)一三種特征到同一種機(jī)制和數(shù)據(jù)結(jié)構(gòu)下時(shí),沒有放棄原來的C代碼編寫的haar檢測浪汪,仍保留了原來的檢測部分巴柿。另外,Haar在檢測中無論是特征計(jì)算環(huán)節(jié)還是判斷環(huán)節(jié)都是三種特征中最簡潔的吟宦,但是筆者的經(jīng)驗(yàn)中他的訓(xùn)練環(huán)節(jié)卻往往是耗時(shí)最長的篮洁。

2)? LBP:LBP在2.2中作為人臉檢測的一種方法和Haar并列出現(xiàn),他的單個(gè)點(diǎn)的檢測方法(將在下面看到具體討論)是三者中較為復(fù)雜的一個(gè)殃姓,所以當(dāng)檢測的點(diǎn)數(shù)相同時(shí)袁波,如果不考慮特征計(jì)算時(shí)間,僅計(jì)算判斷環(huán)節(jié)蜗侈,他的時(shí)間是最長的篷牌。

3)? HOG:在2.4.0中才開始出現(xiàn)在該類中的HOG檢測,其實(shí)并不是OpenCV的新生力量踏幻,因?yàn)樵谳^早的版本中HOG特征已經(jīng)開始作為單獨(dú)的行人檢測模塊出現(xiàn)枷颊。比較起來,雖然HOG在行人檢測和這里的檢測中同樣是滑窗機(jī)制该面,但是一個(gè)是級聯(lián)adaboost夭苗,另一個(gè)是SVM;而且HOG特征為了加入CascadeClassifier支持的特征行列改變了自身的特征計(jì)算方式:不再有相鄰cell之間的影響隔缀,并且采用在Haar和LBP上都可行的積分圖計(jì)算题造,放棄了曾經(jīng)的HOGCache方式,雖然后者的加速性能遠(yuǎn)高于前者猾瘸,而簡單的HOG特征也使得他的分類效果有所下降(如果用SVM分類器對相同樣本產(chǎn)生的兩種HOG特征做分類界赔,沒有了相鄰cell影響的計(jì)算方式下的HOG特征不那么容易完成分類)翘簇。這些是HOG為了加入CascadeClassifier而做出的犧牲设捐,不過你肯定也想得到OpenCV保留了原有的HOG計(jì)算和檢測機(jī)制。另外杖们,HOG在特征計(jì)算環(huán)節(jié)是最耗時(shí)的揽思,但他的判斷環(huán)節(jié)和Haar一樣的簡潔。

關(guān)于三種特征在三個(gè)環(huán)節(jié)的耗時(shí)有下表:


圖片發(fā)自簡書App

3.? 內(nèi)部結(jié)構(gòu)

從CascadeClassifier開始钉汗,以功能和結(jié)構(gòu)來整體介紹內(nèi)部的組織和功能劃分锡宋。

CascadeClassifer中的數(shù)據(jù)結(jié)構(gòu)包括Data和FeatureEvaluator兩個(gè)主要部分特恬。Data中存儲的是從訓(xùn)練獲得的xml文件中載入的分類器數(shù)據(jù)徐钠;而FeatureEvaluator中是關(guān)于特征的載入、存儲和計(jì)算显拜。在此之外還有檢測框架的邏輯部分,是在Data和Featureevaluator之上的一層邏輯远荠。

3.1Data結(jié)構(gòu)

先來看Data的結(jié)構(gòu),如下圖:


圖片發(fā)自簡書App


首先失息,在Data中存儲著一系列的DTreeNode譬淳,該結(jié)構(gòu)體中記錄的是一個(gè)弱分類器。其中盹兢,feature ID表明它計(jì)算的是怎樣的一個(gè)特征邻梆,threshold1是它的閾值,據(jù)此判斷某個(gè)特征值應(yīng)當(dāng)屬于left還是right绎秒,后面的left leafvalue和right leaf value是左右葉節(jié)點(diǎn)的值浦妄。這里需要結(jié)合Stage的判斷環(huán)節(jié)來理解:

假設(shè)某個(gè)stage(也就是一個(gè)強(qiáng)分類器)中包含有num個(gè)弱分類器(也就是num個(gè)DTreeNode),按照下面的過程計(jì)算stage對某個(gè)采樣圖像im的結(jié)果见芹。

1)? 初始化sum = 0

2)? for i = 1:num

計(jì)算

if f > threshold1

? sum = sum + leftVal

else

? sum = sum + rightVal

end

3)if sum > threshold2

? output = 1

else

? output = 0

? ? ? 其中剂娄,ID、threshold1玄呛、leftVal和rightVal是第i個(gè)弱分類器中的變量阅懦,featureExtract表示對im提取第ID個(gè)特征值,是該強(qiáng)分類器中的閾值把鉴,當(dāng)結(jié)果為正時(shí)故黑,輸出output=1,否則為0.

另外庭砍,可以從上圖看到stage結(jié)構(gòu)中僅僅保存了第一個(gè)弱分類器的下標(biāo)first场晶、弱分類器數(shù)量num和自身的閾值threshold2,所有弱分類器或者說所有節(jié)點(diǎn)都是連續(xù)存儲在一個(gè)vector內(nèi)的怠缸。

3.2 FeatureEvaluator

如果Data結(jié)構(gòu)主要是在載入時(shí)保存分類器內(nèi)部的數(shù)據(jù)诗轻,F(xiàn)eatureEvaluator則是負(fù)責(zé)特征計(jì)算環(huán)節(jié)。這是一個(gè)基類揭北,在此之上衍生了HaarEvaluator扳炬、LBPEvaluator和HOGEvaluator三種特征各自的特征計(jì)算結(jié)構(gòu)吏颖。每個(gè)Evaluator中都保存了一個(gè)vector,這是在read環(huán)節(jié)中從分類器中載入的特征池(feature pool)半醉,前面提到的feature ID對應(yīng)的就是在這個(gè)vector內(nèi)的下標(biāo)缩多。三種Evaluator中的Feature定義有所不同衬吆,因?yàn)橛?jì)算特征所需的信息不同逊抡,具體如下:

Haar——Feature中保存的是3個(gè)包含權(quán)重的rect冒嫡,如果要計(jì)算下圖的特征灯谣,


圖片發(fā)自簡書App


對應(yīng)的rect為[(R2,-3),(R1,1)]和[(R1,1),(R2,-1)]胎许。這里的R1對應(yīng)于上圖中的紅色矩形辜窑,R2對應(yīng)綠色矩形穆碎,圓括號內(nèi)的第二個(gè)值為對應(yīng)的權(quán)重所禀。所有Haar特征的描述只需要至多3個(gè)加權(quán)矩形即可描述色徘,所以HaarEvaluator的Feature中保存的是三個(gè)加權(quán)矩形褂策;

LBP——Feature中僅保存一個(gè)rect耿焊,這里需要指出的是罗侯,LBP特征計(jì)算的不是一個(gè)3x3大小的區(qū)域中每個(gè)點(diǎn)與中心點(diǎn)的大小關(guān)系歇父,而是一個(gè)3x3個(gè)相同大小的矩形區(qū)域之間的對比關(guān)系,這也是為什么LBP特征計(jì)算過程也用到積分圖方法的原因翎冲。如下圖所示抗悍,


圖片發(fā)自簡書App

Feature中保存的就是紅色的矩形位置缴渊,而我們要先提取上圖中9個(gè)矩形內(nèi)的所有像素點(diǎn)的和衔沼,然后比較外圍8個(gè)矩形內(nèi)的值和中間矩形內(nèi)的值的關(guān)系指蚁,從而得到LBP特征凝化。

HOG——與LBP中類似搓劫,F(xiàn)eature中同樣僅一個(gè)rect枪向,HOG特征是在2x2個(gè)rect大小的范圍內(nèi)提取出的遣疯,也就是說給出的rect是HOG計(jì)算過程中4個(gè)block里的左上角的block缠犀。

除此之外辨液,Evaluator中還有另外一個(gè)很重要的數(shù)據(jù)結(jié)構(gòu)——數(shù)據(jù)指針滔迈。這個(gè)結(jié)構(gòu)在三種Evaluator中同樣不同燎悍,但他們所指向的都是積分圖中的一個(gè)值谈山。在Haar和LBP中是先計(jì)算一個(gè)整圖的積分圖奏路,而HOG中則是計(jì)算梯度方向和梯度幅值鸽粉,然后按照梯度方向劃分的區(qū)間將梯度幅值圖映射成n個(gè)積分圖帚戳。每個(gè)特征的計(jì)算過程中要維護(hù)一系列指向積分圖中的指針销斟,通過訪問積分圖快速計(jì)算某個(gè)矩形內(nèi)的像素值的和蚂踊,從而加速特征計(jì)算環(huán)節(jié)犁钟。這里暫不詳細(xì)展開涝动。

3.3 檢測框架邏輯

這里的檢測框架簡而言之就是一個(gè)多尺度縮放+滑動窗口遍歷搜索的框架醋粟。在CascadeClassifier中包含detectMultiScale和detectSingleScale成員函數(shù)米愿,分別對應(yīng)多尺度和單尺度檢測育苟,其中多尺度檢測中會調(diào)用單尺度的方法违柏。

分類器僅能夠?qū)δ骋还潭╯ize的采樣圖像做判斷漱竖,給出當(dāng)前的采樣圖像是否為真實(shí)目標(biāo)的“非正即負(fù)”的結(jié)果(size是由訓(xùn)練數(shù)據(jù)決定的)闲孤。要找到某個(gè)圖像中的目標(biāo)位置讼积,就要以size大小的采樣窗口對圖像逐行逐列地掃描勤众,然后對每個(gè)采樣圖像判斷是否為正们颜,將結(jié)果以矩形位置保存下來就獲得了目標(biāo)的位置窥突。但是這僅僅是單尺度檢測阻问,也就是說称近,一個(gè)以40x40大小訓(xùn)練數(shù)據(jù)訓(xùn)練獲得的分類器只能檢測當(dāng)前圖像里40x40大小的目標(biāo)刨秆,要檢測80x80大小的目標(biāo)該如何做呢衡未?可以把原圖像縮放到原來的1/2如失,這樣原圖中80x80大小的目標(biāo)就變成40x40了岖常,再做一次上面的掃描檢測過程竭鞍,并且將得到的矩形換算到原圖中對應(yīng)的位置偎快,從而檢測到了80x80大小的目標(biāo)晒夹。實(shí)際上丐怯,我們每次對原圖進(jìn)行固定步長的縮放读跷,形成一個(gè)圖像金字塔效览,對圖像金字塔的每一層都掃描檢測丐枉,這就是多尺度檢測的框架瘦锹。

4.? 模塊功能

CascadeClassifier的使用中只要調(diào)用兩個(gè)外部接口沼本,一個(gè)是read抽兆,另一個(gè)是detectMultiScale辫红。

4.1? CascadeClassifier::read

4.1.1? 分類器的XML形式

read的過程就是對類的成員變量進(jìn)行初始化的過程贴妻,經(jīng)過這一步名惩,Data結(jié)構(gòu)按照之前已經(jīng)討論的邏輯被填充娩鹉。

先來看一下一個(gè)分類器的xml文件是怎樣組織的弯予。


圖片發(fā)自簡書App

整體上它包括stageType受楼、featureType艳汽、height骚灸、width、stageParams义郑、featureParams非驮、stages劫笙、features幾個(gè)節(jié)點(diǎn)填大。

這里的參數(shù)內(nèi)容就不展開了允华,主要來看一下stage結(jié)構(gòu)和feature在xml里是怎樣保存的,這樣訓(xùn)練結(jié)束后你可以自己打開這個(gè)文件看一下就明白訓(xùn)練了一個(gè)什么分類器出來了召耘。

下面是一個(gè)stage的內(nèi)部結(jié)構(gòu)污它,maxWeakCount是stage包含的弱分類器個(gè)數(shù)衫贬,stageThreshold是該stage的閾值祥山,也就是上面我們提到過的缝呕。接下來就是5個(gè)弱分類器了供常,每個(gè)弱分類器中包括internalNodes和leafValues兩個(gè)節(jié)點(diǎn)栈暇。前者分別是left和right標(biāo)記源祈、feature ID和threshold1香缺。

這里可以解釋一下featureID到底是指在哪里的ID了图张。下圖是分類器中的features節(jié)點(diǎn)中保存的該分類器使用到的各種特征值祸轮,feature ID就是在這些中的ID适袜,就是在這些之中的順序位置痪蝇。圖中的特征是一個(gè)HOG特征躏啰,rect節(jié)點(diǎn)中的前四個(gè)數(shù)字代表我們提到的矩形给僵,而最后的1表示要提取的特征值是block中提取的36維向量中的哪一個(gè)帝际。當(dāng)然蹲诀,Haar和LBP特征的feature節(jié)點(diǎn)與此不同脯爪,不過也是類似的結(jié)構(gòu)痕慢。


圖片發(fā)自簡書App


4.1.2? 讀取的過程

清楚了分類器的xml形式之后快骗,就要從文件中讀取內(nèi)容至cascadeClassifier中了方篮」。可以把這部分分為Data的讀取和features的讀取兩部分。

bool CascadeClassifier::read(constFileNode& root)

{

? if( !data.read(root) )//Data的讀取

? ? ? return false;

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

? FileNode fn= root[CC_FEATURES];

? if( fn.empty() )

? ? ? return false;

? return featureEvaluator->read(fn);//features的讀取

}

4.1.2.1? Data的讀取

先來看看Data的讀取裕照,這里以HOG特征的分類器為例晋南,并且跳過stage的參數(shù)讀取部分负间,直接來看如何在Data中建立stage結(jié)構(gòu)的。

// load stages

? ? fn = root[CC_STAGES];

? ? if( fn.empty() )

? ? ? ? return false;

? ? stages.reserve(fn.size());//先給vector分配空間出來

? ? classifiers.clear();

? ? nodes.clear();

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

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

{

? ? //進(jìn)入單個(gè)stage中

? ? ? ? FileNodefns = *it;

? ? ? Stagestage;//stage結(jié)構(gòu)中包含threshold趾访、ntrees和first三個(gè)變量

? ? ? ? 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();//ntrees和first指出該stage中包含的樹的數(shù)目和起始位置

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

? ? ? ? classifiers.reserve(stages[si].first +stages[si].ntrees);//相應(yīng)地?cái)U(kuò)展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;//一個(gè)節(jié)點(diǎn)包含nodeStep個(gè)值溃槐,計(jì)算得到當(dāng)前的弱分類器中包含幾個(gè)節(jié)點(diǎn)昏滴,無論在哪種特征的分類器中這個(gè)值其實(shí)都可以默認(rèn)為1

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

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

? ? ? ? ? ? leaves.reserve(leaves.size() +leafValues.size());//擴(kuò)展存儲node和leaves的vector結(jié)構(gòu)空間

? ? ? ? ? ? if(subsetSize > 0 )//關(guān)于subsetSize的內(nèi)容都是只在LBP分類器中使用

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

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

//開始訪問節(jié)點(diǎn)內(nèi)部

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

? ? ? ? ? ? {

? ? ? ? ? ? ? ? DTreeNodenode;//一個(gè)node中包含left蟹倾、right、threshold和featureIdx四個(gè)變量培慌。其中l(wèi)eft和right是其對應(yīng)的代號吵护,left=0馅而,right=-1瓮恭;featureIdx指的是整個(gè)分類器中使用的特征池中某個(gè)特征的ID屯蹦,比如共有108個(gè)特征绳姨,那么featureIdx就在0~107之間飘庄;threshold是上面提到的竭宰。同時(shí)可以看到這里的HOG分類器中每個(gè)弱分類器僅包含一個(gè)node切揭,也就是僅對某一個(gè)特征做判斷廓旬,而不是多個(gè)特征的集合

? ? ? ? ? ? ? ? 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結(jié)構(gòu)nodes中

? ? ? ? ? ? }

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

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

? ? ? ? ? ? ? ? leaves.push_back((float)*internalNodesIter);//leaves中保存相應(yīng)每個(gè)node的left leaf和right leaf的值,因?yàn)槊總€(gè)weak tree只有一個(gè)node也就分別只有一個(gè)left leaf和right leaf励背,這些將保存在leaves中

? ? ? ? }

? ? }

通過stage樹的建立可以看出最終是獲取stages叶眉、classifiers衅疙、nodes和leaves四個(gè)vector變量饱溢。其中的nodes和leaves共同組成一系列有序節(jié)點(diǎn)走芋,而classifiers中的變量則是在這些節(jié)點(diǎn)中查詢來構(gòu)成一個(gè)由弱分類器組翁逞,它僅僅是把這些弱分類器組合在一起熄攘,最后stages中每一個(gè)stage也就是一個(gè)強(qiáng)分類器挪圾,它在classifiers中查詢得到自己所屬的弱分類器都有哪些哲思,從而構(gòu)成一個(gè)強(qiáng)分類器的基礎(chǔ)。

4.1.2.2? features的讀取

特征的讀取最終將保留在featureEvaluator中的vector中帝簇。所以先來看一下Feature的定義,仍舊以HOG特征為例:

struct Feature

? ? {

? ? ? ? Feature();

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

? ? ? ? void updatePtrs( const vector&_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

};

其中的CELL_NUM和BIN_NUM分別是HOG特征提取的過程中block內(nèi)cell個(gè)數(shù)和梯度方向劃分的區(qū)間個(gè)數(shù)芋浮。也就是說纸巷,在一個(gè)block內(nèi)將提取出CELL_NUM*BIN_NUM維度的HOG特征向量瘤旨。rect[CELL_NUM]保存的是block的四個(gè)矩形位置存哲,featComponent表明該特征是36維HOG特征中的哪一個(gè)值宏胯。而之后的pF與pN是重點(diǎn):首先我們假設(shè)featComponent=10肩袍,那就是說要提取的特征值是該rect描述的block內(nèi)提取的HOG特征的第10個(gè)值氛赐,而第一個(gè)cell中會產(chǎn)生9個(gè)值,那么第10個(gè)值就是第二個(gè)cell中的第一個(gè)值蒋川。通過原圖計(jì)算梯度和按照區(qū)間劃分的梯度積分圖之后,共產(chǎn)生9個(gè)積分圖夕冲,那么pF應(yīng)當(dāng)指向第1個(gè)積分圖內(nèi)rect描述的block內(nèi)的第二個(gè)cell矩形位置的四個(gè)點(diǎn)歹鱼。

? ? ? ? 在featureEvaluator的read中弥姻,將對所有features遍歷填充到vector中。

在下面的代碼中只是讀取了參數(shù)螺捐,并沒有更新pF和pN指針定血,因?yàn)槲覀冞€沒有獲得梯度積分圖澜沟。

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

{

? ? FileNodernode =node[CC_RECT];//rect節(jié)點(diǎn)下包括一個(gè)矩形和一個(gè)特征類型號featComponent

? ? FileNodeIteratorit =rnode.begin();

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

? ? 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個(gè)矩形之間的關(guān)系如下圖4所示

? ? return true;

}

4.2? CascadeClassifier::detectMultiScale

這里的代碼的偽碼可以簡單寫成如下:

vector results;

for( doublefactor = 1; ;factor*= scaleFact;

{

MatscaledImage(scaledImageSize,CV_8U,imageBuffer.data);

? ? resize( grayImage,scaledImage,scaledImageSize,0, 0, CV_INTER_LINEAR );

? ? ? detectSingleScale( scaledImage,results );

}

oupRectangles( results );

簡單來說号杏,多尺度檢測只是尺度縮放形成圖像金字塔然后在每個(gè)尺度上檢測之后將結(jié)果進(jìn)行合并的過程。

在detectSingleScale中斯棒,使用OpenCV中的并行計(jì)算機(jī)制盾致,以CascadeClassifierInvoker類對整圖掃描檢測。detectSingleScale的檢測過程仍以偽碼表達(dá)如下:

// detectSingleScale

featureEvaluator->setImage(image,data.origWinSize )

//CascadeClassifierInvoker

for( int y = 0; y

for(int x = 0; x

{

doublegypWeight;

int result=classifier->runAt(evaluator,Point(x, y), gypWeight);

results.push_back(R(x,y,W,H,scale));// R(x,y,W,H,scale)表示在scale尺度下檢測到的矩形(x,y,W,H)映射到原圖上時(shí)的矩形

}

可以看到上面的代碼中最重要的兩部分分別是setImage和runAt荣暮。

4.2.1? setImage

前面提到過庭惜,features的read部分僅僅把特征的參數(shù)讀取進(jìn)入vector中,并沒有對指針們初始化穗酥,這正是setImage要做的工作护赊。仍以HOG為例惠遏,setImage的偽碼如下:

vector hist;

Mat? ? norm;

integralHistogram( image,hist, norm );

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

{

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

}

integralHistogram的過程如下:首先計(jì)算image每個(gè)像素點(diǎn)的梯度幅值和梯度方向骏啰,梯度方向的區(qū)間為0~360°厨内,劃分為9個(gè)區(qū)間,按照梯度方向所屬區(qū)間統(tǒng)計(jì)每個(gè)區(qū)間內(nèi)image的梯度幅值的積分圖方仿。也就是說委粉,對于hist中的第一個(gè)Mat來說衷畦,先把所有梯度方向在0~40°之外的像素點(diǎn)的幅值置為0,然后計(jì)算梯度幅值圖的積分圖,保存為hist[0]球匕;第二個(gè)Mat對應(yīng)40~80°的區(qū)間……這樣照卦,得到一個(gè)包含9個(gè)Mat的hist瞬痘,而norm則是9個(gè)Mat對應(yīng)像素點(diǎn)的和。

接下來就是要根據(jù)hist和norm來更新每個(gè)Feature中的指針了,因?yàn)槲覀円呀?jīng)知道自己要計(jì)算的是一個(gè)在什么位置上的矩形、在那個(gè)區(qū)間上的特征,所以只要把指針更新到hist中的那個(gè)Mat上即可睁宰。注意较木,這里并沒有涉及到滑動窗口機(jī)制。

這樣在計(jì)算某個(gè)HOG特征值時(shí)虹蒋,我們只要計(jì)算下面的式子即可:

HOG(i) = (pF[0]+pF[3]-pF[1]-pF[2] )/( pN[0]+pN[3]-pN[1]-pN[2] )

4.2.2 runAt

runAt函數(shù)調(diào)用了其他方法,但它的偽碼可以如下:

? ? ? ? setWindow( hist, cvPoint(x,y) );

for(intstageIdx= 0; stageIdx

{

stage= cascadeStages[stageIdx];//當(dāng)前stage

sum= 0.0;

int ntrees = stage.ntrees;

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

{

node= cascadeNodes[nodeOfs];//當(dāng)前node

doublevalue =featureEvaluator(node.featureIdx);//計(jì)算vector中的第featureIdx個(gè)特征的值

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

}

if( sum < stage.threshold )//如果總和大于stage的threshold則通過喻粹,小于則退出查乒,并返回當(dāng)前stage的相反數(shù)

return-stageIdx;

}

setWindow是根據(jù)當(dāng)前的位置(x,y)計(jì)算Feature中的指針應(yīng)當(dāng)在積分圖上的偏移量蓖议,可以看到這里才是滑動窗口機(jī)制實(shí)現(xiàn)的真正部分修然,而不是在setImage中中贝,setImage只是給出各個(gè)特征對應(yīng)的指針相對位置老厌,而不是真實(shí)位置。

后面在stage和node中的遍歷和檢測薇溃,正是體現(xiàn)弱分類器邑时、強(qiáng)分類器和級聯(lián)分類器的概念。當(dāng)stage中有一個(gè)不滿足時(shí)铜靶,立即退出不再進(jìn)入下一級哩掺,這是級聯(lián)分類器的概念炒刁;弱分類器的判定僅僅給出一個(gè)分?jǐn)?shù)疾瓮,若干個(gè)弱分類器的分?jǐn)?shù)的和作為強(qiáng)分類器的判定依據(jù)盈匾,這是強(qiáng)弱分類器的概念启昧。

4.3 LBP的判定

這里的HOG的例子追城,與Haar很相似冠胯,只是特征計(jì)算環(huán)節(jié)有所不同,在判定環(huán)節(jié)都是根據(jù)某個(gè)閾值來判斷京髓,但是LBP除了在特征計(jì)算環(huán)節(jié)不同以外,在判定環(huán)節(jié)也大不相同蒋困。訓(xùn)練獲得的LBP分類器的node中包含8個(gè)數(shù)存儲在subset中,與node的存儲很類似乾吻。然后在判定階段按照下式

t = subset[c>>5]& (1 << (c & 31))

其中c是提取得到的LBP特征值。當(dāng)t為0時(shí)鹊奖,結(jié)果為左葉,為1時(shí),結(jié)果為右葉重贺。

4.4 groupRectangles

最終的結(jié)果由于在多尺度上獲得,因而矩形之間難免有重合踏志、重疊和包含的關(guān)系,由于縮放尺度可能相對于目標(biāo)大小比較小嗦玖,導(dǎo)致同一個(gè)目標(biāo)在多個(gè)尺度上被檢測出來绘雁,所以有必要進(jìn)行合并。OpenCV的合并規(guī)則中有按照權(quán)重合并的禽拔,也有以MeanShift方法合并的,最簡單的一種是直接按照位置和大小關(guān)系合并秋忙。首先將所有矩形按照大小位置合并成不同的類別,然后將同一類別中的矩形合并成同一個(gè)矩形报强,當(dāng)不滿足給出的閾值條件時(shí),該矩形不會被保存下來格仲。這一部分不是檢測的核心豹芯,不做詳細(xì)討論。

5.? 保留的Haar支持

之前已經(jīng)說過我碟,老版本的OpenCV中只支持Haar特征的分類器訓(xùn)練和檢測矫俺,所以有大量性能表現(xiàn)優(yōu)秀的老版本Haar分類器已經(jīng)訓(xùn)練獲得包斑,如果新版本的CascadeClassifier不能支持這些分類器,那是非常遺憾的事揭糕。所以CascadeClassifier中在做新版本分類器載入之后氢拥,如果失敗將會按照老版本的分類器再做一次載入,并且保存到odlCascade指針中锨侯。在做檢測時(shí)嫩海,如果oldCascade不為空,則按照老版本的Haar分類器做檢測囚痴。這個(gè)過程完全是以C風(fēng)格的代碼完成叁怪,是對OpenCV2.2之前版本的繼承。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末深滚,一起剝皮案震驚了整個(gè)濱河市奕谭,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌痴荐,老刑警劉巖血柳,帶你破解...
    沈念sama閱讀 206,378評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異生兆,居然都是意外死亡难捌,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,356評論 2 382
  • 文/潘曉璐 我一進(jìn)店門鸦难,熙熙樓的掌柜王于貴愁眉苦臉地迎上來根吁,“玉大人,你說我怎么就攤上這事合蔽』鞯校” “怎么了?”我有些...
    開封第一講書人閱讀 152,702評論 0 342
  • 文/不壞的土叔 我叫張陵辈末,是天一觀的道長愚争。 經(jīng)常有香客問我,道長挤聘,這世上最難降的妖魔是什么轰枝? 我笑而不...
    開封第一講書人閱讀 55,259評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮组去,結(jié)果婚禮上鞍陨,老公的妹妹穿的比我還像新娘。我一直安慰自己从隆,他們只是感情好诚撵,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,263評論 5 371
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著键闺,像睡著了一般寿烟。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上辛燥,一...
    開封第一講書人閱讀 49,036評論 1 285
  • 那天筛武,我揣著相機(jī)與錄音,去河邊找鬼挎塌。 笑死徘六,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的榴都。 我是一名探鬼主播待锈,決...
    沈念sama閱讀 38,349評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼嘴高!你這毒婦竟也來了竿音?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 36,979評論 0 259
  • 序言:老撾萬榮一對情侶失蹤拴驮,失蹤者是張志新(化名)和其女友劉穎谍失,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體莹汤,經(jīng)...
    沈念sama閱讀 43,469評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡快鱼,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,938評論 2 323
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了纲岭。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片抹竹。...
    茶點(diǎn)故事閱讀 38,059評論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖止潮,靈堂內(nèi)的尸體忽然破棺而出窃判,到底是詐尸還是另有隱情,我是刑警寧澤喇闸,帶...
    沈念sama閱讀 33,703評論 4 323
  • 正文 年R本政府宣布袄琳,位于F島的核電站询件,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏唆樊。R本人自食惡果不足惜宛琅,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,257評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望逗旁。 院中可真熱鬧嘿辟,春花似錦、人聲如沸片效。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,262評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽淀衣。三九已至昙读,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間膨桥,已是汗流浹背箕戳。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評論 1 262
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留国撵,地道東北人陵吸。 一個(gè)月前我還...
    沈念sama閱讀 45,501評論 2 354
  • 正文 我出身青樓,卻偏偏與公主長得像介牙,于是被迫代替她去往敵國和親壮虫。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,792評論 2 345

推薦閱讀更多精彩內(nèi)容

  • 前一節(jié)OpenCV For iOS(四-上): 人臉檢測及分類器的訓(xùn)練說到分類器使用的 LBP Haar,這里補(bǔ)充...
    hehtao閱讀 2,496評論 0 3
  • 知乎上看到一個(gè)話題——目前火熱的 Deep Learning 會滅絕傳統(tǒng)的 SIFT / SURF 特征提取方法嗎...
    牛奶芝麻閱讀 100,733評論 4 81
  • 姓名:徐嬌 學(xué)號:17011210547 參考360百科 【嵌牛導(dǎo)讀】在圖像處理中环础,若要對圖像進(jìn)行特征提取囚似,經(jīng)常會...
    徐Jiao閱讀 7,201評論 2 6
  • 臨摹的畫作,╰(˙?˙)?━
    只有桂花香暗飄過閱讀 240評論 0 3
  • 我皇萬歲萬萬歲线得!我家有一位皇帝饶唤,還有一位皇后。當(dāng)然你們都應(yīng)該自行腦補(bǔ)一下贯钩,皇帝是誰募狂,皇后是誰。ok腦補(bǔ)時(shí)間到角雷,接下...
    周一秩禾閱讀 188評論 1 1