def show_region(fig, region_list): h = demo_header() n = len(region_list) nx = int(math.ceil(n**.5)) ny = int(math.ceil(1.*n/nx)) nrows_ncols = (ny, nx) grid = [plt.subplot(ny, nx, i+1) for i in range(n)] for ax, reg_name in zip(grid, region_list): ax.set_aspect(1) r = stregion.open(reg_name).as_imagecoord(h) patch_list, text_list = r.get_mpl_patches_texts() for p in patch_list: ax.add_patch(p) for t in text_list: ax.add_artist(t) if plt.rcParams["text.usetex"]: reg_name = reg_name.replace("_", r"\_") ax.set_title(reg_name, size=10) for t in ax.get_xticklabels() + ax.get_yticklabels(): t.set_visible(False) return grid
def test_region(): ref_region_name = "test01_img.reg" region_list = [ "test01_fk5_sexagecimal.reg", "test01_gal.reg", "test01_ds9_physical.reg", "test01_fk5_degree.reg", "test01_mixed.reg", "test01_ciao.reg", "test01_ciao_physical.reg", ] header = demo_header() ref_region = stregion.open(join(rootdir, ref_region_name)).as_imagecoord(header) for reg_name in region_list: r = stregion.open(join(rootdir, reg_name)).as_imagecoord(header) for reg0, reg in zip(ref_region, r): if reg.name == "rotbox": reg.name = "box" assert reg0.name == reg.name if reg0.name in ["ellipse", "box"]: assert np.allclose(reg0.coord_list[:-1], reg.coord_list[:-1], atol=0.01) a0 = reg0.coord_list[-1] a1 = fix_lon(reg.coord_list[-1], 0) assert np.allclose([a0], [a1], atol=0.02) else: assert np.allclose(reg0.coord_list, reg.coord_list, atol=0.01) assert reg0.exclude == reg.exclude
def build_img_ext_reg_list(images, chip_reg=None, img_wcs_ext='sci', verbose=True): # Check that the 'chip_reg' argument is of correct type/form: if chip_reg is None: nreg = 0 multireg = False chip_reg = [] elif isinstance(chip_reg, str): nreg = 1 chip_reg = [chip_reg] multireg = False elif isinstance(chip_reg, list): chip_reg = chip_reg[:] nreg = len(chip_reg) multireg = True # check that all elements of the list are either strings or None: if [ True for reg in chip_reg if reg is not None and not isinstance(reg, str) ]: raise TypeError("Argument 'chip_reg' can be either None, " \ "a string, or a list of stings and/or None.") else: raise TypeError("Argument 'chip_reg' can be either None, " \ "a string, or a list of stings and/or None.") # from the 'images' argument built a list of file *names* # ignoring extensions(!) and check that the files exist: imgfnames = [] if not images: raise TypeError("Argument 'images' cannot be an empty list or None.") if not isinstance(images, list): images = [images] for fname in images: if not isinstance(fname, str): raise RuntimeError("Argument 'images' must be either a string " \ "or a non-empty list of strings of valid file names.") (fn, ext) = extension_from_filename(fname) if not os.path.isfile(fn): raise IOError("The image file \'%s\' does not exist." % fn) imgfnames.append(fn) # Get the HDU list of the first file in the list of images. This will be # re-used to check available extensions. try: hdulist = fits.open(imgfnames[0], memmap=False) hdulist.close() except IOError as e: cmsg = "Unable to open the image file \'%s\'." % imgfnames[0] if e.args: e.args = (e.args[0] + "\n" + cmsg, ) + e.args[1:] else: e.args = (cmsg, ) raise e except: raise # initial processing of extensions (get a list of integers, str, or tuples): exts = parse_ext(img_wcs_ext, default_extver=None) if not isinstance(exts, list): exts = [exts] # for each string extension in the extension list 'exts', find out how many # extension versions exist and create a new list of extensions in which the # string (e.g., 'sci') gets replaced with tuples for each available # extension version: [('sci',1),('sci',2), etc.] extensions = [] for ext in exts: if isinstance(ext, int) or isinstance(ext, tuple): extensions.append(ext) continue extv = get_extver_list(hdulist, extname=ext) for v in extv: extensions.append((ext, v)) if not _check_FITS_extensions(hdulist, extensions): raise RuntimeError("Not all extensions computed based on the " \ "provided 'img_wcs_ext' could be found in the HDU list of the " \ "image(s) specified by the argument 'images'. Make sure that all " \ "extensions in the 'img_wcs_ext' list are present in input 'images'.") nexts = len(extensions) # <- number of "valid" extensions # Warn users if some regions will be dropped out: if nreg > nexts: print("") _print_warning("The number of region files provided through " \ "'chip_reg' is larger than the number of extensions " \ "derived based on 'img_wcs_ext' that were found " \ "in 'images'.") _print_warning("The following \"fixed\" region files will " \ "be dropped out:") for i in range(nexts, nreg): _print_warning("chip_reg[%d]: %s" % (i, str(chip_reg[i]))) chip_reg = chip_reg[:nexts] # Warn users if there are more extensions than "chip" regions: if nreg == 0: chip_reg = nexts * [None] elif nreg < nexts: print("") _print_warning("The number of region files provided through " \ "'chip_reg' is smaller than the number of extensions " \ "derived based on 'img_wcs_ext' that were found " \ "in 'images'.") _print_warning("The following extensions have not been assigned any " \ "\"fixed\" (chip related) regions:") extlist = " " for ext in extensions[nreg:-1]: chip_reg.append(None) extlist += str(ext) + ", " extlist += str(extensions[-1]) chip_reg.append(None) _print_warning(extlist) # Check that the region files exist and try to open/read them: region_lists = [] for fname in chip_reg: if fname is None: region_lists.append(None) continue # check region file existence: if not os.path.isfile(fname): raise IOError("The input \"chip\" region file \'%s\' does not " \ "exist." % fname) # try to read regions: try: reglist = pyregion.open(fname) region_lists.append(reglist) #TODO: Check that regions are all in *image* coordinates except IOError: raise IOError("Unable to open the input region file \'%s\'." % \ fname) except: raise RuntimeError("Unexpected error parsing input region " \ "file \'%s\'. Suspecting either a corrupt " \ "file or an unrecognizable region file format." \ % fname) # Print the list of pairs extension->chip region: if verbose and nreg > 0: print("") _print_important("Please verify that the following association " \ "between the commanded FITS extensions and provided \"fixed\" " \ "region files is correct:") print("-----------------------------") print("EXTENSION: --> REGION FILE:") for i in range(nexts): print("{!s:<18.18s} {}".format(extensions[i], chip_reg[i])) # Return a list of image file names (with any extensions removed!) to which # regions will be mapped AND a list of pairs of the form # [region, extension]. return imgfnames, list(map(list, list(zip(region_lists, extensions))))
def build_reg_refwcs_header_list(input_reg, refimg, ref_wcs_ext, verbose): # check input region file names and initialize region lists: region_lists = [] single_inp_reg = False if isinstance(input_reg, str) and len(input_reg) > 0: # a single region file single_inp_reg = True input_regfnames = [input_reg] ref_wcs_exts = [0] # for a single input region we assume that the # default location of the WCS is in the primary # header (if nothing else specified) elif isinstance(input_reg, list) and input_reg: # a non-empty list of files # select non-zero length file names;replace all other elements with None input_regfnames = [fname if isinstance(fname,str) and \ len(fname)>0 else None for fname in input_reg] # Populate ref_wcs_exts with "default" FITS extension numbers according # to the input region position in the input_regfnames list # (starting with 1). That is, if multiple regions are given, then we # assume that (if no extension name is provided) the WCS is located in # "extensions" of the input reference FITS file (as opposite to the # primary header). ref_wcs_exts = list(range(1, len(input_regfnames) + 1)) # Filter out elements of ref_wcs_exts and input_regfnames that # correspond to None elements in input_regfnames: ref_wcs_exts = [ext for reg,ext in zip(input_regfnames,ref_wcs_exts) \ if reg is not None] input_regfnames = [reg for reg in input_regfnames if reg is not None] else: input_regfnames = [] ref_wcs_exts = [] # unnecessary, really, because of the next "if" # Check that we have at least one "valid" input region file name if len(input_regfnames) < 1: raise RuntimeError("Parameter 'input_reg' must be either a non-zero " \ "length string or a non-empty list with at least " \ "one non-zero length string file name.") # Check that the region files exist and try to open/read them: ireg = 0 for fname in input_regfnames: # check region file existence: if not os.path.isfile(fname): raise IOError("The input region file \'%s\' does not exist." % \ fname) # try to read regions: try: reglist = pyregion.open(fname) region_lists.append(reglist) except IOError: raise IOError("Unable to open the input region file \'%s\'." % \ fname) except: raise RuntimeError("Unexpected error parsing input region " \ "file \'%s\'. Suspecting either a corrupt file or " \ "an unrecognizable region file format." % fname) # check if WCS is needed to convert from image to sky CS if not _needs_ref_WCS(reglist): ref_wcs_exts[ireg] = None ireg += 1 # If WCS is needed for conversion to sky coordinates, find the correct # FITS extension based either on extension specified in the input reference # image 'refimg', optional input argument 'ref_wcs_ext', and/or position of # the region file in the input region list 'input_reg' if [True for ireg in ref_wcs_exts if ireg is not None]: # A reference WCS is required. Check that 'refimg' is an existing file: if refimg is None: raise ValueError("Argument 'refimg' cannot be None when some " \ "input regions are given in logical coordinates.") if not isinstance(refimg, str): raise TypeError("Argument 'refimg' must be a string with the " \ "name of an existing FITS file and, optionally, " \ "followed by an extension specification.") (refimg_fname, frefext) = extension_from_filename(refimg) if not os.path.isfile(refimg_fname): raise IOError("The reference FITS file \'%s\' does not exist." % \ refimg_fname) if frefext: # Try to determine the extension name from the reference file name refext = parse_ext(frefext) if single_inp_reg: if isinstance(refext, tuple) or isinstance(refext, int): ref_wcs_exts = [refext] elif isinstance(refext, str): # it is a string: ref_wcs_exts = [(refext, 1)] else: raise RuntimeError("Logical error in the code.") else: # check that FITS extension specified in the reference image file # name is not too restrictive (for multiple regions it cannot # contain a specific extver): if isinstance(refext, tuple): raise RuntimeError("Extension version ('EXTVER') in the " \ "reference file name should not be present when multiple " \ "region files (in logical CS) are provided as input. " \ "Only extension name ('EXTNAME') is allowed (e.g., " \ "[SCI], [DQ], etc.)") if isinstance(refext, int): raise RuntimeError("Extension number (e.g., [0], [2], " \ "etc.) in the reference file name should not be present " \ "when multiple region files (in logical CS) are " \ "provided as input. Only extension name ('EXTNAME') is " \ "allowed (e.g., [SCI], [DQ], etc.)") ref_wcs_exts = [None if extn is None else (refext, extn) \ for extn in ref_wcs_exts] elif ref_wcs_ext: # Try to determine the extension name from the 'ref_wcs_ext' # argument: refext = parse_ext(ref_wcs_ext) if single_inp_reg: if isinstance(refext, tuple) or isinstance(refext, int): ref_wcs_exts = [refext] elif isinstance(refext, str): # it is a string: ref_wcs_exts = [(refext, 1)] else: raise RuntimeError("Logical error in the code.") else: # check that FITS extension specified in the reference image # file name is not too restrictive (for multiple regions it # cannot contain a specific extver): if isinstance(refext, tuple): raise RuntimeError("Extension version ('EXTVER') in the " \ "'ref_wcs_ext' argument should not be present when " \ "multiple region files (in logical CS) are provided " \ "as input. Only extension name ('EXTNAME') is allowed " \ "(e.g., [SCI], [DQ], etc.)") if isinstance(refext, int): raise RuntimeError("Extension number (e.g., [0], [2], " \ "etc.) in the 'ref_wcs_ext' argument should not be " \ "present when multiple region files (in logical CS) are " \ "provided as input. Only extension name ('EXTNAME') is " \ "allowed (e.g., [SCI], [DQ], etc.)") ref_wcs_exts = [None if extn is None else (refext, extn) \ for extn in ref_wcs_exts] # check if the extensions found above are present # in the reference WCS FITS file: if (refimg is not None and not _check_FITS_extensions(refimg, ref_wcs_exts)): raise RuntimeError("Not all FITS extensions derived based on the " \ "input region(s) and extension names have been found in the " \ "input reference WCS file. Unable to proceed.") # load headers containing WCS from the reference input FITS file: ref_wcs_headers = [None if extn is None \ else fits.getheader(refimg_fname, ext=extn, memmap=False) \ for extn in ref_wcs_exts ] #TODO: return WCS instead of header else: # no WCS is needed (regions are already in sky coordinates): ref_wcs_headers = [None for reg in region_lists] # Return a list of pairs of the form [region, header] with missing regions # (i.e., region = None) filtered out. (We do not need to keep order anymore) return [p for p in map(list, list(zip(region_lists, ref_wcs_headers))) \ if p[0] is not None]
def map_region_files(input_reg, images, img_wcs_ext='sci', refimg=None, ref_wcs_ext='sci', chip_reg=None, outpath='./regions', filter=None, catfname=None, iteractive=False, append=False, verbose=True): # Check that output directory exists: if outpath in [None, ""]: outpath = os.path.curdir + os.path.sep elif not os.path.isdir(outpath): raise IOError("The output directory \'%s\' does not exist." % outpath) if filter is not None and filter.lower() not in ["fast", "precise"]: raise TypeError("The 'filter' argument can be None, 'fast', " \ "or 'precise'.") #TODO: Implement the "precise" checking of region intersection # with the image's bounding box. if filter is not None and filter.lower() == "precise": _print_warning("\"precise\" filter option is not yet supported. "\ "Reverting to filter = \"fast\" instead.") filter = "fast" # create a 2D list of region - header (with WCS) pairs: # [[reg1,ref_wcs_header1],[reg2,ref_wcs_header2],...] regheaders = build_reg_refwcs_header_list(input_reg, refimg, ref_wcs_ext, verbose) # create a list of input files *without* extensions (if present) and # a 2D list of "chip" region - image extension pairs: # [[reg1, ext1], [reg2,ext2],...] imgfnames, cregext = build_img_ext_reg_list(images, chip_reg, img_wcs_ext, verbose) # build a "master" list of regions in sky coordinates: all_sky_regions = [] for p in regheaders: if p[1] is None: # Already in sky WCS all_sky_regions += p[0] else: all_sky_regions += shapes_img2sky( p[0], p[1]) #TODO: implement shapes_img2sky all_sky_regions = pyregion.ShapeList(list(all_sky_regions)) cattb = [] # create a region file (with regions in image coordinates) for each # image extension from the input_reg and chip_reg regions for fname in imgfnames: imghdu = None imghdu = fits.open(fname, memmap=False) catreg = [] try: for extp in cregext: ext = extp[1] # check that the extension is of supported type: if not _is_supported_hdu(imghdu[ext].header): raise RuntimeError("Extension {} is of unsupported " \ "type.".format(ext)) # Remove references to non-SIP distortions from the header #TODO: remove this line of code as well as the remove_header_tdd # function once publicly available release of pyregion uses # all_world2pix and all_pix2world functions and does this header # cleanup itself. # remove_header_tdd(imghdu[ext].header) ####### added to pass hstwcs instead of header to pyregion # if isinstance(ext, str): # ext = findExtname(imghdu, extname = ext, extver = 1) # elif isinstance(ext, tuple): # ext = findExtname(imghdu, extname = ext[0], extver = ext[1]) # wcs = stwcs.wcsutil.HSTWCS(imghdu, ext=ext) wcs = _AuxSTWCS(imghdu, ext=ext) extreg = all_sky_regions.as_imagecoord(wcs, rot_wrt_axis=2) ####### #extreg = all_sky_regions.as_imagecoord(imghdu[ext].header, rot_wrt_axis=2) if extp[0]: # add "fixed" regions if any extreg = pyregion.ShapeList(extreg + extp[0]) # filter out regions outside the image: if filter and filter.lower() == 'fast': fast_filter_outer_regions(extreg, imghdu[ext].header['NAXIS1'], imghdu[ext].header['NAXIS2'], origin=1) if len(extreg) < 1: # do not create empty region files catreg.append('None') continue # generate output region file name: extsuffix = _ext2str_suffix(ext) basefname, fext = os.path.splitext(os.path.basename(fname)) regfname = basefname + extsuffix + os.extsep + "reg" fullregfname = os.path.join(outpath, regfname) catreg.append(regfname) # save regions to a file: if append and os.path.isfile(fullregfname): old_extreg = pyregion.open(fullregfname) extreg = pyregion.ShapeList(old_extreg + extreg) #TODO: pyregion.write does not work well. For now use _regwite # (until the bug fixes get to the pyregion project). #TODO: we replaced pyregion.write with our implementation # of the write (code from _regwrite) in the locally maintained # pyregion until these changes get to be implemented in the # publicly available release of pyregion. # _regwrite(extreg, fullregfname) #extreg.write(fullregfname) # <- use this instead of _regwrite # once the pyregion bugs are fixed. cattb.append([fname, catreg]) except: if imghdu: imghdu.close() raise # create exclusions catalog file: if catfname: catfh = open(os.path.join(outpath, catfname), 'w') for catentry in cattb: catfh.write(catentry[0]) # image file name for reg in catentry[1]: catfh.write(' ' + reg) # region file name catfh.write('\n') catfh.close()
def _combine_exclude_mask(self, mask): # create masks from exclude/include regions and combine it with the # input DQ mask: # regmask = None if self.src_find_filters is not None and \ 'region_file' in self.src_find_filters: reg_file_name = self.src_find_filters['region_file'] if not os.path.isfile(reg_file_name): raise IOError( "The 'exclude' region file '{:s}' does not exist.".format( reg_file_name)) else: return mask # get data image size: (img_ny, img_nx) = self.source.shape # find out if user provided a region file or a mask FITS file: reg_file_ext = os.path.splitext(reg_file_name)[-1] if reg_file_ext.lower().strip() in ['.fits', '.fit'] and \ basicFITScheck(reg_file_name): # likely we are dealing with a FITS file. # check that the file is a simple with 2 axes: hdulist = fits.open(reg_file_name, memmap=False) extlist = get_extver_list(hdulist, extname=None) for ext in extlist: usermask = hdulist[ext].data if usermask.shape == (img_ny, img_nx): regmask = usermask.astype(np.bool) break hdulist.close() if regmask is None: raise ValueError("None of the image-like extensions in the " "user-provided exclusion mask '{}' has a " "correct shape".format(reg_file_name)) else: # we are dealing with a region file: reglist = pyregion.open(reg_file_name) ## check that regions are in image-like coordinates: ##TODO: remove the code below once 'pyregion' package can correctly ## (DS9-like) convert sky coordinates to image coordinates for all ## supported shapes. #if not all([ (x.coord_format == 'image' or \ # x.coord_format == 'physical') for x in reglist]): # print("WARNING: Some exclusion regions are in sky coordinates.\n" # " These regions will be ignored.") # # filter out regions in sky coordinates: # reglist = pyregion.ShapeList( # [x for x in reglist if x.coord_format == 'image' or \ # x.coord_format == 'physical'] # ) #TODO: comment out next lines if we do not support region files # in sky coordinates and uncomment previous block: # Convert regions from sky coordinates to image coordinates: auxwcs = _AuxSTWCS(self.wcs) reglist = reglist.as_imagecoord(auxwcs, rot_wrt_axis=2) # if all regions are exclude regions, then assume that the entire image # should be included and that exclude regions exclude from this # rectangular region representing the entire image: if all([x.exclude for x in reglist]): # we slightly widen the box to make sure that # the entire image is covered: imreg = pyregion.parse( "image;box({:.1f},{:.1f},{:d},{:d},0)".format( (img_nx + 1) / 2.0, (img_ny + 1) / 2.0, img_nx + 1, img_ny + 1)) reglist = pyregion.ShapeList(imreg + reglist) # create a mask from regions: regmask = np.asarray(reglist.get_mask(shape=(img_ny, img_nx)), dtype=np.bool) if mask is not None and regmask is not None: mask = np.logical_and(regmask, mask) else: mask = regmask #DEBUG: if mask is not None: fn = os.path.splitext(self.fname)[0] + '_srcfind_mask.fits' fits.writeto(fn, mask.astype(dtype=np.uint8), overwrite=True) return mask
def _combine_exclude_mask(self, mask): # create masks from exclude/include regions and combine it with the # input DQ mask: # regmask = None if self.src_find_filters is not None and \ 'region_file' in self.src_find_filters: reg_file_name = self.src_find_filters['region_file'] if not os.path.isfile(reg_file_name): raise IOError("The 'exclude' region file '{:s}' does not exist." .format(reg_file_name)) else: return mask # get data image size: (img_ny, img_nx) = self.source.shape # find out if user provided a region file or a mask FITS file: reg_file_ext = os.path.splitext(reg_file_name)[-1] if reg_file_ext.lower().strip() in ['.fits', '.fit'] and \ basicFITScheck(reg_file_name): # likely we are dealing with a FITS file. # check that the file is a simple with 2 axes: hdulist = fits.open(reg_file_name, memmap=False) extlist = get_extver_list(hdulist,extname=None) for ext in extlist: usermask = hdulist[ext].data if usermask.shape == (img_ny, img_nx): regmask = usermask.astype(np.bool) break hdulist.close() if regmask is None: raise ValueError("None of the image-like extensions in the " "user-provided exclusion mask '{}' has a " "correct shape".format(reg_file_name)) else: # we are dealing with a region file: reglist = pyregion.open(reg_file_name) ## check that regions are in image-like coordinates: ##TODO: remove the code below once 'pyregion' package can correctly ## (DS9-like) convert sky coordinates to image coordinates for all ## supported shapes. #if not all([ (x.coord_format == 'image' or \ # x.coord_format == 'physical') for x in reglist]): # print("WARNING: Some exclusion regions are in sky coordinates.\n" # " These regions will be ignored.") # # filter out regions in sky coordinates: # reglist = pyregion.ShapeList( # [x for x in reglist if x.coord_format == 'image' or \ # x.coord_format == 'physical'] # ) #TODO: comment out next lines if we do not support region files # in sky coordinates and uncomment previous block: # Convert regions from sky coordinates to image coordinates: auxwcs = _AuxSTWCS(self.wcs) reglist = reglist.as_imagecoord(auxwcs, rot_wrt_axis=2) # if all regions are exclude regions, then assume that the entire image # should be included and that exclude regions exclude from this # rectangular region representing the entire image: if all([x.exclude for x in reglist]): # we slightly widen the box to make sure that # the entire image is covered: imreg = pyregion.parse("image;box({:.1f},{:.1f},{:d},{:d},0)" .format((img_nx+1)/2.0, (img_ny+1)/2.0, img_nx+1, img_ny+1) ) reglist = pyregion.ShapeList(imreg + reglist) # create a mask from regions: regmask = np.asarray( reglist.get_mask(shape=(img_ny, img_nx)), dtype=np.bool ) if mask is not None and regmask is not None: mask = np.logical_and(regmask, mask) else: mask = regmask #DEBUG: if mask is not None: fn = os.path.splitext(self.fname)[0] + '_srcfind_mask.fits' fits.writeto(fn, mask.astype(dtype=np.uint8), overwrite=True) return mask
111, nrows_ncols, ngrids=n, add_all=True, share_all=True, axes_class=(pywcsgrid2.Axes, dict(header=h))) ax = grid[0] ax.set_xlim(300, 1300) ax.set_ylim(300, 1300) ax.set_aspect(1) #plt.imshow(d, origin="lower", cmap=plt.cm.gray_r) from mpl_toolkits.axes_grid.anchored_artists import AnchoredText for ax, reg_name in zip(grid, region_list): r = stregion.open(reg_name).as_imagecoord(h) patch_list, text_list = r.get_mpl_patches_texts() for p in patch_list: ax.add_patch(p) for t in text_list: ax.add_artist(t) atext = AnchoredText(reg_name.replace("_", r"\_"), loc=2) ax.add_artist(atext) plt.draw() plt.show()
try: from astropy.io import fits as pyfits except ImportError: from astropy.io import fits as pyfits import matplotlib.pyplot as plt import stregion import math if 1: reg_name = "test04_img.reg" ax = plt.subplot(111) ax.set_xlim(600, 1100) ax.set_ylim(600, 1100) ax.set_aspect(1) r = stregion.open(reg_name) patch_list, text_list = r.get_mpl_patches_texts() for p in patch_list: ax.add_patch(p) for t in text_list: ax.add_artist(t) plt.draw() plt.show()
def build_img_ext_reg_list(images, chip_reg=None, img_wcs_ext='sci', verbose=True): # Check that the 'chip_reg' argument is of correct type/form: if chip_reg is None: nreg = 0 multireg = False chip_reg = [] elif isinstance(chip_reg, str): nreg = 1 chip_reg = [chip_reg] multireg = False elif isinstance(chip_reg, list): chip_reg = chip_reg[:] nreg = len(chip_reg) multireg = True # check that all elements of the list are either strings or None: if [True for reg in chip_reg if reg is not None and not isinstance(reg, str)]: raise TypeError("Argument 'chip_reg' can be either None, " \ "a string, or a list of stings and/or None.") else: raise TypeError("Argument 'chip_reg' can be either None, " \ "a string, or a list of stings and/or None.") # from the 'images' argument built a list of file *names* # ignoring extensions(!) and check that the files exist: imgfnames = [] if not images: raise TypeError("Argument 'images' cannot be an empty list or None.") if not isinstance(images, list): images = [images] for fname in images: if not isinstance(fname, str): raise RuntimeError("Argument 'images' must be either a string " \ "or a non-empty list of strings of valid file names.") (fn, ext) = extension_from_filename(fname) if not os.path.isfile(fn): raise IOError("The image file \'%s\' does not exist." % fn) imgfnames.append(fn) # Get the HDU list of the first file in the list of images. This will be # re-used to check available extensions. try: hdulist = fits.open(imgfnames[0], memmap=False) hdulist.close() except IOError as e: cmsg = "Unable to open the image file \'%s\'." % imgfnames[0] if e.args: e.args = (e.args[0] + "\n" + cmsg,) + e.args[1:] else: e.args=(cmsg,) raise e except: raise # initial processing of extensions (get a list of integers, str, or tuples): exts = parse_ext(img_wcs_ext, default_extver=None) if not isinstance(exts, list): exts = [exts] # for each string extension in the extension list 'exts', find out how many # extension versions exist and create a new list of extensions in which the # string (e.g., 'sci') gets replaced with tuples for each available # extension version: [('sci',1),('sci',2), etc.] extensions = [] for ext in exts: if isinstance(ext,int) or isinstance(ext,tuple): extensions.append(ext) continue extv = get_extver_list(hdulist, extname=ext) for v in extv: extensions.append((ext,v)) if not _check_FITS_extensions(hdulist, extensions): raise RuntimeError("Not all extensions computed based on the " \ "provided 'img_wcs_ext' could be found in the HDU list of the " \ "image(s) specified by the argument 'images'. Make sure that all " \ "extensions in the 'img_wcs_ext' list are present in input 'images'.") nexts = len(extensions) # <- number of "valid" extensions # Warn users if some regions will be dropped out: if nreg > nexts: print("") _print_warning("The number of region files provided through " \ "'chip_reg' is larger than the number of extensions " \ "derived based on 'img_wcs_ext' that were found " \ "in 'images'.") _print_warning("The following \"fixed\" region files will " \ "be dropped out:") for i in range(nexts,nreg): _print_warning("chip_reg[%d]: %s" % (i,str(chip_reg[i]))) chip_reg = chip_reg[:nexts] # Warn users if there are more extensions than "chip" regions: if nreg == 0: chip_reg = nexts * [None] elif nreg < nexts: print("") _print_warning("The number of region files provided through " \ "'chip_reg' is smaller than the number of extensions " \ "derived based on 'img_wcs_ext' that were found " \ "in 'images'.") _print_warning("The following extensions have not been assigned any " \ "\"fixed\" (chip related) regions:") extlist = " " for ext in extensions[nreg:-1]: chip_reg.append(None) extlist += str(ext)+", " extlist += str(extensions[-1]) chip_reg.append(None) _print_warning(extlist) # Check that the region files exist and try to open/read them: region_lists = [] for fname in chip_reg: if fname is None: region_lists.append(None) continue # check region file existence: if not os.path.isfile(fname): raise IOError("The input \"chip\" region file \'%s\' does not " \ "exist." % fname) # try to read regions: try: reglist = pyregion.open(fname) region_lists.append(reglist) #TODO: Check that regions are all in *image* coordinates except IOError: raise IOError("Unable to open the input region file \'%s\'." % \ fname) except: raise RuntimeError("Unexpected error parsing input region " \ "file \'%s\'. Suspecting either a corrupt " \ "file or an unrecognizable region file format." \ % fname) # Print the list of pairs extension->chip region: if verbose and nreg > 0: print("") _print_important("Please verify that the following association " \ "between the commanded FITS extensions and provided \"fixed\" " \ "region files is correct:") print("-----------------------------") print("EXTENSION: --> REGION FILE:") for i in range(nexts): print("{!s:<18.18s} {}".format(extensions[i], chip_reg[i])) # Return a list of image file names (with any extensions removed!) to which # regions will be mapped AND a list of pairs of the form # [region, extension]. return imgfnames, list(map(list, list(zip(region_lists, extensions))))
def build_reg_refwcs_header_list(input_reg, refimg, ref_wcs_ext, verbose): # check input region file names and initialize region lists: region_lists = [] single_inp_reg = False if isinstance(input_reg, str) and len(input_reg) > 0: # a single region file single_inp_reg = True input_regfnames = [input_reg] ref_wcs_exts = [0] # for a single input region we assume that the # default location of the WCS is in the primary # header (if nothing else specified) elif isinstance(input_reg, list) and input_reg: # a non-empty list of files # select non-zero length file names;replace all other elements with None input_regfnames = [fname if isinstance(fname,str) and \ len(fname)>0 else None for fname in input_reg] # Populate ref_wcs_exts with "default" FITS extension numbers according # to the input region position in the input_regfnames list # (starting with 1). That is, if multiple regions are given, then we # assume that (if no extension name is provided) the WCS is located in # "extensions" of the input reference FITS file (as opposite to the # primary header). ref_wcs_exts = list(range(1, len(input_regfnames)+1)) # Filter out elements of ref_wcs_exts and input_regfnames that # correspond to None elements in input_regfnames: ref_wcs_exts = [ext for reg,ext in zip(input_regfnames,ref_wcs_exts) \ if reg is not None] input_regfnames = [reg for reg in input_regfnames if reg is not None] else: input_regfnames = [] ref_wcs_exts = [] # unnecessary, really, because of the next "if" # Check that we have at least one "valid" input region file name if len(input_regfnames) < 1: raise RuntimeError("Parameter 'input_reg' must be either a non-zero " \ "length string or a non-empty list with at least " \ "one non-zero length string file name.") # Check that the region files exist and try to open/read them: ireg = 0 for fname in input_regfnames: # check region file existence: if not os.path.isfile(fname): raise IOError("The input region file \'%s\' does not exist." % \ fname) # try to read regions: try: reglist = pyregion.open(fname) region_lists.append(reglist) except IOError: raise IOError("Unable to open the input region file \'%s\'." % \ fname) except: raise RuntimeError("Unexpected error parsing input region " \ "file \'%s\'. Suspecting either a corrupt file or " \ "an unrecognizable region file format." % fname) # check if WCS is needed to convert from image to sky CS if not _needs_ref_WCS(reglist): ref_wcs_exts[ireg] = None ireg += 1 # If WCS is needed for conversion to sky coordinates, find the correct # FITS extension based either on extension specified in the input reference # image 'refimg', optional input argument 'ref_wcs_ext', and/or position of # the region file in the input region list 'input_reg' if [True for ireg in ref_wcs_exts if ireg is not None]: # A reference WCS is required. Check that 'refimg' is an existing file: if refimg is None: raise ValueError("Argument 'refimg' cannot be None when some " \ "input regions are given in logical coordinates.") if not isinstance(refimg, str): raise TypeError("Argument 'refimg' must be a string with the " \ "name of an existing FITS file and, optionally, " \ "followed by an extension specification.") (refimg_fname, frefext) = extension_from_filename(refimg) if not os.path.isfile(refimg_fname): raise IOError("The reference FITS file \'%s\' does not exist." % \ refimg_fname) if frefext: # Try to determine the extension name from the reference file name refext = parse_ext(frefext) if single_inp_reg: if isinstance(refext, tuple) or isinstance(refext, int): ref_wcs_exts = [refext] elif isinstance(refext, str): # it is a string: ref_wcs_exts = [(refext, 1)] else: raise RuntimeError("Logical error in the code.") else: # check that FITS extension specified in the reference image file # name is not too restrictive (for multiple regions it cannot # contain a specific extver): if isinstance(refext, tuple): raise RuntimeError("Extension version ('EXTVER') in the " \ "reference file name should not be present when multiple " \ "region files (in logical CS) are provided as input. " \ "Only extension name ('EXTNAME') is allowed (e.g., " \ "[SCI], [DQ], etc.)") if isinstance(refext, int): raise RuntimeError("Extension number (e.g., [0], [2], " \ "etc.) in the reference file name should not be present " \ "when multiple region files (in logical CS) are " \ "provided as input. Only extension name ('EXTNAME') is " \ "allowed (e.g., [SCI], [DQ], etc.)") ref_wcs_exts = [None if extn is None else (refext, extn) \ for extn in ref_wcs_exts] elif ref_wcs_ext: # Try to determine the extension name from the 'ref_wcs_ext' # argument: refext = parse_ext(ref_wcs_ext) if single_inp_reg: if isinstance(refext, tuple) or isinstance(refext, int): ref_wcs_exts = [refext] elif isinstance(refext, str): # it is a string: ref_wcs_exts = [(refext, 1)] else: raise RuntimeError("Logical error in the code.") else: # check that FITS extension specified in the reference image # file name is not too restrictive (for multiple regions it # cannot contain a specific extver): if isinstance(refext, tuple): raise RuntimeError("Extension version ('EXTVER') in the " \ "'ref_wcs_ext' argument should not be present when " \ "multiple region files (in logical CS) are provided " \ "as input. Only extension name ('EXTNAME') is allowed " \ "(e.g., [SCI], [DQ], etc.)") if isinstance(refext, int): raise RuntimeError("Extension number (e.g., [0], [2], " \ "etc.) in the 'ref_wcs_ext' argument should not be " \ "present when multiple region files (in logical CS) are " \ "provided as input. Only extension name ('EXTNAME') is " \ "allowed (e.g., [SCI], [DQ], etc.)") ref_wcs_exts = [None if extn is None else (refext, extn) \ for extn in ref_wcs_exts] # check if the extensions found above are present # in the reference WCS FITS file: if (refimg is not None and not _check_FITS_extensions(refimg, ref_wcs_exts)): raise RuntimeError("Not all FITS extensions derived based on the " \ "input region(s) and extension names have been found in the " \ "input reference WCS file. Unable to proceed.") # load headers containing WCS from the reference input FITS file: ref_wcs_headers = [None if extn is None \ else fits.getheader(refimg_fname, ext=extn, memmap=False) \ for extn in ref_wcs_exts ] #TODO: return WCS instead of header else: # no WCS is needed (regions are already in sky coordinates): ref_wcs_headers = [None for reg in region_lists] # Return a list of pairs of the form [region, header] with missing regions # (i.e., region = None) filtered out. (We do not need to keep order anymore) return [p for p in map(list, list(zip(region_lists, ref_wcs_headers))) \ if p[0] is not None]
def map_region_files(input_reg, images, img_wcs_ext='sci', refimg=None, ref_wcs_ext='sci', chip_reg=None, outpath='./regions', filter=None, catfname=None, iteractive=False, append=False, verbose=True): # Check that output directory exists: if outpath in [None, ""]: outpath = os.path.curdir + os.path.sep elif not os.path.isdir(outpath): raise IOError("The output directory \'%s\' does not exist." % outpath) if filter is not None and filter.lower() not in ["fast","precise"]: raise TypeError("The 'filter' argument can be None, 'fast', " \ "or 'precise'.") #TODO: Implement the "precise" checking of region intersection # with the image's bounding box. if filter is not None and filter.lower() == "precise": _print_warning("\"precise\" filter option is not yet supported. "\ "Reverting to filter = \"fast\" instead.") filter = "fast" # create a 2D list of region - header (with WCS) pairs: # [[reg1,ref_wcs_header1],[reg2,ref_wcs_header2],...] regheaders = build_reg_refwcs_header_list(input_reg, refimg, ref_wcs_ext, verbose) # create a list of input files *without* extensions (if present) and # a 2D list of "chip" region - image extension pairs: # [[reg1, ext1], [reg2,ext2],...] imgfnames, cregext = build_img_ext_reg_list(images, chip_reg, img_wcs_ext, verbose) # build a "master" list of regions in sky coordinates: all_sky_regions = [] for p in regheaders: if p[1] is None: # Already in sky WCS all_sky_regions += p[0] else: all_sky_regions += shapes_img2sky(p[0], p[1]) #TODO: implement shapes_img2sky all_sky_regions = pyregion.ShapeList(list(all_sky_regions)) cattb = [] # create a region file (with regions in image coordinates) for each # image extension from the input_reg and chip_reg regions for fname in imgfnames: imghdu = None imghdu = fits.open(fname, memmap=False) catreg = [] try: for extp in cregext: ext = extp[1] # check that the extension is of supported type: if not _is_supported_hdu(imghdu[ext].header): raise RuntimeError("Extension {} is of unsupported " \ "type.".format(ext)) # Remove references to non-SIP distortions from the header #TODO: remove this line of code as well as the remove_header_tdd # function once publicly available release of pyregion uses # all_world2pix and all_pix2world functions and does this header # cleanup itself. # remove_header_tdd(imghdu[ext].header) ####### added to pass hstwcs instead of header to pyregion # if isinstance(ext, str): # ext = findExtname(imghdu, extname = ext, extver = 1) # elif isinstance(ext, tuple): # ext = findExtname(imghdu, extname = ext[0], extver = ext[1]) # wcs = stwcs.wcsutil.HSTWCS(imghdu, ext=ext) wcs = _AuxSTWCS(imghdu, ext=ext) extreg = all_sky_regions.as_imagecoord(wcs, rot_wrt_axis=2) ####### #extreg = all_sky_regions.as_imagecoord(imghdu[ext].header, rot_wrt_axis=2) if extp[0]: # add "fixed" regions if any extreg = pyregion.ShapeList(extreg+extp[0]) # filter out regions outside the image: if filter and filter.lower() == 'fast': fast_filter_outer_regions( extreg, imghdu[ext].header['NAXIS1'], imghdu[ext].header['NAXIS2'], origin = 1 ) if len(extreg) < 1: # do not create empty region files catreg.append('None') continue # generate output region file name: extsuffix = _ext2str_suffix(ext) basefname, fext = os.path.splitext(os.path.basename(fname)) regfname = basefname + extsuffix + os.extsep + "reg" fullregfname = os.path.join(outpath, regfname) catreg.append(regfname) # save regions to a file: if append and os.path.isfile(fullregfname): old_extreg = pyregion.open(fullregfname) extreg = pyregion.ShapeList(old_extreg + extreg) #TODO: pyregion.write does not work well. For now use _regwite # (until the bug fixes get to the pyregion project). #TODO: we replaced pyregion.write with our implementation # of the write (code from _regwrite) in the locally maintained # pyregion until these changes get to be implemented in the # publicly available release of pyregion. # _regwrite(extreg, fullregfname) #extreg.write(fullregfname) # <- use this instead of _regwrite # once the pyregion bugs are fixed. cattb.append([fname, catreg]) except: if imghdu: imghdu.close() raise # create exclusions catalog file: if catfname: catfh = open(os.path.join(outpath, catfname), 'w') for catentry in cattb: catfh.write(catentry[0]) # image file name for reg in catentry[1]: catfh.write(' ' + reg) # region file name catfh.write('\n') catfh.close()
from astropy.io import fits as pyfits # read in the image xray_name = "pspc_skyview.fits" f_xray = pyfits.open(xray_name) try: import pywcsgrid2 ax = pywcsgrid2.subplot(111, header=f_xray[0].header) except ImportError: ax = plt.subplot(111) ax.imshow(f_xray[0].data, cmap=cm.gray, vmin=0., vmax=0.00038, origin="lower") reg_name = "test.reg" r = stregion.open(reg_name).as_imagecoord(header=f_xray[0].header) from stregion.mpl_helper import properties_func_default # Use custom function for patch attribute def fixed_color(shape, saved_attrs): attr_list, attr_dict = saved_attrs attr_dict["color"] = "red" kwargs = properties_func_default(shape, (attr_list, attr_dict)) return kwargs # select region shape with tag=="Group 1"