博主也算是刚开始研究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下载最新的文件,包括代码和模型。
这里用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键查看。
该工具使用的模型是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,打开后发现是这样:
完整的程序我就不放出来了,你打开都和我一样,然后检查下面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运行了,一般会正常运行,也可能会出错,比如我就遇到如下错误:
我的办法是,把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的检测结果如下:
换一张复杂一点的图片image = caffe.io.load_image(‘examples/images/my-pic.jpg’) ,检测结果如下:
看来效果也是不错的,但是我觉得如果不是去调试代码,每次都打开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程序,就完全正常了,下图为博主的运行截图。
其实这种程序也是学习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的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.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/