【问题标题】:How to get rid of checkerboard artifacts如何摆脱棋盘伪影
【发布时间】:2020-03-24 20:20:06
【问题描述】:

各位程序员您好,

我正在使用完全卷积自动编码器为黑白图像着色,但是,输出有一个checkerboard pattern,我想摆脱它。到目前为止,我所看到的棋盘伪像总是比我的小得多,摆脱它们的常用方法是用双线性上采样替换所有非池化操作(有人告诉我)。

但我不能简单地替换解池操作,因为我使用不同大小的图像,因此需要解池操作,否则输出张量的大小可能与原始张量不同。

TLDR:

如何在不替换取消池化操作的情况下摆脱这些棋盘伪影?

class AE(nn.Module):
    def __init__(self):
        super(AE, self).__init__()
        self.leaky_reLU = nn.LeakyReLU(0.2)
        self.pool = nn.MaxPool2d(kernel_size=2, stride=2, padding=1, return_indices=True)
        self.unpool = nn.MaxUnpool2d(kernel_size=2, stride=2, padding=1)
        self.softmax = nn.Softmax2d()

        self.conv1 = nn.Conv2d(in_channels=3, out_channels=64, kernel_size=3, stride=1, padding=1)
        self.conv2 = nn.Conv2d(in_channels=64, out_channels=128, kernel_size=3, stride=1, padding=1)
        self.conv3 = nn.Conv2d(in_channels=128, out_channels=256, kernel_size=3, stride=1, padding=1)
        self.conv4 = nn.Conv2d(in_channels=256, out_channels=512, kernel_size=3, stride=1, padding=1)
        self.conv5 = nn.Conv2d(in_channels=512, out_channels=1024, kernel_size=3, stride=1, padding=1)
        self.conv6 = nn.ConvTranspose2d(in_channels=1024, out_channels=512, kernel_size=3, stride=1, padding=1)
        self.conv7 = nn.ConvTranspose2d(in_channels=512, out_channels=256, kernel_size=3, stride=1, padding=1)
        self.conv8 = nn.ConvTranspose2d(in_channels=256, out_channels=128, kernel_size=3, stride=1, padding=1)
        self.conv9 = nn.ConvTranspose2d(in_channels=128, out_channels=64, kernel_size=3, stride=1, padding=1)
        self.conv10 = nn.ConvTranspose2d(in_channels=64, out_channels=2, kernel_size=3, stride=1, padding=1)

    def forward(self, x):

        # encoder
        x = self.conv1(x)
        x = self.leaky_reLU(x)
        size1 = x.size()
        x, indices1 = self.pool(x)

        x = self.conv2(x)
        x = self.leaky_reLU(x)
        size2 = x.size()
        x, indices2 = self.pool(x)

        x = self.conv3(x)
        x = self.leaky_reLU(x)
        size3 = x.size()
        x, indices3 = self.pool(x)

        x = self.conv4(x)
        x = self.leaky_reLU(x)
        size4 = x.size()
        x, indices4 = self.pool(x)

        ######################
        x = self.conv5(x)
        x = self.leaky_reLU(x)

        x = self.conv6(x)
        x = self.leaky_reLU(x)
        ######################

        # decoder
        x = self.unpool(x, indices4, output_size=size4)
        x = self.conv7(x)
        x = self.leaky_reLU(x)

        x = self.unpool(x, indices3, output_size=size3)
        x = self.conv8(x)
        x = self.leaky_reLU(x)

        x = self.unpool(x, indices2, output_size=size2)
        x = self.conv9(x)
        x = self.leaky_reLU(x)

        x = self.unpool(x, indices1, output_size=size1)
        x = self.conv10(x)
        x = self.softmax(x)

        return x

.

编辑 - 解决方案:

跳过连接是要走的路!

class AE(nn.Module):
    def __init__(self):
        super(AE, self).__init__()
        self.leaky_reLU = nn.LeakyReLU(0.2)
        self.pool = nn.MaxPool2d(kernel_size=2, stride=2, padding=1, return_indices=True)
        self.unpool = nn.MaxUnpool2d(kernel_size=2, stride=2, padding=1)
        self.softmax = nn.Softmax2d()

        self.conv1 = nn.Conv2d(in_channels=3, out_channels=64, kernel_size=3, stride=1, padding=1)
        self.conv2 = nn.Conv2d(in_channels=64, out_channels=128, kernel_size=3, stride=1, padding=1)
        self.conv3 = nn.Conv2d(in_channels=128, out_channels=256, kernel_size=3, stride=1, padding=1)
        self.conv4 = nn.Conv2d(in_channels=256, out_channels=512, kernel_size=3, stride=1, padding=1)
        self.conv5 = nn.Conv2d(in_channels=512, out_channels=1024, kernel_size=3, stride=1, padding=1)
        self.conv6 = nn.Conv2d(in_channels=1024, out_channels=512, kernel_size=3, stride=1, padding=1)
        self.conv7 = nn.Conv2d(in_channels=1024, out_channels=256, kernel_size=3, stride=1, padding=1)
        self.conv8 = nn.Conv2d(in_channels=512, out_channels=128, kernel_size=3, stride=1, padding=1)
        self.conv9 = nn.Conv2d(in_channels=256, out_channels=64, kernel_size=3, stride=1, padding=1)
        self.conv10 = nn.Conv2d(in_channels=128, out_channels=2, kernel_size=3, stride=1, padding=1)

    def forward(self, x):

        # encoder
        x = self.conv1(x)
        out1 = self.leaky_reLU(x)
        x = out1
        size1 = x.size()
        x, indices1 = self.pool(x)

        x = self.conv2(x)
        out2 = self.leaky_reLU(x)
        x = out2
        size2 = x.size()
        x, indices2 = self.pool(x)

        x = self.conv3(x)
        out3 = self.leaky_reLU(x)
        x = out3
        size3 = x.size()
        x, indices3 = self.pool(x)

        x = self.conv4(x)
        out4 = self.leaky_reLU(x)
        x = out4
        size4 = x.size()
        x, indices4 = self.pool(x)

        ######################
        x = self.conv5(x)
        x = self.leaky_reLU(x)

        x = self.conv6(x)
        x = self.leaky_reLU(x)
        ######################

        # decoder
        x = self.unpool(x, indices4, output_size=size4)
        x = self.conv7(torch.cat((x, out4), 1))
        x = self.leaky_reLU(x)

        x = self.unpool(x, indices3, output_size=size3)
        x = self.conv8(torch.cat((x, out3), 1))
        x = self.leaky_reLU(x)

        x = self.unpool(x, indices2, output_size=size2)
        x = self.conv9(torch.cat((x, out2), 1))
        x = self.leaky_reLU(x)

        x = self.unpool(x, indices1, output_size=size1)
        x = self.conv10(torch.cat((x, out1), 1))
        x = self.softmax(x)

        return x

【问题讨论】:

    标签: python tensorflow machine-learning deep-learning pytorch


    【解决方案1】:

    跳过连接通常用于编码器-解码器架构,它通过将外观信息从编码器的浅层(鉴别器)传递到解码器的相应深层(生成器)来帮助产生准确的结果。 Unet 是广泛使用的编码器-解码器类型架构。 Linknet 也很流行,它与 Unet 的不同之处在于将编码器层的外观信息与解码器层融合在一起。在 Unet 的情况下,传入的特征(来自编码器)在相应的解码器层中连接。另一方面,Linknet 执行加法,这就是为什么 Linknet 在单次前向传递中需要更少的操作,并且比 Unet 快得多。

    解码器中的每个卷积块可能如下所示:

    另外,我附上了一个描绘 Unet 和 LinkNet 架构的图。希望使用跳过连接会有所帮助。

    【讨论】:

    • 谢谢!这听起来很有希望,但我可以使用通常的解池操作而不是上采样吗?
    • 我不是 pytorch 方面的专家。但是,在forward 函数中,您可以为每个编码器层分配相同的名称,以便您可以访问相应解码器层中编码器层的输出特征,并将该特征与解码器层中的上采样特征连接/添加。我不确定,但ConvTranspose2d 也应该用于上采样,stride=2 应该比MaxUnpool2d 工作得更好,因为在ConvTranspose2d 的情况下,将学习上采样功能。正如我已经提到的,首先使用上采样,然后添加/连接,然后是卷积。
    • 很高兴知道它有帮助。
    【解决方案2】:

    除了使用 upconv 层,例如 nn.ConvTranspose2d,您可以在解码器部分使用插值返回到您的初始格式,例如 torch.nn.functional.interpolate .它会阻止您拥有棋盘工件。

    如果您希望解码器中的权重可学习,您还应该在每次插值后使用 conv 层,例如 nn.Conv2d

    【讨论】:

      【解决方案3】:

      这种模式是因为反卷积 (nn.ConvTranspose2d)。 article详细解释了。

      您可以尝试Upsample 作为替代方案。这不会提供棋盘图案。

      像这样工作:

      import torch 
      input = torch.arange(1, 5, dtype=torch.float32).view(1, 1, 2, 2)
      m = torch.nn.Upsample(scale_factor=2, mode='nearest')
      m(input)
      

      但是,您将无法使用 Upsample 学到任何东西。这只是一个转变。 所以这是一个交易。网上有很多论文如何处理不同问题的棋盘格模式。

      这个想法是训练你的网络,这样棋盘模式就消失了。

      【讨论】:

      • 就像我上面所说的,我根本无法替换 unpooling 操作
      • 那么很明显你需要学习你的网络来摆脱它。
      猜你喜欢
      • 1970-01-01
      • 2010-11-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-11-17
      • 2012-09-20
      • 2020-11-06
      • 1970-01-01
      相关资源
      最近更新 更多