自動駕駛-車輛檢測(YOLO算法)

學習目標:

​ 1.在汽車檢測數據集上應用目標檢測

​ 2.處理邊界框

運行以下單元下載有有助於實現車輛檢測的包和依賴項。

import argparse
import os
import matplotlib.pyplot as plt
from matplotlib.pyplot import imshow
import scipy.io
import scipy.misc
import numpy as np
import pandas as pd
import PIL
import tensorflow as tf
from keras import backend as K
from keras.layers import Input, Lambda, Conv2D
from keras.models import load_model, Model
from yolo_utils import read_classes, read_anchors, generate_colors, preprocess_image, draw_boxes, scale_boxes
from yad2k.models.keras_yolo import yolo_head, yolo_boxes_to_corners, preprocess_true_boxes, yolo_loss, yolo_body

%matplotlib inline

1-問題描述

汽車檢測系統爲了收集數據,在開車時利用車前方的攝像頭每隔幾秒鐘就對行駛的路況拍照,所有圖片收集到文件夾中,並對找到的每輛車並畫出邊框實現檢測。下圖是車輛邊框檢測的例子:

這裏寫圖片描述

如果想讓YOLO識別80種分類,可以將類標籤‘c’表示爲一個從1~80的整數,或者’c’表示爲一個80維的向量,其中一個分量是’1’,其餘分量是‘0’。

2-YOLO

YOLO (“you only look once”) 是非常流行的算法,在實現高準確率的同時可以做到實時檢測。該算法“只看一次”圖片,即它只需要一次前向傳播就可以實現檢測。在非最大抑制以後,算法輸出帶有邊框的車輛識別圖像。

2.1-Model details

第一點:

​ 1.輸入是一組圖像的形狀(m, 608, 608, 3)

​ 2.輸出是一個列表包括邊框信息和被識別的類。每一個邊框信息通過6個 數字表示(p_c, b_x, b_y, b_h, b_w, c)如上述描述。如果你將’c’表達爲80維的向量,每個邊框信息將包括85個數字。

這裏採用5組邊框信息。所以YOLO算法架構如下:

IMAGE (m, 608, 608, 3) –> DEEP CNN –> ENCODING (m, 19, 19, 5, 85).

編碼內容詳細表述如下:

這裏寫圖片描述

Figure 2 : Encoding architecture for YOLO

如果一個目標的中心/中點落入網格單元,那麼此網格單元就負責監測該對象。

這裏採用5組邊框信息,因此,19*19網格單元中的每一個都編碼了5組邊框信息。邊框信息僅通過寬度和高度定義。

爲了簡化,這裏將形狀(19, 19, 5, 85)的最後兩個維度壓平,得到深度卷機網絡輸出爲(19, 19, 425)。

這裏寫圖片描述

​ Figure 3 : Flattening the last two last dimensions

現在,對於每個單元格,這裏將計算下面結果,並提取該單元格包含某個類的概率。

這裏寫圖片描述

Figure 4 : Find the class detected by each box

這裏有一種方法,可將YOLO對圖片的預測可視化:

​ 1.對於每個19*19單元格,找出概率得分最大值(在5組邊框信息和不同類中取一個最大值)。

​ 2.根據網格單元最可能的對象,對網格進行着色。

效果如下:

這裏寫圖片描述
Figure 5 : Each of the 19x19 grid cells colored according to which class has the largest predicted probability in that cell.

注意:這裏的可視化並不是YOLO算法進行預測的核心,這僅僅是可視化中間結果的方式。

另一種可視化YOLO輸出的方法是繪製它的輸出邊框,可視化結果如下:

這裏寫圖片描述

Figure 6 : Each cell gives you 5 boxes. In total, the model predicts: 19x19x5 = 1805 boxes just by looking once at the image (one forward pass through the network)! Different colors denote different classes.

在上圖我們繪製了模型所分配的高分辨率的邊框,但是邊框依然太多。我們希望算法過濾輸出更少的監測對象,爲此採用非最大抑制。具體步驟如下:

​ 1.刪除概率較低的邊框(即,該邊框不確定某一類的檢測)。

​ 2.當多個邊框相互重疊並檢測相同的對象時,只選擇一個邊框。

2.2-Filtering with a threshold on class scores

在第一個過濾器中應用“閾值”,這將會刪除那些分類概率小於所選閾值的邊框。

該模型總共提供了“19x19x5x85”個數字,每個框都有85個數字。將(19、19、5、85)(或(19,19,425))維度張量重新排列爲以下變量:

​ 1.box_confidence:維度(19x19, 5, 1)的張量包含p_c(表示某個對象的置信概率),對於每一個在19x19單元格中預測的5個邊框的每一個。

​ 2.boxes:維度(19x19, 5, 4)的張量包含(b_x, b_y, b_h, b_w),對於每個單元格中的5個邊框信息的每一個。

​ 3.box_class_probs:維度(19x19, 5, 80)的張量包含檢測概率(c_1, c_2,…,c_80),對於每個單元格的5個框中的每一個80個類。

練習:實現yolo_filter_boxes().

​ 1.計算單元格包含某個類的概率如4.1所示,代碼如下:

a = np.random.randn(19*19, 5, 1)
b = np.random.randn(19*19, 5, 80)
c = a * b # shape of c will be (19*19, 5, 80)

​ 2.在每個單元格中,

​ a.找到這個最大概率類的索引值(注意:axis=-1)

​ b.找到對應的單元格概率值(注意:axis=-1)

​ 3.使用閾值進行篩選。如:([0.9, 0.3, 0.4, 0.5, 0.1] < 0.4) returns: [False, True, False, False, True].篩選後你想保留的單元格爲”True“.

​ 4.使用Tensorflow實現篩選box_class_scores,boxes 和box_classes ,刪除你不想要的單元格。你會得到你要保留的單元格的子集。

代碼如下:

def yolo_filter_boxes(box_confidence, boxes, box_class_probs, threshold = .6):
    """Filters YOLO boxes by thresholding on object and class confidence.

    Arguments:
    box_confidence -- tensor of shape (19, 19, 5, 1)
    boxes -- tensor of shape (19, 19, 5, 4)
    box_class_probs -- tensor of shape (19, 19, 5, 80)
    threshold -- real value, if [ highest class probability score < threshold], then get rid of the corresponding box

    Returns:
    scores -- tensor of shape (None,), containing the class probability score for selected boxes
    boxes -- tensor of shape (None, 4), containing (b_x, b_y, b_h, b_w) coordinates of selected boxes
    classes -- tensor of shape (None,), containing the index of the class detected by the selected boxes

    Note: "None" is here because you don't know the exact number of selected boxes, as it depends on the threshold. 
    For example, the actual output size of scores would be (10,) if there are 10 boxes.
    """

    # Step 1: Compute box scores
    ### START CODE HERE ### (≈ 1 line)
    box_scores = box_confidence * box_class_probs   # 19x19x5x80 (80 scores)
    ### END CODE HERE ###

    # Step 2: Find the box_classes thanks to the max box_scores, keep track of the corresponding score
    ### START CODE HERE ### (≈ 2 lines)
    box_classes = K.argmax(box_scores ,axis=-1)      # 19x19x5x1 (1 class idx)
    box_class_scores = K.max(box_scores ,axis=-1)    # 19x19x5x1 (1 class score)
    ### END CODE HERE ###

    # Step 3: Create a filtering mask based on "box_class_scores" by using "threshold". The mask should have the
    # same dimension as box_class_scores, and be True for the boxes you want to keep (with probability >= threshold)
    ### START CODE HERE ### (≈ 1 line)
    filtering_mask = box_class_scores >= threshold   # 19x19x5x1 (1 boolean)
    ### END CODE HERE ###

    # Step 4: Apply the mask to scores, boxes and classes
    ### START CODE HERE ### (≈ 3 lines)
    scores = tf.boolean_mask(box_class_scores, filtering_mask)
    boxes = tf.boolean_mask(boxes, filtering_mask)
    classes = tf.boolean_mask(box_classes, filtering_mask)
    ### END CODE HERE ###

    return scores, boxes, classes

2.3-Non-max suppression

即使在對分類分數過濾後,仍然會有很多重疊的框。選擇正確框的第二個過濾器稱爲非最大抑制(NMS)。
這裏寫圖片描述
Figure 7 : In this example, the model has predicted 3 cars, but it’s actually 3 predictions of the same car. Running non-max suppression (NMS) will select only the most accurate (highest probabiliy) one of the 3 boxes.
非最大抑制使用了非常重要的函數,稱爲“Intersection over Union”or IoU.

這裏寫圖片描述

Figure 8 : Definition of “Intersection over Union”.

練習:實現 IoU(). 提示:

1.在這個練習中,我們用兩個角(左上和右下)定義一個邊框:(x_1, y_1, x_2, y_2), 而不是中點和高度/寬度。
2.計算矩形的面積,需要高(y_2 - y_1)*寬(x_2 - x_1)。
3.還需要找到兩個框的交集的座標(xi_1, yi_1, xi_2, yi_2)。
注意
​ xi_1 =兩個框的x1座標的最大值。
​ yi_1 =兩個框的y1座標的最大值。
​ xi_2 =兩個框的x2座標的最大值。
​ yi_2 =兩個框的y2座標的最大值。

這裏,我們使用的座標是(0,0)在圖片的左上角,(1,0)在圖片的右上角,(1,1)在右下角。

# GRADED FUNCTION: iou
def iou(box1, box2):
    """Implement the intersection over union (IoU) between box1 and box2

    Arguments:
    box1 -- first box, list object with coordinates (x1, y1, x2, y2)
    box2 -- second box, list object with coordinates (x1, y1, x2, y2)
    """

    # Calculate the (y1, x1, y2, x2) coordinates of the intersection of box1 and box2. Calculate its Area.
    ### START CODE HERE ### (≈ 5 lines)
    xi1 = np.max([box1[0], box2[0]])
    yi1 = np.max([box1[1], box2[1]])
    xi2 = np.min([box1[2], box2[2]])
    yi2 = np.min([box1[3], box2[3]])
    inter_area = max((yi2 - yi1), 0) * max((xi2 - xi1), 0)
    ### END CODE HERE ###    

    # Calculate the Union area by using Formula: Union(A,B) = A + B - Inter(A,B)
    ### START CODE HERE ### (≈ 3 lines)
    box1_area = (box1[3]-box1[1]) *(box1[2]- box1[0]) 
    box2_area = (box2[3]-box2[1]) *(box2[2]- box2[0])
    union_area = box1_area + box2_area - inter_area
    ### END CODE HERE ###

    # compute the IoU
    ### START CODE HERE ### (≈ 1 line)
    iou = inter_area / union_area
    ### END CODE HERE ###

    return iou

運行驗證

box1 = (2, 1, 4, 3)
box2 = (1, 2, 3, 4) 
print("iou = " + str(iou(box1, box2)))

輸出結果:
IoU =0.14285714285714285

現在準備實現非最大抑制,關鍵步驟如下:

1.選擇分數(置信率)最高的邊框

2.計算它與其他框的重疊(計算IOU),如果其值大於一定閾值(重合度過高),那麼就將該框剔除;

3.返回第一步,重複上述過程,直到沒有其他邊框比當前邊框得分更低。

這將刪除與所選框有很大重疊的框,只留下“最好(分數最高)”的框。

練習:使用TensorFlow實現yolo_non_max_suppression()。TensorFlow有兩個內置函數用於實現非最大抑制(所以實際上不需要使用iou()實現):

# GRADED FUNCTION: yolo_non_max_suppression
def yolo_non_max_suppression(scores, boxes, classes, max_boxes = 10, iou_threshold = 0.5):
    """
    Applies Non-max suppression (NMS) to set of boxes

    Arguments:
    scores -- tensor of shape (None,), output of yolo_filter_boxes()
    boxes -- tensor of shape (None, 4), output of yolo_filter_boxes() that have been scaled to the image size (see later)
    classes -- tensor of shape (None,), output of yolo_filter_boxes()
    max_boxes -- integer, maximum number of predicted boxes you'd like
    iou_threshold -- real value, "intersection over union" threshold used for NMS filtering

    Returns:
    scores -- tensor of shape (, None), predicted score for each box
    boxes -- tensor of shape (4, None), predicted box coordinates
    classes -- tensor of shape (, None), predicted class for each box

    Note: The "None" dimension of the output tensors has obviously to be less than max_boxes. Note also that this
    function will transpose the shapes of scores, boxes, classes. This is made for convenience.
    """

    max_boxes_tensor = K.variable(max_boxes, dtype='int32')     # tensor to be used in tf.image.non_max_suppression()
    K.get_session().run(tf.variables_initializer([max_boxes_tensor])) # initialize variable max_boxes_tensor

    # Use tf.image.non_max_suppression() to get the list of indices corresponding to boxes you keep
    ### START CODE HERE ### (≈ 1 line)
    #去除首先從所有的檢測框中找到置信度較大的那個框,然後挨個計算其與剩餘框的IOU,
    #如果其值大於一定閾值(重合度過高),那麼就將該框剔除;
    #然後對剩餘的檢測框重複上述過程,直到處理完所有的檢測框。
    nms_indices = tf.image.non_max_suppression(boxes, scores, max_boxes_tensor, iou_threshold)
    ### END CODE HERE ###

    # Use K.gather() to select only nms_indices from scores, boxes and classes
    ### START CODE HERE ### (≈ 3 lines)
    scores = K.gather(scores, nms_indices)
    boxes = K.gather(boxes, nms_indices)
    classes = K.gather(classes, nms_indices)
    ### END CODE HERE ###

    return scores, boxes, classes

2.4-Wrapping up the filtering

下面實現一個函數,該函數包含深度CNN(19x19x5x85維編碼)的輸出,並使用上述實現的函數過濾所有的框。
練習:實現yolo_eval( ) ,該函數實現YOLO編碼的輸出,並使用置信率閾值和非最大抑制濾除其他邊框。還要注意最後一個細節,邊框有幾種表示方法,比如通過角或者通過中點和高度/寬度。不同時間YOLO會在幾種格式之間轉換,以下函數可以實現格式轉換功能:

boxes = yolo_boxes_to_corners(box_xy, box_wh)

該代碼實現YOLO邊框座標(x, y, w, h)轉換爲邊框角座標(x1, y1, x2, y2),以適應函數yolo_filter_boxes的輸入。

boxes = scale_boxes(boxes, image_shape)

YOLO網絡訓練後運行在608x608的圖片上。如果你在不同大小的圖片上測試數據——比如車輛檢測集圖片維度是720x1280——該步驟會重新調整邊框,以便將邊框繪製在720x1280大小圖像上。

def yolo_eval(yolo_outputs, image_shape = (720., 1280.), max_boxes=10, score_threshold=.6, iou_threshold=.5):
    """
    Converts the output of YOLO encoding (a lot of boxes) to your predicted boxes along with their scores, box coordinates and classes.

    Arguments:
    yolo_outputs -- output of the encoding model (for image_shape of (608, 608, 3)), contains 4 tensors:
                    box_confidence: tensor of shape (None, 19, 19, 5, 1)
                    box_xy: tensor of shape (None, 19, 19, 5, 2)
                    box_wh: tensor of shape (None, 19, 19, 5, 2)
                    box_class_probs: tensor of shape (None, 19, 19, 5, 80)
    image_shape -- tensor of shape (2,) containing the input shape, in this notebook we use (608., 608.) (has to be float32 dtype)
    max_boxes -- integer, maximum number of predicted boxes you'd like
    score_threshold -- real value, if [ highest class probability score < threshold], then get rid of the corresponding box
    iou_threshold -- real value, "intersection over union" threshold used for NMS filtering

    Returns:
    scores -- tensor of shape (None, ), predicted score for each box
    boxes -- tensor of shape (None, 4), predicted box coordinates
    classes -- tensor of shape (None,), predicted class for each box
    """

    ### START CODE HERE ### 

    # Retrieve outputs of the YOLO model (≈1 line)
    box_confidence, box_xy, box_wh, box_class_probs = yolo_outputs

    # Convert boxes to be ready for filtering functions 
    boxes = yolo_boxes_to_corners(box_xy, box_wh)

    # Use one of the functions you've implemented to perform Score-filtering with a threshold of score_threshold (≈1 line)
    scores, boxes, classes = yolo_filter_boxes(box_confidence, boxes, box_class_probs)

    # Scale boxes back to original image shape.
    boxes = scale_boxes(boxes, image_shape)

    # Use one of the functions you've implemented to perform Non-max suppression with a threshold of iou_threshold (≈1 line)
    scores, boxes, classes = yolo_non_max_suppression(scores, boxes, classes)

    ### END CODE HERE ###

    return scores, boxes, classes

YOLO總結:

1.輸入圖片(608,608,3)

2.輸入圖片經過CNN處理,結果輸出(19,19,5,85)維;

3.將最後兩個維度壓平後,輸出的體積維度是(19,19,425):

​ a.輸入圖像的19x19網格中,每個單元格都有425個數字。

​ b.425=5x85,因爲每個單元格包含5個框的預測,對應5個錨點框,如上文所示。

​ c.85=5+80,其中5是因爲(p_c, b_x, b_y, b_h, b_w)有5個數字,80是將要預測的分類數。

4.然後根據以下內容選擇幾個框:

​ a.置信率閾值:丟棄那些置信率低於檢測類的閾值的邊框。

​ b.非最大抑制:計算IOU,避免選擇重疊的邊框。

5.給出YOLO最終的輸出。

3-Test YOLO pretrained model on images

該部分,將使用一個預訓練過的模型,並在車輛檢測數據集上進行測試。一般,首先創建一個會話來啓動圖表,運行下面代碼:

sess = K.get_session()
3.1-Defining classes, anchors and image shape

再次強調,我們將檢測80個分類,並使用5組邊框。我們在兩個文件夾“coco_classes.txt” 和 “yolo_anchors.txt”中得到關於80個類和5個邊框的信息。通過運行下面代碼,將這些參量加載到模型中。

汽車檢測數據集有720x1280張圖像,我們將圖像預處理成608x608的圖像。

class_names = read_classes("model_data/coco_classes.txt")
anchors = read_anchors("model_data/yolo_anchors.txt")
image_shape = (720., 1280.)  
3.2-Loading a pretrained model

訓練一個模型需要很長時間,並需要一個相當大的標籤邊框數據集,用於範圍的目標類。這裏將加載存儲在“yolo.h5”中的現有預訓練的Keras YOLO模型。運行下面代碼,從這個文件加載模型。

yolo_model = load_model("model_data/yolo.h5")

這就加載了訓練好的YOLO模型的權重參數。

注意:該模型將預處理的一批輸入圖像(形狀:(m,608,608,3)轉換爲形狀張量(m,19,19,5,85)),如圖2所示。

3.3-Convert output of the model to usable bounding box tensors

yolo_model的輸出是一個(m、19、19、5、85)張量,需要通過非線性的處理和轉換。通過以下代碼實現。

yolo_outputs = yolo_head(yolo_model.output, anchors, len(class_names))

將yolo_output添加到圖中,這4個張量的集合將作爲yolo_eval函數的輸入。

3.4-Filtering boxes

yolo_outputs以正確的格式提供了yolo_model的所有預測框。 現在準備好執行過濾並僅選擇最佳框。 下面調用之前實現的yolo_eval來做到這一點。

scores, boxes, classes = yolo_eval(yolo_outputs, image_shape)
3.5-Run the graph on an image

下面開始,你已經創建了一個(sess)圖,可以總結如下:

​ 1.yolo_model.input被賦予yolo_model,該模型用於計算輸出yolo_model.output;

​ 2.yolo_model.output由yolo_head進行處理, 輸出yolo_outputs;

​ 3.yolo_output通過一個過濾函數yolo_eval進行處理,輸出預測值:置信率(scores),邊框(boxes),分類(classes).

練習:實現expect(),運行圖形在圖像上測試YOLO,這裏需要運行TensorFlow會話,來計算置信率(scores),邊框(boxes),分類(classes).

下面的代碼還是使用了以下函數:

image, image_data = preprocess_image("images/" + image_file, model_image_size = (608, 608))

輸出:
1.image:用於繪製邊框的圖像的python表示。不需要使用。

​ 2.image_data: 表示圖像的numpy-array,這將是CNN的輸入。

注意:當一個模型使用BatchNorm(就像YOLO一樣時),你需要在feed_dict{K.learning_phase(): 0}中傳遞一個額外的佔位符。

def predict(sess, image_file):
    """
    Runs the graph stored in "sess" to predict boxes for "image_file". Prints and plots the preditions.

    Arguments:
    sess -- your tensorflow/Keras session containing the YOLO graph
    image_file -- name of an image stored in the "images" folder.

    Returns:
    out_scores -- tensor of shape (None, ), scores of the predicted boxes
    out_boxes -- tensor of shape (None, 4), coordinates of the predicted boxes
    out_classes -- tensor of shape (None, ), class index of the predicted boxes

    Note: "None" actually represents the number of predicted boxes, it varies between 0 and max_boxes. 
    """

    # Preprocess your image
    image, image_data = preprocess_image("images/" + image_file, model_image_size = (608, 608))

    # Run the session with the correct tensors and choose the correct placeholders in the feed_dict.
    # You'll need to use feed_dict={yolo_model.input: ... , K.learning_phase(): 0})
    ### START CODE HERE ### (≈ 1 line)
    out_scores, out_boxes, out_classes = sess.run([scores, boxes, classes], feed_dict={yolo_model.input: image_data, K.learning_phase(): 0})
    ### END CODE HERE ###

    # Print predictions info
    print('Found {} boxes for {}'.format(len(out_boxes), image_file))
    # Generate colors for drawing bounding boxes.
    colors = generate_colors(class_names)
    # Draw bounding boxes on the image file
    draw_boxes(image, out_scores, out_boxes, out_classes, class_names, colors)
    # Save the predicted bounding box on the image
    image.save(os.path.join("out", image_file), quality=90)
    # Display the results in the notebook
    output_image = scipy.misc.imread(os.path.join("out", image_file))
    imshow(output_image)

    return out_scores, out_boxes, out_classes

在”test.jpg”圖片上運行以下代碼,驗證你函數是否正確。

out_scores, out_boxes, out_classes = predict(sess, "test.jpg")
#輸出
Found 7 boxes for test.jpg
car 0.60 (925, 285) (1045, 374)
car 0.66 (706, 279) (786, 350)
bus 0.67 (5, 266) (220, 407)
car 0.70 (947, 324) (1280, 705)
car 0.74 (159, 303) (346, 440)
car 0.80 (761, 282) (942, 412)
car 0.89 (367, 300) (745, 648)

這裏寫圖片描述

剛剛運行的模型實際能夠檢測出“coco_classes.txt”中列出的80個不同的類。

要點

​ 1.YOLO是最先進的物體檢測模型,它快速準確。

​ 2.它通過CNN運行一個輸入圖像,輸出19x19x5x85 維體積。

​ 3.編碼可以看作是一個網格,其中19x19單元格中的每個單元格包含有關5個框的信息。

​ 4.使用非最大抑制過濾所有邊框,具體如下:

​ a.對檢測類的概率進行閾值處理,以僅保留準確率高的框;

​ b.聯合交集(IoU)閾值消除重疊框。

​ 5.從隨機初始化的權重訓練一個YOLO模型是非常重要的,需要大量的數據集和大量的計算資源,所以我們在這個練習中使用了預訓練的模型參數。你也可以嘗試用自己的數據集對YOLO模型進行微調。

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