目标检测:选择性搜索策略(C++ / Python)
2017-09-21 14:31
936 查看
导读:通过本教程,我们将彻底理解一个重要的概念:目标检测中的常用方法“Selective Search”。文末也会给出使用C++或者Python的Opencv代码。
所有目标检测算法的核心是一种目标识别算法。假设我们训练一个可以从区域中(pathes)识别狗的目标识别模型,这个模型可以告诉我们该patch中有无狗,它并不会给出狗的坐标。
为了对目标进行定位,我们必须选择图像的子区域(pathes),然后将目标识别算法应用于这些图像块。目标的位置是由目标识别算法返回的类概率高的图像子区域的位置给出的。
生成较小子区域(pathes)的最直接的方法称为滑动窗口方法。然而,滑动窗口方法有几个局限性。这些局限性被一类称为“区域建议”算法的算法所克服。选择性搜索是最受欢迎的区域建议算法。
问题并没有到此为止。滑动窗口方法对于固定的纵横比对象,例如人脸或行人是很好的。图像是三维物体的二维投影,对象特征,如纵横比和形状根据所拍摄图像的角度而显著变化。滑动窗口的方法因为需要搜索多个纵横比,因此变得非常昂贵。
区域建议算法利用分割的方法识别图像中的前景物体。在分割时我们认为相邻的区域是彼此相似,基于一些标准,如颜色、纹理等不同的滑动窗口的方法,我们正在寻找所有的像素的位置和在所有尺度的对象,区域算法工作的分组像素到一个较小的段数。因此,提议的最终数量比滑动窗口方法少很多倍。这减少了我们必须分类的图像块的数量。这些生成的区域建议具有不同的尺度和长宽比。
目前提出了几种区域建议方法,如
1. Objectness
2. Constrained Parametric Min-Cuts for Automatic Object Segmentation
3. Category Independent Object Proposals
4. Randomized Prim
5. Selective Search
在所有这些区域建议方法中,选择性搜索是最常用的,因为它速度快,召回率高。
选择性搜索是一种用于目标检测的区域推荐算法。它的设计速度快,召回率高。它是根据颜色、纹理、大小和形状的兼容性,计算相似区域的层次分组。
选择性搜索开始了基于利用图由Felzenszwalb和Huttenlocher分割方法的像素的图像分割。该算法的输出如下所示。右边的图像包含用纯色表示的分段区域。
我们可以在这个图像中使用分段部分作为区域建议吗?答案是否定的,有两个原因可以解释为什么我们不能做到这一点:
1. 原始图像中的大多数实际对象包含2个或多个分段部分。
2. 用这种方法不能为被遮挡的物体提出建议,例如杯子覆盖的盘子或装满咖啡的杯子。
如果我们试图通过进一步合并相似的相邻区域来解决第一个问题,我们将得到一个覆盖两个对象的分段区域。完美的分割不是我们的目标。我们只是想预测许多区域的建议,其中一些建议应该与实际对象有很高的重叠。选择性搜索使用oversegments Felzenszwalb Huttenlocher的方法作为初始种子。过分割图像看起来像这样:
选择搜索算法将这些oversegments作为初始输入并执行以下步骤:
将分段部分对应的所有边界框添加到区域建议列表中
基于相似性的群邻近段
转到步骤1
在每次迭代中,都会生成较大的段,并添加到区域建议列表中。因此,我们用自下而上的方法从更小的部分到更大的部分创建区域建议。这就是我们所说的“层次”的基础上计算分割使用Huttenlocher的oversegments。
该图像显示了分层分割过程的初始、中间和最后一个步骤。其中文章中涉及的各种相似性参考原文中:
http://www.learnopencv.com/selective-search-for-object-detection-cpp-python/
Selective Search: C++
Selective Search: Python
Bug:在上述代码中,选择性搜索的Python绑定中出现了一个bug。所以Python代码使用OpenCV 3.3.0而不是OpenCV 3.2.0工作。如果你不想编译OpenCV 3.3.0,构建OpenCV 3.2.0你编译前的文件夹,你也可以修复这个bug。如果你看看GitHub上,这只是一个小小的改变。你必须在文件行# 239变化
opencv_contrib-3.2.0/modules/ximgproc/include/opencv2/ximgproc/segmentation.hpp
现在你又重新编译OpenCV 3.2.0。如果您有一个在早期编译过opencv的构建文件夹,那么运行make命令就会编译这个模块。
目标检测 vs 目标识别
目标识别解决了是什么的问题,目标检测解决了在哪里的问题。所有目标检测算法的核心是一种目标识别算法。假设我们训练一个可以从区域中(pathes)识别狗的目标识别模型,这个模型可以告诉我们该patch中有无狗,它并不会给出狗的坐标。
为了对目标进行定位,我们必须选择图像的子区域(pathes),然后将目标识别算法应用于这些图像块。目标的位置是由目标识别算法返回的类概率高的图像子区域的位置给出的。
生成较小子区域(pathes)的最直接的方法称为滑动窗口方法。然而,滑动窗口方法有几个局限性。这些局限性被一类称为“区域建议”算法的算法所克服。选择性搜索是最受欢迎的区域建议算法。
滑动窗口算法/Sliding Window Algorithm
在滑动窗口方法中,我们在图像上滑动一个框或窗口来选择一个区域,并使用目标识别模型对窗口覆盖的每个图像块进行分类。这是一个穷尽搜索整个图像的对象。我们不仅需要搜索图像中所有可能的位置,还得在不同的尺度上搜索。这是因为物体识别模型通常是在特定的尺度(或范围)上进行训练的。这将对成千上万的图像块进行分类。问题并没有到此为止。滑动窗口方法对于固定的纵横比对象,例如人脸或行人是很好的。图像是三维物体的二维投影,对象特征,如纵横比和形状根据所拍摄图像的角度而显著变化。滑动窗口的方法因为需要搜索多个纵横比,因此变得非常昂贵。
区域建议的算法/Region Proposal Algorithms
我们目前已经讨论过的问题可以用区域建议算法来解决。这些方法将图像作为输入和输出边界框,对应于图像中最可能成为对象的所有子区域。这些区域建议可能是嘈杂的,重叠的,可能不完全包含对象,但在这些区域建议中,将有一个与图像中的实际对象非常接近的建议。然后,我们可以使用对象识别模型对这些提议进行分类。具有高概率分数的区域建议是对象的位置。区域建议算法利用分割的方法识别图像中的前景物体。在分割时我们认为相邻的区域是彼此相似,基于一些标准,如颜色、纹理等不同的滑动窗口的方法,我们正在寻找所有的像素的位置和在所有尺度的对象,区域算法工作的分组像素到一个较小的段数。因此,提议的最终数量比滑动窗口方法少很多倍。这减少了我们必须分类的图像块的数量。这些生成的区域建议具有不同的尺度和长宽比。
目前提出了几种区域建议方法,如
1. Objectness
2. Constrained Parametric Min-Cuts for Automatic Object Segmentation
3. Category Independent Object Proposals
4. Randomized Prim
5. Selective Search
在所有这些区域建议方法中,选择性搜索是最常用的,因为它速度快,召回率高。
目标识别的选择性搜索
什么是选择性搜索?选择性搜索是一种用于目标检测的区域推荐算法。它的设计速度快,召回率高。它是根据颜色、纹理、大小和形状的兼容性,计算相似区域的层次分组。
选择性搜索开始了基于利用图由Felzenszwalb和Huttenlocher分割方法的像素的图像分割。该算法的输出如下所示。右边的图像包含用纯色表示的分段区域。
我们可以在这个图像中使用分段部分作为区域建议吗?答案是否定的,有两个原因可以解释为什么我们不能做到这一点:
1. 原始图像中的大多数实际对象包含2个或多个分段部分。
2. 用这种方法不能为被遮挡的物体提出建议,例如杯子覆盖的盘子或装满咖啡的杯子。
如果我们试图通过进一步合并相似的相邻区域来解决第一个问题,我们将得到一个覆盖两个对象的分段区域。完美的分割不是我们的目标。我们只是想预测许多区域的建议,其中一些建议应该与实际对象有很高的重叠。选择性搜索使用oversegments Felzenszwalb Huttenlocher的方法作为初始种子。过分割图像看起来像这样:
选择搜索算法将这些oversegments作为初始输入并执行以下步骤:
将分段部分对应的所有边界框添加到区域建议列表中
基于相似性的群邻近段
转到步骤1
在每次迭代中,都会生成较大的段,并添加到区域建议列表中。因此,我们用自下而上的方法从更小的部分到更大的部分创建区域建议。这就是我们所说的“层次”的基础上计算分割使用Huttenlocher的oversegments。
该图像显示了分层分割过程的初始、中间和最后一个步骤。其中文章中涉及的各种相似性参考原文中:
http://www.learnopencv.com/selective-search-for-object-detection-cpp-python/
结果
在OpenCV实现了选择性搜索区域建议的递减顺序排列对象名。为清楚起见,我们与顶级200-250盒画在图像共享成果。一般在1000-1200建议是好的足以让所有的正确区域的建议。选择性搜索代码
让我们来看看如何在opencv中实现基于选择性搜索的分割。Selective Search: 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; 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; }
Selective Search: Python
#!/usr/bin/env python ''' 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 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()
Bug:在上述代码中,选择性搜索的Python绑定中出现了一个bug。所以Python代码使用OpenCV 3.3.0而不是OpenCV 3.2.0工作。如果你不想编译OpenCV 3.3.0,构建OpenCV 3.2.0你编译前的文件夹,你也可以修复这个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;
现在你又重新编译OpenCV 3.2.0。如果您有一个在早期编译过opencv的构建文件夹,那么运行make命令就会编译这个模块。
相关文章推荐
- learn opencv-选择性搜索对象检测(C ++ / Python)
- python开启摄像头以及深度学习实现目标检测
- [目标检测]windows下实现c++版faster-rcnn
- Python 数据科学入门教程:TensorFlow 目标检测
- opencv3-python目标跟踪—基本的运动检测
- 飘逸的python - __get__ vs __getattr__ vs __getattribute__以及属性的搜索策略
- 程 | 深度学习 + OpenCV,Python 实现实时视频目标检测 机器之心 09-21
- 《OpenCV 3计算机视觉:Python语言实现》学习笔记——目标跟踪中基本运动检测的思考
- 使用python进行图像目标检测需要配置的环境
- 直接可以用的Python和OpenCV检测及分割图像的目标区域例子
- 基于OpenCV的人脸检测——C++和Python实现
- 使用SSD目标检测c++接口编译问题解决记录
- 目标检测Faster_r_cnn代码的使用(python+caffe版本)
- C++和python如何获取百度搜索结果页面下信息对应的真实链接(百度搜索爬虫,可指定页数)
- OpenCV实践之路——人脸检测(C++/Python)
- Linux下python 与 C++ dlib人脸检测
- [目标检测]windows下实现c++版faster-rcnn
- python+opencv+caffe+摄像头做目标检测
- 飘逸的python - __get__ vs __getattr__ vs __getattribute__以及属性的搜索策略
- c++导入python模块,如果python脚本与exe不在同一目录,需要增加搜索路径