Faster R-CNN 之变形篇

本文旨在说明小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
复制代码

再往右看过去,上面画着整个迷宫的设计图

image.png

故知在当前迷宫内只需要经历三个步骤: Normalize \rightarrow Resize \rightarrow 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扩展到最大宽高时,白色区域都设置为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之中究竟经历了什么呢?且听下回分解……

猜你喜欢

转载自juejin.im/post/7023992633315819533