1. 程式人生 > >SSD: Single Shot MultiBox Detector 檢測單張圖片

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模型

該工具使用的模型是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’) ,檢測結果如下:

這裡寫圖片描述

儲存執行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程式,就完全正常了,下圖為博主的執行截圖。

這裡寫圖片描述

其實這種程式也是學習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.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。那好,我們先看一下它的用法。

終端輸入:

$ 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資料夾下找到了三張圖片,已經畫好框,大功告成。
這裡寫圖片描述
這裡寫圖片描述
這裡寫圖片描述