yolov3代码解读(3)

yolo3--utils.py
###在模型训练时进行数据处理的工具文件,共3个函数

"""Miscellaneous utility functions."""  ###其他实用功能

from functools import reduce  ##为了实用reduce函数

from PIL import Image   ###PIL(Python Image Library)是python的第三方图像处理库,Image是PIL中的核心类
import numpy as np
from matplotlib.colors import rgb_to_hsv, hsv_to_rgb


###函数1:参数为多个函数名,按照reduce的功能执行,把前一个函数的结果作为下一个函数的输入,直到最后执行完毕
def compose(*funcs):
    """Compose arbitrarily many functions, evaluated left to right.
       ###组合任意多个函数,从左向右依次求取值
    Reference: https://mathieularose.com/function-composition-in-python/
    """
    ###reduce函数用途,对迭代对象中的元素从左至右两两送入参数1传入的function中进行运算 (参数2是迭代对象,该函数一共就两个函数)
    ### 下面的reduce函数在这里实现的是每一层网络计算过后参数传递给下一层 ,funcs迭代对象中的元素是函数名称,然后依次从左至右取函数名称送入reduce函数第一个参数所表示的函数中,,
    ####lambda *a,**kw:g(f(*a,**kw)) ,lambda表达式返回一个函数对象,例如这里*a,**kw是参数,:后面的是参数的计算方式(*a是卷积上一层输出的激活值,**kw是这一层卷积运算的权重矩阵,因为是矩阵所以二维数组,f函数是正常的卷基层运算,g函数是对卷积运算后的激活运算)
    #### 这里lambda f,g:lambda *a,**kw:g(f(*a,**kw))   是将f,g作为参数送入后面(lambda *a,**kw:g(f(*a,*kw)))这个运算中,而后面这个运算是将*a,**kw作为参数送入到g(f(*a,**kw))这个运算中,,,其实就是函数嵌套的另一种表示
    # return lambda x: reduce(lambda v, f: f(v), funcs, x)
    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:使用padding方法将所有图片变为一个尺寸的,并且要保证纵横比不变
def letterbox_image(image, size):
    '''resize image with unchanged aspect ratio using padding'''
    iw, ih = image.size   ###原始尺寸
    w, h = size           ###转换为416*416
    scale = min(w/iw, h/ih)   ###转换比例 ,nw,nh是转换之后的图像尺寸,416*416下的,其他的空白部分像素填灰色
    nw = int(iw*scale)
    nh = int(ih*scale)

    image = image.resize((nw,nh), Image.BICUBIC)    ####BICUBIC是resize的插值方法
    new_image = Image.new('RGB', size, (128,128,128))   ###Image.new(mode,size,color=0)
    new_image.paste(image, ((w-nw)//2, (h-nh)//2))     ###img.paste(im,box=None,mask=None)  就是将im粘贴到img上box位置处(box=粘贴位置的左上角坐标)  //运算符是将除法结果向下取整
    return new_image



####函数3:产生随机数
def rand(a=0, b=1):
    return np.random.rand()*(b-a) + a



####函数4:获取真实的数据,根据输入的尺寸对原始数据进行缩放处理得到input_shape大小的数据图片,随机进行图片的翻转,标记数据也需要根据比例进行改变
####annotation_line:单条图片信息的列表
def 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'''###实时数据增强的随机预处理
    line = annotation_line.split()
    image = Image.open(line[0])  ##读取图片
    iw, ih = image.size   ##原始图片尺寸
    h, w = input_shape    ##获取
    box = np.array([np.array(list(map(int,box.split(',')))) for box in line[1:]])   ###图像中目标框信息每个框包含4个位置大小信息和一个类别信息


######如果不随机变换原始数据,进行下述部分
    if not random:
        # resize image   ###将原始图像转换为416*416的,要做的内容和letterbox_image函数一样
        scale = min(w/iw, h/ih)
        nw = int(iw*scale)
        nh = int(ih*scale)
        dx = (w-nw)//2
        dy = (h-nh)//2
        image_data=0
        if 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
        box_data = np.zeros((max_boxes,5)) 
        if len(box)>0:
            np.random.shuffle(box) ###shuffle函数是将box中的元素进行随机排列
            if len(box)>max_boxes: box = box[:max_boxes]   ###这里最多只取20个,也就是这里规定只取图像中众多目标框的20个
            box[:, [0,2]] = box[:, [0,2]]*scale + dx     ###对目标框信息按比例进行缩放和移位,这里处理后的坐标是相对于416*416尺寸的,即input_shape
            box[:, [1,3]] = box[:, [1,3]]*scale + dy
            box_data[:len(box)] = box

        return image_data, box_data   ###image_data是resize之后的图像(原始图像的纵横比不能改变),box_data是resize之后的目标边界框信息(注意这里边界框是相对于416*416坐标的)

#####如果是处于数据增强阶段 ,则要随机改变图像尺寸和颜色空间取值范围,执行下述部分,
#####这使用到的数据增强手段有:
    # resize image
    ##1.缩放图片
    ###随机生成宽高比
    new_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
    ###2.平移变换
    ###随机的把变换后的图像放置在灰色图像上,随机水平位移
    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
    ###3.翻转
    ####是否翻转图像
    flip = rand()<.5
    if flip: image = image.transpose(Image.FLIP_LEFT_RIGHT)

    # distort image
    ###颜色抖动
    ####在hsv坐标域中,改变图片的颜色范围,hue值相加,sat和vat相乘
    ####先由RGB转HSV,再由HSV转RGB,添加若干错误判断,避免范围过大
    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)
    x = rgb_to_hsv(np.array(image)/255.)
    x[..., 0] += hue
    x[..., 0][x[..., 0]>1] -= 1
    x[..., 0][x[..., 0]<0] += 1
    x[..., 1] *= sat
    x[..., 2] *= val
    x[x>1] = 1
    x[x<0] = 0
    image_data = hsv_to_rgb(x) # numpy array, 0 to 1

    # correct boxes
    ####在经过缩放和水平变换之后,box的左上角信息也需要有相应的变化#####
    ####将所有的图片变换,增加至质检框中,并包含若干异常处理,避免变换之后的值过大或过小,去除异常box
    ####变换图像中目标框的大小位置信息(根据随机变换的信息变换比例)
    box_data = np.zeros((max_boxes,5))
    if len(box)>0:
        np.random.shuffle(box)
        ##变换所有目标尺寸
        box[:, [0,2]] = box[:, [0,2]]*nw/iw + dx
        box[:, [1,3]] = box[:, [1,3]]*nh/ih + dy
        if flip: box[:, [0,2]] = w - box[:, [2,0]]
        box[:, 0:2][box[:, 0:2]<0] = 0
        box[:, 2][box[:, 2]>w] = w
        box[:, 3][box[:, 3]>h] = h
        box_w = box[:, 2] - box[:, 0]
        box_h = box[:, 3] - box[:, 1]
        box = box[np.logical_and(box_w>1, box_h>1)] # discard invalid box
        if len(box)>max_boxes: box = box[:max_boxes]
        box_data[:len(box)] = box

return image_data, box_data





######from import 是从模块中导入一个指定的部分到当前命名空间中

猜你喜欢

转载自blog.csdn.net/lyychlj/article/details/107246375