Course 4 - 卷积神经网络 - 第三周作业

1.问题:yolo汽车识别

2.思路:

  • 实现对所有输出框的过滤yolo_eval函数,包括:

             首先实现根据阈值过滤锚框的yolo_filter_boxes函数

              再次实现非最大值抑制函数过滤交并比大于一定阈值的锚框的函数yolo_non_max_suppression

  • 然后加载已经实现好的yolo模型,该模型的输出就是锚框,但是需要将宽长,变为两个点的坐标表示,将处理后的锚框输出使用yolo_eval函数进行过滤
  • 实现预测函数,并实现可以批量绘制图片的函数

3.实现代码

yolo代码

#!/usr/bin/env python
# _*_ coding:utf-8 _*_
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 yad2k.models.keras_yolo import yolo_head, yolo_boxes_to_corners, preprocess_true_boxes, yolo_loss, yolo_body

import yolo_utils

# 1.根据阈值过滤锚框:每个格子有5个锚框,将每个锚框的置信度乘以类别1*80=80,并取最大的值作为该锚框的最大值,统计每个锚框的最大值
def yolo_filter_boxes(box_confidence,boxes,box_class_probs,threshold=0.6):
    '''

    :param box_confidence: tensor类型,维度为(19,19,5,1),包含19x19单元格中每个单元格预测的5个锚框中的所有的锚框的pc (一些对象的置信概率)
    :param boxes: tensor类型,维度为(19,19,5,4),包含了所有的锚框的(px,py,ph,pw )。
    :param box_class_probs:tensor类型,维度为(19,19,5,80),包含了所有单元格中所有锚框的所有对象( c1,c2,c3,···,c80 )检测的概率。
    :param threshold:实数,阈值,如果分类预测的概率高于它,那么这个分类预测的概率就会被保留
    :return:scores - tensor 类型,维度为(None,),包含了保留了的锚框的分类概率。
        boxes - tensor 类型,维度为(None,4),包含了保留了的锚框的(b_x, b_y, b_h, b_w)
        classess - tensor 类型,维度为(None,),包含了保留了的锚框的索引
    '''
    # 第一步:计算锚框的得分
    box_scores=box_confidence*box_class_probs  # shape=(19,19,5,80)

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

    # 根据阈值创建掩码
    filtering_mask=(box_class_scores>=threshold)  # shape=(19,19,5)

    # 对sores ,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)
#
#     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))
#
#     test_a.close()

# 2.实现交并比函数iou
def iou(box1,box2):
    '''

    :param box1: 第一个锚框,元组类型,(x1, y1, x2, y2)
    :param box2: 第二个锚框,元组类型,(x1, y1, x2, y2)
    :return:实数,交并比。
    '''

    # 计算相交区域的面积
    xi1=np.maximum(box1[0],box2[0])
    yi1=np.maximum(box1[1],box2[1])
    xi2=np.minimum(box1[2],box2[2])
    yi2=np.minimum(box1[3],box2[3])
    inter_area=(xi1-xi2)*(yi1-yi2)

    # 计算并集
    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

    iou=inter_area/union_area
    return iou
# 测试
# box1 = (2,1,4,3)
# box2 = (1,2,3,4)
# print("iou = " + str(iou(box1, box2)))

# 3.实现非最大值抑制函数,tf中有内置的函数实现非最大值抑制函数
def yolo_non_max_suppression(scores,boxes,classes,max_boxes=10,iou_threshold=0.5):
    '''
    为锚框实现非最大值抑制( Non-max suppression (NMS))
    :param scores:维度为(None,),yolo_filter_boxes()的输出
    :param boxes:维度为(None,4),yolo_filter_boxes()的输出,
    :param classes:维度为(None,),yolo_filter_boxes()的输出
    :param max_boxes:整数,预测的锚框数量的最大值
    :param iou_threshold: 实数,交并比阈值
    :return: scores - tensor类型,维度为(,None),每个锚框的预测的可能值
        boxes - tensor类型,维度为(4,None),预测的锚框的坐标
        classes - tensor类型,维度为(,None),每个锚框的预测的分类
    '''
    max_boxes_tensor=K.variable(max_boxes,dtype='int32')  # 用于tf.image.non_max_suppression()
    # 初始化变量max_boxes_tensor
    K.get_session().run(tf.variables_initializer([max_boxes_tensor]))

    #使用tf.image.non_max_suppression()来获取与我们保留的框相对应的索引列表
    nms_indices=tf.image.non_max_suppression(boxes,scores,max_boxes_tensor,iou_threshold)

    # 使用K.gather()来选择保留的锚框
    scores=K.gather(scores,nms_indices)
    boxes=K.gather(boxes,nms_indices)
    classes=K.gather(classes,nms_indices)

    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))
#
#     test_b.close()

# 4.对所有的输出框进行过滤
def yolo_eval(yolo_outputs,image_shape=(720.,1280.),
              max_boxes=10,score_threshold=0.6,iou_threshold=0.5):
    '''
    将YOLO编码的输出(很多锚框)转换为预测框以及它们的分数,框坐标和类
    :param 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)
    :param image_shape: tensor类型,维度为(2,),包含了输入的图像的维度,这里是(608.,608.)
    :param max_boxes:整数,预测的锚框数量的最大值
    :param score_threshold:实数,可能性阈值。
    :param iou_threshold:实数,交并比阈值。
    :return:  scores - tensor类型,维度为(,None),每个锚框的预测的可能值
        boxes - tensor类型,维度为(4,None),预测的锚框的坐标
        classes - tensor类型,维度为(,None),每个锚框的预测的分类
    '''
    # 获取YOLO模型的输出
    box_confidence,box_xy,box_wh,box_classes_probs=yolo_outputs
    # 将中心点转换为边角
    boxes=yolo_boxes_to_corners(box_xy,box_wh)

    # 可信度分值过滤
    scores,boxes,classes=yolo_filter_boxes(box_confidence,boxes,box_classes_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

# 测试
# with tf.Session() as test_c:
#     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))
#     scores, boxes, classes = yolo_eval(yolo_outputs)
#
#     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))
#
#     test_c.close()

# 3.测试已经训练好的YOLO模型
##  创建一个会话来启动一个计算图
sess=K.get_session()
## 3.1定义分类、锚框与图像宽度
# 我们在试着分类80个类别,使用5个锚框。我们收集了两个文件
# “coco_classes.txt”和“yolo_anchors.txt”中
# 关于80个类和5个锚框的信息。 我们将这些数据加载到模型中。
class_names=yolo_utils.read_classes('model_data/coco_classes.txt')
anchors=yolo_utils.read_anchors('model_data/yolo_anchors.txt')
## 3.2加载已经训练好的模型
# 将加载存储在“yolov2.h5”中的现有预训练Keras YOLO模型。 (这些权值来自官方YOLO
yolo_model=load_model('model_data/yolov2.h5')
# 显示模型包含的图层的摘要
# yolo_model.summary()

## 3.3将模型的输出转换为边界框
# 将yolo模型的输出是一个(m,19,19,5,85)的tensor变量,它需要进行处理和转换
yolo_outputs=yolo_head(yolo_model.output,anchors,len(class_names))
# 已经把yolo_outputs添加进了计算图中,这4个tensor变量已准备好用作yolo_eval函数的输入。

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

## 3.5在实际图像中运行计算图
'''
回顾:
1.yolo_model.input是yolo_model的输入,yolo_model.output是yolo_model的输出。
2.yolo_model.output会让yolo_head进行处理,这个函数最后输出yolo_outputs
3.yolo_outputs会让一个过滤函数yolo_eval进行处理,然后输出预测:scores、 boxes、 classes

'''
# image_file=''
# # 预处理图像
# image,image_data=yolo_utils.preprocess_image("images/"+image_file,model_image_size=(608,608))
# image:用于绘制框的图像的Python(PIL)表示,这里你不需要使用它。
# image_data:图像的numpy数组,这将是CNN的输入。
# !!!当模型使用BatchNorm时,需要在feed_dict {K.learning_phase():0}中传递一个额外的占位符。

# 4.预测函数
def predict(sess,image_file,is_show_info=True,is_plot=True):
    '''
     运行存储在sess的计算图以预测image_file的边界框,打印出预测的图与信息
    :param sess: 包含了YOLO计算图的TensorFlow/Keras的会话。
    :param image_file:存储在images文件夹下的图片名称
    :param show_info:
    :param is_plot:
    :return: out_scores - tensor类型,维度为(None,),锚框的预测的可能值。
        out_boxes - tensor类型,维度为(None,4),包含了锚框位置信息。
        out_classes - tensor类型,维度为(None,),锚框的预测的分类索引。
    '''
    # 1.图像预处理
    image,image_data=yolo_utils.preprocess_image("images/"+image_file,model_image_size = (608, 608))

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

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

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

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

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

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

    return out_scores,out_boxes,out_classes

# 测试
# out_scores,out_boxes,out_classes=predict(sess,"test.jpg")

# 5.批量绘制图
def draw_pictures():
    for i in range(10,121):
        # 计算需要在前面填充几个0
        num_fill=int(len("0000")-len(str(1)))+1

        # 对索引进行填充,在前面补充0长度到4
        filename=str(i).zfill(num_fill)+".jpg"
        print("当前文件:"+str(filename))

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

    print("绘制完成")

# 测试
draw_pictures()

    yolo_utils代码

import colorsys
import imghdr
import os
import random
from keras import backend as K

import numpy as np
from PIL import Image, ImageDraw, ImageFont

def read_classes(classes_path):
    with open(classes_path) as f:
        class_names = f.readlines()
    class_names = [c.strip() for c in class_names]
    return class_names

def read_anchors(anchors_path):
    with open(anchors_path) as f:
        anchors = f.readline()
        anchors = [float(x) for x in anchors.split(',')]
        anchors = np.array(anchors).reshape(-1, 2)
    return anchors

def generate_colors(class_names):
    hsv_tuples = [(x / len(class_names), 1., 1.) for x in range(len(class_names))]
    colors = list(map(lambda x: colorsys.hsv_to_rgb(*x), hsv_tuples))
    colors = list(map(lambda x: (int(x[0] * 255), int(x[1] * 255), int(x[2] * 255)), colors))
    random.seed(10101)  # Fixed seed for consistent colors across runs.
    random.shuffle(colors)  # Shuffle colors to decorrelate adjacent classes.
    random.seed(None)  # Reset seed to default.
    return colors

def scale_boxes(boxes, image_shape):
    """ Scales the predicted boxes in order to be drawable on the image"""
    height = image_shape[0] # 720
    width = image_shape[1] # 1280
    image_dims = K.stack([height, width, height, width])
    image_dims = K.reshape(image_dims, [1, 4])
    boxes = boxes * image_dims
    return boxes

def preprocess_image(img_path, model_image_size):
    image_type = imghdr.what(img_path)
    image = Image.open(img_path)
    resized_image = image.resize(tuple(reversed(model_image_size)), Image.BICUBIC)
    image_data = np.array(resized_image, dtype='float32')
    image_data /= 255.
    image_data = np.expand_dims(image_data, 0)  # Add batch dimension.
    return image, image_data

def draw_boxes(image, out_scores, out_boxes, out_classes, class_names, colors):
    
    font = ImageFont.truetype(font='font/FiraMono-Medium.otf',size=np.floor(3e-2 * image.size[1] + 0.5).astype('int32'))
    thickness = (image.size[0] + image.size[1]) // 300

    for i, c in reversed(list(enumerate(out_classes))):
        predicted_class = class_names[c]
        box = out_boxes[i]
        score = out_scores[i]

        label = '{} {:.2f}'.format(predicted_class, score)

        draw = ImageDraw.Draw(image)
        label_size = draw.textsize(label, font)

        top, left, bottom, right = box
        top = max(0, np.floor(top + 0.5).astype('int32'))
        left = max(0, np.floor(left + 0.5).astype('int32'))
        bottom = min(image.size[1], np.floor(bottom + 0.5).astype('int32'))
        right = min(image.size[0], np.floor(right + 0.5).astype('int32'))
        print(label, (left, top), (right, bottom))

        if top - label_size[1] >= 0:
            text_origin = np.array([left, top - label_size[1]])
        else:
            text_origin = np.array([left, top + 1])

        # My kingdom for a good redistributable image drawing library.
        for i in range(thickness):
            draw.rectangle([left + i, top + i, right - i, bottom - i], outline=colors[c])
        draw.rectangle([tuple(text_origin), tuple(text_origin + label_size)], fill=colors[c])
        draw.text(text_origin, label, fill=(0, 0, 0), font=font)
        del draw

 

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值