Python中的3D卷积

3

我需要编写一段代码,使用numpy在python中执行3D卷积,使用3x3的卷积核。对于像B&W图像这样的2D数组,我已经做得很好了,但是当我尝试将其扩展到像RGB这样的3D数组时,就会出现问题。我需要帮助改进我的方法。 以下是2D代码:

def convolucion_3x3(arreglo, kernel):
  (dim_x, dim_y) = arreglo.shape
  (ker_x, ker_y) = kernel.shape

  matriz_convolucionada = np.zeros((dim_x, dim_y))

  for i in range(dim_x):
    for j in range(dim_y):
      resultado = 0
      for x in range(-1, 2):
        try:
          if i + x not in range(dim_x):
              raise ValueError()
          for y in range(-1, 2):
            try:
              if j + y not in range(dim_y):
                  raise ValueError()

              resultado += arreglo[i + x, j + y] * kernel[x + 1][y + 1]
              '''
              Para el kernel sumo un 1 a cada índice para que lo corra desde 0 hasta 2 y no de -1 a 1
              '''
            except ValueError:
                pass
        except ValueError:
            pass
      matriz_convolucionada[i][j] = resultado
  return matriz_convolucionada

下面是我对RGB图像的尝试:

def convolucion(arreglo, kernel): (dim_x, dim_y, dim_z) = arreglo.shape (ker_x, ker_y) = kernel.shape


注意:本文中包含HTML标签,请勿删除。
matriz_convolucionada = np.zeros((dim_x, dim_y, dim_z))

for k in range(dim_z):
    for i in range(dim_x):
        for j in range(dim_y):
            resultado = 0
            for x in range(-1, 2):
                try:
                    if i + x not in range(dim_x):
                        raise ValueError()

                    for y in range(-1, 2):
                        try:
                            if j + y not in range(dim_y):
                                raise ValueError()

                            resultado += arreglo[i + x, j + y, k] * kernel[x + 1][y + 1]

                            '''
                            Para el kernel sumo un 1 a cada índice para que lo corra desde 0 hasta 2 y no de -1 a 1
                            '''

                        except ValueError:
                            pass

                except ValueError:
                    pass

            matriz_convolucionada[i][j][k] = resultado

return matriz_convolucionada

先修复你的缩进。 - user1767754
我已经修复了你第一个代码块的缩进。请注意,现在所有的代码都被高亮显示了,并且使用2个缩进而不是4个缩进使得代码在这个网站上更容易阅读。 - Richard
我认为应该将(dim_x, dim_y) = arreglo.shape改为(dim_y, dim_x) = arreglo.shape,其他变量同理。 - Jason
1个回答

7
虽然使用循环可以实现卷积,但是嵌套的循环可能难以理解。您可以考虑使用卷积定理来更轻松地执行卷积。请参见此处
使用numpy的fft模块,您可以计算原始图像堆栈的n维离散傅里叶变换,并将其乘以相同大小的核的n维傅里叶变换(文档在此处)。由于您的2D核是一个3x3数组,因此它是一个3x3xz的正方形'柱子'。您只需用零填充此数组即可相应增加维数。
尝试这个:
import numpy as np
import math

radius = 2
r2 = np.arange(-radius, radius+1)**2
sphere = r2[:, None, None] + r2[:, None] + r2
sphere -= np.max(sphere)
sphere = -sphere*2
array_len = 10*radius

array = np.zeros((array_len, array_len, array_len))
center = slice(array_len//2-radius,
               array_len//2+radius+1), slice(array_len//2-radius,
                                             array_len//2+radius+1),slice(array_len//2-radius,
                                                                          array_len//2+radius+1)
array[center] = sphere


k_len = 3
kernel_2D = np.ones((k_len,k_len))
kernel = np.zeros_like(array)

center_k = slice(array_len//2-math.ceil(k_len/2),
           array_len//2+k_len//2), slice(array_len//2-math.ceil(k_len/2),
                                         array_len//2+k_len//2)
for i in range(kernel.shape[2]):
    kernel[center_k+(i,)] = kernel_2D

def fft(array):
  fft = np.fft.ifftshift(np.fft.fftn(np.fft.fftshift(array)))
  return fft

def ifft(array):
  ifft = np.fft.fftshift(np.fft.ifftn(np.fft.ifftshift(array)))
  return ifft

def conv_3D(array, kernel):
  conv = np.abs(ifft(fft(array)*fft(kernel)))
  return conv

conv = conv_3D(array, kernel)

这个操作将一个半径为2的球体与一个边长为3的柱体进行卷积。

1
这个概念也在scipy中实现为scipy.signal.fftconvolve - bcattle

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接