Je sais que, dans le cas 1D, la convolution entre deux vecteurs, a
et b
, peut être calculée comme conv(a, b)
, mais aussi comme le produit entre les T_a
et b
, où T_a
est la matrice de Toeplitz correspondante pour a
.
Est-il possible d'étendre cette idée à la 2D?
Donné a = [5 1 3; 1 1 2; 2 1 3]
et b=[4 3; 1 2]
, est-il possible de convertir a
dans une matrice de Toeplitz et de calculer le produit matrice-matrice entre T_a
et b
comme dans le cas 1-D?
Oui, c'est possible et vous devez également utiliser une matrice circulante à double bloc (qui est un cas particulier de la matrice Toeplitz ). Je vais vous donner un exemple avec une petite taille de noyau et l'entrée, mais il est possible de construire une matrice Toeplitz pour n'importe quel noyau. Vous avez donc une entrée 2d x
et un noyau 2d k
et vous voulez calculer la convolution x * k
. Supposons également que k
est déjà retourné. Supposons également que x
ait la taille n×n
et k
est m×m
.
Vous déroulez donc k
dans une matrice clairsemée de taille (n-m+1)^2 × n^2
, et déroulez x
dans un long vecteur n^2 × 1
. Vous calculez une multiplication de cette matrice clairsemée avec un vecteur et convertissez le vecteur résultant (qui aura une taille (n-m+1)^2 × 1
) dans une n-m+1
Matrice Carrée.
Je suis presque sûr que c'est difficile à comprendre rien qu'en lisant. Voici donc un exemple pour le noyau 2 × 2 et l'entrée 3 × 3.
Voici une matrice construite avec un vecteur:
Et c'est le même résultat que vous auriez obtenu en faisant une fenêtre coulissante de k
sur x
.
Soit [~ # ~] i [~ # ~] le signal d'entrée et [~ # ~] f [~ # ~] être le filtre ou le noyau.
Si I est m1 x n1 et F est m2 x n2, la taille de la sortie sera:
Mettez le filtre à zéro pour lui donner la même taille que la sortie.
Maintenant, toutes ces petites matrices de Toeplitz doivent être disposées dans une grande matrice de Toeplitz doublement bloquée.
Cette multiplication donne le résultat de la convolution.
Pour plus de détails et python jetez un œil à mon dépôt github:
Si vous démêlez k en un vecteur m ^ 2 et déroulez X, vous obtiendrez alors:
m**2
vecteur k
((n-m)**2, m**2)
matrice pour unrolled_X
où unrolled_X
peut être obtenu par le code Python code:
from numpy import zeros
def unroll_matrix(X, m):
flat_X = X.flatten()
n = X.shape[0]
unrolled_X = zeros(((n - m) ** 2, m**2))
skipped = 0
for i in range(n ** 2):
if (i % n) < n - m and ((i / n) % n) < n - m:
for j in range(m):
for l in range(m):
unrolled_X[i - skipped, j * m + l] = flat_X[i + j * n + l]
else:
skipped += 1
return unrolled_X
Dérouler X et non k permet une représentation plus compacte (matrices plus petites) que l'inverse pour chaque X - mais vous devez dérouler chaque X. Vous pourriez préférer dérouler k en fonction de ce que vous voulez faire.
Ici le unrolled_X
n'est pas rare, alors que unrolled_k
serait rare, mais de taille ((n-m+1)^2,n^2)
comme l'a mentionné @Salvador Dali.
Dérouler k
pourrait se faire comme ceci:
from scipy.sparse import lil_matrix
from numpy import zeros
import scipy
def unroll_kernel(kernel, n, sparse=True):
m = kernel.shape[0]
if sparse:
unrolled_K = lil_matrix(((n - m)**2, n**2))
else:
unrolled_K = zeros(((n - m)**2, n**2))
skipped = 0
for i in range(n ** 2):
if (i % n) < n - m and((i / n) % n) < n - m:
for j in range(m):
for l in range(m):
unrolled_K[i - skipped, i + j * n + l] = kernel[j, l]
else:
skipped += 1
return unrolled_K
Le code ci-dessus ne produit pas la matrice déroulée des bonnes dimensions. La dimension doit être (n-k + 1) * (m-k + 1), (k) (k). k: dimension du filtre, n: num lignes dans la matrice d'entrée, m: num colonnes.
def unfold_matrix(X, k):
n, m = X.shape[0:2]
xx = zeros(((n - k + 1) * (m - k + 1), k**2))
row_num = 0
def make_row(x):
return x.flatten()
for i in range(n- k+ 1):
for j in range(m - k + 1):
#collect block of m*m elements and convert to row
xx[row_num,:] = make_row(X[i:i+k, j:j+k])
row_num = row_num + 1
return xx
Pour plus de détails, consultez mon article de blog: