def test_overscanCorrection_isNotInt(self): """Expect smaller median/smaller std after. Expect exception if overscan fit type isn't known. """ inputExp = isrMock.RawMock().run() amp = inputExp.getDetector()[0] ampI = inputExp.maskedImage[amp.getRawDataBBox()] overscanI = inputExp.maskedImage[amp.getRawHorizontalOverscanBBox()] for fitType in ('MEAN', 'MEDIAN', 'MEANCLIP', 'POLY', 'CHEB', 'NATURAL_SPLINE', 'CUBIC_SPLINE', 'UNKNOWN'): if fitType in ('NATURAL_SPLINE', 'CUBIC_SPLINE'): order = 3 else: order = 1 if fitType == 'UNKNOWN': with self.assertRaises(pexExcept.Exception, msg=f"overscanCorrection overscanIsNotInt fitType: {fitType}"): ipIsr.overscanCorrection(ampI, overscanI, fitType=fitType, order=order, collapseRej=3.0, statControl=None, overscanIsInt=False) else: response = ipIsr.overscanCorrection(ampI, overscanI, fitType=fitType, order=order, collapseRej=3.0, statControl=None, overscanIsInt=False) self.assertIsInstance(response, pipeBase.Struct, msg=f"overscanCorrection overscanIsNotInt Bad response: {fitType}") self.assertIsNotNone(response.imageFit, msg=f"overscanCorrection overscanIsNotInt Bad imageFit: {fitType}") self.assertIsNotNone(response.overscanFit, msg=f"overscanCorrection overscanIsNotInt Bad overscanFit: {fitType}") self.assertIsInstance(response.overscanImage, afwImage.MaskedImageF, msg=f"overscanCorrection overscanIsNotInt Bad overscanImage: {fitType}")
def checkPolyOverscanCorrectionY(self, **kwargs): bbox = afwGeom.Box2I(afwGeom.Point2I(0, 0), afwGeom.Point2I(9, 12)) maskedImage = afwImage.MaskedImageF(bbox) maskedImage.set(10, 0x0, 1) dataBox = afwGeom.Box2I(afwGeom.Point2I(0, 0), afwGeom.Extent2I(10, 10)) dataImage = afwImage.MaskedImageF(maskedImage, dataBox) # these should be functionally equivalent bbox = afwGeom.Box2I(afwGeom.Point2I(0, 10), afwGeom.Point2I(9, 12)) overscan = afwImage.MaskedImageF(maskedImage, bbox) overscan.set(2, 0x0, 1) for i in range(bbox.getDimensions()[0]): for j, off in enumerate([-0.5, 0.0, 0.5]): overscan.image[i, j, afwImage.LOCAL] = 2+i+off ipIsr.overscanCorrection(dataImage, overscan.getImage(), **kwargs) height = maskedImage.getHeight() width = maskedImage.getWidth() for j in range(height): for i in range(width): if j == 10: self.assertEqual(maskedImage.image[i, j, afwImage.LOCAL], -0.5) elif j == 11: self.assertEqual(maskedImage.image[i, j, afwImage.LOCAL], 0) elif j == 12: self.assertEqual(maskedImage.image[i, j, afwImage.LOCAL], 0.5) else: self.assertEqual(maskedImage.image[i, j, afwImage.LOCAL], 10 - 2 - i)
def testOverscanCorrectionX(self, **kwargs): bbox = afwGeom.Box2I(afwGeom.Point2I(0, 0), afwGeom.Point2I(12, 9)) maskedImage = afwImage.MaskedImageF(bbox) maskedImage.set(10, 0x0, 1) dataBox = afwGeom.Box2I(afwGeom.Point2I(0, 0), afwGeom.Extent2I(10, 10)) dataImage = afwImage.MaskedImageF(maskedImage, dataBox) # these should be functionally equivalent bbox = afwGeom.Box2I(afwGeom.Point2I(10, 0), afwGeom.Point2I(12, 9)) biassec = '[11:13,1:10]' overscan = afwImage.MaskedImageF(maskedImage, bbox) overscan.set(2, 0x0, 1) exposure = afwImage.ExposureF(maskedImage, None) metadata = exposure.getMetadata() metadata.setString(self.overscanKeyword, biassec) ipIsr.overscanCorrection(dataImage, overscan.getImage(), fitType="MEDIAN") height = maskedImage.getHeight() width = maskedImage.getWidth() for j in range(height): for i in range(width): if i >= 10: self.assertEqual(maskedImage.getImage().get(i, j), 0) else: self.assertEqual(maskedImage.getImage().get(i, j), 8)
def checkPolyOverscanCorrectionX(self, **kwargs): bbox = afwGeom.Box2I(afwGeom.Point2I(0,0), afwGeom.Point2I(12,9)) maskedImage = afwImage.MaskedImageF(bbox) maskedImage.set(10, 0x0, 1) # these should be functionally equivalent bbox = afwGeom.Box2I(afwGeom.Point2I(10,0), afwGeom.Point2I(12,9)) biassec = '[11:13,1:10]' overscan = afwImage.MaskedImageF(maskedImage, bbox) overscan.set(2, 0x0, 1) for i in range(bbox.getDimensions()[1]): for j,off in enumerate([-0.5, 0.0, 0.5]): overscan.getImage().set(j,i,2+i+off) exposure = afwImage.ExposureF(maskedImage, None) ipIsr.overscanCorrection(maskedImage, overscan.getImage(), **kwargs) height = maskedImage.getHeight() width = maskedImage.getWidth() for j in range(height): for i in range(width): if i == 10: self.assertEqual(maskedImage.getImage().get(i,j), -0.5) elif i == 11: self.assertEqual(maskedImage.getImage().get(i,j), 0) elif i == 12: self.assertEqual(maskedImage.getImage().get(i,j), 0.5) else: self.assertEqual(maskedImage.getImage().get(i,j), 10 - 2 - j)
def checkPolyOverscanCorrectionY(self, **kwargs): bbox = afwGeom.Box2I(afwGeom.Point2I(0, 0), afwGeom.Point2I(9, 12)) maskedImage = afwImage.MaskedImageF(bbox) maskedImage.set(10, 0x0, 1) dataBox = afwGeom.Box2I(afwGeom.Point2I(0, 0), afwGeom.Extent2I(10, 10)) dataImage = afwImage.MaskedImageF(maskedImage, dataBox) # these should be functionally equivalent bbox = afwGeom.Box2I(afwGeom.Point2I(0, 10), afwGeom.Point2I(9, 12)) overscan = afwImage.MaskedImageF(maskedImage, bbox) overscan.set(2, 0x0, 1) for i in range(bbox.getDimensions()[0]): for j, off in enumerate([-0.5, 0.0, 0.5]): overscan.getImage().set(i, j, 2 + i + off) ipIsr.overscanCorrection(dataImage, overscan.getImage(), **kwargs) height = maskedImage.getHeight() width = maskedImage.getWidth() for j in range(height): for i in range(width): if j == 10: self.assertEqual(maskedImage.getImage().get(i, j), -0.5) elif j == 11: self.assertEqual(maskedImage.getImage().get(i, j), 0) elif j == 12: self.assertEqual(maskedImage.getImage().get(i, j), 0.5) else: self.assertEqual(maskedImage.getImage().get(i, j), 10 - 2 - i)
def testOverscanCorrectionY(self, **kwargs): bbox = lsst.geom.Box2I(lsst.geom.Point2I(0, 0), lsst.geom.Point2I(9, 12)) maskedImage = afwImage.MaskedImageF(bbox) maskedImage.set(10, 0x0, 1) dataBox = lsst.geom.Box2I(lsst.geom.Point2I(0, 0), lsst.geom.Extent2I(10, 10)) dataImage = afwImage.MaskedImageF(maskedImage, dataBox) # these should be functionally equivalent bbox = lsst.geom.Box2I(lsst.geom.Point2I(0, 10), lsst.geom.Point2I(9, 12)) biassec = '[1:10,11:13]' overscan = afwImage.MaskedImageF(maskedImage, bbox) overscan.set(2, 0x0, 1) exposure = afwImage.ExposureF(maskedImage, None) metadata = exposure.getMetadata() metadata.setString(self.overscanKeyword, biassec) ipIsr.overscanCorrection(dataImage, overscan.getImage(), fitType="MEDIAN") height = maskedImage.getHeight() width = maskedImage.getWidth() for j in range(height): for i in range(width): if j >= 10: self.assertEqual(maskedImage.image[i, j, afwImage.LOCAL], 0) else: self.assertEqual(maskedImage.image[i, j, afwImage.LOCAL], 8)
def testOverscanCorrectionX(self, **kwargs): bbox = afwGeom.Box2I(afwGeom.Point2I(0,0), afwGeom.Point2I(12,9)) maskedImage = afwImage.MaskedImageF(bbox) maskedImage.set(10, 0x0, 1) # these should be functionally equivalent bbox = afwGeom.Box2I(afwGeom.Point2I(10,0), afwGeom.Point2I(12,9)) biassec = '[11:13,1:10]' overscan = afwImage.MaskedImageF(maskedImage, bbox) overscan.set(2, 0x0, 1) exposure = afwImage.ExposureF(maskedImage, None) metadata = exposure.getMetadata() metadata.setString(self.overscanKeyword, biassec) ipIsr.overscanCorrection(maskedImage, overscan.getImage(), fitType = "MEDIAN") height = maskedImage.getHeight() width = maskedImage.getWidth() for j in range(height): for i in range(width): if i >= 10: self.assertEqual(maskedImage.getImage().get(i,j), 0) else: self.assertEqual(maskedImage.getImage().get(i,j), 8)
def overscanCorrection(self, exposure, amp): """Apply overscan correction in place If the input exposure is on the readout backplanes listed in config.overscanBiasJumpBKP, cut the amplifier in two vertically and correct each piece separately. @param[in,out] exposure: exposure to process; must include both DataSec and BiasSec pixels @param[in] amp: amplifier device data """ if not ( exposure.getMetadata().exists("FPA") and exposure.getMetadata().get("FPA") in self.config.overscanBiasJumpBKP ): IsrTask.overscanCorrection(self, exposure, amp) return dataBox = amp.getRawDataBBox() overscanBox = amp.getRawHorizontalOverscanBBox() if amp.getReadoutCorner() in (afwTable.LL, afwTable.LR): yLower = self.config.overscanBiasJumpLocation yUpper = dataBox.getHeight() - yLower else: yUpper = self.config.overscanBiasJumpLocation yLower = dataBox.getHeight() - yUpper lowerDataBBox = afwGeom.Box2I(dataBox.getBegin(), afwGeom.Extent2I(dataBox.getWidth(), yLower)) upperDataBBox = afwGeom.Box2I( dataBox.getBegin() + afwGeom.Extent2I(0, yLower), afwGeom.Extent2I(dataBox.getWidth(), yUpper) ) lowerOverscanBBox = afwGeom.Box2I(overscanBox.getBegin(), afwGeom.Extent2I(overscanBox.getWidth(), yLower)) upperOverscanBBox = afwGeom.Box2I( overscanBox.getBegin() + afwGeom.Extent2I(0, yLower), afwGeom.Extent2I(overscanBox.getWidth(), yUpper) ) maskedImage = exposure.getMaskedImage() lowerDataView = maskedImage.Factory(maskedImage, lowerDataBBox) upperDataView = maskedImage.Factory(maskedImage, upperDataBBox) expImage = exposure.getMaskedImage().getImage() lowerOverscanImage = expImage.Factory(expImage, lowerOverscanBBox) upperOverscanImage = expImage.Factory(expImage, upperOverscanBBox) overscanCorrection( ampMaskedImage=lowerDataView, overscanImage=lowerOverscanImage, fitType=self.config.overscanFitType, order=self.config.overscanOrder, collapseRej=self.config.overscanRej, ) overscanCorrection( ampMaskedImage=upperDataView, overscanImage=upperOverscanImage, fitType=self.config.overscanFitType, order=self.config.overscanOrder, collapseRej=self.config.overscanRej, )
def test_overscanCorrection_isNotInt(self): """Expect smaller median/smaller std after. Expect exception if overscan fit type isn't known. """ inputExp = isrMock.RawMock().run() amp = inputExp.getDetector()[0] ampI = inputExp.maskedImage[amp.getRawDataBBox()] overscanI = inputExp.maskedImage[amp.getRawHorizontalOverscanBBox()] for fitType in ('MEAN', 'MEDIAN', 'MEANCLIP', 'POLY', 'CHEB', 'NATURAL_SPLINE', 'CUBIC_SPLINE', 'UNKNOWN'): if fitType in ('NATURAL_SPLINE', 'CUBIC_SPLINE'): order = 3 else: order = 1 if fitType == 'UNKNOWN': with self.assertRaises( pexExcept.Exception, msg= f"overscanCorrection overscanIsNotInt fitType: {fitType}" ): ipIsr.overscanCorrection(ampI, overscanI, fitType=fitType, order=order, collapseRej=3.0, statControl=None, overscanIsInt=False) else: response = ipIsr.overscanCorrection(ampI, overscanI, fitType=fitType, order=order, collapseRej=3.0, statControl=None, overscanIsInt=False) self.assertIsInstance( response, pipeBase.Struct, msg= f"overscanCorrection overscanIsNotInt Bad response: {fitType}") self.assertIsNotNone( response.imageFit, msg= f"overscanCorrection overscanIsNotInt Bad imageFit: {fitType}") self.assertIsNotNone( response.overscanFit, msg= f"overscanCorrection overscanIsNotInt Bad overscanFit: {fitType}" ) self.assertIsInstance( response.overscanImage, afwImage.MaskedImageF, msg= f"overscanCorrection overscanIsNotInt Bad overscanImage: {fitType}" )
def checkOverscanCorrectionSineWave(self, **kwargs): """vertical sine wave along long direction""" # Full image: (500,100) longAxis = 500 shortAxis = 100 overscanWidth = 30 bbox = lsst.geom.Box2I(lsst.geom.Point2I(0, 0), lsst.geom.Point2I(shortAxis - 1, longAxis - 1)) maskedImage = afwImage.MaskedImageF(bbox) maskedImage.set(50.0, 0x0, 1) # vertical sine wave along long direction x = np.linspace(0, 2 * 3.14159, longAxis) a, w = 15, 50 * 3.14159 sineWave = 20 + a * np.sin(w * x) sineWave = sineWave.astype(int) fullImage = np.repeat(sineWave, shortAxis).reshape( (longAxis, shortAxis)) maskedImage.image.array += fullImage # data part of the full image: (500,70) dataBox = lsst.geom.Box2I( lsst.geom.Point2I(0, 0), lsst.geom.Extent2I(shortAxis - overscanWidth, longAxis)) dataImage = afwImage.MaskedImageF(maskedImage, dataBox) # these should be functionally equivalent bbox = lsst.geom.Box2I(lsst.geom.Point2I(shortAxis - overscanWidth, 0), lsst.geom.Point2I(shortAxis - 1, longAxis - 1)) biassec = '[1:500,71:100]' overscan = afwImage.MaskedImageF(maskedImage, bbox) overscan.image.array -= 50.0 # subtract initial pedestal exposure = afwImage.ExposureF(maskedImage, None) metadata = exposure.getMetadata() metadata.setString(self.overscanKeyword, biassec) ipIsr.overscanCorrection(dataImage, overscan.getImage(), **kwargs) height = maskedImage.getHeight() width = maskedImage.getWidth() for j in range(height): for i in range(width): if i >= 70: self.assertEqual(maskedImage.image[i, j, afwImage.LOCAL], 0.0) else: self.assertEqual(maskedImage.image[i, j, afwImage.LOCAL], 50.0)
def process(self, clipboard): """ """ self.log.log(Log.INFO, "Doing overscan subtraction.") #grab exposure and overscan bbox from clipboard exposure = clipboard.get(self.policy.getString("inputKeys.exposure")) fittype = self.policy.getString("parameters.overscanFitType") amp = cameraGeom.cast_Amp(exposure.getDetector()) overscanBbox = amp.getDiskBiasSec() dataBbox = amp.getDiskDataSec() #It just so happens that this is an o.k. place to put the SDQA #calculation because the ratings requested at the moment can all be #calculated here. If, for example, an Amp rating an the flat fielded #amp were requested, it would have to be calculated separately. ipIsr.calculateSdqaAmpRatings(exposure, overscanBbox, dataBbox) ipIsr.overscanCorrection(exposure, overscanBbox, fittype) #TODO optionally trim #output products clipboard.put(self.policy.get("outputKeys.overscanCorrectedExposure"), exposure)
def checkPolyOverscanCorrectionX(self, **kwargs): bbox = lsst.geom.Box2I(lsst.geom.Point2I(0, 0), lsst.geom.Point2I(12, 9)) maskedImage = afwImage.MaskedImageF(bbox) maskedImage.set(10, 0x0, 1) dataBox = lsst.geom.Box2I(lsst.geom.Point2I(0, 0), lsst.geom.Extent2I(10, 10)) dataImage = afwImage.MaskedImageF(maskedImage, dataBox) # these should be functionally equivalent bbox = lsst.geom.Box2I(lsst.geom.Point2I(10, 0), lsst.geom.Point2I(12, 9)) overscan = afwImage.MaskedImageF(maskedImage, bbox) overscan.set(2, 0x0, 1) for i in range(bbox.getDimensions()[1]): for j, off in enumerate([-0.5, 0.0, 0.5]): overscan.image[j, i, afwImage.LOCAL] = 2 + i + off ipIsr.overscanCorrection(dataImage, overscan.getImage(), **kwargs) height = maskedImage.getHeight() width = maskedImage.getWidth() for j in range(height): for i in range(width): if i == 10: self.assertEqual(maskedImage.image[i, j, afwImage.LOCAL], -0.5) elif i == 11: self.assertEqual(maskedImage.image[i, j, afwImage.LOCAL], 0) elif i == 12: self.assertEqual(maskedImage.image[i, j, afwImage.LOCAL], 0.5) else: self.assertEqual(maskedImage.image[i, j, afwImage.LOCAL], 10 - 2 - j)
def overscanCorrection(self, exposure, amp): """Apply overscan correction in place If the input exposure is on the readout backplanes listed in config.overscanBiasJumpBKP, cut the amplifier in two vertically and correct each piece separately. @param[in,out] exposure: exposure to process; must include both DataSec and BiasSec pixels @param[in] amp: amplifier device data """ if not (exposure.getMetadata().exists('FPA') and exposure.getMetadata().get('FPA') in self.config.overscanBiasJumpBKP): IsrTask.overscanCorrection(self, exposure, amp) return dataBox = amp.getRawDataBBox() overscanBox = amp.getRawHorizontalOverscanBBox() if amp.getReadoutCorner() in (afwTable.LL, afwTable.LR): yLower = self.config.overscanBiasJumpLocation yUpper = dataBox.getHeight() - yLower else: yUpper = self.config.overscanBiasJumpLocation yLower = dataBox.getHeight() - yUpper lowerDataBBox = afwGeom.Box2I( dataBox.getBegin(), afwGeom.Extent2I(dataBox.getWidth(), yLower)) upperDataBBox = afwGeom.Box2I( dataBox.getBegin() + afwGeom.Extent2I(0, yLower), afwGeom.Extent2I(dataBox.getWidth(), yUpper)) lowerOverscanBBox = afwGeom.Box2I( overscanBox.getBegin(), afwGeom.Extent2I(overscanBox.getWidth(), yLower)) upperOverscanBBox = afwGeom.Box2I( overscanBox.getBegin() + afwGeom.Extent2I(0, yLower), afwGeom.Extent2I(overscanBox.getWidth(), yUpper)) maskedImage = exposure.getMaskedImage() lowerDataView = maskedImage.Factory(maskedImage, lowerDataBBox) upperDataView = maskedImage.Factory(maskedImage, upperDataBBox) expImage = exposure.getMaskedImage().getImage() lowerOverscanImage = expImage.Factory(expImage, lowerOverscanBBox) upperOverscanImage = expImage.Factory(expImage, upperOverscanBBox) overscanCorrection( ampMaskedImage=lowerDataView, overscanImage=lowerOverscanImage, fitType=self.config.overscanFitType, order=self.config.overscanOrder, collapseRej=self.config.overscanRej, ) overscanCorrection( ampMaskedImage=upperDataView, overscanImage=upperOverscanImage, fitType=self.config.overscanFitType, order=self.config.overscanOrder, collapseRej=self.config.overscanRej, )
def draw_bias_sequence(exposures, amp_num=None, overscan_correct=False, \ save_fig=False, same_scale=True, run_num=None, lsst_num=None, \ raft=None, detector_name=None, plot_medians=False): figdims = (10, 8) if amp_num is None: figdims = (15, 5) fig, axes = plt.subplots(nrows=1, ncols=len(exposures), figsize=figdims) clow, chigh = None, None if run_num is None: run_num = exposures[0].getInfo().getMetadata()['RUNNUM'] if lsst_num is None: lsst_num = exposures[0].getInfo().getMetadata()['LSST_NUM'] if raft is None: raft = exposures[0].getInfo().getMetadata()['RAFTBAY'] if detector_name is None: detector_name = exposures[0].getInfo().getMetadata()['CCDSLOT'] arrays = [] medians = [] ampnames = [] for i in range(len(exposures))[::-1]: raw = exposures[i] detector = raw.getDetector() array = None if amp_num is not None: amp = detector[amp_num] im = raw.getMaskedImage()[amp.getRawBBox()].clone() if overscan_correct: isr.overscanCorrection(im[amp.getRawDataBBox()], im[amp.getRawHorizontalOverscanBBox()], fitType='MEDIAN_PER_ROW') array = im[amp.getRawDataBBox()].getImage().getArray().copy() if plot_medians: medians.append(np.median(array)) del im else: raw_clone = raw.clone() median_by_amp = [] if plot_medians: for amp in detector: median = afwMath.makeStatistics( raw.getMaskedImage()[amp.getRawDataBBox()], afwMath.MEDIAN).getValue() median_by_amp.append(median) if not len(ampnames) == 16: ampnames.append(amp.getName()) #print(median_by_amp) medians.append(median_by_amp) task = isr.isrTask.IsrTask() task.config.doAssembleCcd = True task.assembleCcd.config.doTrim = True task.config.overscanFitType = 'MEDIAN_PER_ROW' task.config.doOverscan = overscan_correct task.config.doBias = False task.config.doLinearize = False task.config.doDark = False task.config.doFlat = False task.config.doDefect = False assembled = task.run(raw_clone).exposure array = assembled.getImage().getArray().copy() del assembled, raw_clone if not same_scale: implot = axes[i].imshow(array, cmap='gist_heat') axes[i].set_axis_off() divider = make_axes_locatable(axes[i]) cax = divider.new_vertical(size="5%", pad=0.1, pack_start=True) fig.add_axes(cax) cbar = fig.colorbar(implot, cax=cax, orientation="horizontal") cbar.set_label('Raw ADU'.format(' (Median Row Overscan Corrected)' if overscan_correct else '')) continue arrays.append(array) this_clow = np.amin(array) this_chigh = np.amax(array) if clow is None: clow = this_clow elif this_clow < clow: clow = this_clow if chigh is None: chigh = this_chigh elif this_chigh > chigh: chigh = this_chigh fig.patch.set_facecolor('white') fig.suptitle('Consecutive biases in run {}, {} {} {} : {}'.format( run_num, raft, detector_name, '' if (amp_num is None) else 'Amp {}'.format(amp_num), lsst_num)) if same_scale: for i in range(len(exposures))[::-1]: array = arrays[::-1][i] im = axes[i].imshow(array, clim=(clow, chigh), cmap='gist_heat') axes[i].set_axis_off() if amp_num is None: fig.tight_layout(rect=[0, 0, 1, 1.1]) else: fig.tight_layout(rect=[0, 0, 1, 0.95]) cbar = fig.colorbar(im, ax=axes.ravel().tolist(), shrink=.7) cbar.set_label('Raw ADU{}'.format( ' (Median Row Overscan Corrected)' if overscan_correct else '')) else: fig.tight_layout(rect=[0, 0, 1, .95]) if save_fig: im_path = '{}/{}/{}'.format(run_num, raft, detector_name) Path(im_path).mkdir(parents=True, exist_ok=True) plt.savefig('{}/biases_{}_{}_{}_Amp{:02d}'.format( im_path, run_num, raft, detector_name, amp_num)) plt.show() if plot_medians: fig = plt.figure() plt.plot([str(x) for x in range(1, len(exposures) + 1)], medians[::-1]) fig.patch.set_facecolor('white') plt.xlabel('Exposure') plt.ylabel('Median Pixel Value (ADU)') plt.legend(ampnames, bbox_to_anchor=(1.25, 1)) plt.show() plt.close() return np.array(medians)
def bias_sequence(exposures, amp_num=None, overscan_correct=False, \ save_fig=False, same_scale=True, run_num=None, lsst_num=None, \ raft=None, detector_name=None, plot_medians=True, outfile=None): if run_num is None: run_num = exposures[0].getInfo().getMetadata()['RUNNUM'] if lsst_num is None: lsst_num = exposures[0].getInfo().getMetadata()['LSST_NUM'] if raft is None: raft = exposures[0].getInfo().getMetadata()['RAFTBAY'] if detector_name is None: detector_name = exposures[0].getInfo().getMetadata()['CCDSLOT'] arrays = [] medians = [] higherrs = [] lowerrs = [] ampnames = [] for i in range(len(exposures)): raw = exposures[i] detector = raw.getDetector() array = None if amp_num is not None: amp = detector[amp_num] im = raw.getMaskedImage()[amp.getRawBBox()].clone() if overscan_correct: isr.overscanCorrection(im[amp.getRawDataBBox()], im[amp.getRawHorizontalOverscanBBox()], fitType='MEDIAN_PER_ROW') array = im[amp.getRawDataBBox()].getImage().getArray().copy() median = np.median(array) higherr = np.percentile(array, 84) lowerr = np.percentile(array, 16) medians.append(median) higherrs.append(higherr) lowerrs.append(lowerr) del im else: median_by_amp = [] higherr_by_amp = [] lowerr_by_amp = [] if plot_medians: for amp in detector: im = raw.getMaskedImage()[amp.getRawDataBBox()] array = im.getImage().getArray().copy() median = np.median(array) higherr = np.percentile(array, 84) - median lowerr = np.percentile(array, 16) - median median_by_amp.append(median) higherr_by_amp.append(higherr) lowerr_by_amp.append(lowerr) if not len(ampnames) == 16: ampnames.append(amp.getName()) medians.append(median_by_amp) higherrs.append(higherr_by_amp) lowerrs.append(lowerr_by_amp) medians_arr = np.array(medians) higherrs_arr = np.array(higherrs) lowerrs_arr = np.array(lowerrs) if plot_medians: fig = plt.figure() for i in range(len(ampnames)): plt.errorbar([str(x) for x in range(1, len(exposures) + 1)], medians_arr[:, i], yerr=[lowerrs_arr[:, i], higherrs_arr[:, i]]) fig.patch.set_facecolor('white') plt.xlabel('Exposure') plt.ylabel('Median Pixel Value (ADU)') plt.legend([ '{}: std = {:.1f} ADU'.format(ampnames[i], np.std(medians_arr[1:, i])) for i in range(len(ampnames)) ], bbox_to_anchor=(1.25, 1)) fig.suptitle('Consecutive biases in run {}, {} {} {} : {}'.format( run_num, raft, detector_name, '' if (amp_num is None) else 'Amp {}'.format(amp_num), lsst_num)) plt.show() plt.close() if not outfile is None: filename = 'bias_seqs/{}/bias_seqs_{}_{}_{}_{}.csv'.format( lsst_num, run_num, raft, detector_name, lsst_num) if not outfile == True: filename = outfile else: outdir = 'bias_seqs/{}'.format(lsst_num) os.makedirs(outdir, exist_ok=True) out = csv.writer(open(filename, 'w')) header = [ 'Amp', 'Exposure Number', 'Median Bias (ADU)', 'High Error (ADU)', 'Low Error (ADU)' ] out.writerow(header) for ampnum in range(len(ampnames)): for expnum in range(len(exposures)): row = [ ampnames[ampnum], expnum, medians_arr[expnum, ampnum], higherrs_arr[expnum, ampnum], lowerrs_arr[expnum, ampnum] ] out.writerow(row) return medians_arr, higherrs_arr, lowerrs_arr
def overscanCorrection(self, exposure, amp): """Apply overscan correction in place If the input exposure is on the readout backplanes listed in config.overscanBiasJumpBKP, cut the amplifier in two vertically and correct each piece separately. Parameters ---------- exposure: `lsst.afw.image.Exposure` exposure to process; must include both DataSec and BiasSec pixels amp: `lsst.afw.table.AmpInfoRecord` amplifier device data Returns ------- exposure: `lsst.afw.image.Exposure` exposure corrected in place with updated metadata """ if not (exposure.getMetadata().exists('FPA') and exposure.getMetadata().get('FPA') in self.config.overscanBiasJumpBKP): IsrTask.overscanCorrection(self, exposure, amp) return dataBox = amp.getRawDataBBox() overscanBox = amp.getRawHorizontalOverscanBBox() if amp.getReadoutCorner() in (afwTable.LL, afwTable.LR): yLower = self.config.overscanBiasJumpLocation yUpper = dataBox.getHeight() - yLower else: yUpper = self.config.overscanBiasJumpLocation yLower = dataBox.getHeight() - yUpper lowerDataBBox = afwGeom.Box2I( dataBox.getBegin(), afwGeom.Extent2I(dataBox.getWidth(), yLower)) upperDataBBox = afwGeom.Box2I( dataBox.getBegin() + afwGeom.Extent2I(0, yLower), afwGeom.Extent2I(dataBox.getWidth(), yUpper)) lowerOverscanBBox = afwGeom.Box2I( overscanBox.getBegin(), afwGeom.Extent2I(overscanBox.getWidth(), yLower)) upperOverscanBBox = afwGeom.Box2I( overscanBox.getBegin() + afwGeom.Extent2I(0, yLower), afwGeom.Extent2I(overscanBox.getWidth(), yUpper)) maskedImage = exposure.getMaskedImage() lowerDataView = maskedImage.Factory(maskedImage, lowerDataBBox) upperDataView = maskedImage.Factory(maskedImage, upperDataBBox) expImage = exposure.getMaskedImage().getImage() lowerOverscanImage = expImage.Factory(expImage, lowerOverscanBBox) upperOverscanImage = expImage.Factory(expImage, upperOverscanBBox) overscanCorrection( ampMaskedImage=lowerDataView, overscanImage=lowerOverscanImage, fitType=self.config.overscanFitType, order=self.config.overscanOrder, collapseRej=self.config.overscanRej, ) overscanCorrection( ampMaskedImage=upperDataView, overscanImage=upperOverscanImage, fitType=self.config.overscanFitType, order=self.config.overscanOrder, collapseRej=self.config.overscanRej, ) # Note that overscan correction has been done in exposure metadata metadata = exposure.getMetadata() metadata.set( 'OVERSCAN', 'Overscan corrected on {0}'.format( dt.datetime.now().strftime("%Y-%m-%dT%H:%M:%S"))) exposure.setMetadata(metadata)