Example #1
0
    def __init__(self, *args):
        '''
        MogParams(amp, mean, var)

        or

        MogParams(a0,a1,a2, mx0,my0,mx1,my1,mx2,my2,
                  vxx0,vyy0,vxy0, vxx1,vyy1,vxy1, vxx2,vyy2,vxy2)

        amp:  np array (size K) of Gaussian amplitudes
        mean: np array (size K,2) of means
        var:  np array (size K,2,2) of variances
        '''
        from tractor import mixture_profiles as mp

        if len(args) == 3:
            amp, mean, var = args
        else:
            assert (len(args) % 6 == 0)
            K = len(args) // 6
            amp = np.array(args[:K])
            mean = np.array(args[K:3 * K]).reshape((K, 2))
            args = args[3 * K:]
            var = np.zeros((K, 2, 2))
            var[:, 0, 0] = args[::3]
            var[:, 1, 1] = args[1::3]
            var[:, 0, 1] = var[:, 1, 0] = args[2::3]

        self.mog = mp.MixtureOfGaussians(amp, mean, var)
        K = self.mog.K
        assert (self.mog.D == 2)
        super(MogParams, self).__init__()
        # drop the ParamList storage
        del self.vals
        self._set_param_names(K)
Example #2
0
def getCircularMog(amps, sigmas):
    K = len(amps)
    amps = np.array(amps).astype(np.float32)
    means = np.zeros((K, 2), np.float32)
    vars = np.zeros((K, 2, 2), np.float32)
    for k in range(K):
        vars[k, 0, 0] = vars[k, 1, 1] = sigmas[k]**2
    return mp.MixtureOfGaussians(amps, means, vars, quick=True)
Example #3
0
 def getMixtureOfGaussians(self, px=None, py=None):
     K = len(self.myweights)
     amps = np.array(self.myweights)
     means = np.zeros((K, 2))
     vars = np.zeros((K, 2, 2))
     for k in range(K):
         vars[k, 0, 0] = vars[k, 1, 1] = self.mysigmas[k]**2
     return mp.MixtureOfGaussians(amps, means, vars)
Example #4
0
class GaussianGalaxy(HoggGalaxy):
    nre = 6.
    profile = mp.MixtureOfGaussians(np.array([1.]), np.zeros((1, 2)),
                                    np.array([[[1., 0.], [0., 1.]]]))
    profile.normalize()

    def __init__(self, *args, **kwargs):
        self.nre = GaussianGalaxy.nre
        super(GaussianGalaxy, self).__init__(*args, **kwargs)

    def getName(self):
        return 'GaussianGalaxy'

    def getProfile(self):
        return GaussianGalaxy.profile
Example #5
0
    def _realGetUnitFluxModelPatch(self, img, px, py, minval, modelMask=None):
        if modelMask is not None:
            x0, y0 = modelMask.x0, modelMask.y0
        else:
            # choose the patch size
            halfsize = self._getUnitFluxPatchSize(img,
                                                  px=px,
                                                  py=py,
                                                  minval=minval)
            # find overlapping pixels to render
            (outx,
             inx) = get_overlapping_region(int(np.floor(px - halfsize)),
                                           int(np.ceil(px + halfsize + 1)), 0,
                                           img.getWidth())
            (outy,
             iny) = get_overlapping_region(int(np.floor(py - halfsize)),
                                           int(np.ceil(py + halfsize + 1)), 0,
                                           img.getHeight())
            if inx == [] or iny == []:
                # no overlap
                return None
            x0, x1 = outx.start, outx.stop
            y0, y1 = outy.start, outy.stop

        psf = img.getPsf()

        # We have two methods of rendering profile galaxies: If the
        # PSF can be represented as a mixture of Gaussians, then we do
        # the analytic Gaussian convolution, producing a larger
        # mixture of Gaussians, and we render that.  Otherwise
        # (pixelized PSFs), we FFT the PSF, multiply by the analytic
        # FFT of the galaxy, and IFFT back to get the rendered
        # profile.

        # The "HybridPSF" class is just a marker to indicate whether this
        # code should treat the PSF as a hybrid.
        from tractor.psf import HybridPSF
        hybrid = isinstance(psf, HybridPSF)

        def run_mog(amix=None, mm=None):
            ''' This runs the mixture-of-Gaussians convolution method.
            '''
            if amix is None:
                amix = self._getAffineProfile(img, px, py)
            if mm is None:
                mm = modelMask
            # now convolve with the PSF, analytically
            # (note that the psf's center is *not* set to px,py; that's just
            #  the position to use for spatially-varying PSFs)
            psfmix = psf.getMixtureOfGaussians(px=px, py=py)
            cmix = amix.convolve(psfmix)
            if mm is None:
                #print('Mixture to patch: amix', amix, 'psfmix', psfmix, 'cmix', cmix)
                return mp.mixture_to_patch(cmix, x0, x1, y0, y1, minval)
            # The convolved mixture *already* has the px,py offset added
            # (via px,py to amix) so set px,py=0,0 in this call.
            if mm.mask is not None:
                p = cmix.evaluate_grid_masked(mm.x0, mm.y0, mm.mask, 0., 0.)
            else:
                p = cmix.evaluate_grid(mm.x0, mm.x1, mm.y0, mm.y1, 0., 0.)
            assert (p.shape == mm.shape)
            return p

        if hasattr(psf, 'getMixtureOfGaussians') and not hybrid:
            return run_mog(mm=modelMask)

        # Otherwise, FFT:
        imh, imw = img.shape
        if modelMask is None:
            # Avoid huge galaxies -> huge halfsize in a tiny image (blob)
            imsz = max(imh, imw)
            halfsize = min(halfsize, imsz)
            # FIXME -- should take some kind of combination of
            # modelMask, PSF, and Galaxy sizes!

        else:
            # ModelMask sets the sizes.
            mh, mw = modelMask.shape
            x1 = x0 + mw
            y1 = y0 + mh

            halfsize = max(mh / 2., mw / 2.)
            # How far from the source center to furthest modelMask edge?
            # FIXME -- add 1 for Lanczos margin?
            halfsize = max(
                halfsize,
                max(max(1 + px - x0, 1 + x1 - px), max(1 + py - y0,
                                                       1 + y1 - py)))
            psfh, psfw = psf.shape
            halfsize = max(halfsize, max(psfw / 2., psfh / 2.))
            #print('Halfsize:', halfsize)

            # is the source center outside the modelMask?
            sourceOut = (px < x0 or px > x1 - 1 or py < y0 or py > y1 - 1)
            # print('mh,mw', mh,mw, 'sourceout?', sourceOut)

            if sourceOut:
                if hybrid:
                    return run_mog(mm=modelMask)

                # Super Yuck -- FFT, modelMask, source is outside the
                # box.
                neardx, neardy = 0., 0.
                if px < x0:
                    neardx = x0 - px
                if px > x1:
                    neardx = px - x1
                if py < y0:
                    neardy = y0 - py
                if py > y1:
                    neardy = py - y1
                nearest = np.hypot(neardx, neardy)
                #print('Nearest corner:', nearest, 'vs radius', self.getRadius())
                if nearest > self.getRadius():
                    return None
                # how far is the furthest point from the source center?
                farw = max(abs(x0 - px), abs(x1 - px))
                farh = max(abs(y0 - py), abs(y1 - py))
                bigx0 = int(np.floor(px - farw))
                bigx1 = int(np.ceil(px + farw))
                bigy0 = int(np.floor(py - farh))
                bigy1 = int(np.ceil(py + farh))
                bigw = 1 + bigx1 - bigx0
                bigh = 1 + bigy1 - bigy0
                boffx = x0 - bigx0
                boffy = y0 - bigy0
                assert (bigw >= mw)
                assert (bigh >= mh)
                assert (boffx >= 0)
                assert (boffy >= 0)
                bigMask = np.zeros((bigh, bigw), bool)
                if modelMask.mask is not None:
                    bigMask[boffy:boffy + mh,
                            boffx:boffx + mw] = modelMask.mask
                else:
                    bigMask[boffy:boffy + mh, boffx:boffx + mw] = True
                bigMask = ModelMask(bigx0, bigy0, bigMask)
                # print('Recursing:', self, ':', (mh,mw), 'to', (bigh,bigw))
                bigmodel = self._realGetUnitFluxModelPatch(img,
                                                           px,
                                                           py,
                                                           minval,
                                                           modelMask=bigMask)
                return Patch(
                    x0, y0, bigmodel.patch[boffy:boffy + mh, boffx:boffx + mw])

        # print('Getting Fourier transform of PSF at', px,py)
        # print('Tim shape:', img.shape)
        P, (cx, cy), (pH, pW), (v,
                                w) = psf.getFourierTransform(px, py, halfsize)

        dx = px - cx
        dy = py - cy
        if modelMask is not None:
            # the Patch we return *must* have this origin.
            ix0 = x0
            iy0 = y0
            # the difference that we have to handle by shifting the model image
            mux = dx - ix0
            muy = dy - iy0
            # we will handle the integer portion by computing a shifted image
            # and copying it into the result
            sx = int(np.round(mux))
            sy = int(np.round(muy))
            # the subpixel portion will be handled with a Lanczos interpolation
            mux -= sx
            muy -= sy
        else:
            # Put the integer portion of the offset into Patch x0,y0
            ix0 = int(np.round(dx))
            iy0 = int(np.round(dy))
            # the subpixel portion will be handled with a Lanczos interpolation
            mux = dx - ix0
            muy = dy - iy0

        # At this point, mux,muy are both in [-0.5, 0.5]
        assert (np.abs(mux) <= 0.5)
        assert (np.abs(muy) <= 0.5)

        amix = self._getShearedProfile(img, px, py)
        fftmix = amix
        mogmix = None

        #print('Galaxy affine profile:', amix)

        if hybrid:
            # Split "amix" into terms that we will evaluate using MoG
            # vs FFT.
            vv = amix.var[:, 0, 0] + amix.var[:, 1, 1]
            nsigma = 3.
            # Terms that will wrap-around significantly...
            I = ((vv * nsigma**2) > (pW**2))
            if np.any(I):
                # print('Evaluating', np.sum(I), 'terms as MoGs')
                mogmix = mp.MixtureOfGaussians(
                    amix.amp[I],
                    amix.mean[I, :] + np.array([px, py])[np.newaxis, :],
                    amix.var[I, :, :],
                    quick=True)
            I = np.logical_not(I)
            if np.any(I):
                # print('Evaluating', np.sum(I), 'terms with FFT')
                fftmix = mp.MixtureOfGaussians(amix.amp[I],
                                               amix.mean[I, :],
                                               amix.var[I, :, :],
                                               quick=True)
            else:
                fftmix = None

        if fftmix is not None:
            Fsum = fftmix.getFourierTransform(v, w, zero_mean=True)
            # In Intel's mkl_fft library, the irfftn code path is faster than irfft2
            # (the irfft2 version sets args (to their default values) which triggers padding
            #  behavior, changing the FFT size and copying behavior)
            #G = np.fft.irfft2(Fsum * P, s=(pH, pW))
            G = np.fft.irfftn(Fsum * P)

            assert (G.shape == (pH, pW))
            # FIXME -- we could try to be sneaky and Lanczos-interp
            # after cutting G down to nearly its final size... tricky
            # tho

            # Lanczos-3 interpolation in ~the same way we do for
            # pixelized PSFs.
            from tractor.psf import lanczos_shift_image
            G = G.astype(np.float32)
            lanczos_shift_image(G, mux, muy, inplace=True)
        else:
            G = np.zeros((pH, pW), np.float32)

        if modelMask is not None:
            gh, gw = G.shape
            if sx != 0 or sy != 0:
                yi, yo = get_overlapping_region(-sy, -sy + mh - 1, 0, gh - 1)
                xi, xo = get_overlapping_region(-sx, -sx + mw - 1, 0, gw - 1)
                # shifted
                # FIXME -- are yo,xo always the whole image?  If so, optimize
                shG = np.zeros((mh, mw), G.dtype)
                shG[yo, xo] = G[yi, xi]

                if debug_ps is not None:
                    _fourier_galaxy_debug_plots(G, shG, xi, yi, xo, yo, P,
                                                Fsum, pW, pH, psf)

                G = shG
            if gh > mh or gw > mw:
                G = G[:mh, :mw]
            assert (G.shape == modelMask.shape)

        else:
            # Clip down to suggested "halfsize"
            if x0 > ix0:
                G = G[:, x0 - ix0:]
                ix0 = x0
            if y0 > iy0:
                G = G[y0 - iy0:, :]
                iy0 = y0
            gh, gw = G.shape
            if gw + ix0 > x1:
                G = G[:, :x1 - ix0]
            if gh + iy0 > y1:
                G = G[:y1 - iy0, :]

        if mogmix is not None:
            if modelMask is not None:
                mogpatch = run_mog(amix=mogmix, mm=modelMask)
            else:
                gh, gw = G.shape
                mogpatch = run_mog(amix=mogmix, mm=ModelMask(ix0, iy0, gw, gh))
            assert (mogpatch.patch.shape == G.shape)
            G += mogpatch.patch

        return Patch(ix0, iy0, G)
Example #6
0
plt.clf()
plt.subplot(2,2,2)
dimshow(np.log10(np.maximum(floor, patch2.patch)), **ima)
plt.subplot(2,2,3)
dimshow(dx2.patch)
plt.subplot(2,2,4)
dimshow(dy2.patch)
ps.savefig()

sys.exit(0)




mg = mp.MixtureOfGaussians([1.], [0.,0.], np.array([1.]))
x = mg.evaluate(np.array([0,0]))
x = mg.evaluate(np.array([0.,1.]))
x = mg.evaluate(np.array([-3.,2.]))
x = mg.evaluate(np.array([[-3.,2.], [-17,4], [4,-2]]))

mg = mp.MixtureOfGaussians([1.], [0.,1.], np.array([2.]))
x = mg.evaluate(np.array([0.,1.]))
x = mg.evaluate(np.array([0,0]))
x = mg.evaluate(np.array([-3.,2.]))
x = mg.evaluate(np.array([[-3.,2.], [-17,4], [4,-2]]))

mg = mp.MixtureOfGaussians([1.], [0.,1.], np.array([ [[1.3, 0.1],[0.1,3.1]], ]))
x = mg.evaluate(np.array([0.,1.]))
x = mg.evaluate(np.array([0,0]))
x = mg.evaluate(np.array([-3.,2.]))