目标检测:选择性搜索策略(C++ / Python)

导读:通过本教程,我们将彻底理解一个重要的概念:目标检测中的常用方法“Selective Search”。文末也会给出使用C++或者Python的Opencv代码。

目标检测 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. 将分段部分对应的所有边界框添加到区域建议列表中
  2. 基于相似性的群邻近段
  3. 转到步骤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命令就会编译这个模块。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏一名叫大蕉的程序员

尝试克服一下小伙伴对神经网络的恐惧No.26

我是小蕉。 研表究明,这的网官的demo,代码确实的是己打自的。 这两天仔细研究了一下神经网络,简单的结构其实没想象中那么恐怖,只是我们自己吓自己,今天希望能把...

2026
来自专栏AI研习社

浏览器中的姿态检测:PoseNet 模型(附代码)

这里附上 Youtube 上这段视频的源代码,作者是 Siraj Raval:https://youtu.be/9KqNk5keyCc

3373
来自专栏互联网大杂烩

逻辑斯蒂回归(Logistic Regression)

定义x=<x1,x2,...xn>来表示n维特征向量,权重为w=<w1,w2,...wn>,同时,截距(Intercept)为b。则这种线性关系为: f(w,...

1022
来自专栏AI科技评论

开发 | 图片数据集太少?看我七十二变,Keras Image Data Augmentation 各参数详解

AI科技评论按,本文作者Professor ho,该文首发于知乎专栏Keras花式工具箱,AI科技评论获其授权转载。 图像深度学习任务中,面对小数据集,我们往往...

44710

用Python的长短期记忆神经网络进行时间序列预测

长短期记忆递归神经网络具有学习长的观察序列的潜力。

3K8
来自专栏大数据挖掘DT机器学习

如何用TensorFlow和TF-Slim实现图像标注、分类与分割

本文github源码地址: 在公众号 datadw 里 回复 图像 即可获取。 笔者将和大家分享一个结合了TensorFlow和slim库的小应用,来实现...

6134
来自专栏利炳根的专栏

学习笔记CB010:递归神经网络、LSTM、自动抓取字幕

递归神经网络(RNN),时间递归神经网络(recurrent neural network),结构递归神经网络(recursive neural network...

5894
来自专栏人工智能

使用Keras在训练深度学习模型时监控性能指标

Keras库提供了一套供深度学习模型训练时的用于监控和汇总的标准性能指标并且开放了接口给开发者使用。

2.2K10
来自专栏杂七杂八

神经网络训练细节part1(下)

学习率的选择通过先用少部分数据来进行测试,选择一个较好的学习速率。选择的方式可以为在某个范围内随机取值,观察样本的准确率

1042
来自专栏AI科技大本营的专栏

一文解决图片数据集太少的问题:详解KerasImageDataAugmentation各参数

作者 | Professor ho 本文转自Professor ho的知乎专栏 图像深度学习任务中,面对小数据集,我们往往需要利用Image Data Aug...

4326

扫码关注云+社区

领取腾讯云代金券