Ejemplo n.º 1
0
def snr_astropy(image,sx,sy,r,r_in,r_out):
    """
    Returns signal to noise ratio, signal value, noise value using Astropy's Photutils module
    Args:
        image (2d float array): Image array extracted from fits file
        sx,sy (float): x and y pixel locations for the center of the source
        r (float): radius for aperture
        r_in,r_out (float): inner and outer radii for annulus
    Return:
        snr (float): signal-to-noise ratio
        signal (float): sum of all pixel values within the specified aperture minus sky background
        noise (float): noise in the signal calculated as std deviation of pixels in the sky annulus times sqrt of the area of the
        signal aperture
        poisson_noise (float): snr determined from the poisson noise in the source signal.  Poisson noise = sqrt(counts) [because variance
        of a poisson distribution is the parameter itself].  Poisson snr = Signal/sqrt(signal) = sqrt(signal)
    Written by: Logan Pearce, 2018
    """
    import warnings
    warnings.filterwarnings('ignore')
    from photutils import CircularAperture, CircularAnnulus
    positions = (cx-1,cy-1)
    ap = CircularAperture(positions,r=r)
    skyan = CircularAnnulus(positions,r_in=11,r_out=14)
    apsum = ap.do_photometry(image)[0]
    skysum = skyan.do_photometry(image)[0]
    averagesky = skysum/skyan.area()
    signal = (apsum - ap.area()*averagesky)[0]
    n = ap.area()
    ap_an = aperture_annulus(sx,sy,r,r_in,r_out)
    skyan = ap_an[1]
    poisson_noise = np.sqrt(signal)
    noise = noise = np.std(image[skyan[1],skyan[0]])
    noise = noise*np.sqrt(n)
    snr = signal/noise
    return snr,signal,noise,poisson_noise
Ejemplo n.º 2
0
def compute_snr(x, y):
    positions = (x - 1, y - 1)
    ap = CircularAperture(positions, r=radius)
    skyan = CircularAnnulus(positions, r_in=11, r_out=14)
    ap = CircularAperture(positions, r=radius)
    skyan = CircularAnnulus(positions, r_in=11, r_out=14)
    apsum = ap.do_photometry(image)[0]
    skysum = skyan.do_photometry(image)[0]
    averagesky = skysum / skyan.area()
    signal = (apsum - ap.area() * averagesky)[0]
    n = ap.area()
    box = image[y + 12:y + 12 + 15, x + 12:x + 12 + 15]
    noise = np.std(box)
    noise = noise * np.sqrt(n)
    snr = signal / noise
    return snr
Ejemplo n.º 3
0
def tso_aperture_photometry(datamodel, xcenter, ycenter, radius, radius_inner,
                            radius_outer):
    """
    Create a photometric catalog for NIRCam TSO imaging observations.

    Parameters
    ----------
    datamodel : `CubeModel`
        The input `CubeModel` of a NIRCam TSO imaging observation.

    xcenter, ycenter : float
        The ``x`` and ``y`` center of the aperture.

    radius : float
        The radius (in pixels) of the circular aperture.

    radius_inner, radius_outer : float
        The inner and outer radii (in pixels) of the circular-annulus
        aperture, used for local background estimation.

    Returns
    -------
    catalog : `~astropy.table.QTable`
        An astropy QTable (Quantity Table) containing the source
        photometry.
    """

    if not isinstance(datamodel, CubeModel):
        raise ValueError('The input data model must be a CubeModel.')

    # For the SUB64P subarray with the WLP8 pupil, the circular aperture
    # extends beyond the image and the circular annulus does not have any
    # overlap with the image.  In that case, we simply sum all values
    # in the array and skip the background subtraction.
    sub64p_wlp8 = False
    if (datamodel.meta.instrument.pupil == 'WLP8'
            and datamodel.meta.subarray.name == 'SUB64P'):
        sub64p_wlp8 = True

    if not sub64p_wlp8:
        phot_aper = CircularAperture((xcenter, ycenter), r=radius)
        bkg_aper = CircularAnnulus((xcenter, ycenter),
                                   r_in=radius_inner,
                                   r_out=radius_outer)

    # convert the input data and errors from MJy/sr to Jy
    if datamodel.meta.bunit_data != 'MJy/sr':
        raise ValueError('data is expected to be in units of MJy/sr')
    factor = 1.e6 * datamodel.meta.photometry.pixelarea_steradians
    datamodel.data *= factor
    datamodel.err *= factor
    datamodel.meta.bunit_data = 'Jy'
    datamodel.meta.bunit_err = 'Jy'

    aperture_sum = []
    aperture_sum_err = []
    annulus_sum = []
    annulus_sum_err = []

    nimg = datamodel.data.shape[0]

    if sub64p_wlp8:
        info = ('Photometry measured as the sum of all values in the '
                'subarray.  No background subtraction was performed.')

        for i in np.arange(nimg):
            aperture_sum.append(np.sum(datamodel.data[i, :, :]))
            aperture_sum_err.append(np.sqrt(np.sum(datamodel.err[i, :, :]**2)))
    else:
        info = ('Photometry measured in a circular aperture of r={0} '
                'pixels.  Background calculated as the mean in a '
                'circular annulus with r_inner={1} pixels and '
                'r_outer={2} pixels.'.format(radius, radius_inner,
                                             radius_outer))
        for i in np.arange(nimg):
            aper_sum, aper_sum_err = phot_aper.do_photometry(
                datamodel.data[i, :, :], error=datamodel.err[i, :, :])
            ann_sum, ann_sum_err = bkg_aper.do_photometry(
                datamodel.data[i, :, :], error=datamodel.err[i, :, :])

            aperture_sum.append(aper_sum[0])
            aperture_sum_err.append(aper_sum_err[0])
            annulus_sum.append(ann_sum[0])
            annulus_sum_err.append(ann_sum_err[0])

    aperture_sum = np.array(aperture_sum)
    aperture_sum_err = np.array(aperture_sum_err)
    annulus_sum = np.array(annulus_sum)
    annulus_sum_err = np.array(annulus_sum_err)

    # construct metadata for output table
    meta = OrderedDict()
    meta['instrument'] = datamodel.meta.instrument.name
    meta['detector'] = datamodel.meta.instrument.detector
    meta['channel'] = datamodel.meta.instrument.channel
    meta['subarray'] = datamodel.meta.subarray.name
    meta['filter'] = datamodel.meta.instrument.filter
    meta['pupil'] = datamodel.meta.instrument.pupil
    meta['target_name'] = datamodel.meta.target.catalog_name
    meta['xcenter'] = xcenter
    meta['ycenter'] = ycenter
    ra_icrs, dec_icrs = datamodel.meta.wcs(xcenter, ycenter)
    meta['ra_icrs'] = ra_icrs
    meta['dec_icrs'] = dec_icrs
    meta['apertures'] = info

    # initialize the output table
    tbl = QTable(meta=meta)

    # check for the INT_TIMES table extension
    if hasattr(datamodel, 'int_times') and datamodel.int_times is not None:
        nrows = len(datamodel.int_times)
    else:
        nrows = 0
        log.warning("The INT_TIMES table in the input file is missing or "
                    "empty.")

    # load the INT_TIMES table data
    if nrows > 0:
        shape = datamodel.data.shape
        if len(shape) == 2:
            num_integ = 1
        else:  # len(shape) == 3
            num_integ = shape[0]
        int_start = datamodel.meta.exposure.integration_start
        if int_start is None:
            int_start = 1
            log.warning(f"INTSTART not found; assuming a value of {int_start}")

        # Columns of integration numbers & times of integration from the
        # INT_TIMES table.
        int_num = datamodel.int_times['integration_number']
        mid_utc = datamodel.int_times['int_mid_MJD_UTC']
        offset = int_start - int_num[0]  # both are one-indexed
        if offset < 0:
            log.warning("Range of integration numbers in science data extends "
                        "outside the range in INT_TIMES table.")
            log.warning("Can't use INT_TIMES table.")
            del int_num, mid_utc
            nrows = 0  # flag as bad
        else:
            log.debug("Times are from the INT_TIMES table")
            time_arr = mid_utc[offset:offset + num_integ]
            int_times = Time(time_arr, format='mjd', scale='utc')

    # compute integration time stamps on the fly
    if nrows == 0:
        log.debug("Times computed from EXPSTART and EFFINTTM")
        dt = datamodel.meta.exposure.integration_time
        n_dt = (datamodel.meta.exposure.integration_end -
                datamodel.meta.exposure.integration_start + 1)
        dt_arr = (np.arange(1, 1 + n_dt) * dt - (dt / 2.))
        int_dt = TimeDelta(dt_arr, format='sec')
        int_times = (Time(datamodel.meta.exposure.start_time, format='mjd') +
                     int_dt)

    # populate table columns
    unit = u.Unit(datamodel.meta.bunit_data)
    tbl['MJD'] = int_times.mjd
    tbl['aperture_sum'] = aperture_sum << unit
    tbl['aperture_sum_err'] = aperture_sum_err << unit

    if not sub64p_wlp8:
        tbl['annulus_sum'] = annulus_sum << unit
        tbl['annulus_sum_err'] = annulus_sum_err << unit

        if LooseVersion(photutils.__version__) >= '0.7':
            annulus_mean = annulus_sum / bkg_aper.area
            annulus_mean_err = annulus_sum_err / bkg_aper.area

            aperture_bkg = annulus_mean * phot_aper.area
            aperture_bkg_err = annulus_mean_err * phot_aper.area
        else:
            annulus_mean = annulus_sum / bkg_aper.area()
            annulus_mean_err = annulus_sum_err / bkg_aper.area()

            aperture_bkg = annulus_mean * phot_aper.area()
            aperture_bkg_err = annulus_mean_err * phot_aper.area()

        tbl['annulus_mean'] = annulus_mean << unit
        tbl['annulus_mean_err'] = annulus_mean_err << unit

        tbl['aperture_bkg'] = aperture_bkg << unit
        tbl['aperture_bkg_err'] = aperture_bkg_err << unit

        net_aperture_sum = aperture_sum - aperture_bkg
        net_aperture_sum_err = np.sqrt(aperture_sum_err**2 +
                                       aperture_bkg_err**2)
        tbl['net_aperture_sum'] = net_aperture_sum << unit
        tbl['net_aperture_sum_err'] = net_aperture_sum_err << unit
    else:
        colnames = [
            'annulus_sum', 'annulus_sum_err', 'annulus_mean',
            'annulus_mean_err', 'aperture_bkg', 'aperture_bkg_err'
        ]
        for col in colnames:
            tbl[col] = np.full(nimg, np.nan)

        tbl['net_aperture_sum'] = aperture_sum << unit
        tbl['net_aperture_sum_err'] = aperture_sum_err << unit

    return tbl
Ejemplo n.º 4
0
def tso_aperture_photometry(datamodel, xcenter, ycenter, radius, radius_inner,
                            radius_outer):
    """
    Create a photometric catalog for NIRCam TSO imaging observations.

    Parameters
    ----------
    datamodel : `CubeModel`
        The input `CubeModel` of a NIRCam TSO imaging observation.

    xcenter, ycenter : float
        The ``x`` and ``y`` center of the aperture.

    radius : float
        The radius (in pixels) of the circular aperture.

    radius_inner, radius_outer : float
        The inner and outer radii (in pixels) of the circular-annulus
        aperture, used for local background estimation.

    Returns
    -------
    catalog : `~astropy.table.QTable`
        An astropy QTable (Quantity Table) containing the source
        photometry.
    """

    if not isinstance(datamodel, CubeModel):
        raise ValueError('The input data model must be a CubeModel.')

    # For the SUB64P subarray with the WLP8 pupil, the circular aperture
    # extends beyond the image and the circular annulus does not have any
    # overlap with the image.  In that case, we simply sum all values
    # in the array and skip the background subtraction.
    sub64p_wlp8 = False
    if (datamodel.meta.instrument.pupil == 'WLP8' and
            datamodel.meta.subarray.name == 'SUB64P'):
        sub64p_wlp8 = True

    if not sub64p_wlp8:
        phot_aper = CircularAperture((xcenter, ycenter), r=radius)
        bkg_aper = CircularAnnulus((xcenter, ycenter), r_in=radius_inner,
                                   r_out=radius_outer)

    aperture_sum = []
    aperture_sum_err = []
    annulus_sum = []
    annulus_sum_err = []

    nimg = datamodel.data.shape[0]

    if sub64p_wlp8:
        info = ('Photometry measured as the sum of all values in the '
               'subarray.  No background subtraction was performed.')

        for i in np.arange(nimg):
            aperture_sum.append(np.sum(datamodel.data[i, :, :]))
            aperture_sum_err.append(
                np.sqrt(np.sum(datamodel.err[i, :, :]**2)))
    else:
        info = ('Photometry measured in a circular aperture of r={0} '
                'pixels.  Background calculated as the mean in a '
                'circular annulus with r_inner={1} pixels and '
                'r_outer={2} pixels.'.format(radius, radius_inner,
                                                radius_outer))
        for i in np.arange(nimg):
            aper_sum, aper_sum_err = phot_aper.do_photometry(
                datamodel.data[i, :, :], error=datamodel.err[i, :, :])
            ann_sum, ann_sum_err = bkg_aper.do_photometry(
                datamodel.data[i, :, :], error=datamodel.err[i, :, :])

            aperture_sum.append(aper_sum[0])
            aperture_sum_err.append(aper_sum_err[0])
            annulus_sum.append(ann_sum[0])
            annulus_sum_err.append(ann_sum_err[0])

    aperture_sum = np.array(aperture_sum)
    aperture_sum_err = np.array(aperture_sum_err)
    annulus_sum = np.array(annulus_sum)
    annulus_sum_err = np.array(annulus_sum_err)

    # construct metadata for output table
    meta = OrderedDict()
    meta['instrument'] = datamodel.meta.instrument.name
    meta['detector'] = datamodel.meta.instrument.detector
    meta['channel'] = datamodel.meta.instrument.channel
    meta['subarray'] = datamodel.meta.subarray.name
    meta['filter'] = datamodel.meta.instrument.filter
    meta['pupil'] = datamodel.meta.instrument.pupil
    meta['target_name'] = datamodel.meta.target.catalog_name
    meta['xcenter'] = xcenter
    meta['ycenter'] = ycenter
    ra_icrs, dec_icrs = datamodel.meta.wcs(xcenter, ycenter)
    meta['ra_icrs'] = ra_icrs
    meta['dec_icrs'] = dec_icrs
    meta['apertures'] = info

    # initialize the output table
    tbl = QTable(meta=meta)

    if hasattr(datamodel, 'int_times') and datamodel.int_times is not None:
        nrows = len(datamodel.int_times)
    else:
        nrows = 0
    if nrows == 0:
        log.warning("There is no INT_TIMES table in the input file.")

    if nrows > 0:
        shape = datamodel.data.shape
        if len(shape) == 2:
            num_integ = 1
        else:                                   # len(shape) == 3
            num_integ = shape[0]
        int_start = datamodel.meta.exposure.integration_start
        if int_start is None:
            int_start = 1
            log.warning("INTSTART not found; assuming a value of %d",
                        int_start)

        # Columns of integration numbers & times of integration from the
        # INT_TIMES table.
        int_num = datamodel.int_times['integration_number']
        mid_utc = datamodel.int_times['int_mid_MJD_UTC']
        offset = int_start - int_num[0]                 # both are one-indexed
        if offset < 0:
            log.warning("Range of integration numbers in science data extends "
                        "outside the range in INT_TIMES table.")
            log.warning("Can't use INT_TIMES table.")
            del int_num, mid_utc
            nrows = 0                   # flag as bad
        else:
            log.debug("Times are from the INT_TIMES table.")
            time_arr = mid_utc[offset: offset + num_integ]
            int_times = Time(time_arr, format='mjd', scale='utc')
    else:
        log.debug("Times were computed from EXPSTART and TGROUP.")

        dt = (datamodel.meta.exposure.group_time *
              (datamodel.meta.exposure.ngroups + 1))
        dt_arr = (np.arange(1, 1 + datamodel.meta.exposure.nints) *
                  dt - (dt / 2.))
        int_dt = TimeDelta(dt_arr, format='sec')
        int_times = (Time(datamodel.meta.exposure.start_time, format='mjd') +
                     int_dt)

    tbl['MJD'] = int_times.mjd

    tbl['aperture_sum'] = aperture_sum
    tbl['aperture_sum_err'] = aperture_sum_err

    if not sub64p_wlp8:
        tbl['annulus_sum'] = annulus_sum
        tbl['annulus_sum_err'] = annulus_sum_err

        annulus_mean = annulus_sum / bkg_aper.area()
        annulus_mean_err = annulus_sum_err / bkg_aper.area()
        tbl['annulus_mean'] = annulus_mean
        tbl['annulus_mean_err'] = annulus_mean_err

        aperture_bkg = annulus_mean * phot_aper.area()
        aperture_bkg_err = annulus_mean_err * phot_aper.area()
        tbl['aperture_bkg'] = aperture_bkg
        tbl['aperture_bkg_err'] = aperture_bkg_err

        net_aperture_sum = aperture_sum - aperture_bkg
        net_aperture_sum_err = np.sqrt(aperture_sum_err ** 2 +
                                       aperture_bkg_err ** 2)
        tbl['net_aperture_sum'] = net_aperture_sum
        tbl['net_aperture_sum_err'] = net_aperture_sum_err
    else:
        colnames = ['annulus_sum', 'annulus_sum_err', 'annulus_mean',
                    'annulus_mean_err', 'aperture_bkg', 'aperture_bkg_err']
        for col in colnames:
            tbl[col] = np.full(nimg, np.nan)

        tbl['net_aperture_sum'] = aperture_sum
        tbl['net_aperture_sum_err'] = aperture_sum_err

    return tbl
Ejemplo n.º 5
0
import photometry

cx, cy = (np.int_(np.mean(xcc)), np.int_(np.mean(ycc)))
sx, sy = (np.int_(np.mean(xcs)), np.int_(np.mean(ycs)))
radius = 5
r_in, r_out = 11, 14

# Companion:
positions = (cx - 1, cy - 1)
ap = CircularAperture(positions, r=radius)
skyan = CircularAnnulus(positions, r_in=11, r_out=14)
ap = CircularAperture(positions, r=radius)
skyan = CircularAnnulus(positions, r_in=11, r_out=14)
apsum = ap.do_photometry(image)[0]
skysum = skyan.do_photometry(image)[0]
averagesky = skysum / skyan.area()
signal = (apsum - ap.area() * averagesky)[0]
n = ap.area()
box = image[cy + 12:cy + 12 + 15, cx + 12:cx + 12 + 15]
noise = np.std(box)
noise = noise * np.sqrt(n)
compsnr = signal / noise

# Star:
positions = (sx - 1, sy - 1)
ap = CircularAperture(positions, r=radius)
skyan = CircularAnnulus(positions, r_in=11, r_out=14)
ap = CircularAperture(positions, r=radius)
skyan = CircularAnnulus(positions, r_in=11, r_out=14)
apsum = ap.do_photometry(image)[0]
Ejemplo n.º 6
0
def smoothness(image: np.ndarray, mask: np.ndarray, centroid: List[float],
               Rmax: float, r20: float, sky: float) -> float:
    r'''Calculate the smoothness or clumpiness of the galaxy of interest.

    .. math:: S = \frac{\sum \left|I - I_s\right| - B_s} {\sum \left|I\right|}

    Where I is the image
    :math:`I_s` is the smoothed image
    :math:`B_s` is the background smoothness

    see Lotz et al. 2004 https://doi.org/10.1086/421849


    Parameters
    ----------

    image : float, 2d np.ndarray
        Image of galaxy

    mask : float [0. - 1.], 2d np.ndarray
        Mask which contains the pixels belonging to the galaxy of interest.

    centroid : List[float]
        Pixel location of the brightest pixel in galaxy.

    Rmax : float
        Distance from brightest pixel to furthest pixel in galaxy

    r20 : float
        Distance from brightest pixel to radius at which 20% of light of galaxy
        is enclosed.

    sky : float
        Value of the sky background.

    Returns
    -------

    Result: float
        The smoothness or clumpiness parameter, S.

    '''

    r_in = r20
    r_out = Rmax

    # Exclude inner 20% of light. See Concelice 2003
    imageApeture = CircularAnnulus(centroid, r_in, r_out)

    # smooth image
    imageSmooth = ndi.uniform_filter(image, size=int(r20))

    # calculate residual, setting any negative pixels to 0.
    imageDiff = image - imageSmooth
    imageDiff[imageDiff < 0.] = 0.

    # calculate S, accounting for the background smoothness.
    imageFlux = imageApeture.do_photometry(image, method="exact")[0][0]
    diffFlux = imageApeture.do_photometry(imageDiff, method="exact")[0][0]
    backgroundSmooth = _getBackgroundSmoothness(image, mask, sky, r20)
    S = (diffFlux - imageApeture.area * backgroundSmooth) / imageFlux

    return S