当前位置: 首页 > 工具软件 > PaddleSeg > 使用案例 >

[PaddleSeg 源码阅读] PaddleSeg Transform 的 Normalize操作

林星华
2023-12-01

接上一篇,PaddleSeg 自定义数据类 https://blog.csdn.net/HaoZiHuang/article/details/125566058 这里再说一下 Normalize 操作

咱们自己写 Transform 时,总会担心一些细节,比如:

  • np.uint8 转了没,要不要转为 np.float32
  • 归一化做了没(/255做了没)
  • 用不用减去0.5,然后除以0.5,将其移至 -1 到 1
  • 送入数据之前,有没有 transpose 之类的
  • 何时将 numpy.ndarray 初始化为 paddle.Tensor

上一篇PaddleSeg说到,在Compose结尾,会做输入通道数的转换(Transpose),而看源代码:

batch_sampler = paddle.io.DistributedBatchSampler(
        eval_dataset, batch_size=1, shuffle=False, drop_last=False)
loader = paddle.io.DataLoader(
    eval_dataset,
    batch_sampler=batch_sampler,
    num_workers=num_workers,
    return_list=True, )

这个是实例化,Dataset 和 DataLoader 的部分,尽管 eval_dataset 返回的每个数据都是 np.ndarray,但是 paddle.io.DataLoader 这个类,会直接返回 Paddle.Tensor 类的数据,也就是无需再做 paddle.to_tensor操作

OK, 接下里的这三项:

  • np.uint8 转了没,要不要转为 np.float32
  • 归一化做了没
  • 用不用减去0.5,然后除以0.5,将其移至 -1 到 1

就是 Normalize 部分的操作了,来看看它实现的源码

paddleseg\transforms\transforms.pyNormalize

@manager.TRANSFORMS.add_component
class Normalize:
    """
    Normalize an image.

    Args:
        mean (list, optional): The mean value of a data set. Default: [0.5, 0.5, 0.5].
        std (list, optional): The standard deviation of a data set. Default: [0.5, 0.5, 0.5].

    Raises:
        ValueError: When mean/std is not list or any value in std is 0.
    """

    def __init__(self, mean=(0.5, 0.5, 0.5), std=(0.5, 0.5, 0.5)):
        self.mean = mean
        self.std = std
        if not (isinstance(self.mean,
                           (list, tuple)) and isinstance(self.std,
                                                         (list, tuple))):
            raise ValueError(
                "{}: input type is invalid. It should be list or tuple".format(
                    self))
        from functools import reduce
        if reduce(lambda x, y: x * y, self.std) == 0:
            raise ValueError('{}: std is invalid!'.format(self))

    def __call__(self, im, label=None):
        """
        Args:
            im (np.ndarray): The Image data.
            label (np.ndarray, optional): The label data. Default: None.

        Returns:
            (tuple). When label is None, it returns (im, ), otherwise it returns (im, label).
        """

        mean = np.array(self.mean)[np.newaxis, np.newaxis, :]
        std = np.array(self.std)[np.newaxis, np.newaxis, :]
        im = functional.normalize(im, mean, std)

        if label is None:
            return (im, )
        else:
            return (im, label)

paddleseg\transforms\functional.pynormalize 函数

def normalize(im, mean, std):
    im = im.astype(np.float32, copy=False) / 255.0
    im -= mean
    im /= std
    return im

可以看到 在 normalize 函数中,做了转 np.float32, 归一化

然后 -0.5 之后 /0.5


如果你对这两行感兴趣:

mean = np.array(self.mean)[np.newaxis, np.newaxis, :]
std  = np.array(self.std )[np.newaxis, np.newaxis, :]

自己打印看看吧,就是个原来的array维度前面加了两个1
shape 从 (3,) 变成了 (1, 1, 3)

np.newaxis 其实就是 None

(np.newaxis == None) == True


如果对 Normalize __init__函数中的 reduce 感兴趣:
可以看看这个:
functools下的reduce函数

 类似资料: