參考:
1、https://github.com/spmallick/learnopencv
2、https://github.com/opencv/
選擇性搜索對象檢測(C ++ / Python)
在本教程中,我們將了解在對象檢測中稱為“選擇性搜索”的重要概念。 我們還將分享C ++和Python中的OpenCV代碼。
對象檢測與對象識別
物體識別算法識別圖像中存在哪些物體。 它將整個圖像作為輸入并輸出該圖像中存在的對象的類標簽和類概率。 例如,一個類標簽可能是“狗”,相關(guān)的類概率可能是97%。
另一方面,物體檢測算法不僅告訴您圖像中存在哪些物體,還會輸出邊界框(x,y,寬度,高度)來指示圖像內(nèi)物體的位置。
所有對象檢測算法的核心是一個對象識別算法。 假設(shè)我們訓練了一個識別圖像塊中的狗的對象識別模型。 這個模型會告訴一個圖像是否有一只狗在里面。 它不會告訴對象所在的位置。
為了本地化對象,我們必須選擇圖像的子區(qū)域(補丁),然后將對象識別算法應用于這些圖像補丁。 物體的位置由物體識別算法返回的類概率高的圖像塊的位置給出。
生成較小子區(qū)域(補?。┑淖钪苯拥姆椒ㄊ腔瑒哟翱诜椒?。 但是,滑動窗口方法有幾個限制。 這些限制被稱為“區(qū)域提議”算法的一類算法所克服。 選擇性搜索是最受歡迎的地區(qū)提議算法之一。
滑動窗口算法
在滑動窗口方法中,我們在圖像上滑動一個方框或窗口來選擇一個補丁,并使用對象識別模型對窗口覆蓋的每個圖像補丁進行分類。 這是一個徹底的搜索整個圖像的對象。 我們不僅需要搜索圖像中所有可能的位置,還必須以不同的比例搜索。 這是因為對象識別模型通常是以特定的尺度(或尺度范圍)進行訓練的。 這導致對成千上萬的圖像塊進行分類。
問題并沒有在這里結(jié)束。 滑動窗口方法適用于固定寬高比的物體,如人臉或行人。 圖像是3D對象的2D投影。 諸如縱橫比和形狀的對象特征根據(jù)拍攝圖像的角度而顯著變化。 滑動窗口的方法,因為當我們搜索多個縱橫比時,計算上非常昂貴。
區(qū)域建議算法
到目前為止我們討論的問題可以通過使用區(qū)域提議算法來解決。 這些方法將圖像作為與圖像中最可能是對象的所有補丁對應的輸入和輸出邊界框。 這些地區(qū)的建議可以是嘈雜的,重疊的,可能不完全包含對象,但在這些地區(qū)的建議中,會有一個與圖像中的實際對象非常接近的建議。 然后,我們可以使用對象識別模型對這些建議進行分類。 具有高概率分數(shù)的區(qū)域提議是對象的位置。
Blue Boxes: False Positives; Green Boxes: True Positives
區(qū)域提議算法使用分割來識別圖像中的預期對象。 在分割中,我們根據(jù)一些標準(如顏色,紋理等)將相鄰的區(qū)域進行分組。不同于滑動窗口方法,我們在所有像素位置和所有尺度上查找對象,區(qū)域提議算法通過 將像素分組為較少的片段。 所以生成的提案的最終數(shù)量比滑動窗口方法少很多倍。 這減少了我們必須分類的圖像補丁的數(shù)量。 這些生成的區(qū)域提案具有不同的比例和長寬比。
區(qū)域建議方法的一個重要特性是具有很高的召回率。 這只是一個奇特的說法,即包含我們所看到的對象的區(qū)域必須位于我們的區(qū)域提案列表中。 為了實現(xiàn)這個目標,我們的區(qū)域提案列表可能最終會包含很多不包含任何對象的區(qū)域。 換句話說,區(qū)域提議算法可以產(chǎn)生大量的誤報,只要它能夠捕捉到所有的真實的肯定。 這些誤報大部分將被對象識別算法拒絕。 當我們有更多的誤報,準確性受到一些影響時,檢測所花費的時間就會增加。 但是高回憶率仍然是一個好主意,因為缺少包含實際對象的區(qū)域的選擇會嚴重影響檢測率。
已經(jīng)提出了幾種區(qū)域建議方法,例如
1、對象性
2、用于自動對象分割的約束參數(shù)Min-Cuts
3、類別獨立對象建議
4、隨機Prim
5、選擇性搜索
在所有這些區(qū)域建議方法中,選擇性搜索是最常用的,因為它是快速的并具有很高的召回率。
對象識別的選擇性搜索
什么是選擇性搜索?
選擇性搜索是用于對象檢測的區(qū)域提議算法。 它被設(shè)計成具有非常高的召回速度。 它基于計算基于顏色,紋理,尺寸和形狀兼容性的相似區(qū)域的分層分組。
選擇性搜索通過使用Felzenszwalb和Huttenlocher的基于圖的分割方法,基于像素的強度對圖像進行過分割開始。 算法的輸出如下所示。 右側(cè)的圖像包含使用純色表示的分段區(qū)域。
這個圖像中的部分分段作為區(qū)域建議? 答案是否定的,為什么我們不能這樣做有兩個原因:
1、原始圖像中的大多數(shù)實際對象都包含2個或多個分段部分
2、用這種方法不能產(chǎn)生封閉物體的區(qū)域建議,例如被杯子覆蓋的板或充滿咖啡的杯子
如果我們試圖通過進一步合并相鄰區(qū)域來解決第一個問題,我們將最終得到一個覆蓋兩個物體的分割區(qū)域。
完美的細分不是我們的目標。 我們只是想預測很多地區(qū)的建議,其中一些建議應該與實際的對象有很高的重疊。
選擇性搜索使用Felzenszwalb和Huttenlocher的方法作為最初的種子。 一個外向的圖像看起來像這樣。
選擇性搜索算法將這些外生作為初始輸入并執(zhí)行以下步驟
1、將所有與分割部分對應的邊界框添加到區(qū)域投標列表中
2、根據(jù)相似性對相鄰的段進行分組
3、轉(zhuǎn)到第1步
在每次迭代中,形成更大的片段并將其添加到區(qū)域提案列表中。 因此,我們使用自下而上的方法從較小的部分向較大的部分創(chuàng)建區(qū)域提案。 這就是我們所說的使用Felzenszwalb和Huttenlocher的規(guī)則來計算“等級”的分割。
該圖顯示了分層分割過程的初始,中間和最后一步。
相似
讓我們深入了解如何計算兩個區(qū)域之間的相似度。
選擇性搜索使用基于顏色,紋理,尺寸和形狀兼容性的4種相似性度量。
顏色相似性
為圖像的每個通道計算25個區(qū)域的顏色直方圖,并且連接所有通道的直方圖以獲得顏色描述符,得到25×3 = 75維顏色描述符。
兩個區(qū)域的顏色相似度基于直方圖交點,可以計算為:
cki是顏色描述符中kth bin的直方圖值
紋理相似性
紋理特征是通過提取每個通道8個方向的高斯導數(shù)來計算的。 對于每個方向和每個顏色通道,計算10個直方圖,得到10×8×3 = 240維特征描述符。
兩個區(qū)域的紋理相似性也使用直方圖交點來計算。
tki是紋理描述符中的第k個{bin}的直方圖值
尺寸相似
尺寸相似性鼓勵較小的地區(qū)早期合并。 它確保了所有地區(qū)的地區(qū)建議都形成在圖像的各個部分。 如果不考慮這種相似性度量,單個區(qū)域會一個接一個地吞噬所有較小的相鄰區(qū)域,因此只能在這個位置生成多個尺度的區(qū)域提案。 大小相似性定義為:
size(im)是圖像的大小(以像素為單位)
形狀兼容性
形狀兼容性測量兩個區(qū)域(ri和rj)彼此適合的程度。 如果ri適合于rj,我們希望合并它們以彌補差距,如果它們甚至不相互接觸,則不應該合并。
形狀兼容性定義為:
size(BBij)是圍繞ri和rj的邊界框
最后的相似性
兩個區(qū)域之間的最終相似性被定義為上述4個相似性的線性組合。
其中ri和rj是圖像中的兩個區(qū)域或片段,ai∈0,1表示是否使用相似性度量。
結(jié)果
OpenCV中的選擇性搜索實現(xiàn)使成千上萬個區(qū)域提案按照對象的順序排列。 為了清楚起見,我們正在共享結(jié)果,在圖像上繪制200-250個框。 一般而言,1000-1200個建議足以獲得所有正確的地區(qū)建議。
選擇性搜索代碼
我們來看看如何使用在OpenCV中實現(xiàn)的基于選擇性搜索的分割。
選擇性搜索:C ++
下面的代碼是使用OpenCV進行選擇性搜索的C ++教程。 請通讀注釋以了解代碼。
#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;
//程序執(zhí)行命令:./ssearch input_image (f|q)
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 調(diào)整尺寸
int newHeight = 200;
int newWidth = im.cols*newHeight/im.rows;
resize(im, im, Size(newWidth, newHeight));
// create Selective Search Segmentation Object using default parameters
// 使用默認參數(shù)創(chuàng)建選擇性搜索分段對象
Ptr<SelectiveSearchSegmentation> ss = createSelectiveSearchSegmentation();
// set input image on which we will run segmentation
// 設(shè)置我們將運行分割的輸入圖像
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
// 如果最后一個參數(shù)既不是f也不是q打印幫助信息
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
// 顯示區(qū)域建議的數(shù)量
int numShowRects = 100;
// increment to increase/decrease total number
// of reason proposals to be shown
// 增加/減少要顯示的理由建議的總數(shù)
int increment = 50;
while(1) {
// create a copy of original image
// 創(chuàng)建一個原始圖像的副本
Mat imOut = im.clone();
// itereate over all the region proposals
// 遍歷所有地區(qū)的建議
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
// m被按下
if (k == 109) {
// increase total number of rectangles to show by increment
// 增加矩形的總數(shù)以增量顯示
numShowRects += increment;
}
// l is pressed
// l被按下
else if (k == 108 && numShowRects > increment) {
// decrease total number of rectangles to show by increment
// 減少總數(shù)的矩形顯示增量
numShowRects -= increment;
}
// q is pressed
// 按下q 退出
else if (k == 113) {
break;
}
}
return 0;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 100
- 101
- 102
- 103
- 104
- 105
- 106
- 107
- 108
- 109
- 110
- 111
- 112
- 113
- 114
- 115
- 116
- 117
- 118
- 119
- 120
- 121
- 122
- 123
- 124
- 125
注:
缺失的模塊ximgproc可以去
https://github.com/opencv/opencv_contrib下載安裝
編譯參考:http://blog.csdn.net/wc781708249/article/details/78273241
選擇性搜索:Python
下面的代碼是使用OpenCV 3.3進行選擇性搜索的Python教程。 注意在代碼塊之后提到的OpenCV 3.2的錯誤警報。 請通讀注釋以了解代碼。
#!/usr/bin/env python
# -*- coding: UTF-8 -*-
'''
Usage:
./ssearch.py input_image (f|q)
f=fast, q=quality
Use "l" to display less rects, 'm' to display more rects, "q" to quit.
'''
import sys
import 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
# 使用默認參數(shù)創(chuàng)建選擇性搜索分段對象
ss = cv2.ximgproc.segmentation.createSelectiveSearchSegmentation()
# set input image on which we will run segmentation
# 設(shè)置我們將運行分割的輸入圖像
ss.setBaseImage(im)
# Switch to fast but low recall Selective Search method
# 切換到快速但低回調(diào)選擇性搜索方法
if (sys.argv[2] == 'f'):
ss.switchToSelectiveSearchFast()
# Switch to high recall but slow Selective Search method
# 切換到高回調(diào),但慢選擇性搜索方法
elif (sys.argv[2] == 'q'):
ss.switchToSelectiveSearchQuality()
# if argument is neither f nor q print help message
# 如果參數(shù)既不是f也不是q打印幫助信息
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
# 顯示區(qū)域建議的數(shù)量
numShowRects = 100
# increment to increase/decrease total number
# of reason proposals to be shown
# 增加/減少要顯示的理由建議的總數(shù)
increment = 50
while True:
# create a copy of original image
# 創(chuàng)建一個原始圖像的副本
imOut = im.copy()
# itereate over all the region proposals
# 遍歷所有地區(qū)的建議
for i, rect in enumerate(rects):
# draw rectangle for region proposal till numShowRects
# 為區(qū)域提議繪制矩形,直到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
# 增加矩形的總數(shù)以增量顯示
numShowRects += increment
# l is pressed
elif k == 108 and numShowRects > increment:
# decrease total number of rectangles to show by increment
# 減少總數(shù)的矩形顯示增量
numShowRects -= increment
# q is pressed
elif k == 113:
break
# close image show window
cv2.destroyAllWindows()
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 100
- 101
- 102
- 103
- 104
Bug警告:在此提交中修復了選擇性搜索的Python綁定中的一個錯誤。 所以Python代碼可以用于OpenCV 3.3.0,但不適用于OpenCV 3.2.0。
如果你不想編譯OpenCV 3.3.0,并且擁有之前編譯的OpenCV 3.2.0的build文件夾,你也可以修復這個bug。
如果你看看Github提交,這只是一個小小的改變。 您必須更改文件中的第239行
opencv_contrib-3.2.0/modules/ximgproc/include/opencv2/ximgproc/segmentation.hpp
// from
CV_WRAP virtual void process(std::vector<Rect>& rects) = 0;
// to
CV_WRAP virtual void process(CV_OUT std::vector<Rect>& rects) = 0;
現(xiàn)在再次重新編譯你的OpenCV 3.2.0。 如果你有一個早期編譯OpenCV的生成文件夾,運行make命令將會編譯這個模塊。
|