Example #1
0
    def __init__(self, *args, **kwargs):
        self.verts = None
        self.weights = None

        if len(args) == 0:
            return None

        assert len(
            args) == 6, "args = im, im_header, locx, locy, ap_type, npix"

        im, im_header, locx, locy, ap_type, npix = args

        if ap_type == 'circular':
            radius = np.sqrt(npix / np.pi)
            _ap = circular_aperture(im, locx, locy, radius)
        elif ap_type == 'region':
            radius = np.sqrt(npix / np.pi)
            _ap = region_aperture(im, locx, locy, npix)
        else:
            assert False, "ap_type must be circular or aperture"

        verts = _ap.verts
        verts = pd.DataFrame(verts, columns=['x', 'y'])

        # Convert x, y to sky coordinates
        wcs = astropy.wcs.find_all_wcs(im_header, keysel=['binary'])[0]
        verts['ra'], verts['dec'] = wcs.all_pix2world(verts.x, verts.y, 0)

        self.verts = verts
        self.weights = _ap.weights
        self.npix = npix
        self.ap_type = ap_type
        self.name = "{}-{:.1f}".format(ap_type, npix)
Example #2
0
 def get_solid_angle(hdu):
     wcs = astropy.wcs.WCS(hdu.header)
     nx = hdu.header['NAXIS1']
     ny = hdu.header['NAXIS2']
     xx = numpy.arange(-0.5, nx, 1)
     xy = numpy.arange(0, ny, 1)
     yx = numpy.arange(0, nx, 1)
     yy = numpy.arange(-0.5, ny, 1)
     xX, xY = numpy.meshgrid(xx, xy)
     yX, yY = numpy.meshgrid(yx, yy)
     xXw, xYw = wcs.all_pix2world(xX, xY, 0)
     yXw, yYw = wcs.all_pix2world(yX, yY, 0)
     dXw = xXw[:, 1:] - xXw[:, :-1]
     dYw = yYw[1:] - yYw[:-1]
     A = abs(dXw * dYw) * deg * deg
     return A
Example #3
0
 def get_solid_angle(hdu):
     wcs = astropy.wcs.WCS(hdu.header)
     nx = hdu.header['NAXIS1']
     ny = hdu.header['NAXIS2']  
     xx = numpy.arange(-0.5, nx, 1)
     xy = numpy.arange(0, ny, 1)
     yx = numpy.arange(0, nx, 1)
     yy = numpy.arange(-0.5, ny, 1)
     xX, xY = numpy.meshgrid(xx, xy)
     yX, yY = numpy.meshgrid(yx, yy)
     xXw, xYw = wcs.all_pix2world(xX, xY, 0)
     yXw, yYw = wcs.all_pix2world(yX, yY, 0)
     dXw = xXw[:,1:] - xXw[:,:-1]
     dYw = yYw[1:] - yYw[:-1]
     A = abs(dXw * dYw) * deg * deg
     return A
Example #4
0
    def pix2world(self, x, y):
        """ Transform pixel coordinates to world coordinates.

        Return a two-element tuple with the right ascension and declination to
        which the specified x- and y-coordinates correspond in the FITS image.
        Raises NoWCSInformationError if the header of the FITS image does not
        contain an astrometric solution -- i.e., if the astropy.wcs.WCS class
        is unable to recognize it as such. This is something that should very
        rarely happen, and almost positively caused by non-standard systems or
        FITS keywords.

        """

        coords = (x, y)
        wcs = self._get_wcs()
        pixcrd = numpy.array([coords])
        ra, dec = wcs.all_pix2world(pixcrd, 1)[0]

        # We could use astropy.wcs.WCS.has_celestial for this, but as of today
        # [Tue Jan 20 2015] it is only available in the development version of
        # Astropy. Therefore, do a simple (but in theory enough) check: if the
        # header does not contain an astrometric solution, WCS.all_pix2world()
        # will not be able to transform the pixel coordinates, and therefore
        # will return the same coordinates as the FITSImage.center attribute.

        if (ra, dec) == coords:
            msg = ("{0}: the header of the FITS image does not seem to "
                   "contain WCS information. You may want to make sure that "
                   "the image has been solved astrometrically, for example "
                   "with the 'astrometry' LEMON command.".format(self.path))
            raise NoWCSInformationError(msg)

        return ra, dec
Example #5
0
    def pix2world(self, x, y):
        """ Transform pixel coordinates to world coordinates.

        Return a two-element tuple with the right ascension and declination to
        which the specified x- and y-coordinates correspond in the FITS image.
        Raises NoWCSInformationError if the header of the FITS image does not
        contain an astrometric solution -- i.e., if the astropy.wcs.WCS class
        is unable to recognize it as such. This is something that should very
        rarely happen, and almost positively caused by non-standard systems or
        FITS keywords.

        """

        coords = (x, y)
        wcs = self._get_wcs()
        pixcrd = numpy.array([coords])
        ra, dec = wcs.all_pix2world(pixcrd, 1)[0]

        # We could use astropy.wcs.WCS.has_celestial for this, but as of today
        # [Tue Jan 20 2015] it is only available in the development version of
        # Astropy. Therefore, do a simple (but in theory enough) check: if the
        # header does not contain an astrometric solution, WCS.all_pix2world()
        # will not be able to transform the pixel coordinates, and therefore
        # will return the same coordinates as the FITSImage.center attribute.

        if (ra, dec) == coords:
            msg = ("{0}: the header of the FITS image does not seem to "
                   "contain WCS information. You may want to make sure that "
                   "the image has been solved astrometrically, for example "
                   "with the 'astrometry' LEMON command.".format(self.path))
            raise NoWCSInformationError(msg)

        return ra, dec
Example #6
0
    def __init__(self, fname, fill_value=0.0):
        with fits.open(fname) as hdul:
            rawdata = hdul[0].data
            wcs = astropy.wcs.WCS(hdul[0].header)
            # WL is in Angstroms -> to microns
            all_wl = wcs.all_pix2world(range(hdul[0].data.size), 0)

        self._interp = ii.interp1d(all_wl[0] / 1e4, numpy.abs(rawdata),
                                   bounds_error=False,
                                   fill_value=fill_value)
Example #7
0
def pc_gaia_cat(exp, mag_thresh=None, edge_pad_pix=0, nmp=None,
                max_n_stars=3000, pm_corr=False):

    print('Reading Gaia DR2 catalogs...')

    xgrid, ygrid = xy_subsamp_grid()

    wcs = exp.wcs

    # last arg is 0 rather than 1 because that's what agrees with IDL
    ra, dec = wcs.all_pix2world(xgrid, ygrid, 0)

    cat = read_gaia_cat(ra, dec, nmp=nmp)

    if mag_thresh is not None:
        keep = (cat['PHOT_G_MEAN_MAG'] <= mag_thresh)
        assert(np.sum(keep) > 0)
        cat = cat[keep]

    if pm_corr:
        gaia_pm_corr(cat, exp.header['MJD-OBS'])

    x_gaia_guess, y_gaia_guess = wcs.all_world2pix(cat['RA'], cat['DEC'], 0)

    par = common.pc_params()

    keep  = (x_gaia_guess > edge_pad_pix) & (y_gaia_guess > edge_pad_pix) & (x_gaia_guess < (par['nx'] - 1 - edge_pad_pix)) & (y_gaia_guess < (par['ny'] - 1 - edge_pad_pix))

    assert(np.sum(keep) > 0)

    cat = cat[keep]

    x_gaia_guess = x_gaia_guess[keep]
    y_gaia_guess = y_gaia_guess[keep]

    cat = Table(cat)
    cat['x_gaia_guess'] = x_gaia_guess
    cat['y_gaia_guess'] = y_gaia_guess

    if len(cat) > max_n_stars:
        # retain brightest max_n_stars
        # it'd be better to do this cut based on
        # 'G_PRIME', the color-corrected pointing
        # camera Gaia-based mag
        # in the future can evaluate trying to spread the
        # selected max_n_stars evenly across quadrants
        # (could imagine a pathological case with e.g., a
        # globular cluster in the FOV)
        print('Restricting to the brightest ' + str(max_n_stars) + \
              ' of ' + str(len(cat)) + ' stars')
        sind = np.argsort(cat['PHOT_G_MEAN_MAG'])
        cat = cat[sind[0:max_n_stars]]

    return cat
def indices_to_velocity(
        indices: np.ndarray,
        base_wavelength: u.Quantity,
        wcs: astropy.wcs.WCS,
        wscale: int
):
    wavelengths, _, _ = wcs.all_pix2world(wscale * indices, 0, 0, 0)
    wavelengths = wavelengths << u.AA
    velocities = astropy.constants.c * (wavelengths - base_wavelength) / base_wavelength
    velocities = velocities.to(u.km / u.s)

    return velocities
Example #9
0
def corner_catalog_1ext(telra, teldec, extname):
    # LL -> UL -> UR -> LR
    xpix, ypix = util.ci_corner_pixel_coords()

    wcs = ci_wcs.nominal_tan_wcs(telra, teldec, extname)

    ra, dec = wcs.all_pix2world(xpix, ypix, 0)

    name = [(extname + '_' + corner) for corner in ['LL', 'UL', 'UR', 'LR']]

    tab = Table([ra, dec, name], names=('ra', 'dec', 'name'))

    return tab
Example #10
0
    def test_qphot_run_proper_motions(self):

        # Do photometry on Barnard's Star, the star with the largest-known
        # proper motion relative to the Solar System. Its coordinates are
        # J2000, but the DSS image dates back from 1993: qphot.run() must
        # correct the right ascension and declination before doing photometry,
        # adjusting for the change in its position over those seven years.

        barnard_path = "./test/test_data/fits/Barnard's_Star.fits"
        barnard = astromatic.Coordinates(269.452075, 4.693391, -0.79858,
                                         10.32812)

        nbits = get_nbits()
        if nbits == 64:
            expected_output = (  #  x        y       mag     sum     flux      stdev
                qphot.QPhotResult(440.947, 382.595, 17.245, 8563646, 5311413,
                                  1039.844))
        else:
            assert nbits == 32
            expected_output = (  # <<>>
                qphot.QPhotResult(440.947, 382.595, 17.245, 8563646, 5312029,
                                  1039.844))

        path = fix_DSS_image(barnard_path)
        with test.test_fitsimage.FITSImage(path) as img:

            # Fix incorrect date in FITS header: '1993-07-26T04:87:00'.
            # Subtract sixty minutes and add one hour: 4h87m == 5h27m
            keyword = 'DATE-OBS'
            assert img.read_keyword(keyword) == '1993-07-26T04:87:00'
            img.update_keyword(keyword, '1993-07-26T05:27:00')

            # The proper-motion corrected coordinates
            year = img.year(exp_keyword='EXPOSURE')
            expected_coordinates = barnard.get_exact_coordinates(year)

            result = qphot.run(img, [barnard], **self.QPHOT_KWARGS)[0]
            self.assertEqual(result, expected_output)

            # Transform the pixel coordinates that IRAF's qphot outputs for
            # each measured object to celestial coordinates. This allows us to
            # make sure that photometry has been effectively done on the right,
            # proper-motion corrected coordinates.

            wcs = astropy.wcs.WCS(img._header)
            ra, dec = wcs.all_pix2world(result.x, result.y, 1)
            f = self.assertAlmostEqual
            f(ra, expected_coordinates.ra, delta=1e-3)  # delta = 0.24 arcsec
            f(dec, expected_coordinates.dec, delta=1e-3)  # delta = 3.6 arcsec
Example #11
0
    def test_qphot_run_proper_motions(self):

        # Do photometry on Barnard's Star, the star with the largest-known
        # proper motion relative to the Solar System. Its coordinates are
        # J2000, but the DSS image dates back from 1993: qphot.run() must
        # correct the right ascension and declination before doing photometry,
        # adjusting for the change in its position over those seven years.

        barnard_path = "./test/test_data/fits/Barnard's_Star.fits"
        barnard = astromatic.Coordinates(269.452075, 4.693391, -0.79858, 10.32812)

        nbits = methods.get_nbits()
        if nbits == 64:
            expected_output = (   #  x        y       mag     sum     flux      stdev
                qphot.QPhotResult(440.947, 382.595, 17.245, 8563646, 5311413, 1039.844))
        else:
            assert nbits == 32
            expected_output = (                                      # <<>>
                qphot.QPhotResult(440.947, 382.595, 17.245, 8563646, 5312029, 1039.844))

        path = fix_DSS_image(barnard_path)
        with test.test_fitsimage.FITSImage(path) as img:

            # Fix incorrect date in FITS header: '1993-07-26T04:87:00'.
            # Subtract sixty minutes and add one hour: 4h87m == 5h27m
            keyword = 'DATE-OBS'
            assert img.read_keyword(keyword) == '1993-07-26T04:87:00'
            img.update_keyword(keyword, '1993-07-26T05:27:00')

            # The proper-motion corrected coordinates
            year = img.year(exp_keyword = 'EXPOSURE')
            expected_coordinates = barnard.get_exact_coordinates(year)

            result = qphot.run(img, [barnard], **self.QPHOT_KWARGS)[0]
            self.assertEqual(result, expected_output)

            # Transform the pixel coordinates that IRAF's qphot outputs for
            # each measured object to celestial coordinates. This allows us to
            # make sure that photometry has been effectively done on the right,
            # proper-motion corrected coordinates.

            wcs = astropy.wcs.WCS(img._header)
            ra, dec = wcs.all_pix2world(result.x, result.y, 1)
            f = self.assertAlmostEqual
            f(ra,  expected_coordinates.ra,  delta = 1e-3) # delta = 0.24 arcsec
            f(dec, expected_coordinates.dec, delta = 1e-3) # delta = 3.6 arcsec
Example #12
0
def pix2world(pix, header, axis):
    wcs = astropy.wcs.WCS(header)
    cunit = astropy.units.Unit(header.get('CUNIT%d' % (axis)))

    pix = numpy.array(pix)
    if pix.ndim == 0:
        plen = 1
    else:
        plen = len(pix)
        pass

    p = numpy.zeros([plen, wcs.naxis])
    p[:, axis - 1] = pix
    world = wcs.all_pix2world(p, 0)[:, axis - 1] * cunit

    if pix.ndim == 0:
        return world[0]

    return world
Example #13
0
def convertImgCoords(coords, image, to_pix=None, to_radec=None):
    ''' Transform the WCS info in an image

    Convert image pixel coordinates to RA/Dec based on
    PNG image metadata or vice_versa

    Parameters:
        coords (tuple):
            The input coordindates to transform
        image (str):
            The full path to the image
        to_pix (bool):
            Set to convert to pixel coordinates
        to_radec (bool):
            Set to convert to RA/Dec coordinates

    Returns:
        newcoords (tuple):
            Tuple of either (x, y) pixel coordinates
            or (RA, Dec) coordinates

    '''

    try:
        wcs = getWCSFromPng(image)
    except Exception as e:
        raise MarvinError('Cannot get wcs info from image {0}: {1}'.format(
            image, e))

    if to_radec:
        try:
            newcoords = wcs.all_pix2world([coords], 1)[0]
        except AttributeError as e:
            raise MarvinError(
                'Cannot convert coords to RA/Dec.  No wcs! {0}'.format(e))
    if to_pix:
        try:
            newcoords = wcs.all_world2pix([coords], 1)[0]
        except AttributeError as e:
            raise MarvinError(
                'Cannot convert coords to image pixels.  No wcs! {0}'.format(
                    e))
    return newcoords
Example #14
0
def pix2world(pix, header, axis):
    wcs = astropy.wcs.WCS(header)
    cunit = astropy.units.Unit(header.get('CUNIT%d'%(axis)))
    
    pix = numpy.array(pix)
    if pix.ndim == 0:
        plen = 1
    else:
        plen = len(pix)
        pass
    
    p = numpy.zeros([plen, wcs.naxis])
    p[:,axis-1] = pix
    world = wcs.all_pix2world(p, 0)[:,axis-1] * cunit
    
    if pix.ndim == 0:
        return world[0]

    return world
Example #15
0
    def center_wcs(self):
        """ Return the world coordinates of the central pixel of the image.

        Transform the pixel coordinates of the center of the image to world
        coordinates. Return a two-element tuple with the right ascension and
        declination. Most of the time the celestial coordinates of the field
        center can be found in the FITS header, but (a) these keywords are
        non-standard and (b) it would not be the first time that we come
        across incorrect (or, at least, not as accurate as we would expect)
        coordinates. Instead of blindly trusting the FITS header, compute these
        values ourselves -- provided, of course, that our images are calibrated
        astrometrically.

        Raises NoWCSInformationError if the header of the FITS image does not
        contain an astrometric solution -- i.e., if the astropy.wcs.WCS class
        is unable to recognize it as such. This is something that should very
        rarely happen, and almost positively caused by non-standard systems or
        FITS keywords.

        """

        center = tuple(self.center)
        wcs = self._get_wcs()
        pixcrd = numpy.array([center])
        ra, dec = wcs.all_pix2world(pixcrd, 1)[0]

        # We could use astropy.wcs.WCS.has_celestial for this, but as of today
        # [Tue Jan 20 2015] it is only available in the development version of
        # Astropy. Therefore, do a simple (but in theory enough) check: if the
        # header does not contain an astrometric solution, WCS.all_pix2world()
        # will not be able to transform the pixel coordinates, and therefore
        # will return the same coordinates as the FITSImage.center attribute.

        if (ra, dec) == center:
            msg = ("{0}: the header of the FITS image does not seem to "
                   "contain WCS information. You may want to make sure that "
                   "the image has been solved astrometrically, for example "
                   "with the 'astrometry' LEMON command.".format(self.path))
            raise NoWCSInformationError(msg)

        return ra, dec
Example #16
0
def get_coordinate_grid_gal(hdu, frame='fk5'):
    """Returns world coordinate grid of an image
        [Image has to be in J2000 at the moment]
        Gives both RA/DEC and GALACTIC
        Input:
            hdu = Input fits hdu in J2000 coordinates
            frame = Input reference frame; default is fk5
        Output:
            (coordinate_grid_l, coordinate_grid_b) = coordinates of each array index in GALACTIC
            (coordinate_grid_ra, coordinate_grid_dec) = coordinates of each array index in RA/DEC
        """

    wcs = astropy.wcs.WCS(hdu)
    shape = hdu.shape

    index_grid_x = np.empty(shape)
    index_grid_y = np.empty(shape)
    coordinate_grid_ra = np.empty(shape)
    coordinate_grid_dec = np.empty(shape)
    coordinate_grid_l = np.empty(shape)
    coordinate_grid_b = np.empty(shape)

    for row in range(shape[1]):
        for col in range(shape[0]):

            index_grid_x[col, row] = row
            index_grid_y[col, row] = col

    coordinate_grid_radec = wcs.all_pix2world(index_grid_x, index_grid_y, 0)
    coordinate_grid_ra, coordinate_grid_dec = coordinate_grid_radec

    lb = coordinates.SkyCoord(coordinate_grid_ra,
                              coordinate_grid_dec,
                              frame=frame,
                              unit=(au.deg, au.deg)).galactic

    coordinate_grid_l = lb.l.deg
    coordinate_grid_b = lb.b.deg

    return (coordinate_grid_l, coordinate_grid_b), (coordinate_grid_ra,
                                                    coordinate_grid_dec)
Example #17
0
def update_flux_limits(header, pixlims, wcs=None, ref=1):
    """Update keywords used for flux limits"""

    pixlim_coms = ["Start of valid flux calibration",
                   "End of valid flux calibration",
                   'Start of region with at least one fiber',
                   'End of region with at least one fiber',
                   'Start of region with all fibers',
                   'End of region with all fibers',
                   ]

    if ref not in [0, 1]:
        raise ValueError("ref must be 0 or 1")

    off = (ref + 1) % 2

    if wcs is None:
        wcs = astropy.wcs.WCS(header)

    for key, com in zip(PIXLIM_KEYS, pixlim_coms):
        header[key] = (pixlims[key] + off, com)

    r1 = numpy.array([pixlims[key] for key in PIXLIM_KEYS])
    r2 = numpy.zeros_like(r1)
    lm = numpy.array([r1, r2])
    # Values are 0-based
    wavelen_ = wcs.all_pix2world(lm.T, ref)
    if wcs.wcs.cunit[0] == u.dimensionless_unscaled:
        # CUNIT is empty, assume Angstroms
        wavelen = wavelen_[:, 0] * u.AA
    else:
        wavelen = wavelen_[:, 0] * wcs.wcs.cunit[0]

    for idx, (key, com) in enumerate(zip(WAVLIM_KEYS, pixlim_coms)):
        header[key] = (wavelen[idx].to(u.AA).value, com)

    return header
Example #18
0
 def xy2rd(self, wcs, pixx, pixy):
     """ Transform input pixel positions into sky positions in the WCS provided.
     """
     return wcs.all_pix2world(pixx, pixy, 1)
Example #19
0
def calcNewEdges(wcs, shape):
    """
    This method will compute sky coordinates for all the pixels around
    the edge of an image AFTER applying the geometry model.

    Parameters
    ----------
    wcs : obj
        HSTWCS object for image

    shape : tuple
        numpy shape tuple for size of image

    Returns
    -------
    border : arr
        array which contains the new positions for
        all pixels around the border of the edges in alpha,dec

    """

    naxis1 = shape[1]
    naxis2 = shape[0]
    # build up arrays for pixel positions for the edges
    # These arrays need to be: array([(x,y),(x1,y1),...])
    numpix = naxis1*2 + naxis2*2
    border = np.zeros(shape=(numpix,2),dtype=np.float64)

    # Now determine the appropriate values for this array
    # We also need to account for any subarray offsets
    xmin = 1.
    xmax = naxis1
    ymin = 1.
    ymax = naxis2

    # Build range of pixel values for each side
    # Add 1 to make them consistent with pixel numbering in IRAF
    # Also include the LTV offsets to represent position in full chip
    #   since the model works relative to full chip positions.
    xside = np.arange(naxis1) + xmin
    yside = np.arange(naxis2) + ymin

    #Now apply them to the array to generate the appropriate tuples
    #bottom
    _range0 = 0
    _range1 = naxis1
    border[_range0:_range1,0] = xside
    border[_range0:_range1,1] = ymin
    #top
    _range0 = _range1
    _range1 = _range0 + naxis1
    border[_range0:_range1,0] = xside
    border[_range0:_range1,1] = ymax
    #left
    _range0 = _range1
    _range1 = _range0 + naxis2
    border[_range0:_range1,0] = xmin
    border[_range0:_range1,1] = yside
    #right
    _range0 = _range1
    _range1 = _range0 + naxis2
    border[_range0:_range1,0] = xmax
    border[_range0:_range1,1] = yside

    edges = wcs.all_pix2world(border[:,0],border[:,1],1)
    return edges
Example #20
0
def make_radio_combination_signature(radio_annotation, wcs, atlas_positions,
                                     subject, pix_offset):
    """Generates a unique signature for a radio annotation.

    radio_annotation: 'radio' dictionary from a classification.
    wcs: World coordinate system associated with the ATLAS image.
    atlas_positions: [[RA, DEC]] NumPy array.
    subject: RGZ subject dict.
    pix_offset: (x, y) pixel position of this radio subject on the ATLAS image.
    -> Something immutable
    """
    from . import rgz_data as data
    # TODO(MatthewJA): This only works on ATLAS. Generalise.
    # My choice of immutable object will be stringified crowdastro ATLAS
    # indices.
    zooniverse_id = subject['zooniverse_id']
    subject_fits = data.get_radio_fits(subject)
    subject_wcs = astropy.wcs.WCS(subject_fits.header)

    atlas_ids = []
    x_offset, y_offset = pix_offset
    for c in radio_annotation.values():
        # Note that the x scale is not the same as the IR scale, but the scale
        # factor is included in the annotation, so I have multiplied this out
        # here for consistency.
        scale_width = c.get('scale_width', '')
        scale_height = c.get('scale_height', '')
        if scale_width:
            scale_width = float(scale_width)
        else:
            # Sometimes, there's no scale, so I've included a default scale.
            scale_width = config['surveys']['atlas']['scale_width']

        if scale_height:
            scale_height = float(scale_height)
        else:
            scale_height = config['surveys']['atlas']['scale_height']

        # These numbers are in terms of the PNG images, so I need to multiply by
        # the click-to-fits ratio.
        scale_width *= config['surveys']['atlas']['click_to_fits_x']
        scale_height *= config['surveys']['atlas']['click_to_fits_y']

        subject_bbox = [
            [
                float(c['xmin']) * scale_width,
                float(c['xmax']) * scale_width,
            ],
            [
                float(c['ymin']) * scale_height,
                float(c['ymax']) * scale_height,
            ],
        ]

        # ...and by the mosaic ratio. There's probably double-up here, but this
        # makes more sense.
        scale_width *= config['surveys']['atlas']['mosaic_scale_x']
        scale_height *= config['surveys']['atlas']['mosaic_scale_y']
        # Get the bounding box of the radio source in pixels.
        # Format: [xs, ys]
        bbox = [
            [
                float(c['xmin']) * scale_width,
                float(c['xmax']) * scale_width,
            ],
            [
                float(c['ymin']) * scale_height,
                float(c['ymax']) * scale_height,
            ],
        ]
        assert bbox[0][0] < bbox[0][1]
        assert bbox[1][0] < bbox[1][1]

        # Convert the bounding box into RA/DEC.
        bbox = wcs.all_pix2world(bbox[0] + x_offset, bbox[1] + y_offset,
                                 FITS_CONVENTION)
        subject_bbox = subject_wcs.all_pix2world(subject_bbox[0],
                subject_bbox[1], FITS_CONVENTION)
        # TODO(MatthewJA): Remove (or disable) this sanity check.

        # The bbox is backwards along the x-axis for some reason.
        bbox[0] = bbox[0][::-1]
        assert bbox[0][0] < bbox[0][1]
        assert bbox[1][0] < bbox[1][1]

        bbox = numpy.array(bbox)

        # What is this radio source called? Check if we have an object in the
        # bounding box. We'll cache these results because there is a lot of
        # overlap.
        cache_key = tuple(tuple(b) for b in bbox)
        if cache_key in bbox_cache_:
            index = bbox_cache_[cache_key]
        else:
            x_gt_min = atlas_positions[:, 0] >= bbox[0, 0]
            x_lt_max = atlas_positions[:, 0] <= bbox[0, 1]
            y_gt_min = atlas_positions[:, 1] >= bbox[1, 0]
            y_lt_max = atlas_positions[:, 1] <= bbox[1, 1]
            within = numpy.all([x_gt_min, x_lt_max, y_gt_min, y_lt_max], axis=0)
            indices = numpy.where(within)[0]

            if len(indices) == 0:
                logging.debug('Skipping radio source not in catalogue for '
                              '%s', zooniverse_id)
                continue
            else:
                if len(indices) > 1:
                    logging.debug('Found multiple (%d) ATLAS matches '
                                  'for %s', len(indices), zooniverse_id)

                index = indices[0]

            bbox_cache_[cache_key] = index

        atlas_ids.append(str(index))

    atlas_ids.sort()

    if not atlas_ids:
        raise CatalogueError('No catalogued radio sources.')

    return ';'.join(atlas_ids)
Example #21
0
        (check_sources, check_dead, check_sky,
         check_source_sky) = check_hdulists
        pyfits.HDUList(check_sources).writeto("check_sources.fits",
                                              overwrite=True)
        pyfits.HDUList(check_dead).writeto("check_dead.fits", overwrite=True)
        pyfits.HDUList(check_sky).writeto("check_sky.fits", overwrite=True)
        pyfits.HDUList(check_source_sky).writeto("check_source_sky.fits",
                                                 overwrite=True)

        # src_data.info()
        # convert polygon center coordinates from native pixels to Ra/Dec
        #src_data.info()
        #print(src_data['center_x'].astype(numpy.float).to_numpy())
        _ra, _dec = wcs.all_pix2world(
            src_data['center_x'].astype(numpy.float).to_numpy(),
            src_data['center_y'].astype(numpy.float).to_numpy(), 1)
        src_data['center_ra'] = _ra
        src_data['center_dec'] = _dec
        #print(radec)

        # apply flux calibrations
        calib_factor = 1.0
        if (name in calibration_factors):
            calib_factor = calibration_factors[name]
            named_logger.info("Apply calibration factor: %g" % (calib_factor))

        sky_error = (src_data['src_area'] * src_data['sky_var']).astype(
            numpy.float).to_numpy()
        src_error = gain * src_data['src_flux'].astype(numpy.float).to_numpy()
        flx_error = numpy.fabs(src_error) + sky_error * gain**2
Example #22
0
def detect_sources(image, cat_name=None):
    """ A generator of (ra, dec) tuples (PROOF OF CONCEPT) """

    log.debug("Reading FITS file")
    with astropy.io.fits.open(image) as hdulist:
        data = hdulist[0].data
        header = hdulist[0].header

    with warnings.catch_warnings():
        warnings.simplefilter("ignore")
        log.info("Loading WCS data")
        wcs = astropy.wcs.WCS(header)

    try:
        log.info("Estimating background")
        background = sextractor.Background(data)
    except ValueError:
        # Fix for error "Input array with dtype '>f4' has non-native
        # byte order. Only native byte order arrays are supported".
        log.debug("Converting data to native byte order")
        data = data.byteswap(True).newbyteorder()
        background = sextractor.Background(data)

    log.info("Subtracting background")
    background.subfrom(data) # in-place


    # Global "average" RMS of background
    log.info("Detecting sources")
    rms_ntimes = 1.5
    while True:
        try:
            threshold = rms_ntimes * background.globalrms
            objects = sextractor.extract(data, threshold)

        except Exception as e:

            # Fix for error "internal pixel buffer full: The limit of 300000
            # active object pixels over the detection threshold was reached.
            # Check that the image is background subtracted and the detection
            # threshold is not too low. detection threshold". If a different
            # exception is raised, just re-raise it.

            if "threshold is not too low" in str(e):
                rms_ntimes *= 1.25
                log.debug("Internal pixel buffer full")
                log.debug("Retrying with threshold {0:.2} times RMS of background".format(rms_ntimes))
            else:
                raise
        else:
            break

    pixel_coords = numpy.empty([0,2])
    print "len(objects) = ", len(objects)
    print "Image = ", image
    sys.exit()

    # https://github.com/kbarbary/sep/blob/master/sep.pyx#L555
    log.info("Transforming pixel to celestial coordinates")
    for index in range(len(objects)):
        x, y = objects[index]['x'], objects[index]['y']
        pixel_coords = numpy.row_stack(pixel_coords, (x, y))

    print pixel_coords.shape

    # If cat_name is present, save the coordinates to a file
    if cat_name:
        fd = open(cat_name, 'w')
        for ra, dec in list(wcs.all_pix2world(pixel_coords[:,0], pixel_coords[:,1], 0)):
            fd.write( "{0}  {1} \n ".format(ra, dec) )
        fd.close()

    return list(wcs.all_pix2world(pixel_coords[:,0], pixel_coords[:,1], 0))
Example #23
0
                           basedir,
                           uncompressed=args.uncompressed)
    else:
        print('No bright star catalog, not marking bright stars.')

    res = process(im,
                  sqivar,
                  flag,
                  psf,
                  refit_psf=args.refit_psf,
                  verbose=args.verbose,
                  nx=4,
                  ny=4,
                  derivcentroids=True)
    outfn = args.outfn[0]

    x = (res[0])['x']
    y = (res[0])['y']

    wcs = wcs.WCS(hdr)
    ra, dec = wcs.all_pix2world(y, x, 0)

    import numpy.lib.recfunctions as rfn
    cat = rfn.append_fields(res[0], ['ra', 'dec'], [ra, dec])

    fits.writeto(outfn, cat)
    fits.append(outfn, res[1])
    fits.append(outfn, res[2])
    psfext = numpy.array([tpsf(0, 0, stampsz=19) for tpsf in res[3]])
    fits.append(outfn, psfext)
Example #24
0
def make_radio_combination_signature(radio_annotation, wcs, atlas_positions,
                                     subject, pix_offset):
    """Generates a unique signature for a radio annotation.

    radio_annotation: 'radio' dictionary from a classification.
    wcs: World coordinate system associated with the ATLAS image.
    atlas_positions: [[RA, DEC]] NumPy array.
    subject: RGZ subject dict.
    pix_offset: (x, y) pixel position of this radio subject on the ATLAS image.
    -> Something immutable
    """
    from . import rgz_data as data
    # TODO(MatthewJA): This only works on ATLAS. Generalise.
    # My choice of immutable object will be stringified crowdastro ATLAS
    # indices.
    zooniverse_id = subject['zooniverse_id']
    subject_fits = data.get_radio_fits(subject)
    subject_wcs = astropy.wcs.WCS(subject_fits.header)

    atlas_ids = []
    x_offset, y_offset = pix_offset
    for c in radio_annotation.values():
        # Note that the x scale is not the same as the IR scale, but the scale
        # factor is included in the annotation, so I have multiplied this out
        # here for consistency.
        scale_width = c.get('scale_width', '')
        scale_height = c.get('scale_height', '')
        if scale_width:
            scale_width = float(scale_width)
        else:
            # Sometimes, there's no scale, so I've included a default scale.
            scale_width = config['surveys']['atlas']['scale_width']

        if scale_height:
            scale_height = float(scale_height)
        else:
            scale_height = config['surveys']['atlas']['scale_height']

        # These numbers are in terms of the PNG images, so I need to multiply by
        # the click-to-fits ratio.
        scale_width *= config['surveys']['atlas']['click_to_fits_x']
        scale_height *= config['surveys']['atlas']['click_to_fits_y']

        subject_bbox = [
            [
                float(c['xmin']) * scale_width,
                float(c['xmax']) * scale_width,
            ],
            [
                float(c['ymin']) * scale_height,
                float(c['ymax']) * scale_height,
            ],
        ]

        # ...and by the mosaic ratio. There's probably double-up here, but this
        # makes more sense.
        scale_width *= config['surveys']['atlas']['mosaic_scale_x']
        scale_height *= config['surveys']['atlas']['mosaic_scale_y']
        # Get the bounding box of the radio source in pixels.
        # Format: [xs, ys]
        bbox = [
            [
                float(c['xmin']) * scale_width,
                float(c['xmax']) * scale_width,
            ],
            [
                float(c['ymin']) * scale_height,
                float(c['ymax']) * scale_height,
            ],
        ]
        assert bbox[0][0] < bbox[0][1]
        assert bbox[1][0] < bbox[1][1]

        # Convert the bounding box into RA/DEC.
        bbox = wcs.all_pix2world(bbox[0] + x_offset, bbox[1] + y_offset,
                                 FITS_CONVENTION)
        subject_bbox = subject_wcs.all_pix2world(subject_bbox[0],
                                                 subject_bbox[1],
                                                 FITS_CONVENTION)
        # TODO(MatthewJA): Remove (or disable) this sanity check.

        # The bbox is backwards along the x-axis for some reason.
        bbox[0] = bbox[0][::-1]
        assert bbox[0][0] < bbox[0][1]
        assert bbox[1][0] < bbox[1][1]

        bbox = numpy.array(bbox)

        # What is this radio source called? Check if we have an object in the
        # bounding box. We'll cache these results because there is a lot of
        # overlap.
        cache_key = tuple(tuple(b) for b in bbox)
        if cache_key in bbox_cache_:
            index = bbox_cache_[cache_key]
        else:
            x_gt_min = atlas_positions[:, 0] >= bbox[0, 0]
            x_lt_max = atlas_positions[:, 0] <= bbox[0, 1]
            y_gt_min = atlas_positions[:, 1] >= bbox[1, 0]
            y_lt_max = atlas_positions[:, 1] <= bbox[1, 1]
            within = numpy.all([x_gt_min, x_lt_max, y_gt_min, y_lt_max],
                               axis=0)
            indices = numpy.where(within)[0]

            if len(indices) == 0:
                logging.debug(
                    'Skipping radio source not in catalogue for '
                    '%s', zooniverse_id)
                continue
            else:
                if len(indices) > 1:
                    logging.debug(
                        'Found multiple (%d) ATLAS matches '
                        'for %s', len(indices), zooniverse_id)

                index = indices[0]

            bbox_cache_[cache_key] = index

        atlas_ids.append(str(index))

    atlas_ids.sort()

    if not atlas_ids:
        raise CatalogueError('No catalogued radio sources.')

    return ';'.join(atlas_ids)
Example #25
0
                                                      (_median - 3 * _sigma))
                use_for_fit[bad] = False

            area_ellipse = numpy.pi * a * b
            # break

            fit_results.append([a, b, theta, x0, y0, area, area_ellipse])

        df = pandas.DataFrame(data=fit_results,
                              columns=[
                                  'A', 'B', 'theta', 'x0', 'y0',
                                  'area_contours', "area_ellipse"
                              ])

        wcs = astropy.wcs.WCS(header)
        radec = wcs.all_pix2world(df[['x0', 'y0']].to_numpy(), 0)
        print(radec)
        df.loc[:, ['ra', 'dec']] = radec
        df.loc[:, 'filename'] = fn
        df.loc[:, ['a_arcsec', 'b_arcsec']] = df[['A', 'B'
                                                  ]].to_numpy() * pixelscale
        df.loc[:, 'area_error'] = (df['area_ellipse'] -
                                   df['area_contours']) / df['area_contours']
        df.loc[:, 'good_fit'] = numpy.fabs(df['area_error']) < 0.05
        df.info()

        if (master_df is None):
            master_df = df
        else:
            master_df = master_df.append(df, ignore_index=False)
Example #26
0
    # now write the region file again, this time using WCS instead of pixel information
    hdr = img_hdu[0].header
    try:
        pixelscale = numpy.hypot(hdr['CD1_1'],
                                 hdr['CD1_2']) * 3600.  # in arcsec/pixel
    except:
        try:
            pixelscale = hdr['CDELT1'] * 3600
        except:
            pixelscale = 1.
            pass

    # convert all x/y into ra/dec
    wcs = astropy.wcs.WCS(hdr)
    (ra0, dec0) = wcs.all_pix2world(df['x0'], df['y0'], 0)
    # print(ra_dec)
    df['ra0'] = ra0  #_dec[:,0]
    df['dec0'] = dec0  #ra_dec[:,1]
    ds9_reg_fn = output_basename + "_ellipses_wcs.reg"
    with open(ds9_reg_fn, "w") as ds9_reg:
        print("""\
# Region file format: DS9 version 4.1
global color=green dashlist=8 3 width=1 font="helvetica 10 normal roman" select=1 highlite=1 dash=0 fixed=0 edit=1 move=1 delete=1 include=1 source=1
fk5""",
              file=ds9_reg)
        for index, e in df.iterrows():
            # print(e)
            print('ellipse(%f,%f,%f",%f",%f) # %s' %
                  (e['ra0'], e['dec0'], e['sma'] * pixelscale,
                   (1. - e['ellipticity']) * e['sma'] * pixelscale, e['pa'],