casa  $Rev:20696$
 All Classes Namespaces Files Functions Variables
imfit_pg.py
Go to the documentation of this file.
00001 #
00002 # This file was generated using xslt from its XML file
00003 #
00004 # Copyright 2008, Associated Universities Inc., Washington DC
00005 #
00006 import sys
00007 import os
00008 from casac import *
00009 import string
00010 import time
00011 import inspect
00012 import gc
00013 import numpy
00014 from odict import odict
00015 from task_imfit import imfit
00016 from task_imfit import casalog
00017 
00018 class imfit_pg_:
00019     __name__ = "imfit"
00020 
00021     def __init__(self) :
00022        self.__bases__ = (imfit_pg_,)
00023        self.__doc__ = self.__call__.__doc__
00024 
00025 
00026     def __call__(self, imagename=None, box=None, region=None, chans=None, stokes=None, mask=None, includepix=None, excludepix=None, residual=None, model=None, estimates=None, logfile=None, append=None, newestimates=None, complist=None, overwrite=None, dooff=None, offset=None, fixoffset=None, stretch=None, async=None):
00027 
00028         """Fit one or more elliptical Gaussian components on an image region(s)
00029 PARAMETER SUMMARY
00030 imagename        Name of the input image
00031 box              One or more box regions to use for fitting,
00032                  eg "100, 120, 200, 220, 300, 300, 400, 400"
00033                  to use two boxes. If both box and region
00034                  parameters are specified, box is used.
00035 region           Region of interest. See help par.region for specification options.
00036 stokes           Stokes parameter to fit. If blank, first polarization plane is used.
00037 mask             Mask to use. See help par.mask. Default is none.
00038 includepix       Range of pixel values to include for fitting. Array of two numeric
00039                  values assumed to have same units as image pixel values. Only one
00040                  of includepix or excludepix can be specified.
00041 excludepix       Range of pixel values to exclude for fitting. Array of two numeric
00042                  values assumed to have same units as image pixel values. Only one
00043                  of includepix or excludepix can be specified.
00044 residual         Name of the residual image to write.
00045 model            Name of the model image to write.
00046 estimates        Name of file containing initial estimates of component parameters
00047                  (see below for formatting details).
00048 logfile          Name of file to write fit results.
00049 append           If logfile exists, append to it (True) or overwrite it (False).
00050 newestimates     File to write fit results which can be used as initial estimates
00051                  for next run.
00052 complist         Name of output component list table.
00053 overwrite        Overwrite component list table if it exists?
00054 dooff            Simultaneously fit a zero-level offset?
00055 offset           Initial estimate for the zero-level offset. Only used if dooff is True.
00056 fixoffset        Hold zero-level offset constant during fit? Only used if dooff is True.
00057 stretch          Stretch the input mask if necessary and possible. Only used if a mask is specified.
00058                  See help par.stretch.
00059 
00060 OVERVIEW
00061 imfit is used to fit one or more gaussians to sources in an image as
00062 well as an optional zero-level offset. Fitting is limited to a single polarization
00063 but can be performed over several contiguous spectral channels.
00064 If the image has a clean beam, the report will contain both the convolved
00065 and the deconvolved fit results. It is a simple wrapper around the ia.fitcomponents()
00066 tool method.
00067 
00068 When dooff is False, the method returns a dictionary with two keys, 'converged' and 'results'.
00069 The value of 'converged' is a boolean array which indicates if the fit converged
00070 on a channel by channel basis.
00071 The value of 'results' is a dictionary representing a component list reflecting the
00072 fit results. In the case of a deconvolved image, the sizes and position angles are those
00073 of the source convolved with the restoring beam. For convenience, the 'results' dictionary can
00074 be read into a compoenent list tool (default tool is named cl) using the fromrecord() method
00075 for easier inspection using tool methods, eg
00076 
00077 cl.fromrecord(res['results'])
00078 
00079 If dooff is True, in addtion to the specified number of
00080 gaussians, a zero-level offset will also be fit. The initial estimate for this
00081 offset is specified using the offset parameter. Units are assumed to be the
00082 same as the image brightness units.The zero level offset can be held constant during
00083 the fit by specifying fixoffset=True. In the case of dooff=True, the returned
00084 dictionary contains two additional keys, 'zerooff' and 'zeroofferr', which are both
00085 arrays containing the the fitted zero level offset value and its error, respectively,
00086 for each channel. Both sets of values are in image brightness units.
00087 In cases where the fit did not converge, these values are set to NaN.
00088 
00089 The region can either be specified by a box(es) or a region (there is no
00090 garauntee which will be used if both are specified, so be certain only one is).
00091 If specified using the box parameter, multiple boxes can be given using the format
00092 box="blcx1, blcy1, trcx1, trcy1, blcx2, blcy2, trcx2, trcy2, ... , blcxN, blcyN, trcxN, trcyN"
00093 where N is the number of boxes. In this case, the union of the specified boxes will be used.
00094 
00095 Ranges of pixel values can be included or excluded from the fit via includepix and excludepix.
00096 
00097 If specified, the task will write the residual and/or model images for
00098 successful fits.
00099 
00100 If an estimates file is not specified, the task will attempt to estimate
00101 initial parameters and fit a single Gaussian. If a multiple Gaussian fit
00102 is desired, the user must specify initial estimates via a text file
00103 (see below for details).
00104 
00105 The user has the option of writing the result of the fit to a log file,
00106 and has the option of either appending to or overwriting an existing file.
00107 
00108 The user has the option of writing the (convolved) parameters of a successful
00109 fit to a file which can be fed back to imfit as the estimates file for a
00110 subsequent run.
00111 
00112 FITTING OVER MULTIPLE CHANNELS
00113 
00114 For fitting over multiple channels, the result
00115 of the previous successful fit is used as the estimate for the next channel. The number
00116 of gaussians fit cannot be varied on a channel by channel basis. Thus the variation
00117 of source structure should be reasonably smooth in frequency to produce reliable fit
00118 results.
00119 
00120 MASK SPECIFICATION
00121 
00122 Mask specification can be done using an LEL expression. For example
00123 
00124 mask = '"myimage">5' will use only pixels with values greater than 5.
00125 
00126 INCLUDING AND EXCLUDING PIXELS
00127 
00128 Pixels can be included or excluded from the fit based on their values
00129 using includepix and excludepix. Note that specifying both is not permitted
00130 and will result in  an error. If specified, both take an array of two numeric
00131 values.
00132 
00133 ESTIMATES
00134 
00135 Initial estimates of fit parameters may be specified via an estimates
00136 text file. Each line of this file should contain a set of parameters for
00137 a single gaussian. Optionally, some of these parameters can be fixed during
00138 the fit. The format of each line is
00139 
00140 peak intensity, peak x-pixel value, peak y-pixel value, major axis, minor axis, position angle, fixed
00141 
00142 The fixed parameter is optional. The peak intensity is assumed to be in the
00143 same units as the image pixel values (eg Jy/beam). The peak coordinates are specified
00144 by pixel values. The major and minor axes and the position angle are the convolved
00145 parameters if the image has been convolved with a clean beam and are specified as quantities.
00146 The fixed parameter is optional and is a string. It may contain any combination of the
00147 following characters 'f' (peak intensity), 'x' (peak x position), 'y' (peak y position),
00148 'a' (major axis), 'b' (minor axis), 'p' (position angle).
00149 
00150 In addition, lines in the file starting with a "#" are considered comments.
00151 
00152 An example of such a file is:
00153 
00154 # peak intensity must be in map units
00155 120, 150, 110, 23.5arcsec, 18.9arcsec, 120deg  
00156 90, 60, 200, 46arcsec, 23arcsec, 140deg, fxp
00157 
00158 This is a file which specifies that two gaussians are to be simultaneously fit,
00159 and for the second gaussian the specified peak intensity, x position, and position angle
00160 are to be held fixed during the fit.
00161 
00162 ERROR ESTIMATES
00163 
00164 The reported estimated errors are the maxima from two methods. In the first method, errors are calculated
00165 according to the description given in AIPS++ Note 224: AIPS++ Least Squares Background available at
00166 http://www.astron.nl/casacore/trunk/casacore/doc/notes/224.html . In the second method (Condon et al
00167 1998AJ.115.1693; Richards et al. 1999MNRAS.306..954), the following algorithms are used to compute the
00168 various errors.
00169 
00170 R = rms of residual image
00171 P = fitted peak intensity
00172 S = P/R (signal to noise)
00173 
00174 longitude error = (FWHM beam width in longitude direction)/(2*S)
00175 latitude error = (FWHM beam width in latitude direction)/(2*S)
00176 FWHM of major axis error = (FWHM beam along major axis)/S
00177 FWHM of minor axis error = (FWHM beam along minor axis)/S
00178 position angle error is calculated via standard error propogation of the FWHM major and minor axes sizes and errors
00179 flux error is computed via standard error propogation of the peak intensity and its error and the FWHM major
00180 and minor axes sizes and their errors.
00181 
00182 In the case where the image has no beam, the pixel width is used as the diameter of the resolution element.
00183 
00184 In the vast majority of (and perhaps all) cases, the errors reported come from the second method.
00185 
00186 The deconvolved size and position angle errors are computed by taking the maximum of the absolute values of the
00187 differences of the best fit deconvolved value of the given parameter and the deconvolved size of the eight
00188 possible combinations of (FWHM major axis +/- major axis error), (FWHM minor axis +/- minor axis error),
00189 and (position andle +/- position angle error).
00190 
00191 CAUTIONARY NOTES ON ERRORS
00192 imfit makes the following implicit assumptions:
00193 
00194 1. The noise associated with the pixels it is given to fit is truly random and gaussian
00195 2. There is no offset (constant (unless one is fit), gradient, or more complex function) in pixel values.
00196 3. The things that are trying to be fit are bona-fide gaussians.
00197 
00198 If any of these assumptions is not true, the errors reported will in general be lower limits. These
00199 assumptions do not hold if for example, there is structure in the noise of the image (eg the residual map produced
00200 by deconvolution has structure), the area being fit lies in a negative bowl, in extended emission, or on an image
00201 artifact (eg ripple), or the source(s) is nongaussian (eg a disk or more complex function convolved with a gaussian;
00202 just because something looks gaussian to the human image analysis system does not necessarily mean it is).
00203 In addition, in the case of an image produced from interferometric observations, the errors given are appropriate
00204 for good uv plane coverage. They may be a factor of two or more higher for sparse arrays or snapshots.
00205 In short, imfit is not a silver bullet for determining parameters in flawed images and reported errors
00206 should be interpreted cautiously in the vast majority of all "real life" cases. The closer the source being fit is to
00207 fulfilling these assumptions the more realistic reported errors will be.
00208 
00209 EXAMPLE: 
00210 
00211 Here is how one might fit two gaussians to multiple channels of a cube using the fit
00212 from the previous channel as the initial estimate for the next. It also illustrates
00213 how one can specify a region in the associated continuum image as the region to use
00214 as the fit for the channel.
00215 
00216 default imfit
00217 imagename = "co_cube.im"
00218 # specify region using region from continuum
00219 region = "continuum.im:source.rgn"
00220 chans = "2~20"
00221 # only use pixels with positive values in the fit
00222 excludepix = [-1e10,0]
00223 # estimates file contains initial parameters for two Gaussians in channel 2
00224 estimates = "initial_estimates.txt"
00225 logfile = "co_fit.log"
00226 # append results to the log file for all the channels
00227 append = "True"
00228 imfit()
00229 
00230 
00231         """
00232         a=inspect.stack()
00233         stacklevel=0
00234         for k in range(len(a)):
00235           if (string.find(a[k][1], 'ipython console') > 0) or (string.find(a[k][1], '<string>') >= 0):
00236                 stacklevel=k
00237                 break
00238         myf=sys._getframe(stacklevel).f_globals
00239         myf['__last_task'] = 'imfit'
00240         myf['taskname'] = 'imfit'
00241         ###
00242         myf['update_params'](func=myf['taskname'],printtext=False)
00243         ###
00244         ###
00245         #Handle globals or user over-ride of arguments
00246         #
00247         function_signature_defaults=dict(zip(self.__call__.func_code.co_varnames,self.__call__.func_defaults))
00248         useLocalDefaults = False
00249 
00250         for item in function_signature_defaults.iteritems():
00251                 key,val = item
00252                 keyVal = eval(key)
00253                 if (keyVal == None):
00254                         #user hasn't set it - use global/default
00255                         pass
00256                 else:
00257                         #user has set it - use over-ride
00258                         if (key != 'self') :
00259                            useLocalDefaults = True
00260                         #myf[key]=keyVal
00261 
00262         myparams = {}
00263         if useLocalDefaults :
00264            for item in function_signature_defaults.iteritems():
00265                key,val = item
00266                keyVal = eval(key)
00267                exec('myparams[key] = keyVal')
00268                if (keyVal == None):
00269                    exec('myparams[key] = '+ key + ' = self.itsdefault(key)')
00270                    keyVal = eval(key)
00271                    if(type(keyVal) == dict) :
00272                       exec('myparams[key] = ' + key + ' = keyVal[len(keyVal)-1][\'value\']')
00273 
00274         else :
00275             uselessvariable = None 
00276             myparams['imagename'] = imagename = myf['imagename']
00277             myparams['box'] = box = myf['box']
00278             myparams['region'] = region = myf['region']
00279             myparams['chans'] = chans = myf['chans']
00280             myparams['stokes'] = stokes = myf['stokes']
00281             myparams['mask'] = mask = myf['mask']
00282             myparams['includepix'] = includepix = myf['includepix']
00283             myparams['excludepix'] = excludepix = myf['excludepix']
00284             myparams['residual'] = residual = myf['residual']
00285             myparams['model'] = model = myf['model']
00286             myparams['estimates'] = estimates = myf['estimates']
00287             myparams['logfile'] = logfile = myf['logfile']
00288             myparams['append'] = append = myf['append']
00289             myparams['newestimates'] = newestimates = myf['newestimates']
00290             myparams['complist'] = complist = myf['complist']
00291             myparams['overwrite'] = overwrite = myf['overwrite']
00292             myparams['dooff'] = dooff = myf['dooff']
00293             myparams['offset'] = offset = myf['offset']
00294             myparams['fixoffset'] = fixoffset = myf['fixoffset']
00295             myparams['stretch'] = stretch = myf['stretch']
00296 
00297         if type(includepix)==int: includepix=[includepix]
00298         if type(excludepix)==int: excludepix=[excludepix]
00299 
00300         result = None
00301 
00302 #
00303 #    The following is work around to avoid a bug with current python translation
00304 #
00305         mytmp = {}
00306 
00307         mytmp['imagename'] = imagename
00308         mytmp['box'] = box
00309         mytmp['region'] = region
00310         mytmp['chans'] = chans
00311         mytmp['stokes'] = stokes
00312         mytmp['mask'] = mask
00313         mytmp['includepix'] = includepix
00314         mytmp['excludepix'] = excludepix
00315         mytmp['residual'] = residual
00316         mytmp['model'] = model
00317         mytmp['estimates'] = estimates
00318         mytmp['logfile'] = logfile
00319         mytmp['append'] = append
00320         mytmp['newestimates'] = newestimates
00321         mytmp['complist'] = complist
00322         mytmp['overwrite'] = overwrite
00323         mytmp['dooff'] = dooff
00324         mytmp['offset'] = offset
00325         mytmp['fixoffset'] = fixoffset
00326         mytmp['stretch'] = stretch
00327         pathname='file:///'+os.environ.get('CASAPATH').split()[0]+'/share/xml/'
00328         trec = casac.utils().torecord(pathname+'imfit.xml')
00329 
00330         casalog.origin('imfit')
00331         if not trec.has_key('imfit') or not casac.utils().verify(mytmp, trec['imfit']) :
00332             return False
00333 
00334 
00335         try :
00336           casalog.post('')
00337           casalog.post('##########################################')
00338           casalog.post('##### Begin Task: imfit           #####')
00339           casalog.post('')
00340           result = imfit(imagename, box, region, chans, stokes, mask, includepix, excludepix, residual, model, estimates, logfile, append, newestimates, complist, overwrite, dooff, offset, fixoffset, stretch)
00341           casalog.post('')
00342           casalog.post('##### End Task: imfit           #####')
00343           casalog.post('##########################################')
00344 
00345 
00346 # saveinputs for individule engine has no use
00347 # saveinputs should alos be removed from casa_in_py.py
00348 #
00349 #
00350 #          saveinputs = myf['saveinputs']
00351 #          saveinputs('imfit', 'imfit.last', myparams)
00352 #
00353 #
00354         except Exception, instance:
00355           #print '**** Error **** ',instance
00356           pass
00357 
00358         gc.collect()
00359         return result
00360 #
00361 #
00362 ##
00363 #    def paramgui(self, useGlobals=True):
00364 #        """
00365 #        Opens a parameter GUI for this task.  If useGlobals is true, then any relevant global parameter settings are used.
00366 #        """
00367 #        import paramgui
00368 #
00369 #        a=inspect.stack()
00370 #        stacklevel=0
00371 #        for k in range(len(a)):
00372 #          if (string.find(a[k][1], 'ipython console') > 0) or (string.find(a[k][1], '<string>') >= 0):
00373 #            stacklevel=k
00374 #            break
00375 #        myf = sys._getframe(stacklevel).f_globals
00376 #
00377 #        if useGlobals:
00378 #            paramgui.setGlobals(myf)
00379 #        else:
00380 #            paramgui.setGlobals({})
00381 #
00382 #        paramgui.runTask('imfit', myf['_ip'])
00383 #        paramgui.setGlobals({})
00384 #
00385 #
00386 #
00387 #
00388     def defaults(self, param=None):
00389         a=inspect.stack()
00390         stacklevel=0
00391         for k in range(len(a)):
00392           if (string.find(a[k][1], 'ipython console') > 0) or (string.find(a[k][1], '<string>') >= 0):
00393                 stacklevel=k
00394                 break
00395         myf=sys._getframe(stacklevel).f_globals
00396         a = odict()
00397         a['imagename']  = ''
00398         a['box']  = ''
00399         a['region']  = ''
00400         a['chans']  = ''
00401         a['stokes']  = ''
00402         a['mask']  = ''
00403         a['includepix']  = []
00404         a['excludepix']  = []
00405         a['residual']  = ''
00406         a['model']  = ''
00407         a['estimates']  = ''
00408         a['logfile']  = ''
00409         a['newestimates']  = ''
00410         a['complist']  = ''
00411         a['dooff']  = False
00412 
00413         a['async']=False
00414         a['mask'] = {
00415                     0:odict([{'notvalue':''}, {'stretch':False}])}
00416         a['logfile'] = {
00417                     0:odict([{'notvalue':''}, {'append':True}])}
00418         a['complist'] = {
00419                     0:odict([{'notvalue':''}, {'overwrite':False}])}
00420         a['dooff'] = {
00421                     0:odict([{'notvalue':False}, {'offset':0.0}, {'fixoffset':False}])}
00422 
00423 ### This function sets the default values but also will return the list of
00424 ### parameters or the default value of a given parameter
00425         if(param == None):
00426                 myf['__set_default_parameters'](a)
00427         elif(param == 'paramkeys'):
00428                 return a.keys()
00429         else:
00430                 if(a.has_key(param)):
00431                    #if(type(a[param]) == dict) :
00432                    #   return a[param][len(a[param])-1]['value']
00433                    #else :
00434                       return a[param]
00435 
00436 
00437 #
00438 #
00439     def check_params(self, param=None, value=None):
00440       a=inspect.stack() 
00441       stacklevel=0
00442       for k in range(len(a)):
00443         if (string.find(a[k][1], 'ipython console') > 0) or (string.find(a[k][1], '<string>') >= 0):
00444             stacklevel=k
00445             break
00446       myf=sys._getframe(stacklevel).f_globals
00447 
00448 #      print 'param:', param, 'value:', value
00449       try :
00450          if str(type(value)) != "<type 'instance'>" :
00451             value0 = value
00452             value = myf['cu'].expandparam(param, value)
00453             matchtype = False
00454             if(type(value) == numpy.ndarray):
00455                if(type(value) == type(value0)):
00456                   myf[param] = value.tolist()
00457                else:
00458                   #print 'value:', value, 'value0:', value0
00459                   #print 'type(value):', type(value), 'type(value0):', type(value0)
00460                   myf[param] = value0
00461                   if type(value0) != list :
00462                      matchtype = True
00463             else :
00464                myf[param] = value
00465             value = myf['cu'].verifyparam({param:value})
00466             if matchtype:
00467                value = False
00468       except Exception, instance:
00469          #ignore the exception and just return it unchecked
00470          myf[param] = value
00471       return value
00472 
00473 #
00474 #
00475     def description(self, key='imfit', subkey=None):
00476         desc={'imfit': 'Fit one or more elliptical Gaussian components on an image region(s)',
00477                'imagename': 'Name of the input image',
00478                'box': 'Specify one or more box regions for the fit.',
00479                'region': 'Region. See help par.region for specs.',
00480                'chans': 'Spectral channels on which to perform fit.',
00481                'stokes': 'Stokes parameter to fit. If blank, first stokes plane is used.',
00482                'mask': 'Mask to use. See help par.mask. Default is none.',
00483                'includepix': 'Range of pixel values to include for fitting.',
00484                'excludepix': 'Range of pixel values to exclude for fitting.',
00485                'residual': 'Name of output residual image.',
00486                'model': 'Name of output model image.',
00487                'estimates': 'Name of file containing initial estimates of component parameters.',
00488                'logfile': 'Name of file to write fit results.',
00489                'append': 'If logfile exists, append to it if True or overwrite it if False',
00490                'newestimates': 'File to write fit results which can be used as initial estimates for next run.',
00491                'complist': 'Name of output component list table.',
00492                'overwrite': 'Overwrite component list table if it exists?',
00493                'dooff': 'Also fit a zero level offset? Default is False',
00494                'offset': 'Initial estimate of zero-level offset. Only used if doff is True. Default is 0.0',
00495                'fixoffset': 'Keep the zero level offset fixed during fit? Default is False ',
00496                'stretch': 'Stretch the mask if necessary and possible? See help par.stretch ',
00497 
00498                'async': 'If true the taskname must be started using imfit(...)'
00499               }
00500 
00501 #
00502 # Set subfields defaults if needed
00503 #
00504 
00505         if(desc.has_key(key)) :
00506            return desc[key]
00507 
00508     def itsdefault(self, paramname) :
00509         a = {}
00510         a['imagename']  = ''
00511         a['box']  = ''
00512         a['region']  = ''
00513         a['chans']  = ''
00514         a['stokes']  = ''
00515         a['mask']  = ''
00516         a['includepix']  = []
00517         a['excludepix']  = []
00518         a['residual']  = ''
00519         a['model']  = ''
00520         a['estimates']  = ''
00521         a['logfile']  = ''
00522         a['append']  = True
00523         a['newestimates']  = ''
00524         a['complist']  = ''
00525         a['overwrite']  = False
00526         a['dooff']  = False
00527         a['offset']  = 0.0
00528         a['fixoffset']  = False
00529         a['stretch']  = False
00530 
00531         if a.has_key(paramname) :
00532               return a[paramname]
00533 imfit_pg = imfit_pg_()