def read_fits(cls, filename): """Build an instance of this class from a file. Parameters ---------- filename : `str` Name of the file to read. """ # Extract info from metadata. global_metadata = afwFits.readMetadata(filename, hdu=0) has_default = global_metadata.getBool("HAS_DEFAULT") if global_metadata.getBool("HAS_REGIONS"): focal_plane_region_names = global_metadata.getArray("REGION_NAMES") else: focal_plane_region_names = [] f = afwFits.Fits(filename, "r") n_extensions = f.countHdus() extended_psf_parts = {} for j in range(1, n_extensions): md = afwFits.readMetadata(filename, hdu=j) if has_default and md["REGION"] == "DEFAULT": if md["EXTNAME"] == "IMAGE": default_image = afwImage.ImageF(filename, hdu=j) elif md["EXTNAME"] == "MASK": default_mask = afwImage.MaskX(filename, hdu=j) continue if md["EXTNAME"] == "IMAGE": extended_psf_part = afwImage.ImageF(filename, hdu=j) elif md["EXTNAME"] == "MASK": extended_psf_part = afwImage.MaskX(filename, hdu=j) extended_psf_parts.setdefault( md["REGION"], {})[md["EXTNAME"].lower()] = extended_psf_part # Handle default if present. if has_default: extended_psf = cls( afwImage.MaskedImageF(default_image, default_mask)) else: extended_psf = cls() # Ensure we recovered an extended PSF for all focal plane regions. if len(extended_psf_parts) != len(focal_plane_region_names): raise ValueError( f"Number of per-region extended PSFs read ({len(extended_psf_parts)}) does not " "match with the number of regions recorded in the metadata " f"({len(focal_plane_region_names)}).") # Generate extended PSF regions mappings. for r_name in focal_plane_region_names: extended_psf_image = afwImage.MaskedImageF( **extended_psf_parts[r_name]) detector_list = global_metadata.getArray(r_name) extended_psf.add_regional_extended_psf(extended_psf_image, r_name, detector_list) # Instantiate ExtendedPsf. return extended_psf
def write_fits(self, filename): """Write this object to a file. Parameters ---------- filename : `str` Name of file to write. """ # Create primary HDU with global metadata. metadata = PropertyList() metadata["HAS_DEFAULT"] = self.default_extended_psf is not None if self.focal_plane_regions: metadata["HAS_REGIONS"] = True metadata["REGION_NAMES"] = list(self.focal_plane_regions.keys()) for region, e_psf_region in self.focal_plane_regions.items(): metadata[region] = e_psf_region.detector_list else: metadata["HAS_REGIONS"] = False fits_primary = afwFits.Fits(filename, "w") fits_primary.createEmpty() fits_primary.writeMetadata(metadata) fits_primary.closeFile() # Write default extended PSF. if self.default_extended_psf is not None: default_hdu_metadata = PropertyList() default_hdu_metadata.update({ "REGION": "DEFAULT", "EXTNAME": "IMAGE" }) self.default_extended_psf.image.writeFits( filename, metadata=default_hdu_metadata, mode="a") default_hdu_metadata.update({ "REGION": "DEFAULT", "EXTNAME": "MASK" }) self.default_extended_psf.mask.writeFits( filename, metadata=default_hdu_metadata, mode="a") # Write extended PSF for each focal plane region. for j, (region, e_psf_region) in enumerate(self.focal_plane_regions.items()): metadata = PropertyList() metadata.update({"REGION": region, "EXTNAME": "IMAGE"}) e_psf_region.extended_psf_image.image.writeFits(filename, metadata=metadata, mode="a") metadata.update({"REGION": region, "EXTNAME": "MASK"}) e_psf_region.extended_psf_image.mask.writeFits(filename, metadata=metadata, mode="a")
def writeFits(filename, stamp_ims, metadata, write_mask, write_variance): """Write a single FITS file containing all stamps. Parameters ---------- filename : `str` A string indicating the output filename stamps_ims : iterable of `lsst.afw.image.MaskedImageF` An iterable of masked images metadata : `PropertyList` A collection of key, value metadata pairs to be written to the primary header write_mask : `bool` Write the mask data to the output file? write_variance : `bool` Write the variance data to the output file? """ metadata['HAS_MASK'] = write_mask metadata['HAS_VARIANCE'] = write_variance metadata['N_STAMPS'] = len(stamp_ims) # create primary HDU with global metadata fitsPrimary = afwFits.Fits(filename, "w") fitsPrimary.createEmpty() fitsPrimary.writeMetadata(metadata) fitsPrimary.closeFile() # add all pixel data optionally writing mask and variance information for i, stamp in enumerate(stamp_ims): metadata = PropertyList() # EXTVER should be 1-based, the index from enumerate is 0-based metadata.update({'EXTVER': i + 1, 'EXTNAME': 'IMAGE'}) stamp.getImage().writeFits(filename, metadata=metadata, mode='a') if write_mask: metadata = PropertyList() metadata.update({'EXTVER': i + 1, 'EXTNAME': 'MASK'}) stamp.getMask().writeFits(filename, metadata=metadata, mode='a') if write_variance: metadata = PropertyList() metadata.update({'EXTVER': i + 1, 'EXTNAME': 'VARIANCE'}) stamp.getVariance().writeFits(filename, metadata=metadata, mode='a') return None
def writeFits(filename, stamps, metadata, type_name, write_mask, write_variance, write_archive=False): """Write a single FITS file containing all stamps. Parameters ---------- filename : `str` A string indicating the output filename stamps : iterable of `BaseStamp` An iterable of Stamp objects metadata : `PropertyList` A collection of key, value metadata pairs to be written to the primary header type_name : `str` Python type name of the StampsBase subclass to use write_mask : `bool` Write the mask data to the output file? write_variance : `bool` Write the variance data to the output file? write_archive : `bool`, optional Write an archive to store Persistables along with each stamp? Default: ``False``. """ metadata['HAS_MASK'] = write_mask metadata['HAS_VARIANCE'] = write_variance metadata['HAS_ARCHIVE'] = write_archive metadata['N_STAMPS'] = len(stamps) metadata['STAMPCLS'] = type_name # Record version number in case of future code changes metadata['VERSION'] = 1 # create primary HDU with global metadata fitsFile = afwFits.Fits(filename, "w") fitsFile.createEmpty() # Store Persistables in an OutputArchive and write it if write_archive: oa = afwTable.io.OutputArchive() archive_ids = [oa.put(stamp.archive_element) for stamp in stamps] metadata["ARCHIVE_IDS"] = archive_ids fitsFile.writeMetadata(metadata) oa.writeFits(fitsFile) else: fitsFile.writeMetadata(metadata) fitsFile.closeFile() # add all pixel data optionally writing mask and variance information for i, stamp in enumerate(stamps): metadata = PropertyList() # EXTVER should be 1-based, the index from enumerate is 0-based metadata.update({'EXTVER': i + 1, 'EXTNAME': 'IMAGE'}) stamp.stamp_im.getImage().writeFits(filename, metadata=metadata, mode='a') if write_mask: metadata = PropertyList() metadata.update({'EXTVER': i + 1, 'EXTNAME': 'MASK'}) stamp.stamp_im.getMask().writeFits(filename, metadata=metadata, mode='a') if write_variance: metadata = PropertyList() metadata.update({'EXTVER': i + 1, 'EXTNAME': 'VARIANCE'}) stamp.stamp_im.getVariance().writeFits(filename, metadata=metadata, mode='a') return None
def readFitsWithOptions(filename, stamp_factory, options): """Read stamps from FITS file, allowing for only a subregion of the stamps to be read. Parameters ---------- filename : `str` A string indicating the file to read stamp_factory : classmethod A factory function defined on a dataclass for constructing stamp objects a la `lsst.meas.alrogithm.Stamp` options : `PropertyList` or `dict` A collection of parameters. If it contains a bounding box (``bbox`` key), or if certain other keys (``llcX``, ``llcY``, ``width``, ``height``) are available for one to be constructed, the bounding box is passed to the ``FitsReader`` in order to return a sub-image. Returns ------- stamps : `list` of dataclass objects like `Stamp`, PropertyList A tuple of a list of `Stamp`-like objects metadata : `PropertyList` The metadata """ # extract necessary info from metadata metadata = afwFits.readMetadata(filename, hdu=0) nStamps = metadata["N_STAMPS"] has_archive = metadata["HAS_ARCHIVE"] if has_archive: archive_ids = metadata.getArray("ARCHIVE_IDS") with afwFits.Fits(filename, 'r') as f: nExtensions = f.countHdus() # check if a bbox was provided kwargs = {} if options: # gen3 API if "bbox" in options.keys(): kwargs["bbox"] = options["bbox"] # gen2 API elif "llcX" in options.keys(): llcX = options["llcX"] llcY = options["llcY"] width = options["width"] height = options["height"] bbox = Box2I(Point2I(llcX, llcY), Extent2I(width, height)) kwargs["bbox"] = bbox stamp_parts = {} # We need to be careful because nExtensions includes the primary # header data unit for idx in range(nExtensions - 1): md = afwFits.readMetadata(filename, hdu=idx + 1) if md['EXTNAME'] in ('IMAGE', 'VARIANCE'): reader = afwImage.ImageFitsReader(filename, hdu=idx + 1) elif md['EXTNAME'] == 'MASK': reader = afwImage.MaskFitsReader(filename, hdu=idx + 1) elif md['EXTNAME'] == 'ARCHIVE_INDEX': f.setHdu(idx + 1) archive = afwTable.io.InputArchive.readFits(f) continue elif md['EXTTYPE'] == 'ARCHIVE_DATA': continue else: raise ValueError(f"Unknown extension type: {md['EXTNAME']}") stamp_parts.setdefault( md['EXTVER'], {})[md['EXTNAME'].lower()] = reader.read(**kwargs) if len(stamp_parts) != nStamps: raise ValueError( f'Number of stamps read ({len(stamp_parts)}) does not agree with the ' f'number of stamps recorded in the metadata ({nStamps}).') # construct stamps themselves stamps = [] for k in range(nStamps): # Need to increment by one since EXTVER starts at 1 maskedImage = afwImage.MaskedImageF(**stamp_parts[k + 1]) archive_element = archive.get(archive_ids[k]) if has_archive else None stamps.append(stamp_factory(maskedImage, metadata, k, archive_element)) return stamps, metadata
def readFitsWithOptions(filename, stamp_factory, options): """Read stamps from FITS file, allowing for only a subregion of the stamps to be read. Parameters ---------- filename : `str` A string indicating the file to read stamp_factory : classmethod A factory function defined on a dataclass for constructing stamp objects a la `lsst.meas.alrogithm.Stamp` options : `PropertyList` A collection of parameters. If certain keys are available (``llcX``, ``llcY``, ``width``, ``height``), a bounding box is constructed and passed to the ``FitsReader`` in order to return a sub-image. Returns ------- stamps : `list` of dataclass objects like `Stamp`, PropertyList A tuple of a list of `Stamp`-like objects metadata : `PropertyList` The metadata """ # extract necessary info from metadata metadata = afwFits.readMetadata(filename, hdu=0) f = afwFits.Fits(filename, 'r') nExtensions = f.countHdus() nStamps = metadata["N_STAMPS"] # check if a bbox was provided kwargs = {} if options and options.exists("llcX"): llcX = options["llcX"] llcY = options["llcY"] width = options["width"] height = options["height"] bbox = Box2I(Point2I(llcX, llcY), Extent2I(width, height)) kwargs["bbox"] = bbox stamp_parts = {} # We need to be careful because nExtensions includes the primary # header data unit for idx in range(nExtensions - 1): md = afwFits.readMetadata(filename, hdu=idx + 1) if md['EXTNAME'] in ('IMAGE', 'VARIANCE'): reader = afwImage.ImageFitsReader(filename, hdu=idx + 1) elif md['EXTNAME'] == 'MASK': reader = afwImage.MaskFitsReader(filename, hdu=idx + 1) else: raise ValueError(f"Unknown extension type: {md['EXTNAME']}") stamp_parts.setdefault( md['EXTVER'], {})[md['EXTNAME'].lower()] = reader.read(**kwargs) if len(stamp_parts) != nStamps: raise ValueError( f'Number of stamps read ({len(stamp_parts)}) does not agree with the ' f'number of stamps recorded in the metadata ({nStamps}).') # construct stamps themselves stamps = [] for k in range(nStamps): # Need to increment by one since EXTVER starts at 1 maskedImage = afwImage.MaskedImageF(**stamp_parts[k + 1]) stamps.append(stamp_factory(maskedImage, metadata, k)) return stamps, metadata