SSD: Single Shot MultiBox Detector 检测单张图片

前言

博主也算是刚开始研究SSD项目,之前写了一篇SSD:Single Shot MultiBox Detector的安装配置和运行,这次是简单介绍下如何用SSD检测单张图片,其实过程也比较简单,下面正式开始。

准备工作

当然,首先你要把SSD按照教程编译好,设置好python环境变量(相当重要),然后重启计算机(建议),开始本次工作。SSD项目检测单张图片有C++程序和ipython程序,这里建议用ipython,主要是方便简单(最近新增了C++程序使用说明)。该工具路径为/home/mx/caffe/example/ssd_detect.ipynb,使用jupyter notebook工具操作。SSD项目常在更新,如果有模型不匹配的问题(报错信息为:shape mismatch. Source param shape is 12 512 3 3 (55296); target param shape is 16 512 3 3 (73728)),请去github下载最新的文件,包括代码和模型

安装 jupyter notebook

这里用pip安装该工具,安装pip的过的可以跳过下面一段:
下载安装包pip压缩包解压之后,发现里面有个setup.py文件,终端运行:

$ sudo python setup.py install

这样,pip就安装上了。
下面用pip安装jupyter notebook

#这里临时使用了清华大学的源,使用前后是蜗牛和火箭的速度对比
$ sudo pip install jupyter -i https://pypi.tuna.tsinghua.edu.cn/simple 

安装成功后,运行notebook

$ jupyter notebook

程序会在浏览器中打开notebook, 点击右上角的New-python2, 就可以新建一个网页一样的文件,扩展名为ipynb。在这个网页上,我们就可以像在命令行下面一样运行python代码了。输入代码后,按shift+enter运行,更多的快捷键,可点击上方的help-Keyboard shortcuts查看,或者先按esc退出编辑状态,再按h键查看。

SSD: Single Shot MultiBox Detector 检测单张图片_第1张图片

下载SSD模型

该工具使用的模型是VGG_VOC0712_SSD_300x300_iter_120000.caffemodel(以前是60000,作者更新了),为了第一次就成功,我们就用它了,这是下载地址 ,下载完后解压,把里面的VGGNet文件夹移动到/home/mx/caffe/models/之下。当然,你掌握这个程序后,完全可以修改路径,检测其他模型的效果。

顺便说下,部分人下载运行上述SSD模型会报错,有些错误很难排查,可以使用我正在使用的SSD300模型,暂时没有问题(对应的是最新的SSD代码):https://pan.baidu.com/s/1eSECLEU

运行程序检测单张图片

$ cd /home/mx/caffe/examples # 在该目录下打开jupyter notebook
$ jupyter notebook

在jupyter notebook打开的网页中找到ssd_detect.ipynb,打开后发现是这样:

SSD: Single Shot MultiBox Detector 检测单张图片_第2张图片

完整的程序我就不放出来了,你打开都和我一样,然后检查下面4个路径是否正确:

labelmap_file = 'data/VOC0712/labelmap_voc.prototxt'
model_def = 'models/VGGNet/VOC0712/SSD_300x300/deploy.prototxt'
model_weights = 'models/VGGNet/VOC0712/SSD_300x300/VGG_VOC0712_SSD_300x300_iter_120000.caffemodel'
image = caffe.io.load_image('examples/images/cat.jpg')

如果检查无误,就可以开始从头shift+enter运行了,一般会正常运行,也可能会出错,比如我就遇到如下错误:

SSD: Single Shot MultiBox Detector 检测单张图片_第3张图片

我的办法是,把caffe路径换成绝对路径:

把 caffe_root = ‘../’ 换成 caffe_root = ‘/home/mx/caffe/’ ,理论上不会再出错了,然后在诸如labelmp的路径前面添加caffe_root构成完整的绝对路径,比如:labelmap_file = caffe_root+’data/VOC0712/labelmap_voc.prototxt’

还有一种模型错误也很常见,NameError:name ‘net’ is not defined

在caffe加载正常的情况下出现这种错误,很有可能是deploy.prototxt文件的问题,如果deploy.prototxt最后一层中能查找到save_output_param超参数,删除后能解决问题(后文有详细说明),如果不是,那还要继续排查。

fish-bike.jpg的检测结果如下:

SSD: Single Shot MultiBox Detector 检测单张图片_第4张图片

换一张复杂一点的图片image = caffe.io.load_image(‘examples/images/my-pic.jpg’) ,检测结果如下:

保存运行Python文件

看来效果也是不错的,但是我觉得如果不是去调试代码,每次都打开jupyter notebook运行程序太麻烦,也不方便保存图片之类的操作。这里我的做法是把程序另存为py文件(使用jupyter notebook打开ipynb文件,然后点选另存为),稍作修改,再单独运行这个py文件即可。改动的地方貌似只有一处,就是把get_ipython().magic(u'matplotlib inline')这句话给注释掉,因为脱离了jupyter notebook的环境,用不上了。然后python ssd_detect.py,代码正常,没有报错,但是无法弹出窗口显示最终的带框图片,上网查询一番,原因是是默认的matplotlib的backend(后端)渲染有问题。那么先查看目前的后端情况:

Python 2.7.12 (default, Jul  1 2016, 15:12:24) 
[GCC 5.4.0 20160609] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import matplotlib
>>> matplotlib.get_backend()
u'Agg'

目前是Agg,但为了正确将图像显示在屏幕上,需要设置为TkAgg,解决方法是安装几个库文件:

$ sudo apt-get install tcl-dev tk-dev python-tk python3-tk

重启电脑,再次查看,这里应该是变成了TkAgg,如果没有变化,那还可以sudo pip uninstall matplotlib 先卸载,然后pip install matplotlib 安装回去,重启电脑,这样就可以确保后端设置变成TkAgg。如此设置后再运行py程序,就完全正常了,下图为博主的运行截图。

SSD: Single Shot MultiBox Detector 检测单张图片_第5张图片

其实这种程序也是学习pycaffe的好帮手,推荐博文:Caffe Python 接口学习笔记,如果不懂某个函数,记得善用help,比如:

>>>import caffe
>>>help(caffe.io)

Help on module caffe.io in caffe:
NAME
    caffe.io
FILE
    /home/mx/caffe/python/caffe/io.py
CLASSES
    Transformer    
    class Transformer
     |  Transform input for feeding into a Net.
     |  
     |  Note: this is mostly for illustrative purposes and it is likely better
     |  to define your own input preprocessing routine for your needs.
     |  
     |  Parameters
     |  ----------
     |  net : a Net for which the input should be prepared
     |  
     |  Methods defined here:
     |  

附:
ssd_detect.ipynb 可运行版
ssd_detect.py

—————————————–分割线————————————-

关于ssd_detect.cpp

好像有部分同学比较关心ssd的C++检测图片程序,也就是ssd_detect.cpp的用法。我们知道,C++程序要经过编译,生成可执行程序才能进行使用(把它作为一个类来调用则是另外一回事了),这个cpp理论上也是可以编译的,有评论说Eclipse中实现了编译,前提是你要会配置那些依赖项,我尝试用“g++ -o ssd_detect ssd_detect.cpp”来进行编译,发现一直报错,主要是找不到需要的include项,很自然的想到添加CPLUS_INCLUDE_PATH环境变量来解决,可是后来又遇到google proto等等的问题,凭我这种三流水平只能投降了,暂时宣告手动编译cpp失败。

其实呢,这个cpp最好是通过make命令(Makefile文件指定包含目录和依赖项等)编译。所以,我们在配置caffe阶段make all的时候,已经从ssd_detect.cpp生成了可执行的二进制文件(留意make all命令的终端显示就可知道),也就是ssd的C++接口程序,路径就在caffe/build/examples/ssd/ssd_detect.bin。那好,我们先看一下它的用法。

ps:最新博客已经实现了eclipse的SSD配置,可以移步:Ubuntu 16.04使用Eclipse运行SSD-Caffe的cpp代码

终端输入:

$ cd caffe
$ build/examples/ssd/ssd_detect.bin
#使用方法说明
ssd_detect.bin: Do detection using SSD mode.
Usage:
    ssd_detect [FLAGS] model_file weights_file list_file


  Flags from examples/ssd/ssd_detect.cpp:
    -confidence_threshold (Only store detections with score higher than the
      threshold.) type: double default: 0.01
    -file_type (The file type in the list_file. Currently support image and
      video.) type: string default: "image"
    -mean_file (The mean file used to subtract from the input image.)
      type: string default: ""
    -mean_value (If specified, can be one value or can be same as image
      channels - would subtract from the corresponding channel). Separated by
      ','.Either mean_file or mean_value should be provided, not both.)
      type: string default: "104,117,123"
    -out_file (If provided, store the detection results in the out_file.)
      type: string default: ""

可以看到,该程序需要三个必选参数以及一些可选参数,必选参数首先是model_file,也就是检测模型描述文件;然后是weights_file,网络权值文件;list_file,待检测文件,格式为txt,里面列出检测图片/视频的路径。可选参数包括阈值、文件格式、均值文件、图片均值、输出文件这些,都有解释,一看就懂。

运行一个命令试试:

$ cd caffe
$ build/examples/ssd/ssd_detect.bin models/VGGNet/VOC0712/SSD_300x300/deploy.prototxt models/VGGNet/VOC0712/SSD_300x300/VGG_VOC0712_SSD_300x300_iter_120000.caffemodel examples/images/test.txt 

其中test.txt内容为“examples/images/cat.jpg”,是待检测图片的路径,可以写多行。

呵呵,又报错了:“ImportError: libcaffe.so.1.0.0-rc3: cannot open shared object file: No such file or directory”,还好这个错误在本人知识范围内,也就是静态链接库的问题,so easy。

$ cd /etc/ld.so.conf.d
$ sudo vi caffe.conf 
# conf文件加入 /home/mx/caffe/.build_release/lib,保存退出
sudo ldconfig

也有可能会出现这种错误:terminate called after throwing an instance of ‘boost::filesystem::filesystem_error’ what(): boost::filesystem::create_directories: Permission denied: “/home-2/wliu/data/VOCdevkit/results/VOC2007/SSD_300x300”。原因是作者提供的最新SSD300模型中deploy.prototxt里面有些东西(代码如下)没有删干净,找到最后一层,直接把超参数save_output_param整个删掉(也可以替换为个人路径)。

save_output_param {
      output_directory: "/home-2/wliu/data/VOCdevkit/results/VOC2007/SSD_300x300/Main"
      output_name_prefix: "comp4_det_test_"
      output_format: "VOC"
      label_map_file: "data/VOC0712/labelmap_voc.prototxt"
      name_size_file: "data/VOC0712/test_name_size.txt"
      num_test_image: 4952
    } #deploy.prototxt的错误代码,里面是原作者个人路径,可以直接删除

然后重新运行检测命令,这次有结果了:examples/images/cat.jpg 8 0.999853 163 38 350 358

这个C++程序目前只能得到这样的描述结果,想要可视化的结果要么写一个程序读取里面的文本信息,然后用opencv画框;也可以修改源cpp程序重新编译,这个还没尝试,等有空了研究。

复杂一些的例子,检测视频(得到的结果也是每一帧的文本描述),改变阈值并保存检测结果:

$ build/examples/ssd/ssd_detect.bin models/VGGNet/VOC0712/SSD_300x300/deploy.prototxt models/VGGNet/VOC0712/SSD_300x300/VGG_VOC0712_SSD_300x300_iter_120000.caffemodel examples/videos/test.txt --file_type video --out_file output.txt --confidence_threshold 0.4 #检测视频,阈值为0.4并保存结果

ssd_detect.cpp检测结果的可视化

之前博主说过,只用ssd_detect.bin检测图片,只能得到一些文本描述结果,大家想要的其实是可视化的带框图片。原本还想自己写一个小程序实现,结果是我想多了,作者已经在新一版的SSD中增加了这一功能,具体而言,就是examples/ssd/plot_detections.py文件。下面来说说怎么用。

首先想到这个py文件多半是读取一些参数(examples/images/cat.jpg 8 0.999853 163 38 350 358这种)才能画框,那么打开这个py文件,找一下接口部分:

if __name__ == "__main__":
    parser = argparse.ArgumentParser(
            description = "Plot the detection results output by ssd_detect.")
    parser.add_argument("resultfile",
            help = "A file which contains all the detection results.")
    parser.add_argument("imgdir",
            help = "A directory which contains the images.")
    parser.add_argument("--labelmap-file", default="",
            help = "A file which contains the LabelMap.")
    parser.add_argument("--visualize-threshold", default=0.01, type=float,
            help = "Display detections with score higher than the threshold.")
    parser.add_argument("--save-dir", default="",
            help = "A directory which saves the image with detection results.")
    parser.add_argument("--display-classes", default=None,
            help = "If provided, only display specified class. Separate by ','")

果不其然,包含2个必选参数和4个可选参数。必选参数是检测结果文件txt、原始图片文件夹;可选参数有labelmap.prototxt、筛选阈值、保存路径、特定的类别(比如只显示汽车的检测结果)。

先前已经用ssd_detect.bin得到了自带图片的检测结果,存为result.txt:

examples/images/cat.jpg 8 0.999429 169 26 347 356
examples/images/cropped_panda.jpg 12 0.975958 0 1 95 97
examples/images/fish-bike.jpg 2 0.717551 52 81 448 307
examples/images/fish-bike.jpg 15 0.99994 204 3 344 170

下面开工,用一下这个py文件:

$ python examples/ssd/plot_detections.py examples/images/result.txt examples/images --labelmap-file data/VOC0712/labelmap_voc.prototxt --save-dir examples/

立马报错:

examples/images/examples/images/cat.jpg does not exist
examples/images/examples/images/cropped_panda.jpg does not exist
examples/images/examples/images/fish-bike.jpg does not exist

看来图片路径没对,txt中已经有了图片路径,造成路径重复,这个参数不能这么写,但是不写肯定不行。动下脑筋,变成绝对路径不就行了吗。

$ python examples/ssd/plot_detections.py examples/images/result.txt /home/mx/caffe --labelmap-file data/VOC0712/labelmap_voc.prototxt --save-dir examples/

现在好了,在examples文件夹下找到了三张图片,已经画好框,大功告成。
SSD: Single Shot MultiBox Detector 检测单张图片_第6张图片
SSD: Single Shot MultiBox Detector 检测单张图片_第7张图片
SSD: Single Shot MultiBox Detector 检测单张图片_第8张图片

你可能感兴趣的:(python,ssd,ssd-detect,C++,SSD)