前言
以下内容仅为个人在学习人工智能中所记录的笔记,先将目标识别算法yolo系列的整理出来分享给大家,供大家学习参考。
本文仅对YOLOV3代码中关键部分进行了注释,未掌握基础代码的铁汁可以自己百度一下。
若文中内容有误,希望大家批评指正。
资料下载
YOLOV3论文下载地址:YOLOv3:An Incremental Improvement
回顾
YOLO V1:【YOLO系列】YOLO V1论文思想详解
YOLO V2:【YOLO系列】YOLO V2论文思想详解
YOLO V3:【YOLO系列】 YOLOv3论文思想详解
项目地址
YOLOV3 keras版本:下载地址
YOLOV3 Tensorflow版本:下载地址
YOLOV3 Pytorch版本:下载地址
Gitee仓库
YOLOV3 各版本:yolov3各版本
YOLO V3代码详解
YOLO V3代码详解(一):【YOLO系列】YOLOv3代码详解(一):主脚本yolo_video.py
YOLO V3代码详解(二):【YOLO系列】YOLOv3代码详解(二):检测脚本yolo.py
YOLO V3代码详解(三):【YOLO系列】YOLOv3代码详解(三):训练脚本train.py
YOLO V3代码详解(四):【YOLO系列】YOLOv3代码详解(四):模型脚本model.py
本文主要基于keras版本进行讲解
话不多说,直接上代码
一、代码详解
1、定义递归操作函数
将funcs中的元素进行某种累积或合并
def compose(*funcs):"""Compose arbitrarily many functions, evaluated left to right.Reference: https://mathieularose.com/function-composition-in-python/"""# return lambda x: reduce(lambda v, f: f(v), funcs, x)# 递归操作,将funcs中的元素进行某种累积或合并if funcs:return reduce(lambda f, g: lambda *a, **kw: g(f(*a, **kw)), funcs)else:raise ValueError('Composition of empty sequence not supported.')
2、输入图片的尺寸处理
(1)将输入的图片按最长边确定一个比例,然后按比例缩放(采样方法:BICUBIC)图片;
(2) 再生成一个用“绝对灰”R128-G128-B128填充的416x416新图片后将缩放后的输入图片粘贴上去,粘贴不到的部分保留为灰色。
def letterbox_image(image, size):"""resize image with unchanged aspect ratio using padding"""# 将输入的图片按最长边确定一个比例,然后按比例缩放(采样方法:BICUBIC)图片# 再生成一个用“绝对灰”R128-G128-B128填充的416x416新图片后将缩放后的输入图片粘贴上去,粘贴不到的部分保留为灰色iw, ih = image.sizew, h = size# 选择一个最长边作为缩放比例scale = min(w/iw, h/ih)nw = int(iw*scale)nh = int(ih*scale)image = image.resize((nw, nh), Image.BICUBIC)new_image = Image.new('RGB', size, (128, 128, 128))new_image.paste(image, ((w-nw)//2, (h-nh)//2))return new_image
3、定义get_random_data()函数
实现图片数据增强
def rand(a=0, b=1):# 获得范围为[a,b]的随机数return np.random.rand()*(b-a) + adef get_random_data(annotation_line, input_shape, random=True, max_boxes=20, jitter=.3, hue=.1, sat=1.5, val=1.5, proc_img=True):"""random preprocessing for real-time data augmentation通过随机缩放的方式调整图片的尺寸至(416, 416),随机改变图片的RGB、翻转方式来实现图片数据增强"""line = annotation_line.split()image = Image.open(line[0]) # 打开需要训练的图片iw, ih = image.size # 获取图片的宽度与高度h, w = input_shape # (416, 416)# 获取Gound Truth框box = np.array([np.array(list(map(int, box.split(',')))) for box in line[1:]])if not random:# 这里是将输入的图片大小调整为(416, 416),方法与yolo.py中提到的一致scale = min(w/iw, h/ih)nw = int(iw*scale)nh = int(ih*scale)dx = (w-nw)//2dy = (h-nh)//2image_data = 0if proc_img:image = image.resize((nw, nh), Image.BICUBIC)new_image = Image.new('RGB', (w, h), (128, 128, 128))new_image.paste(image, (dx, dy))image_data = np.array(new_image)/255.# correct boxes# 图片大小被改变后,Gound Truth框也需要调整box_data = np.zeros((max_boxes, 5))if len(box) > 0:np.random.shuffle(box)if len(box) > max_boxes: box = box[:max_boxes]box[:, [0, 2]] = box[:, [0, 2]]*scale + dxbox[:, [1, 3]] = box[:, [1, 3]]*scale + dybox_data[:len(box)] = boxreturn image_data, box_data# resize image# 随机得到一个新的比例,来缩放图片的尺寸# new_ar= w/h * rand(0.7, 1.3)/rand(0.7, 1.3),rand(0.7, 1.3)=np.random.rand()*(1.3-0.7) + 0.7=[0.7,1.3]# new_ar>1说明w>h,new_ar<1说明w<hnew_ar = w/h * rand(1-jitter, 1+jitter)/rand(1-jitter, 1+jitter)scale = rand(.25, 2)if new_ar < 1:nh = int(scale*h)nw = int(nh*new_ar)else:nw = int(scale*w)nh = int(nw/new_ar)image = image.resize((nw, nh), Image.BICUBIC)# place image# 生成一个灰度的(416, 416)图片,再将缩放后的输入图片粘贴到这个灰色图片上,粘贴位置随机生成dx = int(rand(0, w-nw))dy = int(rand(0, h-nh))new_image = Image.new('RGB', (w, h), (128, 128, 128))new_image.paste(image, (dx, dy))image = new_image# flip image or not# 随机翻转图片,左边与右边翻转flip = rand() < .5if flip: image = image.transpose(Image.FLIP_LEFT_RIGHT)# distort image# 随机生成hsv值hue = rand(-hue, hue)sat = rand(1, sat) if rand() < .5 else 1/rand(1, sat)val = rand(1, val) if rand() < .5 else 1/rand(1, val)# 将图片的RGB值转成hsv值x = rgb_to_hsv(np.array(image)/255.)# 改变图片的hsv值x[..., 0] += huex[..., 0][x[..., 0] > 1] -= 1x[..., 0][x[..., 0] < 0] += 1x[..., 1] *= satx[..., 2] *= val# 由于hsv值范围为[0, 1],因此将超出这个范围的值调整至范围内x[x > 1] = 1x[x < 0] = 0# 在将hsv值转成RGBimage_data = hsv_to_rgb(x) # numpy array, 0 to 1# correct boxes# 图片大小、方向被改变后,Gound Truth框也需要调整box_data = np.zeros((max_boxes, 5))if len(box) > 0:# 移动Gound Truth框np.random.shuffle(box)box[:, [0, 2]] = box[:, [0, 2]]*nw/iw + dxbox[:, [1, 3]] = box[:, [1, 3]]*nh/ih + dy# 如果图片翻转,重新计算Gound Truth框x值,if flip: box[:, [0, 2]] = w - box[:, [2, 0]]# 判断调整后的Gound Truth框是否超出边界box[:, 0:2][box[:, 0:2] < 0] = 0box[:, 2][box[:, 2] > w] = wbox[:, 3][box[:, 3] > h] = h# 计算调整后的GT框的高和宽box_w = box[:, 2] - box[:, 0]box_h = box[:, 3] - box[:, 1]# 删除box_w或者box_h<1的框box = box[np.logical_and(box_w > 1, box_h > 1)] # discard invalid boxif len(box) > max_boxes: box = box[:max_boxes]box_data[:len(box)] = boxreturn image_data, box_data