数据加载和处理

数据加载和处理

部分内容来自百度百科,和pytorch官方学习手册

完整代码

from __future__ import print_function, division
import os
import torch
import  pandas as pd
from skimage import io, transform
import  numpy as np
import matplotlib.pyplot as plt
from torch.utils.data import Dataset, DataLoader
from torchvision import transforms, utils

# 非交互模式
plt.ion()

#处理数据集是面部姿态,

# 快速读取CSV文件,获取标注信息
landmarks_frame = pd.read_csv('data/faces/face_landmarks.csv')
# n是标记点的数量
n=65
# 读取csv文件中第n个数据的第一条字段,即图片的名字
img_name = landmarks_frame.iloc[n, 0]
# 读取csv文件第n个数据的从第二个字段开始的之后 的所有字段,即标记点坐标,并将其转换成矩阵形式
landmarks = landmarks_frame.iloc[n, 1:].as_matrix()
# 将该矩阵的数据类型转为float
# 将矩阵的形状改为n*2形式,-1表示该矩阵的行数由该矩阵的列数决定
landmarks = landmarks.astype('float').reshape(-1, 2)


print('Image name: {}'.format(img_name))
print('Landmarks shape: {}'.format(landmarks.shape))
print('First 4 Landmarks: {}'.format(landmarks[:4]))

# 显示图像和标注信息
def show_landmarks(image, landmarks):
"""Show image with landmarks"""
# 显示图像
    plt.imshow(image)
# 根据坐标在图像上画出标记点,标记符号为“.”,颜色为red红色
# landmarks[:, 0]取出所有行里面的第一列字段
# landmarks[:, 1]取出所有行里面的第二列字段
# 即取出所有坐标点
    plt.scatter(landmarks[:, 0], landmarks[:, 1], s=10, marker='.', c='r')
# 图像暂停1秒
    plt.pause(1)

plt.figure()
show_landmarks(io.imread(os.path.join('data/faces/', img_name)),
               landmarks)
plt.show()

知识点

部分注释

landmarks = landmarks_frame.iloc[n, 1:].as_matrix()

读取csv文件第n个数据的从第二个字段开始的之后 的所有字段,即标记点坐标,并将其转换成矩阵形式
在这里插入图片描述

andmarks = landmarks.astype('float').reshape(-1, 2)

将该矩阵的数据类型转为float
将矩阵的形状改为n*2形式,-1表示该矩阵的行数由该矩阵的列数决定。输入如下:
在这里插入图片描述

头文件
from __future__ import print_function, division

如果某个版本中出现了某个新的功能特性,而且这个特性和当前版本中使用的不兼容,即不是该版本中的语言标准,则需要使用的话就要从future模块中导入。

print_function

引入该模块后,即使是低版本的python2,在使用print函数也要像python3 一样加括号

division

引入Python3 的精确除法

import os

操作系统接口模块

import torch

深度学习工具箱

import  pandas as pd

用于更好地解析CS文件

from skimage import io, transform

用于图像的输入输出和变换

import  numpy as np

数学库

import matplotlib.pyplot as plt

数据可视化工具箱

from torch.utils.data import Dataset, DataLoader

torchvisio.datasets封装好了一些数据集datasets,里面所有的datasets都是torch.utils.data.Datasets的子类。DataLoader用来加载数据集

from torchvision import transforms, utils
数据集

我们要处理的数据集是面部数据集,在该数据集中,每张脸上标注了68个不同的标记点:
在这里插入图片描述
集合一个face_landmarks.csv文件:第一列是图片名字,后面是标记的坐标点。
在这里插入图片描述

CSV

csv,字符分隔值,文件以纯文本形式储存表格数据(数字和文本)。纯文本意味着该文件是一个字符序列。CSV文件由任意数目的记录组成,记录间以某种换行符分隔,每条记录由字段组成。

Datesets

完整代码

# 为我们的脸部数据集创建一个Datasets类
# 在__init__中读取CSV文件
# 在__getitem__中获取图像样本
from __future__ import print_function, division
from Data_loading_and_processing import show_landmarks

import os
import torch
import  pandas as pd
from skimage import io, transform
import  numpy as np
import matplotlib.pyplot as plt
from torch.utils.data import Dataset, DataLoader
from torchvision import transforms, utils

class FaceLandmarksDatasets(Dataset):
# transform:可选参数,用以应对可能的数据预处理
def __init__(self, csv_file, root_dir, transform=None):
        self.landmarks_frame = pd.read_csv(csv_file)
        self.root_dir = root_dir
        self.transform = transform

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

def __getitem__(self, idx):
        img_name = os.path.join(self.root_dir, self.landmarks_frame.iloc[idx, 0])
        image = io.imread(img_name)
        landmarks = self.landmarks_frame.iloc[idx, 1:].as_matrix()
        landmarks = landmarks.astype('float').reshape(-1, 2)
# 将该数据集的所有样本存成一个字典
        sample = {'image': image, 'landmarks': landmarks}

if self.transform:
            sample = self.transform(sample)
return sample

face_dataset = FaceLandmarksDatasets(csv_file='data/faces/face_landmarks.csv',
                                     root_dir='data/faces')

fig = plt.figure()

for i in range(len(face_dataset)):
    sample = face_dataset[i]

print(i, sample['image'].shape, sample['landmarks'].shape)
# 可以使用三个整数,或者三个独立的整数来描述子图的位置信息。
# 如果三个整数是行数、列数和索引值,子图将分布在行列的索引位置上
    ax = plt.subplot(1, 4, i + 1)
# 自动调整子图参数,使子图填充整个图像区域
    plt.tight_layout()
    ax.set_title('Sample #{}'.format(i))
# 关闭坐标轴
    ax.axis('off')
    show_landmarks(**sample)

if i == 3 :
        plt.show()
break

部分注释

`torch.utils.data.Dataset`s

是表示数据集的抽象类,自定义的数据集类必须继承Dataset,并覆盖一下两个方法: ## len,返回数据集的size ## getitem,支持dataset[i],返回第i个样本 。

  # 可以使用三个整数,或者三个独立的整数来描述子图的位置信息。
    # 如果三个整数是行数、列数和索引值,子图将分布在行列的索引位置上
    ax = plt.subplot(1, 4, i + 1)
    # 自动调整子图参数,使子图填充整个图像区域
    plt.tight_layout()
    ax.set_title('Sample #{}'.format(i))
    # 关闭坐标轴
    ax.axis('off')
    show_landmarks(**sample)

输出如下:
在这里插入图片描述

数据预处理-变换

预处理

完整代码

# 在数据集中,样本的尺寸不一样
# 而大多数的神经网络都希望能够得到固定大小的图像
# 因此需要一些图像预处理方法
## rescale:改变图像大小
## randomCrop:随机剪裁图像,用于数据增广
## ToTensor:将numpy images转为torch images

# 我们需要将这些变换写成一个可以调用的类。
# 这样每次调用transform时都不需要传递转换的参数

from skimage import io, transform
import torch
import numpy as np

class Rescale(object):
    '''将图像缩放到一个给定的尺寸'''
    def __init__(self, output_size):
        # assert插入调试断点到程序
        # isinstance:判断某个变量是否是某种类型
        # 用于确保参数output_size是int或者tuple元组类型
        assert isinstance(output_size, (int, tuple))
        self.output_size = output_size


    def __call__(self, sample):
        image, landmarks = sample['image'], sample['landmarks']
        #取出shape中的前两个字段,也就是图像的高和宽
        h, w = image.shape[:2]
        # 如果尺寸output_size是一个整数,则算比例
        # 否则就直接覆盖原尺寸
        if isinstance(self.output_size, int):
            if h > w:
                new_h, new_w = self.output_size * h / w, self.output_size
            else:
                new_h, new_w = self.output_size, self.output_size * w / h
            new_h, new_w = int(new_h), int(new_w)
            img = transform.resize(image, (new_h, new_w))
            #
            landmarks = landmarks * [new_w / w, new_h / h]
            return {'image': img, 'landmarks': landmarks}

class RandomCrop(object):
    """在一个图像样本上随机裁切图像.

    Args:
        output_size (tuple or int): 想要的输出尺寸. If int, square crop is made.
    """
    # 初始化剪裁图像大小
    # 如果output_size为整数,则剪裁大小为正方形
    # 如果output_size为二元数组,则剪裁图像大小就为二元数组所表示的大小
    def __init__(self, output_size):
        assert isinstance(output_size, (int, tuple))
        if isinstance(output_size, int):
            self.output_size = (output_size, output_size)
        else:
            assert len(output_size) == 2
            self.output_size = output_size

    def __call__(self, sample):
        image, landmarks = sample['image'], sample['landmarks']

        h, w = image.shape[:2]
        new_h, new_w = self.output_size

        # 随机找一个剪裁点,左下角
        top = np.random.randint(0, h - new_h)
        left = np.random.randint(0, w - new_w)
        # 最后得出的是剪裁图像的右上角的坐标
        image = image[top: top + new_h,
                      left: left + new_w]
        # 计算以该新剪裁下的图像为坐标轴的,标记点的坐标
        landmarks = landmarks - [left, top]

        return {'image': image, 'landmarks': landmarks}


class ToTensor(object):
    """把样本的 ndarrays 转换为 Tensors."""

    def __call__(self, sample):
        image, landmarks = sample['image'], sample['landmarks']

        # swap color axis because
        # numpy image: H x W x C
        # torch image: C X H X W
        # 通俗解释:读入第二维的数作为第零维的值,读入第0维的数作为第一维的值,读入第1维的值作为第2维的值
        # 即从H * W * C——>C * H * W
        image = image.transpose((2, 0, 1))
        return {'image': torch.from_numpy(image),
                'landmarks': torch.from_numpy(landmarks)}

部分注释

# 随机找一个剪裁点,左下角
top = np.random.randint(0, h - new_h)
left = np.random.randint(0, w - new_w)
# 最后得出的是剪裁图像的右上角的坐标
image = image[top: top + new_h,
              left: left + new_w]
# 计算以该新剪裁下的图像为坐标轴的,标记点的坐标
landmarks = landmarks - [left, top]

如下图所示:
在这里插入图片描述

测试

完整代码

scale = Rescale(256)
crop = RandomCrop(128)
composed = transforms.Compose([Rescale(256),
                               RandomCrop(224)])

```python
# 将上述每个转换应用于示例

```python
fig = plt.figure()
sample = face_dataset[65]
for i, tsfrm in enumerate([scale, crop, composed]):
    transformed_sample = tsfrm(sample)

    ax = plt.subplot(1, 3, i + 1)
    plt.tight_layout()
    ax.set_title(type(tsfrm).__name__)
    show_landmarks(**transformed_sample)

plt.show()

但是,仅使用一个for循环来迭代数据,失去了很多特性

  1. 批量化数据
  2. 随机打乱数据
  3. 使用multiprocessing workers并行加载数据

因此做出以下改进,批处理数据

批处理测试

让我们将所有这些放在一起,创建一个具有组合转换的数据集。
总之,每次采样该数据集时:

  1. 从文件中动态读取一张图像。
  2. Transforms 被应用于读取出的图像。
  3. 由于其中一个变换是随机的,所以在采样时会增加数据。

我们可以像以前一样使用 for i in range 循环迭代创建的数据集。

完整代码

# 显示一个批次的辅助函数
def show_landmarks_batch(sample_batched):
    """Show image with landmarks for a batch of samples."""
    # 获取批量样本集的图像和对应的标注点
    images_batch, landmarks_batch = \
            sample_batched['image'], sample_batched['landmarks']
    # 该批次里一共有多少个样本
    batch_size = len(images_batch)
    im_size = images_batch.size(2)
    print("image_batch:", images_batch)
    print("im_size:", im_size)
    # 将一个batch变为一张图,image_batch表示一行几张图片,这里是4
    grid = utils.make_grid(images_batch)
    print("grid:", grid)
    plt.imshow(grid.numpy().transpose((1, 2, 0)))

    for i in range(batch_size):
        plt.scatter(landmarks_batch[i, :, 0].numpy() + i * im_size,
                    landmarks_batch[i, :, 1].numpy(),
                    s=10, marker='.', c='r')

        plt.title('Batch from dataloader')

if __name__ == '__main__':
    transformed_dataset = FaceLandmarksDatasets(csv_file='data/faces/face_landmarks.csv',
                                            root_dir='data/faces/',
                                            transform=transforms.Compose([
                                               Rescale(256),
                                               RandomCrop(224),
                                               ToTensor()
                                            ]))
    # 一次加载4个样本,并且打乱顺序
    dataloader = DataLoader(transformed_dataset, batch_size=4,
                        shuffle=True, num_workers=4)
    for i_batch, sample_batched in enumerate(dataloader):
        print(i_batch, sample_batched['image'].size(),
            sample_batched['landmarks'].size())

        # observe 4th batch and stop.
        if i_batch == 3:
            plt.figure()
            show_landmarks_batch(sample_batched)
            plt.axis('off')
            plt.ioff()
            plt.show()
            break

输出如下:
在这里插入图片描述

总结

将以上所有类综合在一起,得出的数据加载和处理的完整代码:

from skimage import io, transform
import torch
import numpy as np
import os
import torch
import  pandas as pd
from skimage import io, transform
import  numpy as np
import matplotlib.pyplot as plt
from torch.utils.data import Dataset, DataLoader
from torchvision import transforms, utils

# 数据集
class FaceLandmarksDatasets(Dataset):
    # transform:可选参数,用以应对可能的数据预处理
    def __init__(self, csv_file, root_dir, transform=None):
        self.landmarks_frame = pd.read_csv(csv_file)
        self.root_dir = root_dir
        self.transform = transform

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

    def __getitem__(self, idx):
        img_name = os.path.join(self.root_dir, self.landmarks_frame.iloc[idx, 0])
        image = io.imread(img_name)
        landmarks = self.landmarks_frame.iloc[idx, 1:].as_matrix()
        landmarks = landmarks.astype('float').reshape(-1, 2)
        # 将该数据集的所有样本存成一个字典
        sample = {'image': image, 'landmarks': landmarks}

        if self.transform:
            sample = self.transform(sample)
        return sample
# 变换

class Rescale(object):
    '''将图像缩放到一个给定的尺寸'''
    def __init__(self, output_size):
        # assert插入调试断点到程序
        # isinstance:判断某个变量是否是某种类型
        # 用于确保参数output_size是int或者tuple元组类型
        assert isinstance(output_size, (int, tuple))
        self.output_size = output_size


    def __call__(self, sample):
        image, landmarks = sample['image'], sample['landmarks']
        #取出shape中的前两个字段,也就是图像的高和宽
        h, w = image.shape[:2]
        # 如果尺寸output_size是一个整数,则算比例
        # 否则就直接覆盖原尺寸
        if isinstance(self.output_size, int):
            if h > w:
                new_h, new_w = self.output_size * h / w, self.output_size
            else:
                new_h, new_w = self.output_size, self.output_size * w / h
            new_h, new_w = int(new_h), int(new_w)
            img = transform.resize(image, (new_h, new_w))
            #
            landmarks = landmarks * [new_w / w, new_h / h]
            return {'image': img, 'landmarks': landmarks}

class RandomCrop(object):
    """在一个图像样本上随机裁切图像.

    Args:
        output_size (tuple or int): 想要的输出尺寸. If int, square crop is made.
    """
    # 初始化剪裁图像大小
    # 如果output_size为整数,则剪裁大小为正方形
    # 如果output_size为二元数组,则剪裁图像大小就为二元数组所表示的大小
    def __init__(self, output_size):
        assert isinstance(output_size, (int, tuple))
        if isinstance(output_size, int):
            self.output_size = (output_size, output_size)
        else:
            assert len(output_size) == 2
            self.output_size = output_size

    def __call__(self, sample):
        image, landmarks = sample['image'], sample['landmarks']

        h, w = image.shape[:2]
        new_h, new_w = self.output_size

        # 随机找一个剪裁点,左下角
        top = np.random.randint(0, h - new_h)
        left = np.random.randint(0, w - new_w)
        # 最后得出的是剪裁图像的右上角的坐标
        image = image[top: top + new_h,
                      left: left + new_w]
        # 计算以该新剪裁下的图像为坐标轴的,标记点的坐标
        landmarks = landmarks - [left, top]

        return {'image': image, 'landmarks': landmarks}


class ToTensor(object):
    """把样本的 ndarrays 转换为 Tensors."""

    def __call__(self, sample):
        image, landmarks = sample['image'], sample['landmarks']

        # swap color axis because
        # numpy image: H x W x C
        # torch image: C X H X W
        # 通俗解释:读入第二维的数作为第零维的值,读入第0维的数作为第一维的值,读入第1维的值作为第2维的值
        # 即从H * W * C——>C * H * W
        image = image.transpose((2, 0, 1))
        return {'image': torch.from_numpy(image),
                'landmarks': torch.from_numpy(landmarks)}

# 测试

# 显示一个批次的辅助函数
def show_landmarks_batch(sample_batched):
    """Show image with landmarks for a batch of samples."""
    # 获取批量样本集的图像和对应的标注点
    images_batch, landmarks_batch = \
            sample_batched['image'], sample_batched['landmarks']
    # 该批次里一共有多少个样本
    batch_size = len(images_batch)
    im_size = images_batch.size(2)
    print("image_batch:", images_batch)
    print("im_size:", im_size)
    # 将一个batch变为一张图,image_batch表示一行几张图片,这里是4
    grid = utils.make_grid(images_batch)
    print("grid:", grid)
    plt.imshow(grid.numpy().transpose((1, 2, 0)))

    for i in range(batch_size):
        plt.scatter(landmarks_batch[i, :, 0].numpy() + i * im_size,
                    landmarks_batch[i, :, 1].numpy(),
                    s=10, marker='.', c='r')

        plt.title('Batch from dataloader')

if __name__ == '__main__':
    transformed_dataset = FaceLandmarksDatasets(csv_file='data/faces/face_landmarks.csv',
                                            root_dir='data/faces/',
                                            transform=transforms.Compose([
                                               Rescale(256),
                                               RandomCrop(224),
                                               ToTensor()
                                            ]))
    # 一次加载4个样本,并且打乱顺序
    dataloader = DataLoader(transformed_dataset, batch_size=4,
                        shuffle=True, num_workers=4)
    for i_batch, sample_batched in enumerate(dataloader):
        print(i_batch, sample_batched['image'].size(),
            sample_batched['landmarks'].size())

        # observe 4th batch and stop.
        if i_batch == 3:
            plt.figure()
            show_landmarks_batch(sample_batched)
            plt.axis('off')
            plt.ioff()
            plt.show()
            break
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值