本文旨在说明小M在 Transform 关中经历的种种变化
详细代码见:pytorch-tutorial/transform.py
小M来到第二关的门口,之间门口上书有 RCNNImageTransform
几个大字,进门之间还被专门采集了他的高度和宽度。小M心想,只怕这是经过 Transform
之后,怕是要变型了啊。
raw_image_shape: List[Tuple[int, int]] = []
for image in images:
raw_image_shape.append((image.shape[1], image.shape[2])) # 记录原始宽高
images, targets = self.transform(images, targets) # 进入 RCNNImageTransform
复制代码
再往右看过去,上面画着整个迷宫的设计图
故知在当前迷宫内只需要经历三个步骤: Normalize Resize Merge into a Batch
def forward(self,
images, # type: List[Tensor]
targets=None # type: Optional[List[Dict[str, Tensor]]]
):
for idx in range(len(images)):
image = images[idx]
target = targets[idx] if targets is not None else None
'''1. Normalize'''
image = self.normalize(image)
'''2. Resize'''
image, target = self.resize(image, target)
images[idx] = image
if targets is not None:
targets[idx] = target
# 记录调整后的图片大小
image_sizes = [img.shape[-2:] for img in images]
'''3. Merge into a Batch'''
images = self.batch_images(images)
image_size_list: List[Tuple[int, int]] = []
for img_size in image_sizes:
image_size_list.append((img_size[0], img_size[1]))
image_list = ImageList(images, image_size_list)
return image_list, targets
复制代码
Normalize
'''对图片做标准化处理'''
def normalize(self, image: Tensor):
dtype, device = image.dtype, image.device
mean = torch.as_tensor(self.image_mean, dtype=dtype, device=device)
std = torch.as_tensor(self.image_std, dtype=dtype, device=device)
return (image - mean[:, None, None]) / std[:, None, None]
复制代码
当前迷宫的设计者将均值和方差的默认值设置如下
if image_mean is None:
image_mean = [0.485, 0.456, 0.406] # R G B 三层的均值
if image_std is None:
image_std = [0.229, 0.224, 0.225] # R G B 三层的方差
复制代码
小M在经历 Normalize 之后,他的每一个像素点的值都已经减去均值并除以方差了
Resize
'''将图片大小调整到指定的范围'''
def resize(self, image, target):
height, weight = image.shape[-2:]
max_size = max([height, weight])
min_size = min([height, weight])
scale_factor = self.min_size / min_size
if max_size * scale_factor > self.max_size:
scale_factor = self.max_size / max_size
# interpolate 使用插值的方法来缩放图片
image = F.interpolate(
input=image[None],
scale_factor=scale_factor,
mode='bilinear',
align_corners=False
)[0]
if target is None:
return image, target
bbox = target['boxes']
bbox = resize_boxes(bbox, [height, weight], image.shape[-2:])
target['boxes'] = bbox
return image, target
复制代码
resize_boxes()
代码见:pytorch-tutorial/utils.py
小M经过 Resize 之后,其宽高被限定在了min_size ~ max_size 之间了。
Merge into a Batch
'''将一批图片打包成一个batch'''
def batch_images(self,
images, # 输入的一批图片
size_divisible=32 # 将图片的宽高调整到 size_divisible 的整数倍
):
# type: (List[Tensor], int) -> Tensor
# 获取一个batch的所有图片中最大的 channel、height、width
max_shape = get_max_shape([list(img.shape) for img in images])
stride = float(size_divisible)
# 宽高向上调整到 stride 的整数倍
max_shape[1] = int(math.ceil(max_shape[1] / stride) * stride)
max_shape[2] = int(math.ceil(max_shape[2] / stride) * stride)
batch_size = [len(images)] + max_shape
# 创建shape为batch,且全0的tensor, 与 image[0] 设备、类型相同
batch_image = images[0].new_full(batch_size, 0)
for img, pad_img in zip(images, batch_image):
pad_img[: img.shape[0], : img.shape[1], : img.shape[2]].copy_(img)
return batch_image
复制代码
考虑到可能有多张图片一起来走迷宫,所以这一步会将所有的图片打包成一个 Batch。
首先将所有的图片的最大通道MAX_C、最大高宽MAX_H、MAX_W,获取到,然后转成size_divisible
的倍数,便于硬件加速处理。
最后将所有的图片都扩展成MAX_C通道,MAX_H、MAX_W大小宽高的图片,具体步骤为:以图片左上角为基准点,扩展后,不足的地方补 0
。
至此,小M便完成了第二关Transform
出关后的小M整个人都变了样,看到镜子中的自己标标致致,俨然一个帅气的图片模样,心情亦舒畅了起来,在通往第二关Backbone
的路上还哼起了小曲。
# Standardize
images, targets = self.transform(images, targets) # 对图像进行预处理 ImageList
# 将图像输入到 backbone 得到特征图 并存放在 有序字典中
feature_maps = self.backbone(images.image_list)
复制代码
由上面的代码可知,小M经历了Backbone
之后,变成了 Feature Map
,那么他在Backbone
之中究竟经历了什么呢?且听下回分解……