def read(self, filename, hdu=None): self.filename = filename self.name = self.name or os.path.basename(filename) # read FITS file if not hdu: dprint(3, "opening", filename) hdu = pyfits.open(filename)[0] hdu.verify('silentfix') hdr = self.fits_header = hdu.header dprint(3, "reading data") data = hdu.data # NB: all-data operations (such as getting global min/max or computing of histograms) are much faster # (almost x2) when data is iterated # over in the proper order. After a transpose(), data is in fortran order. Tell this to setData(). data = numpy.transpose(data) # .copy() dprint(3, "setting data") self.setData(data, fortran_order=True) dprint(3, "reading header") ndim = hdr['NAXIS'] if ndim < 2: raise ValueError, "Cannot load a one-dimensional FITS file" # setup projection # (strip out history from header, as big histories really slow down FITSWCS) hdr1 = pyfits.Header( filter(lambda x: not str(x).startswith('HISTORY'), hdr.cards)) proj = Projection.FITSWCS(hdr1) nx = ny = None # find X and Y axes for iaxis in range(ndim): axs = str(iaxis + 1) npix = hdr['NAXIS' + axs] name = hdr.get('CTYPE' + axs, axs).strip().upper() # have we found the coordinate axes? if FITSHeaders.isAxisTypeX(name): nx = npix iaxis_ra = iaxis elif FITSHeaders.isAxisTypeY(name): ny = npix iaxis_dec = iaxis # check that we have them if nx is None or ny is None: iaxis_ra, iaxis_dec = 0, 1 nx, ny = hdr.get('NAXIS1'), hdr.get('NAXIS2') for iaxis in range(ndim): axs = str(iaxis + 1) # get axis description npix = hdr['NAXIS' + axs] crval = hdr.get('CRVAL' + axs, 0) cdelt = hdr.get('CDELT' + axs, 1) crpix = hdr.get('CRPIX' + axs, 1) - 1 name = hdr.get('CTYPE' + axs, axs).strip().upper() unit = hdr.get('CUNIT' + axs) # if this is not an X/Y axis, add it to the slicers if iaxis not in (iaxis_ra, iaxis_dec): # values becomes a list of axis values values = list(crval + (numpy.arange(npix) - crpix) * cdelt) unit = unit and unit.lower().capitalize() # FITS knows of two enumerable axes: STOKES and COMPLEX. For these two, replace values with proper names if name == "STOKES": labels = [(self.StokesNames[int(i)] if i > 0 and i < len(self.StokesNames) else "%d" % i) for i in values] elif name == "COMPLEX": labels = [(self.ComplexNames[int(i)] if i > 0 and i < len(self.ComplexNames) else "%d" % i) for i in values] else: name = name.split("-")[0] # if values are a simple sequence startying at 0 or 1, make simple labels if cdelt == 1 and values[0] in (0., 1.): labels = ["%d%s" % (val, unit) for val in values] # else set labels to None: setExtraAxis() will figure it out else: labels = None self.setExtraAxis(iaxis, name or ("axis " + axs), labels, values, unit) # check for beam parameters psf = [hdr.get(x, None) for x in 'BMAJ', 'BMIN', 'BPA'] if all([x is not None for x in psf]): self.setPsfSize(*[p / 180 * math.pi for p in psf]) self.setSkyAxis(0, iaxis_ra, nx, proj.ra0, -proj.xscale, proj.xpix0) self.setSkyAxis(1, iaxis_dec, ny, proj.dec0, proj.yscale, proj.ypix0) self.setDefaultProjection(proj) dprint(3, "setting initial slice") self._setupSlice()
def fitPsf(filename, cropsize=None): """Fits a Gaussian PSF to the FITS file given by 'filename'. If cropsize is specified, crops the central cropsize X cropsize pixels before fitting. Else determines cropsize by looking for the first negative sidelobe from the centre outwards. Returns maj_sigma,min_sigma,pa_NE (in radians) """ # read PSF from file psf = pyfits.open(filename)[0] hdr = psf.header psf = psf.data dprintf(2, "Read PSF of shape %s from file %s\n", psf.shape, filename) # remove stokes and freq axes if len(psf.shape) == 4: psf = psf[0, 0, :, :] elif len(psf.shape) == 3: psf = psf[0, :, :] else: raise RuntimeError("illegal PSF shape %s" + psf.shape) nx, ny = psf.shape # crop the central region if cropsize: size = cropsize psf = psf[(nx - size) // 2:(nx + size) // 2, (ny - size) // 2:(ny + size) // 2] # if size not specified, then auto-crop by looking for the first negative value starting from the center # this will break on very extended diagonal PSFs, but that's a pathological case else: ix = numpy.where(psf[:, ny // 2] < 0)[0] ix0 = max(ix[ix < nx // 2]) ix1 = min(ix[ix > nx // 2]) iy = numpy.where(psf[nx // 2, :] < 0)[0] iy0 = max(iy[iy < ny // 2]) iy1 = min(iy[iy > ny // 2]) print(ix0, ix1, iy0, iy1) psf = psf[ix0:ix1, iy0:iy1] psf[psf < 0] = 0 # estimate gaussian parameters, then fit from . import gaussfitter2 parms0 = gaussfitter2.moments(psf, circle=0, rotate=1, vheight=0) print(parms0) dprint(2, "Estimated parameters are", parms0) parms = gaussfitter2.gaussfit(psf, None, parms0, autoderiv=1, return_all=0, circle=0, rotate=1, vheight=0) dprint(0, "Fitted parameters are", parms) # now swap x and y around, since our axes are in reverse order ampl, y0, x0, sy, sx, rot = parms # get pixel sizes in radians (by constructing a projection object) proj = Projection.FITSWCS(hdr) xscale, yscale = proj.xscale, proj.yscale sx_rad = abs(sx * proj.xscale) sy_rad = abs(sy * proj.yscale) rot -= 90 # convert West through North PA into the conventional North through East if sx_rad < sy_rad: sx_rad, sy_rad = sy_rad, sx_rad rot -= 90 rot %= 180 dprintf( 1, "Fitted gaussian PSF FWHM of %f x %f pixels (%f x %f arcsec), PA %f deg\n", sx * FWHM, sy * FWHM, sx_rad * FWHM * ARCSEC, sy_rad * FWHM * ARCSEC, rot) return sx_rad, sy_rad, rot / DEG