def subtractXTalk(mi, coeffs, minPixelToMask=45000, crosstalkStr="CROSSTALK"): """Subtract the crosstalk from MaskedImage mi given a set of coefficients The pixels affected by signal over minPixelToMask have the crosstalkStr bit set """ sctrl = afwMath.StatisticsControl() sctrl.setAndMask(mi.getMask().getPlaneBitMask("BAD")) bkgd = afwMath.makeStatistics(mi, afwMath.MEDIAN, sctrl).getValue() # # These are the pixels that are bright enough to cause crosstalk (more precisely, # the ones that we label as causing crosstalk; in reality all pixels cause crosstalk) # tempStr = "TEMP" # mask plane used to record the bright pixels that we need to mask msk = mi.getMask() msk.addMaskPlane(tempStr) try: fs = afwDetect.FootprintSet(mi, afwDetect.Threshold(minPixelToMask), tempStr) mi.getMask().addMaskPlane(crosstalkStr) afwDisplay.getDisplay().setMaskPlaneColor(crosstalkStr, afwDisplay.MAGENTA) # the crosstalkStr bit will now be set whenever we subtract crosstalk fs.setMask(mi.getMask(), crosstalkStr) crosstalk = mi.getMask().getPlaneBitMask(crosstalkStr) width, height = mi.getDimensions() for i in range(nAmp): bbox = afwGeom.BoxI(afwGeom.PointI(i*(width//nAmp), 0), afwGeom.ExtentI(width//nAmp, height)) ampI = mi.Factory(mi, bbox) for j in range(nAmp): if i == j: continue bbox = afwGeom.BoxI(afwGeom.PointI(j*(width//nAmp), 0), afwGeom.ExtentI(width//nAmp, height)) if (i + j)%2 == 1: ampJ = afwMath.flipImage(mi.Factory(mi, bbox), True, False) # no need for a deep copy else: ampJ = mi.Factory(mi, bbox, afwImage.LOCAL, True) msk = ampJ.getMask() if np.all(msk.getArray() & msk.getPlaneBitMask("BAD")): # Bad amplifier; ignore it completely --- its effect will come out in the bias continue msk &= crosstalk ampJ -= bkgd ampJ *= coeffs[j][i] ampI -= ampJ # # Clear the crosstalkStr bit in the original bright pixels, where tempStr is set # msk = mi.getMask() temp = msk.getPlaneBitMask(tempStr) xtalk_temp = crosstalk | temp np_msk = msk.getArray() mask_indicies = np.where(np.bitwise_and(np_msk, xtalk_temp) == xtalk_temp) np_msk[mask_indicies] &= getattr(np, np_msk.dtype.name)(~crosstalk) finally: msk.removeAndClearMaskPlane(tempStr, True) # added in afw #1853
def getAmplifier(image, amp, ampReference=None, offset=2): """Extract an image of the amplifier from the CCD, along with an offset version The amplifier image will be flipped (if required) to match the orientation of a nominated reference amplifier. An additional image, with the nominated offset applied, is also produced. @param image Image of CCD @param amp Index of amplifier @param ampReference Index of reference amplifier @param offset Offset to apply @return amplifier image, offset amplifier image """ height = image.getHeight() ampBox = afwGeom.Box2I(afwGeom.Point2I(amp * 512, 0), afwGeom.Extent2I(512, height)) ampImage = image.Factory(image, ampBox, afwImage.LOCAL) if ampReference is not None and amp % 2 != ampReference % 2: ampImage = afwMath.flipImage(ampImage, True, False) offBox = afwGeom.Box2I( afwGeom.Point2I(offset if amp == ampReference else 0, 0), afwGeom.Extent2I(510, height)) offsetImage = ampImage.Factory(ampImage, offBox, afwImage.LOCAL) return ampImage, offsetImage
def _assemble(filename, subtract_background, gains): if subtract_background: imageList = _read_with_bg_subtraction(filename) else: imageList = _read(filename) if gains != None: for i in range(len(imageList)): imageList[i].getImage().getArray()[:] *= gains[i] detsize = set([im.getMetadata().get("DETSIZE") for im in imageList]) assert len(detsize) == 1, "Multiple DETSIZEs detected" detsize = detsize.pop() image = afwImage.ImageF(_regionToBox(detsize)) for im in imageList: header = im.getMetadata() ltm11, ltm22 = map(int, [header.get("LTM1_1"), header.get("LTM2_2")]) assert abs(ltm11) == 1 and abs(ltm22) == 1, "Binned data detected" assert "LTM1_2" not in header.names() and "LTM2_1" not in header.names(), "Rotated data detected" datasec = _regionToBox(header.get("DATASEC")) detsec = _regionToBox(header.get("DETSEC")) if subtract_background: data = afwImage.ImageF(im, datasec) else: data = afwImage.ImageF(im.getImage(), datasec) data = afwMath.flipImage(data, ltm11 < 0, ltm22 < 0) target = image.Factory(image, detsec) target <<= data return image
def subtractXTalk(mi, coeffs, minPixelToMask=45000, crosstalkStr="CROSSTALK"): """Subtract the crosstalk from MaskedImage mi given a set of coefficients The pixels affected by signal over minPixelToMask have the crosstalkStr bit set """ sctrl = afwMath.StatisticsControl() sctrl.setAndMask(mi.getMask().getPlaneBitMask("DETECTED")) bkgd = afwMath.makeStatistics(mi, afwMath.MEDIAN, sctrl).getValue() # # These are the pixels that are bright enough to cause crosstalk (more precisely, # the ones that we label as causing crosstalk; in reality all pixels cause crosstalk) # tempStr = "TEMP" # mask plane used to record the bright pixels that we need to mask mi.getMask().addMaskPlane(tempStr) try: fs = afwDetect.FootprintSet(mi, afwDetect.Threshold(minPixelToMask), tempStr) mi.getMask().addMaskPlane(crosstalkStr) afwDisplay.getDisplay().setMaskPlaneColor(crosstalkStr, afwDisplay.MAGENTA) fs.setMask(mi.getMask(), crosstalkStr) # the crosstalkStr bit will now be set whenever # we subtract crosstalk crosstalk = mi.getMask().getPlaneBitMask(crosstalkStr) width, height = mi.getDimensions() for i in range(nAmp): bbox = afwGeom.BoxI(afwGeom.PointI(i*(width//nAmp), 0), afwGeom.ExtentI(width//nAmp, height)) ampI = mi.Factory(mi, bbox) for j in range(nAmp): if i == j: continue bbox = afwGeom.BoxI(afwGeom.PointI(j*(width//nAmp), 0), afwGeom.ExtentI(width//nAmp, height)) if (i + j)%2 == 1: ampJ = afwMath.flipImage(mi.Factory(mi, bbox), True, False) # no need for a deep copy else: ampJ = mi.Factory(mi, bbox, afwImage.LOCAL, True) msk = ampJ.getMask() if np.all(msk.getArray() & msk.getPlaneBitMask("SAT")): # Bad amplifier; ignore it completely --- its effect will come out in the bias continue msk &= crosstalk ampJ -= bkgd ampJ *= coeffs[j][i] ampI -= ampJ # # Clear the crosstalkStr bit in the original bright pixels, where tempStr is set # msk = mi.getMask() temp = msk.getPlaneBitMask(tempStr) xtalk_temp = crosstalk | temp np_msk = msk.getArray() mask_indicies = np.where(np.bitwise_and(np_msk, xtalk_temp) == xtalk_temp) np_msk[mask_indicies] &= getattr(np, np_msk.dtype.name)(~crosstalk) finally: msk.removeAndClearMaskPlane(tempStr, True) # added in afw #1853
def readImage(self): if self.fileDescriptor.parameters: # It looks like the Gen2 std_raw code wouldn't have handled # flipping vs. subimages correctly, so we won't bother to either. # But we'll make sure no one tries to get a subimage, rather than # doing something confusing. raise NotImplementedError("Formatter does not support subimages.") image = ImageU(self.fileDescriptor.location.path) return flipImage(image, self.FLIP_LR, self.FLIP_TB)
def readImage(self, fileDescriptor): if fileDescriptor.parameters: # It looks like the Gen2 std_raw code wouldn't have handled # flipping vs. subimages correctly, so we won't bother to either. # But we'll make sure no one tries to get a subimage, rather than # doing something confusing. raise NotImplementedError("Formatter does not support subimages.") image = ImageU(fileDescriptor.location.path) return flipImage(image, self.FLIP_LR, self.FLIP_TB)
def eimageCallback(im, ccd=None, imageSource=None): """A callback to handle eimages""" im = im.convertF() nQuarter = 1 im = rotateImageBy90(im, nQuarter) im = flipImage(im, True, False) return im
def _flipChipsLR(exp, wcs, dataId, dims=None): """Flip the chip left/right or top/bottom. Process either/and the pixels and wcs Most chips are flipped L/R, but the rotated ones (100..103) are flipped T/B """ flipLR, flipTB = (False, True) if dataId['ccd'] in (100, 101, 102, 103) else (True, False) if exp: exp.setMaskedImage(afwMath.flipImage(exp.getMaskedImage(), flipLR, flipTB)) if wcs: wcs.flipImage(flipLR, flipTB, exp.getDimensions() if dims is None else dims) return exp
def apply_transform(self, transform: ImageSectionTransform, *, allow_view: bool) -> AfwImageSection[_V]: # Docstring inherited. if not (transform.flip_x or transform.flip_y): image = type(self._image)(self._image, deep=not allow_view) else: from lsst.afw.math import flipImage # type: ignore image: AfwImageLike = flipImage(self._image, transform.flip_x, transform.flip_y) image.setXY0(transform.output_bbox.getMin()) return AfwImageSection(image)
def transform_subimage(self, subimage): """Transform an already-extracted subimage to match the orientation and offset of the target amplifier. Parameters ---------- subimage : image-like The subimage to transform; may be any of `lsst.afw.image.Image`, `lsst.afw.image.Mask`, `lsst.afw.image.MaskedImage`, and `lsst.afw.image.Exposure`. Returns ------- transformed : image-like Transformed image of the same type as ``subimage``. """ from lsst.afw.math import flipImage if hasattr(subimage, "getMaskedImage"): # flipImage doesn't support Exposure natively. # And sadly, there's no way to write to an existing MaskedImage, # so we need to make yet another copy. result = subimage.clone() result.setMaskedImage( flipImage( subimage.getMaskedImage(), self._amplifier_comparison & self._amplifier_comparison.FLIPPED_X, self._amplifier_comparison & self._amplifier_comparison.FLIPPED_Y, ) ) else: result = flipImage( subimage, self._amplifier_comparison & self._amplifier_comparison.FLIPPED_X, self._amplifier_comparison & self._amplifier_comparison.FLIPPED_Y, ) if self._is_parent_trimmed: result.setXY0(self._amplifier.getBBox().getMin()) else: result.setXY0(self._amplifier.getRawBBox().getMin() + self._amplifier.getRawXYOffset()) return result
def testFlip(self): """Test that we end up with the correct image after flipping it""" frame = 2 for flipLR, flipTB, x, y in [(True, False, 19, 0), (True, True, 19, 9), (False, True, 0, 9), (False, False, 0, 0)]: outImage = afwMath.flipImage(self.inImage, flipLR, flipTB) if display: ds9.mtv(outImage, frame=frame, title="%s %s" % (flipLR, flipTB)) frame += 1 self.assertEqual(self.inImage.get(0, 0), outImage.get(x, y))
def testFlip(self): """Test that we end up with the correct image after flipping it. """ frame = 2 for flipLR, flipTB, x, y in [(True, False, 19, 0), (True, True, 19, 9), (False, True, 0, 9), (False, False, 0, 0)]: outImage = afwMath.flipImage(self.inImage, flipLR, flipTB) if display: afwDisplay.Display(frame=frame).mtv(outImage, title="%s %s" % (flipLR, flipTB)) frame += 1 self.assertEqual(self.inImage[0, 0, afwImage.LOCAL], outImage[x, y, afwImage.LOCAL])
def testFlip(self): """Test that we end up with the correct image after flipping it. """ frame = 2 for flipLR, flipTB, x, y in [(True, False, 19, 0), (True, True, 19, 9), (False, True, 0, 9), (False, False, 0, 0)]: outImage = afwMath.flipImage(self.inImage, flipLR, flipTB) if display: afwDisplay.Display(frame=frame).mtv(outImage, title=f"{flipLR} {flipTB}") frame += 1 self.assertEqual(self.inImage[0, 0, afwImage.LOCAL], outImage[x, y, afwImage.LOCAL])
def run(self, expRef, butler): """Make summary plots of full focalplane images. """ sbi = SimButlerImage(butler, type=expRef.butlerSubset.datasetType, visit=expRef.dataId['visit']) # Get the per ccd images def parse_name_to_dataId(name_str): raft, sensor = name_str.split() return {'raft': raft[-3:], 'sensor': sensor[-3:]} for ccd in butler.get('camera'): data_id = parse_name_to_dataId(ccd.getName()) data_id.update(expRef.dataId) try: binned_im = sbi.getCcdImage(ccd, binSize=self.config.sensorBinSize, as_masked_image=True)[0] binned_im = rotateImageBy90(binned_im, ccd.getOrientation().getNQuarter()) if self.config.putFullSensors: butler.put(binned_im, 'binned_sensor_fits', **data_id) except (TypeError, RuntimeError): # butler couldn't put the image or there was no image to put continue (x, y) = binned_im.getDimensions() boxes = { 'A': afwGeom.Box2I(afwGeom.PointI(0, y / 2), afwGeom.ExtentI(x, y / 2)), 'B': afwGeom.Box2I(afwGeom.PointI(0, 0), afwGeom.ExtentI(x, y / 2)) } for half in ('A', 'B'): box = boxes[half] butler.put(afwImage.MaskedImageF(binned_im, box), 'binned_sensor_fits_halves', half=half, **data_id) im = cgu.showCamera(butler.get('camera'), imageSource=sbi, binSize=self.config.binSize) expRef.put(im, 'focalplane_summary_fits') im = flipImage(im, False, True) zmap = ZScaleMapping(im, contrast=self.config.contrast) rgb = zmap.makeRgbImage(im, im, im) file_name = expRef.get('focalplane_summary_png_filename') writeRGB(file_name[0], rgb)
def _flipChipsLR(exp, wcs, detectorId, dims=None): """Flip the chip left/right or top/bottom. Process either/and the pixels and wcs Most chips are flipped L/R, but the rotated ones (100..103) are flipped T/B """ flipLR, flipTB = (False, True) if detectorId in (100, 101, 102, 103) else (True, False) if exp: exp.setMaskedImage(afwMath.flipImage(exp.getMaskedImage(), flipLR, flipTB)) if wcs: ampDimensions = exp.getDimensions() if dims is None else dims ampCenter = geom.Point2D(ampDimensions/2.0) wcs = afwGeom.makeFlippedWcs(wcs, flipLR, flipTB, ampCenter) return exp, wcs
def _flipChipsLR(exp, wcs, dataId, dims=None): """Flip the chip left/right or top/bottom. Process either/and the pixels and wcs Most chips are flipped L/R, but the rotated ones (100..103) are flipped T/B """ flipLR, flipTB = (False, True) if dataId['ccd'] in (100, 101, 102, 103) else (True, False) if exp: exp.setMaskedImage( afwMath.flipImage(exp.getMaskedImage(), flipLR, flipTB)) if wcs: wcs.flipImage(flipLR, flipTB, exp.getDimensions() if dims is None else dims) return exp
def _assemble_alt_metadata(filename, metadata_filename, subtract_background, gains, ADC_Offsets = None): if subtract_background and (ADC_Offsets != None): print "**** - Error - both DM and bias background subtraction selected - ****" print "Defaulting to only using DM subtraction" ADC_Offsets = None if subtract_background: print "Using DM background subtraction" imageList = _read_with_bg_subtraction(filename) else: imageList = _read(filename) if ADC_Offsets == None: ADC_Offsets = numpy.zeros(16, dtype = 'f8') if gains != None: # if statement just deals with the fact that _read_with_bg_subtraction doesn't return decorated images... if not subtract_background: # for normal, decorated imaged for i in range(len(imageList)): imageList[i].getImage().getArray()[:] = (imageList[i].getImage().getArray()[:] - ADC_Offsets[i] ) * gains[i] else: #for un-decorated images, as returned by _read (i.e. without background subtraction for i in range(len(imageList)): imageList[i].getArray()[:] = (imageList[i].getArray()[:] - ADC_Offsets[i] ) * gains[i] metadata_imageList = _read(metadata_filename) detsize = set([md_im.getMetadata().get("DETSIZE") for md_im in metadata_imageList]) assert len(detsize) == 1, "Multiple DETSIZEs detected" detsize = detsize.pop() image = afwImage.ImageF(_regionToBox(detsize)) for i in range(len(imageList)): header = metadata_imageList[i].getMetadata() ltm11, ltm22 = map(int, [header.get("LTM1_1"), header.get("LTM2_2")]) assert abs(ltm11) == 1 and abs(ltm22) == 1, "Binned data detected" assert "LTM1_2" not in header.names() and "LTM2_1" not in header.names(), "Rotated data detected" datasec = _regionToBox(header.get("DATASEC")) detsec = _regionToBox(header.get("DETSEC")) if subtract_background: data = afwImage.ImageF(imageList[i], datasec) else: data = afwImage.ImageF(imageList[i].getImage(), datasec) data = afwMath.flipImage(data, ltm11 < 0, ltm22 < 0) target = image.Factory(image, detsec) target <<= data return image
def getAmplifier(image, amp, ampReference=None, offset=2): """Extract an image of the amplifier from the CCD, along with an offset version The amplifier image will be flipped (if required) to match the orientation of a nominated reference amplifier. An additional image, with the nominated offset applied, is also produced. @param image Image of CCD @param amp Index of amplifier @param ampReference Index of reference amplifier @param offset Offset to apply @return amplifier image, offset amplifier image """ height = image.getHeight() ampBox = afwGeom.Box2I(afwGeom.Point2I(amp*512, 0), afwGeom.Extent2I(512, height)) ampImage = image.Factory(image, ampBox, afwImage.LOCAL) if ampReference is not None and amp % 2 != ampReference % 2: ampImage = afwMath.flipImage(ampImage, True, False) offBox = afwGeom.Box2I(afwGeom.Point2I(offset if amp == ampReference else 0, 0), afwGeom.Extent2I(510, height)) offsetImage = ampImage.Factory(ampImage, offBox, afwImage.LOCAL) return ampImage, offsetImage
def readFull(self): # Docstring inherited. rawFile = self.fileDescriptor.location.path amplifier, detector, _ = standardizeAmplifierParameters( self.checked_parameters, self._instrument.getCamera()[self.observationInfo.detector_num], ) if amplifier is not None: # LSST raws are already per-amplifier on disk, and in a different # assembly state than all of the other images we see in # DM-maintained formatters. And we also need to deal with the # on-disk image having different overscans from our nominal # detector. So we can't use afw.cameraGeom.AmplifierIsolator for # most of the implementation (as other formatters do), but we can # call most of the same underlying code to do the work. def findAmpHdu(name): """Find the HDU for the amplifier with the given name, according to cameraGeom. """ for hdu, amp in enumerate(detector): if amp.getName() == name: return hdu + 1 raise LookupError(f"Could not find HDU for amp with name {name}.") reader = lsst.afw.image.ImageFitsReader(rawFile, hdu=findAmpHdu(amplifier.getName())) image = reader.read(dtype=np.dtype(np.int32), allowUnsafe=True) with warn_once(rawFile) as logCmd: # Extract an amplifier from the on-disk detector and fix its # overscan bboxes as necessary to match the on-disk bbox. adjusted_amplifier_builder, _ = fixAmpGeometry( detector[amplifier.getName()], bbox=image.getBBox(), metadata=reader.readMetadata(), logCmd=logCmd, ) on_disk_amplifier = adjusted_amplifier_builder.finish() # We've now got two Amplifier objects in play: # A) 'amplifier' is what the user wants # B) 'on_disk_amplifier' represents the subimage we have. # The one we want has the orientation/shift state of (A) with # the overscan regions of (B). comparison = amplifier.compareGeometry(on_disk_amplifier) # If the flips or origins differ, we need to modify the image # itself. if comparison & comparison.FLIPPED: from lsst.afw.math import flipImage image = flipImage( image, comparison & comparison.FLIPPED_X, comparison & comparison.FLIPPED_Y, ) if comparison & comparison.SHIFTED: image.setXY0(amplifier.getRawBBox().getMin()) # Make a single-amplifier detector that reflects the image we're # returning. detector_builder = detector.rebuild() detector_builder.clear() detector_builder.unsetCrosstalk() if comparison & comparison.REGIONS_DIFFER: # We can't just install the amplifier the user gave us, because # that has the wrong overscan regions; instead we transform the # on-disk amplifier to have the same orientation and offsets as # the given one. adjusted_amplifier_builder.transform( outOffset=on_disk_amplifier.getRawXYOffset(), outFlipX=amplifier.getRawFlipX(), outFlipY=amplifier.getRawFlipY(), ) detector_builder.append(adjusted_amplifier_builder) detector_builder.setBBox(adjusted_amplifier_builder.getBBox()) else: detector_builder.append(amplifier.rebuild()) detector_builder.setBBox(amplifier.getBBox()) exposure = lsst.afw.image.makeExposure(lsst.afw.image.makeMaskedImage(image)) exposure.setDetector(detector_builder.finish()) else: ampExps = readRawAmps(rawFile, detector) exposure = fixAmpsAndAssemble(ampExps, rawFile) self.attachComponentsFromMetadata(exposure) return exposure
def run(self, exposure, crosstalkSources=None): """Perform crosstalk correction on a DECam exposure and its corresponding dataRef. Parameters ---------- exposure : `lsst.afw.image.Exposure` Exposure to correct. dataRef : `lsst.daf.persistence.butlerSubset.ButlerDataRef` DataRef of exposure to correct which must include a dataId with at least one visit and ccdnum. crosstalkSources : `defaultdict` Must contain image data and corresponding crosstalk coefficients for each crosstalk source of the given dataRef victim. This is returned by prepCrosstalk. Returns ------- `lsst.pipe.base.Struct` Struct with components: - ``exposure``: The exposure after crosstalk correction has been applied (`lsst.afw.image.Exposure`). """ self.log.info('Applying crosstalk correction') assert crosstalkSources is not None, "Sources are required for DECam crosstalk correction; \ you must run CrosstalkTask via IsrTask which will \ call prepCrosstalk to get the sources." # Load data from crosstalk 'victim' exposure we want to correct det = exposure.getDetector() for amp in det: ccdnum = det.getId() ccdnum_str = '%02d' % ccdnum victim = ccdnum_str + amp.getName() # If doCrosstalkBeforeAssemble=True, then use getRawBBox(). Otherwise, getBBox(). dataBBox = amp.getRawBBox() # Check to see if victim overscan has been corrected, and if not, correct it first if not exposure.getMetadata().exists('OVERSCAN'): decamisr = IsrTask() decamisr.overscanCorrection(exposure, amp) self.log.warn( 'Overscan correction did not happen prior to crosstalk correction' ) self.log.info( 'Correcting victim %s overscan before crosstalk' % victim) image = exposure.getMaskedImage().getImage() victim_data = image.Factory(image, dataBBox) # Load data from crosstalk 'source' exposures for source in crosstalkSources[victim]: source_data, source_coeff, source_idx = source victim_idx = 0 if 'A' in victim: victim_idx = 0 elif 'B' in victim: victim_idx = 1 else: self.log.fatal( 'DECam victim amp name does not contain A or B, cannot proceed' ) if source_idx != victim_idx: # Occurs with amp A and B mismatch; need to flip horizontally source_data = afwMath.flipImage(source_data, flipLR=True, flipTB=False) # Perform the linear crosstalk correction try: source_data *= source_coeff victim_data -= source_data except RuntimeError: self.log.fatal( 'Crosstalk correction failed for victim %s from source %s' % (victim, source)) return pipeBase.Struct(exposure=exposure, )
def makeImage(self): """Generate a simulated ISR image. Returns ------- exposure : `lsst.afw.image.Exposure` or `dict` Simulated ISR image data. Notes ----- This method currently constructs a "raw" data image by: * Generating a simulated sky with noise * Adding a single Gaussian "star" * Adding the fringe signal * Multiplying the frame by the simulated flat * Adding dark current (and noise) * Adding a bias offset (and noise) * Adding an overscan gradient parallel to the pixel y-axis * Simulating crosstalk by adding a scaled version of each amplifier to each other amplifier. The exposure with image data constructed this way is in one of three formats. * A single image, with overscan and prescan regions retained * A single image, with overscan and prescan regions trimmed * A `dict`, containing the amplifer data indexed by the amplifier name. The nonlinearity, CTE, and brighter fatter are currently not implemented. Note that this method generates an image in the reverse direction as the ISR processing, as the output image here has had a series of instrument effects added to an idealized exposure. """ exposure = self.getExposure() for idx, amp in enumerate(exposure.getDetector()): bbox = None if self.config.isTrimmed is True: bbox = amp.getBBox() else: bbox = amp.getRawDataBBox() ampData = exposure.image[bbox] if self.config.doAddSky is True: self.amplifierAddNoise(ampData, self.config.skyLevel, np.sqrt(self.config.skyLevel)) if self.config.doAddSource is True: for sourceAmp, sourceFlux, sourceX, sourceY in zip( self.config.sourceAmp, self.config.sourceFlux, self.config.sourceX, self.config.sourceY): if idx == sourceAmp: self.amplifierAddSource(ampData, sourceFlux, sourceX, sourceY) if self.config.doAddFringe is True: self.amplifierAddFringe(amp, ampData, np.array(self.config.fringeScale), x0=np.array(self.config.fringeX0), y0=np.array(self.config.fringeY0)) if self.config.doAddFlat is True: if ampData.getArray().sum() == 0.0: self.amplifierAddNoise(ampData, 1.0, 0.0) u0 = exposure.getDimensions().getX() v0 = exposure.getDimensions().getY() self.amplifierMultiplyFlat(amp, ampData, self.config.flatDrop, u0=u0, v0=v0) if self.config.doAddDark is True: self.amplifierAddNoise( ampData, self.config.darkRate * self.config.darkTime / self.config.gain, np.sqrt(self.config.darkRate * self.config.darkTime / self.config.gain)) if self.config.doAddCrosstalk is True: for idxS, ampS in enumerate(exposure.getDetector()): for idxT, ampT in enumerate(exposure.getDetector()): ampDataS = exposure.image[ampS.getBBox( ) if self.config.isTrimmed else ampS.getRawDataBBox()] ampDataT = exposure.image[ampT.getBBox( ) if self.config.isTrimmed else ampT.getRawDataBBox()] ampDataS = afwMath.flipImage( ampDataS, (X_FLIP[ampS.getReadoutCorner()] ^ X_FLIP[ampT.getReadoutCorner()]), (Y_FLIP[ampS.getReadoutCorner()] ^ Y_FLIP[ampT.getReadoutCorner()])) self.amplifierAddCT(ampDataS, ampDataT, self.crosstalkCoeffs[idxT][idxS]) for amp in exposure.getDetector(): bbox = None if self.config.isTrimmed is True: bbox = amp.getBBox() else: bbox = amp.getRawDataBBox() ampData = exposure.image[bbox] if self.config.doAddBias is True: self.amplifierAddNoise( ampData, self.config.biasLevel, self.config.readNoise / self.config.gain) if self.config.doAddOverscan is True: oscanBBox = amp.getRawHorizontalOverscanBBox() oscanData = exposure.image[oscanBBox] self.amplifierAddNoise( oscanData, self.config.biasLevel, self.config.readNoise / self.config.gain) self.amplifierAddYGradient(ampData, -1.0 * self.config.overscanScale, 1.0 * self.config.overscanScale) self.amplifierAddYGradient(oscanData, -1.0 * self.config.overscanScale, 1.0 * self.config.overscanScale) if self.config.doGenerateAmpDict is True: expDict = dict() for amp in exposure.getDetector(): expDict[amp.getName()] = exposure return expDict else: return exposure
def _flipChipsLR(exp, wcs, dataId, dims=None): flipLR, flipTB = (True, False) exp.setMaskedImage( afwMath.flipImage(exp.getMaskedImage(), flipLR, flipTB)) return exp
def testMask(self): """Test that we can flip a Mask""" mask = afwImage.MaskU(10, 20) afwMath.flipImage( mask, True, False) # for a while, swig couldn't handle the resulting Mask::Ptr
def runDataRef(self, expRefList, butler): """Make summary plots of full focalplane images. """ if len(expRefList) == 0: return pipeBase.Struct(exitStatus=1) lsst.afw.fits.setAllowImageCompression( self.config.allowFitsCompression) dstype = expRefList[0].butlerSubset.datasetType if dstype == "raw": def callback(im, ccd, imageSource): return cgu.rawCallback(im, ccd, imageSource, correctGain=True, subtractBias=True) elif dstype == "eimage": callback = eimageCallback elif self.config.doApplySkyCorr: callback = skyCorrCallback else: callback = None for visit in set([er.dataId["visit"] for er in expRefList]): self.log.info("Processing visit %d", visit) expRefListForVisit = [ er for er in expRefList if er.dataId["visit"] == visit ] dataId = expRefListForVisit[0].dataId bi = cgu.ButlerImage(butler, dstype, visit=visit, callback=callback, verbose=True) if self.config.doSensorImages: for dataId in (er.dataId for er in expRefListForVisit): ccd = butler.get('calexp_detector', **dataId) try: md = butler.get('calexp_md', **dataId) except RuntimeError: md = None if md: afwGeom.makeSkyWcs( md, strip=True ) # strip WCS cards; they're invalidated by binning try: binned_im = bi.getCcdImage( ccd, binSize=self.config.sensorBinSize, asMaskedImage=True)[0] binned_im = rotateImageBy90( binned_im, ccd.getOrientation().getNQuarter()) if self.config.putFullSensors: binned_exp = afwImage.ExposureF(binned_im) binned_exp.setMetadata(md) butler.put(binned_exp, 'binned_sensor_fits', **dataId, dstype=dstype) except (TypeError, RuntimeError) as e: # butler couldn't put the image or there was no image to put self.log.warn("Unable to make binned image: %s", e) continue (x, y) = binned_im.getDimensions() boxes = { 'A': afwGeom.Box2I(afwGeom.PointI(0, y / 2), afwGeom.ExtentI(x, y / 2)), 'B': afwGeom.Box2I(afwGeom.PointI(0, 0), afwGeom.ExtentI(x, y / 2)) } for half in ('A', 'B'): box = boxes[half] binned_exp = afwImage.ExposureF(binned_im[box]) binned_exp.setMetadata(md) butler.put(binned_exp, 'binned_sensor_fits_halves', half=half, **dataId, dstype=dstype) im = cgu.showCamera(butler.get('camera'), imageSource=bi, binSize=self.config.binSize) dstypeName = "%s-%s" % ( dstype, self.config.fpId) if self.config.fpId else dstype butler.put(im, 'focal_plane_fits', visit=visit, dstype=dstypeName) # Compute the zscale stretch for just the CCDs that have data. detectorNameList = [ "%s_%s" % (er.dataId["raftName"], er.dataId["detectorName"]) for er in expRefListForVisit ] im_scaling = cgu.showCamera(butler.get('camera'), imageSource=bi, binSize=self.config.binSize, detectorNameList=detectorNameList) zmap = ZScaleMapping(im_scaling, contrast=self.config.contrast) im = flipImage(im, False, True) rgb = zmap.makeRgbImage(im, im, im) file_name = butler.get('focal_plane_png_filename', visit=visit, dstype=dstypeName) writeRGB(file_name[0], rgb) return pipeBase.Struct(exitStatus=0)
def _flipChipsLR(exp, wcs, dataId, dims=None): flipLR, flipTB = (True, False) exp.setMaskedImage(afwMath.flipImage(exp.getMaskedImage(), flipLR, flipTB)) return exp
def testMask(self): """Test that we can flip a Mask. """ mask = afwImage.Mask(10, 20) # for a while, swig couldn't handle the resulting std::shared_ptr<Mask> afwMath.flipImage(mask, True, False)
def checkRepo(self, files=None): # Test amp parameter implementation for the LSST raw formatter. This # is the same for all instruments, so repeating it in other test cases # is wasteful. butler = Butler(self.root, run=self.outputRun) ref = butler.registry.findDataset("raw", self.dataIds[0]) full_assembled = butler.getDirect(ref) unassembled_detector = self.instrumentClass().getCamera()[ ref.dataId["detector"]] assembled_detector = full_assembled.getDetector() for unassembled_amp, assembled_amp in zip(unassembled_detector, assembled_detector): # Check that we're testing what we think we're testing: these # amps should differ in assembly state (offsets, flips), and they # _may_ differ in fundamental geometry if we had to patch the # overscan region sizes. comparison = unassembled_amp.compareGeometry(assembled_amp) self.assertTrue(comparison & AmplifierGeometryComparison.ASSEMBLY_DIFFERS) assembled_subimage = butler.getDirect( ref, parameters={"amp": assembled_amp}) unassembled_subimage = butler.getDirect( ref, parameters={"amp": unassembled_amp.getName()}) self.assertEqual(len(assembled_subimage.getDetector()), 1) self.assertEqual(len(unassembled_subimage.getDetector()), 1) self.assertEqual(len(assembled_subimage.getDetector()), 1) self.assertEqual(len(unassembled_subimage.getDetector()), 1) self.assertImagesEqual( assembled_subimage.image, full_assembled.image[assembled_amp.getRawBBox()]) self.assertImagesEqual( unassembled_subimage.image, flipImage( full_assembled.image[assembled_amp.getRawBBox()], flipLR=unassembled_amp.getRawFlipX(), flipTB=unassembled_amp.getRawFlipY(), ), ) self.assertAmplifiersEqual(assembled_subimage.getDetector()[0], assembled_amp) if comparison & comparison.REGIONS_DIFFER: # We needed to patch overscans, but unassembled_amp (which # comes straight from the camera) won't have those patches, so # we can't compare it to the amp attached to # unassembled_subimage (which does have those patches). comparison2 = unassembled_subimage.getDetector( )[0].compareGeometry(unassembled_amp) self.assertTrue(comparison2 & AmplifierGeometryComparison.REGIONS_DIFFER) # ...and that unassembled_subimage's amp has the same regions # (after accounting for assembly/orientation) as assembled_amp. comparison3 = unassembled_subimage.getDetector( )[0].compareGeometry(assembled_amp) self.assertTrue(comparison3 & AmplifierGeometryComparison.ASSEMBLY_DIFFERS) self.assertFalse(comparison3 & AmplifierGeometryComparison.REGIONS_DIFFER) else: self.assertAmplifiersEqual( unassembled_subimage.getDetector()[0], unassembled_amp)
def readImage(self): index, metadata = self._determineHDU(self.dataId['detector']) image = lsst.afw.image.ImageF(self.fileDescriptor.location.path, index) return flipImage(image, self.FLIP_LR, self.FLIP_TB)
def testMask(self): """Test that we can flip a Mask""" mask = afwImage.MaskU(10, 20) afwMath.flipImage(mask, True, False) # for a while, swig couldn't handle the resulting Mask::Ptr