Implementazione ottimale della deformazione del trasporto in Matlab


11

Sto implementando il documento " Trasporto di massa ottimale per la registrazione e la deformazione ", il mio obiettivo è quello di metterlo online poiché non riesco proprio a trovare alcun codice di trasporto di massa euleriano online e questo sarebbe interessante almeno per la comunità di ricerca nell'elaborazione delle immagini.

Il documento può essere riassunto come segue:
- trova una mappa iniziale usando gli abbinamenti dell'istogramma 1D lungo le coordinate xey - risolvi per il punto fisso di u t = 1u
ut=1μ0Du1div(u)u1Du
dt<min|1μ01div(u)|

Per le simulazioni numeriche (eseguite su una griglia regolare), indicano l'uso del poicalc di matlab per risolvere l'equazione di poisson, usano differenze finite centrate per le derivate spaziali, ad eccezione di che viene calcolato usando uno schema controvento.Du

Usando il mio codice, l'energia funzionale e l'arricciatura della mappatura stanno diminuendo correttamente per un paio di iterazioni (da poche decine a poche migliaia a seconda del passo temporale). Ma dopo, la simulazione esplode: l'energia aumenta per raggiungere un NAN in pochissime iterazioni. Ho provato diversi ordini per le differenziazioni e le integrazioni (una sostituzione di ordine superiore a cumptrapz può essere trovata qui ) e diversi schemi di interpolazione, ma ottengo sempre lo stesso problema (anche su immagini molto fluide, diverse da zero ovunque ecc.).
Qualcuno sarebbe interessato a guardare il codice e / o il problema teorico che sto affrontando? Il codice è piuttosto breve.

Sostituire gradiente2 () alla fine con gradiente (). Questo era un gradiente di ordine superiore ma non risolveva neppure le cose.

Per il momento sono interessato solo alla parte di trasporto ottimale del documento, non al termine aggiuntivo di regolarizzazione.

Grazie !

Risposte:


5

Il mio buon amico Pascal l'ha fatto pochi anni fa (è quasi in Matlab):

#! /usr/bin/env python

#from scipy.interpolate import interpolate
from pylab import *
from numpy import *


def GaussianFilter(sigma,f):
    """Apply Gaussian filter to an image"""
    if sigma > 0:
        n = ceil(4*sigma)
        g = exp(-arange(-n,n+1)**2/(2*sigma**2))
        g = g/g.sum()

        fg = zeros(f.shape)

        for i in range(f.shape[0]):
            fg[i,:] = convolve(f[i,:],g,'same')
        for i in range(f.shape[1]):
            fg[:,i] = convolve(fg[:,i],g,'same')
    else:
        fg = f

    return fg


def clamp(x,xmin,xmax):
    """Clamp values between xmin and xmax"""
    return minimum(maximum(x,xmin),xmax)


def myinterp(f,xi,yi):
    """My bilinear interpolator (scipy's has a segfault)"""
    M,N = f.shape
    ix0 = clamp(floor(xi),0,N-2).astype(int)
    iy0 = clamp(floor(yi),0,M-2).astype(int)
    wx = xi - ix0
    wy = yi - iy0
    return ( (1-wy)*((1-wx)*f[iy0,ix0] + wx*f[iy0,ix0+1]) +
        wy*((1-wx)*f[iy0+1,ix0] + wx*f[iy0+1,ix0+1]) )


def mkwarp(f1,f2,sigma,phi,showplot=0):
    """Image warping by solving the Monge-Kantorovich problem"""
    M,N = f1.shape[:2]

    alpha = 1
    f1 = GaussianFilter(sigma,f1)
    f2 = GaussianFilter(sigma,f2)

    # Shift indices for going from vertices to cell centers
    iUv = arange(M)             # Up
    iDv = arange(1,M+1)         # Down
    iLv = arange(N)             # Left
    iRv = arange(1,N+1)         # Right
    # Shift indices for cell centers (to cell centers)
    iUc = r_[0,arange(M-1)]
    iDc = r_[arange(1,M),M-1]
    iLc = r_[0,arange(N-1)]
    iRc = r_[arange(1,N),N-1]
    # Shifts for going from centers to vertices
    iUi = r_[0,arange(M)]
    iDi = r_[arange(M),M-1]
    iLi = r_[0,arange(N)]
    iRi = r_[arange(N),N-1]


    ### The main gradient descent loop ###      
    for iter in range(0,30):
        ### Approximate derivatives ###
        # Compute gradient phix and phiy at pixel centers.  Array phi has values
        # at the pixel vertices.
        phix = (phi[iUv,:][:,iRv] - phi[iUv,:][:,iLv] + 
            phi[iDv,:][:,iRv] - phi[iDv,:][:,iLv])/2
        phiy = (phi[iDv,:][:,iLv] - phi[iUv,:][:,iLv] + 
            phi[iDv,:][:,iRv] - phi[iUv,:][:,iRv])/2
        # Compute second derivatives at pixel centers using central differences.
        phixx = (phix[:,iRc] - phix[:,iLc])/2
        phixy = (phix[iDc,:] - phix[iUc,:])/2
        phiyy = (phiy[iDc,:] - phiy[iUc,:])/2
        # Hessian determinant
        detD2 = phixx*phiyy - phixy*phixy

        # Interpolate f2 at (phix,phiy) with bilinear interpolation
        f2gphi = myinterp(f2,phix,phiy)

        ### Update phi ###
        # Compute M'(phi) at pixel centers
        dM = alpha*(f1 - f2gphi*detD2)
        # Interpolate to pixel vertices
        phi = phi - (dM[iUi,:][:,iLi] + 
            dM[iDi,:][:,iLi] + 
            dM[iUi,:][:,iRi] + 
            dM[iDi,:][:,iRi])/4


    ### Plot stuff ###      
    if showplot:
        pad = 2
        x,y = meshgrid(arange(N),arange(M))
        x = x[pad:-pad,:][:,pad:-pad]
        y = y[pad:-pad,:][:,pad:-pad]
        phix = phix[pad:-pad,:][:,pad:-pad]
        phiy = phiy[pad:-pad,:][:,pad:-pad]

        # Vector plot of the mapping
        subplot(1,2,1)
        quiver(x,y,flipud(phix-x),-flipud(phiy-y))
        axis('image')
        axis('off')
        title('Mapping')

        # Grayscale plot of mapping divergence
        subplot(1,2,2)  
        divs = phixx + phiyy # Divergence of mapping s(x)
        imshow(divs[pad:-pad,pad:-pad],cmap=cm.gray)
        axis('off')
        title('Divergence of Mapping')
        show()

    return phi


if __name__ == "__main__":  # Demo
    from pylab import *
    from numpy import * 

    f1 = imread('brain-tumor.png')
    f2 = imread('brain-healthy.png')
    f1 = f1[:,:,1]
    f2 = f2[:,:,1]

    # Initialize phi as the identity map
    M,N = f1.shape
    n,m = meshgrid(arange(N+1),arange(M+1))
    phi = ((m-0.5)**2 + (n-0.5)**2)/2

    sigma = 3
    phi = mkwarp(f1,f2,sigma,phi)
    phi = mkwarp(f1,f2,sigma/2,phi,1)
#   phi = mkwarp(f1,f2,sigma/4,phi,1)

Test eseguito, dura circa 2 secondi.

L'approccio della discesa gradiente è spiegato qui: people.clarkson.edu/~ebollt/Papers/quadcost.pdf


eccellente .. grazie mille! Proverò questo codice e lo confronterò con il mio per verificare la presenza di errori. Questo approccio sembra in realtà essere la versione locale del documento di Haker et al. di cui ho parlato - vale a dire, senza risolvere per un laplaciano. Grazie ancora !
WhitAngl,

Finalmente sto riscontrando un paio di problemi con questo codice ...: se computo sono abbastanza lontano da (con l'assia) - anche quando rimuovo il gaussiano sfocatura. Inoltre, se aumento il numero di iterazioni a un paio di migliaia, il codice esplode e fornisce valori NaN (e arresti anomali). Qualche idea ? Grazie ! f2(ϕ)detHϕf1H
WhitAngl,

Più sfocatura aiuta con il problema NaN?
dranxo,

Sì, in effetti, dopo altri test aiuta con più sfocatura - grazie !. Ora sto provando 8 passaggi con una sfocatura iniziale di 140 pixel di deviazione standard fino a 1 pixel stdev (ancora in elaborazione). Ho ancora una quantità significativa dell'immagine originale nel mio ultimo risultato (con sfocatura 64px). Controllerò anche l'eventuale arricciamento residuo in . ϕ
WhitAngl,

Oh ok, bene. Penso. La sfocatura è presente poiché le immagini naturalmente non sono continue (bordi) e il gradiente sarà problematico. Spero che tu stia ancora ottenendo buone risposte senza offuscare troppo.
dranxo,
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.