计算机视觉——数据使用篇

数据使用

一、数据获取

1. 数据获取的常见方法

  • 数据集(ImageNet等)
      - 数据质量高
      - 成本低
  • 外包平台(Amazon Mechanical Turk,阿里众包等)
      - 大规模
      - 成本高
  • 自己采集或爬虫
      - 成本低
      - 速度快

2. 爬虫

2.1 图片爬虫工具

https://github.com/QianyanTech/Image-Downloader

  • 图片种类丰富(所有类型图片),来源于搜索引擎

  • 爬取速度快(5min/1000张)

  • 接口稳定,使用简单

  • 数量限制

    爬虫工具
2.2 视频爬虫工具

https://github.com/iawia002/annie

  • 使用方法:annie [可选参数] http://...(视频网站)

  • 支持主流的视频平台

  • 支持多个视频同时下载

  • 支持其他文件下载

  • 支持下载合集

2.3 复杂爬虫(flickr)

https://qithub.com/chenusc11/flickr-crawler

  • Flickr,高质量图片网站,官方提供爬虫接口,可用于ImageNet等数据集的构建

  • 使用步骤:

    1. 注册Flickr API key: API_KEY 和 API_SECRET

    2. 根据关键词获取图片地址,生成脚本文件(代码如下)

      import flickr
      # tags: 关键词列表
      # per_page: 图片数量
      def get_urls_for_tags(tags,number,ff):
          photos = flickr.photos_search(tags=tags,tag_mode='all',per_page=number)
          url = []
          for photo in photos:
              try:
                  #size:图片大小(Small,Medium,Large,Original)
                  urls.append(photo.getURL(size='Original',urlType='source'))
                  ff.write(photo.getURL(size='Original',urlType='source'))
                  ff.write('\n')
              except:
                  continue
          return urls  
      
    3. 下载图片 :使用 wget 命令爬取

  • 不限量,接口稳定,可获得百万量级的高质量图片

  • 速度慢

  • 按用户ID爬取:https://github.com/hellock/icrawler

2.4 其他爬虫

二、数据整理

1. 数据检查与归一化

1.1 检查照片
  • 去除坏图与尺寸异常
    • 损坏图片(防止读取失败)
    • 尺寸异常图片(如长宽比大于10)
1.2 归一化
  • 格式归一化
    • 类型归一化(方便遍历,*.jpg | *.png, 统一压缩方式)
    • 命名归一化(方便归类)
1.3 脚本案例
  • 去除破坏图,重命名图片
def listfiles(rootDir,ifrename=False):
    list_dirs = os.walk(rootDir)
    num = 0
    for root,dirs,files in list_dirs:
        for d in dirs:
            print (os.path.join(root,d))
        for f in files:
            fileid = f.split('.')[0]
            filepath = os.path.join(root,f)
            try:
                src = cv2.imread(filepath,1)
                print ("src=",filepath,src.shape)
                os.remove(filepath) #去除原来的照片
                if ifrename:
                    cv2.imwrite(os.path.join(root,str(num)+".jpg"),src) #写入新的图片,重新命名
                    num = num + 1
                else:
                    cv2.imwrite(os.path.join(root,fileid+".jpg"),src) #写入新的图片,名字不变
            except:
                os.remove(filepath)  #去除不能读取的图片
                continue

2. 数据去重

  • 数据去重不是必要的步骤

  • 相同的图片

    • 名字不同,内容相同
    • 大小不同,内容相同
  • 相似的图片

    • 连续的视频帧

    • 扰动污染(水印等)

2.1 去重关键技术
  • 去除相同的图片(基于MD5)
# 获取文件的md5
def get_md5(file):
    file = open(file,'rb')
    md5 = hashlib.md5(file.read())
    file.close()
    md5_values = md5.hexdigest()
    return md5_values
# 单个文件去重
def remove_by_md5_singledir(file_dir):
    file_list = os.listdir(file_dir)
    md5_list = []
    print("去重前图像数量:"+str(len(file_list)))
    for filepath in file_list:
        filemd5 = get_md5(os.path.join(file_dir,filepath))
        if filemd5 not in md5_list:
            md5_list.append(filemd5)
        else:
            os.remove(os.path.join(file_dir,filepath))
    print("去重后图像数量:"+str(len(os.listdir(file_dir))))
  • 去除相似的图片
# 计算图像相似度
def compare_image(image1,image2,mode='same'):
    # 比较是否完全相同
    if mode == 'same':
        assert(image1.shape == image2.shape)  #判断两张图片大小是否相同,大小不同则抛出AssertionError
        diff = (image1==image2).astype(np.int) #比较每个像素,存入diff数组(1|0)
        if cv2.countNonZero(diff) == image1.shape[0]*image1.shape[1]:
            return 1.0
    # 比较是否相似,基于绝对值阈值
    elif mode=='abs':
        assert(image1.shape == image2.shape)
        diff = np.sum(np.abs((image1.astype(np.float)-image2.astype(np.float))))
        return diff/(image1.shape[0]*image1.shape[1])
    return 0
# 单文件夹去重
def remove_by_pixel_singledir(file_dir,mode,th=5.0): #th为绝对差阈值,小于这个阈值属于相似图片
    file_list = os.listdir(file_dir)
    print("去重前图像数量:"+str(len(file_list)))
    for i in range(0,len(file_list)):
        if i < len(file_list)-1:
            imagei = cv2.imread(os.path.join(file_dir,file_list[i]),0)
            imagei = cv2.resize(imagei,(128,128),interpolation=cv2.INTER_NEAREST) #将图片转化为相同大小(128*128),方便比较像素,interpolation指定缩放算法为最近邻插值法
            print("testing image"+os.path.join(file_dir,file_list[i]))
            for j in range(i+1,len(file_list)):
                if j < len(file_list):
                    while j < len(file_list):
                        imagej = cv2.imread(os.path.join(file_dir,file_list[j]),0)
                        imagej = cv2.resize(imagej,(128,128),interpolation=cv2.INTER_NEAREST)
                        similarity = compare_image(imagei,imagej,mode=mode)
                        print("simi="+str(similarity))
                        if similarity >= 1.0 and mode == 'same':
                            os.remove(os.path.join(file_dir,file_list[i]))
                            print('删除'+os.path.join(file_dir,file_list[j]))
                            file_list.pop(j)
                        elif similarity < th and mode == 'abs':
                            os.remove(os.path.join(file_dir,file_list[j]))
                            print('删除'+os.path.join(file _dir,file_list[j]))
                            file_list.pop(j)
                        else:
                            break
            print("去重后图像数量:"+str(len(os.listdir(file_dir))))
2.2 去重技术改进
  • 多文件夹去除模式
def remove_by_md5_multidir(file_list):
    md5_list=[]
    print("去重前图像数量:"+str(len(file_list)))
    for filepath in file_list:
        filemd5 = get_md5(filepath)
        file_id = filepath.split('/')[-1]
        file_dir = filepath[0:len(filepath)-len(file_id)]
        if filemd5 not in md5_list:
            md5_list.append(filemd5)
        else:
            os.remove(filepath)
    print("去重后图像数量:"+str(len(md5_list)))
file_dirl = sys.argv[1]
file_list1 = os.listdir(file_dir1)
file_listl = [ os.path.join(file_dirl,x) for x in file_list1 ]
file_dir2 = sys.argv[2]
file_list2 = os.listdir(file_dir2)
file_list2 = [ os.path.join(file_dir2,x) for x in file_list2 ]
remove_by_md5_multidir(file_list1+file_list2)
  • 基于图片的相似度计算改进

更多的相似度准则::MSE距离,leveshtein距离,DNN特征相似度

  • 基于遍历方案的改进

根据相关准则(文件物理大小,图像尺寸,文件名字)进行预先排序,搜索一定的深度或最近邻

3. 数据集划分

3.1 训练验证数据集划分
  • 随机打乱数据+划分
#file_in 输入txt文件
#file_out 输出txt文件
def shuffle(file_in,file_out):
    fin = open(file_in,'r')
    fout = open(file_out,'w')
    lines = fin.readlines()
    random.shuffle(lines)
    for line in lines:
        fout.write(line)
  • 常用划分比例:7 : 3、9 : 1
  • 划分模式:随机、均匀
# 均匀划分
import sys
# 将训练集测试集进行划分,valratio即测试集的比例
def splittrain_val(fileall,valratio=0.1):
    fileids = fileall.split('.')   #按点号进行分割来获取文件名和扩展名
    fileid = fileids[len(fileids)-2] #获取文件名
    f = open(fileall)
    ftrain = open(fileid+"_train.txt",'w')  #创建新的文件对象,训练集
    fval = open(fileid+"_val.txt",'w')
    count = 0
    if valratio == 0 or valratio >= 1:
        valratio = 0.1
    interval = (int)(1.0 / valratio) 
    
    #如果valratio=0.1,以下代码表示每隔10个值取一个valratio,其他都是interval
    while 1:
        line = f.readline()
        if line:
            count = count + 1
            if count % interval == 0:
                fval.write(line)
            else:
                ftrain.write(line)
        else:
            break
3.2 数据集难度划分
  • 常见数据集难度划分,按照Edgebox检测率划分炜三个级别:
    1. 简单(Easy):EdgeBox检测率高于90%的数据集属于简单级别。这些数据集通常包含比较简单、易于检测的目标,例如MNIST手写数字数据集、CIFAR-10图像分类数据集等。
    2. 中等(Medium):EdgeBox检测率在70%-90%之间的数据集属于中等级别。这些数据集中的目标相对较复杂,例如PASCAL VOC和COCO等物体识别数据集。
    3. 困难(Hard):EdgeBox检测率低于70%的数据集属于困难级别。这些数据集中的目标非常具有挑战性,通常包含大量的噪声、遮挡和复杂的背景,例如KITTI和Cityscapes等自动驾驶场景的数据集。

4. 数据标注

4.1 数据标注工具
  • 常见视觉标注任务

    1. 图片分类
    2. 语义分割/实例分割
    3. 目标检测
    4. 关键点检测
  • 常用工具:Labelme

标注案例:

image-20230326154231257

标注生成结果(json格式):

image-20230326154544930image-20230326154625086image-20230326154728166

将标注结果json文件转换为图片:labelme_json_to_dataset 1.json -o 1

4.2 智能数据标注

5. 数据增强

​ 数据增强( Data Augmentation)也叫数据扩增、数据增广,意思是在不实质性的增加数据的情况下,从有限的数据产生更多变种,让有限的数据产生等价于更多数据的价值。

5.1 数据增强与泛化能力
  • 如何增强模型的泛化能力
    • 显式正则化(模型集成,参数正则化等)
    • 隐式正则化(数据增强,随机梯度下降等)
5.2 基本的数据增强方法

​ 基本的数据增强,采用固定的预设规则进行数据扩增包括单样本数据增强和多样本数据增强。

​ 单样本增强:几何操作类和颜色操作类。

​ 多样本增强:离散样本点连续化来进行插值拟合。

  • 单样本集合变换:翻转、旋转、裁剪、缩放、仿射等

    • 没有改变图像的内容,选择了图像的一部分或者像素的空间重分布,256 x 256裁剪224 x 224,增加了32 x 32倍。
    • 裁剪(固定裁剪,多尺度裁剪)
    • 翻转(方向敏感任务不可用)
    • 随机旋转(角度敏感任务不可用)
  • 单样本像素变换:噪声、模糊、颜色扰动、对比度扰动、擦除

    • 内容变动大,多样性强
  • 多样本插值-SMOT:Synthetic Minority Over-sampling Technique方法

    • 第一步:定义好特征空间,将每个样本对应到特征空间中的某一点,根据样本不平衡比例确定好一个采样倍率N;

    • 第二步:对每一个少样本类样本(x,,按欧氏距离找出K个最近邻样本,从中随机选取一个样本点,假设选择的近邻点为(x)。在特征空间中样本点与最近邻样本点的连线段上随机选取一点作为新样本点,满足以下公式:
      ( x n e w + y n e w ) = ( x , y ) + λ ∗ ( ( x n − x ) , ( y n − y ) ) , λ 取值范围介于 0 到 1 (x_{new}+y_{new})=(x,y)+λ*((x_n-x),(y_n-y)), λ取值范围介于0到1 (xnew+ynew)=(x,y)+λ((xnx),(yny))λ取值范围介于01

    • 第三步,重复以上的步骤,直到大、小样本数量平衡。

image-20230327100911730

  • 多样本插值-SamplePairing

​ 随机抽取两张图片分别经过基础数据增强操作(如随机翻转等)处理后,直接加合成一个新的样本,标签为原样本标签中的一种。降低测试集误差,对相似样本数据集比较有效。

  • 多样本插值-Mixup

    Facebook提出的Mixup,对图像和标签都进行线性插值。
    ( x n , y n ) = λ ( x i , y i ) + ( 1 − λ ) ( x j , y j ) , λ   β ( α , α ) 取值范围介于 0 到 1 (x_n,y_n)=λ(x_i,y_i)+(1-λ)(x_j,y_j),λ~β(α,α)取值范围介于0到1 (xn,yn)=λ(xi,yi)+(1λ)(xj,yj),λ β(α,α)取值范围介于01
    对图像,语音,GAN等问题都有效。

  • 综合变换:综合使用几何变换、颜色变化

    通用领域:https://github.com/aleju/imgaug

5.3 自动数据增强方法
  • Autoaugment:学习已有数据增强操作的组合,不同的任务,需要不同的数据增强操作。
    1. 准备16个常用的数据增强操作。
    2. 从16个中选择5个操作,随机产生使用该操作的概率和相应的幅度,将其称为一个sub-policy,一共产生5个sub-polices。
    3. 对训练过程中每一个batch的图片,随机采用5个sub-polices操作中的一种。
    4. 通过模型在验证集上的泛化能力来反馈,使用的优化方法是增强学习方法。
    5. 经过80~100个epoch后网络开始学习到有效的sub-policies。
    6. 之后串接这5个sub-policies,然后再进行最后的训练。

​ 16个常用的数据增强操作:

3a9609d75f974a6bfe8724517630af1

5.4 从零生成新的数据
  • 1、生成对抗网络

    ​ 生成对抗网络(Generative Adversarial Network)生成新数据,提升仿真数据的质量。

    image-20230327103307289

    ​ 黑色虚线是真实分布,绿色实线是生成模型的学习过程,蓝色虚线是判别模型的学习过程。

  • 2、生成对抗网络结构与优化目标

    image-20230327103609361
  • 3、生成样本

  • 4、仿真样本:提升仿真数据的质量

6. Pytorch图像分类的数据增强

6.1 数据预处理与增强接口
  • 通过torchvision包的transforms进行数据预处理,包括标准化函数等
data_transforms = {
    'train':transforms.Compose([
            transforms.RandomResizedCrop(48),   # 随机裁剪,将图像随机裁剪为指定大小(48 x 48)的正方形。
            transforms.RandomHorizontalFlip(),  # 随机水平翻转,以一定概率(默认为0.5)随机翻转图像。
            transforms.ToTensor(),              # 把一个取值范围是[0,255]的PILImage或者shape为(H,W,C)的numpy.ndarray,转换成形状为[C,H,W],取值范围是[0,1.0]的torch.FloatTensor
            transforms.Normalize([0.5,0.5,0.5],[0.5,0.5,0.5])  # 对归一化后的图像数据进行标准化,减去RGB均值(0.5),除以方差(0.5)。
    ]),
    'val':transforms.Compose([   # 测试集的数据预处理都不能用随机
            transforms.Resize(48),  
            transforms.CenterCrop(48),   # 中心裁剪
            transforms.ToTensor(),
            transforms.Normalize([0.5,0.5,0.5],[0.5,0.5,0.5])
    ])
}
# RandomSizedCrop参数
torchvision.transforms.RandomSizedCrop(size,scale=(0.08,1.0),ratio=(0.75,1.3333333333333333),interpolation=<InterpolationMode.BILINEAR:'bilinear'>)
# 将输入图进行随机裁剪,尺度由面积比参数scale控制,长宽比由ratio控制,然后缩放为48*48
  • 常见的数据预处理与增强操作

    • 数据增强: CenterCrop,ColorJitter,FiveCrop,Grayscale,RandomAffine,RandomApply,RandomCrop,RandomGrayscale,RandomHorizontalFlip,RandomPerspective,RandomResizedCrop,RandomRotation,RandomSizedCrop,RandomVerticalFlip,TenCrop,GaussianBlur,RandomChoice,RandomOrder,LinearTransformation,RandomErasing,Lambda
    • 预处理: ToPILImage,ToTensor,Normalize,Resize,Scale,Pad,ConvertImageDtype
  • 自定义数据增强函数,使用torchvision包的functional接口

    import torchvision.transforms.functional as TF
    import random
    def my_segmentation_transforms(image, mask):    # 输入多个处理对象,控制使用相同的随机参数
        if random.random() > 0.5:
            angle = random.randint(-30, 30)    # 随机生成旋转角度,对样本和标签同时旋转
            image = TF.rotate(image, angle)
            mask = TF.rotate(mask, angle)
    return image, mask
    
模型训练精度曲线增强效果
image-20230327112222281无数据增强,val_acc≈0.9
image-20230327112340394裁剪+翻转数据增强,val_acc≈0.95
image-20230327113208686裁剪+翻转+旋转数据增强,val_acc≈0.95
image-20230327135453753裁剪+翻转+旋转+颜色数据增强,val_acc≈0.98
  • 总结:在数据太少时,数据增强大大提升了模型的泛化能力,很重要。

7. 数据增强开源库imgaug

  • 强大的数据增强库:imgaug

  • 安装指令:pip install imgaug

  • 项目地址:https://github.com/aleju/imgaug

  • imgaug支持的增强操作:affine transformations,perspective transformations,contrast changes,gaussian noise,dropout of regions,hue/saturation changes,cropping/padding,blurring

  • imgaug支持的各项视觉任务:Images(uint8),Heatmaps (float32),Segmentation Maps (int),Masks(bool),Keypoints/Landmarks (int/float coordinates),Bounding Boxes (int/float coordinates),Polygons (int/float coordinates),Line Strings (int/float coordinates)

7.1 imgaug数据增强API
  • augmenter.Sequential:组合一系列增强函数

    import imgaug.augmenters as iaa
    
    aug = iaa.Sequential([
        iaa.Affine(translate_px={"x":-40}), #进行仿射变换,将图像沿 x 轴向左平移 40 个像素。
        iaa.AdditiveGaussianNoise(scale=0.1*255),  #给图像添加高斯噪声,scale 参数表示噪声的强度,这里设置为 0.1 倍的像素灰度范围(255)。
    ],random_order=True) #将 deterministic=False,则数据增强序列中的增强操作将在每个样本上以随机顺序应用。
    
  • augmenters.arithmetic:数学操作
    image-20230327144932181

  • agumenters.flip,augmenters.geometric :翻转与平移等操作

image-20230327145111894image-20230327145216353

  • agumenters.blend:与输入图进行图像融合
    image-20230327145432945

  • augmenters.color:颜色操作
    image-20230327145709202image-20230327145832981

  • augmenters.contrast:亮度操作
    image-20230327145952407

  • augmenters.size:尺度变换操作
    image-20230327150048896image-20230327150122452

  • 卷积变换操作

    • augmenters.convolutional:卷积操作
      image-20230327150304796

    • augmenters.pooling:池化操作
      image-20230327150352304

    • augmenters.edges:边缘操作
      image-20230327150434993

  • augmenters.artistic:艺术增强操作
    image-20230327150616095
    image-20230327150648476

  • augmenters.segmentation:分割增强操作
    image-20230327150742221

  • augmenters.weather:天气增强操作
    image-20230327150856621

7.2 imgaug数据增强案例
  • 单个图像的数据增强
import numpy as np
import imgaug as ia
import imgaug.augmenters as iaa

ia.seed(1) # 设置随机数种子,保证随机数的可重复性
## 创建矩阵(16, 64, 64, 3)
images = np.array(   # 将多个 64x64x3 的图像转换成了一个 16x64x64x3 的 numpy 数组
    [ia.quokka(size=(64, 64)) for _ in range(16)], # 来生成一个包含 16 张随机的 64x64x3 的图像,ia.quokka() 函数可以生成一张随机的“袋狸猫”图片
    dtype=np.uint8  # 使用 8 位无符号整数来表示每个像素的值,取值范围为 0 到 255
)

seq = iaa.Sequential([
    iaa.Fliplr(0.5), ## 以0.5的概率进行水平翻转horizontal flips
    iaa.Crop(percent=(0, 0.1)), ## 随机裁剪random crops
    ## 对50%的图片进行高斯模糊,标准差参数取值0~0.5.
    iaa.Sometimes(
        0.5,
        iaa.GaussianBlur(sigma=(0, 0.5))
    ),
    ## 对50%的通道添加高斯噪声
    iaa.AdditiveGaussianNoise(loc=0, scale=(0.0, 0.05*255), per_channel=0.5),
], random_order=True) ## 以上所有操作,使用随机顺序

images_aug = seq(images=images) ## 应用操作增强
grid_image = ia.draw_grid(images_aug,4) #生成一个网格图像,每行显示4张图,

import imageio
imageio.imwrite("example1.jpg", grid_image)

增强结果:

image-20230327154445242
  • 对同一个batch内容不同图像使用不同参数
import imgaug.augmenters as iaa
import imgaug.parameters as iap
import numpy as np
import imgaug as ia


ia.seed(1) # 设置随机数种子,保证随机数的可重复性
## 创建矩阵(16, 64, 64, 3)
images = np.array(   # 将多个 64x64x3 的图像转换成了一个 16x64x64x3 的 numpy 数组
    [ia.quokka(size=(64, 64)) for _ in range(16)], # 来生成一个包含 16 张随机的 64x64x3 的图像,ia.quokka() 函数可以生成一张随机的“袋狸猫”图片
    dtype=np.uint8  # 使用 8 位无符号整数来表示每个像素的值,取值范围为 0 到 255
)

seq = iaa.Sequential([
    iaa.GaussianBlur(sigma=iap.Uniform(0.0,1.0)),
    iaa.ContrastNormalization(
        iap.Choice(
            [1.0,1.5,3.0],
            p=[0.5,0.3,0.2])
    ),
    iaa.Affine(
        rotate=iap.Normal(0.0,3.0),
        translate_px=iap.RandomSign(iap.Poisson(3))
    ),
    iaa.AddElementwise(
        iap.Discretize((iap.Beta(0.5,0.5)*2-1.0)*64)
    ),
    iaa.Multiply(iap.Positive(iap.Normal(0.0,1.0))+1.0)
])

images_aug = seq(images=images) ## 应用操作增强
grid_image = ia.draw_grid(images_aug,4) #生成一个网格图像,每行显示4张图,

import imageio
imageio.imwrite("example2.jpg", grid_image)

增强结果:
image-20230327155833897

7.3 imgaug典型视觉任务使用
  • 常见的视觉任务:分类、分割、检测
  1. 关键点数据增强
import imgaug as ia 
import imgaug.augmenters as iaa 
from imgaug.augmentables import Keypoint, KeypointsOnImage
ia.seed(1)

# 创建图片和关键点
image = ia.quokka(size=(256, 256))
kps = KeypointsOnImage([  # 创建一个关键点对象kps,用于表示一组关键点在图像上的位置
    Keypoint(x=65, y=100),
    Keypoint(x=75, y=200),
    Keypoint(x=100, y=100),
    Keypoint(x=200, y=80)
], shape=image.shape)

# 创建数据增强对象
seq = iaa.Sequential([
    iaa.Multiply((1.2, 1.5)), # 改变亮度
    iaa.Affine(
        rotate=10,
        scale=(0.5, 0.7)
    )
])

# 对关键点和图片进行增强
image_aug, kps_aug = seq(image=image, keypoints=kps)

for i in range(len(kps.keypoints)):
    before = kps.keypoints[i]
    after = kps_aug.keypoints[i]
    print("Keypoint %d: (%.8f, %.8f) -> (%.8f, %.8f)" % (
        i, before.x, before.y, after.x, after.y)
)

image_before = kps.draw_on_image(image, size=7)   # 利用draw_on_image函数将关键点画到image上
image_after = kps_aug.draw_on_image(image_aug, size=7)

import imageio
imageio.imwrite("before_keypoint.jpg", image_before)
imageio.imwrite("after_keypoint.jpg", image_after)

增强前后比较:
image-20230327194748482image-20230327194828320

  1. 目标框数据增强——目标框未超出边界
import imgaug as ia
import imgaug.augmenters as iaa
from imgaug.augmentables.bbs import BoundingBox, BoundingBoxesOnImage
ia.seed(1)

image = ia.quokka(size=(256, 256))
bbs = BoundingBoxesOnImage([   # 创建目标框对象bbs,包含两个目标框,(x1, y1) 表示目标框左上角的坐标,(x2, y2) 表示目标框右下角的坐标。
    BoundingBox(x1=65, y1=100, x2=200, y2=150),
    BoundingBox(x1=150, y1=80, x2=200, y2=130)
], shape=image.shape)

seq = iaa.Sequential([
    iaa.Multiply((1.2, 1.5)),
    iaa.Affine(
        translate_px={"x": 40, "y": 60},
        scale=(0.5, 0.7)
    ) ## 对x和y方向分别平移40/60px,尺度缩放为原来的0-70%
])

# 对目标框和图片进行增强
image_aug, bbs_aug = seq(image=image, bounding_boxes=bbs)

for i in range(len(bbs.bounding_boxes)):
    before = bbs.bounding_boxes[i]
    after = bbs_aug.bounding_boxes[i]
    print("BB %d: (%.4f, %.4f, %.4f, %.4f) -> (%.4f, %.4f, %.4f, %.4f)" % (
        i,
        before.x1, before.y1, before.x2, before.y2,
        after.x1, after.y1, after.x2, after.y2)
    )

# 绘制增强前后框
image_before = bbs.draw_on_image(image, size=2)
image_after = bbs_aug.draw_on_image(image_aug, size=2, color=[0, 0, 255])  # 增强后的框用蓝色区分

import imageio
imageio.imwrite("before_boundingbox.jpg", image_before)
imageio.imwrite("after_boundingbox.jpg", image_after)

增强前后比较:
image-20230327195559942image-20230327195617180

  1. 目标框数据增强——目标框超出边界
#coding:utf8
import imgaug as ia
import imgaug.augmenters as iaa
from imgaug.augmentables.bbs import BoundingBox, BoundingBoxesOnImage

ia.seed(1)

image = ia.quokka(size=(256, 256))
bbs = BoundingBoxesOnImage([
    BoundingBox(x1=25, x2=75, y1=25, y2=75),
    BoundingBox(x1=100, x2=150, y1=25, y2=75),
    BoundingBox(x1=175, x2=225, y1=25, y2=75)
], shape=image.shape)

seq = iaa.Affine(translate_percent={"x": 120})  # 将图像在水平方向上平移 120%,x 和 y 分别表示水平和竖直方向上的平移距离
image_aug, bbs_aug = seq(image=image, bounding_boxes=bbs)

## 边界填充,1个白色像素,(BY-1)个黑色像素
def pad(image, by):
    image_border1 = iaa.size.pad(image, top=1, right=1, bottom=1, left=1,
                           mode="constant", cval=255)
    image_border2 = iaa.size.pad(image_border1, top=by-1, right=by-1,
                           bottom=by-1, left=by-1,
                           mode="constant", cval=0)
    return image_border2

## 自己定义边框绘制函数
GREEN = [0, 255, 0]
ORANGE = [255, 140, 0]
RED = [255, 0, 0]
def draw_bbs(image, bbs, border):
    image_border = pad(image, border)
    for bb in bbs.bounding_boxes:
        if bb.is_fully_within_image(image.shape):  # 目标框全在图像中,用绿色绘制
            color = GREEN
        elif bb.is_partly_within_image(image.shape): # 目标框部分在图像中,用橘色绘制
            color = ORANGE
        else:           # 目标框全在图像外,用红色绘制
            color = RED
        image_border = bb.shift(x=border, y=border).draw_on_image(image_border, size=2, color=color)
        # 目标框bb中的坐标沿着x轴和y轴方向移动border个单位,并用不同颜色绘制在图像上
    return image_border

image_before = draw_bbs(image, bbs, 100)
image_after1 = draw_bbs(image_aug, bbs_aug, 100)
image_after2 = draw_bbs(image_aug, bbs_aug.remove_out_of_image(), 100)  # 将超出图像范围的目标框移除
image_after3 = draw_bbs(image_aug, bbs_aug.remove_out_of_image().clip_out_of_image(), 100) # 将超出图像范围的目标框截断到图像边缘

import imageio
imageio.imwrite("normal_boundingbox.jpg", image_before)
imageio.imwrite("after1_boundingbox.jpg", image_after1)
imageio.imwrite("after2_boundingbox.jpg", image_after2)
imageio.imwrite("after3_boundingbox.jpg", image_after3)

增强前后比较:
image-20230327195840791image-20230327195900040

image-20230327195918038image-20230327195933428

  1. 分割数据增强
import imageio
import numpy as np
import imgaug as ia
import imgaug.augmenters as iaa
from imgaug.augmentables.segmaps import SegmentationMapsOnImage
ia.seed(1)

image = ia.quokka(size=(128, 128), extract="square")

segmap = np.zeros((128, 128, 1), dtype=np.int32)  # 创建了一个 128x128x1 的全 0 数组 segmap,表示一张分割图
segmap[28:71, 35:85, 0] = 1    # (28, 35) 到 (70, 84) 的矩形区域,使用整数 1 表示
segmap[10:25, 30:45, 0] = 2
segmap[10:25, 70:85, 0] = 3
segmap[10:110, 5:10, 0] = 4
segmap[118:123, 10:110, 0] = 5
segmap = SegmentationMapsOnImage(segmap, shape=image.shape)  # 将 segmap 转换成SegmentationMapsOnImage 对象,用于存储分割图

# 数据增强操作
seq = iaa.Sequential([
    iaa.Dropout([0.05, 0.2]),      # 随机丢掉 5% or 20%的像素
    iaa.Sharpen((0.0, 1.0)),       # 锐化操作sharpen
    iaa.Affine(rotate=(-45, 45)),  # 旋转-45到45度
    iaa.ElasticTransformation(alpha=50, sigma=5)  # 应用ElasticTransformation操作,弹性变形操作
], random_order=True)

# 对分割掩膜和图片进行增强
images_aug = []
segmaps_aug = []
for _ in range(5):
    images_aug_i, segmaps_aug_i = seq(image=image, segmentation_maps=segmap)
    images_aug.append(images_aug_i)
    segmaps_aug.append(segmaps_aug_i)

cells = []
for image_aug, segmap_aug in zip(images_aug, segmaps_aug):
    cells.append(image)                                         # column 1,原图
    cells.append(segmap.draw_on_image(image)[0])                # column 2,分割后的图像
    cells.append(image_aug)                                     # column 3,增强后图像
    cells.append(segmap_aug.draw_on_image(image_aug)[0])        # column 4,分割+增强后的图像
    cells.append(segmap_aug.draw(size=image_aug.shape[:2])[0])  # column 5,增强分割标注单独显示

grid_image = ia.draw_grid(cells, cols=5)
imageio.imwrite("example_segmaps.jpg", grid_image)

增强前后比较:
image-20230327200341754

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值