Esempio n. 1
0
def fitsInfo(fitsname=None):
    """
    Get fits info
    """
    hdu = pyfits.open(fitsname)
    hdr = hdu[0].header
    ra = hdr['CRVAL1']
    dra = abs(hdr['CDELT1'])
    raPix = hdr['CRPIX1']
    dec = hdr['CRVAL2']
    ddec = abs(hdr['CDELT2'])
    decPix = hdr['CRPIX2']
    freq0 = 0
    for i in range(1, hdr['NAXIS']+1):
        if hdr['CTYPE%d' % i].strip() == 'FREQ':
            freq0 = hdr['CRVAL%d' % i]
            break

    ndim = hdr["NAXIS"]
    imslice = np.zeros(ndim, dtype=int).tolist()
    imslice[-2:] = slice(None), slice(None)
    image = hdu[0].data[imslice]
    wcs = WCS(hdr, mode='pyfits')

    return {'image': image, 'wcs': wcs, 'ra': ra, 'dec': dec, 'dra': dra, 'ddec': ddec, 'raPix': raPix, 'decPix': decPix, 'freq0': freq0}
Esempio n. 2
0
def loadFits(imagename):

    with pyfits.open(imagename) as hdu:
        data = hdu[0].data
        hdr = hdu[0].header

    wcs = WCS(hdr, mode="pyfits")

    return data, hdr, wcs
Esempio n. 3
0
 def __init__ (self,header):
   """Constructor. Create from filename (treated as FITS file), or a FITS header object""";
   # attach to FITS file or header
   if isinstance(header,str):
     header = pyfits.open(header)[0].header;
   else:
     self.wcs = WCS(header,mode="pyfits");
   try:
     ra0,dec0 = self.wcs.getCentreWCSCoords();
     self.xpix0,self.ypix0 = self.wcs.wcs2pix(*self.wcs.getCentreWCSCoords());
     self.xscale = self.wcs.getXPixelSizeDeg()*DEG;
     self.yscale = self.wcs.getYPixelSizeDeg()*DEG;
     has_projection = True;
   except:
     print "No WCS in FITS file, falling back to pixel coordinates.";
     ra0 = dec0 = self.xpix0 = self.ypix0 = 0;
     self.xscale = self.yscale = DEG/3600;
     has_projection = False;
   _Projector.__init__(self,ra0*DEG,dec0*DEG,has_projection=has_projection);
Esempio n. 4
0
def reshape_data (image, prefix=None):

    """ Reshape FITS data to (stokes,freq,npix_ra,npix_dec).

    Returns reshaped data, wcs, the image header, and
    pixel size.
         
    image: Fits data  
    """
    
    with pyfits.open(image) as hdu:
        data = hdu[0].data
        hdr = hdu[0].header
        shape = list(data.shape)
        ndim = len(shape)

    wcs = WCS(hdr, mode="pyfits")
    log = logger(level=0, prefix=prefix)

    pixel_size = abs(hdr["CDELT1"])
    if ndim < 2:
        log.error(" The FITS file needs at least two dimensions")
        

 # This is the shape I want the data in
    want = (
            ["STOKES",0],
            ["FREQ",1],
            ["RA",2],
            ["DEC",3],
)
   
    # Assume RA,DEC is first (FITS) or last two (NUMPY)
    if ndim > 3:
        for ctype, ind in want[:2]:
            for axis in range(1, ndim+1):
                if hdr["CTYPE%d"%axis].startswith(ctype):
                    want[ind].append(ndim-axis)
        if want[0][-1] == want[1][-2] and want[0][-2] == want[1][-1]:
            tmp = shape[0]
            shape[0] = shape[1]
            shape[1] = tmp
            data = numpy.reshape(data,shape)
    if ndim == 3:
        if not hdr["CTYPE3"].startswith("FREQ"):
            data = data[0,...]
    elif ndim > 4:
        log.error(" FITS file has more than 4 axes. Aborting")
 
    return data, wcs, hdr, pixel_size
Esempio n. 5
0
 def __init__ (self,header):
   """Constructor. Create from filename (treated as FITS file), or a FITS header object""";
   # attach to FITS file or header
   if isinstance(header,str):
     header = pyfits.open(header)[0].header;
   else:
     self.wcs = WCS(header,mode="pyfits");
   try:
     ra0,dec0 = self.wcs.getCentreWCSCoords();
     self.xpix0,self.ypix0 = self.wcs.wcs2pix(*self.wcs.getCentreWCSCoords());
     self.xscale = self.wcs.getXPixelSizeDeg()*DEG;
     self.yscale = self.wcs.getYPixelSizeDeg()*DEG;
     has_projection = True;
   except:
     print "No WCS in FITS file, falling back to pixel coordinates.";
     ra0 = dec0 = self.xpix0 = self.ypix0 = 0;
     self.xscale = self.yscale = DEG/3600;
     has_projection = False;
   _Projector.__init__(self,ra0*DEG,dec0*DEG,has_projection=has_projection);
Esempio n. 6
0
class AddBrickDialog(QDialog):
    def __init__(self, parent, modal=True, flags=Qt.WindowFlags()):
        QDialog.__init__(self, parent, flags)
        self.setModal(modal)
        self.setWindowTitle("Add FITS brick")
        lo = QVBoxLayout(self)
        lo.setMargin(10)
        lo.setSpacing(5)
        # file selector
        self.wfile = FileSelector(self,
                                  label="FITS filename:",
                                  dialog_label="FITS file",
                                  default_suffix="fits",
                                  file_types="FITS files (*.fits *.FITS)",
                                  file_mode=QFileDialog.ExistingFile)
        lo.addWidget(self.wfile)
        # overwrite or add mode
        lo1 = QGridLayout()
        lo.addLayout(lo1)
        lo1.setContentsMargins(0, 0, 0, 0)
        lo1.addWidget(QLabel("Padding factor:", self), 0, 0)
        self.wpad = QLineEdit("2", self)
        self.wpad.setValidator(QDoubleValidator(self))
        lo1.addWidget(self.wpad, 0, 1)
        lo1.addWidget(QLabel("Assign source name:", self), 1, 0)
        self.wname = QLineEdit(self)
        lo1.addWidget(self.wname, 1, 1)
        # OK/cancel buttons
        lo.addSpacing(10)
        lo2 = QHBoxLayout()
        lo.addLayout(lo2)
        lo2.setContentsMargins(0, 0, 0, 0)
        lo2.setMargin(5)
        self.wokbtn = QPushButton("OK", self)
        self.wokbtn.setMinimumWidth(128)
        QObject.connect(self.wokbtn, SIGNAL("clicked()"), self.accept)
        self.wokbtn.setEnabled(False)
        cancelbtn = QPushButton("Cancel", self)
        cancelbtn.setMinimumWidth(128)
        QObject.connect(cancelbtn, SIGNAL("clicked()"), self.reject)
        lo2.addWidget(self.wokbtn)
        lo2.addStretch(1)
        lo2.addWidget(cancelbtn)
        self.setMinimumWidth(384)
        # signals
        QObject.connect(self.wfile, SIGNAL("filenameSelected"),
                        self._fileSelected)
        # internal state
        self.qerrmsg = QErrorMessage(self)

    def setModel(self, model):
        self.model = model
        if model.filename():
            self._model_dir = os.path.dirname(os.path.abspath(
                model.filename()))
        else:
            self._model_dir = os.path.abspath('.')
        self.wfile.setDirectory(self._model_dir)
        self._fileSelected(self.wfile.filename(), quiet=True)

    def _fileSelected(self, filename, quiet=False):
        self.wokbtn.setEnabled(False)
        if not filename:
            return None
        # check that filename matches model
        if not os.path.samefile(self._model_dir, os.path.dirname(filename)):
            self.wfile.setFilename('')
            if not quiet:
                QMessageBox.warning(
                    self, "Directory mismatch",
                    """<P>The FITS file must reside in the same directory
          as the current sky model.</P>""")
            self.wfile.setDirectory(self._model_dir)
            return None
        # if filename is not in model already, enable the "add to model" control
        for src in self.model.sources:
            if isinstance(getattr(src, 'shape', None), ModelClasses.FITSImage):
                if os.path.exists(src.shape.filename) and os.path.samefile(
                        src.shape.filename, filename):
                    if not quiet:
                        QMessageBox.warning(
                            self, "Already in model",
                            "This FITS brick is already present in the model.")
                    self.wfile.setFilename('')
                    return None
        if not str(self.wname.text()):
            self.wname.setText(
                os.path.splitext(os.path.basename(str(filename)))[0])
        self.wokbtn.setEnabled(True)
        return filename

    def accept(self):
        """Tries to add brick, and closes the dialog if successful."""
        filename = self.wfile.filename()
        # read fits file
        busy = BusyIndicator()
        try:
            input_hdu = pyfits.open(filename)[0]
        except Exception, err:
            busy = None
            QMessageBox.warning(
                self, "Error reading FITS",
                "Error reading FITS file %s: %s" % (filename, str(err)))
            return
        # check name
        srcname = str(self.wname.text()) or os.path.splitext(
            os.path.basename(str(filename)))[0]
        if srcname in set([src.name for src in self.model.sources]):
            QMessageBox.warning(
                self, "Already in model",
                "<p>The model already contains a source named '%s'. Please select a different name.</p>"
                % srcname)
            return
        # get image parameters
        hdr = input_hdu.header
        max_flux = float(input_hdu.data.max())
        wcs = WCS(hdr, mode='pyfits')
        # Get reference pixel coordinates
        # wcs.getCentreWCSCoords() doesn't work, as that gives us the middle of the image
        # So scan the header to get the CRPIX values
        ra0 = dec0 = 1
        for iaxis in range(hdr['NAXIS']):
            axs = str(iaxis + 1)
            name = hdr.get('CTYPE' + axs, axs).upper()
            if name.startswith("RA"):
                ra0 = hdr.get('CRPIX' + axs, 1) - 1
            elif name.startswith("DEC"):
                dec0 = hdr.get('CRPIX' + axs, 1) - 1
        # convert pixel to degrees


#    print ra0,dec0;
        ra0, dec0 = wcs.pix2wcs(ra0, dec0)
        ra0 *= DEG
        dec0 *= DEG
        #    print ModelClasses.Position.ra_hms_static(ra0);
        #    print ModelClasses.Position.dec_sdms_static(dec0);
        sx, sy = wcs.getHalfSizeDeg()
        sx *= DEG
        sy *= DEG
        nx, ny = input_hdu.data.shape[-1:-3:-1]
        pos = ModelClasses.Position(ra0, dec0)
        flux = ModelClasses.Flux(max_flux)
        shape = ModelClasses.FITSImage(sx,
                                       sy,
                                       0,
                                       os.path.basename(filename),
                                       nx,
                                       ny,
                                       pad=float(str(self.wpad.text()) or "1"))
        img_src = SkyModel.Source(srcname, pos, flux, shape=shape)
        self.model.setSources(self.model.sources + [img_src])
        self.model.emitUpdate(SkyModel.SkyModel.UpdateAll, origin=self)
        busy = None
        return QDialog.accept(self)
Esempio n. 7
0
  class FITSWCSpix (_Projector):
    """FITS WCS projection, as determined by a FITS header. lm is in pixels (0-based)."""
    def __init__ (self,header):
      """Constructor. Create from filename (treated as FITS file), or a FITS header object""";
      # attach to FITS file or header
      if isinstance(header,str):
        header = pyfits.open(header)[0].header;
      else:
        self.wcs = WCS(header,mode="pyfits");
      try:
        ra0,dec0 = self.wcs.getCentreWCSCoords();
        self.xpix0,self.ypix0 = self.wcs.wcs2pix(*self.wcs.getCentreWCSCoords());
        self.xscale = self.wcs.getXPixelSizeDeg()*DEG;
        self.yscale = self.wcs.getYPixelSizeDeg()*DEG;
        has_projection = True;
      except:
        print "No WCS in FITS file, falling back to pixel coordinates.";
        ra0 = dec0 = self.xpix0 = self.ypix0 = 0;
        self.xscale = self.yscale = DEG/3600;
        has_projection = False;
      _Projector.__init__(self,ra0*DEG,dec0*DEG,has_projection=has_projection);

    def lm (self,ra,dec):
      if not self.has_projection():
        return numpy.sin(ra)/self.xscale,numpy.sin(dec)/self.yscale;
      if numpy.isscalar(ra) and numpy.isscalar(dec):
        if ra - self.ra0 > math.pi:
          ra -= 2*math.pi;
        if ra - self.ra0 < -math.pi:
          ra += 2*math.pi;
        return self.wcs.wcs2pix(ra/DEG,dec/DEG);
      else:
        if numpy.isscalar(ra):
          ra = numpy.array(ra);
        ra[ra - self.ra0 > math.pi] -= 2*math.pi;
        ra[ra - self.ra0 < -math.pi] += 2*math.pi;
        ## when fed in arrays of ra/dec, wcs.wcs2pix will return a nested list of
        ## [[l1,m1],[l2,m2],,...]. Convert this to an array and extract columns.
        lm = numpy.array(self.wcs.wcs2pix(ra/DEG,dec/DEG));
        return lm[...,0],lm[...,1];

    def radec (self,l,m):
      if not self.has_projection():
        return numpy.arcsin(l*self.xscale),numpy.arcsin(m*self.yscale);
      if numpy.isscalar(l) and numpy.isscalar(m):
        ra,dec = self.wcs.pix2wcs(l,m);
      else:
## this is slow as molasses because of the way astLib.WCS implements the loop. ~120 seconds for 4M pixels
        ## when fed in arrays of ra/dec, wcs.wcs2pix will return a nested list of
        ## [[l1,m1],[l2,m2],,...]. Convert this to an array and extract columns.
#        radec = numpy.array(self.wcs.pix2wcs(l,m));
#        ra = radec[...,0];
#        dec = radec[...,1];
### try a faster implementation -- oh well, only a bit faster, ~95 seconds for the same
### can also replace list comprehension with map(), but that doesn't improve things.
### Note also that the final array constructor takes ~10 secs!
        radec = numpy.array([ PyWCSTools.wcs.pix2wcs(self.wcs.WCSStructure,x,y) for x,y in zip(l+1,m+1) ]);
        ra = radec[...,0];
        dec = radec[...,1];
      return ra*DEG,dec*DEG;


    def offset (self,dra,ddec):
      return self.xpix0 - dra/self.xscale,self.ypix0 + ddec/self.xscale;

    def __eq__ (self,other):
      """By default, two projections are the same if their classes match, and their ra0/dec0 match."""
      return type(self) is type(other) and (self.ra0,self.dec0,self.xpix0,self.ypix0,self.xscale,self.yscale)  == (other.ra0,other.dec0,other.xpix0,other.ypix0,other.xscale,other.yscale);
Esempio n. 8
0
   traceback.print_exc();
   busy = None;
   QMessageBox.warning(self,"Error writing FITS","Error writing FITS file %s: %s"%(filename,str(err)));
   return;
 changed = False;
 sources = self.model.sources;
 # remove sources from model if asked to
 if self.wdel.isChecked():
   sources = [ src for src in sources if not (src.selected and src.typecode == 'pnt') ];
   changed = True;
 # add image to model if asked to
 if self.wadd.isChecked():
   hdr = input_hdu.header;
   # get image parameters
   max_flux = float(input_hdu.data.max());
   wcs = WCS(hdr,mode='pyfits');
   # Get reference pixel coordinates
   # wcs.getCentreWCSCoords() doesn't work, as that gives us the middle of the image
   # So scan the header to get the CRPIX values
   ra0 = dec0 = 1;
   for iaxis in range(hdr['NAXIS']):
     axs = str(iaxis+1);
     name = hdr.get('CTYPE'+axs,axs).upper();
     if name.startswith("RA"):
       ra0 = hdr.get('CRPIX'+axs,1)-1;
     elif name.startswith("DEC"):
       dec0 = hdr.get('CRPIX'+axs,1)-1;
   # convert pixel to degrees
   ra0,dec0 = wcs.pix2wcs(ra0,dec0);
   ra0 *= DEG;
   dec0 *= DEG;
Esempio n. 9
0
  class FITSWCSpix (_Projector):
    """FITS WCS projection, as determined by a FITS header. lm is in pixels (0-based)."""
    def __init__ (self,header):
      """Constructor. Create from filename (treated as FITS file), or a FITS header object""";
      # attach to FITS file or header
      if isinstance(header,str):
        header = pyfits.open(header)[0].header;
      else:
        self.wcs = WCS(header,mode="pyfits");
      try:
        ra0,dec0 = self.wcs.getCentreWCSCoords();
        self.xpix0,self.ypix0 = self.wcs.wcs2pix(*self.wcs.getCentreWCSCoords());
        self.xscale = self.wcs.getXPixelSizeDeg()*DEG;
        self.yscale = self.wcs.getYPixelSizeDeg()*DEG;
        has_projection = True;
      except:
        print "No WCS in FITS file, falling back to pixel coordinates.";
        ra0 = dec0 = self.xpix0 = self.ypix0 = 0;
        self.xscale = self.yscale = DEG/3600;
        has_projection = False;
      _Projector.__init__(self,ra0*DEG,dec0*DEG,has_projection=has_projection);

    def lm (self,ra,dec):
      if not self.has_projection():
        return numpy.sin(ra)/self.xscale,numpy.sin(dec)/self.yscale;
      if numpy.isscalar(ra) and numpy.isscalar(dec):
        if ra - self.ra0 > math.pi:
          ra -= 2*math.pi;
        if ra - self.ra0 < -math.pi:
          ra += 2*math.pi;
        return self.wcs.wcs2pix(ra/DEG,dec/DEG);
      else:
        if numpy.isscalar(ra):
          ra = numpy.array(ra);
        ra[ra - self.ra0 > math.pi] -= 2*math.pi;
        ra[ra - self.ra0 < -math.pi] += 2*math.pi;
        ## when fed in arrays of ra/dec, wcs.wcs2pix will return a nested list of
        ## [[l1,m1],[l2,m2],,...]. Convert this to an array and extract columns.
        lm = numpy.array(self.wcs.wcs2pix(ra/DEG,dec/DEG));
        return lm[...,0],lm[...,1];

    def radec (self,l,m):
      if not self.has_projection():
        return numpy.arcsin(l*self.xscale),numpy.arcsin(m*self.yscale);
      if numpy.isscalar(l) and numpy.isscalar(m):
        ra,dec = self.wcs.pix2wcs(l,m);
      else:
## this is slow as molasses because of the way astLib.WCS implements the loop. ~120 seconds for 4M pixels
        ## when fed in arrays of ra/dec, wcs.wcs2pix will return a nested list of
        ## [[l1,m1],[l2,m2],,...]. Convert this to an array and extract columns.
#        radec = numpy.array(self.wcs.pix2wcs(l,m));
#        ra = radec[...,0];
#        dec = radec[...,1];
### try a faster implementation -- oh well, only a bit faster, ~95 seconds for the same
### can also replace list comprehension with map(), but that doesn't improve things.
### Note also that the final array constructor takes ~10 secs!
        radec = numpy.array([ PyWCSTools.wcs.pix2wcs(self.wcs.WCSStructure,x,y) for x,y in zip(l+1,m+1) ]);
        ra = radec[...,0];
        dec = radec[...,1];
      return ra*DEG,dec*DEG;


    def offset (self,dra,ddec):
      return self.xpix0 - dra/self.xscale,self.ypix0 + ddec/self.xscale;

    def __eq__ (self,other):
      """By default, two projections are the same if their classes match, and their ra0/dec0 match."""
      return type(self) is type(other) and (self.ra0,self.dec0,self.xpix0,self.ypix0,self.xscale,self.yscale)  == (other.ra0,other.dec0,other.xpix0,other.ypix0,other.xscale,other.yscale);
Esempio n. 10
0
def psf_image_correlation(catalog, psfimage, imagedata, header, wcs=None ,
                     pixelsize=None, corr_region=5, thresh=0.4, tags=None,
                     coefftag='high_corr', setatr=True, do_high=False, 
                     prefix=None):


    """ Computes correlation of the image and PSF image

    catalog : Source model, Tigger format.
    psfimage : Instrument's Point spread functions Fits data.
    imagedata : Fits data
    header : Fits header e.g., img=pyfits.open("test.fits")
        header=img[0].header
    wcs :This class provides methods
        for accessing information from the World  Coordinate System
        (WCS) contained in the header of a FITS image. Conversions 
        between pixel and WCS coordinates can also be performed.
        If not provided it is directly obtained from the Fits header
        provided.
    pixelsize: float, Default is None.
         If not provided then it is directly obtained form a Fits header.
    corr_region : int, optional. A default value of 5.
        The size of the region to correlate given in beam sizes.
    thresh : float, optional. A default value of 0.4. 
        Correlation threshold. Sources with correlation > threshold
        are sources with high correlation.
    tags: str, optional. Default is None.
        If specified only sources with 'Tag' will be evaluated.
    coefftag: str, optional. A Default string is 'high_corr'.
        If provided sources with correlation > thresh will be tagged
        using the user specified tag.
    setatr : bool, optional. Default is True.
        If True all sources will be tagged with 'cf'
        giving each detection an extra correlation with PSF parameter.
    do_high: bool, optional.  Default is False.
        If True, sources of high correlation are tagged using 'coefftag',
        if False no tagging will be made.
    """

    model = Tigger.load(catalog)
   
    image_data = imagedata 
    beam = header["BMAJ"]
    psf_data, wcs_psf, psf_hdr, psf_pix = reshape_data(image=psfimage)
    
    shape = image_data.shape
    log = logger(level=0, prefix=prefix)

    if pixelsize is None:
        pixelsize = abs(header["CDELT1"])
    if wcs is None:
        wcs = WCS(header, mode="pyfits")

    bmaj = int(round(beam/pixelsize))
    log.info("Beam major= %d degrees"%bmaj)

    if bmaj == 0:
        log.debug("Beam major axis was read as 0, setting it to 1")
        bmaj = 1.0

    if not isinstance(corr_region, int):
        if isinstance(corr_region, float):
            corr_region = int(round(corr_region))
            log.debug("Float is provided and int is required,"
                      "arounding off to the nearest integer")
            if corr_region == 0:
                log.error("Rounding off to 0. Provide an integer."
                          "Aborting")
        else:
            log.error("corr_region must be an integer. Abort")
    
    step = corr_region * bmaj
    
    sources = []

    if tags:
        log.debug("Only sources with tag %s will be correlated"
                  "with the PSF"%tags)
        sources = filter(lambda src: src.getTag(tags),model.sources) 
    else:
         for src in model.sources:
             sources.append(src)
    
    positions_sky = [map(lambda rad: numpy.rad2deg(rad),
                    (src.pos.ra, src.pos.dec))  for src in sources]
    pos = [wcs.wcs2pix(*pos) for pos in positions_sky]

    step = [step,step]
    ndim = len(shape)
    if ndim == 4:
        image_data = image_data[0,0,...]
    if ndim == 3:
        image_data = image_data[0,...]

    pdim = len(psf_data.shape)
    if pdim == 4:
        psf_data = psf_data[0,0,...]
    if pdim == 3:
        psf_data = psf_data[0,...]
  
    
    m = 0
    for i, (p, src) in enumerate(zip(pos, sources)):      
        x,y = p
        if x>shape[-2] or y>shape[-1] or numpy.array(p).any()<0:
            pos.remove(p)
            sources.remove(src)
            model.sources.remove(src)
            m += 1

        if (y+step[1] > shape[-1]) or (y-step[1] < 0):
            if p in pos:
                pos.remove(p)
                model.sources.remove(src)
                sources.remove(src)
            m += 1
        if (x+step[0] > shape[-2]) or (x-step[0] < 0):
            if p in pos:
                pos.remove(p)
                model.sources.remove(src)
                sources.remove(src)
            m += 1
    if m > 0:
        log.debug("It will be useful to increase the image size,"
                  "sources with ra+step or dec+step > image size"
                  "are removed")

    central = psf_hdr["CRPIX2"]
    psf_region = psf_data[central-step[0] : central+step[0],
                 central-step[1] : central+step[1]]
    psf_region = psf_region.flatten()
     
    corr = []
    n = 0
    for src, (ra, dec) in zip(sources, pos): 
        data_region = image_data[dec-step[0] : dec+step[0],
                                ra-step[1] : ra+step[1]].flatten()
        norm_data = (data_region-data_region.min())/(data_region.max()-
                                                     data_region.min())
        c_region = numpy.corrcoef((norm_data, psf_region))
        cf_region =  (numpy.diag((numpy.rot90(c_region))**2)
                                  .sum())**0.5/2**0.5
        cf = cf_region

        if math.isnan(float(cf)) or cf == 0:
            model.sources.remove(src)
            sources.remove(src)
            n += 1
        else:
            corr.append(cf)

            if setatr:
                src.setAttribute("cf",cf)

    if n > 0:
        log.debug("%d sources were removed due to 0/nan correlation"%n)

    thresh = thresh 
    coefftag = coefftag
    if do_high:
        for src, crr in zip(sources, corr):
            if crr > thresh:
               src.setTag(coefftag, True)
    model.save(catalog)     

    return corr
Esempio n. 11
0
    def __init__(self, imagename, psfname, pmodel, nmodel, local_step=10,
                 snr_thresh=40, high_corr_thresh=0.5, negdetec_region=10,
                 negatives_thresh=5, phasecenter_excl_radius=None,
                 prefix=None, loglevel=0):


        """ Determines sources that require direction-dependent (DD)
            calibration solutions.

        psfname: PSF fits data

        pmodel: Model of the positive image.

        nmodel: Model of the negative image
 
        header: The header of the input image

        snr_thresh: float, optional. Default is 40.
             Any source with 40 x the minimum SNR.

        high_corr_thresh:  float, optional. Default is 0.5.
             Sources of high PSF correlation have correlation above 0.5.

        negdetec_region:  float, optional. Default is 10.
             Region to lookup for negative detections around. In beam size.

        negative_thresh:  float, optional. Default is 5.
             The number of nearby negative detections. Sources
             with number > 5 require direction
             dependent (DD) calibration solutions.

        phasecenter_excl_region:  float (in degrees), optional.
             A radius from the phase center (in beam sizes) to exclude the sources
             from the evaluation.

        prefix: str, optional. Sets a prefix to the output directory.

        loglevel: int, optional. Default 0. Python logging.
                  0, 1, 2, 3 for info, debug, error and 
                  critical, respectively.
        """

        self.loglevel = loglevel
        self.prefix = prefix
        self.log = utils.logger(self.loglevel, prefix=self.prefix)
        #image and psf image

        self.pmodel = pmodel
        self.nmodel = nmodel
        self.psfname =  psfname
  
        self.log.info(" Loading image data")

        # tags
        self.dd_tag = "dE"

        # thresholds
        self.snr_factor = snr_thresh
        #self.localthresh = local_thresh
        self.corrthresh = high_corr_thresh
        self.negthresh = negatives_thresh
        
        with pyfits.open(imagename) as hdu:
            self.hdr = hdu[0].header
            self.data = utils.image_data(hdu[0].data)

        self.wcs = WCS(self.hdr, mode="pyfits")
        
        self.locstep = local_step

        #regions
        self.phaserad = phasecenter_excl_radius # radius from the phase center
        self.negregion =  negdetec_region # region to look for negatives
        
       # conversion
        self.bmaj = self.hdr["BMAJ"] # in degrees
        
        self.ra0 =  numpy.deg2rad(self.hdr["CRVAL1"])
        self.dec0 = numpy.deg2rad(self.hdr["CRVAL2"])
Esempio n. 12
0
def stackem(imagename, positions, width, pixels=False, stokes_ind=0, prefix="stacker-default"):
    """ Perfom continuun stacking """
    
    with pyfits.open(imagename) as hdu:
        hdr = hdu[0].header
        _data = hdu[0].data

    ndim = hdr["NAXIS"]
    # I only want the image data in the first plane
    # May deal with multiple channels/polarizations later

    sind = ndim - filter( lambda a: hdr["CTYPE%d"%a].startswith("STOKES"), range(1, ndim+1))[0]
    imslice = [slice(None)]*ndim
    imslice[:-2] = [0]*(ndim-2)
    imslice[sind] = stokes_ind

    data = _data[imslice]

    if not pixels:
        wcs = WCS(hdr, mode="pyfits")
        positions = [wcs.wcs2pix(*pos) for pos in positions]

    if width%2==1:
        width += 1
    stacked = numpy.zeros([width,width])

    for pos in positions:
        stacked += subregion(data, pos, width/2)

    # Get average stacked signal
    stacked /= len(positions)
    # Fit gaussian to stacked emission

    # First create a threshhold mask
    std = stacked.std()
    smooth = filters.gaussian_filter(stacked,[2,2])
    mask = smooth>2*std
    #p = gaussfitter2.gaussfit(stacked)
    #peak = p[1]
    #cell = numpy.deg2rad(abs(hdr["CDELT1"]))
    #tot = peak*(2*math.pi)*abs(p[4]*p[5])

    
    pylab.imshow(stacked)
    pylab.colorbar()
    #text = "Peak=%.4g  : Tot=%s"%(peak, tot)
    #pylab.title(text)
    x,y = [numpy.linspace(0,width,width)]*2
    xx,yy = numpy.meshgrid(x,y)
    #pylab.contour( gaussfitter2.twodgaussian(p,0,1,1)(xx,yy))

    pylab.savefig(prefix+"2d.png")
    pylab.clf()

    pylab.plot(numpy.diagonal(stacked)*1e3, "b-", label="stacked")
    noise = data.std()
    pylab.plot( [noise*1e3],"r--", label="noise")
    pylab.ylabel("mJy/beam")
    pylab.legend()
    pylab.title("Noise = %.3g mJy"%(noise*1e3))
    pylab.savefig(prefix+"1d.png")
    pylab.clf()

    return stacked.max() 
Esempio n. 13
0
def main():

    # setup some standard command-line option parsing
    #
    from optparse import OptionParser
    parser = OptionParser(usage="""%prog: [options] <image names...>""")
    parser.add_option("-o",
                      "--output",
                      dest="output",
                      type="string",
                      help="name of output FITS file")
    parser.add_option(
        "-r",
        "--replace",
        action="store_true",
        help="replace (first) input file by output. Implies '--force'.")
    parser.add_option("-f",
                      "--force",
                      dest="force",
                      action="store_true",
                      help="overwrite output file even if it exists")
    parser.add_option(
        "-S",
        "--sanitize",
        type="float",
        metavar="VALUE",
        help="sanitize FITS files by replacing NANs and INFs with VALUE")
    parser.add_option("-N",
                      "--nonneg",
                      action="store_true",
                      help="replace negative values by 0")
    parser.add_option("-m",
                      "--mean",
                      dest="mean",
                      action="store_true",
                      help="take mean of input images")
    parser.add_option("-d",
                      "--diff",
                      dest="diff",
                      action="store_true",
                      help="take difference of 2 input images")
    parser.add_option(
        "-t",
        "--transfer",
        action="store_true",
        help=
        "transfer data from image 2 into image 1, preserving the FITS header of image 1"
    )
    parser.add_option("-z",
                      "--zoom",
                      dest="zoom",
                      type="int",
                      metavar="NPIX",
                      help="zoom into central region of NPIX x NPIX size")
    parser.add_option("-R",
                      "--rescale",
                      dest="rescale",
                      type="float",
                      help="rescale image values")
    parser.add_option(
        "-E",
        "--edit-header",
        metavar="KEY=VALUE",
        type="string",
        action="append",
        help=
        "replace header KEY with VALUE. Use KEY=VALUE for floats and KEY='VALUE' for strings."
    )
    parser.add_option("-D",
                      "--delete-header",
                      metavar="KEY",
                      type="string",
                      action="append",
                      help="Remove KEY from header")
    parser.add_option(
        "--stack",
        metavar="outfile:axis",
        help=
        "Stack a list of FITS images along a given axis. This axis may given as an integer"
        "(as it appears in the NAXIS keyword), or as a string (as it appears in the CTYPE keyword)"
    )
    parser.add_option("--reorder",
                      help="Required order. List of comma seperated indeces")
    parser.add_option(
        "--add-axis",
        metavar="CTYPE:CRVAL:CRPIX:CDELT[:CUNIT:CROTA]",
        action="append",
        default=[],
        help=
        "Add axis to a FITS image. The AXIS will be described by CTYPE:CRVAL:CRPIX:CDELT[:CUNIT:CROTA]. "
        "The keywords in brackets are optinal, while those not in brackets are mendatory. "
        "This axis will be the last dimension. Maybe specified more than once."
    )
    parser.add_option(
        "--unstack",
        metavar="prefix:axis:each_chunk",
        help=
        "Unstack a FITS image into smaller chunks each having [each_chunk] planes along a given axis. "
        "This axis may given as an integer (as it appears in the NAXIS keyword), or as a string "
        "(as it appears in the CTYPE keyword)")
    parser.add_option("-H",
                      "--header",
                      action="store_true",
                      help="print header(s) of input image(s)")
    parser.add_option(
        "-s",
        "--stats",
        action="store_true",
        help="print stats on images and exit. No output images will be written."
    )

    parser.set_defaults(output="",
                        mean=False,
                        zoom=0,
                        rescale=1,
                        edit_header=[],
                        delete_header=[])

    (options, imagenames) = parser.parse_args()

    if not imagenames:
        parser.error("No images specified. Use '-h' for help.")

    # print "%d input image(s): %s"%(len(imagenames),", ".join(imagenames));
    images = [pyfits.open(img) for img in imagenames]
    updated = False

    # Stack FITS images
    if options.stack:
        if len(imagenames) < 1:
            parser.error("Need more than one image to stack")
        stack_args = options.stack.split(":")
        if len(stack_args) != 2:
            parser.error(
                "Two --stack options are required. See ./fitstool.py -h")

        outfile, axis = stack_args

        _string = True
        try:
            axis = int(axis)
            _string = False
        except ValueError:
            _string = True

        stack_planes(imagenames,
                     ctype=axis if _string else None,
                     keep_old=True,
                     axis=None if _string else axis,
                     outname=outfile,
                     fits=True)

    # Unstack FITS image
    if options.unstack:
        image = imagenames[0]
        if len(imagenames) < 1:
            parser.error("Need more than one image to stack")
        unstack_args = options.unstack.split(":")
        if len(unstack_args) != 3:
            parser.error(
                "Two --unstack options are required. See ./fitstool.py -h")

        prefix, axis, each_chunk = unstack_args

        _string = True
        try:
            axis = int(axis)
            _string = False
        except ValueError:
            _string = True

        each_chunk = int(each_chunk)

        unstack_planes(image,
                       each_chunk,
                       ctype=axis if _string else None,
                       axis=None if _string else axis,
                       prefix=prefix,
                       fits=True)
        sys.exit(0)

    if options.add_axis:
        for axis in options.add_axis:
            hdu = pyfits.open(imagenames[0])
            hdr = hdu[0].header
            ndim = hdr["NAXIS"]
            hdu[0].data = hdu[0].data[numpy.newaxis, ...]
            _mendatory = "CTYPE CRVAL CDELT CRPIX".split()
            _optional = "CUNIT CROTA".split()
            L = len(_mendatory + _optional)
            values = axis.split(":")
            Lv = len(values)

            if len(_mendatory) > len(values):
                parser.error(
                    "Something with the way specified --add-axis. See %prog -h for help"
                )

            for i, value in enumerate(values):
                try:
                    value = float(value)
                except ValueError:
                    if isinstance(value, str):
                        value = value.upper()

                hdu[0].header["%s%d" %
                              ((_mendatory + _optional)[i], ndim + 1)] = value

            hdu.writeto(imagenames[0], clobber=True)
            print("Successfully added axis %s to %s" %
                  (values[0], imagenames[0]))

    if options.reorder:
        for image in imagenames:
            order = map(int, options.reorder.split(","))
            reorder(image, order=order, outfile=image)

    if options.header:
        for filename, img in zip(imagenames, images):
            img.verify('silentfix')
            if len(imagenames) > 1:
                print "======== FITS header for", filename
            for hdrline in img[0].header.cards:
                print hdrline

    if options.replace or len(imagenames) < 2:
        if options.output:
            parser.error("Cannot combine -r/--replace with -o/--output")
        outname = imagenames[0]
        options.force = True
        autoname = False
    else:
        outname = options.output
        autoname = not outname
        if autoname:
            outname = re.split('[_]', imagenames[0], 1)[-1]

    for keyval in options.edit_header:
        key, val = keyval.split("=")
        q = ''
        if val[0] == "'" and val[-1] == "'":
            images[0][0].header[key] = val[1:-1:]
            q = '"'
        elif val[-1] == 'd' or key.startswith('NAXIS'):
            images[0][0].header[key] = int(val[:-1] if val[-1] == 'd' else val)
        else:
            try:
                images[0][0].header[key] = float(val)
            except:
                images[0][0].header[key] = val
                q = '"'
        print "Setting header %s=%s%s%s" % (key, q, val, q)
        updated = True

    for key in options.delete_header:
        try:
            del images[0][0].header[key]
        except KeyError:
            raise "Key '%s' not found in FITS header" % key
        print "Deleting key '%s' header" % key
        updated = True

    if options.sanitize is not None:
        print "Sanitizing: replacing INF/NAN with", options.sanitize
        for img in images:
            d = img[0].data
            d[numpy.isnan(d) + numpy.isinf(d)] = options.sanitize
        # if using stats, do not generate output
        if not options.stats:
            updated = True

    if options.nonneg:
        print "Replacing negative value by 0"
        for img, name in zip(images, imagenames)[:1]:
            d = img[0].data
            wh = d < 0
            d[wh] = 0
            print "Image %s: replaced %d points" % (name, wh.sum())
        updated = True

    if options.transfer:
        if len(images) != 2:
            parser.error(
                "The --transfer option requires exactly two input images.")
        if autoname:
            outname = "xfer_" + outname
        print "Transferring %s into coordinate system of %s" % (imagenames[1],
                                                                imagenames[0])
        images[0][0].data = images[1][0].data
        updated = True
    elif options.diff:
        if len(images) != 2:
            parser.error(
                "The --diff option requires exactly two input images.")
        if autoname:
            outname = "diff_" + outname
        print "Computing difference"
        data = images[0][0].data
        data -= images[1][0].data
        updated = True
    elif options.mean:
        if autoname:
            outname = "mean%d_" % len(images) + outname
        print "Computing mean"
        data = images[0][0].data
        for img in images[1:]:
            data += img[0].data
        data /= len(images)
        images = [images[0]]
        updated = True

    if options.zoom:
        z = options.zoom
        if autoname:
            outname = "zoom%d_" % z + outname
        if len(images) > 1:
            "Too many input images specified for this operation, at most 1 expected"
            sys.exit(2)
        data = images[0][0].data
        nx = data.shape[-2]
        ny = data.shape[-1]
        zdata = data[:, :, (nx - z) / 2:(nx + z) / 2,
                     (ny - z) / 2:(ny + z) / 2]
        #update header
        hdr = images[0][0].header
        wcs = WCS(hdr, mode="pyfits")
        cr1, cr2 = wcs.pix2wcs(ny / 2, nx / 2)
        hdr["CRVAL1"] = cr1
        hdr["CRVAL2"] = cr2
        hdr["CRPIX1"] = zdata.shape[-1] / 2
        hdr["CRPIX2"] = zdata.shape[-2] / 2

        print "Making zoomed image of shape", "x".join(map(str, zdata.shape))
        images = [pyfits.PrimaryHDU(zdata, hdr)]
        updated = True

    if options.rescale != 1:
        if autoname and not updated:
            outname = "rescale_" + outname
        if len(images) > 1:
            "Too many input images specified for this operation, at most 1 expected"
            sys.exit(2)
        print "Applying scaling factor of %f to image values" % options.rescale
        images[0][0].data *= options.rescale
        updated = True

    if updated:
        imagenames[0] = outname

    if options.stats:
        for ff, filename in zip(images, imagenames):
            data = ff[0].data
            min, max, dum1, dum2 = scipy.ndimage.measurements.extrema(data)
            sum = data.sum()
            mean = sum / data.size
            std = math.sqrt(((data - mean)**2).mean())
            print "%s: min %g, max %g, sum %g, np %d, mean %g, std %g" % (
                filename, min, max, sum, data.size, mean, std)
        sys.exit(0)

    if updated:
        print "Writing output image", outname
        if os.path.exists(outname) and not options.force:
            print "Output image exists, rerun with the -f switch to overwrite."
            sys.exit(1)
        images[0].writeto(outname, clobber=True)
    elif not (options.header or options.stack or options.add_axis
              or options.reorder):
        print "No operations specified. Use --help for help."
def correlation_factor(src, psf, img, pos_sky, step=None):

    image = pf.open(img)
    psf_image = pf.open(psf)

    hdr = image[0].header
    psf_hdr = psf_image[0].header

    ndim = len(image[0].shape)
    if ndim == 4:
        image_data = image[0].data[0, 0, :, :]
        psf_data_ = psf_image[0].data[0, 0, :, :]
    if ndim == 3:
        image_data = image[0].data[0, :, :]
        psf_data_ = psf_image[0].data[0, :, :]
    if ndim == 2:
        image_data = image[0].data[:, :]
        psf_data_ = psf_image[0].data[:, :]
    elif ndim < 2:
        raise ValueError('The FITS file needs at least two dimensions')
    elif ndim > 4:
        raise ValueError('FITS file has more than 4 axes. Aborting')

    # image padding
    n_pix = hdr['NAXIS1']

    padding = np.zeros([n_pix + 2.0 * step, n_pix + 2.0 * step])
    padding[step:-step, step:-step] = image_data
    hdr['CRPIX1'], hdr['CRPIX2'] = (n_pix + 2.0 * step) / 2.0, (
        n_pix + 2.0 * step) / 2.0
    # taking the positions

    wcs = WCS(hdr, mode='pyfits')

    pos_sky = [pos_sky]
    pos = [wcs.wcs2pix(*position) for position in pos_sky]
    step = [step, step]

    wcs_psf = WCS(psf_hdr, mode='pyfits')

    #pf.writeto('expore.fits', padding, header=hdr, clobber=True)
    center_psf_ra0, center_psf_dec0 = wcs_psf.wcs2pix(psf_hdr['CRVAL1'],
                                                      psf_hdr['CRVAL2'])
    psf_region = psf_data_[center_psf_dec0 - step[0]:center_psf_dec0 + step[0],
                           center_psf_ra0 - step[1]:center_psf_ra0 + step[1]]

    psf_data = psf_region.flatten()

    for (ra, dec) in pos:
        data = padding[dec - step[0]:dec + step[0], ra - step[1]:ra + step[1]]
    data_region = data.flatten()

    #if len(data_region) and len(psf_data) > 1:
    if data_region.shape == psf_data.shape:
        # computing th correlation matrix and cf is the correlation factor
        cmatrix = np.corrcoef((data_region, psf_data))
        if len(cmatrix) > 0:

            cf = (np.diag((np.rot90(cmatrix))**2).sum())**0.5 / 2**0.5
    else:
        cf = 0.0000001
    src.setTag('cf', cf)

    return cf
Esempio n. 15
0
    def __init__(self,
                 imagename,
                 psfname,
                 pmodel,
                 nmodel,
                 local_step=10,
                 snr_thresh=40,
                 high_corr_thresh=0.5,
                 negdetec_region=10,
                 negatives_thresh=5,
                 phasecenter_excl_radius=None,
                 prefix=None,
                 loglevel=0):
        """ Determines sources that require direction-dependent (DD)
            calibration solutions.

        psfname: PSF fits data

        pmodel: Model of the positive image.

        nmodel: Model of the negative image
 
        header: The header of the input image

        snr_thresh: float, optional. Default is 40.
             Any source with 40 x the minimum SNR.

        high_corr_thresh:  float, optional. Default is 0.5.
             Sources of high PSF correlation have correlation above 0.5.

        negdetec_region:  float, optional. Default is 10.
             Region to lookup for negative detections around. In beam size.

        negative_thresh:  float, optional. Default is 5.
             The number of nearby negative detections. Sources
             with number > 5 require direction
             dependent (DD) calibration solutions.

        phasecenter_excl_region:  float (in degrees), optional.
             A radius from the phase center (in beam sizes) to exclude the sources
             from the evaluation.

        prefix: str, optional. Sets a prefix to the output directory.

        loglevel: int, optional. Default 0. Python logging.
                  0, 1, 2, 3 for info, debug, error and 
                  critical, respectively.
        """

        self.loglevel = loglevel
        self.prefix = prefix
        self.log = utils.logger(self.loglevel, prefix=self.prefix)
        #image and psf image

        self.pmodel = pmodel
        self.nmodel = nmodel
        self.psfname = psfname

        self.log.info(" Loading image data")

        # tags
        self.dd_tag = "dE"

        # thresholds
        self.snr_factor = snr_thresh
        #self.localthresh = local_thresh
        self.corrthresh = high_corr_thresh
        self.negthresh = negatives_thresh

        with pyfits.open(imagename) as hdu:
            self.hdr = hdu[0].header
            self.data = utils.image_data(hdu[0].data)

        self.wcs = WCS(self.hdr, mode="pyfits")

        self.locstep = local_step

        #regions
        self.phaserad = phasecenter_excl_radius  # radius from the phase center
        self.negregion = negdetec_region  # region to look for negatives

        # conversion
        self.bmaj = self.hdr["BMAJ"]  # in degrees

        self.ra0 = numpy.deg2rad(self.hdr["CRVAL1"])
        self.dec0 = numpy.deg2rad(self.hdr["CRVAL2"])
Esempio n. 16
0
class load(object):
    def __init__(self,
                 imagename,
                 psfname,
                 pmodel,
                 nmodel,
                 local_step=10,
                 snr_thresh=40,
                 high_corr_thresh=0.5,
                 negdetec_region=10,
                 negatives_thresh=5,
                 phasecenter_excl_radius=None,
                 prefix=None,
                 loglevel=0):
        """ Determines sources that require direction-dependent (DD)
            calibration solutions.

        psfname: PSF fits data

        pmodel: Model of the positive image.

        nmodel: Model of the negative image
 
        header: The header of the input image

        snr_thresh: float, optional. Default is 40.
             Any source with 40 x the minimum SNR.

        high_corr_thresh:  float, optional. Default is 0.5.
             Sources of high PSF correlation have correlation above 0.5.

        negdetec_region:  float, optional. Default is 10.
             Region to lookup for negative detections around. In beam size.

        negative_thresh:  float, optional. Default is 5.
             The number of nearby negative detections. Sources
             with number > 5 require direction
             dependent (DD) calibration solutions.

        phasecenter_excl_region:  float (in degrees), optional.
             A radius from the phase center (in beam sizes) to exclude the sources
             from the evaluation.

        prefix: str, optional. Sets a prefix to the output directory.

        loglevel: int, optional. Default 0. Python logging.
                  0, 1, 2, 3 for info, debug, error and 
                  critical, respectively.
        """

        self.loglevel = loglevel
        self.prefix = prefix
        self.log = utils.logger(self.loglevel, prefix=self.prefix)
        #image and psf image

        self.pmodel = pmodel
        self.nmodel = nmodel
        self.psfname = psfname

        self.log.info(" Loading image data")

        # tags
        self.dd_tag = "dE"

        # thresholds
        self.snr_factor = snr_thresh
        #self.localthresh = local_thresh
        self.corrthresh = high_corr_thresh
        self.negthresh = negatives_thresh

        with pyfits.open(imagename) as hdu:
            self.hdr = hdu[0].header
            self.data = utils.image_data(hdu[0].data)

        self.wcs = WCS(self.hdr, mode="pyfits")

        self.locstep = local_step

        #regions
        self.phaserad = phasecenter_excl_radius  # radius from the phase center
        self.negregion = negdetec_region  # region to look for negatives

        # conversion
        self.bmaj = self.hdr["BMAJ"]  # in degrees

        self.ra0 = numpy.deg2rad(self.hdr["CRVAL1"])
        self.dec0 = numpy.deg2rad(self.hdr["CRVAL2"])

    def number_negatives(self, source):

        #sources = filter(lambda src: src.getTag(tag), psources)

        tolerance = numpy.deg2rad(self.negregion * self.bmaj)

        if self.phaserad:
            radius = numpy.deg2rad(self.phaserad * self.bmaj)

        ra, dec = source.pos.ra, source.pos.dec  # in radians
        within = self.nmodel.getSourcesNear(ra, dec, tolerance)
        if len(within) >= self.negthresh:
            if self.phaserad:
                if dist(self.ra0, self.dec0, ra, dec)[0] > radius:
                    source.setTag(self.dd_tag, True)
            else:
                source.setTag(self.dd_tag, True)

    def local_noise(self, pos):

        # computing the local noise using MAD
        x, y = pos

        sub_data = self.data[y - self.locstep:y + self.locstep,
                             x - self.locstep:x + self.locstep]
        noise = numpy.mean(abs(sub_data - numpy.mean(sub_data)))
        return noise

    def source_selection(self):

        sources = self.pmodel.sources
        noise, mean = utils.negative_noise(self.data, self.prefix)
        for srs in sources:
            pos = map(lambda rad: numpy.rad2deg(rad),
                      (srs.pos.ra, srs.pos.dec))
            positions = self.wcs.wcs2pix(*pos)

            # local noise, determining SNR using the local noise
            # threshold is determined using the global noise
            local_noise = self.local_noise(positions)
            signal_to_noise = srs.flux.I / local_noise
            thresh = self.snr_factor * noise

            if signal_to_noise > thresh and srs.rel > 0.99:
                if self.psfname:
                    corr = srs.correlation_factor
                    if corr > self.corrthresh:
                        self.number_negatives(srs)

                if not self.psfname:
                    self.number_negatives(srs)

        return self.pmodel, self.nmodel
Esempio n. 17
0
 def accept(self):
     """Tries to make a brick, and closes the dialog if successful."""
     sources = [src for src in self.model.sources if src.selected and src.typecode == 'pnt']
     filename = self.wfile.filename()
     if not self._fileSelected(filename):
         return
     # get PB expression
     pbfunc = None
     if self.wpb_apply.isChecked():
         pbexp = str(self.wpb_exp.text())
         try:
             pbfunc = eval("lambda r,fq:" + pbexp)
         except Exception as err:
             QMessageBox.warning(self, "Error parsing PB experssion",
                                 "Error parsing primary beam expression %s: %s" % (pbexp, str(err)))
             return
     # get frequency
     freq = str(self.wfreq.text())
     freq = float(freq) * 1e+6 if freq else None
     # get pad factor
     pad = str(self.wpad.text())
     pad = max(float(pad), 1) if pad else 1
     # read fits file
     busy = BusyIndicator()
     try:
         input_hdu = pyfits.open(filename)[0]
     except Exception as err:
         busy = None
         QMessageBox.warning(self, "Error reading FITS", "Error reading FITS file %s: %s" % (filename, str(err)))
         return
     # reset data if asked to
     if self.woverwrite.isChecked():
         input_hdu.data[...] = 0
     # insert sources
     Imaging.restoreSources(input_hdu, sources, 0, primary_beam=pbfunc, freq=freq)
     # save fits file
     try:
         # pyfits seems to produce an exception:
         #         TypeError: formatwarning() takes exactly 4 arguments (5 given)
         # when attempting to overwrite a file. As a workaround, remove the file first.
         if os.path.exists(filename):
             os.remove(filename)
         input_hdu.writeto(filename)
     except Exception as err:
         traceback.print_exc()
         busy = None
         QMessageBox.warning(self, "Error writing FITS", "Error writing FITS file %s: %s" % (filename, str(err)))
         return
     changed = False
     sources = self.model.sources
     # remove sources from model if asked to
     if self.wdel.isChecked():
         sources = [src for src in sources if not (src.selected and src.typecode == 'pnt')]
         changed = True
     # add image to model if asked to
     if self.wadd.isChecked():
         hdr = input_hdu.header
         # get image parameters
         max_flux = float(input_hdu.data.max())
         wcs = WCS(hdr, mode='pyfits')
         # Get reference pixel coordinates
         # wcs.getCentreWCSCoords() doesn't work, as that gives us the middle of the image
         # So scan the header to get the CRPIX values
         ra0 = dec0 = 1
         for iaxis in range(hdr['NAXIS']):
             axs = str(iaxis + 1)
             name = hdr.get('CTYPE' + axs, axs).upper()
             if name.startswith("RA"):
                 ra0 = hdr.get('CRPIX' + axs, 1) - 1
             elif name.startswith("DEC"):
                 dec0 = hdr.get('CRPIX' + axs, 1) - 1
         # convert pixel to degrees
         ra0, dec0 = wcs.pix2wcs(ra0, dec0)
         ra0 *= DEG
         dec0 *= DEG
         sx, sy = wcs.getHalfSizeDeg()
         sx *= DEG
         sy *= DEG
         nx, ny = input_hdu.data.shape[-1:-3:-1]
         # check if this image is already contained in the model
         for src in sources:
             if isinstance(getattr(src, 'shape', None), ModelClasses.FITSImage) and os.path.samefile(
                     src.shape.filename, filename):
                 # update source parameters
                 src.pos.ra, src.pos.dec = ra0, dec0
                 src.flux.I = max_flux
                 src.shape.ex, src.shape.ey = sx, sy
                 src.shape.nx, src.shape.ny = nx, ny
                 src.shape.pad = pad
                 break
         # not contained, make new source object
         else:
             pos = ModelClasses.Position(ra0, dec0)
             flux = ModelClasses.Flux(max_flux)
             shape = ModelClasses.FITSImage(sx, sy, 0, os.path.basename(filename), nx, ny, pad=pad)
             img_src = SkyModel.Source(os.path.splitext(os.path.basename(filename))[0], pos, flux, shape=shape)
             sources.append(img_src)
         changed = True
     if changed:
         self.model.setSources(sources)
         self.model.emitUpdate(SkyModel.SkyModel.UpdateAll, origin=self)
     self.parent().showMessage("Wrote %d sources to FITS file %s" % (len(sources), filename))
     busy = None
     return QDialog.accept(self)
Esempio n. 18
0
class load(object):


    def __init__(self, imagename, psfname, pmodel, nmodel, local_step=10,
                 snr_thresh=40, high_corr_thresh=0.5, negdetec_region=10,
                 negatives_thresh=5, phasecenter_excl_radius=None,
                 prefix=None, loglevel=0):


        """ Determines sources that require direction-dependent (DD)
            calibration solutions.

        psfname: PSF fits data

        pmodel: Model of the positive image.

        nmodel: Model of the negative image
 
        header: The header of the input image

        snr_thresh: float, optional. Default is 40.
             Any source with 40 x the minimum SNR.

        high_corr_thresh:  float, optional. Default is 0.5.
             Sources of high PSF correlation have correlation above 0.5.

        negdetec_region:  float, optional. Default is 10.
             Region to lookup for negative detections around. In beam size.

        negative_thresh:  float, optional. Default is 5.
             The number of nearby negative detections. Sources
             with number > 5 require direction
             dependent (DD) calibration solutions.

        phasecenter_excl_region:  float (in degrees), optional.
             A radius from the phase center (in beam sizes) to exclude the sources
             from the evaluation.

        prefix: str, optional. Sets a prefix to the output directory.

        loglevel: int, optional. Default 0. Python logging.
                  0, 1, 2, 3 for info, debug, error and 
                  critical, respectively.
        """

        self.loglevel = loglevel
        self.prefix = prefix
        self.log = utils.logger(self.loglevel, prefix=self.prefix)
        #image and psf image

        self.pmodel = pmodel
        self.nmodel = nmodel
        self.psfname =  psfname
  
        self.log.info(" Loading image data")

        # tags
        self.dd_tag = "dE"

        # thresholds
        self.snr_factor = snr_thresh
        #self.localthresh = local_thresh
        self.corrthresh = high_corr_thresh
        self.negthresh = negatives_thresh
        
        with pyfits.open(imagename) as hdu:
            self.hdr = hdu[0].header
            self.data = utils.image_data(hdu[0].data)

        self.wcs = WCS(self.hdr, mode="pyfits")
        
        self.locstep = local_step

        #regions
        self.phaserad = phasecenter_excl_radius # radius from the phase center
        self.negregion =  negdetec_region # region to look for negatives
        
       # conversion
        self.bmaj = self.hdr["BMAJ"] # in degrees
        
        self.ra0 =  numpy.deg2rad(self.hdr["CRVAL1"])
        self.dec0 = numpy.deg2rad(self.hdr["CRVAL2"])
        

    def number_negatives(self, source):
        

        #sources = filter(lambda src: src.getTag(tag), psources)

        tolerance = numpy.deg2rad(self.negregion * self.bmaj)

        if self.phaserad:
            radius = numpy.deg2rad(self.phaserad * self.bmaj)
            
        
        ra, dec = source.pos.ra, source.pos.dec # in radians
        within = self.nmodel.getSourcesNear(ra, dec, tolerance)    
        if len(within) >= self.negthresh:
            if self.phaserad:
                if dist(self.ra0, self.dec0, ra, dec)[0] > radius: 
                        source.setTag(self.dd_tag, True)
            else:
                source.setTag(self.dd_tag, True)
                

    def local_noise(self, pos):
        
        # computing the local noise using MAD
        x, y = pos
        
        sub_data =  self.data[y-self.locstep:y+self.locstep,
                              x-self.locstep:x+self.locstep]
        noise = numpy.mean(abs(sub_data - numpy.mean(sub_data)))
        return noise
     

    def source_selection(self):
        
        sources = self.pmodel.sources
        noise, mean = utils.negative_noise(self.data, self.prefix)
        for srs in sources:
            pos = map(lambda rad: numpy.rad2deg(rad),(srs.pos.ra,srs.pos.dec))
            positions = self.wcs.wcs2pix(*pos)

			# local noise, determining SNR using the local noise
			# threshold is determined using the global noise 
            local_noise = self.local_noise(positions)
            signal_to_noise = srs.flux.I/local_noise
            thresh = self.snr_factor * noise

            if signal_to_noise > thresh and srs.rel > 0.99:
                if self.psfname:
                    corr = srs.correlation_factor
                    if corr > self.corrthresh:
                        self.number_negatives(srs)

                if not self.psfname:
                    self.number_negatives(srs)
                            
        return self.pmodel, self.nmodel
Esempio n. 19
0
 def accept(self):
     """Tries to add brick, and closes the dialog if successful."""
     filename = self.wfile.filename()
     # read fits file
     busy = BusyIndicator()
     try:
         input_hdu = pyfits.open(filename)[0]
     except Exception as err:
         busy.reset_cursor()
         QMessageBox.warning(
             self, "Error reading FITS",
             "Error reading FITS file %s: %s" % (filename, str(err)))
         return
     # check name
     srcname = str(self.wname.text()) or os.path.splitext(
         os.path.basename(str(filename)))[0]
     if srcname in set([src.name for src in self.model.sources]):
         QMessageBox.warning(
             self, "Already in model",
             "<p>The model already contains a source named '%s'. Please select a different name.</p>"
             % srcname)
         return
     # get image parameters
     hdr = input_hdu.header
     max_flux = float(input_hdu.data.max())
     wcs = WCS(hdr, mode='pyfits')
     # Get reference pixel coordinates
     # wcs.getCentreWCSCoords() doesn't work, as that gives us the middle of the image
     # So scan the header to get the CRPIX values
     ra0 = dec0 = 1
     for iaxis in range(hdr['NAXIS']):
         axs = str(iaxis + 1)
         name = hdr.get('CTYPE' + axs, axs).upper()
         if name.startswith("RA"):
             ra0 = hdr.get('CRPIX' + axs, 1) - 1
         elif name.startswith("DEC"):
             dec0 = hdr.get('CRPIX' + axs, 1) - 1
     # convert pixel to degrees
     #    print ra0,dec0
     ra0, dec0 = wcs.pix2wcs(ra0, dec0)
     ra0 *= DEG
     dec0 *= DEG
     #    print ModelClasses.Position.ra_hms_static(ra0)
     #    print ModelClasses.Position.dec_sdms_static(dec0)
     sx, sy = wcs.getHalfSizeDeg()
     sx *= DEG
     sy *= DEG
     nx, ny = input_hdu.data.shape[-1:-3:-1]
     pos = ModelClasses.Position(ra0, dec0)
     flux = ModelClasses.Flux(max_flux)
     shape = ModelClasses.FITSImage(sx,
                                    sy,
                                    0,
                                    os.path.basename(filename),
                                    nx,
                                    ny,
                                    pad=float(str(self.wpad.text()) or "1"))
     img_src = SkyModel.Source(srcname, pos, flux, shape=shape)
     self.model.setSources(self.model.sources + [img_src])
     self.model.emitUpdate(SkyModel.SkyModel.UpdateAll, origin=self)
     busy.reset_cursor()
     return QDialog.accept(self)
Esempio n. 20
0
def local_variance(imagedata, header, catalog, wcs=None,
                   pixelsize=None, tag=None,local_region=5,
                   noise=None, savefig=True, highvariance_factor=0.8,
                   high_local_tag=None, neg_side=False, 
                   setatr=True, do_high_loc=False, prefix=None):

    """ Calculates the local varience (lv) around a source on 
        one side of interest. 
 
    imagedata : Reshaped Fits data
    header : Image header
    catalog : Source catalog, in Tigger format.
        Source model to compute local variance around.

    tag : str, optional.
        if specified then the local variance will be
        computed for only a subset of sources with a tag,
        e.g., 'tag=snr'.
    header : Fits header e.g., img=pyfits.open("test.fits")
        header=img[0].header
    wcs :This class provides methods
        for accessing information from the World  Coordinate System
        (WCS) contained in the header of a FITS image. Conversions 
        between pixel and WCS coordinates can also be performed.
        If not provided it is directly obtained from the Fits header
        provided.
    pixelsize: float, Default is None.
         If not provided then it is directly obtained form a Fits header.
    local_region: int, optional. A default value of 5. 
        Gives a region to compute the local variance in
        psf sizes, e.g, 'local_region = 2',
        then a region (= 2 * beam size) around a source is used.

    highvariance_factor: float, optional. A default value of 0.8. 
        If highvariance_factor=0.8 is given this means that
        the sources with local variance greater than  
        0.8*image_noise will be tagged 'high_var' if
        high_local_tag=None.

    high_local_tag : str, optional. A default tag None.
        Tag assigned to sources of high local variance.
        If None is provided the default tag will be used
        i.e 'high_var'. Else it uses the specified tag.

    setatr : bool, optional. Default is True.
        If True all sources will be tagged with 'l'
        giving each detection an extra local variance
        parameter.

    do_high_loc : bool, optional. Default is False.
        If True sources with high local variance will be tagged
        using 'localvariance_tag' (see above).
    prefix :  str, optional. Default is None.
    """

    data = imagedata
    beam = header["BMAJ"]

    if pixelsize is None:
        pixelsize = abs(header["CDELT1"])
    if wcs is None:
        wcs = WCS(header, mode="pyfits")

    bmaj = int(round(beam/pixelsize)) # beam size in pixels

    log = logger(level=0, prefix=prefix)
    if not isinstance(local_region, int):
        if isinstance(local_region, float):
            local_region = int(round(local_region))
            log.debug("Float is provided and int is required,"
                       "arounding offto the nearest integer")
            if local_region == 0:
                log.error("It rounded off to zero now,"
                          "change local_region into an integer."
                          "Aborting")
        else:
            log.error("local_region must be an integer. Abort")
    
    step = local_region * bmaj

    noise = noise or negative_noise(data)
    
    model = Tigger.load(catalog)
    sources = []

    if tag:
        log.debug("Local variance is only computed"
                  "for sources with a tag %s are"%
                   tag)
        sources = filter(lambda src: src.getTag(tag), model.sources) 
    else:
        for src in model.sources:
            sources.append(src)
    
    positions_sky = [map(lambda rad: numpy.rad2deg(rad),
                    (src.pos.ra,src.pos.dec)) for src in sources]
    positions = [wcs.wcs2pix(*pos) for pos in positions_sky]

    shape = data.shape 
    ndim = len( data.shape)
    if ndim == 4:
        data = data[0,0,...]
    if ndim == 3:
        data = data[0,...]
 
    step = [step, step]
    
    m = 0 
    for i, (pos, src) in enumerate(zip(positions, sources)):
        x,y = pos
        if x>shape[-2] or y>shape[-1] or numpy.array(pos).any()<0:
            positions.remove(pos)
            model.sources.remove(src)
            sources.remove(src)
            m += 1

        if (y+step[1] > shape[-1]) or (y-step[1] < 0):
            if pos in positions:
                positions.remove(pos)
                model.sources.remove(src)
                sources.remove(src)
            m += 1

        if (x+step[0] > shape[-2]) or (x-step[0] < 0):
            if pos in positions:
                positions.remove(pos)
                model.sources.remove(src)
                sources.remove(src)
            m += 1
    if m > 0:
        log.debug("It will be useful to increase the image size,"
                  "sources with ra+step or dec+step > image size"
                  "are removed")
        
    _std = []
    
    if neg_side:
        data = -data
        log.debug("Using the negative side of the provided image.")
 
    n = 0
    for (x, y), srs in zip(positions, sources):
        pos = [x,y]
        subrgn = data[y-step[0] : y+step[0], x-step[1] : x+step[1]]
        subrgn = subrgn[subrgn > 0]
        std = subrgn.std()

        if math.isnan(float(std)) or _std == 0:
            sources.remove(srs)
            model.sources.remove(srs)
            positions.remove(srs)
            n += 1
        else:
            _std.append(std)
            if setatr:
                srs.setAttribute("l", std)
        
    if n > 0:
        log.debug("Nan encountered %d times. Increase the size of the"
                  "region or check the image. Otherwise sources with"
                  "0 or nan are flagged." %n)


    def high_variance_sources(
            pos, local_variance, noise, model, threshold,
            savefig=savefig, prefix=None, localtag=None):

        if savefig:
            save_fig = prefix + "_variance.png" or catalog.replace(
                                                      ".lsm.html", ".png")

        local = [l/1.0e-6 for l in local_variance]
        x = numpy.arange(len(pos))
        pylab.figure()
        pylab.plot(x, local)
        pylab.plot([noise/1.0e-6] * len(local_variance))

        localtag = localtag     
        for i, (pos, src) in enumerate(zip( pos, model.sources)):
            if _std[i] > threshold:
                src.setTag(localtag, True)
                pylab.plot(x[i], local[i], "rD")
                pylab.annotate(src.name, xy=(x[i],local[i]))
        if savefig:
            pylab.ylabel("local variance[$\mu$]")
            pylab.savefig(save_fig)

    if high_local_tag is None:
         high_local_tag = "high_var"
    if do_high_loc:
        threshold = highvariance_factor * noise
        high_variance_sources(positions, _std, noise, model,
                              threshold=threshold, savefig=savefig,
                              prefix=prefix, localtag=high_local_tag)
    model.save(catalog)   

    return _std 
Esempio n. 21
0
class load(object):


    def __init__(self, imagename, psfname=None, sourcefinder_name='pybdsm',
	             saveformat="gaul", makeplots=True, do_psf_corr=True, 
				 do_local_var=True, psf_corr_region=5, local_var_region=10,
				 rel_excl_src=None, pos_smooth=2, neg_smooth=2, loglevel=0, 
				 thresh_pix=5, thresh_isl=3, neg_thresh_isl=3,
				 neg_thresh_pix=5, reset_rel=None, prefix=None, 
				 do_nearsources=False, savefits=False, 
				 increase_beam_cluster=False, savemask_pos=False,
				 savemask_neg=False, no_smooth=True, **kw):

        """ Takes in image and extracts sources and makes 
            reliability estimations..
           
		
        imagename: Fits image
        psfname: PSF fits image, optional. 

        sourcefinder_name: str, optional. Default 'pybdsm'.
            Uses source finder specified.

        makeplots: bool, optional. Default is True.
            Make reliability plots.

        do_psf_corr : bool, optional. Default True.
            If True, PSF correlation will be added
            as an extra parameter for density estimations.
            NB: the PSF fits image must be provided.

        do_local_var : bool, optional. Default is True.
            If True, adds local variance as an extra parameter,
            for density estimations. 
        
        do_nearsources: boolean. Default is False.
            If true it adds number of nearest neighnours as an extra
            parameter. It looks for sources around 5 beam sizes.

        psf_corr_region : int, optional. Default value is 5. 
            Data size to correlate around a source, in beam sizes.
 
        local_var_region: int, optional. Default 10.
            Data size to compute the local variance in beam sizes.

        rel_excl_src : floats, optional. Default is None. 
            Excludes sources in a specified region
            e.g ra, dec, radius in degrees. For
            2 regions: ra1, dec1, radius1: ra2, dec2, radius2, etc.

        pos_smooth : float, optional. Default 2.
            Masking threshold for the positive image.
            For default value 2, data peaks < 2 * image noise
            are masked.

        neg_smooth : float, optional. Default 2.
            Similar to pos_smooth but applied to the negative image.

        thresh_isl :  float, optional. Default is 3.
            Threshold for forming islands in the positive image

        thresh_pix : float, optional. Default is 5.
            Threshold for model fitting, in positive image.

        neg_thresh_isl : float, optional. Default is 3. 
            Simialr to thresh_isl but for negative image.

        neg_thresh_pix : float, optional. Default is 5. 
            Similar to thresh_pix but for negative image.

        savefits: boolean. Default is False.
            If True a negative image is saved.

        reset_rel: boolean. Default is False. If true then
            sources with correlation < 0.002 and rel >0.60
            have their reliabilities set to 0.

        increase_beam_cluster: boolean, optional. If True, sources
            groupings will be increase by 20% the beam size. If False,
            the actual beam size will be used. Default is False.

        savemask_pos: boolean, optional. If true the mask applied on 
            the positive side of an image after smoothing is saved.
            
        savemask_neg: Similar to savemask_pos but for the negative
            side of an image.
        
        loglevel : int, optional. Default is 0.
            Provides Pythonlogging options, 0, 1, 2 and 3 are for info, debug,
            error and critial respectively.
   
         kw : kward for source extractions. Should be a mapping e.g
            kw['thresh_isl'] = 2.0 or kw['do_polarization'] = True 
        """


        #
        self.smoothing = not no_smooth
       
        self.prefix = prefix

        # log level  
        self.loglevel = loglevel
        self.log = utils.logger(self.loglevel, prefix=self.prefix)

        
        # image, psf image
        self.imagename = imagename
        self.psfname = psfname 

        with pyfits.open(imagename) as hdu:
            self.header = hdu[0].header
            self.wcs = WCS(self.header, mode="pyfits")
            self.pixelsize = abs(self.header["cdelt1"])
      
        self.bmaj = numpy.deg2rad(self.header["BMAJ"])

        # boolean optionals    
        self.makeplots = makeplots
        self.do_local_var = do_local_var
        self.nearsources = do_nearsources
        self.do_psf_corr = do_psf_corr
        self.savemaskpos = savemask_pos
        self.savemaskneg = savemask_neg
        self.savefits = savefits
        self.derel = reset_rel
        self.log.info("Catalogues will be saved as %s, where srl is source "
					  " and gaul is Gaussians. "%saveformat)
        self.catalogue_format = "." + saveformat
        if not self.psfname:
            self.log.info(" No psf provided, do_psf_corr is set to False.")
            self.do_psf_corr = False

 
        # computing negative noise
        self.noise, self.mean = utils.negative_noise(self.imagename, self.prefix)
        
        self.log.info(" The negative noise is %e Jy/beam"%self.noise)
        if self.noise == 0: 
            self.log.debug(" The negative noise is 0, check image")

        # source finder initialization
        self.sourcefinder_name  = sourcefinder_name
        self.log.info(" Using %s source finder to extract the sources."%
                      self.sourcefinder_name)

        self.negimage = self.prefix + "_negative.fits"
        utils.invert_image(self.imagename, self.negimage)  
       
        # smoothing factors
        self.pos_smooth = pos_smooth
        self.neg_smooth = neg_smooth
        
        # region to evaluate
        self.corrstep = psf_corr_region
        self.localstep = local_var_region
        self.radiusrm = rel_excl_src
        self.do_beam = increase_beam_cluster
         
        beam_pix = int(round(numpy.rad2deg(self.bmaj)/self.pixelsize))
        self.locstep = self.localstep * beam_pix
        self.cfstep = self.corrstep * beam_pix
        self.bmin, self.bpa =  self.header["BMIN"], self.header["BPA"]

        self.opts_pos = {}
        if self.do_beam:
            bmaj = self.header["BMAJ"]
            self.opts_pos["beam"] = (1.2*bmaj, 1.2*self.bmin, self.bpa)

        # Pybdsm or source finder fitting thresholds
        self.thresh_isl = thresh_isl
        self.thresh_pix = thresh_pix
        self.opts_pos = dict(thresh_pix=self.thresh_pix,
                             thresh_isl=self.thresh_isl)
        
        self.opts_pos.update(kw)
        self.opts_neg = {}
        self.opts_neg.update(kw)
        self.neg_thresh_isl = neg_thresh_isl
        self.neg_thresh_pix = neg_thresh_pix
        self.opts_neg["thresh_isl"] = self.neg_thresh_isl
        self.opts_neg["thresh_pix"] = self.neg_thresh_pix
 
     
    def source_finder(self, image=None, thresh=None, prefix=None,
                      noise=None, output=None, savemask=None, **kw):
        
        #kw.update(kwards)
        tpos = None
        naxis = self.header["NAXIS1"] 
        boundary = numpy.array([self.locstep, self.cfstep])
        #trim_box = (boundary.max(), naxis - boundary.max(),
        #          boundary.max(), naxis - boundary.max())
        trim_box = None
        # data smoothing
        if self.smoothing:

            ext = utils.fits_ext(image)
            tpos = tempfile.NamedTemporaryFile(suffix="."+ext, dir=".")
            tpos.flush()

            mask, noise = utils.thresh_mask(image, tpos.name,
                          thresh=thresh, noise=self.noise, 
                          sigma=True, smooth=True, prefix=prefix, 
                          savemask=savemask)

            # using the masked image for forming islands
            kw["detection_image"] = tpos.name
            kw["blank_limit"] = self.noise/1.0e5

        # source extraction
        utils.sources_extraction(
             image=image, output=output, 
             sourcefinder_name=self.sourcefinder_name,
             trim_box=trim_box,
             prefix=self.prefix, **kw)

        if tpos:
            tpos.close()


    def remove_sources_within(self, model):
        
        sources = model.sources
        rel_remove = self.radiusrm[0].split(":")
        for i in range(len(rel_remove)):
                ra, dec, tolerance = rel_remove[i].split(",")
                ra_r =  numpy.deg2rad(float(ra))
                dec_r =  numpy.deg2rad(float(dec))
                tolerance_r = numpy.deg2rad(float(tolerance))
                within = model.getSourcesNear(ra_r, dec_r, tolerance_r)   
                for src in sorted(sources):
                    if src in within:
                         model.sources.remove(src)
        return model
    

    def params(self, modelfits):
     
        # reads in source finder output             
        with pyfits.open(modelfits) as hdu:
            data = hdu[1].data

        tfile = tempfile.NamedTemporaryFile(suffix=".txt")
        tfile.flush() 

        # writes a catalogue in a temporaty txt file
        with open(tfile.name, "w") as std:
            std.write("#format:name ra_rad dec_rad i emaj_r emin_r pa_r\n")

        model = Tigger.load(tfile.name) # open a tmp. file
    
        peak, total, area, loc, corr = [], [], [], [], []
        for i in range(len(data)):
            flux = data["Total_flux"][i] 
            dc_emaj, dc_emin = data["DC_Maj"][i], data["DC_Min"][i]
            ra, dec = data["RA"][i], data["DEC"][i]
            pa = data["DC_PA"][i]
            name = "SRC%d"%i
            peak_flux = data["Peak_flux"][i]

            posrd =  ModelClasses.Position(numpy.deg2rad(ra), numpy.deg2rad(dec))
            flux_I = ModelClasses.Polarization(flux, 0, 0, 0)
            if dc_emaj == 0 and dc_emin == 0: 
                shape = None
            else:
                shape = ModelClasses.Gaussian(numpy.deg2rad(dc_emaj), numpy.deg2rad(dc_emin),
                                         numpy.deg2rad(pa))
            
            srs = SkyModel.Source(name, posrd, flux_I, shape=shape)
            
            # using convolved maj and min for reliability estimate
            emaj, emin = data["Maj"][i], data["Min"][i]

            # area: find ex and ey if are 0 assign beam size
            if emaj or emin == 0:
                srcarea = math.pi * (numpy.rad2deg(self.bmaj)) * pow(3600.0, 2) *\
                       (numpy.rad2deg(self.bmin)) 
            if  emaj and emin > 0: 
                srcarea = emaj * emin * math.pi * pow(3600.0, 2) # arcsecond
            
            # only accepts sources with flux > 0 and not nan RA and DEC
            # and local variance
            pos = [self.wcs.wcs2pix(*(ra, dec))][0] #positions from deg to pixel

            with pyfits.open(self.negimage) as hdu:
                negdata = utils.image_data( hdu[0].data )

            if flux > 0 and peak_flux > 0 and not math.isnan(float(ra))\
                and not math.isnan(float(dec)):

                  local = utils.compute_local_variance(negdata,
                            pos, self.locstep)

                  srs.setAttribute("local_variance", local)

                  
                  if not math.isnan(float(local)) or local  > 0:
                      if self.psfname:
                          pdata, psf = utils.compute_psf_correlation(self.imagename,
                                         self.psfname, pos, self.cfstep)

                          if len(pdata) == len(psf):
                              c_region = numpy.corrcoef((pdata, psf))
                              cf =  (numpy.diag((numpy.rot90(c_region))**2)
                                           .sum())**0.5/2**0.5

                              srs.setAttribute("correlation_factor", cf)
                              corr.append(cf)
                              model.sources.append(srs) 
                              peak.append(peak_flux)
                              total.append(flux)
                              area.append(srcarea)
                              loc.append(local)
                      else:
                          model.sources.append(srs) 
                          peak.append(peak_flux)
                          total.append(flux)
                          area.append(srcarea)
                          loc.append(local)
    
        labels = dict(size=(0, "Log$_{10}$(Source area)"), 
                      peak=(1, "Log$_{10}$( Peak flux [Jy] )"), 
                      tot=(2, "Log$_{10}$( Total flux [Jy] )"))

        if self.do_psf_corr:
            labels.update( {"coeff":(len(labels),
                            "Log$_{10}$ (CF)")})
        if self.do_local_var:
            labels.update( {"local": (len(labels),
                            "Log$_{10}$(Local Variance)")})
        if self.nearsources:
            labels.update( {"near": (len(labels),
                            "Log$_{10}$(Near Sources)")})

        nsrc = len(model.sources)
        out = numpy.zeros([nsrc, len(labels)])         
         
        # returning parameters
        for i, src in enumerate(model.sources):

            ra, dec = src.pos.ra, src.pos.dec
            near = model.getSourcesNear(ra, dec, 5 * self.bmaj)
            nonear = len(near) 
            if self.nearsources:
                src.setAttribute("neibours", nonear)

            if self.do_psf_corr and self.do_local_var and self.nearsources:
                 out[i,...] =  area[i], peak[i], total[i], corr[i], loc[i], nonear

            elif self.do_psf_corr and self.do_local_var and not self.nearsources:
                 out[i,...] =   area[i], peak[i], total[i] , corr[i], loc[i]
        
            elif self.do_psf_corr and self.nearsources and not self.do_local_var:
                out[i,...] =   area[i], peak[i], total[i] , corr[i], nonear
            
            elif not self.do_psf_corr and self.do_local_var and self.nearsources:
                out[i,...] =   area[i], peak[i], total[i] , loc[i], nonear
            
            elif self.do_psf_corr and not self.do_local_var and not self.nearsources:
                out[i,...] =   area[i], peak[i], total[i] , corr[i]
            
            elif not self.do_psf_corr and self.do_local_var and not self.nearsources:
                out[i,...] =   area[i], peak[i], total[i] , loc[i]
            
            elif not self.do_psf_corr and not self.do_local_var and self.nearsources:
                out[i,...] =   area[i], peak[i], total[i] , nonear

            else:
                out[i,...] =   area[i], peak[i], total[i]


        # removes the rows with 0s
        removezeros = (out == 0).sum(1)
        output = out[removezeros <= 0, :]
                  
        return model, numpy.log10(output), labels 


    def get_reliability(self):


        # finding sources 
        self.log.info(" Extracting the sources on both sides ")

        pfile = self.prefix + self.catalogue_format + ".fits"
        nfile = self.prefix + "_negative" + self.catalogue_format + ".fits"
        # i need to catch mmap.mmap error here

        # running a source finder
        self.source_finder(image=self.negimage,
                           output=nfile, thresh=self.neg_smooth,
                           savemask=self.savemaskneg,
                           prefix=self.prefix, **self.opts_neg)

        self.source_finder(image=self.imagename,
                           output=pfile, thresh=self.pos_smooth, 
                           savemask=self.savemaskpos,
                           prefix=self.prefix, **self.opts_pos)

        self.log.info(" Source Finder completed successfully ")


         
        pmodel, positive, labels = self.params(pfile)
        nmodel, negative, labels = self.params(nfile)
     
        # setting up a kernel, Gaussian kernel
        bandwidth = []

        for plane in negative.T:
            bandwidth.append(plane.std())

        nplanes = len(labels)
        cov = numpy.zeros([nplanes, nplanes])
        nnsrc = len(negative)
        npsrc = len(positive)
        
        self.log.info(" There are %d positive and %d negtive detections "%(npsrc, nnsrc))
 
        if nnsrc == 0 or npsrc ==0:
            self.log.error("The resulting array has length of 0 thus cannot compute"
                    " the reliability. Aborting.")

        self.log.info(" Computing the reliabilities ")
        for i in range(nplanes):
            for j in range(nplanes):
                if i == j:
                    cov[i, j] = bandwidth[i]*((4.0/((nplanes+2)*
                                  nnsrc))**(1.0/(nplanes+4.0)))
        self.log.info("The resulting covariance matrix is %r"%cov)

        pcov = utils.gaussian_kde_set_covariance(positive.T, cov)
        ncov = utils.gaussian_kde_set_covariance(negative.T, cov)
    
        # get number densities
        nps = pcov(positive.T) * npsrc
        nns = ncov(positive.T) * nnsrc

        # define reliability of positive catalog
        rel = (nps-nns)/nps
        for src, rf in zip(pmodel.sources, rel):
            src.setAttribute("rel", rf)
        self.log.info(" Saved the reliabilities values.")

        # remove sources with poor correlation and high reliability,
        # the values are currently arbitrary
        if self.do_psf_corr and self.derel:
            for s in pmodel.sources:
                cf, r = s.correlation_factor, s.rel
                if cf < 0.006 and r > 0.60:
                    s.rel = 0.0    

        if self.makeplots:
            savefig = self.prefix + "_planes.png"
            utils.plot(positive, negative, rel=rel, labels=labels,
                        savefig=savefig, prefix=self.prefix)

        # removes sources in a given radius from the phase center
        if self.radiusrm:
            self.log.info(" Remove sources ra, dec, radius of  %r" 
                          " from the phase center" %self.radiusrm)
            pmodel = self.remove_sources_within(pmodel)


        if not self.savefits:
            self.log.info(" Deleting the negative image.")
            os.system("rm -r %s"%self.negimage)

        # Set field Center
        pmodel.ra0, pmodel.dec0 = map(numpy.deg2rad, self.wcs.getCentreWCSCoords())

        return  pmodel, nmodel, self.locstep
Esempio n. 22
0
def main():

  # setup some standard command-line option parsing
  #
  from optparse import OptionParser
  parser = OptionParser(usage="""%prog: [options] <image names...>""");
  parser.add_option("-o","--output",dest="output",type="string",
                    help="name of output FITS file");
  parser.add_option("-r","--replace",action="store_true",
                    help="replace (first) input file by output. Implies '--force'.");
  parser.add_option("-f","--force",dest="force",action="store_true",
                    help="overwrite output file even if it exists");
  parser.add_option("-S","--sanitize",type="float",metavar="VALUE",
                    help="sanitize FITS files by replacing NANs and INFs with VALUE");
  parser.add_option("-N","--nonneg",action="store_true",
                    help="replace negative values by 0");
  parser.add_option("-m","--mean",dest="mean",action="store_true",
                    help="take mean of input images");
  parser.add_option("-d","--diff",dest="diff",action="store_true",
                    help="take difference of 2 input images");
  parser.add_option("-t","--transfer",action="store_true",
                    help="transfer data from image 2 into image 1, preserving the FITS header of image 1");
  parser.add_option("-z","--zoom",dest="zoom",type="int",metavar="NPIX",                    
                    help="zoom into central region of NPIX x NPIX size");
  parser.add_option("-R","--rescale",dest="rescale",type="float",
                    help="rescale image values");
  parser.add_option("-E","--edit-header",metavar="KEY=VALUE",type="string",action="append",
                    help="replace header KEY with VALUE. Use KEY=VALUE for floats and KEY='VALUE' for strings.");
  parser.add_option("--stack",metavar="outfile:axis",
                    help="Stack a list of FITS images along a given axis. This axis may given as an integer"
                    "(as it appears in the NAXIS keyword), or as a string (as it appears in the CTYPE keyword)")
  parser.add_option("--reorder",
                    help="Required order. List of comma seperated indeces")
  parser.add_option("--add-axis",metavar="CTYPE:CRVAL:CRPIX:CDELT[:CUNIT:CROTA]",action="append",default=[],
                    help="Add axis to a FITS image. The AXIS will be described by CTYPE:CRVAL:CRPIX:CDELT[:CUNIT:CROTA]. "
                    "The keywords in brackets are optinal, while those not in brackets are mendatory. "
                    "This axis will be the last dimension. Maybe specified more than once.")
  parser.add_option("--unstack",metavar="prefix:axis:each_chunk",
                    help="Unstack a FITS image into smaller chunks each having [each_chunk] planes along a given axis. "
                    "This axis may given as an integer (as it appears in the NAXIS keyword), or as a string "
                    "(as it appears in the CTYPE keyword)")
  parser.add_option("-H","--header",action="store_true",help="print header(s) of input image(s)");
  parser.add_option("-s","--stats",action="store_true",help="print stats on images and exit. No output images will be written.");

  parser.set_defaults(output="",mean=False,zoom=0,rescale=1,edit_header=[]);

  (options,imagenames) = parser.parse_args();
  
  if not imagenames:
    parser.error("No images specified. Use '-h' for help.");

  # print "%d input image(s): %s"%(len(imagenames),", ".join(imagenames));
  images = [ pyfits.open(img) for img in imagenames ];
  updated = False;

  # Stack FITS images
  if options.stack:
    if len(imagenames)<1:
      parser.error("Need more than one image to stack")
    stack_args = options.stack.split(":")
    if len(stack_args) != 2:
      parser.error("Two --stack options are required. See ./fitstool.py -h")
   
    outfile,axis = stack_args

    _string = True
    try:
      axis = int(axis)
      _string = False
    except ValueError:
      _string = True

    stack_planes(imagenames,ctype=axis if _string else None,keep_old=True,
                 axis=None if _string else axis,outname=outfile,fits=True)
  
  # Unstack FITS image
  if options.unstack:
    image = imagenames[0]
    if len(imagenames)<1:
      parser.error("Need more than one image to stack")
    unstack_args = options.unstack.split(":")
    if len(unstack_args) != 3:
      parser.error("Two --unstack options are required. See ./fitstool.py -h")

    prefix,axis,each_chunk = unstack_args
    
    _string = True
    try:
      axis = int(axis)
      _string = False
    except ValueError:
      _string = True

    each_chunk = int(each_chunk)
    
    unstack_planes(image,each_chunk,ctype=axis if _string else None,
            axis=None if _string else axis,prefix=prefix,fits=True)
    sys.exit(0)
  
  if options.add_axis:
    for axis in options.add_axis:
      hdu = pyfits.open(imagenames[0])
      hdr = hdu[0].header
      ndim = hdr["NAXIS"]
      hdu[0].data = hdu[0].data[numpy.newaxis,...]
      _mendatory = "CTYPE CRVAL CDELT CRPIX".split()
      _optional = "CUNIT CROTA".split()
      L = len(_mendatory+_optional)
      values = axis.split(":")
      Lv = len(values)

      if len(_mendatory)>len(values) :
        parser.error("Something with the way specified --add-axis. See %prog -h for help")

      for i,value in enumerate(values ) :
        try:
          value = float(value)
        except ValueError:
          if isinstance(value, str):
            value = value.upper()

        hdu[0].header["%s%d"%( (_mendatory+_optional)[i],ndim+1)] = value

      hdu.writeto(imagenames[0],clobber=True)
      print("Successfully added axis %s to %s"%(values[0],imagenames[0]))
        

  if options.reorder:
    for image in imagenames:
      order = map(int, options.reorder.split(","))
      reorder(image, order=order, outfile=image)
  
  if options.header:
    for filename,img in zip(imagenames,images):
      if len(imagenames)>1:
        print "======== FITS header for",filename;
      for hdrline in img[0].header.ascard:
        print hdrline; 
  
  if options.replace or len(imagenames)<2:
    if options.output:
      parser.error("Cannot combine -r/--replace with -o/--output");
    outname = imagenames[0];
    options.force = True;
    autoname = False;
  else:
    outname = options.output;
    autoname = not outname;
    if autoname:
      outname = re.split('[_]',imagenames[0],1)[-1];

  for keyval in options.edit_header:
    key,val = keyval.split("=");
    q = ''
    if val[0] == "'" and val[-1] == "'":
      images[0][0].header[key] = val[1:-1:];
      q = '"'
    elif val[-1] == 'd' or key.startswith('NAXIS'):
      images[0][0].header[key] = int(val[:-1] if val[-1]=='d' else val);
    else:
      try:
        images[0][0].header[key] = float(val);
      except:
        images[0][0].header[key] = val;
        q = '"';
    print "Setting header %s=%s%s%s"%(key,q,val,q);
    updated = True;

  if options.sanitize is not None:
    print "Sanitizing: replacing INF/NAN with",options.sanitize;
    for img in images:
      d = img[0].data;
      d[numpy.isnan(d)+numpy.isinf(d)] = options.sanitize;
    # if using stats, do not generate output
    if not options.stats:
      updated = True;

  if options.nonneg:
    print "Replacing negative value by 0";
    for img,name in zip(images,imagenames)[:1]:
      d = img[0].data;
      wh = d<0;
      d[wh] = 0;
      print "Image %s: replaced %d points"%(name,wh.sum());
    updated = True;

  if options.transfer:
    if len(images) != 2:
      parser.error("The --transfer option requires exactly two input images.");
    if autoname:
      outname = "xfer_" + outname;
    print "Transferring %s into coordinate system of %s"%(imagenames[1],imagenames[0]);
    images[0][0].data = images[1][0].data;
    updated = True;
  elif options.diff:
    if len(images) != 2:
      parser.error("The --diff option requires exactly two input images.");
    if autoname:
      outname = "diff_" + outname;
    print "Computing difference";
    data = images[0][0].data;
    data -= images[1][0].data;
    updated = True;
  elif options.mean:
    if autoname:
      outname = "mean%d_"%len(images) + outname;
    print "Computing mean";
    data = images[0][0].data;
    for img in images[1:]:
      data += img[0].data;
    data /= len(images);
    images = [ images[0] ];
    updated = True;

  if options.zoom:
    z = options.zoom;
    if autoname:
      outname = "zoom%d_"%z + outname;
    if len(images) > 1:
      "Too many input images specified for this operation, at most 1 expected";
      sys.exit(2);
    data = images[0][0].data;
    nx = data.shape[-2];
    ny = data.shape[-1];
    zdata = data[:,:,(nx-z)/2:(nx+z)/2,(ny-z)/2:(ny+z)/2];
    #update header
    hdr = images[0][0].header
    wcs = WCS(hdr, mode="pyfits")
    cr1, cr2 = wcs.pix2wcs(ny/2, nx/2)
    hdr["CRVAL1"] = cr1
    hdr["CRVAL2"] = cr2 
    hdr["CRPIX1"] = zdata.shape[-1]/2
    hdr["CRPIX2"] = zdata.shape[-2]/2
    
    print "Making zoomed image of shape","x".join(map(str,zdata.shape));
    images = [ pyfits.PrimaryHDU(zdata, hdr) ];
    updated = True;

  if options.rescale != 1:
    if autoname and not updated:
      outname = "rescale_" + outname;
    if len(images) > 1:
      "Too many input images specified for this operation, at most 1 expected";
      sys.exit(2);
    print "Applying scaling factor of %f to image values"%options.rescale;
    images[0][0].data *= options.rescale;
    updated = True;

  if updated:
    imagenames[0] = outname;

  if options.stats:
    for ff,filename in zip(images,imagenames):
      data = ff[0].data;
      min,max,dum1,dum2 = scipy.ndimage.measurements.extrema(data);
      sum = data.sum();
      mean = sum/data.size;
      std = math.sqrt(((data-mean)**2).mean());
      print "%s: min %g, max %g, sum %g, np %d, mean %g, std %g"%(filename,min,max,sum,data.size,mean,std); 
    sys.exit(0);
      
  if updated:
    print "Writing output image",outname;
    if os.path.exists(outname) and not options.force:
      print "Output image exists, rerun with the -f switch to overwrite.";
      sys.exit(1);
    images[0].writeto(outname,clobber=True);
  elif not (options.header or options.stack or options.add_axis or options.reorder):
    print "No operations specified. Use --help for help."
Esempio n. 23
0
        shape = None
    source = SkyModel.Source(name, pos, flux, shape=shape)
    # Adding source peak flux (error) as extra flux attributes for sources,
    # and to avoid null values for point sources I_peak = src["Total_flux"]
    if shape:
        source.setAttribute("I_peak", float(src["peak_flux"]))
        source.setAttribute("I_peak_err", float(src["err_peak_flux"]))
    else:
        source.setAttribute("I_peak", float(src["int_flux"]))
        source.setAttribute("I_peak_err", float(src["err_int_flux"]))

    return source


data = Table.read('{0}_comp.{1}'.format(
    outfile.split('.')[0],
    outfile.split('.')[-1]),
                  format=outfile.split('.')[-1])

for i, src in enumerate(data):

    model.sources.append(tigger_src(src, i))

wcs = WCS(image)
centre = wcs.getCentreWCSCoords()
model.ra0, model.dec0 = map(numpy.deg2rad, centre)

model.save(tname_lsm)
# Rename using CORPAT
utils.xrun("tigger-convert", [tname_lsm, "--rename -f"])
Esempio n. 24
0
    def __init__(self, imagename, psfname=None, sourcefinder_name='pybdsm',
	             saveformat="gaul", makeplots=True, do_psf_corr=True, 
				 do_local_var=True, psf_corr_region=5, local_var_region=10,
				 rel_excl_src=None, pos_smooth=2, neg_smooth=2, loglevel=0, 
				 thresh_pix=5, thresh_isl=3, neg_thresh_isl=3,
				 neg_thresh_pix=5, reset_rel=None, prefix=None, 
				 do_nearsources=False, savefits=False, 
				 increase_beam_cluster=False, savemask_pos=False,
				 savemask_neg=False, no_smooth=True, **kw):

        """ Takes in image and extracts sources and makes 
            reliability estimations..
           
		
        imagename: Fits image
        psfname: PSF fits image, optional. 

        sourcefinder_name: str, optional. Default 'pybdsm'.
            Uses source finder specified.

        makeplots: bool, optional. Default is True.
            Make reliability plots.

        do_psf_corr : bool, optional. Default True.
            If True, PSF correlation will be added
            as an extra parameter for density estimations.
            NB: the PSF fits image must be provided.

        do_local_var : bool, optional. Default is True.
            If True, adds local variance as an extra parameter,
            for density estimations. 
        
        do_nearsources: boolean. Default is False.
            If true it adds number of nearest neighnours as an extra
            parameter. It looks for sources around 5 beam sizes.

        psf_corr_region : int, optional. Default value is 5. 
            Data size to correlate around a source, in beam sizes.
 
        local_var_region: int, optional. Default 10.
            Data size to compute the local variance in beam sizes.

        rel_excl_src : floats, optional. Default is None. 
            Excludes sources in a specified region
            e.g ra, dec, radius in degrees. For
            2 regions: ra1, dec1, radius1: ra2, dec2, radius2, etc.

        pos_smooth : float, optional. Default 2.
            Masking threshold for the positive image.
            For default value 2, data peaks < 2 * image noise
            are masked.

        neg_smooth : float, optional. Default 2.
            Similar to pos_smooth but applied to the negative image.

        thresh_isl :  float, optional. Default is 3.
            Threshold for forming islands in the positive image

        thresh_pix : float, optional. Default is 5.
            Threshold for model fitting, in positive image.

        neg_thresh_isl : float, optional. Default is 3. 
            Simialr to thresh_isl but for negative image.

        neg_thresh_pix : float, optional. Default is 5. 
            Similar to thresh_pix but for negative image.

        savefits: boolean. Default is False.
            If True a negative image is saved.

        reset_rel: boolean. Default is False. If true then
            sources with correlation < 0.002 and rel >0.60
            have their reliabilities set to 0.

        increase_beam_cluster: boolean, optional. If True, sources
            groupings will be increase by 20% the beam size. If False,
            the actual beam size will be used. Default is False.

        savemask_pos: boolean, optional. If true the mask applied on 
            the positive side of an image after smoothing is saved.
            
        savemask_neg: Similar to savemask_pos but for the negative
            side of an image.
        
        loglevel : int, optional. Default is 0.
            Provides Pythonlogging options, 0, 1, 2 and 3 are for info, debug,
            error and critial respectively.
   
         kw : kward for source extractions. Should be a mapping e.g
            kw['thresh_isl'] = 2.0 or kw['do_polarization'] = True 
        """


        #
        self.smoothing = not no_smooth
       
        self.prefix = prefix

        # log level  
        self.loglevel = loglevel
        self.log = utils.logger(self.loglevel, prefix=self.prefix)

        
        # image, psf image
        self.imagename = imagename
        self.psfname = psfname 

        with pyfits.open(imagename) as hdu:
            self.header = hdu[0].header
            self.wcs = WCS(self.header, mode="pyfits")
            self.pixelsize = abs(self.header["cdelt1"])
      
        self.bmaj = numpy.deg2rad(self.header["BMAJ"])

        # boolean optionals    
        self.makeplots = makeplots
        self.do_local_var = do_local_var
        self.nearsources = do_nearsources
        self.do_psf_corr = do_psf_corr
        self.savemaskpos = savemask_pos
        self.savemaskneg = savemask_neg
        self.savefits = savefits
        self.derel = reset_rel
        self.log.info("Catalogues will be saved as %s, where srl is source "
					  " and gaul is Gaussians. "%saveformat)
        self.catalogue_format = "." + saveformat
        if not self.psfname:
            self.log.info(" No psf provided, do_psf_corr is set to False.")
            self.do_psf_corr = False

 
        # computing negative noise
        self.noise, self.mean = utils.negative_noise(self.imagename, self.prefix)
        
        self.log.info(" The negative noise is %e Jy/beam"%self.noise)
        if self.noise == 0: 
            self.log.debug(" The negative noise is 0, check image")

        # source finder initialization
        self.sourcefinder_name  = sourcefinder_name
        self.log.info(" Using %s source finder to extract the sources."%
                      self.sourcefinder_name)

        self.negimage = self.prefix + "_negative.fits"
        utils.invert_image(self.imagename, self.negimage)  
       
        # smoothing factors
        self.pos_smooth = pos_smooth
        self.neg_smooth = neg_smooth
        
        # region to evaluate
        self.corrstep = psf_corr_region
        self.localstep = local_var_region
        self.radiusrm = rel_excl_src
        self.do_beam = increase_beam_cluster
         
        beam_pix = int(round(numpy.rad2deg(self.bmaj)/self.pixelsize))
        self.locstep = self.localstep * beam_pix
        self.cfstep = self.corrstep * beam_pix
        self.bmin, self.bpa =  self.header["BMIN"], self.header["BPA"]

        self.opts_pos = {}
        if self.do_beam:
            bmaj = self.header["BMAJ"]
            self.opts_pos["beam"] = (1.2*bmaj, 1.2*self.bmin, self.bpa)

        # Pybdsm or source finder fitting thresholds
        self.thresh_isl = thresh_isl
        self.thresh_pix = thresh_pix
        self.opts_pos = dict(thresh_pix=self.thresh_pix,
                             thresh_isl=self.thresh_isl)
        
        self.opts_pos.update(kw)
        self.opts_neg = {}
        self.opts_neg.update(kw)
        self.neg_thresh_isl = neg_thresh_isl
        self.neg_thresh_pix = neg_thresh_pix
        self.opts_neg["thresh_isl"] = self.neg_thresh_isl
        self.opts_neg["thresh_pix"] = self.neg_thresh_pix
Esempio n. 25
0
class load(object):
    def __init__(self,
                 imagename,
                 psfname=None,
                 sourcefinder_name='pybdsm',
                 saveformat="gaul",
                 makeplots=True,
                 do_psf_corr=True,
                 do_local_var=True,
                 psf_corr_region=5,
                 local_var_region=10,
                 rel_excl_src=None,
                 pos_smooth=2,
                 neg_smooth=2,
                 loglevel=0,
                 thresh_pix=5,
                 thresh_isl=3,
                 neg_thresh_isl=3,
                 neg_thresh_pix=5,
                 reset_rel=None,
                 prefix=None,
                 do_nearsources=False,
                 savefits=False,
                 increase_beam_cluster=False,
                 savemask_pos=False,
                 savemask_neg=False,
                 no_smooth=True,
                 **kw):
        """ Takes in image and extracts sources and makes 
            reliability estimations..
           
		
        imagename: Fits image
        psfname: PSF fits image, optional. 

        sourcefinder_name: str, optional. Default 'pybdsm'.
            Uses source finder specified.

        makeplots: bool, optional. Default is True.
            Make reliability plots.

        do_psf_corr : bool, optional. Default True.
            If True, PSF correlation will be added
            as an extra parameter for density estimations.
            NB: the PSF fits image must be provided.

        do_local_var : bool, optional. Default is True.
            If True, adds local variance as an extra parameter,
            for density estimations. 
        
        do_nearsources: boolean. Default is False.
            If true it adds number of nearest neighnours as an extra
            parameter. It looks for sources around 5 beam sizes.

        psf_corr_region : int, optional. Default value is 5. 
            Data size to correlate around a source, in beam sizes.
 
        local_var_region: int, optional. Default 10.
            Data size to compute the local variance in beam sizes.

        rel_excl_src : floats, optional. Default is None. 
            Excludes sources in a specified region
            e.g ra, dec, radius in degrees. For
            2 regions: ra1, dec1, radius1: ra2, dec2, radius2, etc.

        pos_smooth : float, optional. Default 2.
            Masking threshold for the positive image.
            For default value 2, data peaks < 2 * image noise
            are masked.

        neg_smooth : float, optional. Default 2.
            Similar to pos_smooth but applied to the negative image.

        thresh_isl :  float, optional. Default is 3.
            Threshold for forming islands in the positive image

        thresh_pix : float, optional. Default is 5.
            Threshold for model fitting, in positive image.

        neg_thresh_isl : float, optional. Default is 3. 
            Simialr to thresh_isl but for negative image.

        neg_thresh_pix : float, optional. Default is 5. 
            Similar to thresh_pix but for negative image.

        savefits: boolean. Default is False.
            If True a negative image is saved.

        reset_rel: boolean. Default is False. If true then
            sources with correlation < 0.002 and rel >0.60
            have their reliabilities set to 0.

        increase_beam_cluster: boolean, optional. If True, sources
            groupings will be increase by 20% the beam size. If False,
            the actual beam size will be used. Default is False.

        savemask_pos: boolean, optional. If true the mask applied on 
            the positive side of an image after smoothing is saved.
            
        savemask_neg: Similar to savemask_pos but for the negative
            side of an image.
        
        loglevel : int, optional. Default is 0.
            Provides Pythonlogging options, 0, 1, 2 and 3 are for info, debug,
            error and critial respectively.
   
         kw : kward for source extractions. Should be a mapping e.g
            kw['thresh_isl'] = 2.0 or kw['do_polarization'] = True 
        """

        #
        self.smoothing = not no_smooth

        self.prefix = prefix

        # log level
        self.loglevel = loglevel
        self.log = utils.logger(self.loglevel, prefix=self.prefix)

        # image, psf image
        self.imagename = imagename
        self.psfname = psfname

        with pyfits.open(imagename) as hdu:
            self.header = hdu[0].header
            self.wcs = WCS(self.header, mode="pyfits")
            self.pixelsize = abs(self.header["cdelt1"])

        self.bmaj = numpy.deg2rad(self.header["BMAJ"])

        # boolean optionals
        self.makeplots = makeplots
        self.do_local_var = do_local_var
        self.nearsources = do_nearsources
        self.do_psf_corr = do_psf_corr
        self.savemaskpos = savemask_pos
        self.savemaskneg = savemask_neg
        self.savefits = savefits
        self.derel = reset_rel
        self.log.info("Catalogues will be saved as %s, where srl is source "
                      " and gaul is Gaussians. " % saveformat)
        self.catalogue_format = "." + saveformat
        if not self.psfname:
            self.log.info(" No psf provided, do_psf_corr is set to False.")
            self.do_psf_corr = False

        # computing negative noise
        self.noise, self.mean = utils.negative_noise(self.imagename,
                                                     self.prefix)

        self.log.info(" The negative noise is %e Jy/beam" % self.noise)
        if self.noise == 0:
            self.log.debug(" The negative noise is 0, check image")

        # source finder initialization
        self.sourcefinder_name = sourcefinder_name
        self.log.info(" Using %s source finder to extract the sources." %
                      self.sourcefinder_name)

        self.negimage = self.prefix + "_negative.fits"
        utils.invert_image(self.imagename, self.negimage)

        # smoothing factors
        self.pos_smooth = pos_smooth
        self.neg_smooth = neg_smooth

        # region to evaluate
        self.corrstep = psf_corr_region
        self.localstep = local_var_region
        self.radiusrm = rel_excl_src
        self.do_beam = increase_beam_cluster

        beam_pix = int(round(numpy.rad2deg(self.bmaj) / self.pixelsize))
        self.locstep = self.localstep * beam_pix
        self.cfstep = self.corrstep * beam_pix
        self.bmin, self.bpa = self.header["BMIN"], self.header["BPA"]

        self.opts_pos = {}
        if self.do_beam:
            bmaj = self.header["BMAJ"]
            self.opts_pos["beam"] = (1.2 * bmaj, 1.2 * self.bmin, self.bpa)

        # Pybdsm or source finder fitting thresholds
        self.thresh_isl = thresh_isl
        self.thresh_pix = thresh_pix
        self.opts_pos = dict(thresh_pix=self.thresh_pix,
                             thresh_isl=self.thresh_isl)

        self.opts_pos.update(kw)
        self.opts_neg = {}
        self.opts_neg.update(kw)
        self.neg_thresh_isl = neg_thresh_isl
        self.neg_thresh_pix = neg_thresh_pix
        self.opts_neg["thresh_isl"] = self.neg_thresh_isl
        self.opts_neg["thresh_pix"] = self.neg_thresh_pix

    def source_finder(self,
                      image=None,
                      thresh=None,
                      prefix=None,
                      noise=None,
                      output=None,
                      savemask=None,
                      **kw):

        #kw.update(kwards)
        tpos = None
        naxis = self.header["NAXIS1"]
        boundary = numpy.array([self.locstep, self.cfstep])
        #trim_box = (boundary.max(), naxis - boundary.max(),
        #          boundary.max(), naxis - boundary.max())
        trim_box = None
        # data smoothing
        if self.smoothing:

            ext = utils.fits_ext(image)
            tpos = tempfile.NamedTemporaryFile(suffix="." + ext, dir=".")
            tpos.flush()

            mask, noise = utils.thresh_mask(image,
                                            tpos.name,
                                            thresh=thresh,
                                            noise=self.noise,
                                            sigma=True,
                                            smooth=True,
                                            prefix=prefix,
                                            savemask=savemask)

            # using the masked image for forming islands
            kw["detection_image"] = tpos.name
            kw["blank_limit"] = self.noise / 1.0e5

        # source extraction
        utils.sources_extraction(image=image,
                                 output=output,
                                 sourcefinder_name=self.sourcefinder_name,
                                 trim_box=trim_box,
                                 prefix=self.prefix,
                                 **kw)

        if tpos:
            tpos.close()

    def remove_sources_within(self, model):

        sources = model.sources
        rel_remove = self.radiusrm[0].split(":")
        for i in range(len(rel_remove)):
            ra, dec, tolerance = rel_remove[i].split(",")
            ra_r = numpy.deg2rad(float(ra))
            dec_r = numpy.deg2rad(float(dec))
            tolerance_r = numpy.deg2rad(float(tolerance))
            within = model.getSourcesNear(ra_r, dec_r, tolerance_r)
            for src in sorted(sources):
                if src in within:
                    model.sources.remove(src)
        return model

    def params(self, modelfits):

        # reads in source finder output
        with pyfits.open(modelfits) as hdu:
            data = hdu[1].data

        tfile = tempfile.NamedTemporaryFile(suffix=".txt")
        tfile.flush()

        # writes a catalogue in a temporaty txt file
        with open(tfile.name, "w") as std:
            std.write("#format:name ra_rad dec_rad i emaj_r emin_r pa_r\n")

        model = Tigger.load(tfile.name)  # open a tmp. file

        peak, total, area, loc, corr = [], [], [], [], []
        for i in range(len(data)):
            flux = data["Total_flux"][i]
            dc_emaj, dc_emin = data["DC_Maj"][i], data["DC_Min"][i]
            ra, dec = data["RA"][i], data["DEC"][i]
            pa = data["DC_PA"][i]
            name = "SRC%d" % i
            peak_flux = data["Peak_flux"][i]

            posrd = ModelClasses.Position(numpy.deg2rad(ra),
                                          numpy.deg2rad(dec))
            flux_I = ModelClasses.Polarization(flux, 0, 0, 0)
            if dc_emaj == 0 and dc_emin == 0:
                shape = None
            else:
                shape = ModelClasses.Gaussian(numpy.deg2rad(dc_emaj),
                                              numpy.deg2rad(dc_emin),
                                              numpy.deg2rad(pa))

            srs = SkyModel.Source(name, posrd, flux_I, shape=shape)

            # using convolved maj and min for reliability estimate
            emaj, emin = data["Maj"][i], data["Min"][i]

            # area: find ex and ey if are 0 assign beam size
            if emaj or emin == 0:
                srcarea = math.pi * (numpy.rad2deg(self.bmaj)) * pow(3600.0, 2) *\
                       (numpy.rad2deg(self.bmin))
            if emaj and emin > 0:
                srcarea = emaj * emin * math.pi * pow(3600.0, 2)  # arcsecond

            # only accepts sources with flux > 0 and not nan RA and DEC
            # and local variance
            pos = [self.wcs.wcs2pix(*(ra, dec))
                   ][0]  #positions from deg to pixel

            with pyfits.open(self.negimage) as hdu:
                negdata = utils.image_data(hdu[0].data)

            if flux > 0 and peak_flux > 0 and not math.isnan(float(ra))\
                and not math.isnan(float(dec)):

                local = utils.compute_local_variance(negdata, pos,
                                                     self.locstep)

                srs.setAttribute("local_variance", local)

                if not math.isnan(float(local)) or local > 0:
                    if self.psfname:
                        pdata, psf = utils.compute_psf_correlation(
                            self.imagename, self.psfname, pos, self.cfstep)

                        if len(pdata) == len(psf):
                            c_region = numpy.corrcoef((pdata, psf))
                            cf = (numpy.diag((numpy.rot90(c_region))**
                                             2).sum())**0.5 / 2**0.5

                            srs.setAttribute("correlation_factor", cf)
                            corr.append(cf)
                            model.sources.append(srs)
                            peak.append(peak_flux)
                            total.append(flux)
                            area.append(srcarea)
                            loc.append(local)
                    else:
                        model.sources.append(srs)
                        peak.append(peak_flux)
                        total.append(flux)
                        area.append(srcarea)
                        loc.append(local)

        labels = dict(size=(0, "Log$_{10}$(Source area)"),
                      peak=(1, "Log$_{10}$( Peak flux [Jy] )"),
                      tot=(2, "Log$_{10}$( Total flux [Jy] )"))

        if self.do_psf_corr:
            labels.update({"coeff": (len(labels), "Log$_{10}$ (CF)")})
        if self.do_local_var:
            labels.update(
                {"local": (len(labels), "Log$_{10}$(Local Variance)")})
        if self.nearsources:
            labels.update({"near": (len(labels), "Log$_{10}$(Near Sources)")})

        nsrc = len(model.sources)
        out = numpy.zeros([nsrc, len(labels)])

        # returning parameters
        for i, src in enumerate(model.sources):

            ra, dec = src.pos.ra, src.pos.dec
            near = model.getSourcesNear(ra, dec, 5 * self.bmaj)
            nonear = len(near)
            if self.nearsources:
                src.setAttribute("neibours", nonear)

            if self.do_psf_corr and self.do_local_var and self.nearsources:
                out[i,
                    ...] = area[i], peak[i], total[i], corr[i], loc[i], nonear

            elif self.do_psf_corr and self.do_local_var and not self.nearsources:
                out[i, ...] = area[i], peak[i], total[i], corr[i], loc[i]

            elif self.do_psf_corr and self.nearsources and not self.do_local_var:
                out[i, ...] = area[i], peak[i], total[i], corr[i], nonear

            elif not self.do_psf_corr and self.do_local_var and self.nearsources:
                out[i, ...] = area[i], peak[i], total[i], loc[i], nonear

            elif self.do_psf_corr and not self.do_local_var and not self.nearsources:
                out[i, ...] = area[i], peak[i], total[i], corr[i]

            elif not self.do_psf_corr and self.do_local_var and not self.nearsources:
                out[i, ...] = area[i], peak[i], total[i], loc[i]

            elif not self.do_psf_corr and not self.do_local_var and self.nearsources:
                out[i, ...] = area[i], peak[i], total[i], nonear

            else:
                out[i, ...] = area[i], peak[i], total[i]

        # removes the rows with 0s
        removezeros = (out == 0).sum(1)
        output = out[removezeros <= 0, :]

        return model, numpy.log10(output), labels

    def get_reliability(self):

        # finding sources
        self.log.info(" Extracting the sources on both sides ")

        pfile = self.prefix + self.catalogue_format + ".fits"
        nfile = self.prefix + "_negative" + self.catalogue_format + ".fits"
        # i need to catch mmap.mmap error here

        # running a source finder
        self.source_finder(image=self.negimage,
                           output=nfile,
                           thresh=self.neg_smooth,
                           savemask=self.savemaskneg,
                           prefix=self.prefix,
                           **self.opts_neg)

        self.source_finder(image=self.imagename,
                           output=pfile,
                           thresh=self.pos_smooth,
                           savemask=self.savemaskpos,
                           prefix=self.prefix,
                           **self.opts_pos)

        self.log.info(" Source Finder completed successfully ")

        pmodel, positive, labels = self.params(pfile)
        nmodel, negative, labels = self.params(nfile)

        # setting up a kernel, Gaussian kernel
        bandwidth = []

        for plane in negative.T:
            bandwidth.append(plane.std())

        nplanes = len(labels)
        cov = numpy.zeros([nplanes, nplanes])
        nnsrc = len(negative)
        npsrc = len(positive)

        self.log.info(" There are %d positive and %d negtive detections " %
                      (npsrc, nnsrc))

        if nnsrc == 0 or npsrc == 0:
            self.log.error(
                "The resulting array has length of 0 thus cannot compute"
                " the reliability. Aborting.")

        self.log.info(" Computing the reliabilities ")
        for i in range(nplanes):
            for j in range(nplanes):
                if i == j:
                    cov[i, j] = bandwidth[i] * ((4.0 / (
                        (nplanes + 2) * nnsrc))**(1.0 / (nplanes + 4.0)))
        self.log.info("The resulting covariance matrix is %r" % cov)

        pcov = utils.gaussian_kde_set_covariance(positive.T, cov)
        ncov = utils.gaussian_kde_set_covariance(negative.T, cov)

        # get number densities
        nps = pcov(positive.T) * npsrc
        nns = ncov(positive.T) * nnsrc

        # define reliability of positive catalog
        rel = (nps - nns) / nps
        for src, rf in zip(pmodel.sources, rel):
            src.setAttribute("rel", rf)
        self.log.info(" Saved the reliabilities values.")

        # remove sources with poor correlation and high reliability,
        # the values are currently arbitrary
        if self.do_psf_corr and self.derel:
            for s in pmodel.sources:
                cf, r = s.correlation_factor, s.rel
                if cf < 0.006 and r > 0.60:
                    s.rel = 0.0

        if self.makeplots:
            savefig = self.prefix + "_planes.png"
            utils.plot(positive,
                       negative,
                       rel=rel,
                       labels=labels,
                       savefig=savefig,
                       prefix=self.prefix)

        # removes sources in a given radius from the phase center
        if self.radiusrm:
            self.log.info(" Remove sources ra, dec, radius of  %r"
                          " from the phase center" % self.radiusrm)
            pmodel = self.remove_sources_within(pmodel)

        if not self.savefits:
            self.log.info(" Deleting the negative image.")
            os.system("rm -r %s" % self.negimage)

        # Set field Center
        pmodel.ra0, pmodel.dec0 = map(numpy.deg2rad,
                                      self.wcs.getCentreWCSCoords())

        return pmodel, nmodel, self.locstep
Esempio n. 26
0
 def accept(self):
     """Tries to make a brick, and closes the dialog if successful."""
     sources = [
         src for src in self.model.sources
         if src.selected and src.typecode == 'pnt'
     ]
     filename = self.wfile.filename()
     if not self._fileSelected(filename):
         return
     # get PB expression
     pbfunc = None
     if self.wpb_apply.isChecked():
         pbexp = str(self.wpb_exp.text())
         try:
             pbfunc = eval("lambda r,fq:" + pbexp)
         except Exception as err:
             QMessageBox.warning(
                 self, "Error parsing PB experssion",
                 "Error parsing primary beam expression %s: %s" %
                 (pbexp, str(err)))
             return
     # get frequency
     freq = str(self.wfreq.text())
     freq = float(freq) * 1e+6 if freq else None
     # get pad factor
     pad = str(self.wpad.text())
     pad = max(float(pad), 1) if pad else 1
     # read fits file
     busy = BusyIndicator()
     try:
         input_hdu = pyfits.open(filename)[0]
     except Exception as err:
         busy = None
         QMessageBox.warning(
             self, "Error reading FITS",
             "Error reading FITS file %s: %s" % (filename, str(err)))
         return
     # reset data if asked to
     if self.woverwrite.isChecked():
         input_hdu.data[...] = 0
     # insert sources
     Imaging.restoreSources(input_hdu,
                            sources,
                            0,
                            primary_beam=pbfunc,
                            freq=freq)
     # save fits file
     try:
         # pyfits seems to produce an exception:
         #         TypeError: formatwarning() takes exactly 4 arguments (5 given)
         # when attempting to overwrite a file. As a workaround, remove the file first.
         if os.path.exists(filename):
             os.remove(filename)
         input_hdu.writeto(filename)
     except Exception as err:
         traceback.print_exc()
         busy = None
         QMessageBox.warning(
             self, "Error writing FITS",
             "Error writing FITS file %s: %s" % (filename, str(err)))
         return
     changed = False
     sources = self.model.sources
     # remove sources from model if asked to
     if self.wdel.isChecked():
         sources = [
             src for src in sources
             if not (src.selected and src.typecode == 'pnt')
         ]
         changed = True
     # add image to model if asked to
     if self.wadd.isChecked():
         hdr = input_hdu.header
         # get image parameters
         max_flux = float(input_hdu.data.max())
         wcs = WCS(hdr, mode='pyfits')
         # Get reference pixel coordinates
         # wcs.getCentreWCSCoords() doesn't work, as that gives us the middle of the image
         # So scan the header to get the CRPIX values
         ra0 = dec0 = 1
         for iaxis in range(hdr['NAXIS']):
             axs = str(iaxis + 1)
             name = hdr.get('CTYPE' + axs, axs).upper()
             if name.startswith("RA"):
                 ra0 = hdr.get('CRPIX' + axs, 1) - 1
             elif name.startswith("DEC"):
                 dec0 = hdr.get('CRPIX' + axs, 1) - 1
         # convert pixel to degrees
         ra0, dec0 = wcs.pix2wcs(ra0, dec0)
         ra0 *= DEG
         dec0 *= DEG
         sx, sy = wcs.getHalfSizeDeg()
         sx *= DEG
         sy *= DEG
         nx, ny = input_hdu.data.shape[-1:-3:-1]
         # check if this image is already contained in the model
         for src in sources:
             if isinstance(getattr(src, 'shape', None),
                           ModelClasses.FITSImage) and os.path.samefile(
                               src.shape.filename, filename):
                 # update source parameters
                 src.pos.ra, src.pos.dec = ra0, dec0
                 src.flux.I = max_flux
                 src.shape.ex, src.shape.ey = sx, sy
                 src.shape.nx, src.shape.ny = nx, ny
                 src.shape.pad = pad
                 break
         # not contained, make new source object
         else:
             pos = ModelClasses.Position(ra0, dec0)
             flux = ModelClasses.Flux(max_flux)
             shape = ModelClasses.FITSImage(sx,
                                            sy,
                                            0,
                                            os.path.basename(filename),
                                            nx,
                                            ny,
                                            pad=pad)
             img_src = SkyModel.Source(os.path.splitext(
                 os.path.basename(filename))[0],
                                       pos,
                                       flux,
                                       shape=shape)
             sources.append(img_src)
         changed = True
     if changed:
         self.model.setSources(sources)
         self.model.emitUpdate(SkyModel.SkyModel.UpdateAll, origin=self)
     self.parent().showMessage("Wrote %d sources to FITS file %s" %
                               (len(sources), filename))
     busy = None
     return QDialog.accept(self)
Esempio n. 27
0
    def __init__(self,
                 imagename,
                 psfname=None,
                 sourcefinder_name='pybdsm',
                 saveformat="gaul",
                 makeplots=True,
                 do_psf_corr=True,
                 do_local_var=True,
                 psf_corr_region=5,
                 local_var_region=10,
                 rel_excl_src=None,
                 pos_smooth=2,
                 neg_smooth=2,
                 loglevel=0,
                 thresh_pix=5,
                 thresh_isl=3,
                 neg_thresh_isl=3,
                 neg_thresh_pix=5,
                 reset_rel=None,
                 prefix=None,
                 do_nearsources=False,
                 savefits=False,
                 increase_beam_cluster=False,
                 savemask_pos=False,
                 savemask_neg=False,
                 no_smooth=True,
                 **kw):
        """ Takes in image and extracts sources and makes 
            reliability estimations..
           
		
        imagename: Fits image
        psfname: PSF fits image, optional. 

        sourcefinder_name: str, optional. Default 'pybdsm'.
            Uses source finder specified.

        makeplots: bool, optional. Default is True.
            Make reliability plots.

        do_psf_corr : bool, optional. Default True.
            If True, PSF correlation will be added
            as an extra parameter for density estimations.
            NB: the PSF fits image must be provided.

        do_local_var : bool, optional. Default is True.
            If True, adds local variance as an extra parameter,
            for density estimations. 
        
        do_nearsources: boolean. Default is False.
            If true it adds number of nearest neighnours as an extra
            parameter. It looks for sources around 5 beam sizes.

        psf_corr_region : int, optional. Default value is 5. 
            Data size to correlate around a source, in beam sizes.
 
        local_var_region: int, optional. Default 10.
            Data size to compute the local variance in beam sizes.

        rel_excl_src : floats, optional. Default is None. 
            Excludes sources in a specified region
            e.g ra, dec, radius in degrees. For
            2 regions: ra1, dec1, radius1: ra2, dec2, radius2, etc.

        pos_smooth : float, optional. Default 2.
            Masking threshold for the positive image.
            For default value 2, data peaks < 2 * image noise
            are masked.

        neg_smooth : float, optional. Default 2.
            Similar to pos_smooth but applied to the negative image.

        thresh_isl :  float, optional. Default is 3.
            Threshold for forming islands in the positive image

        thresh_pix : float, optional. Default is 5.
            Threshold for model fitting, in positive image.

        neg_thresh_isl : float, optional. Default is 3. 
            Simialr to thresh_isl but for negative image.

        neg_thresh_pix : float, optional. Default is 5. 
            Similar to thresh_pix but for negative image.

        savefits: boolean. Default is False.
            If True a negative image is saved.

        reset_rel: boolean. Default is False. If true then
            sources with correlation < 0.002 and rel >0.60
            have their reliabilities set to 0.

        increase_beam_cluster: boolean, optional. If True, sources
            groupings will be increase by 20% the beam size. If False,
            the actual beam size will be used. Default is False.

        savemask_pos: boolean, optional. If true the mask applied on 
            the positive side of an image after smoothing is saved.
            
        savemask_neg: Similar to savemask_pos but for the negative
            side of an image.
        
        loglevel : int, optional. Default is 0.
            Provides Pythonlogging options, 0, 1, 2 and 3 are for info, debug,
            error and critial respectively.
   
         kw : kward for source extractions. Should be a mapping e.g
            kw['thresh_isl'] = 2.0 or kw['do_polarization'] = True 
        """

        #
        self.smoothing = not no_smooth

        self.prefix = prefix

        # log level
        self.loglevel = loglevel
        self.log = utils.logger(self.loglevel, prefix=self.prefix)

        # image, psf image
        self.imagename = imagename
        self.psfname = psfname

        with pyfits.open(imagename) as hdu:
            self.header = hdu[0].header
            self.wcs = WCS(self.header, mode="pyfits")
            self.pixelsize = abs(self.header["cdelt1"])

        self.bmaj = numpy.deg2rad(self.header["BMAJ"])

        # boolean optionals
        self.makeplots = makeplots
        self.do_local_var = do_local_var
        self.nearsources = do_nearsources
        self.do_psf_corr = do_psf_corr
        self.savemaskpos = savemask_pos
        self.savemaskneg = savemask_neg
        self.savefits = savefits
        self.derel = reset_rel
        self.log.info("Catalogues will be saved as %s, where srl is source "
                      " and gaul is Gaussians. " % saveformat)
        self.catalogue_format = "." + saveformat
        if not self.psfname:
            self.log.info(" No psf provided, do_psf_corr is set to False.")
            self.do_psf_corr = False

        # computing negative noise
        self.noise, self.mean = utils.negative_noise(self.imagename,
                                                     self.prefix)

        self.log.info(" The negative noise is %e Jy/beam" % self.noise)
        if self.noise == 0:
            self.log.debug(" The negative noise is 0, check image")

        # source finder initialization
        self.sourcefinder_name = sourcefinder_name
        self.log.info(" Using %s source finder to extract the sources." %
                      self.sourcefinder_name)

        self.negimage = self.prefix + "_negative.fits"
        utils.invert_image(self.imagename, self.negimage)

        # smoothing factors
        self.pos_smooth = pos_smooth
        self.neg_smooth = neg_smooth

        # region to evaluate
        self.corrstep = psf_corr_region
        self.localstep = local_var_region
        self.radiusrm = rel_excl_src
        self.do_beam = increase_beam_cluster

        beam_pix = int(round(numpy.rad2deg(self.bmaj) / self.pixelsize))
        self.locstep = self.localstep * beam_pix
        self.cfstep = self.corrstep * beam_pix
        self.bmin, self.bpa = self.header["BMIN"], self.header["BPA"]

        self.opts_pos = {}
        if self.do_beam:
            bmaj = self.header["BMAJ"]
            self.opts_pos["beam"] = (1.2 * bmaj, 1.2 * self.bmin, self.bpa)

        # Pybdsm or source finder fitting thresholds
        self.thresh_isl = thresh_isl
        self.thresh_pix = thresh_pix
        self.opts_pos = dict(thresh_pix=self.thresh_pix,
                             thresh_isl=self.thresh_isl)

        self.opts_pos.update(kw)
        self.opts_neg = {}
        self.opts_neg.update(kw)
        self.neg_thresh_isl = neg_thresh_isl
        self.neg_thresh_pix = neg_thresh_pix
        self.opts_neg["thresh_isl"] = self.neg_thresh_isl
        self.opts_neg["thresh_pix"] = self.neg_thresh_pix