如何在具有三个(RGB)通道的图像上计算卷积?

How is a convolution calculated on an image with three (RGB) channels?

假设我们有一个单通道图像 (5x5)

A = [ 1 2 3 4 5
      6 7 8 9 2
      1 4 5 6 3
      4 5 6 7 4
      3 4 5 6 2 ]

还有一个过滤器 K (2x2)

K = [ 1 1
      1 1 ]

一个应用卷积的例子(让我们从 A 中取出第一个 2x2)是

1*1 + 2*1 + 6*1 + 7*1 = 16

这很简单。但是让我们为矩阵 A 引入一个深度因子,即在深度网络中具有 3 个通道或什至 conv 层的 RGB 图像(深度可能 = 512)。如何使用相同的过滤器完成卷积运算? 类似的计算对于 RGB 情况将非常有帮助。

它们将与您处理单通道图像的方式相同,只是您将获得三个矩阵而不是一个。 This是一篇关于CNN基础知识的讲义,我想可能对你有帮助。

在卷积神经网络中,卷积运算的实现方式如下,(注意:模糊/滤波运算中的卷积是分开的)

对于类似 RGB 的输入,过滤器实际上是 223,每个过滤器对应一个颜色通道,从而产生三个过滤器响应。这三个加起来就是一个由偏置和激活引起的流动。最后,这是输出图中的一个像素。

假设我们有一个由某个矩阵 A 给出的 3 通道 (RGB) 图像


    A = [[[198 218 227]
          [196 216 225]
          [196 214 224]
          ...
          ...
          [185 201 217]
          [176 192 208]
          [162 178 194]]

和一个模糊内核为


    K = [[0.1111, 0.1111, 0.1111],
         [0.1111, 0.1111, 0.1111],
         [0.1111, 0.1111, 0.1111]]

    #which is actually 0.111 ~= 1/9

卷积可以表示如下图所示

正如您在图像中看到的那样,每个通道都单独进行卷积,然后组合形成一个像素。

如果你想在 RGB 图像上实现 Conv2d,那么这个在 pytorch 中的实现应该有所帮助。

抓取一张图像并将其设为 uint8 的 numpy ndarray (note that imshow needs uint8 to be values between 0-255 whilst floats should be between 0-1):

link = 'https://oldmooresalmanac.com/wp-content/uploads/2017/11/cow-2896329_960_720-Copy-476x459.jpg'

r = requests.get(link, timeout=7)
im = Image.open(BytesIO(r.content))
pic = np.array(im)

您可以通过

查看
f, axarr = plt.subplots()
axarr.imshow(pic)
plt.show()

创建你的卷积层(以随机权重启动)

conv_layer = nn.Conv2d(in_channels=3, 
           out_channels=3,kernel_size=3, 
           stride=1, bias=None)

将输入图像转换为浮动并添加一个空维度,因为这是 pytorch 期望的输入

pic_float = np.float32(pic)
pic_float = np.expand_dims(pic_float,axis=0)

运行 通过卷积层的图像(围绕维度位置排列变化,使它们与 pytorch 所期望的相匹配)

out = conv_layer(torch.tensor(pic_float).permute(0,3,1,2))

删除我们添加的额外第一个 dim(可视化不需要),从 GPU 分离并转换为 numpy ndarray

out = out.permute(0,2,3,1).detach().numpy()[0, :, :, :]

可视化输出(转换为 uint8,这是我们开始的地方)

f, axarr = plt.subplots()
axarr.imshow(np.uint8(out))
plt.show()

然后您可以通过访问过滤器来更改过滤器的权重。例如:

kernel = torch.Tensor([[[[0.01, 0.02, 0.01],
                     [0.02, 0.04, 0.02],
                     [0.01, 0.02, 0.01]]]])

kernel = kernel.repeat(3, 3, 1, 1)
conv_layer.weight.data = kernel