【问题标题】:PyTorch : How to apply the same random transformation to multiple image?PyTorch:如何将相同的随机变换应用于多个图像?
【发布时间】:2021-04-03 11:26:07
【问题描述】:

我正在为包含多对图像的数据集编写一个简单的转换。作为数据增强,我想对每一对应用一些随机变换,但该对中的图像应该以相同的方式进行变换。 例如,给定一对两个图像AB,如果A水平翻转,B必须水平翻转为A。然后下一对CD 应该与AB 进行不同的转换,但CD 的转换方式相同。我正在尝试以下方式

import random
import numpy as np
import torchvision.transforms as transforms
from PIL import Image

img_a = Image.open("sample_ajpg") # note that two images have the same size
img_b = Image.open("sample_b.png")
img_c, img_d = Image.open("sample_c.jpg"), Image.open("sample_d.png")

transform = transforms.RandomChoice(
    [transforms.RandomHorizontalFlip(), 
     transforms.RandomVerticalFlip()]
)
random.seed(0)
display(transform(img_a))
display(transform(img_b))

random.seed(1)
display(transform(img_c))
display(transform(img_d))

然而,上面的代码没有选择相同的转换,我测试过,它取决于transform被调用的次数。

有没有办法强制transforms.RandomChoice 在指定时使用相同的转换?

【问题讨论】:

    标签: python pytorch torchvision


    【解决方案1】:

    我意识到 OP 要求使用 torchvision 提供解决方案,我认为 @Ivan 的 answer 可以很好地解决这个问题。

    但是,对于那些不绑定到特定增强库的人,我想指出,Albumentations 似乎在native fashion 中很好地处理了这些情况,允许用户将多个源图像、框等传递到相同的变换。返回结构为字典

    import albumentations as A
    
    transform = A.Compose(
        transforms=[
            A.VerticalFlip(p=0.5),
            A.HorizontalFlip(p=0.5)],
        additional_targets={'image0': 'image', 'image1': 'image'}
    )
    transformed = transform(image=image, image0=image0, image1=image1)
    

    现在您可以访问transformed['image0']transformed['image1'] 等,它们都将应用随机参数

    【讨论】:

      【解决方案2】:

      简单地说,将 PyTorch 中的随机化部分放入 if 语句中。 下面的代码使用vflip。对于水平或其他变换也是如此。

      import random
      import torchvision.transforms.functional as TF
      
      if random.random() > 0.5:
          image = TF.vflip(image)
          mask  = TF.vflip(mask)
      

      此问题已在 PyTorch forum 中讨论。在官方 GitHub 存储库page 上讨论了几种解决方案的优缺点。 PyTorch 维护者提出了这种简单的方法。

      不要使用torchvision.transforms.RandomVerticalFlip(p=1)。使用torchvision.transforms.functional.vflip

      函数式转换可让您对转换管道进行细粒度控制。与上述变换相反,函数变换不包含用于其参数的随机数生成器。这意味着您必须指定/生成所有参数,但您可以重用函数转换。

      【讨论】:

      • 虽然此代码可能会解决问题,但 including an explanation 关于如何以及为什么解决问题将真正有助于提高您的帖子质量,并可能导致更多的赞成票。请记住,您正在为将来的读者回答问题,而不仅仅是现在提问的人。请edit您的答案添加解释并说明适用的限制和假设。
      • @AdrianMole 感谢您的建议。我已经添加了解释:-)
      【解决方案3】:

      通常一种解决方法是在第一张图像上应用变换,检索该变换的参数,然后在剩余图像上应用具有这些参数的确定性变换。但是,这里RandomChoice 没有提供API 来获取应用变换的参数,因为它涉及可变数量的变换。 在这些情况下,我通常会实现对原始函数的覆盖。

      torchvision implementation,就这么简单:

      class RandomChoice(RandomTransforms):
          def __call__(self, img):
              t = random.choice(self.transforms)
              return t(img)
      

      这里有两种可能的解决方案。

      1. 您可以从__init__ 上的转换列表中采样,而不是__call__

        import random
        import torchvision.transforms as T
        
        class RandomChoice(torch.nn.Module):
            def __init__(self):
                super().__init__()
                self.t = random.choice(self.transforms)
        
            def __call__(self, img):
                return self.t(img)
        

        所以你可以这样做:

        transform = T.RandomChoice([
             T.RandomHorizontalFlip(), 
             T.RandomVerticalFlip()
        ])
        display(transform(img_a)) # both img_a and img_b will
        display(transform(img_b)) # have the same transform
        
        transform = T.RandomChoice([
            T.RandomHorizontalFlip(), 
            T.RandomVerticalFlip()
        ])
        display(transform(img_c)) # both img_c and img_d will
        display(transform(img_d)) # have the same transform
        

      1. 或者更好的是,批量转换图像:

        import random
        import torchvision.transforms as T
        
        class RandomChoice(torch.nn.Module):
            def __init__(self, transforms):
               super().__init__()
               self.transforms = transforms
        
            def __call__(self, imgs):
                t = random.choice(self.transforms)
                return [t(img) for img in imgs]
        

        允许这样做:

        transform = T.RandomChoice([
             T.RandomHorizontalFlip(), 
             T.RandomVerticalFlip()
        ])
        
        img_at, img_bt = transform([img_a, img_b])
        display(img_at) # both img_a and img_b will
        display(img_bt) # have the same transform
        
        img_ct, img_dt = transform([img_c, img_d])
        display(img_ct) # both img_c and img_d will
        display(img_dt) # have the same transform
        

      【讨论】:

      • 澄清一下,transform = transforms.RandomChoice([ 在你的回答中其实是transform = RandomChoice([,对吧?
      • 哎呀,抱歉应该是T.RandomChoice(),因为我将torchvision.transforms 导入为T
      • 我同意批量转换(在可能的情况下)似乎是最好的解决方案
      【解决方案4】:

      我不知道修复随机输出的函数。 也许尝试不同的逻辑,例如自己创建随机化以便能够重用相同的转换。 逻辑:

      • 生成随机数
      • 根据数字对两个图像应用转换
      • 生成另一个随机数
      • 对其他两个图像执行相同的操作 试试这个:
      import random
      import numpy as np
      import torchvision.transforms as transforms
      from PIL import Image
      
      img_a = Image.open("sample_ajpg") # note that two images have the same size
      img_b = Image.open("sample_b.png")
      img_c, img_d = Image.open("sample_c.jpg"), Image.open("sample_d.png")
      
      if random.random() > 0.5:
              image_a_flipped = transforms.functional_pil.vflip(img_a)
              image_b_flipped = transforms.functional_pil.vflip(img_b)
      else:
          image_a_flipped = transforms.functional_pil.hflip(img_a)
          image_b_flipped = transforms.functional_pil.hflip(img_b)
      
      if random.random() > 0.5:
              image_c_flipped = transforms.functional_pil.vflip(img_c)
              image_d_flipped = transforms.functional_pil.vflip(img_d)
      else:
          image_c_flipped = transforms.functional_pil.hflip(img_c)
          image_d_flipped = transforms.functional_pil.hflip(img_d)
          
      display(image_a_flipped)
      display(image_b_flipped)
      
      display(image_c_flipped)
      display(image_d_flipped)
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2021-07-15
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-11-27
        • 1970-01-01
        • 1970-01-01
        • 2016-09-28
        相关资源
        最近更新 更多