Пример #1
0
def v_cycle_3d(n, k, delta_field, sigma_sq_field, gradient_field, target,
               lambda_param, displacement, depth=0):
    r"""Multi-resolution Gauss-Seidel solver using V-type cycles

    Multi-resolution Gauss-Seidel solver: solves the linear system by first
    filtering (GS-iterate) the current level, then solves for the residual
    at a coarser resolution and finally refines the solution at the current
    resolution. This scheme corresponds to the V-cycle proposed by Bruhn and
    Weickert[1].
    [1] Andres Bruhn and Joachim Weickert, "Towards ultimate motion estimation:
        combining highest accuracy with real-time performance",
        10th IEEE International Conference on Computer Vision, 2005.
        ICCV 2005.

    Parameters
    ----------
    n : int
        number of levels of the multi-resolution algorithm (it will be called
        recursively until level n == 0)
    k : int
        the number of iterations at each multi-resolution level
    delta_field : array, shape (S, R, C)
        the difference between the static and moving image (the 'derivative
        w.r.t. time' in the optical flow model)
    sigma_sq_field : array, shape (S, R, C)
        the variance of the gray level value at each voxel, according to the
        EM model (for SSD, it is 1 for all voxels). Inf and 0 values
        are processed specially to support infinite and zero variance.
    gradient_field : array, shape (S, R, C, 3)
        the gradient of the moving image
    target : array, shape (S, R, C, 3)
        right-hand side of the linear system to be solved in the Weickert's
        multi-resolution algorithm
    lambda_param : float
        smoothness parameter, the larger its value the smoother the
        displacement field
    displacement : array, shape (S, R, C, 3)
        the displacement field to start the optimization from

    Returns
    -------
    energy : the energy of the EM (or SSD if sigmafield[...]==1) metric at this
        iteration
    """
    # pre-smoothing
    for i in range(k):
        ssd.iterate_residual_displacement_field_ssd_3d(delta_field,
                                                       sigma_sq_field,
                                                       gradient_field,
                                                       target,
                                                       lambda_param,
                                                       displacement)
    if n == 0:
        energy = ssd.compute_energy_ssd_3d(delta_field)
        return energy
    # solve at coarser grid
    residual = ssd.compute_residual_displacement_field_ssd_3d(delta_field,
                                                              sigma_sq_field,
                                                              gradient_field,
                                                              target,
                                                              lambda_param,
                                                              displacement,
                                                              None)
    sub_residual = np.array(vfu.downsample_displacement_field_3d(residual))
    del residual
    subsigma_sq_field = None
    if sigma_sq_field is not None:
        subsigma_sq_field = vfu.downsample_scalar_field_3d(sigma_sq_field)
    subdelta_field = vfu.downsample_scalar_field_3d(delta_field)
    subgradient_field = np.array(
        vfu.downsample_displacement_field_3d(gradient_field))
    shape = np.array(displacement.shape).astype(np.int32)
    sub_displacement = np.zeros(
        shape=((shape[0]+1)//2, (shape[1]+1)//2, (shape[2]+1)//2, 3),
        dtype=floating)
    sublambda_param = lambda_param*0.25
    v_cycle_3d(n-1, k, subdelta_field, subsigma_sq_field, subgradient_field,
               sub_residual, sublambda_param, sub_displacement, depth+1)
    del subdelta_field
    del subsigma_sq_field
    del subgradient_field
    del sub_residual
    displacement += vfu.resample_displacement_field_3d(sub_displacement,
                                                       0.5 * np.ones(3),
                                                       shape)
    del sub_displacement
    # post-smoothing
    for i in range(k):
        ssd.iterate_residual_displacement_field_ssd_3d(delta_field,
                                                       sigma_sq_field,
                                                       gradient_field,
                                                       target,
                                                       lambda_param,
                                                       displacement)
    energy = ssd.compute_energy_ssd_3d(delta_field)
    return energy
Пример #2
0
def v_cycle_3d(n,
               k,
               delta_field,
               sigma_sq_field,
               gradient_field,
               target,
               lambda_param,
               displacement,
               depth=0):
    r"""Multi-resolution Gauss-Seidel solver using V-type cycles

    Multi-resolution Gauss-Seidel solver: solves the linear system by first
    filtering (GS-iterate) the current level, then solves for the residual
    at a coarser resolution and finally refines the solution at the current
    resolution. This scheme corresponds to the V-cycle proposed by Bruhn and
    Weickert[1].
    [1] Andres Bruhn and Joachim Weickert, "Towards ultimate motion estimation:
        combining highest accuracy with real-time performance",
        10th IEEE International Conference on Computer Vision, 2005.
        ICCV 2005.

    Parameters
    ----------
    n : int
        number of levels of the multi-resolution algorithm (it will be called
        recursively until level n == 0)
    k : int
        the number of iterations at each multi-resolution level
    delta_field : array, shape (S, R, C)
        the difference between the static and moving image (the 'derivative
        w.r.t. time' in the optical flow model)
    sigma_sq_field : array, shape (S, R, C)
        the variance of the gray level value at each voxel, according to the
        EM model (for SSD, it is 1 for all voxels). Inf and 0 values
        are processed specially to support infinite and zero variance.
    gradient_field : array, shape (S, R, C, 3)
        the gradient of the moving image
    target : array, shape (S, R, C, 3)
        right-hand side of the linear system to be solved in the Weickert's
        multi-resolution algorithm
    lambda_param : float
        smoothness parameter, the larger its value the smoother the
        displacement field
    displacement : array, shape (S, R, C, 3)
        the displacement field to start the optimization from

    Returns
    -------
    energy : the energy of the EM (or SSD if sigmafield[...]==1) metric at this
        iteration
    """
    # pre-smoothing
    for i in range(k):
        ssd.iterate_residual_displacement_field_ssd_3d(delta_field,
                                                       sigma_sq_field,
                                                       gradient_field, target,
                                                       lambda_param,
                                                       displacement)
    if n == 0:
        energy = ssd.compute_energy_ssd_3d(delta_field)
        return energy
    # solve at coarser grid
    residual = ssd.compute_residual_displacement_field_ssd_3d(
        delta_field, sigma_sq_field, gradient_field, target, lambda_param,
        displacement, None)
    sub_residual = np.array(vfu.downsample_displacement_field_3d(residual))
    del residual
    subsigma_sq_field = None
    if sigma_sq_field is not None:
        subsigma_sq_field = vfu.downsample_scalar_field_3d(sigma_sq_field)
    subdelta_field = vfu.downsample_scalar_field_3d(delta_field)
    subgradient_field = np.array(
        vfu.downsample_displacement_field_3d(gradient_field))
    shape = np.array(displacement.shape).astype(np.int32)
    sub_displacement = np.zeros(shape=((shape[0] + 1) // 2,
                                       (shape[1] + 1) // 2,
                                       (shape[2] + 1) // 2, 3),
                                dtype=floating)
    sublambda_param = lambda_param * 0.25
    v_cycle_3d(n - 1, k, subdelta_field, subsigma_sq_field, subgradient_field,
               sub_residual, sublambda_param, sub_displacement, depth + 1)
    del subdelta_field
    del subsigma_sq_field
    del subgradient_field
    del sub_residual
    displacement += vfu.resample_displacement_field_3d(sub_displacement,
                                                       0.5 * np.ones(3), shape)
    del sub_displacement
    # post-smoothing
    for i in range(k):
        ssd.iterate_residual_displacement_field_ssd_3d(delta_field,
                                                       sigma_sq_field,
                                                       gradient_field, target,
                                                       lambda_param,
                                                       displacement)
    energy = ssd.compute_energy_ssd_3d(delta_field)
    return energy
Пример #3
0
def test_compute_residual_displacement_field_ssd_3d():
    #Select arbitrary images' shape (same shape for both images)
    sh = (20, 15, 10)

    #Select arbitrary centers
    c_f = np.asarray(sh)/2
    c_g = c_f + 0.5

    #Compute the identity vector field I(x) = x in R^2
    x_0 = np.asarray(range(sh[0]))
    x_1 = np.asarray(range(sh[1]))
    x_2 = np.asarray(range(sh[2]))
    X = np.ndarray(sh + (3,), dtype = np.float64)
    O = np.ones(sh)
    X[...,0]= x_0[:, None, None] * O
    X[...,1]= x_1[None, :, None] * O
    X[...,2]= x_2[None, None, :] * O


    #Compute the gradient fields of F and G
    np.random.seed(9223102)

    grad_F = X - c_f
    grad_G = X - c_g

    Fnoise = np.random.ranf(np.size(grad_F)).reshape(grad_F.shape) * grad_F.max() * 0.1
    Fnoise = Fnoise.astype(floating)
    grad_F += Fnoise

    Gnoise = np.random.ranf(np.size(grad_G)).reshape(grad_G.shape) * grad_G.max() * 0.1
    Gnoise = Gnoise.astype(floating)
    grad_G += Gnoise

    #The squared norm of grad_G
    sq_norm_grad_G = np.sum(grad_G**2,-1)

    #Compute F and G
    F = 0.5*np.sum(grad_F**2,-1)
    G = 0.5*sq_norm_grad_G

    Fnoise = np.random.ranf(np.size(F)).reshape(F.shape) * F.max() * 0.1
    Fnoise = Fnoise.astype(floating)
    F += Fnoise

    Gnoise = np.random.ranf(np.size(G)).reshape(G.shape) * G.max() * 0.1
    Gnoise = Gnoise.astype(floating)
    G += Gnoise

    delta_field =  np.array(F - G, dtype = floating)

    sigma_field = np.random.randn(delta_field.size).reshape(delta_field.shape)
    sigma_field = sigma_field.astype(floating)

    #Select some pixels to force sigma_field = infinite
    inf_sigma = np.random.randint(0, 2, sh[0]*sh[1]*sh[2])
    inf_sigma = inf_sigma.reshape(sh)
    sigma_field[inf_sigma == 1] = np.inf

    #Select an initial displacement field
    d = np.random.randn(grad_G.size).reshape(grad_G.shape).astype(floating)
    #d = np.zeros_like(grad_G, dtype=floating)
    lambda_param = 1.5

    #Implementation under test
    iut = ssd.compute_residual_displacement_field_ssd_3d

    #In the first iteration we test the case target=None
    #In the second iteration, target is not None
    target = None
    rtol = 1e-9
    atol = 1e-4
    for it in range(2):
        # Sum of differences with the neighbors
        s = np.zeros_like(d, dtype = np.float64)
        s[:,:,:-1] += d[:,:,:-1] - d[:,:,1:]#right
        s[:,:,1:] += d[:,:,1:] - d[:,:,:-1]#left
        s[:,:-1,:] += d[:,:-1,:] - d[:,1:,:]#down
        s[:,1:,:] += d[:,1:,:] - d[:,:-1,:]#up
        s[:-1,:,:] += d[:-1,:,:] - d[1:,:,:]#below
        s[1:,:,:] += d[1:,:,:] - d[:-1,:,:]#above
        s *= lambda_param

        # Dot product of displacement and gradient
        dp = d[...,0]*grad_G[...,0] + \
             d[...,1]*grad_G[...,1] + \
             d[...,2]*grad_G[...,2]

        # Compute expected residual
        expected = None
        if target is None:
            expected = np.zeros_like(grad_G)
            for i in range(3):
                expected[...,i] = delta_field*grad_G[...,i]
        else:
            expected = target.copy().astype(np.float64)

        # Expected residuals when sigma != infinte
        for i in range(3):
            expected[inf_sigma==0,i] -= grad_G[inf_sigma==0, i] * dp[inf_sigma==0] + \
                                        sigma_field[inf_sigma==0] * s[inf_sigma==0, i]
        # Expected residuals when sigma == infinte
        expected[inf_sigma==1] = -1.0 * s[inf_sigma==1]

        # Test residual field computation starting with residual = None
        actual = iut(delta_field, sigma_field, grad_G.astype(floating),
                     target, lambda_param, d, None)
        assert_allclose(actual, expected, rtol = rtol, atol = atol)
        actual = np.ndarray(actual.shape, dtype=floating) #destroy previous result

        # Test residual field computation starting with residual is not None
        iut(delta_field, sigma_field, grad_G.astype(floating),
            target, lambda_param, d, actual)
        assert_allclose(actual, expected, rtol = rtol, atol = atol)

        # Set target for next iteration
        target = actual

        # Test Gauss-Seidel step with residual=None and residual=target
        for residual in [None, target]:
            expected = d.copy()
            iterate_residual_field_ssd_3d(delta_field, sigma_field,
                grad_G.astype(floating), residual, lambda_param, expected)

            actual = d.copy()
            ssd.iterate_residual_displacement_field_ssd_3d(delta_field,
                sigma_field, grad_G.astype(floating), residual, lambda_param, actual)

            # the numpy linear solver may differ from our custom implementation
            # we need to increase the tolerance a bit
            assert_allclose(actual, expected, rtol = rtol, atol = atol*5)