在之前的文章中,先容了打算机视觉领域中目标检测的干系方法——RCNN系列算法事理,以及Faster RCNN的实现。这些算法面临的一个问题,不是端到真个模型,几个构件拼凑在一起组成全体检测系统,操作起来比较繁芜,本文将先容其余一个端到真个方法——YOLO算法,该方法操作简便且仿真速率快,效果也不差。
YOLO算法是什么?
YOLO框架(You Only Look Once)与RCNN系列算法不一样,因此不同的办法处理工具检测。它将全体图像放在一个实例中,并预测这些框的边界框坐标和及所属种别概率。利用YOLO算法最大优的点是速率极快,每秒可处理45帧,也能够理解一样平常的工具表示。
YOLO框架如何运作?
在本节中,将先容YOLO用于检测给定图像中的工具的处理步骤。
首先,输入图像:然后,YOLO将输入图像划分为网格形式(例如3 X 3):
末了,对每个网格运用图像分类和定位处理,得到预测工具的边界框及其对应的类概率。
全体过程是不是很清晰,下面逐一详细先容。首先须要将标记数据通报给模型以进行演习。假设已将图像划分为大小为3 X 3的网格,且统共只有3个种别,分别是行人(c1)、汽车(c2)和摩托车(c3)。因此,对付每个单元格,标签y将是一个八维向量:
个中:
pc定义工具是否存在于网格中(存在的概率);bx、by、bh、bw指定边界框;c1、c2、c3代表种别。如果检测工具是汽车,则c2位置处的值将为1,c1和c3处的值将为0;假设从上面的例子中选择第一个网格:
由于此网格中没有工具,因此pc将为零,此网格的y标签将为:
?意味着其它值是什么并不主要,由于网格中没有工具。下面举例另一个有车的网格(c2=1):
在为此网格编写y标签之前,首先要理解YOLO如何确定网格中是否存在实际工具。大图中有两个物体(两辆车),因此YOLO将取这两个物体的中央点,物体将被分配到包含这些物体中央的网格中。中央点左侧网格的y标签会是这样的:
由于此网格中存在工具,因此pc将即是1,bx、by、bh、bw将相对付正在处理的特定网格单元打算。由于检测出的工具是汽车,以是c2=1,c1和c3均为0。对付9个网格中的每一个单元格,都具有八维输出向量。终极的输出形状为3X3X8。
利用上面的例子(输入图像:100X100X3,输出:3X3X8),模型将按如下办法进行演习:
利用经典的CNN网络构建模型,并进行模型演习。在测试阶段,将图像通报给模型,经由一次前向传播就得到输出y。为了大略起见,利用3X3网格阐明这一点,但常日在实际场景中会采取更大的网格(比如19X19)。
纵然一个工具超过多个网格,它也只会被分配到个中点所在的单个网格。可以通过增加更多网格来减少多个工具涌如今同一网格单元中的几率。
如何编码边界框?
如前所述,bx、by、bh和bw是相对付正在处理的网格单元打算而言的。下面通过一个例子来解释这一点。以包含汽车的右边网格为例:
由于bx、by、bh和bw将仅相对付该网格打算。此网格的y标签将为:
由于这个网格中有一个工具汽车,以是pc=1、c2=1。现在,看看如何决定bx、by、bh和bw的取值。在YOLO中,分配给所有网格的坐标都如下图所示:
bx、by是工具相对付该网格的中央点的x和y坐标。在例子中,近似bx=0.4和by=0.3:
bh是边界框的高度与相应单元网格的高度之比,在例子中约为0.9:bh=0.9,bw是边界框的宽度与网格单元的宽度之比,bw=0.5。此网格的y标签将为:
请把稳,bx和by将始终介于0和1之间,由于中央点始终位于网格内,而在边界框的尺寸大于网格尺寸的情形下,bh和bw可以大于1。
非极大值抑制|Non-Max Suppression
这里有一些思考的问题——如何判断预测的边界框是否是一个好结果(或一个坏结果)?单元格之间的交叉点,打算实际边界框和预测的边界框的并集交集。假设汽车的实际和预测边界框如下所示:
个中,赤色框是实际的边界框,蓝色框是预测的边界框。如何判断它是否是一个好的预测呢?IoU将打算这两个框的并集交叉区域:
IoU =交叉面积/联合的面积;在本例中:IoU =黄色面积/绿色面积;如果IoU大于0.5,就可以说预测足够好。0.5是在这里采纳的任意阈值,也可以根据详细问题进行变动。阈值越大,预测就越准确。
还有一种技能可以显著提高YOLO的效果——非极大值抑制。
工具检测算法最常见的问题之一是,它不是一次仅检测出一次工具,而可能得到多次检测结果。假设:
上图中,汽车不止一次被识别,那么如何剖断边界框呢。非极大值抑可以办理这个问题,使得每个工具只能进行一次检测。下面理解该方法的事情事理。
1.它首先查看与每次检测干系的概率并取最大的概率。在上图中,0.9是最高概率,因此首先选择概率为0.9的方框:2.现在,它会查看图像中的所有其他框。与当前边界框较高的IoU的边界框将被抑制。因此,在示例中,0.6和0.7概率的边界框将被抑制:
3.在部分边界框被抑制后,它会从概率最高的所有边界框中选择下一个,在例子中为0.8的边界框:
4.再次打算与该边界框相连边界框的IoU,去掉较高IoU值的边界框:
5.重复这些步骤,得到末了的边界框:
以上就是非极大值抑制的全部内容,总结一下关于非极大值抑制算法的要点:
丢弃概率小于或即是预定阈值(例如0.5)的所有方框;对付剩余的边界框:选择具有最高概率的边界框并将其作为输出预测;打算干系联的边界框的IoU值,舍去IoU大于阈值的边界框;重复步骤2,直到所有边界框都被视为输出预测或被舍弃;Anchor Boxes
在上述内容中,每个网格只能识别一个工具。但是如果单个网格中有多个工具呢?这就行须要理解 Anchor Boxes的观点。假设将下图按照3X3网格划分:
获取工具的中央点,并根据其位置将工具分配给相应的网格。在上面的示例中,两个工具的中央点位于同一网格中:
上述方法只会得到两个边界框个中的一个,但是如果利用Anchor Boxes,可能会输出两个边界框!
我们该怎么做呢?首先,预先定义两种不同的形状,称为Anchor Boxes。对付每个网格将有两个输出。这里为了易于理解,这里选取两个Anchor Boxes,也可以根据实际情形增加Anchor Boxes的数量:
有Anchor Boxes的YOLO输出标签如下所示:
前8行属于Anchor Boxes1,别的8行属于Anchor Boxes2。基于边界框和框形状的相似性将工具分配给Anchor Boxes。由于Anchor Boxes1的形状类似于人的边界框,后者将被分配给Anchor Boxes1,并且车将被分配给Anchor Boxes2.在这种情形下的输出,将是3X3X16大小。
因此,对付每个网格,可以根据Anchor Boxes的数量检测两个或更多个工具。
结合思想
在本节中,首先先容如何演习YOLO模型,然后是新的图像进行预测。
演习
演习模型时,输入数据是由图像及其相应的y标签构成。样例如下:
假设每个网格有两个Anchor Boxes,并划分为3X3网格,并且有3个不同的种别。因此,相应的y标签具有3X3X16的形状。演习过程的完成办法便是将特定形状的图像映射到对应3X3X16大小的目标。
测试
对付每个网格,模型将预测·3X3X16·大小的输出。该预测中的16个值将与演习标签的格式相同。前8个值将对应于Anchor Boxes1,个中第一个值将是该网络中工具的概率,2-5的值将是该工具的边界框坐标,末了三个值表明工具属于哪个类。以此类推。
末了,非极大值抑制方法将运用于预测框以得到每个工具的单个预测结果。
以下是YOLO算法遵照的确切维度和步骤:
准备对应的图像(608,608,3);将图像通报给卷积神经网络(CNN),该网络返回(19,19,5,85)维输出;输出的末了两个维度被展平以得到(19,19,425)的输出量:19×19网格的每个单元返回425个数字;425=5 85,个中5是每个网格的Anchor Boxes数量;85= 5+80,个中5表示(pc、bx、by、bh、bw),80是检测的种别数;末了,利用IoU和非极大值抑制去除重叠框;YOLO算法实现
本节中用于实现YOLO的代码来自Andrew NG的GitHub存储库,须要下载此zip文件,个中包含运行此代码所需的预演习权重。
首先定义一些函数,这些函数将用来选择高于某个阈值的边界框,并对其运用非极大值抑制。首先,导入所需的库:
import osimport matplotlib.pyplot as pltfrom matplotlib.pyplot import imshowimport scipy.ioimport scipy.miscimport numpy as npimport pandas as pdimport PILimport tensorflow as tffrom skimage.transform import resizefrom keras import backend as Kfrom keras.layers import Input, Lambda, Conv2Dfrom keras.models import load_model, Modelfrom yolo_utils import read_classes, read_anchors, generate_colors, preprocess_image, draw_boxes, scale_boxesfrom yad2k.models.keras_yolo import yolo_head, yolo_boxes_to_corners, preprocess_true_boxes, yolo_loss, yolo_body%matplotlib inline
然后,实现基于概率和阈值过滤边界框的函数:
def yolo_filter_boxes(box_confidence, boxes, box_class_probs, threshold = .6): box_scores = box_confidencebox_class_probs box_classes = K.argmax(box_scores,-1) box_class_scores = K.max(box_scores,-1) filtering_mask = box_class_scores>threshold 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
之后,实现打算IoU的函数:
def iou(box1, box2): 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) 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 iou = inter_area/union_area return iou
然后,实现非极大值抑制的函数:
def yolo_non_max_suppression(scores, boxes, classes, max_boxes = 10, iou_threshold = 0.5): max_boxes_tensor = K.variable(max_boxes, dtype='int32') K.get_session().run(tf.variables_initializer([max_boxes_tensor])) nms_indices = tf.image.non_max_suppression(boxes,scores,max_boxes,iou_threshold) scores = K.gather(scores,nms_indices) boxes = K.gather(boxes,nms_indices) classes = K.gather(classes,nms_indices) return scores, boxes, classes
随机初始化下大小为(19,19,5,85)的输出向量:
yolo_outputs = (tf.random_normal([19, 19, 5, 1], mean=1, stddev=4, seed = 1), tf.random_normal([19, 19, 5, 2], mean=1, stddev=4, seed = 1), tf.random_normal([19, 19, 5, 2], mean=1, stddev=4, seed = 1), tf.random_normal([19, 19, 5, 80], mean=1, stddev=4, seed = 1))
末了,实现一个将CNN的输出作为输入并返回被抑制的边界框的函数:
def yolo_eval(yolo_outputs, image_shape = (720., 1280.), max_boxes=10, score_threshold=.6, iou_threshold=.5): 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, threshold = score_threshold) boxes = scale_boxes(boxes, image_shape) scores, boxes, classes = yolo_non_max_suppression(scores, boxes, classes, max_boxes, iou_threshold) return scores, boxes, classes
利用yolo_eval函数对之前创建的随机输出向量进行预测:
scores, boxes, classes = yolo_eval(yolo_outputs)with tf.Session() as test_b: print(\"大众scores[2] = \公众 + str(scores[2].eval())) print(\"大众boxes[2] = \"大众 + str(boxes[2].eval())) print(\公众classes[2] = \"大众 + str(classes[2].eval()))
score表示工具在图像中的可能性,boxes返回检测到的工具的(x1,y1,x2,y2)坐标,classes表示识别工具所属的类。
现在,在新的图像上利用预演习的YOLO算法,看看其事情效果:
sess = K.get_session()class_names = read_classes(\"大众model_data/coco_classes.txt\"大众)anchors = read_anchors(\公众model_data/yolo_anchors.txt\公众)yolo_model = load_model(\公众model_data/yolo.h5\"大众)
在加载种别信息和预演习模型之后,利用上面定义的函数来获取·yolo_outputs·。
yolo_outputs = yolo_head(yolo_model.output, anchors, len(class_names))
之后,定义一个函数来预测边界框并在图像上标记边界框:
def predict(sess, image_file): image, image_data = preprocess_image(\"大众images/\"大众 + image_file, model_image_size = (608, 608)) out_scores, out_boxes, out_classes = sess.run([scores, boxes, classes], feed_dict={yolo_model.input: image_data, K.learning_phase(): 0}) 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)) plt.figure(figsize=(12,12)) imshow(output_image) return out_scores, out_boxes, out_classes
接下来,将利用预测函数读取图像并进行预测:
img = plt.imread('images/img.jpg')image_shape = float(img.shape[0]), float(img.shape[1])scores, boxes, classes = yolo_eval(yolo_outputs, image_shape)
末了,输出预测结果:
out_scores, out_boxes, out_classes = predict(sess, \"大众img.jpg\"大众)
以上便是YOLO算法的全部内容,更多详细内容可以关注darknet的官网。
作者信息
PULKIT SHARMA,机器学习和深度学习
本文由阿里如斯栖社区组织翻译。
文章原标题《A Practical Guide to Object Detection using the Popular YOLO Framework – Part III (with Python codes)》,译者:海棠,审校:Uncle_LLD。