Task2 数据读取与数据增广

2020-05-23  本文已影响0人  蔓藤树下的甜蜜

1、数据的读取

1.1 对于图片的读取,一般计算机视觉领域常用的是opencv和pillow。

对于pillow库我用的比较少,所以我这里就介绍一下opencv库。opencv是Intel开源的计算机视觉库,它是由一系列c函数和少量的c++类构成,实现了图像处理和计算机视觉方面的很多通用算法。opencv拥有包括300多个c函数的跨平台的中、高层API。它不依赖于其它的外部库-尽管可以使用某些外部库。最重要的是opencv对于商业和非商业都是free的。它同时支持linux,windows,android平台,提供有python,c,c++,java的接口函数。
关于opencv的python接口函数的读取处理数据的简单用法,见下图:


图片.png

1.2 pytorch对于图片数据的读取操作代码示例:

import os, sys, glob, shutil, json
import cv2

from PIL import Image
import numpy as np

import torch
from torch.utils.data.dataset import Dataset
import torchvision.transforms as transforms

class SVHNDataset(Dataset):
    def __init__(self, img_path, img_label, transform=None):
        self.img_path = img_path
        self.img_label = img_label 
        if transform is not None:
            self.transform = transform
        else:
            self.transform = None

    def __getitem__(self, index):
        img = Image.open(self.img_path[index]).convert('RGB')

        if self.transform is not None:
            img = self.transform(img)
    
        # 原始SVHN中类别10为数字0
        lbl = np.array(self.img_label[index], dtype=np.int)
        lbl = list(lbl)  + (5 - len(lbl)) * [10]
    
        return img, torch.from_numpy(np.array(lbl[:5]))

   def __len__(self):
        return len(self.img_path)

   train_path = glob.glob('../input/train/*.png')
   train_path.sort()
   train_json = json.load(open('../input/train.json'))
   train_label = [train_json[x]['label'] for x in train_json]

   data = SVHNDataset(train_path, train_label,
          transforms.Compose([
              # 缩放到固定尺寸
          transforms.Resize((64, 128)),

          # 随机颜色变换
          transforms.ColorJitter(0.2, 0.2, 0.2),

          # 加入随机旋转
          transforms.RandomRotation(5),

          # 将图片转换为pytorch 的tesntor
          # transforms.ToTensor(),

          # 对图像像素进行归一化
          # transforms.Normalize([0.485,0.456,0.406],[0.229,0.224,0.225])
        ]))

2、数据的增广

数据增广是深度学习中常用的技巧之一,主要用于增加训练集,让数据尽可能的多样化,使得训练的模型具有更强的泛化能力。现有的各大深度学习框架都已经自带了数据增广,它只是提供了接口函数。现在我们从:水平/垂直翻转,旋转,缩放,裁剪,剪切,平移,对比度,色彩抖动,噪声等几个方面讲解和实现数据增广。
所有的数据增广在操作的时候默认是以图像中心点进行的。从数学角度来看,任何操作都可以分成以下几个步骤:1、首先将旋转点移动到原点处;2、执行如下图所描述的绕原点旋转;3、再将旋转点移回到原来的位置;为了更好的理解见下图:

图片.png

假设图像的原始坐标为(x1,y1),平移后的坐标为(x,y),则平移前和平移后的坐标关系为:
\begin{matrix} \left[\begin{array}{rr} x \\ y \\ 1 \end{array}\right] & = H& \left[\begin{array}{rr} x1 \\ y1 \\ 1 \end{array}\right] \end{matrix}

2.1、图像平移

平移是指所有的像素在x和y方向各平移一定距离,平移变换对应的数学矩阵为:
\begin{matrix} & H= & \left[\begin{array}{rr} 1&0&dx \\ 0&1&dy\\ 0&0&1 \end{array}\right] \end{matrix}
如图平移的具体实例(这里平移采用的是到映射填充)

图片.png

2.2、图像翻转(图像镜像)

图像翻转包括水平翻转和垂直翻转。水平翻转的变换矩阵为:
\begin{matrix} & H= & \left[\begin{array}{rr} -1&0&0 \\ 0&1&0\\ 0&0&1 \end{array}\right] \end{matrix}
垂直翻转的变换矩阵为:
\begin{matrix} & H= & \left[\begin{array}{rr} 1&0&0 \\ 0&-1&0\\ 0&0&1 \end{array}\right] \end{matrix}
翻转后的具体实例(这里是采用倒映射填充):

图片.png

2.3、图像旋转

图像旋转是指以某个点(默认为图像中心点)为中心进行任意角度旋转,其变换矩阵为:
\begin{matrix} & H= & \left[\begin{array}{rr} \cos\theta&-\sin\theta&0 \\ \sin\theta&\cos\theta&0\\ 0&0&1 \end{array}\right] \end{matrix}
图像旋转图片(这里平移后采用的是到映射填充):

图片.png

2.4、图像缩放:

图像缩放是指对当前图像进行任意尺度的缩放,其变换矩阵为:

\begin{matrix} & H= & \left[\begin{array}{rr} Sx&0&0 \\ 0&Sy&0\\ 0&0&1 \end{array}\right] \end{matrix}
缩放后的图片如下(这里平移后采用的是到映射填充):

图片.png

2.5、图像错切:

其变换矩阵为:
\begin{matrix} & H= & \left[\begin{array}{rr} 1&SHy&0 \\ SHx&1&0\\ 0&0&1 \end{array}\right] \end{matrix}
错切的图像如下(这里平移后采用的到映射填充):

图片.png

2.6、图像裁剪:

深度学习的图像裁剪的常用做法是将图片缩放到原图的1.1倍,然后在缩放后的图像上进行裁剪操作,具体的裁剪实例如下:


图片.png

2.7、组合变换:

在深度学习中的数据增广一般会采用多种增广的方式的组合,根据其运算的规则,可以知道不同的组合顺序结果是不一样的。
为了更好地解释,假设给定平移变换矩阵Hshift ,旋转矩阵Hrotate,缩放矩阵Hscale ,为了说明这里我给出两个不同的组合变换.对于组合变换一,其组合后的矩阵如下:M = Hshift x Hrotate x Hscale;对于组合变换二,其组合后的矩阵如下:M = Hscale x Hrotate x Hshift,对于两种不同的组合其结果如下:

图片.png

下边是数据增广的源码实现示例:

import numpy as np
import os
import cv2
import copy


class DataAugment:
    def __init__(self,debug=False):
        self.debug=debug
        print("Data augment...")

    def basic_matrix(self,translation):
        """基础变换矩阵"""
        return np.array([[1,0,translation[0]],[0,1,translation[1]],[0,0,1]])

    def adjust_transform_for_image(self,img,trans_matrix):
        """根据图像调整当前变换矩阵"""
        transform_matrix=copy.deepcopy(trans_matrix)
        height, width, channels = img.shape
        transform_matrix[0:2, 2] *= [width, height]
        center = np.array((0.5 * width, 0.5 * height))
        transform_matrix = np.linalg.multi_dot([self.basic_matrix(center), transform_matrix, self.basic_matrix(-center)])
        return transform_matrix

    def apply_transform(self,img,transform):
        """仿射变换"""
        output = cv2.warpAffine(img, transform[:2, :], dsize=(img.shape[1], img.shape[0]),flags=cv2.INTER_LINEAR, borderMode=cv2.BORDER_REFLECT, borderValue=0,)   #cv2.BORDER_REPLICATE,cv2.BORDER_TRANSPARENT
        return output

    def apply(self,img,trans_matrix):
        """应用变换"""
        tmp_matrix=self.adjust_transform_for_image(img, trans_matrix)
        out_img=self.apply_transform(img, tmp_matrix)
        if self.debug:
            self.show(out_img)
        return out_img

    def random_vector(self,min,max):
        """生成范围矩阵"""
        min=np.array(min)
        max=np.array(max)
        print(min.shape,max.shape)
        assert min.shape==max.shape
        assert len(min.shape) == 1
        return np.random.uniform(min, max)

    def show(self,img):
        """可视化"""
        cv2.imshow("outimg",img)
        cv2.waitKey()

    def random_transform(self,img,min_translation,max_translation):
        """平移变换"""
        factor=self.random_vector(min_translation,max_translation)
        trans_matrix=np.array([[1, 0, factor[0]],[0, 1, factor[1]],[0, 0, 1]])
        out_img=self.apply(img,trans_matrix)
        return trans_matrix, out_img

    def random_flip(self,img,factor):
        """水平或垂直翻转"""
        flip_matrix = np.array([[factor[0], 0, 0],[0, factor[1], 0],[0, 0, 1]])
        out_img=self.apply(img,flip_matrix)
        return flip_matrix, out_img

    def random_rotate(self,img,factor):
        """随机旋转"""
        angle=np.random.uniform(factor[0],factor[1])
        print("angle:{}".format(angle))
        rotate_matrix=np.array([[np.cos(angle), -np.sin(angle), 0],[np.sin(angle),np.cos(angle), 0],[0, 0, 1]])
        out_img=self.apply(img,rotate_matrix)
        return rotate_matrix, out_img

    def random_scale(self,img,min_translation,max_translation):
        """随机缩放"""
        factor=self.random_vector(min_translation, max_translation)
        scale_matrix = np.array([[factor[0], 0, 0],[0, factor[1], 0],[0, 0, 1]])
        out_img=self.apply(img,scale_matrix)
        return scale_matrix, out_img

    def random_shear(self,img,factor):
        """随机剪切,包括横向和众向剪切"""
        angle = np.random.uniform(factor[0], factor[1])
        print("fc:{}".format(angle))
        crop_matrix = np.array([[1, factor[0], 0], [factor[1], 1, 0], [0, 0, 1]])
        out_img=self.apply(img,crop_matrix)
        return crop_matrix, out_img


if __name__=="__main__":
    demo=DataAugment(debug=True)
    img=cv2.imread("/pathto/dataArgu/wr.jpg")

    # 平移测试
    _,outimg=demo.random_transform(img,(0.1,0.1),(0.2,0.2))  #(-0.3,-0.3),(0.3,0.3)


   # 垂直变换测试
   _, outimg =demo.random_flip(img,(1.0,-1.0))

  # 水平变换测试
  _, outimg =demo.random_flip(img, (-1.0, 1.0))


   # 旋转变换测试
    _, outimg =demo.random_rotate(img,(0.5,0.8))

    # # 缩放变换测试
    _, outimg =demo.random_scale(img,(1.2, 1.2),(1.3,1.3))

    # 随机裁剪测试
    _, outimg =demo.random_shear(img,(0.2,0.3))

    # 组合变换
    t1,_=demo.random_transform(img,(-0.3,-0.3),(0.3,0.3))
    t2,_=demo.random_rotate(img,(0.5,0.8))
    t3,_=demo.random_scale(img,(1.5,1.5),(1.7,1.7))
    tmp=np.linalg.multi_dot([t1,t2,t3])
    print("tmp:{}".format(tmp))
    out=demo.apply(img,tmp)

3、总结

图片数据读取和数据增广是计算机视觉,特别是搞深度学习的最最基础的部分了,可能对于没有cv基础的同学理解起来会有点困难。

参考链接:
https://zhuanlan.zhihu.com/p/43665254

上一篇下一篇

猜你喜欢

热点阅读