scikit-image:用于图像的IO和变换
pandas:用于更容易地进行csv解析
读取csv中的数据
landmarks_frame = pd.read_csv('data/faces/face_landmarks.csv')
# 此处地址为存储csv文件的地址切记不可放置在C盘中,否则文件为只读无法进行读取和修改
读取到的注释信息做一定的处理,一般第一列为图像名字,后面的列为图像中注释的具体信息
n = 65
# n 为特征点的数量
img_name = landmarks_frame.iloc[n, 0]
# 读取第一列信息,若只需读取某一个图片,具体索引即可
landmarks = landmarks_frame.iloc[n, 1:].as_matrix()
# 以矩阵形式输出注释信息
landmarks = landmarks.astype('float').reshape(-1, 2)
# 对注释信息的数据类型,shape修改为想要的
print('Image name: {}'.format(img_name))
print('Landmarks shape: {}'.format(landmarks.shape))
print('First 4 Landmarks: {}'.format(landmarks[:4]))
编写函数展示图片和它对应的标注点为例子
def show_landmarks(image, landmarks):
"""显示带有地标的图片"""
plt.imshow(image)
# 用于显示图像,可以传递一个二维或者三维的数组作为参数,将图像数据显示为图形
plt.scatter(landmarks[:, 0], landmarks[:, 1], s=10, marker='.', c='r')
# 生成散点图PYthon——plt.scatter各参数详解_plt.scatter()函数参数-CSDN博客
plt.pause(0.001)
# pause a bit so that plots are updated
plt.figure()
show_landmarks(io.imread(os.path.join('data/faces/', img_name)),
landmarks)
plt.show()
数据集类
自定义数据集应继承Dataset并覆盖以下方法*_ _len_ _实现len(dataset)返还数据集的尺寸。
*_ _getitem_ _用来获取一些索引数据
建立数据集类
假设为面部数据创建一个数据集类,_ _init_ _中读取csv的文件内容,再_ _getitem_ _中读取数据图片,在需要图片时才读取,可以有限节省内存空间。
收集面部特征时数据样本按一个字典{‘image’:image,‘landmarks’:landmarks}组织
此时数据集类添加了一个可选参数transform以方便对样本预处理
class FaceLandmarksDataset(Dataset):
"""面部标记数据集."""
def __init__(self, csv_file, root_dir, transform=None):
csv_file(string)
# 带注释的csv文件的路径,string表示输入的字符串
root_dir string)
# 包含所有图像的目录
transform(callable, optional)
# 一个样本上的可用的可选变换
self.landmarks_frame = pd.read_csv(csv_file)
# 读取csv文件中的内容
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])
# 拼接文件路径os.path.join()函数用法详解-CSDN博客
image = io.imread(img_name)
landmarks = self.landmarks_frame.iloc[idx, 1:]
landmarks = np.array([landmarks])
landmarks = landmarks.astype('float').reshape(-1, 2)
sample = {'image': image, 'landmarks': landmarks}
if self.transform:
sample = self.transform(sample)
return sample
数据可视化
实例化这个类并遍历数据样本
face_dataset = FaceLandmarksDataset(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
数据变换
图片尺寸转换三个转换:
Rescale 缩放图片
Randomcrap 对图片进行随机裁剪
Totensor 把numpy格式转换为torch
使用_ _call_ _方法,写成调用的类的形式,而不是简单的函数,在每次调用的时候就不要传递参数,使用_ _call_ _的方法就可以实现调用。
class Rescale(object):
"""将样本中的图像重新缩放到给定大小。.
Args:
output_size(tuple或int):所需的输出大小。 如果是元组,则输出为
与output_size匹配。 如果是int,则匹配较小的图像边缘到output_size保持纵横比相同。
"""
def __init__(self, output_size):
assert isinstance(output_size, (int, tuple))
#assert 插入调试断点,当出现不等的情况时程序中断
# isinstance判断变量是否为某个类型
# 在此例子中为判断输出的是否为int或i者元组型,不是的话返回False,assert检测到false中断程序运行,是的话返回true程序继续运行
self.output_size = output_size
def __call__(self, sample):
# _ _call_ _函数
image, landmarks = sample['image'], sample['landmarks']
h, w = image.shape[:2]
# 将图像的尺寸赋值给参数h和w
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
else:
new_h, new_w = self.output_size
new_h, new_w = int(new_h), int(new_w)
img = transform.resize(image, (new_h, new_w))
# 对输入图像的尺寸进行处理,不满足尺寸的情况进行处理
# h and w are swapped for landmarks because for images,
# x and y axes are axis 1 and 0 respectively
landmarks = landmarks * [new_w / w, new_h / h]
return {'image': img, 'landmarks': landmarks}
class RandomCrop(object):
"""随机裁剪样本中的图像.
Args:
output_size(tuple或int):所需的输出大小。 如果是int,方形裁剪是。
“方形裁剪"
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']
# 交换颜色轴因为
# numpy包的图片是: H * W * C
# torch包的图片是: C * H * W
image = image.transpose((2, 0, 1))
# transpose函数:opencv读入图片的矩阵为(height,width,channels)
torch的需要的是(channels,height,width)
所以进行转换变为2.0.1
return {'image': torch.from_numpy(image),'landmarks': torch.from_numpy(landmarks)}
组合变换
示例:我们想要把图像的短边调整为256,然后随机裁剪(randomcrop) 为224大小的正方形。也就是说,我们打算组合一个Rescale 和 RandomCrop 的变换。
具体实现
scale = Rescale(256)
# 调用Rescale函数
crop = RandomCrop(128)
# 调用RandomCrop函数
composed = transforms.Compose([Rescale(256),RandomCrop(224)])
# transforms.Compose将多个数据变换组合在一起,以便将他们应用于数据
# 在样本上应用上述的每个变换
fig = plt.figure()
sample = face_dataset[65]
for i, tsfrm in enumerate([scale, crop, composed]):
# enumerate(iteration, start)函数默认包含两个参数,其中iteration参数为需要遍历的参数,比如字典、列表、元组等,start参数为开始的参数,默认为0(不写start那就是从0开始)
transformed_sample = tsfrm(sample)
ax = plt.subplot(1, 3, i + 1)
# 将数据划分为几块区域
# subplot(M,N,P)是将多个图画到一个平面上的工具。其中,m表示是图排成m行,n表示图排成n列,也就是整个figure中有n个图是排成一行的,一共m行,如果m=2就是表示2行图。p表示图所在的位置,p=1表示从左到右从上到下的第一个位置。
plt.tight_layout()
ax.set_title(type(tsfrm).__name__)
show_landmarks(**transformed_sample)
plt.show()
迭代数据集
将上述组合起来创建一个带组合转换的数据集
每次这个数据集被采样时: *及时地从文件中读取图片 * 对读取的图片应用转换 * 由于其中一步操作是随机的 (randomcrop) , 数据被增强了