Esempio n. 1
0
def local_contrast_normalise(s, n=7, c=None):
    """Local contrast normalisation of an image.

    Perform local contrast normalisation :cite:`jarret-2009-what` of
    an image, consisting of subtraction of the local mean and division
    by the local norm. The original image can be reconstructed from the
    contrast normalised image as (`snrm` * `scn`) + `smn`.

    Parameters
    ----------
    s : array_like
      Input image or array of images.
    n : int, optional (default 7)
      The size of the local region used for normalisation is :math:`2n+1`.
    c : float, optional (default None)
      The smallest value that can be used in the divisive normalisation.
      If `None`, this value is set to the mean of the local region norms.

    Returns
    -------
    scn : ndarray
      Contrast normalised image(s)
    smn : ndarray
      Additive normalisation correction
    snrm : ndarray
      Multiplicative normalisation correction
    """

    # Construct region weighting filter
    N = 2 * n + 1
    g = gaussian((N, N), sd=1.0)
    # Compute required image padding
    pd = ((n, n), ) * 2
    if s.ndim > 2:
        g = g[..., np.newaxis]
        pd += ((0, 0), )
    sp = np.pad(s, pd, mode='symmetric')
    # Compute local mean and subtract from image
    smn = np.roll(fftconv(g, sp), (-n, -n), axis=(0, 1))
    s1 = sp - smn
    # Compute local norm
    snrm = np.roll(np.sqrt(np.clip(fftconv(g, s1**2), 0.0, np.inf)), (-n, -n),
                   axis=(0, 1))
    # Set c parameter if not specified
    if c is None:
        c = np.mean(snrm, axis=(0, 1), keepdims=True)
    # Divide mean-subtracted image by corrected local norm
    snrm = np.maximum(c, snrm)
    s2 = s1 / snrm
    # Return contrast normalised image and normalisation components
    return s2[n:-n, n:-n], smn[n:-n, n:-n], snrm[n:-n, n:-n]
Esempio n. 2
0
 def test_10(self):
     N = 64
     M = 4
     Nd = 8
     D = np.random.randn(Nd, Nd, M)
     X0 = np.zeros((N, N, M))
     xr = np.random.randn(N, N, M)
     xp = np.abs(xr) > 3
     X0[xp] = np.random.randn(X0[xp].size)
     S = np.sum(fftconv(D, X0), axis=2)
     lmbda = 1e-4
     rho = 1e-1
     opt = cbpdn.ConvBPDN.Options({
         'Verbose': False,
         'MaxMainIter': 500,
         'RelStopTol': 1e-3,
         'rho': rho,
         'AutoRho': {
             'Enabled': False
         }
     })
     b = cbpdn.ConvBPDN(D, S, lmbda, opt)
     b.solve()
     X1 = b.Y.squeeze()
     assert rrs(X0, X1) < 5e-5
     Sr = b.reconstruct().squeeze()
     assert rrs(S, Sr) < 1e-4
Esempio n. 3
0
 def test_04(self):
     x = np.random.randn(5, )
     y = np.zeros((12, ))
     y[4] = 1.0
     xy0 = convolve(y, x)
     xy1 = fft.fftconv(x, y, axes=(0, ), origin=(2, ))
     assert np.allclose(xy0, xy1)
Esempio n. 4
0
    def get_psf(self, subpixel=True, tkhflt=False, tkhlmb=1e-3):
        """Get the estimated psf. If parameter `subpixel` is True, the
        subpixel resolution psf is returned, constructed by interpolation
        of the psf estimated at the resolution of the input image.
        """

        if subpixel:
            Hl = lanczos_filters((self.M, self.M), self.K,
                                 collapse_axes=False)
            gp = np.pad(self.g, ((0, self.img.shape[0] - self.gshp[0]),
                                 (0, self.img.shape[1] - self.gshp[1])),
                        'constant')
            grsp = fftconv(Hl, gp[..., np.newaxis, np.newaxis],
                           origin=(self.K, self.K),
                           axes=(0, 1),
                           )[0:self.gshp[0], 0:self.gshp[1]]
            shp = tuple(np.array(self.gshp) * self.M)
            gsub = np.transpose(grsp, (0, 2, 1, 3)).reshape(shp)
            gsub[gsub < 0.0] = 0.0
            if tkhflt:
                gsub, shp = tikhonov_filter(gsub, tkhlmb)
            gsub /= np.linalg.norm(gsub)
            return gsub
        else:
            return self.g / np.linalg.norm(self.g)
Esempio n. 5
0
 def setup_method(self, method):
     np.random.seed(12345)
     N = 32
     M = 4
     Nd = 5
     self.D0 = cr.normalise(cr.zeromean(np.random.randn(Nd, Nd, M),
                                        (Nd, Nd, M),
                                        dimN=2),
                            dimN=2)
     self.X = np.zeros((N, N, M))
     xr = np.random.randn(N, N, M)
     xp = np.abs(xr) > 3
     self.X[xp] = np.random.randn(self.X[xp].size)
     self.S = np.sum(fftconv(self.X, self.D0, axes=(0, 1)).real, axis=2)
     d0c = np.random.randn(Nd, Nd, M) + 1j * np.random.randn(Nd, Nd, M)
     self.D0c = cr.normalise(cr.zeromean(d0c, (Nd, Nd, M), dimN=2), dimN=2)
     self.Xc = np.zeros((N, N, M)) + 1j * np.zeros((N, N, M))
     self.Xc[xp] = (np.random.randn(self.Xc[xp].size) +
                    1j * np.random.randn(self.Xc[xp].size))
     self.Sc = np.sum(fftconv(self.Xc, self.D0c, axes=(0, 1)), axis=2)
Esempio n. 6
0
    def solve(self):
        """Start (or re-start) optimisation."""

        # Set up display header if verbose operation enabled
        if self.opt['Verbose']:
            hdr = 'Itn  DFidX     PriResX   DuaResX    DFidG' + \
                  '     ResG     '
            print(hdr)
            print('-' * len(hdr))

        # Main iteration loop
        for n in range(self.opt['MaxMainIter']):

            # At start of 2nd iteration, set the numbers of inner
            # iterations for the X and G solvers from the options
            # object for the outer solver
            if n == 1:
                self.slvX.opt['MaxMainIter'] = self.opt['XslvIter']
                self.slvG.opt['MaxMainIter'] = self.opt['GslvIter']

            # Run the configured number of iterations of the X (CSC)
            # solver and assign the result to X
            self.X = self.slvX.solve()

            # Compute the sum of the subpixel shifts of X
            Xhs = np.sum(fftconv(self.H, self.X.squeeze(), axes=(0, 1)),
                         axis=-1)

            # Set the convolution kernel in the deconvolution solver
            # to the sum of the subpixel shifts of X
            self.slvG.setG(Xhs)
            # Run the configured number of iterations of the G
            # (deconvolution) solver and crop the result to obtain the
            # updated g
            self.g = self.slvG.solve()[0:self.gshp[0], 0:self.gshp[1]]

            # Construct a new dictionary for the X (CSC) solver from
            # the updated psf g
            self.D, self.dn = self.getD(self.g)
            self.slvX.setdict(self.D[..., np.newaxis, np.newaxis, :])

            # Display iteration statistics if verbose operation enabled
            if self.opt['Verbose']:
                itsX = self.slvX.getitstat()
                itsG = self.slvG.getitstat()
                fmt = '%3d  %.3e %.3e %.3e  %.3e %.3e'
                tpl = (n, itsX.DFid[-1], itsX.PrimalRsdl[-1],
                       itsX.DualRsdl[-1], itsG.DFid[-1], itsG.Rsdl[-1])
                print(fmt % tpl)

        # Return the (normalised) psf estimate g
        return self.g / np.linalg.norm(self.g)
Esempio n. 7
0
    def getD(self, g):
        """Construct the CSC dictionary corresponding to psf `g`."""

        # Zero pad g to avoid boundary effects
        d = np.pad(g, ((0, self.img.shape[0] - g.shape[0]),
                       (0, self.img.shape[1] - g.shape[1])), 'constant')
        # Convolve padded g with set of interpolation filters to
        # construct a set of subpixel shifted versions of g
        D = fftconv(d[..., np.newaxis], self.H, axes=(0, 1))
        # Get dictionary filter norms
        dn = np.sqrt(np.sum(D**2, axis=(0, 1), keepdims=True))

        return D, dn
Esempio n. 8
0
def interpolate(x, M=5, K=10):
    """Lanczos interpolation of 1D or 2D array.

    Parameters
    ----------
    x : ndarray
        Array to be interpolated
    M : int, optional
        Interpolation factor
    K : int, optional
        Order of Lanczos filters

    Returns
    -------
    ndarray
        Input `x` interpolated to higher resolution
    """

    if x.ndim == 1:
        Hl = lanczos_filters((M, ), K, collapse_axes=False)
        xp = np.pad(x, ((0, Hl.shape[0] - 1), ), 'constant')
        xrsp = fftconv(Hl, xp[..., np.newaxis], axes=(0, ),
                       origin=(K, ))[0:x.shape[0], ]
        shp = tuple(np.array(x.shape) * M)
        xsub = xrsp.reshape(shp)
    else:
        Hl = lanczos_filters((M, M), K, collapse_axes=False)
        xp = np.pad(x, ((0, Hl.shape[0] - 1), (0, Hl.shape[1] - 1)),
                    'constant')
        xrsp = fftconv(Hl,
                       xp[..., np.newaxis, np.newaxis],
                       axes=(0, 1),
                       origin=(K, K))[0:x.shape[0], 0:x.shape[1]]
        shp = tuple(np.array(x.shape) * M)
        xsub = np.transpose(xrsp, (0, 2, 1, 3)).reshape(shp)
    return xsub
Esempio n. 9
0
 def test_22(self):
     N = 32
     M = 4
     Nd = 8
     D = np.random.randn(Nd, Nd, M)
     D /= np.sqrt(np.sum(D**2, axis=(0, 1)))
     X0 = np.zeros((N, N, M))
     xr = np.random.randn(N, N, M)
     xp = np.abs(xr) > 3
     X0[xp] = np.random.randn(X0[xp].size)
     S = np.sum(fftconv(D, X0), axis=2)
     lmbda = 1e-3
     opt = cbpdn.ConvBPDN.Options({
         'Verbose': False,
         'MaxMainIter': 500,
         'RelStopTol': 1e-5,
         'rho': 5e-1,
         'AutoRho': {
             'Enabled': False
         }
     })
     bp = cbpdn.ConvBPDN(D, S, lmbda, opt)
     Xp = bp.solve()
     epsilon = np.linalg.norm(bp.reconstruct(Xp).squeeze() - S)
     opt = cbpdn.ConvMinL1InL2Ball.Options({
         'Verbose': False,
         'MaxMainIter': 500,
         'RelStopTol': 1e-5,
         'rho': 2e2,
         'RelaxParam': 1.0,
         'AutoRho': {
             'Enabled': False
         }
     })
     bc = cbpdn.ConvMinL1InL2Ball(D, S, epsilon=epsilon, opt=opt)
     Xc = bc.solve()
     assert np.linalg.norm(Xp - Xc) / np.linalg.norm(Xp) < 1e-3
     assert np.abs(
         np.linalg.norm(Xp.ravel(), 1) -
         np.linalg.norm(Xc.ravel(), 1)) < 1e-3
Esempio n. 10
0
 def test_03(self):
     x = np.array([[0, 1], [2, 3]])
     y = np.array([[4, 5], [6, 7]])
     xy = np.array([[38, 36], [30, 28]])
     assert np.allclose(fft.fftconv(x, y, axes=(0, 1)), xy)
Esempio n. 11
0
else:
    id = select_device_by_load()
    info = gpu_info()
    if info:
        print('Running on GPU %d (%s)\n' % (id, info[id].name))

b = pdcsc.ConvProdDictBPDN(np2cp(D), np2cp(B), np2cp(shc), lmbda, opt, dimK=0)
X = cp2np(b.solve())
print("ConvProdDictBPDN solve time: %.2fs" % b.timer.elapsed('solve'))


"""
Compute partial and full reconstructions from sparse representation $X$ with respect to convolutional dictionary $D$ and standard dictionary $B$. The partial reconstructions are $DX$ and $XB$, and the full reconstruction is $DXB$.
"""

DX = fft.fftconv(D[..., np.newaxis, np.newaxis, :], X, axes=(0, 1))
XB = linalg.dot(B, X, axis=2)
shr = cp2np(b.reconstruct().squeeze())
imgr = slc + shr
print("Reconstruction PSNR: %.2fdB\n" % metric.psnr(img, imgr))


"""
Display original and reconstructed images.
"""

gamma = lambda x, g: np.sign(x) * (np.abs(x)**g)

fig, ax = plot.subplots(nrows=2, ncols=2, figsize=(14, 14))
plot.imview(img, title='Original image', ax=ax[0, 0], fig=fig)
plot.imview(slc, title='Lowpass component', ax=ax[0, 1], fig=fig)
Esempio n. 12
0
                    'rho': 5e1*lmbda + 1e-1, 'AutoRho': {'Enabled': False,
                    'StdResiduals': False}})


"""
Construct :class:`.admm.cbpdn.AddMaskSim` wrapper for :class:`.admm.cbpdn.ConvBPDN` and solve via wrapper. This example could also have made use of :class:`.admm.cbpdn.ConvBPDNMaskDcpl` (see example `cbpdn_md_gry`), which has similar performance in this application, but :class:`.admm.cbpdn.AddMaskSim` has the advantage of greater flexibility in that the wrapper can be applied to a variety of CSC solver objects. If the ``sporco-cuda`` extension is installed and a GPU is available, use the CUDA implementation of this combination.
"""

if cuda.device_count() > 0:
    ams = None
    print('%s GPU found: running CUDA solver' % cuda.device_name())
    tm = util.Timer()
    with sys_pipes(), util.ContextTimer(tm):
        X = cuda.cbpdnmsk(D, sh, mskp, lmbda, opt)
    t = tm.elapsed()
    imgr = crop(sl + np.sum(fftconv(D, X, axes=(0, 1)), axis=-1))
else:
    ams = cbpdn.AddMaskSim(cbpdn.ConvBPDN, D, sh, mskp, lmbda, opt=opt)
    X = ams.solve()
    t = ams.timer.elapsed('solve')
    imgr = crop(sl + ams.reconstruct().squeeze())


"""
Display solve time and reconstruction performance.
"""

print("AddMaskSim wrapped ConvBPDN solve time: %.2fs" % t)
print("Corrupted image PSNR: %5.2f dB" % metric.psnr(img, imgw))
print("Recovered image PSNR: %5.2f dB" % metric.psnr(img, imgr))
Esempio n. 13
0
if not cupy_enabled():
    print('CuPy/GPU device not available: running without GPU acceleration\n')
else:
    id = select_device_by_load()
    info = gpu_info()
    if info:
        print('Running on GPU %d (%s)\n' % (id, info[id].name))

b = pdcsc.ConvProdDictBPDN(np2cp(D), np2cp(B), np2cp(shc), lmbda, opt, dimK=0)
X = cp2np(b.solve())
print("ConvProdDictBPDN solve time: %.2fs" % b.timer.elapsed('solve'))
"""
Compute partial and full reconstructions from sparse representation $X$ with respect to convolutional dictionary $D$ and standard dictionary $B$. The partial reconstructions are $DX$ and $XB$, and the full reconstruction is $DXB$.
"""

DX = fft.fftconv(D[..., np.newaxis, np.newaxis, :], X)
XB = linalg.dot(B, X, axis=2)
shr = cp2np(b.reconstruct().squeeze())
imgr = slc + shr
print("Reconstruction PSNR: %.2fdB\n" % metric.psnr(img, imgr))
"""
Display original and reconstructed images.
"""

gamma = lambda x, g: np.sign(x) * (np.abs(x)**g)

fig, ax = plot.subplots(nrows=2, ncols=2, figsize=(14, 14))
plot.imview(img, title='Original image', ax=ax[0, 0], fig=fig)
plot.imview(slc, title='Lowpass component', ax=ax[0, 1], fig=fig)
plot.imview(imgr, title='Reconstructed image', ax=ax[1, 0], fig=fig)
plot.imview(gamma(shr, 0.6),
Esempio n. 14
0
    }
})
"""
Construct :class:`.admm.cbpdn.AddMaskSim` wrapper for :class:`.admm.cbpdn.ConvBPDNGradReg` and solve via wrapper. If the ``sporco-cuda`` extension is installed and a GPU is available, use the CUDA implementation of this combination.
"""

if cuda.device_count() > 0:
    opt['L1Weight'] = wl1
    opt['GradWeight'] = wgr
    ams = None
    print('%s GPU found: running CUDA solver' % cuda.device_name())
    tm = util.Timer()
    with sys_pipes(), util.ContextTimer(tm):
        X = cuda.cbpdngrdmsk(Di, imgwp, mskp, lmbda, mu, opt)
    t = tm.elapsed()
    imgr = crop(np.sum(fftconv(Di, X, axes=(0, 1)), axis=-1))
else:
    opt['L1Weight'] = wl1i
    opt['GradWeight'] = wgri
    ams = cbpdn.AddMaskSim(cbpdn.ConvBPDNGradReg,
                           Di,
                           imgwp,
                           mskp,
                           lmbda,
                           mu,
                           opt=opt)
    X = ams.solve().squeeze()
    t = ams.timer.elapsed('solve')
    imgr = crop(ams.reconstruct().squeeze())
"""
Display solve time and reconstruction performance.
Esempio n. 15
0
    with sys_pipes(), util.ContextTimer(tm):
        X = cuda.cbpdn(D, sh, lmbda, opt)
    t = tm.elapsed()
else:
    print('GPU not found: running Python solver')
    c = cbpdn.ConvBPDN(D, sh, lmbda, opt)
    X = c.solve().squeeze()
    t = c.timer.elapsed('solve')
print('Solve time: %.2f s' % t)


"""
Reconstruct the image from the sparse representation.
"""

shr = np.sum(fft.fftconv(D, X, axes=(0, 1)), axis=2)
imgr = sl + shr
print("Reconstruction PSNR: %.2fdB\n" % metric.psnr(img, imgr))


"""
Display representation and reconstructed image.
"""

fig = plot.figure(figsize=(14, 14))
plot.subplot(2, 2, 1)
plot.imview(sl, title='Lowpass component', fig=fig)
plot.subplot(2, 2, 2)
plot.imview(np.sum(abs(X), axis=2).squeeze(),
            cmap=plot.cm.Blues, title='Main representation', fig=fig)
plot.subplot(2, 2, 3)
Esempio n. 16
0
        'Enabled': False,
        'StdResiduals': False
    }
})
"""
Construct :class:`.admm.cbpdn.AddMaskSim` wrapper for :class:`.admm.cbpdn.ConvBPDN` and solve via wrapper. This example could also have made use of :class:`.admm.cbpdn.ConvBPDNMaskDcpl` (see example `cbpdn_md_gry`), which has similar performance in this application, but :class:`.admm.cbpdn.AddMaskSim` has the advantage of greater flexibility in that the wrapper can be applied to a variety of CSC solver objects. If the ``sporco-cuda`` extension is installed and a GPU is available, use the CUDA implementation of this combination.
"""

if cuda.device_count() > 0:
    ams = None
    print('%s GPU found: running CUDA solver' % cuda.device_name())
    tm = util.Timer()
    with sys_pipes(), util.ContextTimer(tm):
        X = cuda.cbpdnmsk(D, sh, mskp, lmbda, opt)
    t = tm.elapsed()
    imgr = crop(sl + np.sum(fftconv(D, X), axis=-1))
else:
    ams = cbpdn.AddMaskSim(cbpdn.ConvBPDN, D, sh, mskp, lmbda, opt=opt)
    X = ams.solve()
    t = ams.timer.elapsed('solve')
    imgr = crop(sl + ams.reconstruct().squeeze())
"""
Display solve time and reconstruction performance.
"""

print("AddMaskSim wrapped ConvBPDN solve time: %.2fs" % t)
print("Corrupted image PSNR: %5.2f dB" % metric.psnr(img, imgw))
print("Recovered image PSNR: %5.2f dB" % metric.psnr(img, imgr))
"""
Display reference, test, and reconstructed image
"""
Esempio n. 17
0
    }
})
"""
Construct :class:`.admm.cbpdn.AddMaskSim` wrapper for :class:`.admm.cbpdn.ConvBPDNGradReg` and solve via wrapper. If the ``sporco-cuda`` extension is installed and a GPU is available, use the CUDA implementation of this combination.
"""

if cuda.device_count() > 0:
    opt['L1Weight'] = wl1
    opt['GradWeight'] = wgr
    ams = None
    print('%s GPU found: running CUDA solver' % cuda.device_name())
    tm = util.Timer()
    with sys_pipes(), util.ContextTimer(tm):
        X = cuda.cbpdngrdmsk(Di, imgwp, mskp, lmbda, mu, opt)
    t = tm.elapsed()
    imgr = crop(np.sum(fftconv(Di, X), axis=-1))
else:
    opt['L1Weight'] = wl1i
    opt['GradWeight'] = wgri
    ams = cbpdn.AddMaskSim(cbpdn.ConvBPDNGradReg,
                           Di,
                           imgwp,
                           mskp,
                           lmbda,
                           mu,
                           opt=opt)
    X = ams.solve().squeeze()
    t = ams.timer.elapsed('solve')
    imgr = crop(ams.reconstruct().squeeze())
"""
Display solve time and reconstruction performance.