def buildERRmask(self, chip, dqarr, scale): """ Builds a weight mask from an input DQ array and an ERR array associated with the input image. """ sci_chip = self._image[self.scienceExt, chip] # Set default value in case of error, or lack of ERR array errmask = dqarr if self.errExt is not None: try: # Attempt to open the ERR image. err = self.getData(exten=self.errExt + ',' + str(chip)) log.info("Applying ERR weighting to DQ mask for chip %s" % chip) # Multiply the scaled ERR file by the input mask in place. #exptime = self.getexptimeimg(chip) exptime = sci_chip._exptime errmask = (exptime / err)**2 * dqarr # Update 'wt_scl' parameter to match use of IVM file #sci_chip._wtscl = pow(sci_chip._exptime,2)/pow(scale,4) sci_chip._wtscl = 1.0 / pow(scale, 4) del err except: # We cannot find an 'ERR' extension and the data isn't WFPC2. # Print a generic warning message and continue on with the # final drizzle step. print(textutil.textbox( 'WARNING: No ERR weighting will be applied to the mask ' 'used in the final drizzle step! Weighting will be only ' 'by exposure time.\n\nThe data provided as input does not ' 'contain an ERR extension'), file=sys.stderr) print('\n Continue with final drizzle step...', sys.stderr) else: # If we were unable to find an 'ERR' extension to apply, one # possible reason was that the input was a 'standard' WFPC2 data # file that does not actually contain an error array. Test for # this condition and issue a Warning to the user and continue on to # the final drizzle. print(textutil.textbox( "WARNING: No ERR weighting will be applied to the mask used " "in the final drizzle step! Weighting will be only by " "exposure time.\n\nThe WFPC2 data provided as input does not " "contain ERR arrays. WFPC2 data is not supported by this " "weighting type.\n\nA workaround would be to create inverse " "variance maps and use 'IVM' as the final_wht_type. See the " "HELP file for more details on using inverse variance maps."), file=sys.stderr) print("\n Continue with final drizzle step...", file=sys.stderr) return errmask.astype(np.float32)
def generateRaDec(self): """ Convert XY positions into sky coordinates using STWCS methods """ self.prefix = self.PAR_PREFIX if not isinstance(self.wcs,pywcs.WCS): print(textutil.textbox('WCS not a valid PyWCS object. Conversion of RA/Dec not possible...'), file=sys.stderr) raise ValueError if self.xypos is None or len(self.xypos[0]) == 0: self.xypos = None warnstr = textutil.textbox('WARNING: \n'+ 'No objects found for this image...') for line in warnstr.split('\n'): log.warning(line) print(warnstr) return if self.radec is None or force: if self.wcs is not None: print(' Found {:d} objects.'.format(len(self.xypos[0]))) self.radec = self.wcs.all_pix2world(self.xypos[0],self.xypos[1],self.origin) else: # If we have no WCS, simply pass along the XY input positions # under the assumption they were already sky positions. self.radec = self.xypos
def generateRaDec(self): """ Convert XY positions into sky coordinates using STWCS methods. """ self.prefix = self.PAR_PREFIX if not isinstance(self.wcs, pywcs.WCS): print(textutil.textbox('WCS not a valid PyWCS object. ' 'Conversion of RA/Dec not possible...'), file=sys.stderr) raise ValueError if self.xypos is None or len(self.xypos[0]) == 0: self.xypos = None warnstr = textutil.textbox('WARNING: \n' 'No objects found for this image...') for line in warnstr.split('\n'): log.warning(line) print(warnstr) return if self.radec is None: print(' Found {:d} objects.'.format(len(self.xypos[0]))) if self.wcs is not None: ra, dec = self.wcs.all_pix2world(self.xypos[0], self.xypos[1], self.origin) self.radec = [ra, dec] + copy.deepcopy(self.xypos[2:]) else: # If we have no WCS, simply pass along the XY input positions # under the assumption they were already sky positions. self.radec = copy.deepcopy(self.xypos)
def buildERRmask(self,chip,dqarr,scale): """ Builds a weight mask from an input DQ array and an ERR array associated with the input image. """ sci_chip = self._image[self.scienceExt,chip] # Set default value in case of error, or lack of ERR array errmask = dqarr if self.errExt is not None: try: # Attempt to open the ERR image. err = self.getData(exten=self.errExt+','+str(chip)) log.info("Applying ERR weighting to DQ mask for chip %s" % chip) # Multiply the scaled ERR file by the input mask in place. #exptime = self.getexptimeimg(chip) exptime = sci_chip._exptime errmask = (exptime/err)**2 * dqarr # Update 'wt_scl' parameter to match use of IVM file #sci_chip._wtscl = pow(sci_chip._exptime,2)/pow(scale,4) sci_chip._wtscl = 1.0/pow(scale,4) del err except: # We cannot find an 'ERR' extension and the data isn't WFPC2. # Print a generic warning message and continue on with the # final drizzle step. print(textutil.textbox( 'WARNING: No ERR weighting will be applied to the mask ' 'used in the final drizzle step! Weighting will be only ' 'by exposure time.\n\nThe data provided as input does not ' 'contain an ERR extension'), file=sys.stderr) print('\n Continue with final drizzle step...', sys.stderr) else: # If we were unable to find an 'ERR' extension to apply, one # possible reason was that the input was a 'standard' WFPC2 data # file that does not actually contain an error array. Test for # this condition and issue a Warning to the user and continue on to # the final drizzle. print(textutil.textbox( "WARNING: No ERR weighting will be applied to the mask used " "in the final drizzle step! Weighting will be only by " "exposure time.\n\nThe WFPC2 data provided as input does not " "contain ERR arrays. WFPC2 data is not supported by this " "weighting type.\n\nA workaround would be to create inverse " "variance maps and use 'IVM' as the final_wht_type. See the " "HELP file for more details on using inverse variance maps."), file=sys.stderr) print("\n Continue with final drizzle step...", file=sys.stderr) return errmask.astype(np.float32)
def buildASNList(rootnames, asnname, check_for_duplicates=True): """ Return the list of filenames for a given set of rootnames """ # Recognize when multiple valid inputs with the same rootname are present # this would happen when both CTE-corrected (_flc) and non-CTE-corrected (_flt) # products are in the same directory as an ASN table filelist, duplicates = checkForDuplicateInputs(rootnames) if check_for_duplicates and duplicates: # Build new ASN tables for each set of input files origasn = changeSuffixinASN(asnname, 'flt') dupasn = changeSuffixinASN(asnname, 'flc') errstr = 'ERROR:\nMultiple valid input files found:\n' for fname, dname in zip(filelist, duplicates): errstr += ' %s %s\n' % (fname, dname) errstr += ('\nNew association files have been generated for each ' 'version of these files.\n %s\n %s\n\nPlease ' 're-start astrodrizzle using of these new ASN files or ' 'use widlcards for the input to only select one type of ' 'input file.' % (dupasn, origasn)) print(textutil.textbox(errstr), file=sys.stderr) # generate new ASN files for each case, # report this case of duplicate inputs to the user then quit raise ValueError return filelist
def writeXYCatalog(self, filename): """ Write out the X,Y catalog to a file """ if self.xypos is None: warnstr = textutil.textbox( 'WARNING: \n No X,Y source catalog to write to file. ') for line in warnstr.split('\n'): log.warning(line) print(warnstr) return f = open(filename, 'w') f.write("# Source catalog derived for %s\n" % self.wcs.filename) f.write("# Columns: \n") if self.use_sharp_round: f.write( '# X Y Flux ID Sharp Round1 Round2\n' ) else: f.write('# X Y Flux ID\n') f.write('# (%s) (%s)\n' % (self.in_units, self.in_units)) for row in range(len(self.xypos[0])): for i in range(len(self.xypos)): f.write("%g " % (self.xypos[i][row])) f.write("\n") f.close()
def writeXYCatalog(self,filename): """ Write out the X,Y catalog to a file """ if self.xypos is None: warnstr = textutil.textbox( 'WARNING: \n No X,Y source catalog to write to file. ') for line in warnstr.split('\n'): log.warning(line) print(warnstr) return f = open(filename,'w') f.write("# Source catalog derived for %s\n"%self.wcs.filename) f.write("# Columns: \n") if self.use_sharp_round: f.write('# X Y Flux ID Sharp Round1 Round2\n') else: f.write('# X Y Flux ID\n') f.write('# (%s) (%s)\n'%(self.in_units,self.in_units)) for row in range(len(self.xypos[0])): for i in range(len(self.xypos)): f.write("%g "%(self.xypos[i][row])) f.write("\n") f.close()
def buildASNList(rootnames, asnname, check_for_duplicates=True): """ Return the list of filenames for a given set of rootnames """ # Recognize when multiple valid inputs with the same rootname are present # this would happen when both CTE-corrected (_flc) and non-CTE-corrected (_flt) # products are in the same directory as an ASN table filelist, duplicates = checkForDuplicateInputs(rootnames) if check_for_duplicates and duplicates: # Build new ASN tables for each set of input files origasn = changeSuffixinASN(asnname, 'flt') dupasn = changeSuffixinASN(asnname, 'flc') errstr = 'ERROR:\nMultiple valid input files found:\n' for fname, dname in zip(filelist, duplicates): errstr += ' %s %s\n' % (fname, dname) errstr += ('\nNew association files have been generated for each ' 'version of these files.\n %s\n %s\n\nPlease ' 're-start astrodrizzle using of these new ASN files or ' 'use widlcards for the input to only select one type of ' 'input file.' % (dupasn, origasn)) print(textutil.textbox(errstr), file=sys.stderr) # generate new ASN files for each case, # report this case of duplicate inputs to the user then quit raise ValueError return filelist
def get_filename(self): """ Return name of rules file to be used It will use a local copy if present, and use the installed version by default. Any local copy will take precendence over the default rules. This function will return the alphabetically first file that applies to the instrument and meets the version requirements """ rules_file = None # get all potential local rules rfiles = glob.glob('*.rules') rfiles.sort() # Sort through list and find only applicable rules files # This would include picking up any rules files using the default # naming convention; namely, <instrument>_header.rules for r in rfiles: v, i = self.get_rules_header(r) if v is None or i is None: continue if v <= __rules_version__ and i == self.instrument.lower(): rules_file = r break if rules_file is None: # define default rules name installed with the software rules_name = self.instrument.lower() + self.rules_name_suffix rules_file = os.path.join(os.path.dirname(__file__), rules_name) if not os.path.exists(rules_file): rules_name = self.telescope + self.rules_name_suffix rules_file = os.path.join(os.path.dirname(__file__), rules_name) if not os.path.exists(rules_file): rules_file = None if rules_file is None: errmsg = 'ERROR:\n' + ' No valid rules file found for:\n' errmsg += ' INSTRUMENT = %s\n' % (self.instrument) errmsg += ' RULES Version <= %s\n' % (__rules_version__) print(textutil.textbox(errmsg)) raise ValueError self.rules_file = rules_file return rules_file
def get_filename(self): """ Return name of rules file to be used It will use a local copy if present, and use the installed version by default. Any local copy will take precendence over the default rules. This function will return the alphabetically first file that applies to the instrument and meets the version requirements """ rules_file = None # get all potential local rules rfiles = glob.glob('*.rules') rfiles.sort() # Sort through list and find only applicable rules files # This would include picking up any rules files using the default # naming convention; namely, <instrument>_header.rules for r in rfiles: v,i = self.get_rules_header(r) if v is None or i is None: continue if v <= __rules_version__ and i == self.instrument.lower(): rules_file = r break if rules_file is None: # define default rules name installed with the software rules_name = self.instrument.lower()+self.rules_name_suffix rules_file = os.path.join(os.path.dirname(__file__),rules_name) if not os.path.exists(rules_file): rules_name = self.telescope+self.rules_name_suffix rules_file = os.path.join(os.path.dirname(__file__),rules_name) if not os.path.exists(rules_file): rules_file = None if rules_file is None: errmsg = 'ERROR:\n'+' No valid rules file found for:\n' errmsg += ' INSTRUMENT = %s\n'%(self.instrument) errmsg += ' RULES Version <= %s\n'%(__rules_version__) print(textutil.textbox(errmsg)) raise ValueError self.rules_file = rules_file return rules_file
def run(configobj): """ Primary Python interface for image registration code This task replaces 'tweakshifts' """ print('TweakReg Version %s(%s) started at: %s \n'%( __version__,__version_date__,util._ptime()[0])) util.print_pkg_versions() # make sure 'updatewcs' is set to False when running from GUI or if missing # from configObj: if 'updatewcs' not in configobj: configobj['updatewcs'] = False # Check to see whether or not the imagefindpars parameters have # already been loaded, as done through the python interface. # Repeat for refimagefindpars if PSET_SECTION not in configobj: # Manage PSETs for source finding algorithms _managePsets(configobj, PSET_SECTION, imagefindpars.__taskname__) #print configobj[PSET_SECTION] if PSET_SECTION_REFIMG not in configobj: # Manage PSETs for source finding algorithms in reference image _managePsets(configobj, PSET_SECTION_REFIMG, refimagefindpars.__taskname__) log.debug('') log.debug("==== TweakReg was invoked with the following parameters: ====") log.debug('') util.print_cfg(configobj, log.debug) # print out user set input parameter values for running this task log.info('') log.info("USER INPUT PARAMETERS common to all Processing Steps:") util.printParams(configobj, log=log) # start interpretation of input parameters input_files = configobj['input'] # Start by interpreting the inputs use_catfile = True expand_refcat = configobj['expand_refcat'] enforce_user_order = configobj['enforce_user_order'] filenames, catnames = tweakutils.parse_input( input_files, sort_wildcards=not enforce_user_order ) catdict = {} for indx,f in enumerate(filenames): if catnames is not None and len(catnames) > 0: catdict[f] = catnames[indx] else: catdict[f] = None if not filenames: print('No filenames matching input %r were found.' % input_files) raise IOError # Verify that files are writable (based on file permissions) so that # they can be updated if either 'updatewcs' or 'updatehdr' have # been turned on (2 cases which require updating the input files) if configobj['updatewcs'] or configobj['UPDATE HEADER']['updatehdr']: filenames = util.verifyFilePermissions(filenames) if filenames is None or len(filenames) == 0: raise IOError if configobj['UPDATE HEADER']['updatehdr']: wname = configobj['UPDATE HEADER']['wcsname'] # verify that a unique WCSNAME has been specified by the user if not configobj['UPDATE HEADER']['reusename']: for fname in filenames: uniq = util.verifyUniqueWcsname(fname,wname) if not uniq: errstr = 'WCSNAME "%s" already present in "%s". '%(wname,fname)+\ 'A unique value for the "wcsname" parameter needs to be ' + \ 'specified. \n\nQuitting!' print(textutil.textbox(errstr,width=60)) raise IOError if configobj['updatewcs']: print('\nRestoring WCS solutions to original state using updatewcs...\n') updatewcs.updatewcs(filenames) if catnames in [None,'',' ','INDEF'] or len(catnames) == 0: catfile_par = configobj['COORDINATE FILE DESCRIPTION']['catfile'] # check to see whether the user specified input catalogs through other parameters if catfile_par not in [None,'',' ','INDEF']: # read in catalog file list provided by user catnames,catdict = tweakutils.parse_atfile_cat('@'+catfile_par) else: use_catfile = False if 'exclusions' in configobj and \ configobj['exclusions'] not in [None,'',' ','INDEF']: if os.path.exists(configobj['exclusions']): excl_files, excl_dict = tweakutils.parse_atfile_cat( '@'+configobj['exclusions']) # make sure the dictionary is well formed and that keys are base # file names and that exclusion files have been expanded: exclusion_files = [] exclusion_dict = {} rootpath = os.path.abspath( os.path.split(configobj['exclusions'])[0] ) for f in excl_dict.keys(): print(f) bf = os.path.basename(f) exclusion_files.append(bf) reglist = excl_dict[f] if reglist is None: exclusion_dict[bf] = None continue new_reglist = [] for regfile in reglist: if regfile in [ None, 'None', '', ' ', 'INDEF' ]: new_reglist.append(None) else: abs_regfile = os.path.normpath( os.path.join(rootpath, regfile) ) new_reglist.append(abs_regfile) exclusion_dict[bf] = new_reglist else: raise IOError('Could not find specified exclusions file "{:s}"' .format(configobj['exclusions'])) else: exclusion_files = [None]*len(filenames) exclusion_dict = {} for f in filenames: exclusion_dict[os.path.basename(f)] = None # Verify that we have the same number of catalog files as input images if catnames is not None and (len(catnames) > 0): missed_files = [] for f in filenames: if f not in catdict: missed_files.append(f) if len(missed_files) > 0: print('The input catalogs does not contain entries for the following images:') print(missed_files) raise IOError else: # setup array of None values as input to catalog parameter for Image class catnames = [None]*len(filenames) use_catfile = False # convert input images and any provided catalog file names into Image objects input_images = [] # copy out only those parameters needed for Image class catfile_kwargs = tweakutils.get_configobj_root(configobj) # define default value for 'xyunits' assuming sources to be derived from image directly catfile_kwargs['xyunits'] = 'pixels' # initialized here, required by Image class del catfile_kwargs['exclusions'] if use_catfile: # reset parameters based on parameter settings in this section catfile_kwargs.update(configobj['COORDINATE FILE DESCRIPTION']) for sort_par in imgclasses.sortKeys: catfile_kwargs['sort_'+sort_par] = catfile_kwargs[sort_par] # Update parameter set with 'SOURCE FINDING PARS' now catfile_kwargs.update(configobj[PSET_SECTION]) uphdr_par = configobj['UPDATE HEADER'] hdrlet_par = configobj['HEADERLET CREATION'] objmatch_par = configobj['OBJECT MATCHING PARAMETERS'] catfit_pars = configobj['CATALOG FITTING PARAMETERS'] catfit_pars['minobj'] = objmatch_par['minobj'] objmatch_par['residplot'] = catfit_pars['residplot'] hdrlet_par.update(uphdr_par) # default hdrlet name catfile_kwargs['updatehdr'] = uphdr_par['updatehdr'] shiftpars = configobj['OPTIONAL SHIFTFILE OUTPUT'] # verify a valid hdrname was provided, if headerlet was set to True imgclasses.verify_hdrname(**hdrlet_par) print('') print('Finding shifts for: ') for f in filenames: print(' {}'.format(f)) print('') log.info("USER INPUT PARAMETERS for finding sources for each input image:") util.printParams(catfile_kwargs, log=log) log.info('') try: minsources = max(1, catfit_pars['minobj']) omitted_images = [] all_input_images = [] for imgnum in range(len(filenames)): # Create Image instances for all input images try: regexcl = exclusion_dict[os.path.basename(filenames[imgnum])] except KeyError: regexcl = None pass img = imgclasses.Image(filenames[imgnum], input_catalogs=catdict[filenames[imgnum]], exclusions=regexcl, **catfile_kwargs) all_input_images.append(img) if img.num_sources < minsources: warn_str = "Image '{}' will not be aligned " \ "since it contains fewer than {} sources." \ .format(img.name, minsources) print('\nWARNING: {}\n'.format(warn_str)) log.warning(warn_str) omitted_images.append(img) continue input_images.append(img) except KeyboardInterrupt: for img in input_images: img.close() print('Quitting as a result of user request (Ctrl-C)...') return # create set of parameters to pass to RefImage class kwargs = tweakutils.get_configobj_root(configobj) # Determine a reference image or catalog and # return the full list of RA/Dec positions # Determine what WCS needs to be used for reference tangent plane refcat_par = configobj['REFERENCE CATALOG DESCRIPTION'] if refcat_par['refcat'] not in [None,'',' ','INDEF']: # User specified a catalog to use # Update kwargs with reference catalog parameters kwargs.update(refcat_par) # input_images list can be modified below. # So, make a copy of the original: input_images_orig_copy = copy(input_images) do_match_refimg = False # otherwise, extract the catalog from the first input image source list if configobj['refimage'] not in [None, '',' ','INDEF']: # User specified an image to use # A hack to allow different source finding parameters for # the reference image: ref_sourcefind_pars = \ tweakutils.get_configobj_root(configobj[PSET_SECTION_REFIMG]) ref_catfile_kwargs = catfile_kwargs.copy() ref_catfile_kwargs.update(ref_sourcefind_pars) ref_catfile_kwargs['updatehdr'] = False log.info('') log.info("USER INPUT PARAMETERS for finding sources for " "the reference image:") util.printParams(ref_catfile_kwargs, log=log) #refimg = imgclasses.Image(configobj['refimage'],**catfile_kwargs) # Check to see whether the user specified a separate catalog # of reference source positions and replace default source list with it if refcat_par['refcat'] not in [None,'',' ','INDEF']: # User specified a catalog to use ref_source = refcat_par['refcat'] cat_src = ref_source xycat = None cat_src_type = 'catalog' else: try: regexcl = exclusion_dict[configobj['refimage']] except KeyError: regexcl = None pass refimg = imgclasses.Image(configobj['refimage'], exclusions=regexcl, **ref_catfile_kwargs) ref_source = refimg.all_radec cat_src = None xycat = refimg.xy_catalog cat_src_type = 'image' try: if 'use_sharp_round' in ref_catfile_kwargs: kwargs['use_sharp_round'] = ref_catfile_kwargs['use_sharp_round'] refimage = imgclasses.RefImage(configobj['refimage'], ref_source, xycatalog=xycat, cat_origin=cat_src, **kwargs) refwcs_fname = refimage.wcs.filename if cat_src is not None: refimage.name = cat_src except KeyboardInterrupt: refimage.close() for img in input_images: img.close() print('Quitting as a result of user request (Ctrl-C)...') return if len(input_images) < 1: warn_str = "Fewer than two images are available for alignment. " \ "Quitting..." print('\nWARNING: {}\n'.format(warn_str)) log.warning(warn_str) for img in input_images: img.close() return image = _max_overlap_image(refimage, input_images, expand_refcat, enforce_user_order) elif refcat_par['refcat'] not in [None,'',' ','INDEF']: # a reference catalog is provided but not the reference image/wcs if len(input_images) < 1: warn_str = "No images available for alignment. Quitting..." print('\nWARNING: {}\n'.format(warn_str)) log.warning(warn_str) for img in input_images: img.close() return if len(input_images) == 1: image = input_images.pop(0) else: image, image2 = _max_overlap_pair(input_images, expand_refcat, enforce_user_order) input_images.insert(0, image2) # Workaround the defect described in ticket: # http://redink.stsci.edu/trac/ssb/stsci_python/ticket/1151 refwcs = [] for i in all_input_images: refwcs.extend(i.get_wcs()) kwargs['ref_wcs_name'] = image.get_wcs()[0].filename # A hack to allow different source finding parameters for # the reference image: ref_sourcefind_pars = \ tweakutils.get_configobj_root(configobj[PSET_SECTION_REFIMG]) ref_catfile_kwargs = catfile_kwargs.copy() ref_catfile_kwargs.update(ref_sourcefind_pars) ref_catfile_kwargs['updatehdr'] = False log.info('') log.info("USER INPUT PARAMETERS for finding sources for " "the reference image (not used):") util.printParams(ref_catfile_kwargs, log=log) ref_source = refcat_par['refcat'] cat_src = ref_source xycat = None try: if 'use_sharp_round' in ref_catfile_kwargs: kwargs['use_sharp_round'] = ref_catfile_kwargs['use_sharp_round'] kwargs['find_bounding_polygon'] = True refimage = imgclasses.RefImage(refwcs, ref_source, xycatalog=xycat, cat_origin=cat_src, **kwargs) refwcs_fname = refimage.wcs.filename refimage.name = cat_src cat_src_type = 'catalog' except KeyboardInterrupt: refimage.close() for img in input_images: img.close() print('Quitting as a result of user request (Ctrl-C)...') return else: if len(input_images) < 2: warn_str = "Fewer than two images available for alignment. " \ "Quitting..." print('\nWARNING: {}\n'.format(warn_str)) log.warning(warn_str) for img in input_images: img.close() return kwargs['use_sharp_round'] = catfile_kwargs['use_sharp_round'] cat_src = None refimg, image = _max_overlap_pair(input_images, expand_refcat, enforce_user_order) refwcs = [] #refwcs.extend(refimg.get_wcs()) #refwcs.extend(image.get_wcs()) #for i in input_images: #refwcs.extend(i.get_wcs()) # Workaround the defect described in ticket: # http://redink.stsci.edu/trac/ssb/stsci_python/ticket/1151 for i in all_input_images: refwcs.extend(i.get_wcs()) kwargs['ref_wcs_name'] = refimg.get_wcs()[0].filename try: ref_source = refimg.all_radec refimage = imgclasses.RefImage(refwcs, ref_source, xycatalog=refimg.xy_catalog, **kwargs) refwcs_fname = refimg.name cat_src_type = 'image' except KeyboardInterrupt: refimage.close() for img in input_images: img.close() print('Quitting as a result of user request (Ctrl-C)...') return omitted_images.insert(0, refimg) # refimage *must* be first do_match_refimg = True print("\n{0}\nPerforming alignment in the projection plane defined by the " "WCS\nderived from '{1}'\n{0}\n".format('='*63, refwcs_fname)) if refimage.outxy is not None: if cat_src is None: cat_src = refimage.name try: log.info("USER INPUT PARAMETERS for matching sources:") util.printParams(objmatch_par, log=log) log.info('') log.info("USER INPUT PARAMETERS for fitting source lists:") util.printParams(configobj['CATALOG FITTING PARAMETERS'], log=log) if hdrlet_par['headerlet']: log.info('') log.info("USER INPUT PARAMETERS for creating headerlets:") util.printParams(hdrlet_par, log=log) if shiftpars['shiftfile']: log.info('') log.info("USER INPUT PARAMETERS for creating a shiftfile:") util.printParams(shiftpars, log=log) # Now, apply reference WCS to each image's sky positions as well as the # reference catalog sky positions, # then perform the fit between the reference catalog positions and # each image's positions quit_immediately = False xycat_lines = '' xycat_filename = None for img in input_images_orig_copy: if xycat_filename is None: xycat_filename = img.rootname+'_xy_catfile.list' # Keep a record of all the generated input_xy catalogs xycat_lines += img.get_xy_catnames() retry_flags = len(input_images)*[0] objmatch_par['cat_src_type'] = cat_src_type while image is not None: print ('\n'+'='*20) print ('Performing fit for: {}\n'.format(image.name)) image.match(refimage, quiet_identity=False, **objmatch_par) assert(len(retry_flags) == len(input_images)) if not image.goodmatch: # we will try to match it again once reference catalog # has expanded with new sources: #if expand_refcat: input_images.append(image) retry_flags.append(1) if len(retry_flags) > 0 and retry_flags[0] == 0: retry_flags.pop(0) image = input_images.pop(0) # try to match next image in the list continue else: # no new sources have been added to the reference # catalog and we have already tried to match # images to the existing reference catalog #input_images.append(image) # <- add it back for later reporting #retry_flags.append(1) break image.performFit(**catfit_pars) if image.quit_immediately: quit_immediately = True image.close() break # add unmatched sources to the reference catalog # (to expand it): if expand_refcat: refimage.append_not_matched_sources(image) image.updateHeader(wcsname=uphdr_par['wcsname'], reusename=uphdr_par['reusename']) if hdrlet_par['headerlet']: image.writeHeaderlet(**hdrlet_par) if configobj['clean']: image.clean() image.close() if refimage.dirty and len(input_images) > 0: # The reference catalog has been updated with new sources. # Clear retry flags and get next image: image = _max_overlap_image( refimage, input_images, expand_refcat, enforce_user_order ) retry_flags = len(input_images)*[0] refimage.clear_dirty_flag() elif len(input_images) > 0 and retry_flags[0] == 0: retry_flags.pop(0) image = input_images.pop(0) else: break assert(len(retry_flags) == len(input_images)) if not quit_immediately: # process images that have not been matched in order to # update their headers: si = 0 if do_match_refimg: image = omitted_images[0] image.match(refimage, quiet_identity=True, **objmatch_par) si = 1 # process omitted (from start) images separately: for image in omitted_images[si:]: image.match(refimage, quiet_identity=False, **objmatch_par) # add to the list of omitted images, images that could not # be matched: omitted_images.extend(input_images) if len(input_images) > 0: print("\nUnable to match the following images:") print("-------------------------------------") for image in input_images: print(image.name) print("") # update headers: for image in omitted_images: image.performFit(**catfit_pars) if image.quit_immediately: quit_immediately = True image.close() break image.updateHeader(wcsname=uphdr_par['wcsname'], reusename=uphdr_par['reusename']) if hdrlet_par['headerlet']: image.writeHeaderlet(**hdrlet_par) if configobj['clean']: image.clean() image.close() if configobj['writecat'] and not configobj['clean']: # Write out catalog file recording input XY catalogs used # This file will be suitable for use as input to 'tweakreg' # as the 'catfile' parameter if os.path.exists(xycat_filename): os.remove(xycat_filename) f = open(xycat_filename, mode='w') f.writelines(xycat_lines) f.close() if expand_refcat: base_reg_name = os.path.splitext( os.path.basename(cat_src))[0] refimage.write_skycatalog( 'cumulative_sky_refcat_{:s}.coo' \ .format(base_reg_name), show_flux=True, show_id=True ) # write out shiftfile (if specified) if shiftpars['shiftfile']: tweakutils.write_shiftfile(input_images_orig_copy, shiftpars['outshifts'], outwcs=shiftpars['outwcs']) except KeyboardInterrupt: refimage.close() for img in input_images_orig_copy: img.close() del img print ('Quitting as a result of user request (Ctrl-C)...') return else: print ('No valid sources in reference frame. Quitting...') return
def run(configobj, wcsmap=None): """ Initial example by Nadia ran MD with configobj EPAR using: It can be run in one of two ways: from stsci.tools import teal 1. Passing a config object to teal teal.teal('drizzlepac/pars/astrodrizzle.cfg') 2. Passing a task name: teal.teal('astrodrizzle') The example config files are in drizzlepac/pars """ raise_status = RAISE # # turn on logging, redirecting stdout/stderr messages to a log file # while also printing them out to stdout as well # also, initialize timing of processing steps # # We need to define a default logfile name from the user's parameters input_list, output, ivmlist, odict = \ processInput.processFilenames(configobj['input']) if output is not None: def_logname = output elif len(input_list) > 0: def_logname = input_list[0] else: print(textutil.textbox( 'ERROR:\nNo valid input files found! Please restart the task ' 'and check the value for the "input" parameter.'), file=sys.stderr) def_logname = None return stateObj = configobj['STATE OF INPUT FILES'] procSteps = util.ProcSteps() print('AstroDrizzle Version %s(%s) started at: %s\n' % (__version__, __vdate__, util._ptime()[0])) util.print_pkg_versions(log=log) #try: try: # Define list of imageObject instances and output WCSObject instance # based on input paramters procSteps.addStep('Initialization') imgObjList = None imgObjList, outwcs = processInput.setCommonInput(configobj) procSteps.endStep('Initialization') if not imgObjList: errmsg = "No valid images found for processing!\n" errmsg += "Check log file for full details.\n" errmsg += "Exiting AstroDrizzle now..." print(textutil.textbox(errmsg,width=65)) raise_status = DO_NOT_RAISE raise ValueError log.info("USER INPUT PARAMETERS common to all Processing Steps:") util.printParams(configobj, log=log) # Call rest of MD steps... #create static masks for each image staticMask.createStaticMask(imgObjList, configobj, procSteps=procSteps) #subtract the sky sky.subtractSky(imgObjList, configobj, procSteps=procSteps) # _dbg_dump_virtual_outputs(imgObjList) #drizzle to separate images adrizzle.drizSeparate(imgObjList, outwcs, configobj, wcsmap=wcsmap, procSteps=procSteps) # _dbg_dump_virtual_outputs(imgObjList) #create the median images from the driz sep images createMedian.createMedian(imgObjList, configobj, procSteps=procSteps) #blot the images back to the original reference frame ablot.runBlot(imgObjList, outwcs, configobj, wcsmap=wcsmap, procSteps=procSteps) #look for cosmic rays drizCR.rundrizCR(imgObjList, configobj, procSteps=procSteps) #Make your final drizzled image adrizzle.drizFinal(imgObjList, outwcs, configobj, wcsmap=wcsmap, procSteps=procSteps) print() print(' '.join(['AstroDrizzle Version', __version__, 'is finished processing at ', util._ptime()[0]]) + '!\n') except: print(textutil.textbox( 'ERROR:\nAstroDrizzle Version %s encountered a problem! ' 'Processing terminated at %s.' % (__version__, util._ptime()[0])), file=sys.stderr) procSteps.reportTimes() if imgObjList: for image in imgObjList: image.close() del imgObjList del outwcs # Raise an exception ONLY if requested... if raise_status == RAISE: raise else: return procSteps.reportTimes() if imgObjList: for image in imgObjList: if stateObj['clean']: image.clean() image.close() del imgObjList del outwcs
def generateXY(self, **kwargs): """ Generate source catalog from input image using DAOFIND-style algorithm """ #x,y,flux,sharp,round = idlphot.find(array,self.pars['hmin'],self.pars['fwhm'], # roundlim=self.pars['roundlim'], sharplim=self.pars['sharplim']) print(" # Source finding for '{}', EXT={} started at: {}" .format(self.fnamenoext, self.wcs.extname, util._ptime()[0])) if self.pars['computesig']: # compute sigma for this image sigma = self._compute_sigma() else: sigma = self.pars['skysigma'] skymode = sigma**2 log.info(' Finding sources using sky sigma = %f'%sigma) if self.pars['threshold'] in [None,"INDEF",""," "]: hmin = skymode else: hmin = sigma*self.pars['threshold'] if 'mask' in kwargs and kwargs['mask'] is not None: dqmask = np.asarray(kwargs['mask'], dtype=bool) else: dqmask = None # get the mask for source finding: mask = self._combine_exclude_mask(dqmask) x, y, flux, src_id, sharp, round1, round2 = tweakutils.ndfind( self.source, hmin, self.pars['conv_width'], skymode, sharplim=[self.pars['sharplo'],self.pars['sharphi']], roundlim=[self.pars['roundlo'],self.pars['roundhi']], peakmin=self.pars['peakmin'], peakmax=self.pars['peakmax'], fluxmin=self.pars['fluxmin'], fluxmax=self.pars['fluxmax'], nsigma=self.pars['nsigma'], ratio=self.pars['ratio'], theta=self.pars['theta'], mask=mask, use_sharp_round=self.use_sharp_round, nbright=self.nbright ) if len(x) == 0: if not self.pars['computesig']: sigma = self._compute_sigma() hmin = sigma * self.pars['threshold'] log.info('No sources found with original thresholds. Trying automatic settings.') x, y, flux, src_id, sharp, round1, round2 = tweakutils.ndfind( self.source, hmin, self.pars['conv_width'], skymode, sharplim=[self.pars['sharplo'],self.pars['sharphi']], roundlim=[self.pars['roundlo'],self.pars['roundhi']], peakmin=self.pars['peakmin'], peakmax=self.pars['peakmax'], fluxmin=self.pars['fluxmin'], fluxmax=self.pars['fluxmax'], nsigma=self.pars['nsigma'], ratio=self.pars['ratio'], theta=self.pars['theta'], mask = mask, use_sharp_round = self.use_sharp_round, nbright=self.nbright ) if len(x) == 0: xypostypes = 3*[float]+[int]+(3 if self.use_sharp_round else 0)*[float] self.xypos = [np.empty(0, dtype=i) for i in xypostypes] warnstr = textutil.textbox('WARNING: \n'+ 'No valid sources found with the current parameter values!') for line in warnstr.split('\n'): log.warning(line) print(warnstr) else: # convert the positions from numpy 0-based to FITS 1-based if self.use_sharp_round: self.xypos = [x+1, y+1, flux, src_id+self.start_id, sharp, round1, round2] else: self.xypos = [x+1, y+1, flux, src_id+self.start_id] log.info('###Source finding finished at: %s'%(util._ptime()[0])) self.in_units = 'pixels' # Not strictly necessary, but documents units when determined self.sharp = sharp self.round1 = round1 self.round2 = round2 self.numcols = 7 if self.use_sharp_round else 4 self.num_objects = len(x) self._apply_flux_limits = False # limits already applied by 'ndfind'
def run(configobj, wcsmap=None): """ Initial example by Nadia ran MD with configobj EPAR using: It can be run in one of two ways: from stsci.tools import teal 1. Passing a config object to teal teal.teal('drizzlepac/pars/astrodrizzle.cfg') 2. Passing a task name: teal.teal('astrodrizzle') The example config files are in drizzlepac/pars """ # turn on logging, redirecting stdout/stderr messages to a log file # while also printing them out to stdout as well # also, initialize timing of processing steps # # We need to define a default logfile name from the user's parameters input_list, output, ivmlist, odict = \ processInput.processFilenames(configobj['input']) if output is not None: def_logname = output elif len(input_list) > 0: def_logname = input_list[0] else: print(textutil.textbox( "ERROR:\nNo valid input files found! Please restart the task " "and check the value for the 'input' parameter."), file=sys.stderr) def_logname = None return # Build name of output trailer file logging_handlers = logging.getLogger().handlers log_name = [lh.name for lh in logging_handlers if lh.level > 0] logfile = log_name[0] if log_name else "{}.tra".format(def_logname) print("AstroDrizzle log file: {}".format(logfile)) clean = configobj['STATE OF INPUT FILES']['clean'] procSteps = util.ProcSteps() print("AstroDrizzle Version {:s} started at: {:s}\n".format( __version__, util._ptime()[0])) util.print_pkg_versions(log=log) log.debug('') log.debug( "==== AstroDrizzle was invoked with the following parameters: ====") log.debug('') util.print_cfg(configobj, log.debug) try: # Define list of imageObject instances and output WCSObject instance # based on input paramters imgObjList = None procSteps.addStep('Initialization') imgObjList, outwcs = processInput.setCommonInput(configobj) procSteps.endStep('Initialization') if imgObjList is None or not imgObjList: errmsg = "No valid images found for processing!\n" errmsg += "Check log file for full details.\n" errmsg += "Exiting AstroDrizzle now..." print(textutil.textbox(errmsg, width=65)) print(textutil.textbox( 'ERROR:\nAstroDrizzle Version {:s} encountered a problem! ' 'Processing terminated at {:s}.'.format( __version__, util._ptime()[0])), file=sys.stderr) return log.info("USER INPUT PARAMETERS common to all Processing Steps:") util.printParams(configobj, log=log) # Call rest of MD steps... # create static masks for each image staticMask.createStaticMask(imgObjList, configobj, procSteps=procSteps) # subtract the sky sky.subtractSky(imgObjList, configobj, procSteps=procSteps) # _dbg_dump_virtual_outputs(imgObjList) # drizzle to separate images adrizzle.drizSeparate(imgObjList, outwcs, configobj, wcsmap=wcsmap, logfile=logfile, procSteps=procSteps) # _dbg_dump_virtual_outputs(imgObjList) # create the median images from the driz sep images createMedian.createMedian(imgObjList, configobj, procSteps=procSteps) # blot the images back to the original reference frame ablot.runBlot(imgObjList, outwcs, configobj, wcsmap=wcsmap, procSteps=procSteps) # look for cosmic rays drizCR.rundrizCR(imgObjList, configobj, procSteps=procSteps) # Make your final drizzled image adrizzle.drizFinal(imgObjList, outwcs, configobj, wcsmap=wcsmap, logfile=logfile, procSteps=procSteps) print() print("AstroDrizzle Version {:s} is finished processing at {:s}.\n". format(__version__, util._ptime()[0])) print("", flush=True) except Exception: clean = False print(textutil.textbox( "ERROR:\nAstroDrizzle Version {:s} encountered a problem! " "Processing terminated at {:s}.".format(__version__, util._ptime()[0])), file=sys.stderr) raise finally: procSteps.reportTimes() if imgObjList: for image in imgObjList: if clean: image.clean() image.close() del imgObjList del outwcs
def buildEmptyDRZ(input, output): """ Create an empty DRZ file. This module creates an empty DRZ file in a valid FITS format so that the HST pipeline can handle the Multidrizzle zero expossure time exception where all data has been excluded from processing. Parameters ---------- input : str filename of the initial input to process_input output : str filename of the default empty _drz.fits file to be generated """ # Identify the first input image inputfile = parseinput.parseinput(input)[0] if not inputfile: print('\n******* ERROR *******', file=sys.stderr) print( 'No input file found! Check specification of parameter ' '"input". ', file=sys.stderr) print('Quitting...', file=sys.stderr) print('******* ***** *******\n', file=sys.stderr) return # raise IOError, "No input file found!" # Set up output file here... if output is None: if len(input) == 1: oname = fileutil.buildNewRootname(input[0]) else: oname = 'final' output = fileutil.buildNewRootname(oname, extn='_drz.fits') else: if '_drz' not in output: output = fileutil.buildNewRootname(output, extn='_drz.fits') print('Building emtpy DRZ file with output name: %s' % output) # Open the first image (of the excludedFileList?) to use as a template to build # the DRZ file. try: log.info('Building empty DRZ file from %s' % inputfile[0]) img = fits.open(inputfile[0], memmap=False) except: raise IOError('Unable to open file %s \n' % inputfile) # Create the fitsobject fitsobj = fits.HDUList() # Copy the primary header hdu = img[0].copy() fitsobj.append(hdu) # Modify the 'NEXTEND' keyword of the primary header to 3 for the #'sci, wht, and ctx' extensions of the newly created file. fitsobj[0].header['NEXTEND'] = 3 # Create the 'SCI' extension hdu = fits.ImageHDU(header=img['sci', 1].header.copy()) hdu.header['EXTNAME'] = 'SCI' fitsobj.append(hdu) # Create the 'WHT' extension hdu = fits.ImageHDU(header=img['sci', 1].header.copy()) hdu.header['EXTNAME'] = 'WHT' fitsobj.append(hdu) # Create the 'CTX' extension hdu = fits.ImageHDU(header=img['sci', 1].header.copy()) hdu.header['EXTNAME'] = 'CTX' fitsobj.append(hdu) # Add HISTORY comments explaining the creation of this file. fitsobj[0].header.add_history("** AstroDrizzle has created this empty " "DRZ product because**") fitsobj[0].header.add_history("** all input images were excluded from " "processing.**") # Change the filename in the primary header to reflect the name of the output # filename. fitsobj[0].header['FILENAME'] = str(output) # +"_drz.fits" # Change the ROOTNAME keyword to the ROOTNAME of the output PRODUCT fitsobj[0].header['ROOTNAME'] = str(output.split('_drz.fits')[0]) # Modify the ASN_MTYP keyword to contain "PROD-DTH" so it can be properly # ingested into the archive catalog. # stis has this keyword in the [1] header, so I am directing the code #t o first look in the primary, then the 1 try: fitsobj[0].header['ASN_MTYP'] = 'PROD-DTH' except: fitsobj[1].header['ASN_MTYP'] = 'PROD-DTH' # If the file is already on disk delete it and replace it with the # new file dirfiles = os.listdir(os.curdir) if dirfiles.count(output) > 0: os.remove(output) log.info(" Replacing %s..." % output) # Write out the empty DRZ file fitsobj.writeto(output) print(textutil.textbox( 'ERROR:\nAstroDrizzle has created an empty DRZ product because all ' 'input images were excluded from processing or a user requested the ' 'program to stop.') + '\n', file=sys.stderr) return
def setCommonInput(configObj, createOutwcs=True): """ The common interface interpreter for MultiDrizzle tasks which not only runs 'process_input()' but 'createImageObject()' and 'defineOutput()' as well to fully setup all inputs for use with the rest of the MultiDrizzle steps either as stand-alone tasks or internally to MultiDrizzle itself. Parameters ---------- configObj : object configObj instance or simple dictionary of input parameters imageObjectList : list of imageObject objects list of imageObject instances, 1 for each input exposure outwcs : object imageObject instance defining the final output frame Notes ----- At a minimum, the configObj instance (dictionary) should contain: configObj = {'input':None,'output':None } If provided, the configObj should contain the values of all the multidrizzle parameters as set by the user with TEAL. If no configObj is given, it will retrieve the default values automatically. In either case, the values from the input_dict will be merged in with the configObj before being used by the rest of the code. Examples -------- You can set *createOutwcs=False* for the cases where you only want the images processed and no output wcs information in necessary; as in: >>> imageObjectList,outwcs = processInput.processCommonInput(configObj) """ # make sure 'updatewcs' is set to False when running from GUI or if missing # from configObj: if 'updatewcs' not in configObj: configObj['updatewcs'] = False if not createOutwcs or not configObj['coeffs']: # we're probably just working on single images here configObj['updatewcs']=False # maybe we can chunk this part up some more so that we can call just the # parts we want # Interpret input, read and convert and update input files, then return # list of input filenames and derived output filename asndict, ivmlist, output = process_input( configObj['input'], configObj['output'], updatewcs=configObj['updatewcs'], wcskey=configObj['wcskey'], **configObj['STATE OF INPUT FILES']) if not asndict: return None, None # convert the filenames from asndict into a list of full filenames files = [fileutil.buildRootname(f) for f in asndict['order']] original_files = asndict['original_file_names'] # interpret MDRIZTAB, if specified, and update configObj accordingly # This can be done here because MDRIZTAB does not include values for # input, output, or updatewcs. if 'mdriztab' in configObj and configObj['mdriztab']: print("Reading in MDRIZTAB parameters for {} files".format(len(files))) mdriztab_dict = mdzhandler.getMdriztabParameters(files) # Update configObj with values from mpars cfgpars.mergeConfigObj(configObj, mdriztab_dict) # Convert interpreted list of input files from process_input into a list # of imageObject instances for use by the MultiDrizzle tasks. instrpars = configObj['INSTRUMENT PARAMETERS'] # pass in 'proc_unit' to initialize unit conversions as necessary instrpars['proc_unit'] = configObj['proc_unit'] undistort = True if not configObj['coeffs']: undistort = False # determine whether parallel processing will be performed use_parallel = False if util.can_parallel: # look to see whether steps which can be run using multiprocessing # have been turned on for stepnum in parallel_steps: sname = util.getSectionName(configObj,stepnum[0]) if configObj[sname][stepnum[1]]: use_parallel = True break # interpret all 'bits' related parameters and convert them to integers configObj['resetbits'] = interpret_bit_flags(configObj['resetbits']) step3name = util.getSectionName(configObj,3) configObj[step3name]['driz_sep_bits'] = interpret_bit_flags( configObj[step3name]['driz_sep_bits'] ) step7name = util.getSectionName(configObj,7) configObj[step7name]['final_bits'] = interpret_bit_flags( configObj[step7name]['final_bits'] ) # Verify any refimage parameters to be used step3aname = util.getSectionName(configObj,'3a') if not util.verifyRefimage(configObj[step3aname]['driz_sep_refimage']): msg = 'No refimage with WCS found!\n '+\ ' This could be caused by one of 2 problems:\n'+\ ' * filename does not specify an extension with a valid WCS.\n'+\ ' * can not find the file.\n'+\ 'Please check the filename specified in the "refimage" parameter.' print(textutil.textbox(msg)) return None,None step7aname = util.getSectionName(configObj,'7a') if not util.verifyRefimage(configObj[step7aname]['final_refimage']): msg = 'No refimage with WCS found!\n '+\ ' This could be caused by one of 2 problems:\n'+\ ' * filename does not specify an extension with a valid WCS.\n'+\ ' * can not find the file.\n'+\ 'Please check the filename specified in the "refimage" parameter.' print(textutil.textbox(msg)) return None,None # Build imageObject list for all the valid, shift-updated input files log.info('-Creating imageObject List as input for processing steps.') if 'in_memory' in configObj: virtual = configObj['in_memory'] else: virtual = False imageObjectList = createImageObjectList(files, instrpars, group=configObj['group'], undistort=undistort, inmemory=virtual) # Add original file names as "hidden" attributes of imageObject assert(len(original_files) == len(imageObjectList)) #TODO: remove after extensive testing for i in range(len(imageObjectList)): imageObjectList[i]._original_file_name = original_files[i] # apply context parameter applyContextPar(imageObjectList, configObj['context']) # reset DQ bits if requested by user resetDQBits(imageObjectList, cr_bits_value=configObj['resetbits']) # Add info about input IVM files at this point to the imageObjectList addIVMInputs(imageObjectList, ivmlist) if createOutwcs: log.info('-Creating output WCS.') # Build output WCS and update imageObjectList with output WCS info outwcs = wcs_functions.make_outputwcs(imageObjectList, output, configObj=configObj, perfect=True) outwcs.final_wcs.printwcs() else: outwcs = None try: # Provide user with some information on resource usage for this run # raises ValueError Exception in interactive mode and user quits num_cores = configObj.get('num_cores') if use_parallel else 1 reportResourceUsage(imageObjectList, outwcs, num_cores) except ValueError: imageObjectList = None return imageObjectList, outwcs
def setCommonInput(configObj, createOutwcs=True): """ The common interface interpreter for MultiDrizzle tasks which not only runs 'process_input()' but 'createImageObject()' and 'defineOutput()' as well to fully setup all inputs for use with the rest of the MultiDrizzle steps either as stand-alone tasks or internally to MultiDrizzle itself. Parameters ---------- configObj : object configObj instance or simple dictionary of input parameters imageObjectList : list of imageObject objects list of imageObject instances, 1 for each input exposure outwcs : object imageObject instance defining the final output frame Notes ----- At a minimum, the configObj instance (dictionary) should contain: configObj = {'input':None,'output':None } If provided, the configObj should contain the values of all the multidrizzle parameters as set by the user with TEAL. If no configObj is given, it will retrieve the default values automatically. In either case, the values from the input_dict will be merged in with the configObj before being used by the rest of the code. Examples -------- You can set *createOutwcs=False* for the cases where you only want the images processed and no output wcs information in necessary; as in: >>> imageObjectList,outwcs = processInput.processCommonInput(configObj) """ # make sure 'updatewcs' is set to False when running from GUI or if missing # from configObj: if 'updatewcs' not in configObj: configObj['updatewcs'] = False if not createOutwcs or not configObj['coeffs']: # we're probably just working on single images here configObj['updatewcs'] = False # maybe we can chunk this part up some more so that we can call just the # parts we want # Interpret input, read and convert and update input files, then return # list of input filenames and derived output filename asndict, ivmlist, output = process_input( configObj['input'], configObj['output'], updatewcs=configObj['updatewcs'], wcskey=configObj['wcskey'], **configObj['STATE OF INPUT FILES']) if not asndict: return None, None # convert the filenames from asndict into a list of full filenames files = [fileutil.buildRootname(f) for f in asndict['order']] original_files = asndict['original_file_names'] # interpret MDRIZTAB, if specified, and update configObj accordingly # This can be done here because MDRIZTAB does not include values for # input, output, or updatewcs. if 'mdriztab' in configObj and configObj['mdriztab']: print("Reading in MDRIZTAB parameters for {} files".format(len(files))) mdriztab_dict = mdzhandler.getMdriztabParameters(files) # Update configObj with values from mpars cfgpars.mergeConfigObj(configObj, mdriztab_dict) # Convert interpreted list of input files from process_input into a list # of imageObject instances for use by the MultiDrizzle tasks. instrpars = configObj['INSTRUMENT PARAMETERS'] # pass in 'proc_unit' to initialize unit conversions as necessary instrpars['proc_unit'] = configObj['proc_unit'] undistort = True if not configObj['coeffs']: undistort = False # determine whether parallel processing will be performed use_parallel = False if util.can_parallel: # look to see whether steps which can be run using multiprocessing # have been turned on for stepnum in parallel_steps: sname = util.getSectionName(configObj, stepnum[0]) if configObj[sname][stepnum[1]]: use_parallel = True break # interpret all 'bits' related parameters and convert them to integers configObj['resetbits'] = interpret_bit_flags(configObj['resetbits']) step3name = util.getSectionName(configObj, 3) configObj[step3name]['driz_sep_bits'] = interpret_bit_flags( configObj[step3name]['driz_sep_bits']) step4name = util.getSectionName(configObj, 4) if len(files) > 5 and 'minmed' in configObj[step4name]['combine_type']: msg = '“minmed” is highly recommended for three images, \n'+\ ' and is good for four to six images, \n'+\ ' but should be avoided for ten or more images.\n' print(textutil.textbox(msg)) step7name = util.getSectionName(configObj, 7) configObj[step7name]['final_bits'] = interpret_bit_flags( configObj[step7name]['final_bits']) # Verify any refimage parameters to be used step3aname = util.getSectionName(configObj, '3a') if not util.verifyRefimage(configObj[step3aname]['driz_sep_refimage']): msg = 'No refimage with WCS found!\n '+\ ' This could be caused by one of 2 problems:\n'+\ ' * filename does not specify an extension with a valid WCS.\n'+\ ' * can not find the file.\n'+\ 'Please check the filename specified in the "refimage" parameter.' print(textutil.textbox(msg)) return None, None step7aname = util.getSectionName(configObj, '7a') if not util.verifyRefimage(configObj[step7aname]['final_refimage']): msg = 'No refimage with WCS found!\n '+\ ' This could be caused by one of 2 problems:\n'+\ ' * filename does not specify an extension with a valid WCS.\n'+\ ' * can not find the file.\n'+\ 'Please check the filename specified in the "refimage" parameter.' print(textutil.textbox(msg)) return None, None # Build imageObject list for all the valid, shift-updated input files log.info('-Creating imageObject List as input for processing steps.') if 'in_memory' in configObj: virtual = configObj['in_memory'] else: virtual = False imageObjectList = createImageObjectList(files, instrpars, output=asndict['output'], group=configObj['group'], undistort=undistort, inmemory=virtual) # Add original file names as "hidden" attributes of imageObject assert (len(original_files) == len(imageObjectList) ) #TODO: remove after extensive testing for i in range(len(imageObjectList)): imageObjectList[i]._original_file_name = original_files[i] # apply context parameter applyContextPar(imageObjectList, configObj['context']) # reset DQ bits if requested by user resetDQBits(imageObjectList, cr_bits_value=configObj['resetbits']) # Add info about input IVM files at this point to the imageObjectList addIVMInputs(imageObjectList, ivmlist) if createOutwcs: log.info('-Creating output WCS.') # Build output WCS and update imageObjectList with output WCS info outwcs = wcs_functions.make_outputwcs(imageObjectList, output, configObj=configObj, perfect=True) outwcs.final_wcs.printwcs() else: outwcs = None try: # Provide user with some information on resource usage for this run # raises ValueError Exception in interactive mode and user quits num_cores = configObj.get('num_cores') if use_parallel else 1 reportResourceUsage(imageObjectList, outwcs, num_cores) except ValueError: imageObjectList = None return imageObjectList, outwcs
def generateXY(self, **kwargs): """ Generate source catalog from input image using DAOFIND-style algorithm """ #x,y,flux,sharp,round = idlphot.find(array,self.pars['hmin'],self.pars['fwhm'], # roundlim=self.pars['roundlim'], sharplim=self.pars['sharplim']) print(" # Source finding for '{}', EXT={} started at: {}".format( self.fnamenoext, self.wcs.extname, util._ptime()[0])) if self.pars['computesig']: # compute sigma for this image sigma = self._compute_sigma() else: sigma = self.pars['skysigma'] skymode = sigma**2 log.info(' Finding sources using sky sigma = %f' % sigma) if self.pars['threshold'] in [None, "INDEF", "", " "]: hmin = skymode else: hmin = sigma * self.pars['threshold'] if 'mask' in kwargs and kwargs['mask'] is not None: dqmask = np.asarray(kwargs['mask'], dtype=bool) else: dqmask = None # get the mask for source finding: mask = self._combine_exclude_mask(dqmask) x, y, flux, src_id, sharp, round1, round2 = tweakutils.ndfind( self.source, hmin, self.pars['conv_width'], skymode, sharplim=[self.pars['sharplo'], self.pars['sharphi']], roundlim=[self.pars['roundlo'], self.pars['roundhi']], peakmin=self.pars['peakmin'], peakmax=self.pars['peakmax'], fluxmin=self.pars['fluxmin'], fluxmax=self.pars['fluxmax'], nsigma=self.pars['nsigma'], ratio=self.pars['ratio'], theta=self.pars['theta'], mask=mask, use_sharp_round=self.use_sharp_round, nbright=self.nbright) if len(x) == 0: if not self.pars['computesig']: sigma = self._compute_sigma() hmin = sigma * self.pars['threshold'] log.info( 'No sources found with original thresholds. Trying automatic settings.' ) x, y, flux, src_id, sharp, round1, round2 = tweakutils.ndfind( self.source, hmin, self.pars['conv_width'], skymode, sharplim=[self.pars['sharplo'], self.pars['sharphi']], roundlim=[self.pars['roundlo'], self.pars['roundhi']], peakmin=self.pars['peakmin'], peakmax=self.pars['peakmax'], fluxmin=self.pars['fluxmin'], fluxmax=self.pars['fluxmax'], nsigma=self.pars['nsigma'], ratio=self.pars['ratio'], theta=self.pars['theta'], mask=mask, use_sharp_round=self.use_sharp_round, nbright=self.nbright) if len(x) == 0: xypostypes = 3 * [float] + [ int ] + (3 if self.use_sharp_round else 0) * [float] self.xypos = [np.empty(0, dtype=i) for i in xypostypes] warnstr = textutil.textbox( 'WARNING: \n' + 'No valid sources found with the current parameter values!') for line in warnstr.split('\n'): log.warning(line) print(warnstr) else: # convert the positions from numpy 0-based to FITS 1-based if self.use_sharp_round: self.xypos = [ x + 1, y + 1, flux, src_id + self.start_id, sharp, round1, round2 ] else: self.xypos = [x + 1, y + 1, flux, src_id + self.start_id] log.info('###Source finding finished at: %s' % (util._ptime()[0])) self.in_units = 'pixels' # Not strictly necessary, but documents units when determined self.sharp = sharp self.round1 = round1 self.round2 = round2 self.numcols = 7 if self.use_sharp_round else 4 self.num_objects = len(x) self._apply_flux_limits = False # limits already applied by 'ndfind'
def buildEmptyDRZ(input, output): """ Create an empty DRZ file. This module creates an empty DRZ file in a valid FITS format so that the HST pipeline can handle the Multidrizzle zero expossure time exception where all data has been excluded from processing. Parameters ---------- input : str filename of the initial input to process_input output : str filename of the default empty _drz.fits file to be generated """ # Identify the first input image inputfile = parseinput.parseinput(input)[0] if not inputfile: print('\n******* ERROR *******', file=sys.stderr) print( 'No input file found! Check specification of parameter ' '"input". ', file=sys.stderr) print('Quitting...', file=sys.stderr) print('******* ***** *******\n', file=sys.stderr) return # raise IOError, "No input file found!" # Set up output file here... if output is None: if len(input) == 1: oname = fileutil.buildNewRootname(input[0]) else: oname = 'final' output = fileutil.buildNewRootname(oname, extn='_drz.fits') else: if 'drz' not in output: output = fileutil.buildNewRootname(output, extn='_drz.fits') log.info('Setting up output name: %s' % output) # Open the first image (of the excludedFileList?) to use as a template to build # the DRZ file. try : log.info('Building empty DRZ file from %s' % inputfile[0]) img = fits.open(inputfile[0], memmap=False) except: raise IOError('Unable to open file %s \n' % inputfile) # Create the fitsobject fitsobj = fits.HDUList() # Copy the primary header hdu = img[0].copy() fitsobj.append(hdu) # Modify the 'NEXTEND' keyword of the primary header to 3 for the #'sci, wht, and ctx' extensions of the newly created file. fitsobj[0].header['NEXTEND'] = 3 # Create the 'SCI' extension hdu = fits.ImageHDU(header=img['sci', 1].header.copy()) hdu.header['EXTNAME'] = 'SCI' fitsobj.append(hdu) # Create the 'WHT' extension hdu = fits.ImageHDU(header=img['sci', 1].header.copy()) hdu.header['EXTNAME'] = 'WHT' fitsobj.append(hdu) # Create the 'CTX' extension hdu = fits.ImageHDU(header=img['sci', 1].header.copy()) hdu.header['EXTNAME'] = 'CTX' fitsobj.append(hdu) # Add HISTORY comments explaining the creation of this file. fitsobj[0].header.add_history("** AstroDrizzle has created this empty " "DRZ product because**") fitsobj[0].header.add_history("** all input images were excluded from " "processing.**") # Change the filename in the primary header to reflect the name of the output # filename. fitsobj[0].header['FILENAME'] = str(output) # +"_drz.fits" # Change the ROOTNAME keyword to the ROOTNAME of the output PRODUCT fitsobj[0].header['ROOTNAME'] = str(output.split('_drz.fits')[0]) # Modify the ASN_MTYP keyword to contain "PROD-DTH" so it can be properly # ingested into the archive catalog. # stis has this keyword in the [1] header, so I am directing the code #t o first look in the primary, then the 1 try: fitsobj[0].header['ASN_MTYP'] = 'PROD-DTH' except: fitsobj[1].header['ASN_MTYP'] = 'PROD-DTH' # If the file is already on disk delete it and replace it with the # new file dirfiles = os.listdir(os.curdir) if dirfiles.count(output) > 0: os.remove(output) log.info(" Replacing %s..." % output) # Write out the empty DRZ file fitsobj.writeto(output) print(textutil.textbox( 'ERROR:\nAstroDrizzle has created an empty DRZ product because all ' 'input images were excluded from processing or a user requested the ' 'program to stop.') + '\n', file=sys.stderr) return
def run(configobj, wcsmap=None): """ Initial example by Nadia ran MD with configobj EPAR using: It can be run in one of two ways: from stsci.tools import teal 1. Passing a config object to teal teal.teal('drizzlepac/pars/astrodrizzle.cfg') 2. Passing a task name: teal.teal('astrodrizzle') The example config files are in drizzlepac/pars """ # turn on logging, redirecting stdout/stderr messages to a log file # while also printing them out to stdout as well # also, initialize timing of processing steps # # We need to define a default logfile name from the user's parameters input_list, output, ivmlist, odict = \ processInput.processFilenames(configobj['input']) if output is not None: def_logname = output elif len(input_list) > 0: def_logname = input_list[0] else: print(textutil.textbox( 'ERROR:\nNo valid input files found! Please restart the task ' 'and check the value for the "input" parameter.'), file=sys.stderr) def_logname = None return stateObj = configobj['STATE OF INPUT FILES'] procSteps = util.ProcSteps() print('AstroDrizzle Version %s(%s) started at: %s\n' % (__version__, __version_date__, util._ptime()[0])) util.print_pkg_versions(log=log) try: # Define list of imageObject instances and output WCSObject instance # based on input paramters imgObjList = None procSteps.addStep('Initialization') imgObjList, outwcs = processInput.setCommonInput(configobj) procSteps.endStep('Initialization') if imgObjList is None or not imgObjList: errmsg = "No valid images found for processing!\n" errmsg += "Check log file for full details.\n" errmsg += "Exiting AstroDrizzle now..." print(textutil.textbox(errmsg, width=65)) print(textutil.textbox( 'ERROR:\nAstroDrizzle Version %s encountered a problem! ' 'Processing terminated at %s.' % (__version__, util._ptime()[0])), file=sys.stderr) procSteps.reportTimes() return log.info("USER INPUT PARAMETERS common to all Processing Steps:") util.printParams(configobj, log=log) # Call rest of MD steps... #create static masks for each image staticMask.createStaticMask(imgObjList, configobj, procSteps=procSteps) #subtract the sky sky.subtractSky(imgObjList, configobj, procSteps=procSteps) # _dbg_dump_virtual_outputs(imgObjList) #drizzle to separate images adrizzle.drizSeparate(imgObjList, outwcs, configobj, wcsmap=wcsmap, procSteps=procSteps) # _dbg_dump_virtual_outputs(imgObjList) #create the median images from the driz sep images createMedian.createMedian(imgObjList, configobj, procSteps=procSteps) #blot the images back to the original reference frame ablot.runBlot(imgObjList, outwcs, configobj, wcsmap=wcsmap, procSteps=procSteps) #look for cosmic rays drizCR.rundrizCR(imgObjList, configobj, procSteps=procSteps) #Make your final drizzled image adrizzle.drizFinal(imgObjList, outwcs, configobj, wcsmap=wcsmap, procSteps=procSteps) print() print(' '.join([ 'AstroDrizzle Version', __version__, 'is finished processing at ', util._ptime()[0] ]) + '!\n') except: print(textutil.textbox( 'ERROR:\nAstroDrizzle Version %s encountered a problem! ' 'Processing terminated at %s.' % (__version__, util._ptime()[0])), file=sys.stderr) procSteps.reportTimes() if imgObjList is not None: for image in imgObjList: image.close() del imgObjList del outwcs procSteps.reportTimes() if imgObjList is not None: for image in imgObjList: if stateObj['clean']: image.clean() image.close() del imgObjList del outwcs
def run(configobj, wcsmap=None): """ Initial example by Nadia ran MD with configobj EPAR using: It can be run in one of two ways: from stsci.tools import teal 1. Passing a config object to teal teal.teal('drizzlepac/pars/astrodrizzle.cfg') 2. Passing a task name: teal.teal('astrodrizzle') The example config files are in drizzlepac/pars """ # turn on logging, redirecting stdout/stderr messages to a log file # while also printing them out to stdout as well # also, initialize timing of processing steps # # We need to define a default logfile name from the user's parameters input_list, output, ivmlist, odict = \ processInput.processFilenames(configobj['input']) if output is not None: def_logname = output elif len(input_list) > 0: def_logname = input_list[0] else: print(textutil.textbox( "ERROR:\nNo valid input files found! Please restart the task " "and check the value for the 'input' parameter."), file=sys.stderr) def_logname = None return clean = configobj['STATE OF INPUT FILES']['clean'] procSteps = util.ProcSteps() print("AstroDrizzle Version {:s} ({:s}) started at: {:s}\n" .format(__version__, __version_date__, util._ptime()[0])) util.print_pkg_versions(log=log) log.debug('') log.debug( "==== AstroDrizzle was invoked with the following parameters: ====" ) log.debug('') util.print_cfg(configobj, log.debug) try: # Define list of imageObject instances and output WCSObject instance # based on input paramters imgObjList = None procSteps.addStep('Initialization') imgObjList, outwcs = processInput.setCommonInput(configobj) procSteps.endStep('Initialization') if imgObjList is None or not imgObjList: errmsg = "No valid images found for processing!\n" errmsg += "Check log file for full details.\n" errmsg += "Exiting AstroDrizzle now..." print(textutil.textbox(errmsg, width=65)) print(textutil.textbox( 'ERROR:\nAstroDrizzle Version {:s} encountered a problem! ' 'Processing terminated at {:s}.' .format(__version__, util._ptime()[0])), file=sys.stderr) return log.info("USER INPUT PARAMETERS common to all Processing Steps:") util.printParams(configobj, log=log) # Call rest of MD steps... #create static masks for each image staticMask.createStaticMask(imgObjList, configobj, procSteps=procSteps) #subtract the sky sky.subtractSky(imgObjList, configobj, procSteps=procSteps) # _dbg_dump_virtual_outputs(imgObjList) #drizzle to separate images adrizzle.drizSeparate(imgObjList, outwcs, configobj, wcsmap=wcsmap, procSteps=procSteps) # _dbg_dump_virtual_outputs(imgObjList) #create the median images from the driz sep images createMedian.createMedian(imgObjList, configobj, procSteps=procSteps) #blot the images back to the original reference frame ablot.runBlot(imgObjList, outwcs, configobj, wcsmap=wcsmap, procSteps=procSteps) #look for cosmic rays drizCR.rundrizCR(imgObjList, configobj, procSteps=procSteps) #Make your final drizzled image adrizzle.drizFinal(imgObjList, outwcs, configobj, wcsmap=wcsmap, procSteps=procSteps) print() print("AstroDrizzle Version {:s} is finished processing at {:s}.\n" .format(__version__, util._ptime()[0])) except: clean = False print(textutil.textbox( "ERROR:\nAstroDrizzle Version {:s} encountered a problem! " "Processing terminated at {:s}." .format(__version__, util._ptime()[0])), file=sys.stderr) raise finally: procSteps.reportTimes() if imgObjList: for image in imgObjList: if clean: image.clean() image.close() del imgObjList del outwcs