web-dev-qa-db-ja.com

Numpyを使用するだけでConvolve2d

私はNumpyを使用して画像処理を研究しており、畳み込みによるフィルタリングの問題に直面しています。

グレースケール画像を畳み込みたい。 (より小さな2D配列で2D配列を畳み込みます)

誰も私の方法をrefineするアイデアを持っていますか?

scipy はconvolve2dをサポートしていることを知っていますが、Numpyを使用することによってのみconvolve2dを作成したいと思います。

私がしたこと

まず、2次元配列を部分行列にしました。

a = np.arange(25).reshape(5,5) # original matrix

submatrices = np.array([
     [a[:-2,:-2], a[:-2,1:-1], a[:-2,2:]],
     [a[1:-1,:-2], a[1:-1,1:-1], a[1:-1,2:]],
     [a[2:,:-2], a[2:,1:-1], a[2:,2:]]])

部分行列は複雑に見えますが、私がしていることを次の図に示します。

submatrices

次に、各サブマトリックスにフィルターを掛けました。

conv_filter = np.array([[0,-1,0],[-1,4,-1],[0,-1,0]])
multiplied_subs = np.einsum('ij,ijkl->ijkl',conv_filter,submatrices)

multiplied_subs

それらを合計しました。

np.sum(np.sum(multiplied_subs, axis = -3), axis = -3)
#array([[ 6,  7,  8],
#       [11, 12, 13],
#       [16, 17, 18]])

したがって、この手順は私のconvolve2dと呼ぶことができます。

def my_convolve2d(a, conv_filter):
    submatrices = np.array([
         [a[:-2,:-2], a[:-2,1:-1], a[:-2,2:]],
         [a[1:-1,:-2], a[1:-1,1:-1], a[1:-1,2:]],
         [a[2:,:-2], a[2:,1:-1], a[2:,2:]]])
    multiplied_subs = np.einsum('ij,ijkl->ijkl',conv_filter,submatrices)
    return np.sum(np.sum(multiplied_subs, axis = -3), axis = -3)

ただし、このmy_convolve2dは3つの理由で面倒です。

  1. 部分行列の生成は読みにくいため、フィルターが3 * 3の場合にのみ使用できます
  2. バリアント部分行列のサイズは、元の行列よりも約9倍大きいため、大きすぎるようです。
  3. 合計は少し直感的ではないようです。簡単に言えば、いです。

ここまで読んでくれてありがとう。

更新の種類。私は自分でconv3dを作成しました。これはパブリックドメインのままにします。

def convolve3d(img, kernel):
    # calc the size of the array of submatracies
    sub_shape = Tuple(np.subtract(img.shape, kernel.shape) + 1)

    # alias for the function
    strd = np.lib.stride_tricks.as_strided

    # make an array of submatracies
    submatrices = strd(img,kernel.shape + sub_shape,img.strides * 2)

    # sum the submatraces and kernel
    convolved_matrix = np.einsum('hij,hijklm->klm', kernel, submatrices)

    return convolved_matrix
12
Allosteric

as_strided[1] を使用してサブアレイを生成できます。

import numpy as np

a = np.array([[ 0,  1,  2,  3,  4],
       [ 5,  6,  7,  8,  9],
       [10, 11, 12, 13, 14],
       [15, 16, 17, 18, 19],
       [20, 21, 22, 23, 24]])

sub_shape = (3,3)
view_shape = Tuple(np.subtract(a.shape, sub_shape) + 1) + sub_shape
strides = a.strides + a.strides

sub_matrices = np.lib.stride_tricks.as_strided(a,view_shape,strides)

2番目の「ugい」合計を取り除くには、einsumを変更して、出力配列にjkのみが含まれるようにします。これは、2番目の合計を意味します。

conv_filter = np.array([[0,-1,0],[-1,5,-1],[0,-1,0]])
m = np.einsum('ij,ijkl->kl',conv_filter,sub_matrices)

# [[ 6  7  8]
#  [11 12 13]
#  [16 17 18]]
11
Crispin

また、fft(畳み込みを実行するためのより高速な方法の1つ)を使用することもできます。

from numpy.fft import fft2, ifft2
import numpy as np

def fft_convolve2d(x,y):
    """ 2D convolution, using FFT"""
    fr = fft2(x)
    fr2 = fft2(np.flipud(np.fliplr(y)))
    m,n = fr.shape
    cc = np.real(ifft2(fr*fr2))
    cc = np.roll(cc, -m/2+1,axis=0)
    cc = np.roll(cc, -n/2+1,axis=1)
    return cc
  • https://Gist.github.com/thearn/5424195
  • 画像と同じサイズになるようにフィルターをパディングする必要があります(zeros_likeマットの中央に配置します)。

乾杯、ダン

6
Dan Erez

上記のas_stridedおよび@Crispinのeinsumトリックを使用してクリーンアップしました。フィルターサイズを拡張された形状に強制します。インデックスに互換性がある場合は、非正方形入力も許可する必要があります。

def conv2d(a, f):
    s = f.shape + Tuple(np.subtract(a.shape, f.shape) + 1)
    strd = numpy.lib.stride_tricks.as_strided
    subM = strd(a, shape = s, strides = a.strides * 2)
    return np.einsum('ij,ijkl->kl', f, subM)
3
Daniel F