waterman 发表于 2024-12-12 12:05

【读书】《计算机视觉之PyTorch数字图像处理》5.数据准备与预处理

本帖最后由 waterman 于 2024-12-12 12:07 编辑

在基于深度学习进行图像处理之前,我们首先要准备相应的数据集。而Torchvision库提供了对图像数据的支持,主要包括常用图像数据集的访问接口、经典的图像分类、分割和目标检测模型,图像数据预处理功能以及一些图像增强的快捷方法。
Torchvision库是由多个功能子包构成的,每个功能子包功能相对独立,其中与数据准备相关的功能包有dataset、transforms和utils。
- dataset:提供了20多个流行的图像数据集访问接口,部分数据集还提供数据下载功能,只需简单调用,即可完成图像数据集的准备。
- transforms:提供了图像数据的预处理功能,可在图像、数组和张量三者之间进行相互转换,能够进行图像数据的增强,扩充数据集。
- utils:提供数据可视化功能。包括:
- make_grid():将一组图像张亮合成一幅表示图像的张量
- save_image():与make_grid类似,但是将合成的图像保存为图像文件
- draw_bounding_boxes():将矩形框绘制在图像张量上
- fraw_segmentation_masks():将分割结果绘制在张量上

# 数据集准备
虽然torchvision的datasets包中提供了一些数据集的访问接口,但是不能用于自有数据集的创建。因此,为使用自建数据集,需要我们使用torch.utils.data.Dataset类将图像数据进行封装,为模型提供统一的数据访问接口。
最关键的是实现torch.utils.data.Dataset类中的\__len__()、\__getitem__()两个抽象方法。其中\__len__()方法返回数据集的样本数量,及数据集规模;\__getitem__()方法接受样本的索引号,返回数据集中特定索引号的样本。
数据集返回的样本数据,在训练集上一般是由图像张量和标签组成的元组,而样本数据在测试集上只返回图像张量。
```python
import torchvision as tvs
from torchvision import transforms as T
import torch as tc
import pickle
from pathlib import Path
import numpy as np
class CIFAR10(tc.utils.data.Dataset):
    #CIFAR10自定义数据集
    #CIFAR10数据下载:https://www.cs.toronto.edu/~kriz/cifar-10-python.tar.gz
    def __init__(self,path='./dataset/cifar-10-batches-py',istrain=True,isimg=True,transform=None, target_transform=None):
      self.path=Path(path)
      self.istrain=istrain
      self.isimg=isimg
      self.transform=transform
      self.target_transform=target_transform
      self.fnames=if self.istrain else
      self.labelfile=self.path/'batches.meta'
      self.labeltext=self.__loadlabel(self.labelfile)
      self.label2str=lambda lbidx: self.labeltext
      self.labels=[]
      self.data=[]
      for f in self.fnames:
            tmpdata,tmplabel=self.__loaddata(f)
            self.labels+=tmplabel
            self.data.append(tmpdata)
      self.data=np.concatenate(self.data,axis=0)
      
    def __loaddata(self,file):
      f=open(file,'rb')
      print(f'加载数据文件{file.name}... ....')
      df=pickle.load(f,encoding='bytes')
      imgs=df
      print(f'从数据文件{file.name}加载了{imgs.shape}个样本... ...')
      lbs=df
      return imgs,lbs
   
    def __loadlabel(self,file):
      f=open(file,'rb')
      lbf=pickle.load(f,encoding='bytes')
      lb=[ lb.decode()for lb in lbf]
      return lb      
    def __len__(self):
      #可通过len()方法得到数据集内的样本数
      return self.data.shape
    def __getitem__(self,idx):
    #得到第idx个样本,idx为样本索引
      if idx>=self.__len__() or idx<0:
            raise IndexError(f'index is out of range: {idx}')
      data=self.data.reshape(3,32,32) if self.isimg else self.data
      label=self.labels
      if self.transform:
            data=self.transform(data)
      if self.target_transform:
            label=self.target_transform(label)
      return data,label
```
在上述代码中,通过继承torch.utils.data.Dataset类,自定义了一个CIFAR10数据集,在\__init__()部分,完成了数据集的读取解析,并将数据保存到类的变量中。

下面我们主要来看一下\__len__()、\__getitem__()两个方法的实现。
- \__len__():self.data第一维的尺寸为样本的数量,即shape属性的0号元素。
- \__getitem__():首先判断idx合法性,之后通过索引获取self.data中第idx号元素,对其形状进行变换,得到格式的图像数据,并将对应的标签保存在label中。最后判断是否需要对样本数据进行变换操作并执行相应变换。

# 数据变换与增强
pytorch提供了transforms包,其中集成了主流的图像变换和增强方法,可直接调用,大大简化了图像处理流程。如果有特殊需求,也可以通过编写自定义的类来实现自定义的数据变换与增强。

## 常用变换操作

下面对一些常用的变换操作进行介绍:
- CenterCrop(size):中心裁切变换。从图像中心裁切出指定长宽(size)的子图像。size可以是由(h,w)构成的序列,也可以是一个整数值裁切出正方形。

- Grayscale(num_output_channels=1):灰度变换。Grayscale将3通道的彩色图像变换成1通道的灰度图像,或返回3通道的灰度图像。
- Pad(padding, fill=0, padding_mode='constant'):扩边变换,用于调整图像的尺寸。padding: 确定扩充像素的数量。整数表示四边等宽扩充;四个整数的序列分别表示左、上、右、下边的扩充宽度。fill: 指定扩充区域的像素填充值。padding_mode: 扩充模式,包括:constant: 使用fill值填充。edge: 使用最近边缘像素值填充。reflect: 反射边缘填充。symmetric: 对称边缘填充。
- RandomHorizontalFlip(p=0.5)与RandomVerticalFlip(p=0.5):随机水平翻转变换与随机竖直翻转变换。参数p表示翻转的概率,常用于训练样本数量的扩充。
- Normalize(mean, std, inplace=False):归一化变换,将张量减去均值mean,再除以标准差std。参数mean为数值序列,长度与输入张量的通道数相同,参数std为数值序列,长度与输入张量的通道数相同,参数inplace表示输出替换输入。
- ToPILImage(mode=None),将张量图像转换为PIL图像。参数mode为PIL中Image对象的模式,例如:‘L’,‘RGB’,‘RGBA’等。 输入是的张量或者的numpy数组,输出是一幅PIL 图像。
- ToTensor():将PIL转换为张量,并将像素值压缩到0-1。

## 变换组合
此外transforms还提供了Compose变换类,能够将多个变换组合构成一个复合的变换,对图像进行一系列的变换与增强,可以简化代码并提高可读性,同时确保一系列预定的操作能够按顺序应用到输入的数据上。
```python
from torchvision import transforms

# 定义一系列的变换
transform = transforms.Compose([
    transforms.Resize(256),# 调整图片大小到256x256
    transforms.CenterCrop(224),# 从中心裁剪出224x224的图片
    transforms.ToTensor(),# 将PIL Image或者numpy.ndarray转换为tensor
    transforms.Normalize(mean=, std=)# 标准化
])
```
页: [1]
查看完整版本: 【读书】《计算机视觉之PyTorch数字图像处理》5.数据准备与预处理