7.SSD目標檢測之一:運行SSD模型

1.參考等。

需要在跟蹤模型的前面把檢測模型加進去,傳統使用一些背景建模和軌跡建模的方式來做,對於動攝像頭以及複雜背景的適應性都比較差,所以考慮用深度學習的方法來做。我以前也只是大概看過這些東西,具體動手只做過分類,並沒有搞過檢測,所以找到一篇SSD訓練自己數據的參考,自己也來實現一下。 參考:SSD目標檢測 SSD的原理介紹可以參見:SSD原理介紹

2.環境準備。

剛重裝了系統,所以都得重來一下,python+tensorflow這個直接下載anaconda來裝就可以了,opencv去網上下載whl文件,然後安裝(anaconda其實也有opencv,但是不知道爲什麼下載不下來就放棄了),裝好之後提示numpy版本不匹配,所以升級了numpy的版本,又提示tensorflow不支持,反正就是版本的問題,然後只能折中選擇了一個numpy的版本。環境配置有時候挺麻煩的,多看論壇就行了。

3.下載SSD框架源碼。

選擇的是tensorflow版本的:SSD_tensorflow checkpoints文件夾下的壓縮包解壓,pycharm新建項目後應該張這個樣子:

ckpt這種文件就是訓練好的模型參數。 demo文件夾下是用來測試的文件夾。

4.測試。

# -*- coding:utf-8 -*-
# -*- author:zzZ_CMing  CSDN address:https://blog.csdn.net/zzZ_CMing
# -*- 2018/07/14; 15:19
# -*- python3.5

import os
import cv2
import math
import random
import tensorflow as tf
import matplotlib.pyplot as plt
import matplotlib.cm as mpcm
import matplotlib.image as mpimg
from notebooks import visualization
from nets import ssd_vgg_300, ssd_common, np_methods
from preprocessing import ssd_vgg_preprocessing
import sys

sys.path.append('./SSD-Tensorflow/')

slim = tf.contrib.slim

# TensorFlow session
gpu_options = tf.GPUOptions(allow_growth=False)
config = tf.ConfigProto(log_device_placement=False, gpu_options=gpu_options)
isess = tf.InteractiveSession(config=config)
l_VOC_CLASS = ['aeroplane', 'bicycle', 'bird',
               'boat', 'bottle', 'bus', 'car',
               'cat', 'chair', 'cow', 'diningTable',
               'dog', 'horse', 'motorbike', 'person',
               'pottedPlant', 'sheep', 'sofa', 'train', 'TV']


# 定義數據格式,設置佔位符
net_shape = (300, 300)
# 預處理,以Tensorflow backend, 將輸入圖片大小改成 300x300,作爲下一步輸入
img_input = tf.placeholder(tf.uint8, shape=(None, None, 3))
# 輸入圖像的通道排列形式,'NHWC'表示 [batch_size,height,width,channel]
data_format = 'NHWC'

# 數據預處理,將img_input輸入的圖像resize爲300大小,labels_pre,bboxes_pre,bbox_img待解析
image_pre, labels_pre, bboxes_pre, bbox_img = ssd_vgg_preprocessing.preprocess_for_eval(
    img_input, None, None, net_shape, data_format, resize=ssd_vgg_preprocessing.Resize.WARP_RESIZE)

# 拓展爲4維變量用於輸入
image_4d = tf.expand_dims(image_pre, 0)

# 定義SSD模型
# 是否複用,目前我們沒有在訓練所以爲None
reuse = True if 'ssd_net' in locals() else None

# 調出基於VGG神經網絡的SSD模型對象,注意這是一個自定義類對象
ssd_net = ssd_vgg_300.SSDNet()
# 得到預測類和預測座標的Tensor對象,這兩個就是神經網絡模型的計算流程
with slim.arg_scope(ssd_net.arg_scope(data_format=data_format)):
    predictions, localisations, _, _ = ssd_net.net(image_4d, is_training=False, reuse=reuse)

# 導入官方給出的 SSD 模型參數
ckpt_filename = '../checkpoints/ssd_300_vgg.ckpt'
# ckpt_filename = '../checkpoints/VGG_VOC0712_SSD_300x300_ft_iter_120000.ckpt'
isess.run(tf.global_variables_initializer())
saver = tf.train.Saver()
saver.restore(isess, ckpt_filename)

# 在網絡模型結構中,提取搜索網格的位置
# 根據模型超參數,得到每個特徵層(這裏用了6個特徵層,分別是4,7,8,9,10,11)的anchors_boxes

ssd_anchors = ssd_net.anchors(net_shape)

"""
每層的anchors_boxes包含4個arrayList,前兩個List分別是該特徵層下x,y座標軸對於原圖(300x300)大小的映射
第三,四個List爲anchor_box的長度和寬度,同樣是經過歸一化映射的,根據每個特徵層box數量的不同,這兩個List元素
個數會變化。其中,長寬的值根據超參數anchor_sizes和anchor_ratios制定。
"""
# 加載輔助作圖函數

def colors_subselect(colors, num_classes=21):
    dt = len(colors) // num_classes
    sub_colors = []
    for i in range(num_classes):
        color = colors[i * dt]
        if isinstance(color[0], float):
            sub_colors.append([int(c * 255) for c in color])
        else:
            sub_colors.append([c for c in color])
    return sub_colors


def bboxes_draw_on_img(img, classes, scores, bboxes, colors, thickness=2):
    shape = img.shape
    for i in range(bboxes.shape[0]):
        bbox = bboxes[i]
        color = colors[classes[i]]
        # Draw bounding box...
        p1 = (int(bbox[0] * shape[0]), int(bbox[1] * shape[1]))
        p2 = (int(bbox[2] * shape[0]), int(bbox[3] * shape[1]))
        cv2.rectangle(img, p1[::-1], p2[::-1], color, thickness)
        # Draw text...
        s = '%s/%.3f' % (l_VOC_CLASS[int(classes[i]) - 1], scores[i])
        p1 = (p1[0] - 5, p1[1])
        #cv2.putText(img, s, p1[::-1], cv2.FONT_HERSHEY_DUPLEX, 1.5, color, 3)

colors_plasma = colors_subselect(mpcm.plasma.colors, num_classes=21)



# 主流程函數
def process_image(img, case, select_threshold=0.15, nms_threshold=.1, net_shape=(300, 300)):
    # select_threshold:box閾值——每個像素的box分類預測數據的得分會與box閾值比較,高於一個box閾值則認爲這個box成功框到了一個對象
    # nms_threshold:重合度閾值——同一對象的兩個框的重合度高於該閾值,則運行下面去重函數


    # 執行SSD模型,得到4維輸入變量,分類預測,座標預測,rbbox_img參數爲最大檢測範圍,本文固定爲[0,0,1,1]即全圖
    rimg, rpredictions, rlocalisations, rbbox_img = isess.run([image_4d, predictions,
                                                               localisations, bbox_img],
                                                              feed_dict={img_input: img})
    # ssd_bboxes_select()函數根據每個特徵層的分類預測分數,歸一化後的映射座標,
    # ancohor_box的大小,通過設定一個閾值計算得到每個特徵層檢測到的對象以及其分類和座標
    rclasses, rscores, rbboxes = np_methods.ssd_bboxes_select(rpredictions, rlocalisations, ssd_anchors,
                                                              select_threshold=select_threshold, img_shape=net_shape,
                                                              num_classes=21, decode=True)
    """
        這個函數做的事情比較多,這裏說的細緻一些:
        首先是輸入,輸入的數據爲每個特徵層(一共6個,見上文)的:
                                                    rpredictions: 分類預測數據,
                                                    rlocalisations: 座標預測數據,
                                                    ssd_anchors: anchors_box數據
                                                其中:
                                                   分類預測數據爲當前特徵層中每個像素的每個box的分類預測
                                                   座標預測數據爲當前特徵層中每個像素的每個box的座標預測
                                                   anchors_box數據爲當前特徵層中每個像素的每個box的修正數據

            函數根據座標預測數據和anchors_box數據,計算得到每個像素的每個box的中心和長寬,這個中心座標和長寬會根據一個算法進行些許的修正,
        從而得到一個更加準確的box座標;修正的算法會在後文中詳細解釋,如果只是爲了理解算法流程也可以不必深究這個,因爲這個修正算法屬於經驗算
        法,並沒有太多邏輯可循。
            修正完box和中心後,函數會計算每個像素的每個box的分類預測數據的得分,當這個分數高於一個閾值(這裏是0.5)則認爲這個box成功
        框到了一個對象,然後將這個box的座標數據,所屬分類和分類得分導出,從而得到:
            rclasses:所屬分類
            rscores:分類得分
            rbboxes:座標

            最後要注意的是,同一個目標可能會在不同的特徵層都被檢測到,並且他們的box座標會有些許不同,這裏並沒有去掉重複的目標,而是在下文
        中專門用了一個函數來去重
        """
    # 檢測有沒有超出檢測邊緣
    rbboxes = np_methods.bboxes_clip(rbbox_img, rbboxes)
    rclasses, rscores, rbboxes = np_methods.bboxes_sort(rclasses, rscores, rbboxes, top_k=400)
    # 去重,將重複檢測到的目標去掉
    rclasses, rscores, rbboxes = np_methods.bboxes_nms(rclasses, rscores, rbboxes, nms_threshold=nms_threshold)
    # 將box的座標重新映射到原圖上(上文所有的座標都進行了歸一化,所以要逆操作一次)
    rbboxes = np_methods.bboxes_resize(rbbox_img, rbboxes)
    if case == 1:
        bboxes_draw_on_img(img, rclasses, rscores, rbboxes, colors_plasma, thickness=8)
        return img
    else:
        return rclasses, rscores, rbboxes

# 做目標定位,同時做預測分析
case = 2
path = '../demo/desk.jpg'
#  讀取圖片
img = mpimg.imread(path)
# 執行主流程函數
rclasses, rscores, rbboxes = process_image(img, case)
#isualization.bboxes_draw_on_img(img, rclasses, rscores, rbboxes, visualization.colors_plasma)
# 顯示分類結果圖
visualization.plt_bboxes(img, rclasses, rscores, rbboxes,figsize=(10,10),linewidth=2)

代碼來源看註釋,反正不是我寫的,需要根據自己需要改的主要有: ①gpu_options = tf.GPUOptions(allow_growth=False)根據自己是否有GPU來設置,我一開始在筆記本上跑的,所以false。 ②path = '../demo/desk.jpg'這裏改成自己測試的圖片。 ③def process_image(img, case, select_threshold=0.5, nms_threshold=.1, net_shape=(300, 300)):這句默認的閾值可以選,意思就是隻顯示置信概率大於0.5的對象。 然後跑起來就可以了,可能加載模型比較慢吧,所以第一次時間還挺長的,自己隨便找了一張照片,結果如下:

看起來還不錯的樣子,標籤對應的類別名稱代碼裏有。

另外,圖片可以的話,視頻測試也是可以的,把視頻讀進來轉換爲圖片寫循環就可以了。 接下來需要採集數據,加標籤以及訓練模型了,估計會要花一段時間。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章