前言:

yolo系列的论文阅读
论文阅读 || 深度学习之目标检测 重磅出击YOLOv3
论文阅读 || 深度学习之目标检测yolov2
论文阅读 || 深度学习之目标检测yolov1

该篇讲解的工程连接是:
tensorflow的yolov3:https://github.com/YunYang1994/tensorflow-yolov3

自己对该工程的解析博客:
YOLOv3 || 1. 使用自己的数据集训练yolov3
YOLOv3 || 2. dataset.py解析
YOLOv3 || 3. dataset.py的多进程改造
YOLOv3 || 4. yolov3.py 网络结构的搭建和loss的定义
YOLOv3 || 5. train.py
YOLOv3 || 6. anchorboxes的获取 kmeans.py
YOLOv3 || 7. yolov3的pb文件的测试

1 代码讲解

1.1 概述

该脚本主要定义了一个数据读取的类,结构如下:

class Dataset(object):"""implement Dataset here"""def __init__(self, dataset_type):...def __iter__(self):return selfdef __next__(self):...def __len__(self):return self.num_batchs

其中,核心部分为def __next__(self):,是整个数据处理过程

  • 创建存放 输入图片和label 的数组
  • 读取训练集或验证集的数据信息(输入图片路径、bboxes)
  • 数据增强:随机翻转、随机裁剪、随机平移、
  • 将图片缩放和填充到target_shape(相同规则处理bboxes)
  • 结合anchorbox等信息,将bboxes的信息,转化为神经网络训练的label

需要注意的是,读取文本得到的bboxes的格式为【左上角-右下角】信息,代码中会将bboxes信息转化为【center_x, center_y, height, width 】。


其中主要内容为def __next__(self),具体定义为

    def __next__(self):with tf.device('/cpu:0'):self.train_input_size = random.choice(self.train_input_sizes)self.train_output_sizes = self.train_input_size // self.strides# 创建存放【输入图片】的数组batch_image = np.zeros((self.batch_size, self.train_input_size, self.train_input_size, 3))# 创建存放【label】的数组batch_label_sbbox = np.zeros((self.batch_size, self.train_output_sizes[0], self.train_output_sizes[0], self.anchor_per_scale, 5 + self.num_classes))batch_label_mbbox = np.zeros((self.batch_size, self.train_output_sizes[1], self.train_output_sizes[1], self.anchor_per_scale, 5 + self.num_classes))batch_label_lbbox = np.zeros((self.batch_size, self.train_output_sizes[2], self.train_output_sizes[2], self.anchor_per_scale, 5 + self.num_classes))# 创建存放 在3个尺度下,负责预测的bboxesbatch_sbboxes = np.zeros((self.batch_size, self.max_bbox_per_scale, 4))batch_mbboxes = np.zeros((self.batch_size, self.max_bbox_per_scale, 4))batch_lbboxes = np.zeros((self.batch_size, self.max_bbox_per_scale, 4))num = 0       # 批内的计数器if self.batch_count < self.num_batchs:     # 当【已读取的批数】小于【一轮总批数】while num < self.batch_size:           # 当【批内读取个数】小于【batch】index = self.batch_count * self.batch_size + num  # 【index】为一轮内已经读取的数据个数if index >= self.num_samples: index -= self.num_samples  # 如果【index】大于【数据总量】,将index置0# 读取训练集或验证集的数据信息(输入图片路径、bboxes)annotation = self.annotations[index]# 数据增强:随机翻转、随机裁剪、随机平移、将图片缩放和填充到target_shape(相同规则处理bboxes)image, bboxes = self.parse_annotation(annotation)# 结合anchorbox等信息,将bboxes的信息,转化为神经网络训练的labellabel_sbbox, label_mbbox, label_lbbox, sbboxes, mbboxes, lbboxes = self.preprocess_true_boxes(bboxes)batch_image[num, :, :, :] = imagebatch_label_sbbox[num, :, :, :, :] = label_sbboxbatch_label_mbbox[num, :, :, :, :] = label_mbboxbatch_label_lbbox[num, :, :, :, :] = label_lbboxbatch_sbboxes[num, :, :] = sbboxesbatch_mbboxes[num, :, :] = mbboxesbatch_lbboxes[num, :, :] = lbboxesnum += 1self.batch_count += 1return batch_image, batch_label_sbbox, batch_label_mbbox, batch_label_lbbox, \batch_sbboxes, batch_mbboxes, batch_lbboxeselse:self.batch_count = 0np.random.shuffle(self.annotations)raise StopIteration

1.2 读取数据集信息def load_annotations()

  • 标签的格式为:
    voc_train.txt:
      image_path x_min, y_min, x_max, y_max, class_id x_min, y_min ,…, class_id
      例:
      xxx/xxx.jpg 18.19,6.32,424.13,421.83,20 323.86,2.65,640.0,421.94,20
      xxx/xxx.jpg 48,240,195,371,11 8,12,352,498,14

    def load_annotations() 读取该文件,以获取【[图片的路径】和【对应的bboxes】
   def load_annotations(self, dataset_type):with open(self.annot_path, 'r') as f:txt = f.readlines()annotations = [line.strip() for line in txt if len(line.strip().split()[1:]) != 0]np.random.shuffle(annotations)return annotations

1.3 数据增强

该工程使用的数据增强有3中方式:随机翻转、随机裁剪、随机平移


1.3.1 图片效果展示

原图:

随机翻转

随机裁剪(观察前后两张图,能看到图片被裁减了)

随机平移


1.3.2 具体实现代码

  • 随机翻转
def random_horizontal_flip(self, image, bboxes):if random.random() < 0.5:_, w, _ = image.shapeimage = image[:, ::-1, :]bboxes[:, [0,2]] = w - bboxes[:, [2,0]]return image, bboxes
  • 随机裁剪
def random_crop(self, image, bboxes):if random.random() < 0.5:h, w, _ = image.shape# print(image.shape)# 求图片中所有框的最小凸集的左上角和右下角max_bbox = np.concatenate([np.min(bboxes[:, 0:2], axis=0), np.max(bboxes[:, 2:4], axis=0)], axis=-1)# 获取【最小凸集的左上角】与【图片的左上角】的距离# 【最小凸集的右下角】与【图片的右下角】的距离max_l_trans = max_bbox[0]max_u_trans = max_bbox[1]max_r_trans = w - max_bbox[2]max_d_trans = h - max_bbox[3]# 随机获取裁剪边框的边界值crop_xmin = max(0, int(max_bbox[0] - random.uniform(0, max_l_trans)))crop_ymin = max(0, int(max_bbox[1] - random.uniform(0, max_u_trans)))crop_xmax = max(w, int(max_bbox[2] + random.uniform(0, max_r_trans)))crop_ymax = max(h, int(max_bbox[3] + random.uniform(0, max_d_trans)))# 对图像进行裁剪image = image[crop_ymin : crop_ymax, crop_xmin : crop_xmax]bboxes[:, [0, 2]] = bboxes[:, [0, 2]] - crop_xminbboxes[:, [1, 3]] = bboxes[:, [1, 3]] - crop_yminreturn image, bboxes
  • 随机平移
def random_translate(self, image, bboxes):if random.random() < 0.5:h, w, _ = image.shape# 求图片中所有框的最小凸集的左上角和右下角max_bbox = np.concatenate([np.min(bboxes[:, 0:2], axis=0), np.max(bboxes[:, 2:4], axis=0)], axis=-1)# 获取【最小凸集的左上角】与【图片的左上角】的距离# 【最小凸集的右下角】与【图片的右下角】的距离max_l_trans = max_bbox[0]max_u_trans = max_bbox[1]max_r_trans = w - max_bbox[2]max_d_trans = h - max_bbox[3]# 对图像进行仿射变换,这里只用到了平移,未添加旋转。这里的(tx,ty)的取值需要注意# 当(tx,ty) = (-(max_l_trans - 1),-(max_u_trans - 1)),目标的最小凸集的左上角与变换后的图片的左上角重合# 当(tx,ty) = ((max_r_trans - 1), (max_d_trans - 1)),目标的最小凸集的右下角与变换后的图片的右下角重合tx = random.uniform(-(max_l_trans - 1), (max_r_trans - 1))ty = random.uniform(-(max_u_trans - 1), (max_d_trans - 1))M = np.array([[1, 0, tx], [0, 1, ty]])image = cv2.warpAffine(image, M, (w, h))bboxes[:, [0, 2]] = bboxes[:, [0, 2]] + txbboxes[:, [1, 3]] = bboxes[:, [1, 3]] + tyreturn image, bboxes

1.4 缩放和填充图片到target_shape

已知神经网络设定的输入数据的大小为target_shape。 def parse_annotation():将图片缩放并填充到target_size,并以相同缩放或填充规则处理bboxes,此时bboxes为【左上角-右下角】的形式。

具体缩放填充的方式为:

  • 缩放:计算图片的长边与target_shape的比值,然后用该比值对原图进行缩放(这样会保持原图的长宽原有比例)
  • 填充:然后将图片的短边填充到target_shape
   def image_preporcess(image, target_size, gt_boxes=None):image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB).astype(np.float32)ih, iw    = target_sizeh,  w, _  = image.shapescale = min(iw/w, ih/h)   # 获取长边与target_shape的比值nw, nh  = int(scale * w), int(scale * h)  # 获取缩放后的图片的shapeimage_resized = cv2.resize(image, (nw, nh)) # 缩放图片image_paded = np.full(shape=[ih, iw, 3], fill_value=128.0) # 创建个target_shape大小的图片,并用128填充dw, dh = (iw - nw) // 2, (ih-nh) // 2 image_paded[dh:nh+dh, dw:nw+dw, :] = image_resized # 将缩放后的图片放在image_paded的中间位置image_paded = image_paded / 255.   # 将图片除以255,进行归一化if gt_boxes is None:return image_padedelse:  # 将缩放和填充的操作,应用到bboxes上gt_boxes[:, [0, 2]] = gt_boxes[:, [0, 2]] * scale + dwgt_boxes[:, [1, 3]] = gt_boxes[:, [1, 3]] * scale + dhreturn image_paded, gt_boxes

1.5 制作神经网络所需的label

def preprocess_true_boxes()该函数返回神经网络的标签,一个尺度下的输出标签的shape为 (batch_size, output_size, output_size, 3, 5+num_class)。其中,3:表示一个grid cell 使用3个anchorbox;5+num_class:4维的位置信息,1维的置信度,num_class是类别的onehot形式。

  • yolov3有3个尺度的输出模块,分别对输入大小进行了8、16、32倍的降采样。每个尺度下有3个anchorbox进行预测。
  • 分别在3个输出尺度下,计算【输出尺度下的anchor_boxes数值】和【缩小的真实框】的iou。
  • 当iou>0.3时,将【真实box-输入尺度上的数值,置信度,分类的onehot】保存在对用anchorbox负责维度项,否则对应label位置数值为0。
    当所有的iou都小于0.3时,选择最大iou的【真实box-输入尺度上的数值,置信度,分类的onehot】保存到相应位置。
   def preprocess_true_boxes(self, bboxes):# label:保存着神经网络的标签label = [np.zeros((self.train_output_sizes[i], self.train_output_sizes[i], self.anchor_per_scale,5 + self.num_classes)) for i in range(3)]# bboxes_xywh: 对于每个输出尺度,如果有进行预测真实框,就将所有的bbox_xywh信息放入bboxes_xywhbboxes_xywh = [np.zeros((self.max_bbox_per_scale, 4)) for _ in range(3)]bbox_count = np.zeros((3,))for bbox in bboxes:# 框的【左上角右下角】坐标信息bbox_coor = bbox[:4]# 框的分类信息bbox_class_ind = bbox[4]# 框的标签向量中的分类信息的onehot形式onehot = np.zeros(self.num_classes, dtype=np.float)onehot[bbox_class_ind] = 1.0# 对onehot进行smooth平滑,减小过拟合,增加泛化性uniform_distribution = np.full(self.num_classes, 1.0 / self.num_classes)deta = 0.01smooth_onehot = onehot * (1 - deta) + deta * uniform_distribution# 将真实框bboxes,从【左上角右下角】的形式 转换为(x,y,w,h)形式bbox_xywh = np.concatenate([(bbox_coor[2:] + bbox_coor[:2]) * 0.5, bbox_coor[2:] - bbox_coor[:2]], axis=-1)# 将target_size下的物体box,缩小到3个输出尺度下的box值           bbox_xywh_scaled = 1.0 * bbox_xywh[np.newaxis, :] / self.strides[:, np.newaxis]iou = []exist_positive = False# 在3个输出尺度下迭代处理# 这里要加强理解numpy的广播操作for i in range(3):anchors_xywh = np.zeros((self.anchor_per_scale, 4))anchors_xywh[:, 0:2] = np.floor(bbox_xywh_scaled[i, 0:2]).astype(np.int32) + 0.5anchors_xywh[:, 2:4] = self.anchors[i]iou_scale = self.bbox_iou(bbox_xywh_scaled[i][np.newaxis, :], anchors_xywh)iou.append(iou_scale)iou_mask = iou_scale > 0.3if np.any(iou_mask):xind, yind = np.floor(bbox_xywh_scaled[i, 0:2]).astype(np.int32)# 这里的iou_mask 的使用,当iou>0.3的时候进行赋值label[i][yind, xind, iou_mask, :] = 0label[i][yind, xind, iou_mask, 0:4] = bbox_xywhlabel[i][yind, xind, iou_mask, 4:5] = 1.0label[i][yind, xind, iou_mask, 5:] = smooth_onehotbbox_ind = int(bbox_count[i] % self.max_bbox_per_scale)bboxes_xywh[i][bbox_ind, :4] = bbox_xywhbbox_count[i] += 1exist_positive = True# 如果所有的真实框与anchorbox的iou<0.3,选择最大的iou的真实框进行保存if not exist_positive:best_anchor_ind = np.argmax(np.array(iou).reshape(-1), axis=-1)best_detect = int(best_anchor_ind / self.anchor_per_scale)best_anchor = int(best_anchor_ind % self.anchor_per_scale)xind, yind = np.floor(bbox_xywh_scaled[best_detect, 0:2]).astype(np.int32)label[best_detect][yind, xind, best_anchor, :] = 0label[best_detect][yind, xind, best_anchor, 0:4] = bbox_xywhlabel[best_detect][yind, xind, best_anchor, 4:5] = 1.0label[best_detect][yind, xind, best_anchor, 5:] = smooth_onehotbbox_ind = int(bbox_count[best_detect] % self.max_bbox_per_scale)bboxes_xywh[best_detect][bbox_ind, :4] = bbox_xywhbbox_count[best_detect] += 1label_sbbox, label_mbbox, label_lbbox = labelsbboxes, mbboxes, lbboxes = bboxes_xywhreturn label_sbbox, label_mbbox, label_lbbox, sbboxes, mbboxes, lbboxes

YOLOv3 代码详解(2) —— 数据处理 dataset.py解析:输入图片增强、制作模型的每层输出的标签相关推荐

  1. yolov3代码详解(七)

    Pytorch | yolov3代码详解七 test.py test.py from __future__ import divisionfrom models import * from utils ...

  2. Keras YOLOv3代码详解(三):目标检测的流程图和源代码+中文注释

    Keras YOLOv3源代码下载地址:https://github.com/qqwweee/keras-yolo3 YOLOv3论文地址:https://pjreddie.com/media/fil ...

  3. yoloV3代码详解(注释)

    原文链接:https://www.cnblogs.com/hujinzhou/p/guobao_2020_3_13.html yolo3各部分代码详解(超详细) </h1><div ...

  4. maskrcnn_benchmark 代码详解之 roi_box_feature_extractors.py

    前言: 在经过RPN层之后,网络会生成多个预测边框(proposal), 这时候需要对这些边框进行RoI池化,使之成为尺度一致的特征.接下来就需要对这些特征进行进一步的特征提取,这就需要用到roi_b ...

  5. maskrcnn_benchmark 代码详解之 poolers.py

    前言: 在目标检测的深度网络中最后一个步骤就是RoI层,其中RoI Pooling会实现将RPN提取的各种形状的边框进行池化,从而形成统一尺度的特征层,这一工程中将涉及到ROIAlign操作.Pool ...

  6. yolov3代码详解_代码资料

    faster RCNN TensorFlow版本: 龙鹏:[技术综述]万字长文详解Faster RCNN源代码(一) buptscdc:tensorflow 版faster rcnn代码理解(1) l ...

  7. maskrcnn-benchmark 代码详解之 resnet.py

    1Resnet 结构 Resnet 一般分为5个卷积(conv)层,每一层为一个stage.其中每一个stage中由不同数量的相同的block(区块)构成,这些区块的个数就是block_count, ...

  8. maskrcnn_benchmark 代码详解之 boxlist_ops.py

    前言: 与Bounding Box有关的操作有很多,例如对边框列表进行非极大线性抑制.去除过小的边框.计算边框之间的Iou以及对两个边框列表进行合并等操作.在maskrcnn_benchmark中,这 ...

  9. pytorch yolov3 代码详解_PyTorch C++ libtorch的使用方法(1)-nightly 版本的 libtorch

    问题描述: 按照PyTorch中文教程的[ 在 C++ 中加载 PYTORCH 模型 ]一文,尝试调用 PyTorch模型. 1. 例子来源 在 C++ 中加载 PYTORCH 模型 我是使用Qt新建 ...

最新文章

  1. JSP标签JSTL(4)--URL
  2. android简单点餐系统_微信点餐和扫码点餐系统能为商家带来什么?
  3. Java集合的线程安全用法
  4. 如何查html病毒svchost.exe,小编教你在Win7系统中检查svchost.exe进程是否为病毒的方法步骤...
  5. vue比php的优势,vue.js的优势是什么
  6. MySQL 5.7.18的安装与主从复制
  7. extjs 网站首页table布局,秀一下
  8. NIM(Network Installation Manager)使用一例(mksysb)
  9. bluecam连接步骤说明_智能门锁安装步骤分享
  10. 1010 Radix(25 分)
  11. (转)jquery对表单元素的取值和赋值
  12. 软件测试中测试用例的简单案例
  13. 华为交换机路由器最新默认密码大全
  14. 2019年研究生数学建模竞赛优秀论文汇总
  15. IOTE 2019物联网嘉年华在深圆满落幕
  16. GIS大数据可视化分析工具
  17. Android开发本地及网络Mp3音乐播放器(十七)已存在歌曲歌词下载
  18. 《红楼梦》人物关系有多复杂?一张图帮你理清楚!
  19. Python 初学者趣味练习题汇编(共42题,中文版)
  20. USB转串口驱动应用于macbook

热门文章

  1. DB2-SQLSTATE 消息大全---[IBM官方]
  2. “新主”难救美赞臣?
  3. 有4个圆塔,圆心分别为(2,2)……今输入任一点坐标,求该点坐标建筑物高度。
  4. 微信分享链接不显示缩略图
  5. oracle 毫秒时间换mysql_Mysql与Oracle常用时间格式的转换
  6. 进入html+css世界的正确姿势
  7. 宏基因组(鸟枪法测序)—微生物同源基因引物设计
  8. python,你也和小猪佩奇一样社会了!
  9. ubuntu20.04虚拟机使用水星mw150us无线usb接口网卡
  10. 文创产品的毕业论文设计要怎么写呢?