深度学习笔记(四)---YOLO目标检测

以下笔记来源:

[1] . Andrew Ng的卷积神经网络week3

[2] .【吴恩达课后编程作业】Course 4 - 卷积神经网络 - 第三周作业 ( https://blog.csdn.net/u013733326/article/details/80341740#commentsedit)                         

[3].yolo.h5文件问题的解决 - 吴恩达深度学习:目标检测之YOLO算法

 ( https://blog.csdn.net/Solo95/article/details/85262828)

[4].卷积神经网络第三周:目标检测(https://www.cnblogs.com/-1024/p/8908771.html)

如有侵权,请联系删除!

在学习吴恩达老师第四课第三周的目标检测过程中,除了熟悉代码,各部分的调试也比前几节课要麻烦一些。下面就调试过程中出现的问题,以及各部分的实现进行。

目录

1.所需环境:

2.目标检测原理

2.1 YOLO算法思想

2.2 YOLO算法的输出

2.3  交并比(IOU)

2.4  非最大抑制

 2.5 Anchor Box

2.6 YOLO算法例子

3.车辆识别算法实现

3.1 导入所需的包

3.2 模型细节

3.3 分类阈值过滤

3.4 非最大抑制

3.5 对所有框进行过滤

4.对YOLO的总结:

5.测试已经训练好了的YOLO模型

5.1  定义分类、锚框与图像维度

5.2 - 加载已经训练好了的模型

5.3 - 将模型的输出转换为边界框

5.4 - 过滤锚框

6.关于一些细节:


1.所需环境:

注:win7/python 3.7/tensoflow-CPU/keras(调试过程遇到一个问题就是python3.7下载的TensorFlow-CPU与TensorFlow-GPU版本不兼容)

# Name                    Version                   Build  Channel
absl-py                   0.7.1                     <pip>
argparse                  1.4.0                     <pip>
astor                     0.7.1                     <pip>
astroid                   2.2.5                     <pip>
backcall                  0.1.0                     <pip>
blas                      1.0                         mkl
ca-certificates           2019.1.23                     0
certifi                   2019.3.9                 py37_0
cffi                      1.12.2                    <pip>
colorama                  0.4.1                     <pip>
configparser              3.7.3                     <pip>
cycler                    0.10.0                    <pip>
decorator                 4.4.0                     <pip>
gast                      0.2.2                     <pip>
graphviz                  0.10.1                    <pip>
grpcio                    1.19.0                    <pip>
h5py                      2.9.0                     <pip>
hdf5                      1.8.20               hac2f561_1
icc_rt                    2019.0.0             h0cc432a_1
intel-openmp              2019.1                      144
ipython                   7.3.0                     <pip>
ipython-genutils          0.2.0                     <pip>
isort                     4.3.15                    <pip>
jedi                      0.13.3                    <pip>
jpeg                      9b                   hb83a4c4_2
Keras                     2.2.4                     <pip>
Keras-Applications        1.0.7                     <pip>
Keras-Preprocessing       1.0.9                     <pip>
kiwisolver                1.0.1                     <pip>
lazy-object-proxy         1.3.1                     <pip>
libopencv                 3.4.2                h20b85fd_0
libpng                    1.6.36               h2a8f88b_0
libtiff                   4.0.10               hb898794_2
Markdown                  3.0.1                     <pip>
matplotlib                3.0.3                     <pip>
mccabe                    0.6.1                     <pip>
mkl                       2019.1                      144
mkl_fft                   1.0.10           py37h14836fe_0
mkl_random                1.0.2            py37h343c172_0
mock                      2.0.0                     <pip>
mypy                      0.670                     <pip>
mypy-extensions           0.4.1                     <pip>
numpy                     1.16.2                    <pip>
numpy                     1.16.2           py37h19fb1c0_0
numpy-base                1.16.2           py37hc3f5095_0
opencv                    3.4.2            py37h40b0b35_0
opencv-python             4.0.0.21                  <pip>
openssl                   1.1.1b               he774522_1
pandas                    0.24.2                    <pip>
parso                     0.3.4                     <pip>
pbr                       5.1.3                     <pip>
pickleshare               0.7.5                     <pip>
Pillow                    5.4.1                     <pip>
pip                       19.0.3                   py37_0
prompt-toolkit            2.0.9                     <pip>
protobuf                  3.7.0                     <pip>
py-opencv                 3.4.2            py37hc319ecb_0
pycparser                 2.19                      <pip>
pydot                     1.4.1                     <pip>
Pygments                  2.3.1                     <pip>
pylint                    2.3.1                     <pip>
pyparsing                 2.3.1                     <pip>
python                    3.7.2               h8c8aaf0_10
python-dateutil           2.8.0                     <pip>
pytz                      2018.9                    <pip>
PyYAML                    5.1                       <pip>
scipy                     1.2.1                     <pip>
setuptools                40.8.0                   py37_0
six                       1.12.0                    <pip>
sqlite                    3.27.2               he774522_0
tensorboard               1.13.1                    <pip>
tensorflow                1.13.1                    <pip>
tensorflow-estimator      1.13.0                    <pip>
termcolor                 1.1.0                     <pip>
Theano                    1.0.4                     <pip>
traitlets                 4.3.2                     <pip>
typed-ast                 1.3.1                     <pip>
vc                        14.1                 h0510ff6_4
vs2015_runtime            14.15.26706          h3a45250_0
wcwidth                   0.1.7                     <pip>
web.py                    0.40.dev1                 <pip>
Werkzeug                  0.14.1                    <pip>
wheel                     0.33.1                   py37_0
wincertstore              0.2                      py37_0
wrapt                     1.11.1                    <pip>
xz                        5.2.4                h2fa13f4_4
zlib                      1.2.11               h62dcd97_3
zstd                      1.3.7                h508b16e_0

2.目标检测原理

  • 1.YOLO算法思想
  • 2.交并比
  • 3.非最大抑制
  • 4.Anchor Box
  • 5.YOLO算法例子

2.1 YOLO算法思想

基本的滑动窗口对象检测算法并不能精准描绘边框,所以我们要学习一个能够得到准确边框的算法YOLO(You Only Look Ones)算法。

 算法思想:在图片上放置n*n的网格,并将图像分类和定位算法运用到每个网格上面去。

                                                     

与滑动窗口对象检测算法的区别:

  • 窗口滑动由放置网格取代,YOLO算法会对每个网格使用图像分类和图像定位算法,相比滑动窗口会大大的减少了运算量。
  • 一个对象可能在多个网格中,YOLO算法会找到该对象的中心点,并把对象分给包含中心的网格。(bh,bw是可以大于网格大小,实践中会使用更精细的去分割网格,所以对象会横跨多个网格)
  • 显示的输出边界框架,让神经网络输出的框架可以具有任意的宽高比,并且能输出更精确的坐标。

                                                       

由于YOLO算法的特性使得,每个格子的对象不能超过一个,但是实践过程中网格会分的很精细所以基本上不用担心这个问题。

2.2 YOLO算法的输出

由于YOLO算法是对每个网格进行运行,在视频给出的例子中输出结果3*3*8

                                                 

3*3是图片的分割的网格数量

8是输出图片预测和边框位置等信息,其实就等于给出的标签y的具体信息:

                                                      

2.3  交并比(IOU)

计算两个边框交集和并集之比,IOU是用来衡量两个边框的重叠的大小。

                                           

交集/并集=

                                         

一般认为IOU>=α(阀值,一般人为约定为0.5)是可接受的,由不同场景可自己设置。

2.4  非最大抑制

 抑制不是极大值的元素,搜索局部的极大值,确保每个对象只检测一次。

YOLO算法在运行中存在一个问题,理论上图片上每一个对象只存在一个中心点,实践中可能会有几个网格都会认为对象的中心在自己网格中。

                                      

于是就会生成多个边框

                                              

非最大抑制的作用就是,对于每个对象只保留其Pc预测最大的值

                                                

非最大抑制用法

  • 去掉IOU没到达到阀值的边框,while(存在剩下的边框):
  • 选择概率Pc的边框,输出为预测结果
  • 剩下边框中所以和输出边框有很大的交并,则它们的输出被抑制。

 *如果存在多种类型的对象(即是c1,c2,c3),每种类型的对象要单独运行最大抑制(不同类型对象的交并值会对结果产生影响)。

 2.5 Anchor Box

使用Anchor Box可以让一个检测出多个对象(不同类别的)。

                                                             

 Anchor Box思路:

  • a.预先定义多个个不同形状的的Anchor Box,Anchor Box的形状是与预测结果关联起来的。

                                             

  • 现在每个对象都和以前一样分配到同一个格子里面,但是现在还要分配到一个Anchor Box,分配的原则是比较并选择该格子内对象与定义的Anchor Box不同形状之间的IOU交并比最高的那个。

于是标签y(对于视频中的例子)就变成下图这样了,每一个Pc对应这一个Anchor Box形状。

                                                                    

使用Anchor Box的好处:

  • 处理两个不同类型的对象出现在同一格子里面,实际情况中如果格子够细致的话,一般不会出现两个格子在一个对象里面的
  • 能够让算法更有针对行(监督学习),如果你的数据给出的对象的形状大概相似。

如何选择Anchor Box:

  一般手动根据对象制定Anchor Box形状,可以选择5个到10个形状,可以涵盖你想要检测对象的各种对象。

2.6 YOLO算法例子

上面讲的都是构建YOLO算法中所需要知道的一些知识,下面就通过一个例子把所有的知识点穿插起来。

视频使用的例子还是在图片中检测pedestrian(行人)、car(车)和motorcycle(摩托车)

                                                           

输出数据形状是3x3x16:

  • 3x3是分的格子形状
  • 16(=2x8)其中2是使用的Anchor Box的个数,8就是输出的参数个数(Pc,bw,bx,bh,bw,c1,c2,c3)

输出标签y:

                                                                           

Anchor Box选择:

使用Anchor Box 1表示pedestrian(行人),Anchor Box 2表示car(车)和motorcycle(摩托车),这里是不知道为什么摩托车没Anchor形状,所以我认为可能摩托车形状与Anchor Box 2差不多。

                                                         

使用卷积神经网络训练:

                                        

最后是使用非最大抑制:

没有使用非最大抑制时,由于使用了两个Another Box,每个格子都会有两个检测边界框,只是Pc概率不同而以。

                                                

你需要做的是:

  • 抛弃概率比较低的预测边框

                                                  

  • 如果存在三个对象检测的类别(行人,车和摩托车),对每一类单独运行非最大抑制。这里的意思对于行人Anchor Box 1的形状的概率明显高于Anchor Box 2的概率。所以去掉Anchor Box 2形状的边框。

3.车辆识别算法实现

所有文件稍后在最后给出!

3.1 导入所需的包

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
import keras_yolo        
import yolo_utils      
from keras.layers import Input, Lambda, Conv2D
from keras.models import load_model, Model
from keras.utils import plot_model
from yolo_utils import read_classes, read_anchors, generate_colors
from yolo_utils import preprocess_image, draw_boxes, scale_boxes
from keras_yolo import yolo_head
from keras_yolo import yolo_boxes_to_corners, preprocess_true_boxes
from keras_yolo import yolo_loss, yolo_body

3.2 模型细节

  • 输入的批量图片的维度为(m,608,608,3)
  • 输出是一个识别分类与边界框的列表。每个边界框由6个数字组成:(px,bx,by,bh,bw,cpx,bx,by,bh,bw,c)。如果你将cc放到80维的向量中,那么每个边界框就由85个数字组成。

我们会使用5个锚框(anchor boxes),所以算法大致流程是这样的:图像输入(m,608,608,3) ⇒ DEEP CNN ⇒ 编码(m,19,19,5,85)。

我们来看看编码的情况: 

                

                                                    YOLO算法的编码
如果对象的中心/中点在单元格内,那么该单元格就负责识别该对象。

我们也使用了5个锚框,19x19的单元格,所以每个单元格内有5个锚框的编码信息,锚框的组pc+px+py+ph+pwpc+px+py+ph+pw
为了方便,我们将把最后的两个维度的数据进行展开,所以最后一步的编码由(m,19,19,5,85)变为了(m,19,19,425)。

                 
                                                            展开最后两维
对于每个单元格的每个锚框而言,我们将计算下列元素的乘积,并提取该框包含某一类的概率。

                         

                                                                         找寻每个锚框的概率
这里有张YOLO预测图的可视化预测:

  • 对于每个19x19的单元格,找寻最大的可能性值,在5个锚框和不同的类之间取最大值
  • 根据单元格预测的最可能的对象来使用添加颜色的方式来标记单元格。

                                                                  
 在19x19的格子中根据哪个类在该单元中具有最大的预测概率来进行着色。.
需要注意的就是该可视化不是YOLO算法本身进行预测的核心部分,这只是一种可视化算法中间结果的比较d好的方法。另一种可视化YOLO输出的方法是绘制它输出的边界框,这样做会导致可视化是这样的: 

                                                                      
每个单元格会输出5个锚框。总的来说,观察一次图像(一次前向传播),该模型需要预测:19x19x5=1805个锚框,不同的颜色代表不同的分类。 
在上图中们只绘制了模型所猜测的高概率的锚框,但锚框依旧是太多了。我们希望将算法的输出过滤为检测到的对象数量更少,要做到这一点,我们将使用非最大抑制。具体来说,我们将执行以下步骤:

  • 舍弃掉低概率的锚框(意思是格子算出来的概率比较低我们就不要)
  • 当几个锚框相互重叠并检测同一个物体时,只选择一个锚框。

3.3 分类阈值过滤

  现在我们要为阈值进行过滤,我们要去掉一些预测值低于预设值的锚框。模型共计会有19×19×5×8519×19×5×85个数字,每一个锚框由85个数字组成(80个分类+pc+px+py+ph+pwpc+px+py+ph+pw),将维度为(19,19,5,85)或者(19,19,425)转换为下面的维度将会有利于我们的下一步操作:

  • box_confidence:tensor类型,维度为(19x19,5,1),包含19x19单元格中每个单元格预测的5个锚框中的所有的锚框的pcpc(一些对象的置信概率)。
  • boxes:tensor类型,维度为(19x19,5,4),包含了所有的锚框的(px,py,ph,pwpx,py,ph,pw)。
  • box_class_probs:tensor类型,维度为(19x19,5,80),包含了所有单元格中所有锚框的所有对象(c1,c2,c3,⋅⋅⋅,c80c1,c2,c3,···,c80)检测的概率。

现在我们要实现函数yolo_filter_boxes(),步骤如下:

1. 计算对象的可能性: 

  • a = np.random.randn(19x19,5,1) 
  • b = np.random.randn(19x19,5,80) 
  • c = a * b 计算后的维度将会是(19x19,5,80) 

2. 对于每个锚框,需要找到:

  • 对分类的预测的概率拥有最大值的锚框的索引(查看中文文档),需要注意的是我们需要选择的轴,我们可以试着使用axis=-1。
  • 对应的最大值的锚框(查看中文文档),需要注意的是我们需要选择的轴,我们可以试着使用axis=-1。

3. 根据阈值来创建掩码,比如执行下列操作:[0.9, 0.3, 0.4, 0.5, 0.1] < 0.4,返回的是[False, True, False, False, True],对于我们要保留的锚框,对应的掩码应该为True或者1.

4. 使用TensorFlow来对box_class_scores、boxes、box_classes进行掩码操作以过滤出我们想要的锚框。

注:如果你想要调用Keras的函数的话,请使用K.function(...)。

# GRADED FUNCTION: yolo_filter_boxes

def yolo_filter_boxes(box_confidence , boxes, box_class_probs, threshold = 0.6):
    """
    通过阈值来过滤对象和分类的置信度。

    参数:
        box_confidence  - tensor类型,维度为(19,19,5,1),包含19x19单元格中每个单元格预测的5个锚框中的所有的锚框的pc (一些对象的置信概率)。
        boxes - tensor类型,维度为(19,19,5,4),包含了所有的锚框的(px,py,ph,pw )。
        box_class_probs - tensor类型,维度为(19,19,5,80),包含了所有单元格中所有锚框的所有对象( c1,c2,c3,···,c80 )检测的概率。
        threshold - 实数,阈值,如果分类预测的概率高于它,那么这个分类预测的概率就会被保留。

    返回:
        scores - tensor 类型,维度为(None,),包含了保留了的锚框的分类概率。
        boxes - tensor 类型,维度为(None,4),包含了保留了的锚框的(b_x, b_y, b_h, b_w)
        classess - tensor 类型,维度为(None,),包含了保留了的锚框的索引

    注意:"None"是因为你不知道所选框的确切数量,因为它取决于阈值。
          比如:如果有10个锚框,scores的实际输出大小将是(10,)
    """

    #第一步:计算锚框的得分
    box_scores  = box_confidence * box_class_probs

    #第二步:找到最大值的锚框的索引以及对应的最大值的锚框的分数
    box_classes = K.argmax(box_scores, axis=-1)
    box_class_scores = K.max(box_scores, axis=-1)

    #第三步:根据阈值创建掩码
    filtering_mask = (box_class_scores >= threshold)

    #对scores, boxes 以及 classes使用掩码
    scores = tf.boolean_mask(box_class_scores,filtering_mask)
    boxes = tf.boolean_mask(boxes,filtering_mask)
    classes = tf.boolean_mask(box_classes,filtering_mask)

    return scores , boxes , classes


with tf.Session() as test_a:
    box_confidence = tf.random_normal([19, 19, 5, 1], mean=1, stddev=4, seed = 1)
    boxes = tf.random_normal([19, 19, 5, 4], mean=1, stddev=4, seed = 1)
    box_class_probs = tf.random_normal([19, 19, 5, 80], mean=1, stddev=4, seed = 1)
    scores, boxes, classes = yolo_filter_boxes(box_confidence, boxes, box_class_probs, threshold = 0.5)
    print("scores[2] = " + str(scores[2].eval()))
    print("boxes[2] = " + str(boxes[2].eval()))
    print("classes[2] = " + str(classes[2].eval()))
    print("scores.shape = " + str(scores.shape))
    print("boxes.shape = " + str(boxes.shape))
    print("classes.shape = " + str(classes.shape))

3.4 非最大抑制

  即使是我们通过阈值来过滤了一些得分较低的分类,但是我们依旧会有很多的锚框被留了下来,第二个过滤器就是让下图左边变为右边,我们叫它非最大值抑制( non-maximum suppression (NMS)) 

                                         

 在这个例子中,这个模型预测了3辆车,但实际上它预测的是同一辆车。运行非最大抑制(NMS)将只选择3个锚框中最准确(最高概率)的一个锚框 
非最大值抑制使用了一个非常重要的功能,叫做交并比(Intersection over Union (IoU)) 


现在我们要实现交并比函数iou(),步骤如下:

# GRADED FUNCTION: iou

def iou(box1, box2):
    """
    实现两个锚框的交并比的计算

    参数:
        box1 - 第一个锚框,元组类型,(x1, y1, x2, y2)
        box2 - 第二个锚框,元组类型,(x1, y1, x2, y2)

    返回:
        iou - 实数,交并比。
    """

    # Calculate the (y1, x1, y2, x2) coordinates of the intersection of box1 and box2. Calculate its Area.
    ### START CODE HERE ### (≈ 5 lines)
    xi1 = max(box1[0], box2[0])
    yi1 = max(box1[1], box2[1])
    xi2 = min(box1[2], box2[2])
    yi2 = min(box1[3], box2[3])
    inter_area = (yi2 - yi1) * (xi2 - xi1)
    ### 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[2] - box1[0]) * (box1[3] - box1[1])
    box2_area = (box2[2] - box2[0]) * (box2[3] - box2[1])
    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_threshold相比重叠的框。
  • 返回第一步,直到不再有比当前选中的框得分更低的框。
  • 这将删除与选定框有较大重叠的其他所有锚框,只有得分最高的锚框仍然存在。

  我们要实现的函数名为yolo_non_max_suppression(),使用TensorFlow实现,TensorFlow有两个内置函数用于实现非最大抑制(所以你实际上不需要使用你的iou()实现):

# GRADED FUNCTION: yolo_non_max_suppression

def yolo_non_max_suppression(scores, boxes, classes, max_boxes = 10, iou_threshold = 0.5):
    """
    为锚框实现非最大值抑制( Non-max suppression (NMS))

    参数:
        scores - tensor类型,维度为(None,),yolo_filter_boxes()的输出
        boxes - tensor类型,维度为(None,4),yolo_filter_boxes()的输出,已缩放到图像大小(见下文)
        classes - tensor类型,维度为(None,),yolo_filter_boxes()的输出
        max_boxes - 整数,预测的锚框数量的最大值
        iou_threshold - 实数,交并比阈值。

    返回:
        scores - tensor类型,维度为(,None),每个锚框的预测的可能值
        boxes - tensor类型,维度为(4,None),预测的锚框的坐标
        classes - tensor类型,维度为(,None),每个锚框的预测的分类

    注意:"None"是明显小于max_boxes的,这个函数也会改变scores、boxes、classes的维度,这会为下一步操作提供方便。

    """
    
    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)
    nms_indices = tf.image.non_max_suppression(boxes=boxes, scores=scores, max_output_size=max_boxes, iou_threshold=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


with tf.Session() as test_b:
    scores = tf.random_normal([54,], mean=1, stddev=4, seed = 1)
    boxes = tf.random_normal([54, 4], mean=1, stddev=4, seed = 1)
    classes = tf.random_normal([54,], mean=1, stddev=4, seed = 1)
    scores, boxes, classes = yolo_non_max_suppression(scores, boxes, classes)
    print("scores[2] = " + str(scores[2].eval()))
    print("boxes[2] = " + str(boxes[2].eval()))
    print("classes[2] = " + str(classes[2].eval()))
    print("scores.shape = " + str(scores.eval().shape))
    print("boxes.shape = " + str(boxes.eval().shape))
    print("classes.shape = " + str(classes.eval().shape))



3.5 对所有框进行过滤

  现在我们要实现一个CNN(19x19x5x85)输出的函数,并使用刚刚实现的函数对所有框进行过滤。

  我们要实现的函数名为yolo_eval(),它采用YOLO编码的输出,并使用分数阈值和NMS来过滤这些框。你必须知道最后一个实现的细节。有几种表示锚框的方式,例如通过它们的角或通过它们的中点和高度/宽度。YOLO使用以下功能(我们提供)在不同时间在几种这样的格式之间进行转换:

  • boxes = yolo_boxes_to_corners(box_xy, box_wh)

它将yolo锚框坐标(x,y,w,h)转换为角的坐标(x1,y1,x2,y2)以适应yolo_filter_boxes()的输入。

  • boxes = yolo_utils.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):
    """
    将YOLO编码的输出(很多锚框)转换为预测框以及它们的分数,框坐标和类。

    参数:
        yolo_outputs - 编码模型的输出(对于维度为(608,608,3)的图片),包含4个tensors类型的变量:
                        box_confidence : tensor类型,维度为(None, 19, 19, 5, 1)
                        box_xy         : tensor类型,维度为(None, 19, 19, 5, 2)
                        box_wh         : tensor类型,维度为(None, 19, 19, 5, 2)
                        box_class_probs: tensor类型,维度为(None, 19, 19, 5, 80)
        image_shape - tensor类型,维度为(2,),包含了输入的图像的维度,这里是(608.,608.)
        max_boxes - 整数,预测的锚框数量的最大值
        score_threshold - 实数,可能性阈值。
        iou_threshold - 实数,交并比阈值。

    返回:
        scores - tensor类型,维度为(,None),每个锚框的预测的可能值
        boxes - tensor类型,维度为(4,None),预测的锚框的坐标
        classes - tensor类型,维度为(,None),每个锚框的预测的分类
    """

    ### START CODE HERE ###

    #获取YOLO模型的输出
    box_confidence, box_xy, box_wh, box_class_probs = yolo_outputs

    #中心点转换为边角
    boxes = yolo_boxes_to_corners(box_xy,box_wh)

    #可信度分值过滤
    scores, boxes, classes = yolo_filter_boxes(box_confidence, boxes, box_class_probs, score_threshold)

    #缩放锚框,以适应原始图像
    boxes = yolo_utils.scale_boxes(boxes, image_shape)

    #使用非最大值抑制
    scores, boxes, classes = yolo_non_max_suppression(scores, boxes, classes, max_boxes, iou_threshold)

    return scores, boxes, classes


4.对YOLO的总结:

  1. 输入图像为(608,608,3)
  2. 输入的图像先要通过一个CNN模型,返回一个(19,19,5,85)的数据。在对最后两维降维之后,输出的维度变为了(19,19,425): 每个19x19的单元格拥有425个数字。425 = 5 x 85,即每个单元格拥有5个锚框,每个锚框由5个基本信息+80个分类预测构成,参见图4。85 = 5 + 85,其中5个基本信息是(pc,px,py,ph,pwpc,px,py,ph,pw),剩下80就是80个分类的预测。

然后我们会根据以下规则选择锚框:

  • 预测分数阈值:丢弃分数低于阈值的分类的锚框。
  • 非最大值抑制:计算交并比,并避免选择重叠框。
  • 最后给出YOLO的最终输出。

5.测试已经训练好了的YOLO模型

  在这部分,我们将使用一个预先训练好的模型并在汽车检测数据集上进行测试。像往常一样,首先创建一个会话来启动计算图:

sess = K.get_session()

5.1  定义分类、锚框与图像维度

  回想一下我们在试着分类80个类别,使用5个锚框。我们收集了两个文件“coco_classes.txt”和“yolo_anchors.txt”中关于80个类和5个锚框的信息。 我们将这些数据加载到模型中。

class_names = read_classes("E:\吴恩达深度学习笔记\编程练习\week3\model_data\coco_classes.txt")
anchors = read_anchors("E:\吴恩达深度学习笔记\编程练习\week3\model_data\yolo_anchors.txt")
image_shape = (720., 1280.)


5.2 - 加载已经训练好了的模型

  训练YOLO模型需要很长时间,并且需要一个相当大的标签边界框数据集,用于大范围的目标类。我们将加载存储在“yolov2.h5”中的现有预训练Keras YOLO模型。 (这些权值来自官方YOLO网站,并使用Allan Zelener编写的函数进行转换,从技术上讲,这些参数来自“YOLOv2”模型。

yolo_model = load_model("E:\吴恩达深度学习笔记\编程练习\week3\model_data\yolo.h5")

这会加载训练的YOLO模型的权重, 以下是模型包含的图层的摘要:

yolo_model.summary()


执行结果:

 warnings.warn('No training configuration found in save file: '
__________________________________________________________________________________________
Layer (type)                    Output Shape         Param #     Connected to
==========================================================================================
input_1 (InputLayer)            (None, 608, 608, 3)  0
__________________________________________________________________________________________
conv2d_1 (Conv2D)               (None, 608, 608, 32) 864         input_1[0][0]
__________________________________________________________________________________________
batch_normalization_1 (BatchNor (None, 608, 608, 32) 128         conv2d_1[0][0]
__________________________________________________________________________________________
leaky_re_lu_1 (LeakyReLU)       (None, 608, 608, 32) 0           batch_normalization_1[0][
__________________________________________________________________________________________
max_pooling2d_1 (MaxPooling2D)  (None, 304, 304, 32) 0           leaky_re_lu_1[0][0]
__________________________________________________________________________________________
conv2d_2 (Conv2D)               (None, 304, 304, 64) 18432       max_pooling2d_1[0][0]
__________________________________________________________________________________________
batch_normalization_2 (BatchNor (None, 304, 304, 64) 256         conv2d_2[0][0]
__________________________________________________________________________________________
leaky_re_lu_2 (LeakyReLU)       (None, 304, 304, 64) 0           batch_normalization_2[0][
__________________________________________________________________________________________
max_pooling2d_2 (MaxPooling2D)  (None, 152, 152, 64) 0           leaky_re_lu_2[0][0]
__________________________________________________________________________________________
conv2d_3 (Conv2D)               (None, 152, 152, 128 73728       max_pooling2d_2[0][0]
__________________________________________________________________________________________
batch_normalization_3 (BatchNor (None, 152, 152, 128 512         conv2d_3[0][0]
__________________________________________________________________________________________
leaky_re_lu_3 (LeakyReLU)       (None, 152, 152, 128 0           batch_normalization_3[0][
__________________________________________________________________________________________
conv2d_4 (Conv2D)               (None, 152, 152, 64) 8192        leaky_re_lu_3[0][0]
__________________________________________________________________________________________
batch_normalization_4 (BatchNor (None, 152, 152, 64) 256         conv2d_4[0][0]
__________________________________________________________________________________________
leaky_re_lu_4 (LeakyReLU)       (None, 152, 152, 64) 0           batch_normalization_4[0][
__________________________________________________________________________________________
conv2d_5 (Conv2D)               (None, 152, 152, 128 73728       leaky_re_lu_4[0][0]
__________________________________________________________________________________________
batch_normalization_5 (BatchNor (None, 152, 152, 128 512         conv2d_5[0][0]
__________________________________________________________________________________________
leaky_re_lu_5 (LeakyReLU)       (None, 152, 152, 128 0           batch_normalization_5[0][
__________________________________________________________________________________________
max_pooling2d_3 (MaxPooling2D)  (None, 76, 76, 128)  0           leaky_re_lu_5[0][0]
__________________________________________________________________________________________
conv2d_6 (Conv2D)               (None, 76, 76, 256)  294912      max_pooling2d_3[0][0]
__________________________________________________________________________________________
batch_normalization_6 (BatchNor (None, 76, 76, 256)  1024        conv2d_6[0][0]
__________________________________________________________________________________________
leaky_re_lu_6 (LeakyReLU)       (None, 76, 76, 256)  0           batch_normalization_6[0][
__________________________________________________________________________________________
conv2d_7 (Conv2D)               (None, 76, 76, 128)  32768       leaky_re_lu_6[0][0]
__________________________________________________________________________________________
batch_normalization_7 (BatchNor (None, 76, 76, 128)  512         conv2d_7[0][0]
__________________________________________________________________________________________
leaky_re_lu_7 (LeakyReLU)       (None, 76, 76, 128)  0           batch_normalization_7[0][
__________________________________________________________________________________________
conv2d_8 (Conv2D)               (None, 76, 76, 256)  294912      leaky_re_lu_7[0][0]
__________________________________________________________________________________________
batch_normalization_8 (BatchNor (None, 76, 76, 256)  1024        conv2d_8[0][0]
__________________________________________________________________________________________
leaky_re_lu_8 (LeakyReLU)       (None, 76, 76, 256)  0           batch_normalization_8[0][
__________________________________________________________________________________________
max_pooling2d_4 (MaxPooling2D)  (None, 38, 38, 256)  0           leaky_re_lu_8[0][0]
__________________________________________________________________________________________
conv2d_9 (Conv2D)               (None, 38, 38, 512)  1179648     max_pooling2d_4[0][0]
__________________________________________________________________________________________
batch_normalization_9 (BatchNor (None, 38, 38, 512)  2048        conv2d_9[0][0]
__________________________________________________________________________________________
leaky_re_lu_9 (LeakyReLU)       (None, 38, 38, 512)  0           batch_normalization_9[0][
__________________________________________________________________________________________
conv2d_10 (Conv2D)              (None, 38, 38, 256)  131072      leaky_re_lu_9[0][0]
__________________________________________________________________________________________
batch_normalization_10 (BatchNo (None, 38, 38, 256)  1024        conv2d_10[0][0]
__________________________________________________________________________________________
leaky_re_lu_10 (LeakyReLU)      (None, 38, 38, 256)  0           batch_normalization_10[0]
__________________________________________________________________________________________
conv2d_11 (Conv2D)              (None, 38, 38, 512)  1179648     leaky_re_lu_10[0][0]
__________________________________________________________________________________________
batch_normalization_11 (BatchNo (None, 38, 38, 512)  2048        conv2d_11[0][0]
__________________________________________________________________________________________
leaky_re_lu_11 (LeakyReLU)      (None, 38, 38, 512)  0           batch_normalization_11[0]
__________________________________________________________________________________________
conv2d_12 (Conv2D)              (None, 38, 38, 256)  131072      leaky_re_lu_11[0][0]
__________________________________________________________________________________________
batch_normalization_12 (BatchNo (None, 38, 38, 256)  1024        conv2d_12[0][0]
__________________________________________________________________________________________
leaky_re_lu_12 (LeakyReLU)      (None, 38, 38, 256)  0           batch_normalization_12[0]
__________________________________________________________________________________________
conv2d_13 (Conv2D)              (None, 38, 38, 512)  1179648     leaky_re_lu_12[0][0]
__________________________________________________________________________________________
batch_normalization_13 (BatchNo (None, 38, 38, 512)  2048        conv2d_13[0][0]
__________________________________________________________________________________________
leaky_re_lu_13 (LeakyReLU)      (None, 38, 38, 512)  0           batch_normalization_13[0]
__________________________________________________________________________________________
max_pooling2d_5 (MaxPooling2D)  (None, 19, 19, 512)  0           leaky_re_lu_13[0][0]
__________________________________________________________________________________________
conv2d_14 (Conv2D)              (None, 19, 19, 1024) 4718592     max_pooling2d_5[0][0]
__________________________________________________________________________________________
batch_normalization_14 (BatchNo (None, 19, 19, 1024) 4096        conv2d_14[0][0]
__________________________________________________________________________________________
leaky_re_lu_14 (LeakyReLU)      (None, 19, 19, 1024) 0           batch_normalization_14[0]
__________________________________________________________________________________________
conv2d_15 (Conv2D)              (None, 19, 19, 512)  524288      leaky_re_lu_14[0][0]
__________________________________________________________________________________________
batch_normalization_15 (BatchNo (None, 19, 19, 512)  2048        conv2d_15[0][0]
__________________________________________________________________________________________
leaky_re_lu_15 (LeakyReLU)      (None, 19, 19, 512)  0           batch_normalization_15[0]
__________________________________________________________________________________________
conv2d_16 (Conv2D)              (None, 19, 19, 1024) 4718592     leaky_re_lu_15[0][0]
__________________________________________________________________________________________
batch_normalization_16 (BatchNo (None, 19, 19, 1024) 4096        conv2d_16[0][0]
__________________________________________________________________________________________
leaky_re_lu_16 (LeakyReLU)      (None, 19, 19, 1024) 0           batch_normalization_16[0]
__________________________________________________________________________________________
conv2d_17 (Conv2D)              (None, 19, 19, 512)  524288      leaky_re_lu_16[0][0]
__________________________________________________________________________________________
batch_normalization_17 (BatchNo (None, 19, 19, 512)  2048        conv2d_17[0][0]
__________________________________________________________________________________________
leaky_re_lu_17 (LeakyReLU)      (None, 19, 19, 512)  0           batch_normalization_17[0]
__________________________________________________________________________________________
conv2d_18 (Conv2D)              (None, 19, 19, 1024) 4718592     leaky_re_lu_17[0][0]
__________________________________________________________________________________________
batch_normalization_18 (BatchNo (None, 19, 19, 1024) 4096        conv2d_18[0][0]
__________________________________________________________________________________________
leaky_re_lu_18 (LeakyReLU)      (None, 19, 19, 1024) 0           batch_normalization_18[0]
__________________________________________________________________________________________
conv2d_19 (Conv2D)              (None, 19, 19, 1024) 9437184     leaky_re_lu_18[0][0]
__________________________________________________________________________________________
batch_normalization_19 (BatchNo (None, 19, 19, 1024) 4096        conv2d_19[0][0]
__________________________________________________________________________________________
conv2d_21 (Conv2D)              (None, 38, 38, 64)   32768       leaky_re_lu_13[0][0]
__________________________________________________________________________________________
leaky_re_lu_19 (LeakyReLU)      (None, 19, 19, 1024) 0           batch_normalization_19[0]
__________________________________________________________________________________________
batch_normalization_21 (BatchNo (None, 38, 38, 64)   256         conv2d_21[0][0]
__________________________________________________________________________________________
conv2d_20 (Conv2D)              (None, 19, 19, 1024) 9437184     leaky_re_lu_19[0][0]
__________________________________________________________________________________________
leaky_re_lu_21 (LeakyReLU)      (None, 38, 38, 64)   0           batch_normalization_21[0]
__________________________________________________________________________________________
batch_normalization_20 (BatchNo (None, 19, 19, 1024) 4096        conv2d_20[0][0]
__________________________________________________________________________________________
space_to_depth_x2 (Lambda)      (None, 19, 19, 256)  0           leaky_re_lu_21[0][0]
__________________________________________________________________________________________
leaky_re_lu_20 (LeakyReLU)      (None, 19, 19, 1024) 0           batch_normalization_20[0]
__________________________________________________________________________________________
concatenate_1 (Concatenate)     (None, 19, 19, 1280) 0           space_to_depth_x2[0][0]
                                                                 leaky_re_lu_20[0][0]
__________________________________________________________________________________________
conv2d_22 (Conv2D)              (None, 19, 19, 1024) 11796480    concatenate_1[0][0]
__________________________________________________________________________________________
batch_normalization_22 (BatchNo (None, 19, 19, 1024) 4096        conv2d_22[0][0]
__________________________________________________________________________________________
leaky_re_lu_22 (LeakyReLU)      (None, 19, 19, 1024) 0           batch_normalization_22[0]
__________________________________________________________________________________________
conv2d_23 (Conv2D)              (None, 19, 19, 425)  435625      leaky_re_lu_22[0][0]
==========================================================================================
Total params: 50,983,561
Trainable params: 50,962,889
Non-trainable params: 20,672
______________________________________________________________________________________________________________________________________________

 提示:如图2所示,该模型将预处理的一批输入图像(shape:(m,608,608,3))转换为tensor类型,维度为(m,19,19,5,85)。

5.3 - 将模型的输出转换为边界框

        yolo_model的输出是一个(m,19,19,5,85)的tensor变量,它需要进行处理和转换。

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

现在你已经把yolo_outputs添加进了计算图中,这4个tensor变量已准备好用作yolo_eval函数的输入。

5.4 - 过滤锚框

  yolo_outputs已经正确的格式为我们提供了yolo_model的所有预测框,我们现在已准备好执行过滤并仅选择最佳的锚框。现在让我们调用之前实现的yolo_eval()

scores, boxes, classes = yolo_eval(yolo_outputs, image_shape)

5.5 - 在实际图像中运行计算图

我们之前已经创建了一个用于会话的sess,这里有一些回顾:

  • yolo_model.input是yolo_model的输入,yolo_model.output是yolo_model的输出。
  • yolo_model.output会让yolo_head进行处理,这个函数最后输出yolo_outputs
  • yolo_outputs会让一个过滤函数yolo_eval进行处理,然后输出预测:scores、 boxes、 classes

现在我们要实现predict()函数,使用它来对图像进行预测,我们需要运行TensorFlow的Session会话,然后在计算图上计算scores、 boxes、 classes,下面的代码可以帮你预处理图像:

#图像预处理
    image, image_data = yolo_utils.preprocess_image("images/" + image_file, model_image_size = (608, 608))
  • image:用于绘制框的图像的Python(PIL)表示,这里你不需要使用它。
  • image_data:图像的numpy数组,这将是CNN的输入。

请注意!当模型使用BatchNorm(比如YOLO中的情况)时,您需要在feed_dict {K.learning_phase():0}中传递一个额外的占位符。

def predict(sess, image_file, is_show_info=True, is_plot=True):
    """
    运行存储在sess的计算图以预测image_file的边界框,打印出预测的图与信息。

    参数:
        sess - 包含了YOLO计算图的TensorFlow/Keras的会话。
        image_file - 存储在images文件夹下的图片名称
    返回:
        out_scores - tensor类型,维度为(None,),锚框的预测的可能值。
        out_boxes - tensor类型,维度为(None,4),包含了锚框位置信息。
        out_classes - tensor类型,维度为(None,),锚框的预测的分类索引。 """
    
    #图像预处理
    image, image_data = yolo_utils.preprocess_image("images/" + image_file, model_image_size = (608, 608))

    #运行会话并在feed_dict中选择正确的占位符.
    out_scores, out_boxes, out_classes = sess.run([scores, boxes, classes], feed_dict = {yolo_model.input:image_data, K.learning_phase(): 0})

    #打印预测信息
    if is_show_info:
        print("在" + str(image_file) + "中找到了" + str(len(out_boxes)) + "个锚框。")

    #指定要绘制的边界框的颜色
    colors = yolo_utils.generate_colors(class_names)

    #在图中绘制边界框
    yolo_utils.draw_boxes(image, out_scores, out_boxes, out_classes, class_names, colors)

    #保存已经绘制了边界框的图
    image.save(os.path.join("out", image_file), quality=100)

    #打印出已经绘制了边界框的图
    if is_plot:
        output_image = scipy.misc.imread(os.path.join("out", image_file))
        plt.imshow(output_image)

    return out_scores, out_boxes, out_classes
  
out_scores, out_boxes, out_classes = predict(sess, "test.jpg")



"""
连续打印多张图片
    for i in range(1,121):

    #计算需要在前面填充几个0
    num_fill = int( len("0000") - len(str(1))) + 1
    #对索引进行填充
    filename = str(i).zfill(num_fill) + ".jpg"
    print("当前文件:" + str(filename))

    #开始绘制,不打印信息,不绘制图
    out_scores, out_boxes, out_classes = predict(sess, filename,is_show_info=False,is_plot=False)



print("绘制完成!")

    
    """

最终结果:

6.关于一些细节:

文件中调用txt/h5文件常常容易出错,txt文件出错大多是因为路径调用错误,此时调用文件的相对路径即可。在使用yolo.h5模型时,报错"F:\downling\YAD2K-master\YAD2K-master\yad2k\models\keras_yolo.py,显示打不开Keras_yolo.py文件,此时并不是调用问题,是模型导入错误。需要重新导入yolo.h5,参照博客(https://blog.csdn.net/Solo95/article/details/85262828)即可解决问题。

项目所有文件请查看百度云:链接:https://pan.baidu.com/s/19IaAJUxKsj9o8XI5GlxiZg 
提取码:1fi8 
 

猜你喜欢

转载自blog.csdn.net/qq_41997920/article/details/88657618