def display(self, adinputs=None, **params): """ Displays an image on the ds9 display, using multiple frames if there are multiple extensions. Saturated pixels can be displayed in red, and overlays can also be shown. Parameters ---------- extname: str 'SCI', 'VAR', or 'DQ': plane to display frame: int starting frame for display ignore: bool setting to True turns off the display remove_bias: bool attempt to subtract bias before displaying? threshold: str='auto'/float level above which to flag pixels as saturated tile: bool attempt to tile arrays before displaying? zscale: bool use zscale algorithm? overlay: list list of overlays for the display """ log = self.log log.debug(gt.log_message("primitive", self.myself(), "starting")) # No-op if ignore=True if params["ignore"]: log.warning("display turned off per user request") return threshold = params['threshold'] remove_bias = params.get('remove_bias', False) extname = params['extname'] tile = params['tile'] zscale = params['zscale'] overlays = params['overlay'] frame = params['frame'] if params['frame'] else 1 overlay_index = 0 lnd = _localNumDisplay() for ad in adinputs: # Allows elegant break from nested loops if frame > 16: log.warning("Too many images; only the first 16 are displayed") break # Threshold and bias make sense only for SCI extension if extname != 'SCI': threshold = None remove_bias = False elif threshold == 'None': threshold = None elif threshold == 'auto': mosaicked = ((ad.phu.get(self.timestamp_keys["mosaicDetectors"]) is not None) or (ad.phu.get(self.timestamp_keys["tileArrays"]) is not None)) has_dq = all([ext.mask is not None for ext in ad]) if not has_dq: if mosaicked: log.warning("Cannot add DQ to mosaicked data; no " "threshold mask will be applied to " "{}".format(ad.filename)) threshold = None else: # addDQ operates in place so deepcopy to preserve input ad = self.addDQ([deepcopy(ad)])[0] if remove_bias: if (ad.phu.get('BIASIM') or ad.phu.get('DARKIM') or any(ad.hdr.get('OVERSCAN'))): log.fullinfo("Bias level has already been removed from " "data; no approximate correction will be " "performed") else: try: bias_level = get_bias_level(ad) except NotImplementedError: # For non-GMOS instruments bias_level = None if bias_level is not None: ad = deepcopy(ad) # Leave original untouched! log.stdinfo("Subtracting approximate bias level from " "{} for display".format(ad.filename)) log.fullinfo("Bias levels used: {}".format(str(bias_level))) for ext, bias in zip(ad, bias_level): ext.subtract(np.float32(bias) if bias is not None else 0) else: log.warning("Bias level not found for {}; approximate " "bias will not be removed".format(ad.filename)) # Check whether data needs to be tiled before displaying # Otherwise, flatten all desired extensions into a single list if tile and len(ad) > 1: log.fullinfo("Tiling extensions together before displaying") # !! This is the replacement call for tileArrays() !! # !! mosaicADdetectors handles both GMOS and GSAOI !! # ad = self.mosaicADdetectors(tile=True)[0] ad = self.tileArrays([ad], tile_all=True)[0] # Each extension is an individual display item (if the data have been # tiled, then there'll only be one extension per AD, of course) for ext in ad: if frame > 16: break # Squeeze the data to remove any empty dimensions (eg, raw F2 data) ext.operate(np.squeeze) # Get the data we're going to display. TODO Replace extname with attr? data = getattr(ext, {'SCI':'data', 'DQ':'mask', 'VAR':'variance'}[extname], None) dqdata = ext.mask if data is None: log.warning("No data to display in {}[{}]".format(ext.filename, extname)) continue # One-dimensional data (ie, extracted spectra) if len(data.shape) == 1: continue # Make threshold mask if desired masks = [] mask_colors = [] if threshold is not None: if threshold != 'auto': satmask = data > float(threshold) else: if dqdata is None: log.warning("No DQ plane found; cannot make " "threshold mask") satmask = None else: satmask = (dqdata & (DQ.non_linear | DQ.saturated)) > 0 if satmask is not None: masks.append(satmask) mask_colors.append(204) if overlays: # Could be single overlay, or list. Replicate behaviour of # gt.make_lists (which we can't use because we haven't # made a complete list of displayed extensions at the start # in order to avoid memory bloat) try: overlay = overlays[overlay_index] except TypeError: overlay = overlays except IndexError: if len(overlays) == 1: overlay = overlays[0] masks.append(overlay) mask_colors.append(206) # Define the display name if tile and extname=='SCI': name = ext.filename elif tile: name = '{}({})'.format(ext.filename, extname) else: name = '{}({},{})'.format(ext.filename, extname, ext.hdr['EXTVER']) try: lnd.display(data, name=name, frame=frame, zscale=zscale, bpm=None if extname=='DQ' else dqdata, quiet=True, masks=masks, mask_colors=mask_colors) except IOError: log.warning("ds9 not found; cannot display input") frame += 1 # Print from statistics for flats if extname=='SCI' and {'GMOS', 'IMAGE', 'FLAT'}.issubset(ext.tags): good_data = data[dqdata==0] if dqdata is not None else data mean = np.mean(good_data) median = np.median(good_data) log.stdinfo("Twilight flat counts for {}:".format(ext.filename)) log.stdinfo(" Mean value: {:.0f}".format(mean)) log.stdinfo(" Median value: {:.0f}".format(median)) return adinputs
def display(self, adinputs=None, **params): """ Displays an image on the ds9 display, using multiple frames if there are multiple extensions. Saturated pixels can be displayed in red, and overlays can also be shown. Parameters ---------- extname: str 'SCI', 'VAR', or 'DQ': plane to display frame: int starting frame for display ignore: bool setting to True turns off the display remove_bias: bool attempt to subtract bias before displaying? threshold: str='auto'/float level above which to flag pixels as saturated tile: bool attempt to tile arrays before displaying? zscale: bool use zscale algorithm? overlay: list list of overlays for the display """ log = self.log log.debug(gt.log_message("primitive", self.myself(), "starting")) # No-op if ignore=True if params["ignore"]: log.warning("display turned off per user request") return threshold = params['threshold'] remove_bias = params.get('remove_bias', False) extname = params['extname'] tile = params['tile'] zscale = params['zscale'] overlays = params['overlay'] frame = params['frame'] if params['frame'] else 1 overlay_index = 0 lnd = _localNumDisplay() for ad in adinputs: # Allows elegant break from nested loops if frame > 16: log.warning("Too many images; only the first 16 are displayed") break # Threshold and bias make sense only for SCI extension if extname != 'SCI': threshold = None remove_bias = False elif threshold == 'None': threshold = None elif threshold == 'auto': mosaicked = ((ad.phu.get( self.timestamp_keys["mosaicDetectors"]) is not None) or (ad.phu.get(self.timestamp_keys["tileArrays"]) is not None)) has_dq = all([ext.mask is not None for ext in ad]) if not has_dq: if mosaicked: log.warning("Cannot add DQ to mosaicked data; no " "threshold mask will be applied to " "{}".format(ad.filename)) threshold = None else: # addDQ operates in place so deepcopy to preserve input ad = self.addDQ([deepcopy(ad)])[0] if remove_bias: if (ad.phu.get('BIASIM') or ad.phu.get('DARKIM') or any(ad.hdr.get('OVERSCAN'))): log.fullinfo("Bias level has already been removed from " "data; no approximate correction will be " "performed") else: try: bias_level = get_bias_level(ad) except NotImplementedError: # For non-GMOS instruments bias_level = None if bias_level is not None: ad = deepcopy(ad) # Leave original untouched! log.stdinfo("Subtracting approximate bias level from " "{} for display".format(ad.filename)) log.fullinfo("Bias levels used: {}".format( str(bias_level))) for ext, bias in zip(ad, bias_level): ext.subtract( np.float32(bias) if bias is not None else 0) else: log.warning("Bias level not found for {}; approximate " "bias will not be removed".format( ad.filename)) # Check whether data needs to be tiled before displaying # Otherwise, flatten all desired extensions into a single list if tile and len(ad) > 1: log.fullinfo("Tiling extensions together before displaying") # !! This is the replacement call for tileArrays() !! # !! mosaicADdetectors handles both GMOS and GSAOI !! # ad = self.mosaicADdetectors(tile=True)[0] ad = self.tileArrays([ad], tile_all=True)[0] # Each extension is an individual display item (if the data have been # tiled, then there'll only be one extension per AD, of course) for ext in ad: if frame > 16: break # Squeeze the data to remove any empty dimensions (eg, raw F2 data) ext.operate(np.squeeze) # Get the data we're going to display. TODO Replace extname with attr? data = getattr(ext, { 'SCI': 'data', 'DQ': 'mask', 'VAR': 'variance' }[extname], None) dqdata = ext.mask if data is None: log.warning("No data to display in {}[{}]".format( ext.filename, extname)) continue # One-dimensional data (ie, extracted spectra) if len(data.shape) == 1: continue # Make threshold mask if desired masks = [] mask_colors = [] if threshold is not None: if threshold != 'auto': satmask = data > float(threshold) else: if dqdata is None: log.warning("No DQ plane found; cannot make " "threshold mask") satmask = None else: satmask = (dqdata & (DQ.non_linear | DQ.saturated)) > 0 if satmask is not None: masks.append(satmask) mask_colors.append(204) if overlays: # Could be single overlay, or list. Replicate behaviour of # gt.make_lists (which we can't use because we haven't # made a complete list of displayed extensions at the start # in order to avoid memory bloat) try: overlay = overlays[overlay_index] except TypeError: overlay = overlays except IndexError: if len(overlays) == 1: overlay = overlays[0] masks.append(overlay) mask_colors.append(206) # Define the display name if tile and extname == 'SCI': name = ext.filename elif tile: name = '{}({})'.format(ext.filename, extname) else: name = '{}({},{})'.format(ext.filename, extname, ext.hdr['EXTVER']) try: lnd.display(data, name=name, frame=frame, zscale=zscale, bpm=None if extname == 'DQ' else dqdata, quiet=True, masks=masks, mask_colors=mask_colors) except IOError: log.warning("ds9 not found; cannot display input") frame += 1 # Print from statistics for flats if extname == 'SCI' and {'GMOS', 'IMAGE', 'FLAT'}.issubset( ext.tags): good_data = data[dqdata == 0] if dqdata is not None else data mean = np.mean(good_data) median = np.median(good_data) log.stdinfo("Twilight flat counts for {}:".format( ext.filename)) log.stdinfo(" Mean value: {:.0f}".format(mean)) log.stdinfo(" Median value: {:.0f}".format(median)) return adinputs
def standardizeInstrumentHeaders(self, adinputs=None, suffix=None): """ This primitive is used to make the changes and additions to the keywords in the headers of GMOS data, specifically. Parameters ---------- suffix: str suffix to be added to output files """ log = self.log log.debug(gt.log_message("primitive", self.myself(), "starting")) timestamp_key = self.timestamp_keys[self.myself()] for ad in adinputs: if ad.phu.get(timestamp_key): log.warning("No changes will be made to {}, since it has " "already been processed by " "standardizeInstrumentHeaders".format(ad.filename)) continue # Standardize the headers of the input AstroData object. Update the # keywords in the headers that are specific to GMOS. log.status("Updating keywords that are specific to GMOS") # #M Some of the header keywords are wrong for certain types of # #M Hamamatsu data. This is temporary fix until GMOS-S DC is fixed # if ad.detector_name(pretty=True) == "Hamamatsu-S": # log.status("Fixing headers for GMOS-S Hamamatsu data") # # Image extension headers appear to be correct - MS 2014-10-01 # # correct_image_extensions=Flase # # As does the DATE-OBS but as this seemed to break even after # # apparently being fixed, still perform this check. - MS # hdulist = ad.to_hdulist() # # correct_headers(hdulist, logger=log, # # correct_image_extensions=False) # # When we create the new AD object, it needs to retain the # # filename information # orig_path = ad.path # ad = astrodata.open(hdulist) # ad.path = orig_path # KL Commissioning GMOS-N Hamamatsu. Headers are not fully # KL settled yet. if ad.detector_name(pretty=True) == "Hamamatsu-N": log.status("Fixing headers for GMOS-N Hamamatsu data") try: ad.phu['DATE-OBS'] = ad.phu['DATE'] except KeyError: pass # Update keywords in the image extensions. The descriptors return # the true values on unprepared data. descriptors = ['pixel_scale', 'read_noise', 'gain_setting', 'gain', 'saturation_level'] for desc in descriptors: keyword = ad._keyword_for(desc) comment = self.keyword_comments[keyword] dv = getattr(ad, desc)() if isinstance(dv, list): for ext, value in zip(ad, dv): ext.hdr.set(keyword, value, comment) else: ad.hdr.set(keyword, dv, comment) if 'SPECT' in ad.tags: kw = ad._keyword_for('dispersion_axis') ad.hdr.set(kw, 1, self.keyword_comments[kw]) # And the bias level too! bias_level = get_bias_level(adinput=ad, estimate='qa' in self.mode) for ext, bias in zip(ad, bias_level): if bias is not None: ext.hdr.set('RAWBIAS', bias, self.keyword_comments['RAWBIAS']) # Timestamp and update filename gt.mark_history(ad, primname=self.myself(), keyword=timestamp_key) ad.update_filename(suffix=suffix, strip=True) return adinputs