Source code for kcwidrp.primitives.SubtractOverscan

from keckdrpframework.primitives.base_primitive import BasePrimitive
from kcwidrp.core.bokeh_plotting import bokeh_plot

from bokeh.plotting import figure
# from bokeh.layouts import gridplot
import numpy as np
import math
import time


[docs]class SubtractOverscan(BasePrimitive): """Fit overscan region and subtract result from image""" def __init__(self, action, context): BasePrimitive.__init__(self, action, context) self.logger = context.pipeline_logger def _perform(self): # image sections for each amp bsec, dsec, tsec, direc = self.action.args.map_ccd namps = len(bsec) # polynomial fit order if namps == 4: porder = 2 else: porder = 7 # header keyword to update key = 'OSCANSUB' keycom = 'Overscan subtracted?' # is it performed? performed = False # loop over amps # plts = [] # plots for each amp for ia in range(namps): # get gain gain = self.action.args.ccddata.header['GAIN%d' % (ia + 1)] # check if we have enough data to fit if (bsec[ia][3] - bsec[ia][2]) > self.config.instrument.minoscanpix: # pull out an overscan vector x0 = bsec[ia][2] + self.config.instrument.oscanbuf x1 = bsec[ia][3] - self.config.instrument.oscanbuf y0 = bsec[ia][0] y1 = bsec[ia][1] + 1 osvec = np.nanmedian( self.action.args.ccddata.data[y0:y1, x0:x1], axis=1) nsam = x1 - x0 xx = np.arange(len(osvec), dtype=np.float) # fit it, avoiding first 50 px if direc[ia]: # forward read skips first 50 px oscoef = np.polyfit(xx[50:], osvec[50:], porder) else: # reverse read skips last 50 px oscoef = np.polyfit(xx[:-50], osvec[:-50], porder) # generate fitted overscan vector for full range osfit = np.polyval(oscoef, xx) # calculate residuals resid = (osvec - osfit) * math.sqrt(nsam) * gain / 1.414 sdrs = float("%.3f" % np.std(resid)) self.logger.info("Amp%d Read noise from oscan in e-: %.3f" % ((ia + 1), sdrs)) self.action.args.ccddata.header['OSCNRN%d' % (ia + 1)] = \ (sdrs, "amp%d RN in e- from oscan" % (ia + 1)) if self.config.instrument.plot_level >= 1: x = np.arange(len(osvec)) p = figure(title=self.action.args.plotlabel + 'OSCAN amp %d' % (ia+1), x_axis_label='overscan px', y_axis_label='counts', plot_width=self.config.instrument.plot_width, plot_height=self.config.instrument.plot_height) p.circle(x, osvec, legend_label="Data") p.line(x, osfit, line_color='red', line_width=3, legend_label="Fit") bokeh_plot(p, self.context.bokeh_session) # plts.append(p) if self.config.instrument.plot_level >= 2: input("Next? <cr>: ") else: time.sleep(self.config.instrument.plot_pause) # subtract it for ix in range(dsec[ia][2], dsec[ia][3] + 1): self.action.args.ccddata.data[y0:y1, ix] = \ self.action.args.ccddata.data[y0:y1, ix] - osfit performed = True else: self.logger.info("not enough overscan px to fit amp %d") # if self.config.instrument.plot_level >= 1 and len(plts) > 0: # bokeh_plot(gridplot(plts, ncols=(2 if namps > 2 else 1), # plot_width=500, plot_height=300, # toolbar_location=None), # self.context.bokeh_session) # if self.config.instrument.plot_level >= 2: # input("Next? <cr>: ") # else: # time.sleep(self.config.instrument.plot_pause) self.action.args.ccddata.header[key] = (performed, keycom) log_string = SubtractOverscan.__module__ self.action.args.ccddata.header['HISTORY'] = log_string self.logger.info(log_string) return self.action.args
# END: class SubtractOverscan()