在本教程中,我們將了解對(duì)象檢測(cè)中稱為“選擇性搜索”的重要概念。我們還將用C ++和Python共享OpenCV代碼。 物體檢測(cè)與物體識(shí)別 對(duì)象識(shí)別算法識(shí)別圖像中存在哪些對(duì)象。它將整個(gè)圖像作為輸入,并輸出該圖像中存在的對(duì)象的類標(biāo)簽和類概率。例如,類標(biāo)簽可以是“狗”,相關(guān)的類概率可以是97%。 另一方面,對(duì)象檢測(cè)算法不僅告訴您圖像中存在哪些對(duì)象,還輸出邊界框(x,y,寬度,高度)以指示圖像內(nèi)對(duì)象的位置。 所有物體檢測(cè)算法的核心是物體識(shí)別算法。假設(shè)我們訓(xùn)練了一個(gè)物體識(shí)別模型,該模型識(shí)別圖像斑塊中的狗。該模型將判斷圖像中是否有狗。它不會(huì)告訴對(duì)象的位置。 為了本地化對(duì)象,我們必須選擇圖像的子區(qū)域(塊),然后將對(duì)象識(shí)別算法應(yīng)用于這些圖像塊。對(duì)象的位置由圖像塊的位置給出,其中對(duì)象識(shí)別算法返回的類概率高。 生成較小子區(qū)域(補(bǔ)?。┑淖钪苯臃椒ǚQ為滑動(dòng)窗口方法。然而,滑動(dòng)窗口方法有幾個(gè)局限性。一類稱為“區(qū)域提議”算法的算法克服了這些限制。選擇性搜索是最受歡迎的區(qū)域提案算法之一。 滑動(dòng)窗口算法 在滑動(dòng)窗口方法中,我們?cè)趫D像上滑動(dòng)框或窗口以選擇補(bǔ)丁,并使用對(duì)象識(shí)別模型對(duì)窗口覆蓋的每個(gè)圖像補(bǔ)丁進(jìn)行分類。它是對(duì)整個(gè)圖像上的對(duì)象的詳盡搜索。我們不僅需要搜索圖像中的所有可能位置,還必須搜索不同的比例。這是因?yàn)閷?duì)象識(shí)別模型通常以特定尺度(或尺度范圍)進(jìn)行訓(xùn)練。這導(dǎo)致對(duì)數(shù)萬(wàn)個(gè)圖像塊進(jìn)行分類。 問(wèn)題并沒(méi)有在這里結(jié)束?;瑒?dòng)窗口方法適用于固定寬高比的物體,如面部或行人。圖像是3D對(duì)象的2D投影。寬高比和形狀等對(duì)象特征會(huì)根據(jù)拍攝圖像的角度而有很大差異?;瑒?dòng)窗口方法,因?yàn)楫?dāng)我們搜索多個(gè)寬高比時(shí),計(jì)算上非常昂貴。 區(qū)域提案算法 到目前為止我們討論的問(wèn)題可以使用區(qū)域提議算法來(lái)解決。這些方法將圖像作為輸入和輸出邊界框,對(duì)應(yīng)于圖像中最可能是對(duì)象的所有面片。這些區(qū)域提議可能是嘈雜的,重疊的并且可能不完全包含對(duì)象,但是在這些區(qū)域提議中,將有一個(gè)非常接近圖像中的實(shí)際對(duì)象的提議。然后我們可以使用對(duì)象識(shí)別模型對(duì)這些提議進(jìn)行分類。具有高概率得分的區(qū)域提議是對(duì)象的位置。 區(qū)域提議算法使用分段識(shí)別圖像中的預(yù)期對(duì)象。在分割中,我們基于一些標(biāo)準(zhǔn)(例如顏色,紋理等)將相鄰區(qū)域彼此相似地分組。與我們?cè)谒邢袼匚恢煤退谐叨壬蠈ふ覍?duì)象的滑動(dòng)窗口方法不同,區(qū)域建議算法通過(guò)以下方式工作:將像素分組為較少數(shù)量的段。因此,生成的最終提案數(shù)量比滑動(dòng)窗口方法少很多倍。這減少了我們必須分類的圖像補(bǔ)丁的數(shù)量。這些生成的區(qū)域提議具有不同的比例和寬高比。 區(qū)域提案方法的一個(gè)重要特性是具有非常高的召回率。這只是一種奇特的說(shuō)法,即包含我們正在尋找的對(duì)象的區(qū)域必須在我們的區(qū)域提案列表中。為了實(shí)現(xiàn)這一點(diǎn),我們的區(qū)域提議列表最終可能會(huì)包含許多不包含任何對(duì)象的區(qū)域。換句話說(shuō),區(qū)域提議算法可以產(chǎn)生大量的誤報(bào),只要它能夠捕獲所有真正的正數(shù)。大多數(shù)這些誤報(bào)將被物體識(shí)別算法拒絕。當(dāng)我們有更多的誤報(bào)并且精度受到輕微影響時(shí),檢測(cè)所需的時(shí)間就會(huì)增加。但是,召回率高仍然是一個(gè)好主意,因?yàn)殄e(cuò)過(guò)包含實(shí)際對(duì)象的區(qū)域的替代方案會(huì)嚴(yán)重影響檢測(cè)率。 已經(jīng)提出了幾種區(qū)域提議方法,例如
在所有這些區(qū)域提議方法中,選擇性搜索是最常用的,因?yàn)樗俣瓤烨艺倩芈屎芨摺?/p> 選擇性搜索對(duì)象識(shí)別 什么是選擇性搜索? 選擇性搜索是用于對(duì)象檢測(cè)的區(qū)域提議算法。它設(shè)計(jì)為快速,具有很高的召回率。它基于根據(jù)顏色,紋理,大小和形狀兼容性計(jì)算相似區(qū)域的分層分組。 選擇性搜索首先使用Felzenszwalb和Huttenlocher 基于圖形的分割方法,根據(jù)像素的強(qiáng)度對(duì)圖像進(jìn)行過(guò)度分割。算法的輸出如下所示。右側(cè)的圖像包含使用純色表示的分段區(qū)域。 中使用分段部分作為區(qū)域提案嗎?答案是否定的,我們不能這樣做有兩個(gè)原因:
如果我們?cè)噲D通過(guò)進(jìn)一步合并彼此相似的相鄰區(qū)域來(lái)解決第一個(gè)問(wèn)題,那么我們將最終得到一個(gè)覆蓋兩個(gè)對(duì)象的分段區(qū)域。 完美的細(xì)分不是我們的目標(biāo)。我們只想預(yù)測(cè)許多區(qū)域提案,使得其中一些提案應(yīng)與實(shí)際對(duì)象具有非常高的重疊。 選擇性搜索使用來(lái)自Felzenszwalb和Huttenlocher方法的調(diào)查作為初始種子。一個(gè)過(guò)度注釋的圖像看起來(lái)像這樣。 選擇性搜索算法將這些過(guò)量作為初始輸入并執(zhí)行以下步驟
在每次迭代中,形成更大的段并將其添加到區(qū)域提議列表中。因此,我們以自下而上的方式創(chuàng)建從較小的細(xì)分市場(chǎng)到較大細(xì)分市場(chǎng)的區(qū)域提案。這就是我們所說(shuō)的使用Felzenszwalb和Huttenlocher的計(jì)劃來(lái)計(jì)算“分層”分段。 此圖顯示了分層分段過(guò)程的初始,中間和最后一步。 相似 讓我們深入探討如何計(jì)算兩個(gè)區(qū)域之間的相似性。 選擇性搜索使用基于顏色,紋理,大小和形狀兼容性的4種相似性度量。 顏色相似 針對(duì)圖像的每個(gè)通道計(jì)算25個(gè)區(qū)間的顏色直方圖,并且連接所有通道的直方圖以獲得顏色描述符,得到25×3 = 75維顏色描述符。 兩個(gè)區(qū)域的顏色相似性基于直方圖交集,可以計(jì)算為: 是 第顏色描述符中bin 的直方圖值 紋理相似性 通過(guò)為每個(gè)通道提取8個(gè)方向的高斯導(dǎo)數(shù)來(lái)計(jì)算紋理特征。對(duì)于每個(gè)方向和每個(gè)顏色通道,計(jì)算10個(gè)箱的直方圖,得到10x8x3 = 240維特征描述符。 還使用直方圖交叉來(lái)計(jì)算兩個(gè)區(qū)域的紋理相似性。 是 第紋理描述符中bin 的直方圖值 大小相似 大小相似性鼓勵(lì)較小的區(qū)域盡早合并。它確保在圖像的所有部分形成所有尺度的區(qū)域提議。如果不考慮該相似性度量,則單個(gè)區(qū)域?qū)⒅饌€(gè)吞噬所有較小的相鄰區(qū)域,因此僅在該位置處生成多個(gè)尺度的區(qū)域提議。大小相似度定義為: 其中大小Size(im)是圖像的大小,以像素為單位。 形狀兼容性 形狀兼容性衡量?jī)蓚€(gè)區(qū)域(Ri和Rj)相互適合的程度。如果R_I適合r_j我們想合并它們以填補(bǔ)空白,如果它們甚至沒(méi)有相互接觸它們就不應(yīng)該合并。 形狀兼容性定義為: 在哪里大小Size(BBijj)是一個(gè)邊界框Ri和Rj。 最終相似性 兩個(gè)區(qū)域之間的最終相似性被定義為前述4個(gè)相似性的線性組合。 中 和 是圖像中的兩個(gè)區(qū)域或區(qū)段,并 表示是否使用相似性度量。 結(jié)果 OpenCV中的選擇性搜索實(shí)現(xiàn)提供了按對(duì)象性遞減順序排列的數(shù)千個(gè)區(qū)域提議。為清楚起見(jiàn),我們與圖像上方的200-250個(gè)框共享結(jié)果。一般來(lái)說(shuō),1000-1200個(gè)提案足以獲得所有正確的區(qū)域提案。 讓我們來(lái)看看我們?nèi)绾卧贠penCV中使用基于選擇性搜索的分段。 選擇性搜索:C ++ 下面的代碼是使用OpenCV進(jìn)行選擇性搜索的C ++教程。請(qǐng)仔細(xì)閱讀評(píng)論以了解代碼。 #include 'opencv2/ximgproc/segmentation.hpp'#include 'opencv2/highgui.hpp'#include 'opencv2/core.hpp'#include 'opencv2/imgproc.hpp'#include <iostream>#include <ctime> using namespace cv;using namespace cv::ximgproc::segmentation; static void help() { std::cout << std::endl << 'Usage:' << std::endl << './ssearch input_image (f|q)' << std::endl << 'f=fast, q=quality' << std::endl << 'Use l to display less rects, m to display more rects, q to quit' << std::endl;} int main(int argc, char** argv) { // If image path and f/q is not passed as command // line arguments, quit and display help message if (argc < 3) { help(); return -1; } // speed-up using multithreads setUseOptimized(true); setNumThreads(4); // read image Mat im = imread(argv[1]); // resize image int newHeight = 200; int newWidth = im.cols*newHeight/im.rows; resize(im, im, Size(newWidth, newHeight)); // create Selective Search Segmentation Object using default parameters Ptr<SelectiveSearchSegmentation> ss = createSelectiveSearchSegmentation(); // set input image on which we will run segmentation ss->setBaseImage(im); // Switch to fast but low recall Selective Search method if (argv[2][0] == 'f') { ss->switchToSelectiveSearchFast(); } // Switch to high recall but slow Selective Search method else if (argv[2][0] == 'q') { ss->switchToSelectiveSearchQuality(); } // if argument is neither f nor q print help message else { help(); return -2; } // run selective search segmentation on input image std::vector<Rect> rects; ss->process(rects); std::cout << 'Total Number of Region Proposals: ' << rects.size() << std::endl; // number of region proposals to show int numShowRects = 100; // increment to increase/decrease total number // of reason proposals to be shown int increment = 50; while(1) { // create a copy of original image Mat imOut = im.clone(); // itereate over all the region proposals for(int i = 0; i < rects.size(); i++) { if (i < numShowRects) { rectangle(imOut, rects[i], Scalar(0, 255, 0)); } else { break; } } // show output imshow('Output', imOut); // record key press int k = waitKey(); // m is pressed if (k == 109) { // increase total number of rectangles to show by increment numShowRects += increment; } // l is pressed else if (k == 108 && numShowRects > increment) { // decrease total number of rectangles to show by increment numShowRects -= increment; } // q is pressed else if (k == 113) { break; } } return 0;} 選擇性搜索:Python 下面的代碼是使用OpenCV 3.3進(jìn)行選擇性搜索的Python教程。請(qǐng)注意代碼塊后提到的OpenCV 3.2的錯(cuò)誤警報(bào)。請(qǐng)仔細(xì)閱讀評(píng)論以了解代碼。 #!/usr/bin/env python'''Usage: ./ssearch.py input_image (f|q) f=fast, q=qualityUse 'l' to display less rects, 'm' to display more rects, 'q' to quit.''' import sysimport cv2 if __name__ == '__main__': # If image path and f/q is not passed as command # line arguments, quit and display help message if len(sys.argv) < 3: print(__doc__) sys.exit(1) # speed-up using multithreads cv2.setUseOptimized(True); cv2.setNumThreads(4); # read image im = cv2.imread(sys.argv[1]) # resize image newHeight = 200 newWidth = int(im.shape[1]*200/im.shape[0]) im = cv2.resize(im, (newWidth, newHeight)) # create Selective Search Segmentation Object using default parameters ss = cv2.ximgproc.segmentation.createSelectiveSearchSegmentation() # set input image on which we will run segmentation ss.setBaseImage(im) # Switch to fast but low recall Selective Search method if (sys.argv[2] == 'f'): ss.switchToSelectiveSearchFast() # Switch to high recall but slow Selective Search method elif (sys.argv[2] == 'q'): ss.switchToSelectiveSearchQuality() # if argument is neither f nor q print help message else: print(__doc__) sys.exit(1) # run selective search segmentation on input image rects = ss.process() print('Total Number of Region Proposals: {}'.format(len(rects))) # number of region proposals to show numShowRects = 100 # increment to increase/decrease total number # of reason proposals to be shown increment = 50 while True: # create a copy of original image imOut = im.copy() # itereate over all the region proposals for i, rect in enumerate(rects): # draw rectangle for region proposal till numShowRects if (i < numShowRects): x, y, w, h = rect cv2.rectangle(imOut, (x, y), (x+w, y+h), (0, 255, 0), 1, cv2.LINE_AA) else: break # show output cv2.imshow('Output', imOut) # record key press k = cv2.waitKey(0) & 0xFF # m is pressed if k == 109: # increase total number of rectangles to show by increment numShowRects += increment # l is pressed elif k == 108 and numShowRects > increment: # decrease total number of rectangles to show by increment numShowRects -= increment # q is pressed elif k == 113: break # close image show window cv2.destroyAllWindows() |
|
來(lái)自: taotao_2016 > 《物理》