Mise en œuvre optimale de la déformation de transport dans Matlab

11

J'implémente le document " Transport de masse optimal pour l'enregistrement et la déformation ", mon objectif étant de le mettre en ligne car je ne trouve aucun code de transport de masse eulérien en ligne et cela serait intéressant au moins pour la communauté des chercheurs en traitement d'images.

Le document peut être résumé comme suit:
- trouver une carte initiale u utilisant des correspondances d'histogramme 1D le long des coordonnées x et y
- résoudre le point fixe de ut=1μ0Du1div(u)u1Du
dt<min|1μ01div(u)|

Pour les simulations numériques (effectuées sur une grille régulière), elles indiquent l'utilisation du poicalc de matlab pour résoudre l'équation du poisson, elles utilisent des différences finies centrées pour les dérivées spatiales, à l'exception de qui est calculé à l'aide d'un schéma au près.Du

En utilisant mon code, la fonction énergétique et la boucle de la cartographie diminuent correctement pendant quelques itérations (de quelques dizaines à quelques milliers selon le pas de temps). Mais après cela, la simulation explose: l'énergie augmente pour atteindre un NAN en très peu d'itérations. J'ai essayé plusieurs commandes pour les différenciations et les intégrations (un remplacement d'ordre supérieur à cumptrapz peut être trouvé ici ), et différents schémas d'interpolation, mais j'ai toujours le même problème (même sur des images très lisses, non nul partout, etc.).
N'importe qui serait intéressé à regarder le code et / ou le problème théorique auquel je suis confronté? Le code est assez court.

Veuillez remplacer gradient2 () à la fin par gradient (). C'était un gradient d'ordre supérieur mais ne résout pas non plus les choses.

Pour l'instant, je ne m'intéresse qu'à la partie transport optimale du papier, pas au terme de régularisation supplémentaire.

Merci !

WhitAngl
la source

Réponses:

5

Mon bon ami Pascal l'a fait il y a quelques années (c'est presque dans 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, dure environ 2 secondes.

L'approche de descente en gradient est expliquée ici: people.clarkson.edu/~ebollt/Papers/quadcost.pdf

dranxo
la source
excellent .. merci beaucoup! Je vais essayer ce code et comparer avec le mien pour vérifier mes erreurs. Cette approche semble en fait être la version locale de l'article de Haker et al. que j'ai mentionné - c'est-à-dire sans résoudre pour un laplacien. Merci encore !
WhitAngl
Je rencontre enfin quelques problèmes avec ce code ...: si je calcule je suis assez loin de (avec la jute) - même lors de la suppression de la gaussienne brouiller. De plus, si j'augmente le nombre d'itérations à quelques milliers, le code explose et donne des valeurs NaN (et se bloque). Une idée ? Merci ! f2(ϕ)detHϕf1H
WhitAngl
Est-ce que plus de flou aide avec le problème NaN?
dranxo
Oui, en effet, après plus de tests, cela aide à plus de flou - merci !. J'essaie maintenant 8 étapes avec un flou de départ de 140 pixels d'écart type jusqu'à 1 pixel stdev (toujours en calcul). J'ai toujours une quantité importante de l'image d'origine dans mon dernier résultat (avec un flou de 64 pixels). Je vérifierai également toute boucle restante dans . ϕ
WhitAngl
Oh ok, bien. Je pense. Le flou est là car les images ne sont naturellement pas continues (bords) et le dégradé sera problématique. J'espère que vous obtenez toujours de bonnes réponses sans trop brouiller.
dranxo