Esempio n. 1
0
 def get_oversampled_psf(self):
     gamma = self.gamma(size=1)[0]
     alpha = self.alpha
     print(self.x0,self.y0,gamma,alpha)
     m2d = models.Moffat2D(x_0=self.x0,y_0=self.y0,gamma=gamma,alpha=alpha)
     psf = m2d(self.x,self.y) 
     return gamma,alpha,psf
Esempio n. 2
0
def fit_moffat2d_outlier_removal(x,
                                 y,
                                 z,
                                 sigma=3.0,
                                 niter=50,
                                 guess=None,
                                 bounds=None):
    """Moffat2D parameters: amplitude, x_mean,y_mean,gamma,alpha"""
    gg_init = models.Moffat2D()
    if guess is not None:
        for ip, p in enumerate(gg_init.param_names):
            getattr(gg_init, p).value = guess[ip]
    if bounds is not None:
        for ip, p in enumerate(gg_init.param_names):
            getattr(gg_init, p).min = bounds[0][ip]
            getattr(gg_init, p).max = bounds[1][ip]
    with warnings.catch_warnings():
        # Ignore model linearity warning from the fitter
        warnings.simplefilter('ignore')
        fit = fitting.LevMarLSQFitter()
        or_fit = fitting.FittingWithOutlierRemoval(fit,
                                                   sigma_clip,
                                                   niter=niter,
                                                   sigma=sigma)
        # get fitted model and filtered data
        filtered_data, or_fitted_model = or_fit(gg_init, x, y, z)
        if parameters.VERBOSE: print or_fitted_model
        return or_fitted_model
Esempio n. 3
0
 def fwhm(self,size=1):
     gammas = self.gamma(size=size)
     fwhm = []
     for g in gammas:
         m2d = models.Moffat2D(x_0=self.x0,y_0=self.y0,gamma=g,alpha=self.alpha)
         fwhm += [m2d.fwhm/self.oversampling]
     return np.array(fwhm)
Esempio n. 4
0
    def __init__(self, fwhm, tpsf_model, fiber_r, imagesize=20, pix_arcsec=10):

        self.fwhm = fwhm
        self.fiber_r = fiber_r
        self.imagesize = imagesize
        self.pix_arcsec = pix_arcsec

        # hard coded in cure
        self.offsets = 6.0*fiber_r*(arange(0, 100)/100.0)

        if tpsf_model == 'gaussian':
            sigma = fwhm/2.35
            self.psf_model = models.Gaussian2D(x_stddev=sigma,
                                               y_stddev=sigma)
        elif tpsf_model == 'moffat':
            alpha = 2.5  # hard coded in Cure
            gamma = fwhm/2.0/sqrt(power(2.0, (1.0/alpha)) - 1.0)
            self.psf_model = models.Moffat2D(alpha=alpha,
                                             gamma=gamma)
            # self.psf_model = moffat_generator(0.0, 0.0, fwhm)

        else:
            # model not supported
            return

        self.initialise_modelfrac()
Esempio n. 5
0
def test_moffat_fwhm(gamma):
    ans = 34.641016151377542
    kwargs = {'gamma': gamma, 'alpha': 0.5}
    m1 = models.Moffat1D(**kwargs)
    m2 = models.Moffat2D(**kwargs)
    assert_allclose([m1.fwhm, m2.fwhm], ans)
    assert_array_less(0, [m1.fwhm, m2.fwhm])
Esempio n. 6
0
 def get_oversampled_psf(self, verbose=True):
     gamma = self.gamma(size=1)[0]
     alpha = self.alpha
     if verbose:
         print("PSF x0,y0,gamma,alpha = ",self.x0,self.y0,gamma,alpha)
     m2d = models.Moffat2D(x_0=self.x0,y_0=self.y0,gamma=gamma,alpha=alpha)
     psf = m2d(self.x,self.y) 
     return gamma,alpha,psf
Esempio n. 7
0
def moffat(xcen, ycen, amp, wid, power, fixed=False):
    if fixed:
        return models.Moffat2D(amplitude=amp,
                               x_0=xcen,
                               y_0=ycen,
                               gamma=wid,
                               alpha=power,
                               fixed={
                                   "gamma": True,
                                   "alpha": True
                               })
    else:
        return models.Moffat2D(amplitude=amp,
                               x_0=xcen,
                               y_0=ycen,
                               gamma=wid,
                               alpha=power)
Esempio n. 8
0
 def __init__(self, gamma, alpha, **kwargs):
     # Compute amplitude, from
     # https://en.wikipedia.org/wiki/Moffat_distribution
     amplitude = (alpha - 1.0) / (np.pi * gamma * gamma)
     self._model = models.Moffat2D(amplitude, 0, 0, gamma, alpha)
     self._default_size = _round_up_to_odd_integer(4.0 * self._model.fwhm)
     super().__init__(**kwargs)
     self.normalize()
Esempio n. 9
0
def fit_star(psf_data, type_fit):
    '''
    Fits Gaussian or Moffat model to star.
    '''
    # Fit the data using astropy.modeling
    if (type_fit == 'Gaussian'):
        p_init = models.Gaussian2D(amplitude=np.nanmax(psf_data),
                                   x_mean=0.5 * psf_data.shape[0],
                                   y_mean=0.5 * psf_data.shape[1])
    elif (type_fit == 'Moffat'):
        p_init = models.Moffat2D(amplitude=np.nanmax(psf_data),
                                 x_0=0.5 * psf_data.shape[0],
                                 y_0=0.5 * psf_data.shape[1])
    fit_p = fitting.LevMarLSQFitter()
    x, y = np.mgrid[:psf_data.shape[0], :psf_data.shape[1]]
    p = fit_p(p_init, x, y, psf_data)
    return (p)
Esempio n. 10
0
    def dofit(self, verbose=True):
        # Fit the data using astropy.modeling
        center_x, center_y = find_center(self.z)

        p_init = models.Moffat2D(x_0=center_x, y_0=center_y)

        fit_p = fitting.LevMarLSQFitter()
        with warnings.catch_warnings():
            # Ignore model linearity warning from the fitter
            warnings.simplefilter('ignore')
            self.model = fit_p(p_init, self.x, self.y, self.z)

        if verbose:
            print("Mean Residual:",
                  (self.z - self.model(self.x, self.y)).mean())

        self.params = Table(
            data=[self.model.param_names, self.model.parameters],
            names=["param_names", "param_vals"])
        self.gamma = self.model.parameters[3]
        self.alpha = self.model.parameters[4]
        self.fwhm = self.model.fwhm
Esempio n. 11
0
def measure_fwhm(array, displ):
    """Fit a Gaussian2D model to a PSF and return the FWHM

    Parameters
    ----------
    array : numpy.ndarray
        Array containing PSF

    Returns
    -------
    x_fwhm : float
        FWHM in x direction in units of pixels

    y_fwhm : float
        FWHM in y direction in units of pixels
    """
    yp, xp = array.shape
    y, x, = np.mgrid[:yp, :xp]
    p_init = models.Moffat2D(amplitude=np.max(array), x_0=xp / 2, y_0=yp / 2)
    fit_p = fitting.LevMarLSQFitter()
    #    print(fit_p.fit_info)
    fitted_psf = fit_p(p_init, x, y, array, maxiter=50)
    #    print(fit_p.fit_info)
    if displ == True:
        plt.figure(figsize=(8, 2.5))
        plt.subplot(1, 3, 1)
        plt.imshow(array, origin='lower', interpolation='nearest')
        plt.title("Data")
        plt.subplot(1, 3, 2)
        plt.imshow(fitted_psf(x, y), origin='lower', interpolation='nearest')
        plt.title("Model")
        plt.subplot(1, 3, 3)
        plt.imshow(array - fitted_psf(x, y),
                   origin='lower',
                   interpolation='nearest')
        plt.title("Residual")
        plt.show()
    return fitted_psf.fwhm
Esempio n. 12
0
    def _initialize_model(self):
        """Initialize a model with first guesses for the parameters.
        The user can select between several astropy models, e.g., 'Gaussian2D', 'Moffat2D'. We will use the data to get
        the first estimates of the parameters of each model. Finally, a Constant2D model is added to account for the
        background or sky level around the star.
        """
        max_value = self.data.max()

        if self.model_type == self._GAUSSIAN2D:
            model = models.Gaussian2D(x_mean=self.x,
                                      y_mean=self.y,
                                      x_stddev=1,
                                      y_stddev=1)
            model.amplitude = max_value

            # Establish reasonable bounds for the fitted parameters
            model.x_stddev.bounds = (0, self._box / 4)
            model.y_stddev.bounds = (0, self._box / 4)
            model.x_mean.bounds = (self.x - 5, self.x + 5)
            model.y_mean.bounds = (self.y - 5, self.y + 5)

        elif self.model_type == self._MOFFAT2D:
            model = models.Moffat2D()
            model.x_0 = self.x
            model.y_0 = self.y
            model.gamma = 2
            model.alpha = 2
            model.amplitude = max_value

            #  Establish reasonable bounds for the fitted parameters
            model.alpha.bounds = (1, 6)
            model.gamma.bounds = (0, self._box / 4)
            model.x_0.bounds = (self.x - 5, self.x + 5)
            model.y_0.bounds = (self.y - 5, self.y + 5)

        model += models.Const2D(self.fit_sky())
        model.amplitude_1.fixed = True
        return model
Esempio n. 13
0
 astmodels.Const2D(amplitude=5.),
 astmodels.Disk2D(amplitude=10., x_0=0.5, y_0=1.5, R_0=5.),
 astmodels.Ellipse2D(amplitude=10., x_0=0.5, y_0=1.5, a=2., b=4.,
                     theta=0.1),
 astmodels.Exponential1D(amplitude=10., tau=3.5),
 astmodels.Gaussian1D(amplitude=10., mean=5., stddev=3.),
 astmodels.Gaussian2D(amplitude=10.,
                      x_mean=5.,
                      y_mean=5.,
                      x_stddev=3.,
                      y_stddev=3.),
 astmodels.KingProjectedAnalytic1D(amplitude=10., r_core=5., r_tide=2.),
 astmodels.Logarithmic1D(amplitude=10., tau=3.5),
 astmodels.Lorentz1D(amplitude=10., x_0=0.5, fwhm=2.5),
 astmodels.Moffat1D(amplitude=10., x_0=0.5, gamma=1.2, alpha=2.5),
 astmodels.Moffat2D(amplitude=10., x_0=0.5, y_0=1.5, gamma=1.2, alpha=2.5),
 astmodels.Planar2D(slope_x=0.5, slope_y=1.2, intercept=2.5),
 astmodels.RedshiftScaleFactor(z=2.5),
 astmodels.RickerWavelet1D(amplitude=10., x_0=0.5, sigma=1.2),
 astmodels.RickerWavelet2D(amplitude=10., x_0=0.5, y_0=1.5, sigma=1.2),
 astmodels.Ring2D(amplitude=10., x_0=0.5, y_0=1.5, r_in=5., width=10.),
 astmodels.Sersic1D(amplitude=10., r_eff=1., n=4.),
 astmodels.Sersic2D(amplitude=10.,
                    r_eff=1.,
                    n=4.,
                    x_0=0.5,
                    y_0=1.5,
                    ellip=0.0,
                    theta=0.0),
 astmodels.Sine1D(amplitude=10., frequency=0.5, phase=1.),
 astmodels.Cosine1D(amplitude=10., frequency=0.5, phase=1.),
Esempio n. 14
0
def img_subtract_bright_star(img,
                             star,
                             x_col='x_pix',
                             y_col='y_pix',
                             gamma=5.0,
                             alpha=6.0,
                             sig=None,
                             x_buffer=4,
                             y_buffer=4,
                             img_maxsize=300):
    """Subtract a bright star from image using a Moffat model."""
    # Use the SLSQP fitter
    fitter_use = fitting.SLSQPLSQFitter()

    # Image dimension
    img_h, img_w = img.shape

    # Only fit the stars on the image
    if ((0 + x_buffer < int(star[x_col]) < img_w - x_buffer)
            and (0 + y_buffer < int(star[y_col]) < img_h - y_buffer)):
        # Get the center of the star
        x_cen, y_cen = int(star[x_col]), int(star[y_col])

        # If the image is too big, cut a part of it
        if (img_h >= img_maxsize) or (img_w >= img_maxsize):
            x_0 = int(x_cen -
                      img_maxsize / 2.0) if (x_cen -
                                             img_maxsize / 2.0) > 0 else 0
            x_1 = int(x_cen + img_maxsize /
                      2.0) if (x_cen + img_maxsize / 2.0) < img_w else (img_w -
                                                                        1)
            y_0 = int(y_cen -
                      img_maxsize / 2.0) if (y_cen -
                                             img_maxsize / 2.0) > 0 else 0
            y_1 = int(y_cen + img_maxsize /
                      2.0) if (y_cen + img_maxsize / 2.0) < img_h else (img_h -
                                                                        1)
            x_cen, y_cen = (x_cen - x_0), (y_cen - y_0)
        else:
            x_0, x_1 = 0, img_w + 1
            y_0, y_1 = 0, img_h + 1

        # Determine the weights for the fitting
        img_use = copy.deepcopy(img[y_0:y_1, x_0:x_1])

        weights = (1.0 / sig[y_0:y_1, x_0:x_1]) if (sig is not None) else None

        # X, Y grids
        y_size, x_size = img_use.shape
        y_arr, x_arr = np.mgrid[:y_size, :x_size]

        # Initial the Moffat model
        p_init = models.Moffat2D(x_0=x_cen,
                                 y_0=y_cen,
                                 amplitude=(img_use[int(x_cen),
                                                    int(y_cen)]),
                                 gamma=gamma,
                                 alpha=alpha,
                                 bounds={
                                     'x_0':
                                     [x_cen - x_buffer, x_cen + x_buffer],
                                     'y_0':
                                     [y_cen - y_buffer, y_cen + y_buffer]
                                 })

        try:
            with np.errstate(all='ignore'):
                best_fit = fitter_use(p_init,
                                      x_arr,
                                      y_arr,
                                      img_use,
                                      weights=weights,
                                      verblevel=0)

                img_new = copy.deepcopy(img)
                img_new[y_0:y_1, x_0:x_1] -= best_fit(x_arr, y_arr)

            return img_new

        except Exception:
            warnings.warn('# Star fitting failed!')
            return img
    else:
        return img
Esempio n. 15
0
def fwhmrGridStatistic(catfile, size, gridNum=40):

    tdata = np.loadtxt(catfile)

    maxEllipticity = 0.1
    mag = tdata[:, 38]
    elpct = tdata[:, 15]
    fwhm = tdata[:, 18]

    mag1 = sigma_clip(mag, sigma=2.5, iters=3)
    minMag = np.min(mag1)
    maxMag = np.max(mag1)
    medianFwhm = np.median(fwhm)

    targetFwhmMax = medianFwhm
    targetMag = maxMag - 6
    targetMagMin = targetMag + 1
    targetMagMax = targetMag + 2
    tdata = tdata[(mag > targetMagMin) & (mag < targetMagMax) &
                  (elpct < maxEllipticity) & (fwhm < targetFwhmMax)]
    print(tdata.shape)

    imgData = fits.getdata(r"E:\fwhm\data1\oi.fit")
    print(imgData.shape)

    tRadius = 5
    for i in range(tdata.shape[1]):
        if i < 10:
            trow = tdata[i]
            print("%.2f, %.2f, %.2f, %.2f, %.2f, %.2f" %
                  (trow[3], trow[4], trow[38], trow[18], trow[15], trow[17]))
            x = int(round(trow[3]))
            y = int(round(trow[4]))
            fwhm = trow[18]
            bkg = trow[17]
            minX = x - tRadius
            maxX = x + tRadius + 1
            minY = y - tRadius
            maxY = y + tRadius + 1
            subImg = imgData[minY:maxY, minX:maxX]

            subImg = subImg - bkg

            x = np.arange(subImg.shape[1])
            y = np.arange(subImg.shape[0])
            X, Y = np.meshgrid(x, y)
            Z = subImg

            fig = plt.figure()
            fig = plt.figure()
            ax = fig.add_subplot(111, projection='3d')
            ax.scatter(X, Y, Z)
            plt.show()
            ''' '''

            #https://github.com/astropy/astropy/issues/4521
            from astropy.modeling import models, fitting
            p_init = models.Moffat2D(amplitude=Z[5, 5],
                                     x_0=5,
                                     y_0=5,
                                     gamma=fwhm,
                                     alpha=3.0)

            fitter = fitting.LevMarLSQFitter()
            p_fit = fitter(p_init, X, Y, Z)
            Z1 = p_fit(X, Y)
            print(p_fit)

            print(p_fit.fwhm)
            tfwhm = FWHM_moffat(p_fit.gamma, p_fit.alpha)
            print(tfwhm)

            fig = plt.figure()
            fig = plt.figure()
            ax = fig.add_subplot(111, projection='3d')
            ax.scatter(X, Y, Z1)
            plt.show()

            break
Esempio n. 16
0
def fit_moon(img, x, y):
    """Fit a Moffat function to the moon in a given image.

    Parameters
    ---------
    img : image.AllSkyImage
        The image.
    x : float
        The x coordinate of the moon"s center.
    y : float
        The y coordinate of the moon"s center.

    Returns
    -------
    float
        The Full Width at Half Maximum of the Moffat function.
    """
    # This block of code runs straight vertical from the center of the moon
    # It gives a predicted rough radius of the moon, it starts counting at the
    # first white pixel it encounters (the center may be black)
    # and stops at the last white pixel. White here defined as > 250 greyscale.
    yfloor = math.floor(y)
    count = False
    size = 0
    xfloor = math.floor(x)
    start = xfloor

    # The only reason we have this if block is to ensure we don"t run for
    # moon radii greater than 35 in this case.
    for i in range(0, 35):
        start += 1

        # Breaks if it reaches the edge of the image.
        if start == img.data.shape[1]:
            break
        if not count and img.data[yfloor, start] >= 250:
            count = True
        elif count and img.data[yfloor, start] >= 250:
            size += 1
        elif count and img.data[yfloor, start] < 250:
            break

    # Add some buffer pixels in case the center is black and the edges of the
    # moon are fuzzed and then convert radius to diameter.
    size = (size + 10) * 2

    # Makes sure the lower/upper slices don"t out of bounds error.
    lowerx = xfloor - size if (xfloor - size > 0) else 0
    lowery = yfloor - size if (yfloor - size > 0) else 0
    upperx = xfloor + size if (xfloor + size < 511) else 511
    uppery = yfloor + size if (yfloor + size < 511) else 511

    # Size of the moon enclosing square.
    deltax = (upperx - lowerx)
    deltay = (uppery - lowery)

    # Creates two arrays, with the array values being the x or y coordinate of
    # that location in the array.
    y, x = np.mgrid[0:deltay, 0:deltax]

    # Slices out the moon square and finds center coords.
    z = img.data[lowery:uppery, lowerx:upperx]
    midy = deltay / 2
    midx = deltax / 2

    # Moffat fit, centered in square, stdev of 20 as a start.
    stddev = 20
    model_init = models.Moffat2D(amplitude=200,
                                 x_0=midx,
                                 y_0=midy,
                                 gamma=stddev)
    fit = fitting.LevMarLSQFitter()

    with warnings.catch_warnings():
        # Ignore model linearity warning from the fitter
        warnings.simplefilter("ignore")
        model = fit(model_init, x, y, z)

    # /2 is average FWHM but FWHM = diameter, so divide by two again.
    #fwhm = (model.x_fwhm + model.y_fwhm) / 4
    fwhm = model.fwhm / 2

    return fwhm
Esempio n. 17
0
def simulate_image(psffits=None,
                   theofits=None,
                   obsfits=None,
                   distance=None,
                   extfits=None,
                   wlen=None,
                   pfov=None,
                   filt=None,
                   psfext=0,
                   obsext=0,
                   bgstd=0,
                   bgmed=0,
                   silent=False,
                   posang=0,
                   foi_as=4,
                   outname=None,
                   manscale=None,
                   fnucdiam_as=0.45,
                   suffix=None,
                   fitsize=None,
                   outfolder='.',
                   writesimfits=False,
                   writetheofits=False,
                   writeallfits=False,
                   writefitplot=False,
                   debug=False,
                   returncmod=True,
                   fitpsf=False,
                   saveplot=False,
                   meastime=False):
    """
    Simulate a (VISIR) imaging observaion given a real image, a PSF reference
    image and a model image for a provided object distance and position angle

    Current constraints:
        - The routine assumes that the images are squares (x = y)
    """

    if meastime:
        tstart = time.time()

    psfim = fits.getdata(psffits, ext=psfext)
    psfhead = fits.getheader(psffits)

    # print(obsfits, obsext)
    # ==== 1. Load Observational and PSF data ====
    if obsfits is not None:
        obsim = fits.getdata(obsfits, ext=obsext)
        obshead = fits.getheader(obsfits)

        if wlen is None:
            wlen = float(obshead['WAVELEN'])

        if pfov is None:
            pfov = obshead['PFOV']

        if filt is None:
            filt = obshead['Filter']

    else:
        if wlen is None:
            wlen = float(psfhead['WAVELEN'])

        if pfov is None:
            pfov = psfhead['PFOV']

        if filt is None:
            filt = psfhead['Filter']

    wlenstr = "{:.1f}".format(wlen)
    diststr = "{:.1f}".format(distance)
    pastr = "{:.0f}".format(posang)

    # --- optinal cropping of the PSF image
    if fitsize is not None:
        psfim = _crop_image(psfim,
                            box=fitsize,
                            cenpos=_get_pointsource(psfim)[0][2:4])

    psfsize = np.array(np.shape(psfim))
    psfsize_as = psfsize[0] * pfov

    if not silent:
        print("Obs. wavelength [um]: " + wlenstr)

    # ==== 2. Prepare theoretical image for convolution ====
    theohdu = fits.open(theofits)
    theohead = theohdu[0].header

    # --- check if provided file is the full cube
    if 'NAXIS3' in theohead:
        # find the right frame to be extracted
        mind, mwlen = find_wlen_match(theofits, wlen)
        theoim = theohdu[0].data[mind]

        if not silent:
            print(" - Model wavelength [um] | frame no: " + str(mwlen) +
                  " | " + str(mind))

    else:
        theoim = theohdu[0].data

    theohdu.close()

    if meastime:
        print(" - All files read. Elapsed time: ", time.time() - tstart)

    if debug is True:
        print("THEOIM: ")
        plt.imshow(theoim,
                   origin='bottom',
                   norm=LogNorm(),
                   interpolation='nearest')
        plt.show()

    # --- if a manual scaling (fudge) factor was provided, apply to the model
    if manscale:
        theoim = theoim * manscale

    # --- rotate the model image (this changes its extent and thus has to be
    #     done first)
    # unrot = np.copy(theoim)
    if np.abs(posang - 0) > 0.01:
        theoim = ndimage.interpolation.rotate(theoim, 180 - posang, order=0)

    if meastime:
        print(" - Model rotated. Elapsed time: ", time.time() - tstart)

    if debug is True:
        print("THEOIM_ROT: ")
        plt.imshow(theoim,
                   origin='bottom',
                   norm=LogNorm(),
                   interpolation='nearest')
        plt.show()

    # --- size units of the theoretical image
    theopixsize_pc = theohead['CDELT1']
    theosize = np.array(np.shape(theoim))
    theosize_pc = theopixsize_pc * theosize[0]  # pc
    theosize_as = 2 * np.arctan(
        theosize_pc / distance / 2.0e6) * 180 / np.pi * 3600
    theopixsize_as = (2 * np.arctan(theopixsize_pc / distance / 2.e6) * 180 /
                      np.pi * 3600)

    #    plt.imshow(unrot, origin='bottom', norm=LogNorm())
    #    plt.imshow(theoim, origin='bottom', norm=LogNorm())
    #    plt.imshow(theoim, origin='bottom')

    #    theopfov = angsize/theosize[0]

    # normalize image to flux = 1
    # theoim = theoim/np.sum(theoim)
    # theoim = theoim/np.max(theoim)

    # --- convert to the right flux units
    # surface brightness unit in cube is W/m^2/arcsec2
    # -> convert to flux density in mJy
    # print(np.sum(theoim))
    freq = 2.99793e8 / (1e-6 * wlen)
    # print(freq)

    theoim = theoim * 1.0e29 / freq * theopixsize_as**2

    theototflux = np.sum(theoim)

    # --- resample to the same pixel size as the observation
    # print(   "- Resampling the model image to instrument pixel size...")
    # --- the ndimage.zoom sometimes creates weird artifacts and thus might not
    #     be a good choice. Instead, use the self-made routine
    # theoim_resres = ndimage.zoom(theoim_res, theopixsize_as/pfov, order=0)
    # --- do the rasmpling before scaling it to the size of the observation to
    #     save computing time
    theoim_res, sizerat = _increase_pixelsize(theoim,
                                              oldpfov=theopixsize_as,
                                              newpfov=pfov,
                                              meastime=False)

    if meastime:
        print(" - Pixelsize increased. Elapsed time: ", time.time() - tstart)

    if debug is True:
        print("sizerat: ", sizerat)
        print("THEOIM_RESRES: ")
        plt.imshow(theoim_res,
                   origin='bottom',
                   norm=LogNorm(),
                   interpolation='nearest')
        plt.show()

    # --- if the PSF frame size is larger than the model extend the model frame
    framerat = psfsize_as / theosize_as

    if debug is True:
        print("psfsize_as: ", psfsize_as)
        print("theosize_as: ", theosize_as)
        print("theopixsize_as: ", theopixsize_as)
        print("theosize_pc: ", theosize_pc)
        print("theosize: ", theosize)
        print("framerat: ", framerat)
    #print("newsize: ", newsize)

    if framerat > 1.0:
        theoim_resres = _extend_image(theoim_res, fac=framerat)

        if meastime:
            print(" - Frame extended. Elapsed time: ", time.time() - tstart)

        if debug is True:
            print("thesize_ext: ", np.shape(theoim_resres))
            print("THEOIM_RESRES: ")
            plt.imshow(theoim_resres,
                       origin='bottom',
                       norm=LogNorm(),
                       interpolation='nearest')
            plt.show()
        # plt.imshow(theoim_res, origin='bottom')

    else:
        theoim_resres = np.copy(theoim_res)

    theosize_new = np.array(np.shape(theoim_resres))

    if debug is True:
        print("theosize_new: ", theosize_new)
        print("new theosize_as: ", theosize_new * pfov)

    # print(np.sum(theoim_resres))

    # --- after resampling we need to re-establish the right flux levels
    theoim_resres = theoim_resres / np.sum(theoim_resres) * theototflux

    # plt.imshow(theoim_resres, origin='bottom', norm=LogNorm())
    # plt.imshow(theoim_resres, origin='bottom')

    # ==== 4. Optionally, apply a foreground extinction map if provided ====
    if extfits:

        exthdu = fits.open(extfits)
        exthead = exthdu[0].header
        extpfov = exthead["PFOV"]

        # check if provided file is the full cube
        if 'NAXIS3' in exthead:

            # find the right frame to be extracted
            if not mind:
                mind, mwlen = find_wlen_match(theofits, wlen)

            extim = exthdu[0].data[mind]

        else:
            extim = exthdu[0].data

        # plt.imshow(extim,origin='bottom')
        # plt.show()

        exthdu.close()

        # --- Do we have to resample the extinction map
        if np.abs(extpfov - pfov) > 0.001:
            # print("Resampling extinction map... ")
            extim = ndimage.zoom(extim, 1.0 * pfov / extpfov, order=0)

        # --- match the size of the extinction map to the one of the model
        # image
        extsize = np.array(np.shape(extim))

        if theosize_new[0] > extsize[0]:
            print(" - ERROR: provided extinction map is too small to cover \
                   the imaged region. Abort...")
            return ()

        if extsize[0] > theosize_new[0]:
            # print("Cuttting exctinction map...")
            extim = _crop_image(extim, box=theosize_new)

        # --- finally apply the extinction map
        theoim_resres = theoim_resres * extim
        # print('Maximum extinction: ',np.min(extim))

    # ==== 5. Obtain the Airy function from the calibrator ====
    if debug is True:
        print("PSF_IM: ")
        plt.imshow(psfim,
                   origin='bottom',
                   norm=LogNorm(),
                   interpolation='nearest')
        plt.show()

    # --- set up the fitting of the STD star image
    # --- assume that the maximum of the (cropped) image is the STD star
    if fitpsf is True:
        psfmax = np.max(psfim)
        psfmaxpos = np.unravel_index(np.argmax(psfim), psfsize)
        # print(im_bg, im_max, im_size, im_maxpos)

        # --- Use an Airy plus a Moffat + constant as PSF model
        c_init = models.Const2D(amplitude=np.median(psfim))

        oa_init = obsc_AiryDisk2D(amplitude=psfmax,
                                  x_0=psfmaxpos[1],
                                  y_0=psfmaxpos[0],
                                  radius=5,
                                  obsrat=0.15)

        m_init = models.Moffat2D(amplitude=psfmax,
                                 x_0=psfmaxpos[1],
                                 y_0=psfmaxpos[0],
                                 gamma=5,
                                 alpha=1)

        a_plus_m = oa_init + m_init + c_init

        # print(psfmax, psfmaxpos)

        # --- Selection of fitting method
        fit_meth = fitting.LevMarLSQFitter()

        # --- Do the Fitting:
        y, x = np.mgrid[:psfsize[0], :psfsize[1]]
        am_fit = fit_meth(a_plus_m, x, y, psfim)
        # print(am_fit.radius_0.value, am_fit.obsrat_0.value)

        # then refine the fit by subtracting another Moffat
        # a_plus_m_minus_m = am_fit - m_init
        # amm_fit = fit_meth(a_plus_m_minus_m, x, y, psfim)
        # z_amm = amm_fit(x, y)
        #        res = psfim - z_amm
        #print(amm_fit.radius_0.value, amm_fit.obsrat_0.value)

        if meastime:
            print(" - PSf fit. Elapsed time: ", time.time() - tstart)

        if debug is True:
            print("PSF_FIT: ")
            plt.imshow(am_fit(x, y),
                       origin='bottom',
                       norm=LogNorm(),
                       interpolation='nearest')
            plt.show()

        # --- Genarate the PSF image with the fitted model
        # --- check whether the model image is on sky larger than the PSF image
        #     and if this is the case increase the grid and adjust the positions
        #     of the fits
        size_diff = theosize_new[0] - psfsize[0]
        if size_diff > 0:
            y, x = np.mgrid[:theosize_new[0], :theosize_new[1]]
            am_fit.x_0_0 = am_fit.x_0_0 + 0.5 * size_diff
            am_fit.y_0_0 = am_fit.y_0_0 + 0.5 * size_diff
            am_fit.x_0_1 = am_fit.x_0_1 + 0.5 * size_diff
            am_fit.y_0_1 = am_fit.y_0_1 + 0.5 * size_diff

        # --- create the final PSF image by subtracting the constant terms
        # and normalise by its area
        psf = am_fit(x, y) - am_fit.amplitude_2.value
        psf = psf / np.sum(psf)

        if debug is True:
            print("PSF_FINAL: ")
            plt.imshow(psf,
                       origin='bottom',
                       norm=LogNorm(),
                       interpolation='nearest')
            plt.show()

        # plt.imshow(psf, origin='bottom', norm=LogNorm())
        # plt.show()
        if writeallfits is True:

            fout = psffits.replace('.fits', '_fit.fits')
            if 'OBS NAME' in psfhead:
                psfhead.remove('OBS NAME')
            fits.writeto(fout, psf, psfhead, overwrite=True)

        # --- optinally, write the a plot documenting the quality of the PSF fit
        if writefitplot:

            fitplotfname = psffits.split('/')[-1]
            fitplotfname = fitplotfname.replace('.fits', '_fitplot.pdf')
            fitplotfname = outfolder + '/' + fitplotfname

            maxrad = int(0.5 * np.sqrt(0.5) * np.min(psfsize))

            _make_fit_plots(psfim,
                            am_fit(x, y),
                            fitplotfname,
                            am_fit.x_0_0,
                            am_fit.y_0_0,
                            maxrad,
                            inv=True,
                            cmap='gist_heat',
                            labelcolor='black')

    # --- alternatively the PSF can also be direclty provided so that no fit is
    #     necessary
    else:
        # normalise the provided psf
        psf = psfim - np.nanmin(psfim)
        psf = psf / np.nansum(psf)
        psf[np.argwhere(np.isnan(psf))] = 0

    # ==== 6. Convolution of the model image with the PSF ====
    simim = scipy.signal.convolve2d(theoim_resres, psf, mode='same')
    # print(np.sum(simim))

    if meastime:
        print(" - Model convolved. Elapsed time: ", time.time() - tstart)

    if debug is True:
        print("SIMIM:")
        plt.imshow(simim,
                   origin='bottom',
                   norm=LogNorm(),
                   interpolation='nearest')
        plt.show()

    # ==== 7. Flux measurements and application of noise ====
    # --- Flux measurement on the model image before applying noise
    ftotmod = int(np.nansum(simim))

    apos = 0.5 * np.array(theosize_new)

    nucrad_px = 0.5 * fnucdiam_as / pfov
    from aper import aper
    (mag, magerr, flux, fluxerr, sky, skyerr, badflag,
     outstr) = aper(simim,
                    apos[1],
                    apos[0],
                    apr=nucrad_px,
                    exact=True,
                    setskyval=0)

    fnucmod = int(flux)

    if not silent:
        print(' - Model total | nuclear flux [mJy]:    ' + str(ftotmod) +
              ' | ' + str(fnucmod))

    plotsize_px = int(1.0 * foi_as / pfov)

    # --- Measure the background noise from the real observation
    if obsfits is not None:
        bg = np.copy(obsim)
        #    bgsize = np.shape(bg)
        params, _, _ = _get_pointsource(obsim)
        xpos = params[2]
        ypos = params[3]

        bg[int(ypos - 0.5 * plotsize_px):int(ypos + 0.5 * plotsize_px),
           int(xpos - 0.5 * plotsize_px):int(xpos + 0.5 * plotsize_px)] = 0

        if bgstd == 0:
            bgstd = np.std(bg[bg != 0])

        if bgmed == 0:
            bgmed = np.median(bg[bg != 0])
        # plt.imshow(bg, origin='bottom', norm=LogNorm(), cmap='gist_heat')

        # --- crop the observational image to the requested plot size
        cim = _crop_image(obsim,
                          box=plotsize_px,
                          cenpos=_get_pointsource(obsim)[0][2:4])
        newsize = np.shape(cim)

        # --- make flux measurements on the nucleus and total
        ftotobs = int(np.sum(cim - bgmed))
        apos = 0.5 * np.array(newsize)

        (mag, magerr, flux, fluxerr, sky, skyerr, badflag,
         outstr) = aper(cim,
                        apos[1],
                        apos[0],
                        apr=nucrad_px,
                        exact=True,
                        setskyval=0)

        fnucobs = int(flux)

        if not silent:
            print(' - Observed total | nuclear flux [mJy]: ' + str(ftotobs) +
                  ' | ' + str(fnucobs))

    else:
        cim = None

    if meastime:
        print(" - Fluxes measured. Elapsed time: ", time.time() - tstart)

    # pdb.set_trace()
    # --- crop the simulated image to the requested plot size
    params, _, _ = _get_pointsource(simim)
    csimim = _crop_image(simim,
                         box=plotsize_px,
                         cenpos=_get_pointsource(simim)[0][2:4])
    if debug is True:
        plt.imshow(simim, origin='bottom', cmap='gist_heat', norm=LogNorm())
        plt.title('simim')
        plt.show()
        print(plotsize_px, np.shape(csimim))
        plt.imshow(csimim, origin='bottom', cmap='gist_heat', norm=LogNorm())
        plt.title('csimim')
        plt.show()

    if meastime:
        print(" - Obs cropped. Elapsed time: ", time.time() - tstart)

    # --- generate an artifical noise frame with same properties as in real
    #     image
    if bgstd > 0:
        artbg = np.random.normal(scale=bgstd,
                                 size=(plotsize_px, plotsize_px)) + bgmed
        # artbg = 0

        # --- apply the artificial background
        # csimim = csimim/np.max(csimim)*(np.max(cim)-bgmed)+bgmed+artbg
        csimim = csimim + artbg

    # --- crop the PSF image to the requested plot size
    cpsf = _crop_image(psf,
                       box=plotsize_px,
                       cenpos=_get_pointsource(psf)[0][2:4])

    # print(bgmed, bgstd, np.std(artbg), np.median(cim), np.median(csimim),
    # np.std(cim), np.std(csimim), np.max(cim), np.min(cim), np.max(csimim),
    # np.min(csimim))

    if meastime:
        print(" - PSF cropped. Elapsed time: ", time.time() - tstart)

    theopfov = theopixsize_as
    theobox = int(np.round(plotsize_px * pfov / theopfov))

    if returncmod:
        # --- crop the model imae to the requested plot size
        if framerat > 1.0:
            theoim_res_0 = _extend_image(theoim, fac=framerat)
        else:
            theoim_res_0 = theoim
        cmod = _crop_image(theoim_res_0, box=theobox, exact=False)
        # plt.imshow(cmod, origin='bottom', norm=LogNorm(), cmap='gist_heat')

        if meastime:
            print(" - Theo cropped. Elapsed time: ", time.time() - tstart)

    else:
        cmod = None

    # --- write out the simulated image as a fits file
    if not outname:

        theofitsfile = theofits.split("/")[-1]

        out_str = theofitsfile.replace("_total.fits", "")

        out_str = (out_str + "_pa" + pastr + "_dist" + diststr + "_wlen" +
                   wlenstr)

        if suffix:
            out_str = out_str + "_" + suffix

    else:
        out_str = outname

    out_str = outfolder + '/' + out_str

    if writeallfits or writetheofits:

        fout = out_str + '_mod.fits'

        theohead["Filter"] = filt
        theohead["WAVELEN"] = wlen
        theohead["PFOV"] = theopixsize_as

        fits.writeto(fout, cmod, theohead, overwrite=True)

        if saveplot:
            _simple_image_plot(cmod, fout.replace(".fits", ".png"), log=True)

    if writeallfits or writesimfits:

        fout = out_str + '_sim.fits'
        theohead["PFOV"] = pfov
        theohead["BUNIT"] = 'mJy'

        ts = np.shape(simim)
        theohead["CRPIX1"] = 0.5 * ts[1]
        theohead["CDELT1"] = pfov
        theohead["CTYPE1"] = 'arcsec'
        theohead["CRPIX2"] = 0.5 * ts[0]
        theohead["CDELT2"] = pfov
        theohead["CTYPE2"] = 'arcsec'

        fits.writeto(fout, csimim, theohead, overwrite=True)

        if saveplot:
            _simple_image_plot(csimim, fout.replace(".fits", ".png"), log=True)

    if meastime:
        print(" - All finished: ", time.time() - tstart)

    return (cpsf, cmod, cim, csimim, wlen, pfov, theopfov)
def extract_photometry(fn):
    f = fits.open(fn)
    d1 = f[1].data

    # Background subtraction : https://photutils.readthedocs.io/en/stable/background.html
    sigma_clip = SigmaClip(sigma=sclip)
    bkg_estimator = MedianBackground()
    bkg = Background2D(d1, (bg_wsize, bg_wsize),
                       sigma_clip=sigma_clip,
                       bkg_estimator=bkg_estimator)
    d2 = d1 - bkg.background

    # Building local positions, fitting Moffat2D models
    positions = []
    invalid = []

    for i, pt in enumerate(src_list):
        # Extracting the window for fitting
        x, y = pt
        x_min = x - mod_fit_size
        x_max = x + mod_fit_size
        y_min = y - mod_fit_size
        y_max = y + mod_fit_size
        window = d2[y_min:y_max + 1, x_min:x_max + 1]

        # Initial guess
        z0 = d2[y, x]
        m_init = models.Moffat2D(z0, x, y)

        # Fitting, we catch warnings as exceptions in case the fit fails
        with warnings.catch_warnings(record=True) as w:
            fit_m = fitting.LevMarLSQFitter()
            xv, yv = np.meshgrid(range(x_min, x_max + 1),
                                 range(y_min, y_max + 1))
            p = fit_m(m_init, xv, yv, window)

            if w and issubclass(w[-1].category, AstropyUserWarning):
                print(
                    'Warning : The fit might not have converged for source #{} at position {}'
                    .format(i, pt))
                invalid.append(i)

        px = p.x_0.value
        py = p.y_0.value
        pz = p.amplitude.value

        # Storing info for animation
        if i == wid:
            global xvv, yvv, windows
            xvv = xv
            yvv = yv

            ix = int(round(px))
            iy = int(round(py))

            x_min = ix - mod_fit_size
            x_max = ix + mod_fit_size
            y_min = iy - mod_fit_size
            y_max = iy + mod_fit_size

            nw = d2[y_min:y_max + 1, x_min:x_max + 1]
            windows += [nw]

        # Rendering fit to file
        if os.path.exists('fits') and plot_fits:
            fig = plt.figure(figsize=(10, 10))
            ax = fig.add_subplot(111, projection='3d')
            ax.plot_surface(xv, yv, window)
            ax.scatter(px, py, pz, s=3, color='red')
            plt.savefig('fits/fit_{}_{}_{}.png'.format(fn, x, y))
            plt.close('all')

        positions += [(p.x_0.value, p.y_0.value)]

    # Aperture photometry : https://photutils.readthedocs.io/en/stable/aperture.html
    apertures = CircularAperture(positions, r=aperture_r)
    annulus_apertures = CircularAnnulus(positions, r_in=sky_in, r_out=sky_out)
    apers = [apertures, annulus_apertures]

    phot_table = aperture_photometry(d2, apers)

    # Mean sky subtraction
    bkg_mean = phot_table['aperture_sum_1'] / annulus_apertures.area()
    bkg_sum = bkg_mean * apertures.area()
    final_sum = phot_table['aperture_sum_0'] - bkg_sum

    # Calculating zero-point : http://www.stsci.edu/hst/wfpc2/analysis/wfpc2_cookbook.html
    h0 = f[0].header
    h1 = f[1].header

    phot_zpt = h1['PHOTZPT']
    phot_flam = h1['PHOTFLAM']
    zero_pt = -2.5 * np.log10(phot_flam) + phot_zpt

    # TODO : Correct from STMAG to Cousins
    magnitudes = []
    for i, flux in enumerate(final_sum):
        if i in invalid:
            magnitudes.append(np.nan)
        else:
            m = -2.5 * np.log10(flux) + zero_pt
            magnitudes.append(m)

    print(fn, h0['EXPEND'], magnitudes)
    return h0['EXPEND'], magnitudes
Esempio n. 19
0
    def moffats(self, x0, y0, fwhmpix=3., dxymax=5., verbose=True, **kwargs):
        """

        Fits N Moffat profiles to N different locations in the image.
        This multiple-component fitting might be done, for example, to fit
         to the multiple lensed quasar images in a gravitational lens system,
         or to a set of stars in an image
        The number of components to be fit is set by the number of elements
         of the x0 (and y0) arrays.

        """

        """ Get the number of components to fit """
        if isinstance(x0, int):
            x0 = float(x0)
        if isinstance(y0, int):
            y0 = float(y0)
        xinit = np.atleast_1d(x0)
        yinit = np.atleast_1d(y0)
        if xinit.size != yinit.size:
            raise ValueError('x0 and y0 are not the same size')
        nmoffat = xinit.size

        """ Create the Moffat profiles with initial guess parameters """
        x2 = np.zeros(nmoffat)
        y2 = np.zeros(nmoffat)
        for i in range(nmoffat):
            """
            First refine the position guess by calling the moments method
            """
            objstats = self.moments(xinit[i], yinit[i], **kwargs)
            x2[i] = objstats['mux']
            y2[i] = objstats['muy']

            """
            Do a crude sky subtraction to estimate the starting amplitude.
            Note that the mean_clip attribute will have been set through
            the call to self.moments
            """
            amp0 = self.data[int(y2[i]), int(x2[i])] - self.mean_clip
            
            """
            Create a 2d Moffat profile with initial guess values
            The initial value for what astropy calls alpha and others
             call beta, i.e., 4.765, comes from Trujillo et al. 2001
            The initial value for what astropy calls gamma and others 
             call alpha is related to the FWHM and (alpha/beta)
            """
            alpha0 = 4.765
            gamma0 = fwhmpix / (2. * sqrt(2.**(1./alpha0) - 1.))
            tmpmod = models.Moffat2D(amplitude=amp0, x_0=x2[i], y_0=y2[i],
                                     alpha=alpha0, gamma=gamma0)

            """ Set bounds for the parameters """
            tmpmod.x_0.bounds = (x2[i] - dxymax, x2[i] + dxymax)
            tmpmod.y_0.bounds = (y2[i] - dxymax, y2[i] + dxymax)
            tmpmod.amplitude.bounds = (0., None)

            """
            Tie the alpha and gamma parameters together, and add this model
            to the compound model
            """
            if i==0:
                mod = tmpmod
            else:
                mod += tmpmod
                mod[i].alpha.tied = tie_alpha
                mod[i].gamma.tied = tie_gamma
                
        """ Fit the model to the data """
        fit = fitting.LevMarLSQFitter()
        outmod = fit(mod, self.x, self.y, self.data)
        # outmod = fit(mod, self.x, self.y, self.data, weights=1.0/rms)

        """ Report on fit if requested """
        print('  Initial           Refined         Final')
        print('-------------   -------------   -------------')
        for i in range(nmoffat):
            print('%6.2f %6.2f   %6.2f %6.2f   %6.2f %6.2f' %
                  (xinit[i], yinit[i], x2[i], y2[i], outmod[i].x_0.value,
                   outmod[i].y_0.value))
        return outmod
Esempio n. 20
0
 def moffatFit(self, image):
     self._getInitParameters(image)
     fit_model = models.Moffat2D(x_0=self.init_guessTable['xcentroid'],
                                 y_0=self.init_guessTable['ycentroid'],
                                 amplitude=self.init_guessTable['peak'])
     self._fit = self._fitter(fit_model, self._x, self._y, image)
Esempio n. 21
0
def create_synth_psf(model='gauss',
                     shape=(9, 9),
                     amplitude=1,
                     x_mean=None,
                     y_mean=None,
                     fwhm=4,
                     theta=0,
                     gamma=None,
                     alpha=1.5,
                     radius=None,
                     msdi=False):
    """ Creates a synthetic 2d or 3d PSF with a 2d model: Airy disk, Gaussian or
    Moffat, depending on ``model``.

    Parameters
    ----------
    model : {'gauss', 'moff', 'airy'}, str optional
        Model to be used to create the synthetic PSF.
    shape : tuple of ints, optional
        Shape of the output 2d array.
    amplitude : float, optional
        Value of the amplitude of the 2d distribution.
    x_mean : float or None, optional
        Value of the centroid in X of the distributions: the mean of the
        Gaussian or the location of the maximum of the Moffat or Airy disk
        models. If None, the centroid is placed at the center of the array.
    y_mean : float or None, optional
        Value of the centroid in Y of the distributions: the mean of the
        Gaussian or the location of the maximum of the Moffat or Airy disk
        models. If None, the centroid is placed at the center of the array.
    fwhm : float, tuple of floats, list or np.ndarray, optional
        FWHM of the model in pixels. For the Gaussian case, it controls the
        standard deviation of the Gaussian. If a tuple is given, then the
        Gaussian will be elongated (fwhm in x, fwhm in y). For the Moffat, it is
        related to the gamma and alpha parameters. For the Airy disk, it is
        related to the radius (of the first zero) parameter. If ``msdi`` is True
        then ``fwhm`` must be a list of 1d np.ndarray (for example for
        SPHERE/IFS this sounds like a reasonable FWHM: np.linspace(4.5,6.7,39)).
    theta : float, optional
        Rotation angle in degrees of the Gaussian.
    gamma : float or None, optional
        Gamma parameter of core width of the Moffat model. If None, then it is
        calculated to correspond to the given ``fwhm``.
    alpha : float, optional
        Power index of the Moffat model.
    radius : float or None, optional
        The radius of the Airy disk (radius of the first zero). If None, then it
        is calculated to correspond to the given ``fwhm``.
    msdi : bool, optional
        Creates a 3d PSF, for emulating an IFS PSF.

    Returns
    -------
    im : numpy ndarray
        2d array with given ``shape`` and containing the synthetic PSF.

    Notes
    -----
    http://docs.astropy.org/en/stable/api/astropy.modeling.functional_models.Gaussian2D.html
    http://docs.astropy.org/en/stable/api/astropy.modeling.functional_models.Moffat2D.html
    http://docs.astropy.org/en/stable/api/astropy.modeling.functional_models.AiryDisk2D.html

    https://www.gnu.org/software/gnuastro/manual/html_node/PSF.html
    web.ipac.caltech.edu/staff/fmasci/home/astro_refs/PSFtheory.pdf
    web.ipac.caltech.edu/staff/fmasci/home/astro_refs/PSFsAndSampling.pdf
    """
    # 2d case
    if not msdi:
        sizex, sizey = shape
        if x_mean is None or y_mean is None:
            y_mean, x_mean = frame_center(np.zeros((sizey, sizex)))
        x = np.arange(sizex)
        y = np.arange(sizey)
        x, y = np.meshgrid(x, y)

        if model == 'gauss':
            if np.isscalar(fwhm):
                fwhm_y = fwhm
                fwhm_x = fwhm
            else:
                fwhm_x, fwhm_y = fwhm
            gauss = models.Gaussian2D(amplitude=amplitude,
                                      x_mean=x_mean,
                                      y_mean=y_mean,
                                      x_stddev=fwhm_x * gaussian_fwhm_to_sigma,
                                      y_stddev=fwhm_y * gaussian_fwhm_to_sigma,
                                      theta=np.deg2rad(theta))
            im = gauss(x, y)
        elif model == 'moff':
            if gamma is None and fwhm is not None:
                gamma = fwhm / (2. * np.sqrt(2**(1 / alpha) - 1))
            moffat = models.Moffat2D(amplitude=amplitude,
                                     x_0=x_mean,
                                     y_0=y_mean,
                                     gamma=gamma,
                                     alpha=alpha)
            im = moffat(x, y)
        elif model == 'airy':
            if radius is None and fwhm is not None:
                diam_1st_zero = (fwhm * 2.44) / 1.028
                radius = diam_1st_zero / 2.
            airy = models.AiryDisk2D(amplitude=amplitude,
                                     x_0=x_mean,
                                     y_0=y_mean,
                                     radius=radius)
            im = airy(x, y)
        return im
    # 3d case
    else:
        if np.isscalar(fwhm):
            raise ValueError('`Fwhm` must be a 1d vector')

        cube = []
        for fwhm_i in fwhm:
            cube.append(
                create_synth_psf(model, shape, amplitude, x_mean, y_mean,
                                 fwhm_i, theta, gamma, alpha, radius))
        cube = np.array(cube)
        return cube
Esempio n. 22
0
def fit_2dmoffat(array,
                 crop=False,
                 cent=None,
                 cropsize=15,
                 fwhm=4,
                 threshold=False,
                 sigfactor=6,
                 full_output=True,
                 debug=True):
    """ Fitting a 2D Moffat to the 2D distribution of the data.

    Parameters
    ----------
    array : numpy ndarray
        Input frame with a single PSF.
    crop : bool, optional
        If True a square sub image will be cropped equal to cropsize.
    cent : tuple of int, optional
        X,Y integer position of source in the array for extracting the subimage.
        If None the center of the frame is used for cropping the subframe (the
        PSF is assumed to be ~ at the center of the frame).
    cropsize : int, optional
        Size of the subimage.
    fwhm : float, optional
        Initial values for the FWHM of the fitted 2d Moffat, in px.
    threshold : bool, optional
        If True the background pixels (estimated using sigma clipped statistics)
        will be replaced by small random Gaussian noise.
    sigfactor : int, optional
        The background pixels will be thresholded before fitting a 2d Moffat
        to the data using sigma clipped statistics. All values smaller than
        (MEDIAN + sigfactor*STDDEV) will be replaced by small random Gaussian
        noise.
    full_output : bool, optional
        If False it returns just the centroid, if True also returns the
        FWHM in X and Y (in pixels), the amplitude and the rotation angle.
    debug : bool, optional
        If True, the function prints out parameters of the fit and plots the
        data, model and residuals.

    Returns
    -------
    mean_y : float
        Source centroid y position on input array from fitting.
    mean_x : float
        Source centroid x position on input array from fitting.

    If ``full_output`` is True it returns a Pandas dataframe containing the
    following columns:
    'alpha': Float value. Alpha parameter.
    'amplitude' : Float value. Moffat Amplitude.
    'centroid_x' : Float value. X coordinate of the centroid.
    'centroid_y' : Float value. Y coordinate of the centroid.
    'fwhm' : Float value. FHWM [px].
    'gamma' : Float value. Gamma parameter.

    """
    check_array(array, dim=2, msg='array')

    if crop:
        if cent is None:
            ceny, cenx = frame_center(array)
        else:
            cenx, ceny = cent

        imside = array.shape[0]
        psf_subimage, suby, subx = get_square(array,
                                              min(cropsize, imside),
                                              ceny,
                                              cenx,
                                              position=True)
    else:
        psf_subimage = array.copy()

    if threshold:
        _, clipmed, clipstd = sigma_clipped_stats(psf_subimage, sigma=2)
        indi = np.where(psf_subimage <= clipmed + sigfactor * clipstd)
        subimnoise = np.random.randn(psf_subimage.shape[0],
                                     psf_subimage.shape[1]) * clipstd
        psf_subimage[indi] = subimnoise[indi]

    # Creating the 2D Moffat model
    init_amplitude = np.ptp(psf_subimage)
    xcom, ycom = cen_com(psf_subimage)
    moffat = models.Moffat2D(amplitude=init_amplitude,
                             x_0=xcom,
                             y_0=ycom,
                             gamma=fwhm / 2.,
                             alpha=1)
    # Levenberg-Marquardt algorithm
    fitter = fitting.LevMarLSQFitter()
    y, x = np.indices(psf_subimage.shape)
    fit = fitter(moffat, x, y, psf_subimage)

    if crop:
        mean_y = fit.y_0.value + suby
        mean_x = fit.x_0.value + subx
    else:
        mean_y = fit.y_0.value
        mean_x = fit.x_0.value

    fwhm = fit.fwhm
    amplitude = fit.amplitude.value
    alpha = fit.alpha.value
    gamma = fit.gamma.value

    if debug:
        if threshold:
            label = ('Subimage thresholded', 'Model', 'Residuals')
        else:
            label = ('Subimage', 'Model', 'Residuals')
        plot_frames((psf_subimage, fit(x, y), psf_subimage - fit(x, y)),
                    grid=True,
                    grid_spacing=1,
                    label=label)
        print('FWHM =', fwhm)
        print('centroid y =', mean_y)
        print('centroid x =', mean_x)
        print('centroid y subim =', fit.y_0.value)
        print('centroid x subim =', fit.x_0.value, '\n')
        print('amplitude =', amplitude)
        print('alpha =', alpha)
        print('gamma =', gamma)

    # compute uncertainties
    if fitter.fit_info['param_cov'] is not None:
        perr = np.sqrt(np.diag(fitter.fit_info['param_cov']))
        amplitude_err, mean_x_err, mean_y_err, gamma_err, alpha_err = perr
        fwhm_err = 2 * gamma_err
    else:
        amplitude_err, mean_x_err, mean_y_err = None, None, None
        gamma_err, alpha_err, fwhm_err = None, None, None

    if full_output:
        return pd.DataFrame(
            {
                'centroid_y': mean_y,
                'centroid_x': mean_x,
                'fwhm': fwhm,
                'alpha': alpha,
                'gamma': gamma,
                'amplitude': amplitude,
                'centroid_y_err': mean_y_err,
                'centroid_x_err': mean_x_err,
                'fwhm_err': fwhm_err,
                'alpha_err': alpha_err,
                'gamma_err': gamma_err,
                'amplitude_err': amplitude_err
            },
            index=[0],
            dtype=np.float64)
    else:
        return mean_y, mean_x
Esempio n. 23
0
def moffat(xcen,ycen,amp,wid,power):
    return models.Moffat2D(amplitude=amp, x_0=xcen, y_0=ycen, gamma=wid, alpha=power)
Esempio n. 24
0
def fit_2D_Moffat(box,
                  center=None,
                  fixed_center=False,
                  deviation_center=None,
                  x_shift=0.0,
                  y_shift=0.0,
                  zoom_factor=1.0,
                  mask=None):
    """
    This function ...
    :param box:
    :param center:
    :param fixed_center:
    :param deviation_center:
    :param x_shift:
    :param y_shift:
    :param zoom_factor:
    :param mask:
    :return:
    """

    # Get the dimensions of the box
    box_ysize = box.shape[0]
    box_xsize = box.shape[1]

    # Set the initial guess for the center of the model (the one that is specified, otherwise the center of the box)
    init_x0 = center[0] if center is not None else 0.5 * (box_xsize - 1)
    init_y0 = center[1] if center is not None else 0.5 * (box_ysize - 1)

    # Initialize an empty dictionary to specify fixed parameters
    fixed_parameters = {}

    if fixed_center:

        fixed_parameters['x_0'] = True
        fixed_parameters['y_0'] = True

    # Initialize an empty dictionary to specify bounds
    bounds = {}

    if deviation_center is not None:

        bounds['x_mean'] = [
            init_x0 - deviation_center, init_x0 + deviation_center
        ]
        bounds['y_mean'] = [
            init_y0 - deviation_center, init_y0 + deviation_center
        ]

    # Fit the data using astropy.modeling
    moffat_init = models.Moffat2D(amplitude=1.,
                                  x_0=init_x0,
                                  y_0=init_y0,
                                  gamma=1.0,
                                  alpha=1.0,
                                  fixed=fixed_parameters,
                                  bounds=bounds)
    fit_model = fitting.LevMarLSQFitter()

    x_values = []
    y_values = []
    z_values = []

    for x in range(box_xsize):
        for y in range(box_ysize):

            # If no mask is specified or the pixel is not masked, add the coordinates and value to the appropriate lists
            if mask is None or not mask[y, x]:

                x_values.append(x)
                y_values.append(y)
                z_values.append(box[y, x])

    # Ignore model linearity warning from the fitter
    with warnings.catch_warnings():

        warnings.simplefilter('ignore')
        moffat = fit_model(
            moffat_init, x_values, y_values,
            z_values)  # What comes out is the model with the parameters set

    # Adjust the position of the model to a different coordinate frame
    if zoom_factor > 1.0:

        moffat.x_0.value = moffat.x_0.value / zoom_factor + x_shift
        moffat.y_0.value = moffat.y_0.value / zoom_factor + y_shift

    else:

        moffat.x_0.value += x_shift
        moffat.y_0.value += y_shift

    # Return the fitted two-dimensional Moffat model
    return moffat
Esempio n. 25
0
def addstarpeak(dir, debug=False, mask=False, ghost=False, wl='Line'):
    """
    fits star (or ghost) and adds STARPEAK to header. needed for PyKLIP.
    :param dir: directory
    :param amp: amplitude guess
    :param fwhm: fwhm
    :param debug: will print comparison of peak pixel values to fit values if set
    :param mask: will mask NaNs with zeros for fitting if set
    :param ghost: will fit ghost in lieu of star and return ghost peak and estimate for star peak in header.
    :param wl: set if ghost is set so it knows which scale factor to pull. values are "Line" or "Cont"
    :return: list of star (or ghost) peaks
    """
    filelist = glob.glob(dir + '/*.fits')
    ##sort sequentially
    filelist.sort(key=lambda f: int(''.join(filter(str.isdigit, f))))
    dummy_im = fits.getdata(filelist[0])
    size = dummy_im.shape
    xcen = int((size[0] - 1) / 2)
    ycen = int((size[1] - 1) / 2)
    # guesses for fit parameters. used as starting point
    # input_parameters = [0, amp, xcen, ycen, fwhm, fwhm, 0]
    if ghost == True:
        xcen = int(xcen + 157.5)
        ycen = int(ycen - 7)
        if wl == "Line":
            ghost_scale = 184.7
        if wl == "Cont":
            ghost_scale = 193.6
        if wl == "False":
            "wl keyword must be set for ghost calibration"
            return
    diff = np.zeros(len(filelist))
    peaks = []
    fwhmlist = []
    #size of image stamp
    width = 31
    stmpsz = int((width - 1) / 2)

    for i in np.arange(len(filelist)):
        im = fits.getdata(filelist[i])
        # make a copy - only invoked in case of ghost=True, but can't think of a cleverer way
        imcopy = np.copy(im)
        head = fits.getheader(filelist[i])
        #crop around star (or ghost) for fitting
        imcopy = imcopy[ycen - stmpsz - 1:ycen + stmpsz,
                        xcen - stmpsz - 1:xcen + stmpsz]

        #set up fit
        y, x = np.mgrid[:width, :width]
        #Moffat PSF model
        g_init = models.Moffat2D(np.nanmax(imcopy), stmpsz, stmpsz, 6, 1)
        fit_g = fitting.LevMarLSQFitter()

        #if ghost == True:
        # CROP AROUND GHOST
        #im = im[ycen - 50:ycen + 50 + 1, xcen - 50:xcen + 50 + 1]

        # do fit
        p = fit_g(g_init, x, y, imcopy)

        # populate headers for each individual image
        if ghost == True:
            head['GSTPEAK'] = p.amplitude.value
            head['STARPEAK'] = p.amplitude.value * ghost_scale
            head['FWHM'] = str(p.fwhm)

        else:
            head['STARPEAK'] = p.amplitude.value
            head['FWHM'] = str(p.fwhm)

        # record peak
        peaks.append(p.amplitude.value)

        # record fwhm
        fwhmlist.append(p.fwhm)

        # print a warning if any peak values are unphysical
        if (p.amplitude.value < 0) or (p.amplitude.value > 17000) or (np.isnan(
                p.amplitude.value)) == True:
            print("warning: unphysical peak value of", p.amplitude.value,
                  'for image', i + 1)

        # write out file with peak info in header
        if ghost == True:
            fits.writeto(filelist[i], im, header=head, overwrite=True)
        else:
            fits.writeto(filelist[i], im, header=head, overwrite=True)

        if debug == True:
            # print(filelist[i])
            # print('fit peak is:', p[0], '. max pixel is: ', np.nanmax(im[cen-10:cen+10,cen-10:cen+10]))
            imsz = im.shape[1]
            imcen = int((imsz - 1) / 2.)
            diff[i] = p.amplitude.value - np.nanmax(im[imcen - 10:imcen + 10,
                                                       imcen - 10:imcen + 10])

    # write out list of peaks one directory up so KLIP doesn't try to pull it
    if ghost == True:
        fits.writeto(dir + '../' + str(wl) + 'ghostpeaks.fits',
                     np.array(peaks),
                     overwrite=True)
        fits.writeto(dir + '../' + str(wl) + 'ghostfwhmlist.fits',
                     np.array(fwhmlist),
                     overwrite=True)
    else:
        fits.writeto(dir + '../' + str(wl) + 'starpeaks.fits',
                     np.array(peaks),
                     overwrite=True)
        fits.writeto(dir + '../' + str(wl) + 'starfwhmlist.fits',
                     np.array(fwhmlist),
                     overwrite=True)

    if debug == True:
        print(
            'standard deviation of difference between fit peak and max pixel is: ',
            np.std(diff))
        print('max difference is:', np.max(abs(diff)))
        print('median difference is:', np.median(diff))
    return (peaks)