def match_2dhist_fit(imglist, reference_catalog, print_fit_parameters=True): """Perform cross-matching and final fit using 2dHistogram matching Parameters ---------- imglist : list List of input image NDData objects with metadata and source catalogs reference_catalog : Table Astropy Table of reference sources for this field print_fit_parameters : bool Specify whether or not to print out FIT results for each chip Returns -------- fit_rms : float Visit level RMS for the FIT fit_num : int Number of sources used to generate visit level FIT and `fit_rms` """ print( "-------------------- STEP 5b: (match_2dhist_fit) Cross matching and fitting --------------------" ) # Specify matching algorithm to use match = tweakwcs.TPMatch(searchrad=75, separation=0.1, tolerance=2.0, use2dhist=True) # Align images and correct WCS tweakwcs.tweak_image_wcs(imglist, reference_catalog, match=match) # Interpret RMS values from tweakwcs interpret_fit_rms(imglist, reference_catalog) # determine the quality of the fit fit_rms, fit_num = determine_fit_quality( imglist, print_fit_parameters=print_fit_parameters) return fit_rms, fit_num
def test_pipeline(self, input_filenames): """Test of new pipeline alignment components (call separately) This test performs separate fits to each chip separately. Success Criteria ----------------- * nmatches > 0 on all chips * xrms and yrms < 30mas on all chips """ self.input_loc = 'catalog_tests' self.curdir = os.getcwd() truth_path = [self.input_repo, self.tree, self.input_loc, *self.ref_loc] if not isinstance(input_filenames, list): input_filenames = [input_filenames] # Use this to collect all failures BEFORE throwing AssertionError failures=False try: # Make local copies of input files local_files = [] for infile in input_filenames: downloaded_files = self.get_input_file(infile, docopy=True) local_files.extend(downloaded_files) # generate reference catalog refcat = amutils.create_astrometric_catalog(local_files, catalog='GAIADR2') # Generate source catalogs for each input image source_catalogs = generate_source_catalogs(local_files) # Convert input images to tweakwcs-compatible NDData objects and # attach source catalogs to them. imglist = [] for group_id,image in enumerate(local_files): imglist.extend(amutils.build_nddata(image, group_id, source_catalogs[image]['catalog_table'])) # Specify matching algorithm to use match = tweakwcs.TPMatch(searchrad=250, separation=0.1, tolerance=5, use2dhist=True) # Align images and correct WCS tweakwcs.tweak_image_wcs(imglist, refcat, match=match) for chip in imglist: status = chip.meta['tweakwcs_info']['status'] if status.startswith('FAIL'): failures = True print("STATUS for {}: {}".format(chip.meta['filename'], status)) break except Exception: failures = True print("ALIGNMENT EXCEPTION: Failed to align {}".format(infile)) if not failures: for chip in imglist: tweak_info = chip.meta.get('tweakwcs_info', None) chip_id = chip.meta.get('chip_id',1) # determine 30mas limit in pixels xylimit = 30 # Perform comparisons nmatches = tweak_info['nmatches'] xrms,yrms = tweak_info['rms'] xrms *= chip.wcs.pscale*1000 yrms *= chip.wcs.pscale*1000 if any([nmatches==0, xrms>xylimit, yrms>xylimit]): failures = True msg1 = "Observation {}[{}] failed alignment with " msg2 = " RMS=({:.4f},{:.4f})mas [limit:{:.4f}mas] and NMATCHES={}" print(msg1.format(infile, chip_id)) print(msg2.format(xrms, yrms, xylimit, nmatches)) assert(not failures)
def perform_align(input_list, archive=False, clobber=False, update_hdr_wcs=False): """Main calling function. Parameters ---------- input_list : list List of one or more IPPSSOOTs (rootnames) to align. archive : Boolean Retain copies of the downloaded files in the astroquery created sub-directories? clobber : Boolean Download and overwrite existing local copies of input files? update_hdr_wcs : Boolean Write newly computed WCS information to image image headers? Returns ------- int value 0 if successful, int value 1 if unsuccessful """ # Define astrometric catalog list in priority order catalogList = ['GAIADR2', 'GSC241'] numCatalogs = len(catalogList) # 0: print git info print( "-------------------- STEP 0: Display Git revision info --------------------" ) full_path = os.path.dirname(__file__) + "/utils" repo_path = None if "hlapipeline/hlapipeline" in full_path: repo_path = full_path.split( "hlapipeline/hlapipeline")[0] + "hlapipeline" elif "hlapipeline" in full_path: repo_path = full_path.split("hlapipeline")[0] + "hlapipeline" else: pass if not os.path.exists(repo_path): repo_path = None # protect against non-existent paths if repo_path: get_git_rev_info.print_rev_id( repo_path) # Display git repository information else: print( "WARNING: Unable to display Git repository revision information.") # 1: Interpret input data and optional parameters print("-------------------- STEP 1: Get data --------------------") imglist = check_and_get_data(input_list, archive=archive, clobber=clobber) print("\nSUCCESS") # 2: Apply filter to input observations to insure that they meet minimum criteria for being able to be aligned print("-------------------- STEP 2: Filter data --------------------") filteredTable = filter.analyze_data(imglist) # Check the table to determine if there is any viable data to be aligned. The # 'doProcess' column (bool) indicates the image/file should or should not be used # for alignment purposes. if filteredTable['doProcess'].sum() == 0: print("No viable images in filtered table - no processing done.\n") return (1) # Get the list of all "good" files to use for the alignment processList = filteredTable['imageName'][np.where( filteredTable['doProcess'])] processList = list( processList ) #Convert processList from numpy list to regular python list print("\nSUCCESS") # 3: Build WCS for full set of input observations print("-------------------- STEP 3: Build WCS --------------------") refwcs = amutils.build_reference_wcs(processList) print("\nSUCCESS") # 4: Retrieve list of astrometric sources from database # While loop to accommodate using multiple catalogs doneFitting = False catalogIndex = 0 extracted_sources = None while not doneFitting: skip_all_other_steps = False retry_fit = False print( "-------------------- STEP 4: Detect astrometric sources --------------------" ) print("Astrometric Catalog: ", catalogList[catalogIndex]) reference_catalog = generate_astrometric_catalog( processList, catalog=catalogList[catalogIndex]) # The table must have at least MIN_CATALOG_THRESHOLD entries to be useful if len(reference_catalog) >= MIN_CATALOG_THRESHOLD: print("\nSUCCESS") else: if catalogIndex < numCatalogs - 1: print("Not enough sources found in catalog " + catalogList[catalogIndex]) print("Try again with the next catalog") catalogIndex += 1 retry_fit = True skip_all_other_steps = True else: print( "Not enough sources found in any catalog - no processing done." ) return (1) if not skip_all_other_steps: # 5: Extract catalog of observable sources from each input image print( "-------------------- STEP 5: Source finding --------------------" ) if not extracted_sources: extracted_sources = generate_source_catalogs(processList) for imgname in extracted_sources.keys(): table = extracted_sources[imgname]["catalog_table"] # The catalog of observable sources must have at least MIN_OBSERVABLE_THRESHOLD entries to be useful total_num_sources = 0 for chipnum in table.keys(): total_num_sources += len(table[chipnum]) if total_num_sources < MIN_OBSERVABLE_THRESHOLD: print( "Not enough sources ({}) found in image {}".format( total_num_sources, imgname)) return (1) # Convert input images to tweakwcs-compatible NDData objects and # attach source catalogs to them. imglist = [] for group_id, image in enumerate(processList): imglist.extend( amutils.build_nddata( image, group_id, extracted_sources[image]['catalog_table'])) print("\nSUCCESS") # 6: Cross-match source catalog with astrometric reference source catalog, Perform fit between source catalog and reference catalog print( "-------------------- STEP 6: Cross matching and fitting --------------------" ) # Specify matching algorithm to use match = tweakwcs.TPMatch(searchrad=250, separation=0.1, tolerance=100, use2dhist=False) # Align images and correct WCS tweakwcs.tweak_image_wcs(imglist, reference_catalog, match=match) # Interpret RMS values from tweakwcs interpret_fit_rms(imglist, reference_catalog) tweakwcs_info_keys = OrderedDict( imglist[0].meta['tweakwcs_info']).keys() imgctr = 0 for item in imglist: retry_fit = False #Handle fitting failures (no matches found) if item.meta['tweakwcs_info']['status'].startswith( "FAILED") == True: if catalogIndex < numCatalogs - 1: print( "No cross matches found between astrometric catalog and sources found in images" ) print("Try again with the next catalog") catalogIndex += 1 retry_fit = True break else: print( "No cross matches found in any catalog - no processing done." ) return (1) max_rms_val = item.meta['tweakwcs_info']['TOTAL_RMS'] num_xmatches = item.meta['tweakwcs_info']['nmatches'] # print fit params to screen print( "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ FIT PARAMETERS ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~" ) if item.meta['chip'] == 1: image_name = processList[imgctr] imgctr += 1 print("image: {}".format(image_name)) print("chip: {}".format(item.meta['chip'])) print("group_id: {}".format(item.meta['group_id'])) for tweakwcs_info_key in tweakwcs_info_keys: if not tweakwcs_info_key.startswith("matched"): print("{} : {}".format( tweakwcs_info_key, item.meta['tweakwcs_info'][tweakwcs_info_key])) # print("Radial shift: {}".format(math.sqrt(item.meta['tweakwcs_info']['shift'][0]**2+item.meta['tweakwcs_info']['shift'][1]**2))) print( "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~" ) if num_xmatches < MIN_CROSS_MATCHES: if catalogIndex < numCatalogs - 1: print( "Not enough cross matches found between astrometric catalog and sources found in images" ) print("Try again with the next catalog") catalogIndex += 1 retry_fit = True break else: print( "Not enough cross matches found in any catalog - no processing done." ) return (1) elif max_rms_val > MAX_FIT_RMS: if catalogIndex < numCatalogs - 1: print( "Fit RMS value = {}mas greater than the maximum threshold value {}." .format( item.meta['tweakwcs_info']['FIT_RMS'].value, MAX_FIT_RMS)) print("Try again with the next catalog") catalogIndex += 1 retry_fit = True break else: print( "Fit RMS values too large using any catalog - no processing done." ) return (1) else: print("Fit calculations successful.") if not retry_fit: print("\nSUCCESS") # 7: Write new fit solution to input image headers print( "-------------------- STEP 7: Update image headers with new WCS information --------------------" ) if update_hdr_wcs: update_image_wcs_info(imglist, processList) print("\nSUCCESS") else: print("\n STEP SKIPPED") return (0)