def do_stage(self, images): for image in images: if len(image.data.shape) > 2: logging_tags = logs.image_config_to_tags( image, self.group_by_keywords) logs.add_tag(logging_tags, 'filename', os.path.basename(image.filename)) n_amps = image.data.shape[0] nx, ny = get_mosaic_size(image, n_amps) mosaiced_data = np.zeros((ny, nx), dtype=np.float32) mosaiced_bpm = np.zeros((ny, nx), dtype=np.uint8) for i in range(n_amps): datasec = image.header['DATASEC{0}'.format(i + 1)] amp_slice = fits_utils.parse_region_keyword(datasec) logs.add_tag(logging_tags, 'DATASEC{0}'.format(i + 1), datasec) detsec = image.header['DETSEC{0}'.format(i + 1)] mosaic_slice = fits_utils.parse_region_keyword(detsec) logs.add_tag(logging_tags, 'DETSEC{0}'.format(i + 1), datasec) mosaiced_data[mosaic_slice] = image.data[i][amp_slice] mosaiced_bpm[mosaic_slice] = image.bpm[i][amp_slice] image.data = mosaiced_data image.bpm = mosaiced_bpm image.update_shape(nx, ny) image.header['NAXIS'] = 2 image.header.pop('NAXIS3') self.logger.info('Mosaiced image', extra=logging_tags) return images
def do_stage(self, images): for image in images: if len(image.data.shape) > 2: logging_tags = logs.image_config_to_tags(image, self.group_by_keywords) logs.add_tag(logging_tags, 'filename', os.path.basename(image.filename)) n_amps = image.data.shape[0] nx, ny = get_mosaic_size(image, n_amps) mosaiced_data = np.zeros((ny, nx), dtype=np.float32) mosaiced_bpm = np.zeros((ny, nx), dtype=np.uint8) for i in range(n_amps): datasec = image.header['DATASEC{0}'.format(i + 1)] amp_slice = fits_utils.parse_region_keyword(datasec) logs.add_tag(logging_tags, 'DATASEC{0}'.format(i + 1), datasec) detsec = image.header['DETSEC{0}'.format(i + 1)] mosaic_slice = fits_utils.parse_region_keyword(detsec) logs.add_tag(logging_tags, 'DETSEC{0}'.format(i + 1), datasec) mosaiced_data[mosaic_slice] = image.data[i][amp_slice] mosaiced_bpm[mosaic_slice] = image.bpm[i][amp_slice] image.data = mosaiced_data image.bpm = mosaiced_bpm image.update_shape(nx, ny) image.header['NAXIS'] = 2 image.header.pop('NAXIS3') self.logger.info('Mosaiced image', extra=logging_tags) return images
def bpm_has_valid_size(bpm, image): # If 3d, check and make sure the number of extensions is the same if len(image.data.shape) > 2: y_slices, x_slices = fits_utils.parse_region_keyword(image.extension_headers[0]['DATASEC']) is_valid = image.data.shape[0] == bpm.shape[0] else: y_slices, x_slices = fits_utils.parse_region_keyword(image.header['DATASEC']) is_valid = True # Check if x and y dimensions are less than the datasec is_valid &= bpm.shape[-1] >= x_slices.stop is_valid &= bpm.shape[-2] >= y_slices.stop return is_valid
def do_stage(self, image): if image.data_is_3d(): logging_tags = {} nx, ny = get_mosaic_size(image, image.get_n_amps()) mosaiced_data = np.zeros((ny, nx), dtype=np.float32) mosaiced_bpm = np.zeros((ny, nx), dtype=np.uint8) x_detsec_limits, y_detsec_limits = get_detsec_limits( image, image.get_n_amps()) xmin = min(x_detsec_limits) - 1 ymin = min(y_detsec_limits) - 1 for i in range(image.get_n_amps()): ccdsum = image.extension_headers[i].get('CCDSUM', image.ccdsum) x_binning, y_binning = ccdsum.split(' ') datasec = image.extension_headers[i]['DATASEC'] amp_slice = fits_utils.parse_region_keyword(datasec) logging_tags['DATASEC{0}'.format(i + 1)] = datasec detsec = image.extension_headers[i]['DETSEC'] mosaic_slice = get_windowed_mosaic_slices( detsec, xmin, ymin, x_binning, y_binning) logging_tags['DATASEC{0}'.format(i + 1)] = detsec mosaiced_data[mosaic_slice] = image.data[i][amp_slice] mosaiced_bpm[mosaic_slice] = image.bpm[i][amp_slice] image.data = mosaiced_data image.bpm = mosaiced_bpm # Flag any missing data image.bpm[image.data == 0.0] = 1 image.update_shape(nx, ny) update_naxis_keywords(image, nx, ny) logger.info('Mosaiced image', image=image, extra_tags=logging_tags) return image
def get_windowed_mosaic_slices(detsec, xmin, ymin, x_binning, y_binning): """ Get the array slices to map the image coordinates for a given detector section to the output mosaic coordinates. Parameters ---------- detsec: str Detector section keyword from the header in IRAF section format. xmin: int Minimum x detector coordinate for the mosaic to go the bottom left of the array ymin: int Minimum y detector coordinate for the mosaic to go the bottom left of the array x_binning: int Binning factor in the x direction for the output mosaic y_binning: int Binning factor in the y direction for the output mosaic Returns ------- y_slice, x_slice: slice, slice Slice to index the output mosaic data array for the given detsec. """ unbinned_slice = fits_utils.parse_region_keyword(detsec) return (slice((unbinned_slice[0].start - ymin) // int(y_binning), (unbinned_slice[0].stop - ymin) // int(y_binning), unbinned_slice[0].step), slice((unbinned_slice[1].start - xmin) // int(x_binning), (unbinned_slice[1].stop - xmin) // int(x_binning), unbinned_slice[1].step))
def bpm_has_valid_size(bpm, image): # If 3d, check and make sure the number of extensions is the same if len(image.data.shape) > 2: y_slices, x_slices = fits_utils.parse_region_keyword( image.extension_headers[0]['DATASEC']) is_valid = image.data.shape[0] == bpm.shape[0] else: y_slices, x_slices = fits_utils.parse_region_keyword( image.header['DATASEC']) is_valid = True # Check if x and y dimensions are less than the datasec is_valid &= bpm.shape[-1] >= x_slices.stop is_valid &= bpm.shape[-2] >= y_slices.stop return is_valid
def _subtract_overscan_2d(image): overscan_region = fits_utils.parse_region_keyword(image.header.get('BIASSEC')) if overscan_region is not None: overscan_level = stats.sigma_clipped_mean(image.data[overscan_region], 3) image.header['L1STATOV'] = (1, 'Status flag for overscan correction') else: overscan_level = 0.0 image.header['L1STATOV'] = (0, 'Status flag for overscan correction') image.header['OVERSCAN'] = (overscan_level, 'Overscan value that was subtracted') image.data -= overscan_level return overscan_level
def _subtract_overscan_3d(image, i): overscan_region = fits_utils.parse_region_keyword(image.extension_headers[i].get('BIASSEC')) if overscan_region is not None: overscan_level = stats.sigma_clipped_mean(image.data[i][overscan_region], 3) image.header['L1STATOV'] = (1, 'Status flag for overscan correction') else: overscan_level = 0.0 image.header['L1STATOV'] = (0, 'Status flag for overscan correction') overscan_comment = 'Overscan value that was subtracted from Q{0}'.format(i + 1) image.header['OVERSCN{0}'.format(i + 1)] = (overscan_level, overscan_comment) image.data[i] -= overscan_level return overscan_level
def _trim_image(image): trimsec = fits_utils.parse_region_keyword(image.header['TRIMSEC']) image.data = image.data[trimsec] image.bpm = image.bpm[trimsec] # Update the NAXIS and CRPIX keywords image.header['NAXIS1'] = trimsec[1].stop - trimsec[1].start image.header['NAXIS2'] = trimsec[0].stop - trimsec[0].start image.header['CRPIX1'] -= trimsec[1].start image.header['CRPIX2'] -= trimsec[0].start image.header['L1STATTR'] = (1, 'Status flag for overscan trimming') return image.header['NAXIS1'], image.header['NAXIS2']
def get_mosaic_size(image, n_amps): x_pixel_limits = [] y_pixel_limits = [] for i in range(n_amps): header_keyword = image.header['DETSEC{0}'.format(i + 1)] y_slice, x_slice = fits_utils.parse_region_keyword(header_keyword) x_pixel_limits.append(x_slice.start + 1) x_pixel_limits.append(x_slice.stop) y_pixel_limits.append(y_slice.start + 1) y_pixel_limits.append(y_slice.stop) # Clean out any Nones x_pixel_limits = [x if x is not None else 1 for x in x_pixel_limits] y_pixel_limits = [y if y is not None else 1 for y in y_pixel_limits] nx = np.max(x_pixel_limits) - np.min(x_pixel_limits) + 1 ny = np.max(y_pixel_limits) - np.min(y_pixel_limits) + 1 return nx, ny
def _trim_image(image): trimsec = fits_utils.parse_region_keyword(image.header['TRIMSEC']) if trimsec is not None: image.data = image.data[trimsec] image.bpm = image.bpm[trimsec] # Update the NAXIS and CRPIX keywords image.header['NAXIS1'] = trimsec[1].stop - trimsec[1].start image.header['NAXIS2'] = trimsec[0].stop - trimsec[0].start if 'CRPIX1' in image.header: image.header['CRPIX1'] -= trimsec[1].start if 'CRPIX2' in image.header: image.header['CRPIX2'] -= trimsec[0].start image.header['L1STATTR'] = (1, 'Status flag for overscan trimming') else: logger.warning('TRIMSEC was not defined.', image=image, extra_tags={'trimsec': image.header['TRIMSEC']}) image.header['L1STATTR'] = (0, 'Status flag for overscan trimming') return image.header['NAXIS1'], image.header['NAXIS2']
def _trim_image(image): trimsec = fits_utils.parse_region_keyword(image.header['TRIMSEC']) if trimsec is not None: image.data = image.data[trimsec] image.bpm = image.bpm[trimsec] # Update the NAXIS and CRPIX keywords image.header['NAXIS1'] = trimsec[1].stop - trimsec[1].start image.header['NAXIS2'] = trimsec[0].stop - trimsec[0].start image.header['CRPIX1'] -= trimsec[1].start image.header['CRPIX2'] -= trimsec[0].start image.header['L1STATTR'] = (1, 'Status flag for overscan trimming') else: logging_tags = logs.image_config_to_tags(image, None) logs.add_tag(logging_tags, 'filename', os.path.basename(image.filename)) logs.add_tag(logging_tags, 'trimsec', image.header['TRIMSEC']) logger.warning('TRIMSEC was not defined.', extra=logging_tags) image.header['L1STATTR'] = (0, 'Status flag for overscan trimming') return image.header['NAXIS1'], image.header['NAXIS2']
def get_mosaic_size(image, n_amps): x_pixel_limits = [] y_pixel_limits = [] for i in range(n_amps): detsec_keyword = image.extension_headers[i]['DETSEC'] detsec = fits_utils.parse_region_keyword(detsec_keyword) if detsec is not None: x_pixel_limits.append(detsec[1].start + 1) x_pixel_limits.append(detsec[1].stop) y_pixel_limits.append(detsec[0].start + 1) y_pixel_limits.append(detsec[0].stop) else: x_pixel_limits.append(None) y_pixel_limits.append(None) # Clean out any Nones x_pixel_limits = [x if x is not None else 1 for x in x_pixel_limits] y_pixel_limits = [y if y is not None else 1 for y in y_pixel_limits] nx = np.max(x_pixel_limits) - np.min(x_pixel_limits) + 1 ny = np.max(y_pixel_limits) - np.min(y_pixel_limits) + 1 return nx, ny
def get_detsec_limits(image, n_amps): """ Parse the detector section keyword from each extension and return a list of x and y detector positions Parameters ---------- image: banzai.images.Image image with detector section header keywords to parse n_amps: int Number of amplifiers (fits extensions) Returns ------- x_detsec_limits, y_detsec_limits: list. list x and y list of the detector positions in the each of the DETSEC keywords """ x_pixel_limits = [] y_pixel_limits = [] for i in range(n_amps): detsec_keyword = image.extension_headers[i]['DETSEC'] detsec = fits_utils.parse_region_keyword(detsec_keyword) if detsec is not None: # Convert starts from 0 indexed to 1 indexed x_pixel_limits.append(detsec[1].start + 1) y_pixel_limits.append(detsec[0].start + 1) # Note python is not inclusive at the end (unlike IRAF) x_pixel_limits.append(detsec[1].stop) y_pixel_limits.append(detsec[0].stop) else: x_pixel_limits.append(None) y_pixel_limits.append(None) # Clean out any Nones x_pixel_limits = [x if x is not None else 1 for x in x_pixel_limits] y_pixel_limits = [y if y is not None else 1 for y in y_pixel_limits] return x_pixel_limits, y_pixel_limits