Modo rapido / efficiente per scomporre i coefficienti di filtro 2D interi separabili


21

Vorrei essere in grado di determinare rapidamente se un dato kernel 2D di coefficienti interi è separabile in due kernel 1D con coefficienti interi. Per esempio

 2   3   2
 4   6   4
 2   3   2

è separabile in

 2   3   2

e

 1
 2
 1

Il vero test di separabilità sembra essere abbastanza semplice usando l'aritmetica intera, ma la decomposizione in filtri 1D con coefficienti interi si sta dimostrando un problema più difficile. La difficoltà sembra risiedere nel fatto che i rapporti tra righe o colonne possono essere non interi (frazioni razionali), ad esempio nell'esempio sopra abbiamo rapporti di 2, 1/2, 3/2 e 2/3.

Non voglio davvero usare un approccio pesante come SVD perché (a) è relativamente computazionalmente costoso per le mie esigenze e (b) non aiuta necessariamente a determinare i coefficienti interi .

Qualche idea ?


ULTERIORI INFORMAZIONI

I coefficienti possono essere positivi, negativi o zero e possono esserci casi patologici in cui la somma di uno o entrambi i vettori 1D è zero, ad es.

-1   2  -1
 0   0   0
 1  -2   1

è separabile in

 1  -2   1

e

-1
 0
 1

1
Ricordo di aver provato a capirlo al college. Ci sono quasi riuscito, ma non ricordo come. =) Non riesco a smettere di pensarci ora che l'hai menzionato!
Phonon,

@Phonon: heh - continua a pensare - Potrei trarre ispirazione da questo. ;-)
Paul R

È possibile fare la stessa cosa ma per valori double o float?
Diego Catalano,

@DiegoCatalano: vedi la risposta di Denis qui sotto e la domanda a cui si collega su math.stackexchange.com - Penso che potrebbe funzionare per il caso più generale di coefficienti in virgola mobile.
Paolo R,

@PaulR, come puoi contattarti via e-mail? Grazie.
Royi,

Risposte:


11

Ho preso @Phononla risposta e l'ho modificata in qualche modo in modo che utilizzi l'approccio GCD solo sulla riga superiore e sulla colonna sinistra, piuttosto che sulle somme riga / colonna. Questo sembra gestire un po 'meglio i casi patologici. Può comunque fallire se la riga superiore o la colonna sinistra sono tutte azzerate, ma è possibile verificare questi casi prima di applicare questo metodo.

function [X, Y, valid] = separate(M)    % separate 2D kernel M into X and Y vectors 
  X = M(1, :);                          % init X = top row of M
  Y = M(:, 1);                          % init Y = left column of M
  nx = numel(X);                        % nx = no of columns in M
  ny = numel(Y);                        % ny = no of rows in M
  gx = X(1);                            % gx = GCD of top row
  for i = 2:nx
    gx = gcd(gx, X(i));
  end
  gy = Y(1);                            % gy = GCD of left column
  for i = 2:ny
    gy = gcd(gy, Y(i));
  end
  X = X / gx;                           % scale X by GCD of X
  Y = Y / gy;                           % scale Y by GCD of Y
  scale = M(1, 1) / (X(1) * Y(1));      % calculate scale factor
  X = X * scale;                        % apply scale factor to X
  valid = all(all((M == Y * X)));       % result valid if we get back our original M
end

Mille grazie @Phonone @Jason Rper le idee originali per questo.


10

Fatto! Pubblicando il codice MATLAB, pubblicheremo una spiegazione stasera o domani

% Two original arrays
N = 3;
range = 800;
a = round( range*(rand(N,1)-0.5) )
b = round( range*(rand(1,N)-0.5) )

% Create a matrix;
M = a*b;
N = size(M,1);

% Sanity check
disp([num2str(rank(M)) ' <- this should be 1!']);

% Sum across rows and columns
Sa = M * ones(N,1);
Sb = ones(1,N) * M;

% Get rid of zeros
SSa = Sa( Sa~=0 );
SSb = Sb( Sb~=0 );

if isempty(SSa) | isempty(SSb)
    break;
end

% Sizes of array without zeros
Na = numel(SSa);
Nb = numel(SSb);

% Find Greatest Common Divisor of Sa and Sb.
Ga = SSa(1);
Gb = SSb(1);

for l=2:Na
    Ga = gcd(Ga,SSa(l));
end

for l=2:Nb
    Gb = gcd(Gb,SSb(l));
end

%Divide by the greatest common divisor
Sa = Sa / Ga;
Sb = Sb / Gb;

%Scale one of the vectors
MM = Sa * Sb;
Sa = Sa * (MM(1) / M(1));

disp('Two arrays found:')
Sa
Sb
disp('Sa * Sb = ');
Sa*Sb
disp('Original = ');
M

Grazie - è grandioso - stavo dormendo sveglio la scorsa notte pensando di scomporre i coefficienti ecc., Ma usare il GCD in questo modo è molto più semplice ed elegante. Sfortunatamente c'è ancora una ruga da appianare: deve lavorare con coefficienti positivi e negativi e questo può portare a casi degenerati, ad es A=[-2 1 0 -1 2]; B=[2 -3 6 0 -1]; M=A'*B;. Il problema qui è che è sum(A) = 0così Sb = [0 0 0 0 0]. Proverò a modificare il tuo algoritmo in modo che utilizzi la somma dei valori assoluti dei coefficienti e vedrò se ciò aiuta. Grazie ancora per il vostro aiuto.
Paolo R

OK - sembra che è ancora possibile ottenere le GCDS e fare il ridimensionamento utilizzando abs(M), ad esempio Sa=abs(M)*ones(N,1); Sb=ones(1,N)*abs(M);e poi continuare come sopra, ma non posso ancora vedere come ripristinare le indicazioni per Sa, Sballa fine. Ho aggiunto un esempio patologico che illustra il problema nella domanda originale sopra.
Paul R

Penso di avere una soluzione funzionante ora: l'ho pubblicata come risposta separata, ma il merito va a te per l'idea di base. Grazie ancora !
Paolo R

7

Forse sto banalizzando il problema, ma sembra che potresti:

  • NMAaii=0,1,,N1
  • j>0

    • aja0jrj
    • rj
    • rjaja0j0x
    • aja0
  • x

xk,norm=xkmini=0N1xi
  • xnorm
    xscaled=Kxnorm,K=1,2,,M
    KM

Non è il metodo più elegante ed è probabile che esista un modo migliore, ma dovrebbe funzionare, è abbastanza semplice da implementare e dovrebbe essere relativamente veloce per matrici di dimensioni modeste.


Grazie - Penso che probabilmente mi stavo dirigendo verso qualcosa di simile in questa direzione prima di impantanarmi nei dettagli. Non è chiaro al 100% che arriverai sempre a una soluzione usando questo metodo, ma probabilmente dovrei codificarlo e provarlo con alcuni esempi. Ho la sensazione che potrebbe essere necessario applicare sia la riga che la colonna per vedere quale produce la soluzione "migliore". Grazie per aver dedicato del tempo a precisare i dettagli: mi occuperò di questo e ti farò sapere come funziona.
Paul R

Non potresti trovare il massimo comune divisore dei primi elementi delle righe e utilizzarlo per determinare il tuo vettore di base?
Jim Clay,

@JimClay: Sì, è effettivamente quello che stai facendo alla fine, se hai quella funzionalità disponibile.
Jason R,

3

xyzA|Axyz|
x y z
yzxx y z x y z ... a sua volta.

(Dall'approssimazione -a-convoluzione-come-una-somma-di-separabile-convoluzioni su math.stackexchange.)


1
Cerca di non rispondere alle domande con link inspiegabili. È meglio spiegare i dettagli necessari nella risposta e includere il collegamento solo come riferimento; in questo modo se il link rompe i dettagli essenziali della risposta sono ancora lì.
Sam Maloney,

@SamMaloney: non vedo alcun motivo per cui sia necessario. Il link spiega tutto in dettaglio. Verrà comunque visualizzato nella ricerca Domande e risposte. Quindi perche no?
Naresh,

1
@Naresh Lo menziono solo perché uno degli obiettivi dei siti di scambio di stack è quello di creare un repository di domande con risposta per riferimento futuro. Quindi, anche se capisco che questo particolare link si trova su un altro sito SE e dovrebbe essere abbastanza sicuro, è una buona pratica generale non contare su link che funzionano ancora tra diversi anni. . Dare un quadro generale di queste "due semplici metodi nella risposta dovrebbe garantire che le informazioni viene mantenuta anche se succede qualcosa alla domanda legata Come ho detto, però, questo è stato più di un commento generale sulle migliori pratiche per quanto riguarda i collegamenti in risposte.
Sam Maloney,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.