Data_Aug(数据增强)

1 python+OpenCV (备用)

github:https://github.com/tranleanh/data-augmentation

wechat:https://mp.weixin.qq.com/s/UFx4AGNEF_qBTBNYsLfZXQ

datagrep官网_github

datagrep官网_html_02

import os
import cv2
import numpy as np
import random


def file_lines_to_list(path):
    '''
    ### Convert Lines in TXT File to List ###
    path: path to file
    '''
    with open(path) as f:
        content = f.readlines()
    content = [(x.strip()).split() for x in content]
    return content


def get_file_name(path):
    '''
    ### Get Filename of Filepath ###
    path: path to file
    '''
    basename = os.path.basename(path)
    onlyname = os.path.splitext(basename)[0]
    return onlyname


def write_anno_to_txt(boxes, filepath):
    '''
    ### Write Annotation to TXT File ###
    boxes: format [[obj x1 y1 x2 y2],...]
    filepath: path/to/file.txt
    '''
    txt_file = open(filepath, "w")
    for box in boxes:
        print(box[0], int(box[1]), int(box[2]), int(box[3]), int(box[4]), file=txt_file)
    txt_file.close()


def cutout(img, gt_boxes, amount=0.5):
    '''
    ### Cutout ###
    img: image
    gt_boxes: format [[obj x1 y1 x2 y2],...]
    amount: num of masks / num of objects 
    '''
    out = img.copy()
    ran_select = random.sample(gt_boxes, round(amount*len(gt_boxes)))

    for box in ran_select:
        x1 = int(box[1])
        y1 = int(box[2])
        x2 = int(box[3])
        y2 = int(box[4])
        mask_w = int((x2 - x1)*0.5)
        mask_h = int((y2 - y1)*0.5)
        mask_x1 = random.randint(x1, x2 - mask_w)
        mask_y1 = random.randint(y1, y2 - mask_h)
        mask_x2 = mask_x1 + mask_w
        mask_y2 = mask_y1 + mask_h
        cv2.rectangle(out, (mask_x1, mask_y1), (mask_x2, mask_y2), (0, 0, 0), thickness=-1)
    return out


def colorjitter(img, cj_type="b"):
    '''
    ### Different Color Jitter ###
    img: image
    cj_type: {b: brightness, s: saturation, c: constast}
    '''
    if cj_type == "b":
        # value = random.randint(-50, 50)
        value = np.random.choice(np.array([-50, -40, -30, 30, 40, 50]))
        hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
        h, s, v = cv2.split(hsv)
        if value >= 0:
            lim = 255 - value
            v[v > lim] = 255
            v[v <= lim] += value
        else:
            lim = np.absolute(value)
            v[v < lim] = 0
            v[v >= lim] -= np.absolute(value)

        final_hsv = cv2.merge((h, s, v))
        img = cv2.cvtColor(final_hsv, cv2.COLOR_HSV2BGR)
        return img
    
    elif cj_type == "s":
        # value = random.randint(-50, 50)
        value = np.random.choice(np.array([-50, -40, -30, 30, 40, 50]))
        hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
        h, s, v = cv2.split(hsv)
        if value >= 0:
            lim = 255 - value
            s[s > lim] = 255
            s[s <= lim] += value
        else:
            lim = np.absolute(value)
            s[s < lim] = 0
            s[s >= lim] -= np.absolute(value)

        final_hsv = cv2.merge((h, s, v))
        img = cv2.cvtColor(final_hsv, cv2.COLOR_HSV2BGR)
        return img
    
    elif cj_type == "c":
        brightness = 10
        contrast = random.randint(40, 100)
        dummy = np.int16(img)
        dummy = dummy * (contrast/127+1) - contrast + brightness
        dummy = np.clip(dummy, 0, 255)
        img = np.uint8(dummy)
        return img


def noisy(img, noise_type="gauss"):
    '''
    ### Adding Noise ###
    img: image
    cj_type: {gauss: gaussian, sp: salt & pepper}
    '''
    if noise_type == "gauss":
        image=img.copy() 
        mean=0
        st=0.7
        gauss = np.random.normal(mean,st,image.shape)
        gauss = gauss.astype('uint8')
        image = cv2.add(image,gauss)
        return image
    
    elif noise_type == "sp":
        image=img.copy() 
        prob = 0.05
        if len(image.shape) == 2:
            black = 0
            white = 255            
        else:
            colorspace = image.shape[2]
            if colorspace == 3:  # RGB
                black = np.array([0, 0, 0], dtype='uint8')
                white = np.array([255, 255, 255], dtype='uint8')
            else:  # RGBA
                black = np.array([0, 0, 0, 255], dtype='uint8')
                white = np.array([255, 255, 255, 255], dtype='uint8')
        probs = np.random.random(image.shape[:2])
        image[probs < (prob / 2)] = black
        image[probs > 1 - (prob / 2)] = white
        return image


def filters(img, f_type = "blur"):
    '''
    ### Filtering ###
    img: image
    f_type: {blur: blur, gaussian: gaussian, median: median}
    '''
    if f_type == "blur":
        image=img.copy()
        fsize = 9
        return cv2.blur(image,(fsize,fsize))
    
    elif f_type == "gaussian":
        image=img.copy()
        fsize = 9
        return cv2.GaussianBlur(image, (fsize, fsize), 0)
    
    elif f_type == "median":
        image=img.copy()
        fsize = 9
        return cv2.medianBlur(image, fsize)


def randomcrop(img, gt_boxes, scale=0.5):
    '''
    ### Random Crop ###
    img: image
    gt_boxes: format [[obj x1 y1 x2 y2],...]
    scale: percentage of cropped area
    '''
    
    # Crop image
    height, width = int(img.shape[0]*scale), int(img.shape[1]*scale)
    x = random.randint(0, img.shape[1] - int(width))
    y = random.randint(0, img.shape[0] - int(height))
    cropped = img[y:y+height, x:x+width]
    resized = cv2.resize(cropped, (img.shape[1], img.shape[0]))
    
    # Modify annotation
    new_boxes=[]
    for box in gt_boxes:
        obj_name = box[0]
        x1 = int(box[1])
        y1 = int(box[2])
        x2 = int(box[3])
        y2 = int(box[4])
        x1, x2 = x1-x, x2-x
        y1, y2 = y1-y, y2-y
        x1, y1, x2, y2 = x1/scale, y1/scale, x2/scale, y2/scale
        if (x1<img.shape[1] and y1<img.shape[0]) and (x2>0 and y2>0):
            if x1<0: x1=0
            if y1<0: y1=0
            if x2>img.shape[1]: x2=img.shape[1]
            if y2>img.shape[0]: y2=img.shape[0]
            new_boxes.append([obj_name, x1, y1, x2, y2])
    return resized, new_boxes



if __name__ == "__main__":

    # Load Image and Its Annotation
    img_name = "tr03-14-18-1-FRONT"
    img_path = f"data/{img_name}.jpg"
    anno_apth = f"data/{img_name}.txt"

    image = cv2.imread(img_path)
    gt_boxes = file_lines_to_list(anno_apth)

    # Create Output Folder
    dir_path = f"outputs"
    if not os.path.exists(dir_path):
        os.makedirs(dir_path)

    # Cutout
    cutout = cutout(image, gt_boxes, amount=0.5)
    cv2.imwrite(f"{dir_path}/{img_name}_cutout.jpg", cutout)

    # ColorJitter
    b_img = colorjitter(image, cj_type="b")
    s_img = colorjitter(image, cj_type="s")
    c_img = colorjitter(image, cj_type="c")
    cv2.imwrite(f"{dir_path}/{img_name}_brightness.jpg", b_img)
    cv2.imwrite(f"{dir_path}/{img_name}_saturation.jpg", s_img)
    cv2.imwrite(f"{dir_path}/{img_name}_contrast.jpg", c_img)

    # Adding Noise
    gaussn_img = noisy(image, noise_type="gauss")
    sp_img = noisy(image, noise_type="sp")
    cv2.imwrite(f"{dir_path}/{img_name}_gaussnoise.jpg", gaussn_img)
    cv2.imwrite(f"{dir_path}/{img_name}_spnoise.jpg", sp_img)

    # Filtering
    blur_img = filters(image, f_type = "blur")
    gaussf_img = filters(image, f_type = "gaussian")
    median_img = filters(image, f_type = "median")
    cv2.imwrite(f"{dir_path}/{img_name}_blur.jpg", blur_img)
    cv2.imwrite(f"{dir_path}/{img_name}_gaussblur.jpg", gaussf_img)
    cv2.imwrite(f"{dir_path}/{img_name}_median.jpg", median_img)

    # Random Crop
    rancrop, new_boxes = randomcrop(image, gt_boxes, scale=0.5)
    cv2.imwrite(f"{dir_path}/{img_name}_rancrop.jpg", rancrop)
    filepath = f"{dir_path}/{img_name}_rancrop.txt"
    write_anno_to_txt(new_boxes, filepath)

    print("Generating Done!")

View Code

datagrep官网_github_03

 

 

2 imague模块(推荐)

文档参考:

Doc:https://imgaug.readthedocs.io/en/latest/source/installation.html

Github:https://github.com/aleju/imgaug

示例:

datagrep官网_github

datagrep官网_html_02

'''
该程序用于查看imgaug中的增广算法的作用效果
1. 修改代码第四行的 op 后的增广算法
2. 修改操作的图片的路径
3. 通过显示的图像可以观察到增广效果
4. 该效果是经过op 和 op_ 两种操作叠加而成的图片
5. op_ 操作为剪切和填充
6. 如果想单独看 op 效果可以将代码12行[op, op_]中的op_去掉
'''

from imgaug import augmenters as iaa
import imgaug as ia
import matplotlib.pyplot as plt
import cv2

op = iaa.CoarseDropout(0.02, size_percent=0.15, per_channel=0.5) # 可修改成别的增广算法

op_ = iaa.CropAndPad( #剪切和填充
    percent=(0, 0.2),
    pad_mode=["constant", "edge"],
    pad_cval=(0, 255)
)

seq = iaa.Sequential([ op , op_])
imgList = [cv2.imread("images/001.jpg"),cv2.imread("images/002.jpg")] # 修改成图片路径
img_aug = seq.augment_images(imgList)

plt.figure(figsize=(8,8))
plt.subplot(2,2,1)
plt.imshow(cv2.cvtColor(imgList[0],cv2.COLOR_BGR2RGB))
plt.subplot(2,2,2)
plt.imshow(cv2.cvtColor(img_aug[0],cv2.COLOR_BGR2RGB))
plt.subplot(2,2,3)
plt.imshow(cv2.cvtColor(imgList[1],cv2.COLOR_BGR2RGB))
plt.subplot(2,2,4)
plt.imshow(cv2.cvtColor(img_aug[1],cv2.COLOR_BGR2RGB))
plt.show()

test_aug

datagrep官网_github

datagrep官网_html_02

"""
#链接:https://www.cnblogs.com/xxmmqg/p/13062556.html
#https://blog.csdn.net/lly1122334/article/details/88944589?utm_medium=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-1.nonecase&depth_1-utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-1.nonecase
"""
#github :https://github.com/aleju/imgaug
#Doc:https://imgaug.readthedocs.io/en/latest/source/examples_basics.html#a-standard-use-case


from imgaug import augmenters as iaa
import imgaug as ia
import os
import cv2

# 1定义多种增广操作,图片处理的不同操作
def img_operation_list():
    # 将图像转换为HSV,然后将每个像素的H值增加10到50
    op_1 = iaa.WithColorspace(
        to_colorspace="HSV",
        from_colorspace="RGB",
        children=iaa.WithChannels(0, iaa.Add((10, 50)))
    )

    op_2 = iaa.WithChannels(0, iaa.Add((10,50))) # BGR中的B值随机增加10-50,因为cv2读取的图片为BGR顺序,下同
    op_3 = iaa.WithChannels(1, iaa.Add((10,50))) # BGR中的G值随机增加10-50
    op_4 = iaa.WithChannels(2, iaa.Add((10,50))) # BGR中的R值随机增加10-50

    op_5 = iaa.WithChannels(0, iaa.Affine((10, 180))) # BGR中的B值随机旋转10-180°
    op_6 = iaa.WithChannels(1, iaa.Affine((10, 180))) # BGR中的G值随机旋转10-180°
    op_7 = iaa.WithChannels(2, iaa.Affine((10, 180))) # BGR中的R值随机旋转10-180°

    op_8 = iaa.Noop() # 无操作增广

    # 用黑色像素替换每个图像的每5行
    def img_func(images, random_state, parents, hooks):
        for img in images:
            img[::5] = 0
        return images
    def keypoint_func(keypoints_on_images, random_state, parents, hooks):
        return keypoints_on_images
    op_9 = iaa.Lambda(img_func, keypoint_func)

    # 裁剪并填充 负则裁剪,正则填充
    op_10 = iaa.CropAndPad(percent=(-0.2, 0.2))

    op_11 = iaa.CropAndPad(
        percent=(0, 0.2), # 每边填充0至20%
        pad_mode=["constant", "edge"], # 填充方式为恒定值(constant)或边缘值(edge)
        pad_cval=(0, 255) # 若为恒定值,则从0-255中随机取值
    )

    op_12 = iaa.CropAndPad(
        px=((0, 30), (0, 10), (0, 30), (0, 10)), # 顶部填充0-30px,右侧0-10px,底部0-30px,左侧0-10px
        pad_mode=ia.ALL,
        pad_cval=(0, 255)
    )

    op_13 = iaa.Fliplr() # 水平翻转所有图片
    op_14 = iaa.Flipud(0.2) # 垂直反转图片20%

    # 每个图像生成16到128个超像素。 用平均像素颜色替换每个超像素的概率在10到50%之间(每个图像采样一次)
    op_15 = iaa.Superpixels(p_replace=(0.1, 0.5), n_segments=(16, 128))

    # 将图像更改为灰度,并通过改变强度将其与原始图像叠加,有效地删除0到80%的颜色
    op_16 = iaa.Grayscale(alpha=(0.0, 0.8))

    op_17 = iaa.GaussianBlur(sigma=(0.0, 2.0)) # 高斯模糊,σ=0-2

    # 在图像中添加-50到50之间的随机值。
    #在50%的图像中,每个通道的值不同(3个采样值)。
    #在剩下的50%图像中,所有通道的值都相同
    op_18 = iaa.Add((-50, 50),per_channel=0.5)

    # 添加高斯噪音
    # 每个像素从正态分布N(0,s)采样一次,s对每个图像采样并在0和0.1 * 255之间变化
    op_19 = iaa.AdditiveGaussianNoise(scale=(0, 0.1*255))

    # 丢弃2%的图像,但是在具有原始大小的50%的图像的较低分辨率版本上执行此操作,丢弃2x2个正方形。
    # 此外,在50%的图像通道中执行此操作,以便仅将某些通道的信息设置为0,而其他通道保持不变
    op_20 = iaa.CoarseDropout(0.02, size_percent=0.15, per_channel=0.5)

    operation_list = [op_1, op_7, op_8, op_9, op_10,
                      op_11, op_12, op_13, op_14, op_16, op_17, op_18, op_19, op_20,]
    return operation_list

# 2将图片分批
#说明:图片数据量大时,可调用此方法实现分批效果
def img_batchs_list(imgpath_file,batch_size=10):
    """
    :param imgpath_file: 保存图片路径的txt文件
    :param batch_size: 每个batch大小默认为10
    :return: img_batchs:[[image1,image2,..image10],...]
    """
    with open(imgpath_file) as img_info:
        batch_size = batch_size # 每个batch的大小
        img_batchs = []
        flag = 0
        while True:
            if flag % batch_size == 0:
                img_batchs.append([])
            img = img_info.readline().strip("\n")
            if(img == ""):
                break
            img_batchs[-1].append(img)
            flag += 1
            if flag >=10000: # 避免进入死循环而必须重启项目
                break
        if img_batchs[-1] == []:
            img_batchs.pop()
        return img_batchs

# 3 数据增广
def data_augment(imagepath_text,NewImageDir,epochs=10,start_img_num=100):
    operation_list=img_operation_list()
    img_batchs=img_batchs_list(imagepath_text)
    print(img_batchs)
    img_name = start_img_num # 每张图片的名字,依次递增
    with open(imagepath_text,"a") as img_text:
        for epoch in range(epochs): # 原始图片循环增广epochs*4次
            for someof_n in [1, 1, 2, 2]: # 对应于SomeOf中的参数someof_n
                seq = iaa.SomeOf(someof_n, operation_list) # 使用的是SomeOf,区别于Sequential
                for batch_ind, img_batch in enumerate(img_batchs):
                    img_batch = [cv2.imread(path) for path in img_batch]
                    imgs_aug = seq.augment_images(img_batch)
                    for i, save_img in enumerate(imgs_aug):
                        save_img_path = fr"{NewImageDir}\{str(img_name)}.jpg" # 保存路径
                        cv2.imwrite(save_img_path, save_img) # 保存图片
                        img_text.write(save_img_path+" "+"\n") # 保存图像列表
                        print(save_img_path,"保存成功!")
                        img_name += 1
    print("Data augment Scccess!!")

#收集图片路径,生成txt文件
def generate_imgpath_txt(imgpath_dir,target_file):
    image_name_list = sorted(os.listdir(imgpath_dir))
    print("total image:", len(image_name_list))
    with open(file=target_file, mode="w", encoding="utf-8") as f:
        for imgname in image_name_list:
            if not imgname.endswith("jpg"):
                continue
            img_path = os.path.join(imgpath_dir, imgname)
            f.write(img_path + "\n")
    print("success!!")


if __name__ == '__main__':
    #1 生成图片路径文件
    #测试
    # imgpath_dir =r"D:\rsrc\code\script\images"
    # target_file =r"D:\rsrc\code\script\images\images.txt"
    # generate_imgpath_txt(imgpath_dir,target_file)

    imgpath_dir = r"D:\rsrc\data\coal-project\truck\shadunzi\JPEGImage"
    target_file = r"D:\rsrc\data\coal-project\truck\shadunzi\imgpath.txt"
    generate_imgpath_txt(imgpath_dir, target_file)

    # 2 数据增强处理
    print("Data Enhancement............")
    imagepath_text=target_file
    start_img_num=16 #新增图片开始序号
    epochs =5 # 原始图片增广epochs*4张
    NewImageDir=r"D:\rsrc\data\coal-project\truck\shadunzi\JPEGImage"
    data_augment(imagepath_text,NewImageDir,epochs,start_img_num)

data_augmentation

 

3 PP

飞浆数据增广参考:https://www.paddlepaddle.org.cn/tutorials/projectdetail/2412195

 

作者:华王