def showPsfCandidates(exposure, psfCellSet, psf=None, frame=None, normalize=True, showBadCandidates=True, variance=None, chi=None): """Display the PSF candidates. If psf is provided include PSF model and residuals; if normalize is true normalize the PSFs (and residuals) If chi is True, generate a plot of residuals/sqrt(variance), i.e. chi """ if chi is None: if variance is not None: # old name for chi chi = variance # # Show us the ccandidates # mos = displayUtils.Mosaic() # candidateCenters = [] candidateCentersBad = [] candidateIndex = 0 for cell in psfCellSet.getCellList(): for cand in cell.begin(False): # include bad candidates cand = algorithmsLib.cast_PsfCandidateF(cand) rchi2 = cand.getChi2() if rchi2 > 1e100: rchi2 = numpy.nan if not showBadCandidates and cand.isBad(): continue if psf: im_resid = displayUtils.Mosaic(gutter=0, background=-5, mode="x") try: im = cand.getMaskedImage() # copy of this object's image xc, yc = cand.getXCenter(), cand.getYCenter() margin = 0 if True else 5 w, h = im.getDimensions() bbox = afwGeom.BoxI(afwGeom.PointI(margin, margin), im.getDimensions()) if margin > 0: bim = im.Factory(w + 2*margin, h + 2*margin) stdev = numpy.sqrt(afwMath.makeStatistics(im.getVariance(), afwMath.MEAN).getValue()) afwMath.randomGaussianImage(bim.getImage(), afwMath.Random()) bim *= stdev var = bim.getVariance(); var.set(stdev**2); del var sbim = im.Factory(bim, bbox) sbim <<= im del sbim im = bim xc += margin; yc += margin im = im.Factory(im, True) im.setXY0(cand.getMaskedImage().getXY0()) except: continue if not variance: im_resid.append(im.Factory(im, True)) # residuals using spatial model chi2 = algorithmsLib.subtractPsf(psf, im, xc, yc) resid = im if variance: resid = resid.getImage() var = im.getVariance() var = var.Factory(var, True) numpy.sqrt(var.getArray(), var.getArray()) # inplace sqrt resid /= var im_resid.append(resid) # Fit the PSF components directly to the data (i.e. ignoring the spatial model) im = cand.getMaskedImage() im = im.Factory(im, True) im.setXY0(cand.getMaskedImage().getXY0()) noSpatialKernel = afwMath.cast_LinearCombinationKernel(psf.getKernel()) candCenter = afwGeom.PointD(cand.getXCenter(), cand.getYCenter()) fit = algorithmsLib.fitKernelParamsToImage(noSpatialKernel, im, candCenter) params = fit[0] kernels = afwMath.KernelList(fit[1]) outputKernel = afwMath.LinearCombinationKernel(kernels, params) outImage = afwImage.ImageD(outputKernel.getDimensions()) outputKernel.computeImage(outImage, False) im -= outImage.convertF() resid = im if margin > 0: bim = im.Factory(w + 2*margin, h + 2*margin) afwMath.randomGaussianImage(bim.getImage(), afwMath.Random()) bim *= stdev sbim = im.Factory(bim, bbox) sbim <<= resid del sbim resid = bim if variance: resid = resid.getImage() resid /= var im_resid.append(resid) im = im_resid.makeMosaic() else: im = cand.getMaskedImage() if normalize: im /= afwMath.makeStatistics(im, afwMath.MAX).getValue() objId = splitId(cand.getSource().getId(), True)["objId"] if psf: lab = "%d chi^2 %.1f" % (objId, rchi2) ctype = ds9.RED if cand.isBad() else ds9.GREEN else: lab = "%d flux %8.3g" % (objId, cand.getSource().getPsfFlux()) ctype = ds9.GREEN mos.append(im, lab, ctype) if False and numpy.isnan(rchi2): ds9.mtv(cand.getMaskedImage().getImage(), title="candidate", frame=1) print "amp", cand.getAmplitude() im = cand.getMaskedImage() center = (candidateIndex, xc - im.getX0(), yc - im.getY0()) candidateIndex += 1 if cand.isBad(): candidateCentersBad.append(center) else: candidateCenters.append(center) if variance: title = "chi(Psf fit)" else: title = "Stars & residuals" mosaicImage = mos.makeMosaic(frame=frame, title=title) with ds9.Buffering(): for centers, color in ((candidateCenters, ds9.GREEN), (candidateCentersBad, ds9.RED)): for cen in centers: bbox = mos.getBBox(cen[0]) ds9.dot("+", cen[1] + bbox.getMinX(), cen[2] + bbox.getMinY(), frame=frame, ctype=color) return mosaicImage
def showPsfMosaic(exposure, psf=None, nx=7, ny=None, showCenter=True, showEllipticity=False, showFwhm=False, stampSize=0, frame=None, title=None): """Show a mosaic of Psf images. exposure may be an Exposure (optionally with PSF), or a tuple (width, height) If stampSize is > 0, the psf images will be trimmed to stampSize*stampSize """ scale = 1.0 if showFwhm: showEllipticity = True scale = 2*math.log(2) # convert sigma^2 to HWHM^2 for a Gaussian mos = displayUtils.Mosaic() try: # maybe it's a real Exposure width, height = exposure.getWidth(), exposure.getHeight() x0, y0 = exposure.getXY0() if not psf: psf = exposure.getPsf() except AttributeError: try: # OK, maybe a list [width, height] width, height = exposure[0], exposure[1] x0, y0 = 0, 0 except TypeError: # I guess not raise RuntimeError("Unable to extract width/height from object of type %s" % type(exposure)) if not ny: ny = int(nx*float(height)/width + 0.5) if not ny: ny = 1 centroidName = "base_GaussianCentroid" shapeName = "base_SdssShape" schema = afwTable.SourceTable.makeMinimalSchema() schema.getAliasMap().set("slot_Centroid", centroidName) schema.getAliasMap().set("slot_Centroid_flag", centroidName+"_flag") control = measBase.GaussianCentroidControl() centroider = measBase.GaussianCentroidAlgorithm(control, centroidName, schema) sdssShape = measBase.SdssShapeControl() shaper = measBase.SdssShapeAlgorithm(sdssShape, shapeName, schema) table = afwTable.SourceTable.make(schema) table.defineCentroid(centroidName) table.defineShape(shapeName) bbox = None if stampSize > 0: w, h = psf.computeImage(afwGeom.PointD(0, 0)).getDimensions() if stampSize <= w and stampSize <= h: bbox = afwGeom.BoxI(afwGeom.PointI((w - stampSize)//2, (h - stampSize)//2), afwGeom.ExtentI(stampSize, stampSize)) centers = [] shapes = [] for iy in range(ny): for ix in range(nx): x = int(ix*(width-1)/(nx-1)) + x0 y = int(iy*(height-1)/(ny-1)) + y0 im = psf.computeImage(afwGeom.PointD(x, y)).convertF() imPeak = psf.computePeak(afwGeom.PointD(x, y)) im /= imPeak if bbox: im = im.Factory(im, bbox) lab = "PSF(%d,%d)" % (x, y) if False else "" mos.append(im, lab) exp = afwImage.makeExposure(afwImage.makeMaskedImage(im)) w, h = im.getWidth(), im.getHeight() centerX = im.getX0() + w//2 centerY = im.getY0() + h//2 src = table.makeRecord() foot = afwDet.Footprint(exp.getBBox()) foot.addPeak(centerX, centerY, 1) src.setFootprint(foot) centroider.measure(src, exp) centers.append((src.getX() - im.getX0(), src.getY() - im.getY0())) shaper.measure(src, exp) shapes.append((src.getIxx(), src.getIxy(), src.getIyy())) mos.makeMosaic(frame=frame, title=title if title else "Model Psf", mode=nx) if centers and frame is not None: with ds9.Buffering(): for i, (cen, shape) in enumerate(zip(centers, shapes)): bbox = mos.getBBox(i) xc, yc = cen[0] + bbox.getMinX(), cen[1] + bbox.getMinY() if showCenter: ds9.dot("+", xc, yc, ctype=ds9.BLUE, frame=frame) if showEllipticity: ixx, ixy, iyy = shape ixx *= scale ixy *= scale iyy *= scale ds9.dot("@:%g,%g,%g" % (ixx, ixy, iyy), xc, yc, frame=frame, ctype=ds9.RED) return mos
def getClumps(self, sigma=1.0, display=False): if self._num <= 0: raise RuntimeError("No candidate PSF sources") psfImage = self.getImage() # # Embed psfImage into a larger image so we can smooth when measuring it # width, height = psfImage.getWidth(), psfImage.getHeight() largeImg = psfImage.Factory(afwGeom.ExtentI(2 * width, 2 * height)) largeImg.set(0) bbox = afwGeom.BoxI(afwGeom.PointI(width, height), afwGeom.ExtentI(width, height)) largeImg.assign(psfImage, bbox, afwImage.LOCAL) # # Now measure that image, looking for the highest peak. Start by building an Exposure # msk = afwImage.MaskU(largeImg.getDimensions()) msk.set(0) var = afwImage.ImageF(largeImg.getDimensions()) var.set(1) mpsfImage = afwImage.MaskedImageF(largeImg, msk, var) mpsfImage.setXY0(afwGeom.PointI(-width, -height)) del msk del var exposure = afwImage.makeExposure(mpsfImage) # # Next run an object detector # maxVal = afwMath.makeStatistics(psfImage, afwMath.MAX).getValue() threshold = maxVal - sigma * math.sqrt(maxVal) if threshold <= 0.0: threshold = maxVal threshold = afwDetection.Threshold(threshold) ds = afwDetection.FootprintSet(mpsfImage, threshold, "DETECTED") # # And measure it. This policy isn't the one we use to measure # Sources, it's only used to characterize this PSF histogram # schema = SourceTable.makeMinimalSchema() psfImageConfig = SingleFrameMeasurementConfig() psfImageConfig.slots.centroid = "base_SdssCentroid" psfImageConfig.plugins["base_SdssCentroid"].doFootprintCheck = False psfImageConfig.slots.psfFlux = None # "base_PsfFlux" psfImageConfig.slots.apFlux = "base_CircularApertureFlux_3_0" psfImageConfig.slots.modelFlux = None psfImageConfig.slots.instFlux = None psfImageConfig.slots.calibFlux = None psfImageConfig.slots.shape = "base_SdssShape" # Formerly, this code had centroid.sdss, flux.psf, flux.naive, # flags.pixel, and shape.sdss psfImageConfig.algorithms.names = [ "base_SdssCentroid", "base_CircularApertureFlux", "base_SdssShape" ] psfImageConfig.algorithms["base_CircularApertureFlux"].radii = [3.0] psfImageConfig.validate() task = SingleFrameMeasurementTask(schema, config=psfImageConfig) sourceCat = SourceCatalog(schema) gaussianWidth = 1.5 # Gaussian sigma for detection convolution exposure.setPsf(algorithmsLib.DoubleGaussianPsf(11, 11, gaussianWidth)) ds.makeSources(sourceCat) # # Show us the Histogram # if display: frame = 1 dispImage = mpsfImage.Factory( mpsfImage, afwGeom.BoxI(afwGeom.PointI(width, height), afwGeom.ExtentI(width, height)), afwImage.LOCAL) ds9.mtv(dispImage, title="PSF Selection Image", frame=frame) clumps = list() # List of clumps, to return e = None # thrown exception IzzMin = 1.0 # Minimum value for second moments IzzMax = ( self._xSize / 8.0)**2 # Max value ... clump radius should be < clumpImgSize/8 apFluxes = [] task.run( sourceCat, exposure) # notes that this is backwards for the new framework for i, source in enumerate(sourceCat): if source.getCentroidFlag(): continue x, y = source.getX(), source.getY() apFluxes.append(source.getApFlux()) val = mpsfImage.getImage().get(int(x) + width, int(y) + height) psfClumpIxx = source.getIxx() psfClumpIxy = source.getIxy() psfClumpIyy = source.getIyy() if display: if i == 0: ds9.pan(x, y, frame=frame) ds9.dot("+", x, y, ctype=ds9.YELLOW, frame=frame) ds9.dot("@:%g,%g,%g" % (psfClumpIxx, psfClumpIxy, psfClumpIyy), x, y, ctype=ds9.YELLOW, frame=frame) if psfClumpIxx < IzzMin or psfClumpIyy < IzzMin: psfClumpIxx = max(psfClumpIxx, IzzMin) psfClumpIyy = max(psfClumpIyy, IzzMin) if display: ds9.dot("@:%g,%g,%g" % (psfClumpIxx, psfClumpIxy, psfClumpIyy), x, y, ctype=ds9.RED, frame=frame) det = psfClumpIxx * psfClumpIyy - psfClumpIxy * psfClumpIxy try: a, b, c = psfClumpIyy / det, -psfClumpIxy / det, psfClumpIxx / det except ZeroDivisionError: a, b, c = 1e4, 0, 1e4 clumps.append( Clump(peak=val, x=x, y=y, a=a, b=b, c=c, ixx=psfClumpIxx, ixy=psfClumpIxy, iyy=psfClumpIyy)) if len(clumps) == 0: msg = "Failed to determine center of PSF clump" if e: msg += ": %s" % e raise RuntimeError(msg) # if it's all we got return it if len(clumps) == 1: return clumps # which clump is the best? # if we've undistorted the moments, stars should only have 1 clump # use the apFlux from the clump measurement, and take the highest # ... this clump has more psf star candidate neighbours than the others. # get rid of any that are huge, and thus poorly defined goodClumps = [] for clump in clumps: if clump.ixx < IzzMax and clump.iyy < IzzMax: goodClumps.append(clump) # if culling > IzzMax cost us all clumps, we'll have to take what we have if len(goodClumps) == 0: goodClumps = clumps # use the 'brightest' clump iBestClump = numpy.argsort(apFluxes)[0] clumps = [clumps[iBestClump]] return clumps
def _makeAmpInfoCatalog(self, ccd): """Construct an amplifier info catalog """ # Much of this will need to be filled in when we know it. assert len(ccd['amplifiers']) > 0 amp = list(ccd['amplifiers'].values())[0] rawBBox = self._makeBBoxFromList(amp['rawBBox']) # total in file xRawExtent, yRawExtent = rawBBox.getDimensions() from lsst.afw.table import LL, LR, UL, UR readCorners = dict(LL=LL, LR=LR, UL=UL, UR=UR) schema = AmpInfoTable.makeMinimalSchema() linThreshKey = schema.addField('linearityThreshold', type=float) linMaxKey = schema.addField('linearityMaximum', type=float) linUnitsKey = schema.addField('linearityUnits', type=str, size=9) hduKey = schema.addField('hdu', type=np.int32) # end placeholder self.ampInfoDict = {} ampCatalog = AmpInfoCatalog(schema) for name, amp in sorted(ccd['amplifiers'].items(), key=lambda x: x[1]['hdu']): record = ampCatalog.addNew() record.setName(name) record.set(hduKey, amp['hdu']) ix, iy = amp['ixy'] perAmpData = amp['perAmpData'] if perAmpData: x0, y0 = 0, 0 # origin of data within each amp image else: x0, y0 = ix * xRawExtent, iy * yRawExtent rawDataBBox = self._makeBBoxFromList( amp['rawDataBBox']) # Photosensitive area xDataExtent, yDataExtent = rawDataBBox.getDimensions() record.setBBox( afwGeom.BoxI( afwGeom.PointI(ix * xDataExtent, iy * yDataExtent), rawDataBBox.getDimensions())) rawBBox = self._makeBBoxFromList(amp['rawBBox']) rawBBox.shift(afwGeom.ExtentI(x0, y0)) record.setRawBBox(rawBBox) rawDataBBox = self._makeBBoxFromList(amp['rawDataBBox']) rawDataBBox.shift(afwGeom.ExtentI(x0, y0)) record.setRawDataBBox(rawDataBBox) rawSerialOverscanBBox = self._makeBBoxFromList( amp['rawSerialOverscanBBox']) rawSerialOverscanBBox.shift(afwGeom.ExtentI(x0, y0)) record.setRawHorizontalOverscanBBox(rawSerialOverscanBBox) rawParallelOverscanBBox = self._makeBBoxFromList( amp['rawParallelOverscanBBox']) rawParallelOverscanBBox.shift(afwGeom.ExtentI(x0, y0)) record.setRawVerticalOverscanBBox(rawParallelOverscanBBox) rawSerialPrescanBBox = self._makeBBoxFromList( amp['rawSerialPrescanBBox']) rawSerialPrescanBBox.shift(afwGeom.ExtentI(x0, y0)) record.setRawPrescanBBox(rawSerialPrescanBBox) if perAmpData: record.setRawXYOffset( afwGeom.Extent2I(ix * xRawExtent, iy * yRawExtent)) else: record.setRawXYOffset(afwGeom.Extent2I(0, 0)) record.setReadoutCorner(readCorners[amp['readCorner']]) record.setGain(amp['gain']) record.setReadNoise(amp['readNoise']) record.setSaturation(amp['saturation']) record.setHasRawInfo(True) # flip data when assembling if needs be (e.g. data from the serial at the top of a CCD) flipX, flipY = amp.get("flipXY") record.setRawFlipX(flipX) record.setRawFlipY(flipY) # linearity placeholder stuff record.setLinearityCoeffs( [float(val) for val in amp['linearityCoeffs']]) record.setLinearityType(amp['linearityType']) record.set(linThreshKey, float(amp['linearityThreshold'])) record.set(linMaxKey, float(amp['linearityMax'])) record.set(linUnitsKey, "DN") return ampCatalog
def makeMosaic(self, images=None, display="deferToFrame", mode=None, background=None, title="", frame=None): """Return a mosaic of all the images provided; if none are specified, use the list accumulated with Mosaic.append(). Note that this mosaic is a patchwork of the input images; if you want to make a mosaic of a set images of the sky, you probably want to use the coadd code If display or frame (deprecated) is specified, display the mosaic """ if not images: images = self.images self.nImage = len(images) if self.nImage == 0: raise RuntimeError, "You must provide at least one image" self.xsize, self.ysize = 0, 0 for im in images: w, h = im.getWidth(), im.getHeight() if w > self.xsize: self.xsize = w if h > self.ysize: self.ysize = h if background is None: background = self.background if mode is None: mode = self.mode if mode == "square": nx, ny = 1, self.nImage while nx * im.getWidth() < ny * im.getHeight(): nx += 1 ny = self.nImage // nx if nx * ny < self.nImage: ny += 1 if nx * ny < self.nImage: nx += 1 if nx > self.nImage: nx = self.nImage assert (nx * ny >= self.nImage) elif mode == "x": nx, ny = self.nImage, 1 elif mode == "y": nx, ny = 1, self.nImage elif isinstance(mode, int): nx = mode ny = self.nImage // nx if nx * ny < self.nImage: ny += 1 else: raise RuntimeError, ("Unknown mosaicing mode: %s" % mode) self.nx, self.ny = nx, ny mosaic = images[0].Factory( afwGeom.Extent2I(nx * self.xsize + (nx - 1) * self.gutter, ny * self.ysize + (ny - 1) * self.gutter)) try: mosaic.set(self.background) except AttributeError: raise RuntimeError( "Attempt to mosaic images of type %s which don't support set" % type(mosaic)) for i in range(len(images)): smosaic = mosaic.Factory(mosaic, self.getBBox(i % nx, i // nx), afwImage.LOCAL) im = images[i] if smosaic.getDimensions() != im.getDimensions( ): # im is smaller than smosaic llc = afwGeom.PointI( (smosaic.getWidth() - im.getWidth()) // 2, (smosaic.getHeight() - im.getHeight()) // 2) smosaic = smosaic.Factory( smosaic, afwGeom.Box2I(llc, im.getDimensions()), afwImage.LOCAL) smosaic <<= im display = _getDisplayFromDisplayOrFrame(display, frame) if display: display.mtv(mosaic, title=title) if images == self.images: self.drawLabels(display=display) return mosaic
def addAmp(ampCatalog, i, eparams): """ Add an amplifier to an AmpInfoCatalog @param ampCatalog: An instance of an AmpInfoCatalog object to fill with amp properties @param i which amplifier? (i == 0 ? left : right) @param eparams: Electronic parameters. This is a list of tuples with (i, params), where params is a dictionary of electronic parameters. """ # # Layout of active and overclock pixels in the as-readout data The layout is: # Amp0 || extended | overclock | data || data | overclock | extended || Amp1 # for each row; all rows are identical in drift-scan data # height = 1361 # number of rows in a frame width = 1024 # number of data pixels read out through one amplifier nExtended = 8 # number of pixels in the extended register nOverclock = 32 # number of (horizontal) overclock pixels # # Construct the needed bounding boxes given that geometrical information. # # Note that all the offsets are relative to the origin of this amp, not to its eventual # position in the CCD # record = ampCatalog.addNew() xtot = width + nExtended + nOverclock allPixels = afwGeom.BoxI(afwGeom.PointI(0, 0), afwGeom.ExtentI(xtot, height)) biasSec = afwGeom.BoxI(afwGeom.PointI(nExtended if i == 0 else width, 0), afwGeom.ExtentI(nOverclock, height)) dataSec = afwGeom.BoxI( afwGeom.PointI(nExtended + nOverclock if i == 0 else 0, 0), afwGeom.ExtentI(width, height)) emptyBox = afwGeom.BoxI() bbox = afwGeom.BoxI(afwGeom.PointI(0, 0), afwGeom.ExtentI(width, height)) bbox.shift(afwGeom.Extent2I(width * i, 0)) shiftp = afwGeom.Extent2I(xtot * i, 0) allPixels.shift(shiftp) biasSec.shift(shiftp) dataSec.shift(shiftp) record.setBBox(bbox) record.setRawXYOffset(afwGeom.ExtentI(0, 0)) record.setName('left' if i == 0 else 'right') record.setReadoutCorner(afwTable.LL if i == 0 else afwTable.LR) record.setGain(eparams['gain']) record.setReadNoise(eparams['readNoise']) record.setSaturation(eparams['fullWell']) record.setSuspectLevel(float("nan")) record.setLinearityType(NullLinearityType) record.setLinearityCoeffs([ 1., ]) record.setHasRawInfo(True) record.setRawFlipX(False) record.setRawFlipY(False) record.setRawBBox(allPixels) record.setRawDataBBox(dataSec) record.setRawHorizontalOverscanBBox(biasSec) record.setRawVerticalOverscanBBox(emptyBox) record.setRawPrescanBBox(emptyBox)
def showPsfResiduals(exposure, sourceSet, magType="psf", scale=10, frame=None): mimIn = exposure.getMaskedImage() mimIn = mimIn.Factory(mimIn, True) # make a copy to subtract from psf = exposure.getPsf() psfWidth, psfHeight = psf.getLocalKernel().getDimensions() # # Make the image that we'll paste our residuals into. N.b. they can overlap the edges # w, h = int(mimIn.getWidth() / scale), int(mimIn.getHeight() / scale) im = mimIn.Factory(w + psfWidth, h + psfHeight) cenPos = [] for s in sourceSet: x, y = s.getX(), s.getY() sx, sy = int(x / scale + 0.5), int(y / scale + 0.5) smim = im.Factory( im, afwGeom.BoxI(afwGeom.PointI(sx, sy), afwGeom.ExtentI(psfWidth, psfHeight))) sim = smim.getImage() try: if magType == "ap": flux = s.getApFlux() elif magType == "model": flux = s.getModelFlux() elif magType == "psf": flux = s.getPsfFlux() else: raise RuntimeError("Unknown flux type %s" % magType) subtractPsf(psf, mimIn, x, y, flux) except Exception as e: print(e) try: expIm = mimIn.getImage().Factory( mimIn.getImage(), afwGeom.BoxI( afwGeom.PointI( int(x) - psfWidth // 2, int(y) - psfHeight // 2), afwGeom.ExtentI(psfWidth, psfHeight)), ) except pexExcept.Exception: continue cenPos.append([x - expIm.getX0() + sx, y - expIm.getY0() + sy]) sim += expIm if frame is not None: ds9.mtv(im, frame=frame) with ds9.Buffering(): for x, y in cenPos: ds9.dot("+", x, y, frame=frame) return im
def write(self, dirName=".", fileName=None, metadata=None): if not pyfits: raise RuntimeError( "I failed to import pyfits, so cannot write to disk") if fileName is None: fileName = self.fileNameFormat % (self.obsDate, self.arm, self.spectrograph) fullFileName = os.path.join(dirName, fileName) # # We'll pack all the traces into a single masked image, so figure out how large it needs to be # # Start by unpacking the traces' BBoxes; we need to do this anyway for the fits I/O # minX = [] minY = [] maxX = [] maxY = [] width = 0 for i in range(len(self.traces)): bbox = self.traces[i].getBBox() minX.append(bbox.getMinX()) minY.append(bbox.getMinY()) maxX.append(bbox.getMaxX()) maxY.append(bbox.getMaxY()) width += bbox.getWidth() height = max(maxY) + 1 allTracesMI = afwImage.MaskedImageF(width, height) # Copy trace's MaskedImages to allTracesMI x0 = 0 origin = afwGeom.PointI(0, 0) for i in range(len(self.traces)): trace = self.traces[i] xy0 = afwGeom.Point2I(x0, minY[i]) # origin in allTracesMI allTracesMI[afwGeom.BoxI(xy0, trace.getDimensions())] = \ trace.Factory(trace, afwGeom.BoxI(origin, trace.getDimensions()), afwImage.LOCAL) x0 += trace.getWidth() # # Time to actually write the data # if metadata is None: hdr = dafBase.PropertySet() else: hdr = metadata hdr.add('OBSTYPE', 'FIBERTRACE') # Write fits file from MaskedImage allTracesMI.writeFits(fullFileName, hdr) # append the additional HDUs hdu = pyfits.BinTableHDU.from_columns([ pyfits.Column(name='FIBERID', format='J', array=np.array(self.fiberId, dtype=np.int32)), pyfits.Column(name='MINX', format='J', array=np.array(minX, dtype=np.int32)), pyfits.Column(name='MINY', format='J', array=np.array(minY, dtype=np.int32)), pyfits.Column(name='MAXX', format='J', array=np.array(maxX, dtype=np.int32)), pyfits.Column(name='MAXY', format='J', array=np.array(maxY, dtype=np.int32)), ]) hdu.name = "ID_BOX" hdu.header["INHERIT"] = True # clobber=True in writeto prints a message, so use open instead with pyfits.open(fullFileName, "update") as fd: fd[1].name = "IMAGE" fd[2].name = "MASK" fd[3].name = "VARIANCE" fd.append(hdu)
def _get_bbox(self, tract): """returns a afwGeom.BoxI instance given a `tract` and the current options (ra, dec, side_pixel)""" xy = afwGeom.PointI(tract.getWcs().skyToPixel(self.radec)) cutout_size = afwGeom.ExtentI(self.side_pixel, self.side_pixel) return afwGeom.BoxI(xy - cutout_size // 2, cutout_size)
def measure(self): """Detect and measure sources""" mi = self.exposure.getMaskedImage() # # We do a pretty good job of interpolating, so don't propagagate the convolved CR/INTRP bits # (we'll keep them for the original CR/INTRP pixels) # savedMask = mi.getMask().Factory(mi.getMask(), True) saveBits = savedMask.getPlaneBitMask("CR") | \ savedMask.getPlaneBitMask("BAD") | \ savedMask.getPlaneBitMask("INTRP") # Bits to not convolve savedMask &= saveBits msk = mi.getMask(); msk &= ~saveBits; del msk # Clear the saved bits # # Smooth image # cnvImage = mi.Factory(mi.getBBox(afwImage.PARENT)) afwMath.convolve(cnvImage, mi, self.psf.getKernel(), afwMath.ConvolutionControl()) msk = cnvImage.getMask(); msk |= savedMask; del msk # restore the saved bits threshold = afwDetection.Threshold(3, afwDetection.Threshold.STDEV) # # Only search the part of the frame that was PSF-smoothed # llc = afwGeom.PointI(self.psf.getKernel().getWidth()/2, self.psf.getKernel().getHeight()/2) urc = afwGeom.PointI(cnvImage.getWidth() - 1, cnvImage.getHeight() - 1) - afwGeom.ExtentI(llc[0], llc[1]); middle = cnvImage.Factory(cnvImage, afwGeom.BoxI(llc, urc), afwImage.LOCAL) ds = afwDetection.FootprintSetF(middle, threshold, "DETECTED") del middle # # ds only searched the middle but it belongs to the entire MaskedImage # ds.setRegion(mi.getBBox(afwImage.PARENT)) # # We want to grow the detections into the edge by at least one pixel so that it sees the EDGE bit # grow, isotropic = 1, False ds = afwDetection.FootprintSetF(ds, grow, isotropic) ds.setMask(mi.getMask(), "DETECTED") # # Reinstate the saved (e.g. BAD) (and also the DETECTED | EDGE) bits in the unsmoothed image # savedMask <<= cnvImage.getMask() msk = mi.getMask(); msk |= savedMask; del msk del savedMask; savedMask = None #msk = mi.getMask(); msk &= ~0x10; del msk # XXXX if self.display: ds9.mtv(mi, frame = 0, lowOrderBits = True) ds9.mtv(cnvImage, frame = 1) objects = ds.getFootprints() # # Time to actually measure # msPolicy = policy.Policy.createPolicy(policy.DefaultPolicyFile("meas_algorithms", "examples/measureSources.paf")) msPolicy = msPolicy.getPolicy("measureSources") measureSources = measAlg.makeMeasureSources(self.exposure, msPolicy) self.sourceList = afwDetection.SourceSet() for i in range(len(objects)): source = afwDetection.Source() self.sourceList.append(source) source.setId(i) source.setFlagForDetection(source.getFlagForDetection() | measAlg.Flags.BINNED1); try: measureSources.apply(source, objects[i]) except Exception, e: try: print e except Exception, ee: print ee
def makeRaft(raftName): dewar = cameraGeom.Raft(cameraGeom.Id("DECam"), 1, 1) dewar.addDetector(afwGeom.PointI(0, 0), cameraGeom.FpPoint(0.0, 0.0), cameraGeom.Orientation(0), makeCcd(raftName)) return dewar
def setBrightObjectMasks(self, exposure, dataId, brightObjectMasks): """Set the bright object masks exposure: Exposure under consideration dataId: Data identifier dict for patch brightObjectMasks: afwTable of bright objects to mask # ---------------------------------------------- # copied from AssembleCoaddTask. To be implemented so that it can be imported more easily see https://jira.lsstcorp.org/browse/DM-15030 # ---------------------------------------------- # """ # # Check the metadata specifying the tract/patch/filter # if brightObjectMasks is None: self.log.warn("Unable to apply bright object mask: none supplied") return self.log.info("Applying %d bright object masks to %s", len(brightObjectMasks), dataId) md = brightObjectMasks.table.getMetadata() for k in dataId: if not md.exists(k): self.log.warn("Expected to see %s in metadata", k) else: if md.get(k) != dataId[k]: self.log.warn( "Expected to see %s == %s in metadata, saw %s", k, md.get(k), dataId[k]) mask = exposure.getMaskedImage().getMask() wcs = exposure.getWcs() plateScale = wcs.getPixelScale().asArcseconds() for rec in brightObjectMasks: center = afwGeom.PointI(wcs.skyToPixel(rec.getCoord())) if rec["type"] == "box": assert rec["angle"] == 0.0, ("Angle != 0 for mask object %s" % rec["id"]) width = rec["width"].asArcseconds( ) / plateScale # convert to pixels height = rec["height"].asArcseconds( ) / plateScale # convert to pixels halfSize = afwGeom.ExtentI(0.5 * width, 0.5 * height) bbox = afwGeom.Box2I(center - halfSize, center + halfSize) bbox = afwGeom.BoxI( afwGeom.PointI(int(center[0] - 0.5 * width), int(center[1] - 0.5 * height)), afwGeom.PointI(int(center[0] + 0.5 * width), int(center[1] + 0.5 * height))) spans = afwGeom.SpanSet(bbox) elif rec["type"] == "circle": radius = int(rec["radius"].asArcseconds() / plateScale) # convert to pixels spans = afwGeom.SpanSet.fromShape(radius, offset=center) else: self.log.warn("Unexpected region type %s at %s" % rec["type"], center) continue spans.clippedTo(mask.getBBox()).setMask(mask, self.brightObjectBitmask)
def readFits(fileName, hdu=0, flags=0): """Read a our list of Backgrounds from a file @param fileName FITS file to read @param hdu First Header/Data Unit to attempt to read from @param flags Flags to control details of reading; currently unused, but present for consistency with afw.table.BaseCatalog.readFits. See also getImage() """ if not isinstance(fileName, MemFileManager) and not os.path.exists(fileName): raise RuntimeError("File not found: %s" % fileName) self = BackgroundList() while True: hdu += 1 md = dafBase.PropertyList() try: img = afwImage.ImageF(fileName, hdu, md) hdu += 1 except FitsError: break msk = afwImage.MaskU(fileName, hdu) hdu += 1 var = afwImage.ImageF(fileName, hdu) statsImage = afwImage.makeMaskedImage(img, msk, var) x0 = md.get("BKGD_X0") y0 = md.get("BKGD_Y0") width = md.get("BKGD_WIDTH") height = md.get("BKGD_HEIGHT") imageBBox = afwGeom.BoxI(afwGeom.PointI(x0, y0), afwGeom.ExtentI(width, height)) interpStyle = md.get("INTERPSTYLE") undersampleStyle = md.get("UNDERSAMPLESTYLE") # Older outputs won't have APPROX* settings. Provide alternative defaults. # Note: Currently X- and Y-orders must be equal due to a limitation in # math::Chebyshev1Function2. Setting approxOrderY = -1 is equivalent # to saying approxOrderY = approxOrderX. approxStyle = md.get("APPROXSTYLE") if "APPROXSTYLE" in md.names() \ else afwMath.ApproximateControl.UNKNOWN approxOrderX = md.get( "APPROXORDERX") if "APPROXORDERX" in md.names() else 1 approxOrderY = md.get( "APPROXORDERY") if "APPROXORDERY" in md.names() else -1 approxWeighting = md.get( "APPROXWEIGHTING") if "APPROXWEIGHTING" in md.names() else True bkgd = afwMath.BackgroundMI(imageBBox, statsImage) bctrl = bkgd.getBackgroundControl() bctrl.setInterpStyle(interpStyle) bctrl.setUndersampleStyle(undersampleStyle) actrl = afwMath.ApproximateControl(approxStyle, approxOrderX, approxOrderY, approxWeighting) bctrl.setApproximateControl(actrl) bgInfo = (bkgd, interpStyle, undersampleStyle, approxStyle, approxOrderX, approxOrderY, approxWeighting) self.append(bgInfo) return self
# From SimonK's tutorial # 3/31/2017 -- MSSG import sys import lsst.afw.image as afwImage import lsst.afw.geom as afwGeom from lsst.pex.exceptions import LengthError import lsst.afw.math as afwMath import lsst.afw.detection as afwDetect import lsst.afw.display as afwDisplay n_objects = 1000 box = afwGeom.BoxI(afwGeom.PointI(300, 500), afwGeom.ExtentI(2000, 2048)) im = afwImage.ImageF(box) print("im.getXY0() = ", im.getXY0()) subbox = afwGeom.BoxI(afwGeom.PointI(10, 10), afwGeom.ExtentI(100, 100)) try: im2 = afwImage.ImageF(im, subbox) except LengthError: im2 = afwImage.ImageF(im, subbox, afwImage.LOCAL) print("im2.getXY0() = ", im2.getXY0()) rand = afwMath.Random() buffer_xy = 150 # Don't put objects near the edges x_positions = [
def makeBbox(x0, y0, x_extent, y_extent): return afwGeom.BoxI(afwGeom.PointI(x0, y0), afwGeom.ExtentI(x_extent, y_extent))
def testDetection(self): """Test object detection""" # # Fix defects # # Mask known bad pixels # measAlgorithmsDir = lsst.utils.getPackageDir('meas_algorithms') badPixels = defects.policyToBadRegionList( os.path.join(measAlgorithmsDir, "policy/BadPixels.paf")) # did someone lie about the origin of the maskedImage? If so, adjust bad pixel list if self.XY0.getX() != self.mi.getX0() or self.XY0.getY( ) != self.mi.getY0(): dx = self.XY0.getX() - self.mi.getX0() dy = self.XY0.getY() - self.mi.getY0() for bp in badPixels: bp.shift(-dx, -dy) algorithms.interpolateOverDefects(self.mi, self.psf, badPixels) # # Subtract background # bgGridSize = 64 # was 256 ... but that gives only one region and the spline breaks bctrl = afwMath.BackgroundControl(afwMath.Interpolate.NATURAL_SPLINE) bctrl.setNxSample(int(self.mi.getWidth() / bgGridSize) + 1) bctrl.setNySample(int(self.mi.getHeight() / bgGridSize) + 1) backobj = afwMath.makeBackground(self.mi.getImage(), bctrl) self.mi.getImage()[:] -= backobj.getImageF() # # Remove CRs # crConfig = algorithms.FindCosmicRaysConfig() crs = algorithms.findCosmicRays(self.mi, self.psf, 0, pexConfig.makePolicy(crConfig)) # # We do a pretty good job of interpolating, so don't propagagate the convolved CR/INTRP bits # (we'll keep them for the original CR/INTRP pixels) # savedMask = self.mi.getMask().Factory(self.mi.getMask(), True) saveBits = savedMask.getPlaneBitMask("CR") | \ savedMask.getPlaneBitMask("BAD") | \ savedMask.getPlaneBitMask("INTRP") # Bits to not convolve savedMask &= saveBits msk = self.mi.getMask() msk &= ~saveBits # Clear the saved bits del msk # # Smooth image # psf = algorithms.DoubleGaussianPsf( 15, 15, self.FWHM / (2 * math.sqrt(2 * math.log(2)))) cnvImage = self.mi.Factory(self.mi.getBBox()) kernel = psf.getKernel() afwMath.convolve(cnvImage, self.mi, kernel, afwMath.ConvolutionControl()) msk = cnvImage.getMask() msk |= savedMask # restore the saved bits del msk threshold = afwDetection.Threshold(3, afwDetection.Threshold.STDEV) # # Only search the part of the frame that was PSF-smoothed # llc = afwGeom.PointI(psf.getKernel().getWidth() / 2, psf.getKernel().getHeight() / 2) urc = afwGeom.PointI(cnvImage.getWidth() - llc[0] - 1, cnvImage.getHeight() - llc[1] - 1) middle = cnvImage.Factory(cnvImage, afwGeom.BoxI(llc, urc), afwImage.LOCAL) ds = afwDetection.FootprintSet(middle, threshold, "DETECTED") del middle # # Reinstate the saved (e.g. BAD) (and also the DETECTED | EDGE) bits in the unsmoothed image # savedMask[:] = cnvImage.getMask() msk = self.mi.getMask() msk |= savedMask del msk del savedMask if display: ds9.mtv(self.mi, frame=0) ds9.mtv(cnvImage, frame=1) # # Time to actually measure # schema = afwTable.SourceTable.makeMinimalSchema() sfm_config = measBase.SingleFrameMeasurementConfig() sfm_config.plugins = [ "base_SdssCentroid", "base_CircularApertureFlux", "base_PsfFlux", "base_SdssShape", "base_GaussianFlux", "base_ClassificationExtendedness", "base_PixelFlags" ] sfm_config.slots.centroid = "base_SdssCentroid" sfm_config.slots.shape = "base_SdssShape" sfm_config.slots.psfFlux = "base_PsfFlux" sfm_config.slots.instFlux = None sfm_config.slots.apFlux = "base_CircularApertureFlux_3_0" sfm_config.slots.modelFlux = "base_GaussianFlux" sfm_config.slots.calibFlux = None sfm_config.plugins["base_SdssShape"].maxShift = 10.0 sfm_config.plugins["base_CircularApertureFlux"].radii = [3.0] task = measBase.SingleFrameMeasurementTask(schema, config=sfm_config) measCat = afwTable.SourceCatalog(schema) # detect the sources and run with the measurement task ds.makeSources(measCat) self.exposure.setPsf(self.psf) task.run(measCat, self.exposure) for source in measCat: if source.get("base_PixelFlags_flag_edge"): continue if display: ds9.dot("+", source.getX() - self.mi.getX0(), source.getY() - self.mi.getY0())
def testEdge(self): """Test that we can interpolate to the edge""" mi = afwImage.MaskedImageF(80, 30) ima = mi.getImage().getArray() # # Loop over number of bad columns at left or right edge of image # for nBadCol in range(0, 20): mi.set((0, 0x0, 0)) np.random.seed(666) ima[:] = np.random.uniform(-1, 1, ima.shape) defects = [] if nBadCol > 0: # # Bad left edge # ima[:, 0:nBadCol] = 10 defects.append( afwGeom.BoxI(afwGeom.PointI(0, 0), afwGeom.ExtentI(nBadCol, mi.getHeight()))) # # With another bad set of columns next to bad left edge # ima[:, -nBadCol:] = 10 defects.append( afwGeom.BoxI(afwGeom.PointI(mi.getWidth() - nBadCol, 0), afwGeom.ExtentI(nBadCol, mi.getHeight()))) # # Bad right edge # ima[0:10, nBadCol + 1:nBadCol + 4] = 100 defects.append( afwGeom.BoxI(afwGeom.PointI(nBadCol + 1, 0), afwGeom.ExtentI(3, 10))) # # With another bad set of columns next to bad right edge # ima[0:10, -nBadCol - 4:-nBadCol - 1] = 100 defects.append((afwGeom.BoxI( afwGeom.PointI(mi.getWidth() - nBadCol - 4, 0), afwGeom.ExtentI(3, 10)))) # # Test cases that left and right bad patches nearly (or do) coalesce # ima[-3:, 0:mi.getWidth() // 2 - 1] = 100 defects.append( afwGeom.BoxI(afwGeom.PointI(0, mi.getHeight() - 3), afwGeom.ExtentI(mi.getWidth() // 2 - 1, 1))) ima[-3:, mi.getWidth() // 2 + 1:] = 100 defects.append( afwGeom.BoxI( afwGeom.PointI(mi.getWidth() // 2 + 1, mi.getHeight() - 3), afwGeom.ExtentI(mi.getWidth() // 2 - 1, 1))) ima[-2:, 0:mi.getWidth() // 2] = 100 defects.append( afwGeom.BoxI(afwGeom.PointI(0, mi.getHeight() - 2), afwGeom.ExtentI(mi.getWidth() // 2, 1))) ima[-2:, mi.getWidth() // 2 + 1:] = 100 defects.append( afwGeom.BoxI( afwGeom.PointI(mi.getWidth() // 2 + 1, mi.getHeight() - 2), afwGeom.ExtentI(mi.getWidth() // 2 - 1, 1))) ima[-1:, :] = 100 defects.append( afwGeom.BoxI(afwGeom.PointI(0, mi.getHeight() - 1), afwGeom.ExtentI(mi.getWidth(), 1))) # Test fix for HSC-978: long defect stops one pixel shy of the edge (when nBadCol == 0) ima[13, :-1] = 100 defects.append( afwGeom.BoxI(afwGeom.PointI(0, 13), afwGeom.ExtentI(mi.getWidth() - 1, 1))) ima[14, 1:] = 100 defects.append( afwGeom.BoxI(afwGeom.PointI(1, 14), afwGeom.ExtentI(mi.getWidth() - 1, 1))) # # Build list of defects to interpolate over # defectList = [] for bbox in defects: defectList.append(algorithms.Defect(bbox)) # # Guess a PSF and do the work # if display: ds9.mtv(mi, frame=0) psf = algorithms.DoubleGaussianPsf( 15, 15, 1. / (2 * math.sqrt(2 * math.log(2)))) algorithms.interpolateOverDefects(mi, psf, defectList, 0, True) if display: ds9.mtv(mi, frame=1) self.assertGreater(np.min(ima), -2) self.assertGreater(2, np.max(ima))
def _makeBBoxFromList(ylist): """Given a list [(x0, y0), (xsize, ysize)], probably from a yaml file, return a BoxI """ (x0, y0), (xsize, ysize) = ylist return afwGeom.BoxI(afwGeom.PointI(x0, y0), afwGeom.ExtentI(xsize, ysize))
def showCamera(camera, imageSource=FakeImageDataSource(), imageFactory=afwImage.ImageF, detectorNameList=None, binSize=10, bufferSize=10, frame=None, overlay=True, title="", ctype=afwDisplay.GREEN, textSize=1.25, originAtCenter=True, display=None, **kwargs): """!Show a Camera on display, with the specified display The rotation of the sensors is snapped to the nearest multiple of 90 deg. Also note that the pixel size is constant over the image array. The lower left corner (LLC) of each sensor amp is snapped to the LLC of the pixel containing the LLC of the image. if overlay show the IDs and detector boundaries @param[in] camera Camera to show @param[in] imageSource Source to get Ccd images from. Must have a getCcdImage method. @param[in] imageFactory Type of image to make @param[in] detectorNameList List of names of Detectors to use. If None use all @param[in] binSize bin factor @param[in] bufferSize size of border in binned pixels to make around camera image. @param[in] frame specify image display (@deprecated; new code should use display) @param[in] overlay Overlay Detector IDs and boundaries? @param[in] title Title in display @param[in] ctype Color to use when drawing Detector boundaries @param[in] textSize Size of detector labels @param[in] originAtCenter Put origin of the camera WCS at the center of the image? Else it will be LL @param[in] display image display on which to display @param[in] **kwargs all remaining keyword arguments are passed to makeImageFromCamera @return the mosaic image """ display = _getDisplayFromDisplayOrFrame(display, frame) if binSize < 1: binSize = 1 cameraImage = makeImageFromCamera(camera, detectorNameList=detectorNameList, bufferSize=bufferSize, imageSource=imageSource, imageFactory=imageFactory, binSize=binSize, **kwargs) if detectorNameList is None: ccdList = [camera[name] for name in camera.getNameIter()] else: ccdList = [camera[name] for name in detectorNameList] if detectorNameList is None: camBbox = camera.getFpBBox() else: camBbox = afwGeom.Box2D() for detName in detectorNameList: for corner in camera[detName].getCorners(FOCAL_PLANE): camBbox.include(corner) pixelSize = ccdList[0].getPixelSize() if originAtCenter: #Can't divide SWIGGED extent type things when division is imported #from future. This is DM-83 ext = cameraImage.getBBox().getDimensions() wcsReferencePixel = afwGeom.PointI(ext.getX() // 2, ext.getY() // 2) else: wcsReferencePixel = afwGeom.Point2I(0, 0) wcs = makeFocalPlaneWcs(pixelSize * binSize, wcsReferencePixel) if display: if title == "": title = camera.getName() display.mtv(cameraImage, title=title, wcs=wcs) if overlay: with display.Buffering(): camBbox = getCameraImageBBox(camBbox, pixelSize, bufferSize * binSize) bboxList = getCcdInCamBBoxList(ccdList, binSize, pixelSize, camBbox.getMin()) for bbox, ccd in zip(bboxList, ccdList): nQuarter = ccd.getOrientation().getNQuarter() # borderWidth to 0.5 to align with the outside edge of the pixel displayUtils.drawBBox(bbox, borderWidth=0.5, ctype=ctype, display=display) dims = bbox.getDimensions() display.dot(ccd.getName(), bbox.getMinX() + dims.getX() / 2, bbox.getMinY() + dims.getY() / 2, ctype=ctype, size=textSize, textAngle=nQuarter * 90) return cameraImage
jmp = {} jmp['x'] = [] jmp['y'] = [] jmp['flux'] = [] for line in file: if line.startswith('#'): continue x = float(line.split()[0]) y = float(line.split()[1]) if ccdnum < 18: x = imsize.getX() - x + xoff y = imsize.getY() - y + yoff else: x = x - xoff y = y - yoff pt = afwGeom.PointI(int(x), int(y)) if bbox.contains(pt): jmp['x'].append(x) jmp['y'].append(y) jmp['flux'].append(float(line.split()[2])) jmp['x'] = np.array(jmp['x'], float) jmp['y'] = np.array(jmp['y'], float) jmp['flux'] = np.array(jmp['flux'], float) except IOError: print 'No JMP file %s' %(jmpfile) jmp = {} jmp['x'] = [] jmp['y'] = [] jmp['flux'] = [] try:
def make_postage_stamp_plot(self, index, stamp_size=100, reverse_colormap=False): """ Gather the data for creating the source postage stamp plot. Parameters ---------- index : int The location of the source data in the Source table. stamp_size : int, optional Size of the generated postage stamp in pixels. reverse_colormap : bool, optional Reverse the colormap. Default is black (low) to white (high). """ ra_target, dec_target = ( self.src['coord_ra'][self.good_indexes][index], self.src['coord_dec'][self.good_indexes][index]) # Radians radec = afwGeom.SpherePoint(ra_target, dec_target, afwGeom.radians) cutoutSize = afwGeom.ExtentI(stamp_size, stamp_size) wcs = self.calexp.getWcs() # Note: This call fails in version 15.0. Requires a weekly after that release. xy = afwGeom.PointI(wcs.skyToPixel(radec)) bbox = afwGeom.BoxI(xy - cutoutSize // 2, cutoutSize) # Check for bounds that fall off the edges of the image. Need to clip them to the # image boundary otherwise the calexp_sub call fails. calexp_extent = self.calexp.getDimensions() clipped_bbox = afwGeom.BoxI(afwGeom.PointI(0, 0), calexp_extent) clipped_bbox.clip(bbox) # Postage stamp image only cutout_image = self.butler.get('calexp_sub', bbox=clipped_bbox, immediate=True, dataId=self.dataid).getMaskedImage() vmin, vmax = self.zscale.get_limits(cutout_image.image.array) self.image_src.data = { 'img': [cutout_image.image.array], 'x': [0], 'y': [0], 'dw': [clipped_bbox.getDimensions().getX()], 'dh': [clipped_bbox.getDimensions().getY()] } gc = gray(256) if reverse_colormap: gc.reverse() lcm = LinearColorMapper(palette=gc, low=vmin, high=vmax) self.img_plt.image('img', 'x', 'y', 'dw', 'dh', color_mapper=lcm, source=self.image_src) # Color bar doesn't come up properly. Need to work on this later. colorbar = ColorBar(color_mapper=lcm, ticker=BasicTicker(), border_line_color=None, label_standoff=5, location=(0, 0)) # self.img_plt.add_layout(colorbar, 'right') # Does the cutout_image have a wcs? It does not appear to... self.img_plt.circle(xy.getX() - cutout_image.getX0(), xy.getY() - cutout_image.getY0(), fill_color=None, line_color='red', radius=int(0.05 * stamp_size))
def addAmp(ampCatalog, amp, eparams): """ Add an amplifier to an AmpInfoCatalog @param ampCatalog: An instance of an AmpInfoCatalog object to fill with amp properties @param amp: Dictionary of amp geometric properties @param eparams: Dictionary of amp electronic properties for this amp """ record = ampCatalog.addNew() xtot = amp['ewidth'] ytot = amp['eheight'] allPixels = afwGeom.Box2I(afwGeom.Point2I(0, 0), afwGeom.Extent2I(xtot, ytot)) biassl = amp['biassec'] biasSec = afwGeom.Box2I(afwGeom.Point2I(biassl[0], biassl[1]), afwGeom.Point2I(biassl[2], biassl[3])) datasl = amp['datasec'] dataSec = afwGeom.Box2I(afwGeom.Point2I(datasl[0], datasl[1]), afwGeom.Point2I(datasl[2], datasl[3])) extended = dataSec.getMin().getX() voverscan = ytot - dataSec.getMaxY() pscan = dataSec.getMin().getY() if not voverscan: voscanSec = afwGeom.Box2I(afwGeom.Point2I(extended, dataSec.getMax().getY()), afwGeom.Extent2I(dataSec.getDimensions().getX(), 0)) else: voscanSec = afwGeom.Box2I(afwGeom.Point2I(extended, dataSec.getMax().getY()+1), afwGeom.Extent2I(dataSec.getDimensions().getX(), voverscan)) pscanSec = afwGeom.Box2I(afwGeom.Point2I(extended, 0), afwGeom.Extent2I(dataSec.getDimensions().getX(), pscan)) if amp['flipX']: #No need to flip bbox or allPixels since they #are at the origin and span the full pixel grid biasSec.flipLR(xtot) dataSec.flipLR(xtot) voscanSec.flipLR(xtot) pscanSec.flipLR(xtot) bbox = afwGeom.BoxI(afwGeom.PointI(0, 0), dataSec.getDimensions()) bbox.shift(afwGeom.Extent2I(dataSec.getDimensions().getX()*eparams['index'][0], 0)) shiftp = afwGeom.Extent2I(xtot*eparams['index'][0], 0) allPixels.shift(shiftp) biasSec.shift(shiftp) dataSec.shift(shiftp) voscanSec.shift(shiftp) pscanSec.shift(shiftp) record.setBBox(bbox) record.setRawXYOffset(afwGeom.ExtentI(0,0)) #Set amplifier names according to the CFHT convention (A, B) if eparams['index'][0] == 0 and eparams['index'][1] == 0 : record.setName("A") elif eparams['index'][0] == 1 and eparams['index'][1] == 0 : record.setName("B") else : raise ValueError("Unexpected index parameter %i, %i"%(eparams['index'][0], eparams['index'][1])) record.setReadoutCorner(afwTable.LR if amp['flipX'] else afwTable.LL) record.setGain(eparams['gain']) record.setReadNoise(eparams['readNoise']) record.setSaturation(int(eparams['saturation'])) #The files do not have any linearity information record.setLinearityType('Proportional') record.setLinearityCoeffs([1.,]) record.setHasRawInfo(True) record.setRawFlipX(False) record.setRawFlipY(False) record.setRawBBox(allPixels) record.setRawDataBBox(dataSec) record.setRawHorizontalOverscanBBox(biasSec) record.setRawVerticalOverscanBBox(voscanSec) record.setRawPrescanBBox(pscanSec)
def fitPatches(exp, nx=4, ny=8, bin=None, frame=None, returnResidualImage=False, r=None, lnGrad=None, theta=None): """Fit planes to a set of patches of an image im If r, theta, and lnGrad are provided they should be lists (more accurately, support .append), and they have the values of r, theta, and dlnI/dr from this image appended. """ width, height = exp.getDimensions() if bin is not None: nx, ny = width // bin, height // bin xsize = width // nx ysize = height // ny if frame is not None: frame0 = frame ds9.mtv(exp, title="im", frame=frame) if True else None frame += 1 if hasattr(exp, "getMaskedImage"): mi = exp.getMaskedImage() ccd = exp.getDetector() else: mi = exp ccd = None try: mi = mi.convertF() except AttributeError: pass if r is not None or lnGrad is not None: assert ccd is not None, "I need a CCD to set r and the logarithmic gradient" assert r is not None and lnGrad is not None, "Please provide both r and lnGrad" z = afwImage.ImageF(nx, ny) za = z.getArray() dlnzdx = afwImage.ImageF(nx, ny) dlnzdxa = dlnzdx.getArray() dlnzdy = afwImage.ImageF(nx, ny) dlnzdya = dlnzdy.getArray() dlnzdr = afwImage.ImageF(nx, ny) dlnzdra = dlnzdr.getArray() if returnResidualImage: residualImage = mi.clone() try: residualImage = residualImage.convertF() except AttributeError: pass else: residualImage = None with ds9.Buffering(): for iy in range(ny): for ix in range(nx): bbox = afwGeom.BoxI(afwGeom.PointI(ix * xsize, iy * ysize), afwGeom.ExtentI(xsize, ysize)) if False and frame is not None: ds9Utils.drawBBox(bbox, frame=frame0) b, res = fitPlane(mi[bbox].getImage(), returnResidualImage=returnResidualImage, niter=5) b[1:] /= b[0] za[iy, ix], dlnzdxa[iy, ix], dlnzdya[iy, ix] = b if returnResidualImage: residualImage[bbox][:] = res if ccd: cen = afwGeom.PointD(bbox.getBegin() + bbox.getDimensions() / 2) x, y = ccd.getPositionFromPixel( cen).getMm() # I really want pixels here t = np.arctan2(y, x) dlnzdra[iy, ix] = np.cos(t) * dlnzdxa[iy, ix] + np.sin( t) * dlnzdya[iy, ix] if r is not None: if not (ix in (0, xsize - 1) or iy in (0, ysize - 1)): r.append(np.hypot(x, y)) lnGrad.append(dlnzdra[iy, ix]) if theta is not None: theta.append(t) if frame is not None: if False: ds9.mtv(z, title="z", frame=frame) frame += 1 ds9.mtv(dlnzdx, title="dlnz/dx", frame=frame) frame += 1 ds9.mtv(dlnzdy, title="dlnz/dy", frame=frame) frame += 1 ds9.mtv(residualImage, title="res", frame=frame) frame += 1 ds9.mtv(dlnzdr, title="dlnz/dr %s" % (ccd.getId().getSerial() if ccd else ""), frame=frame) frame += 1 return dlnzdx, dlnzdy, dlnzdr, residualImage
def setUp(self): width, height = 100, 300 self.mi = afwImage.MaskedImageF(afwGeom.ExtentI(width, height)) self.mi.set(0) self.mi.getVariance().set(10) self.mi.getMask().addMaskPlane("DETECTED") self.FWHM = 5 self.ksize = 25 # size of desired kernel self.exposure = afwImage.makeExposure(self.mi) psf = roundTripPsf( 2, algorithms.DoubleGaussianPsf(self.ksize, self.ksize, self.FWHM / (2 * sqrt(2 * log(2))), 1, 0.1)) self.exposure.setPsf(psf) for x, y in [ (20, 20), #(30, 35), (50, 50), (60, 20), (60, 210), (20, 210) ]: flux = 10000 - 0 * x - 10 * y sigma = 3 + 0.01 * (y - self.mi.getHeight() / 2) psf = roundTripPsf( 3, algorithms.DoubleGaussianPsf(self.ksize, self.ksize, sigma, 1, 0.1)) im = psf.computeImage().convertF() im *= flux smi = self.mi.getImage().Factory( self.mi.getImage(), afwGeom.BoxI( afwGeom.PointI(x - self.ksize / 2, y - self.ksize / 2), afwGeom.ExtentI(self.ksize)), afwImage.LOCAL) if False: # Test subtraction with non-centered psfs im = afwMath.offsetImage(im, 0.5, 0.5) smi += im del psf del im del smi psf = roundTripPsf( 4, algorithms.DoubleGaussianPsf(self.ksize, self.ksize, self.FWHM / (2 * sqrt(2 * log(2))), 1, 0.1)) self.cellSet = afwMath.SpatialCellSet( afwGeom.BoxI(afwGeom.PointI(0, 0), afwGeom.ExtentI(width, height)), 100) ds = afwDetection.FootprintSet(self.mi, afwDetection.Threshold(10), "DETECTED") # # Prepare to measure # msConfig = algorithms.SourceMeasurementConfig() msConfig.load("tests/config/MeasureSources.py") schema = afwTable.SourceTable.makeMinimalSchema() measureSources = msConfig.makeMeasureSources(schema) catalog = afwTable.SourceCatalog(schema) msConfig.slots.calibFlux = None msConfig.slots.setupTable(catalog.table) ds.makeSources(catalog) for i, source in enumerate(catalog): measureSources.applyWithPeak(source, self.exposure) self.cellSet.insertCandidate( algorithms.makePsfCandidate(source, self.exposure))
def showPsfCandidates(exposure, psfCellSet, psf=None, frame=None, normalize=True, showBadCandidates=True, fitBasisComponents=False, variance=None, chi=None): """Display the PSF candidates. If psf is provided include PSF model and residuals; if normalize is true normalize the PSFs (and residuals) If chi is True, generate a plot of residuals/sqrt(variance), i.e. chi If fitBasisComponents is true, also find the best linear combination of the PSF's components (if they exist) """ if chi is None: if variance is not None: # old name for chi chi = variance # # Show us the ccandidates # mos = displayUtils.Mosaic() # candidateCenters = [] candidateCentersBad = [] candidateIndex = 0 for cell in psfCellSet.getCellList(): for cand in cell.begin(False): # include bad candidates rchi2 = cand.getChi2() if rchi2 > 1e100: rchi2 = numpy.nan if not showBadCandidates and cand.isBad(): continue if psf: im_resid = displayUtils.Mosaic(gutter=0, background=-5, mode="x") try: im = cand.getMaskedImage() # copy of this object's image xc, yc = cand.getXCenter(), cand.getYCenter() margin = 0 if True else 5 w, h = im.getDimensions() bbox = afwGeom.BoxI(afwGeom.PointI(margin, margin), im.getDimensions()) if margin > 0: bim = im.Factory(w + 2*margin, h + 2*margin) stdev = numpy.sqrt(afwMath.makeStatistics(im.getVariance(), afwMath.MEAN).getValue()) afwMath.randomGaussianImage(bim.getImage(), afwMath.Random()) bim.getVariance().set(stdev**2) bim.assign(im, bbox) im = bim xc += margin yc += margin im = im.Factory(im, True) im.setXY0(cand.getMaskedImage().getXY0()) except: continue if not variance: im_resid.append(im.Factory(im, True)) if True: # tweak up centroids mi = im psfIm = mi.getImage() config = measBase.SingleFrameMeasurementTask.ConfigClass() config.slots.centroid = "base_SdssCentroid" schema = afwTable.SourceTable.makeMinimalSchema() measureSources = measBase.SingleFrameMeasurementTask(schema, config=config) catalog = afwTable.SourceCatalog(schema) extra = 10 # enough margin to run the sdss centroider miBig = mi.Factory(im.getWidth() + 2*extra, im.getHeight() + 2*extra) miBig[extra:-extra, extra:-extra] = mi miBig.setXY0(mi.getX0() - extra, mi.getY0() - extra) mi = miBig del miBig exp = afwImage.makeExposure(mi) exp.setPsf(psf) footprintSet = afwDet.FootprintSet(mi, afwDet.Threshold(0.5*numpy.max(psfIm.getArray())), "DETECTED") footprintSet.makeSources(catalog) if len(catalog) == 0: raise RuntimeError("Failed to detect any objects") measureSources.run(catalog, exp) if len(catalog) == 1: source = catalog[0] else: # more than one source; find the once closest to (xc, yc) dmin = None # an invalid value to catch logic errors for i, s in enumerate(catalog): d = numpy.hypot(xc - s.getX(), yc - s.getY()) if i == 0 or d < dmin: source, dmin = s, d xc, yc = source.getCentroid() # residuals using spatial model try: subtractPsf(psf, im, xc, yc) except: continue resid = im if variance: resid = resid.getImage() var = im.getVariance() var = var.Factory(var, True) numpy.sqrt(var.getArray(), var.getArray()) # inplace sqrt resid /= var im_resid.append(resid) # Fit the PSF components directly to the data (i.e. ignoring the spatial model) if fitBasisComponents: im = cand.getMaskedImage() im = im.Factory(im, True) im.setXY0(cand.getMaskedImage().getXY0()) try: noSpatialKernel = psf.getKernel() except: noSpatialKernel = None if noSpatialKernel: candCenter = afwGeom.PointD(cand.getXCenter(), cand.getYCenter()) fit = fitKernelParamsToImage(noSpatialKernel, im, candCenter) params = fit[0] kernels = afwMath.KernelList(fit[1]) outputKernel = afwMath.LinearCombinationKernel(kernels, params) outImage = afwImage.ImageD(outputKernel.getDimensions()) outputKernel.computeImage(outImage, False) im -= outImage.convertF() resid = im if margin > 0: bim = im.Factory(w + 2*margin, h + 2*margin) afwMath.randomGaussianImage(bim.getImage(), afwMath.Random()) bim *= stdev bim.assign(resid, bbox) resid = bim if variance: resid = resid.getImage() resid /= var im_resid.append(resid) im = im_resid.makeMosaic() else: im = cand.getMaskedImage() if normalize: im /= afwMath.makeStatistics(im, afwMath.MAX).getValue() objId = splitId(cand.getSource().getId(), True)["objId"] if psf: lab = "%d chi^2 %.1f" % (objId, rchi2) ctype = ds9.RED if cand.isBad() else ds9.GREEN else: lab = "%d flux %8.3g" % (objId, cand.getSource().getPsfFlux()) ctype = ds9.GREEN mos.append(im, lab, ctype) if False and numpy.isnan(rchi2): ds9.mtv(cand.getMaskedImage().getImage(), title="candidate", frame=1) print("amp", cand.getAmplitude()) im = cand.getMaskedImage() center = (candidateIndex, xc - im.getX0(), yc - im.getY0()) candidateIndex += 1 if cand.isBad(): candidateCentersBad.append(center) else: candidateCenters.append(center) if variance: title = "chi(Psf fit)" else: title = "Stars & residuals" mosaicImage = mos.makeMosaic(frame=frame, title=title) with ds9.Buffering(): for centers, color in ((candidateCenters, ds9.GREEN), (candidateCentersBad, ds9.RED)): for cen in centers: bbox = mos.getBBox(cen[0]) ds9.dot("+", cen[1] + bbox.getMinX(), cen[2] + bbox.getMinY(), frame=frame, ctype=color) return mosaicImage
def testDetectorTime(self): """Test that we can ask a calib for the MidTime at a point in a detector (ticket #1337)""" p = afwGeom.PointI(3, 4) self.calib.getMidTime(self.detector, p)
def setUp(self): width, height = 100, 300 self.mi = afwImage.MaskedImageF(afwGeom.ExtentI(width, height)) self.mi.set(0) self.mi.getVariance().set(10) self.mi.getMask().addMaskPlane("DETECTED") self.FWHM = 5 self.ksize = 25 # size of desired kernel self.exposure = afwImage.makeExposure(self.mi) psf = roundTripPsf( 2, algorithms.DoubleGaussianPsf( self.ksize, self.ksize, self.FWHM / (2 * math.sqrt(2 * math.log(2))), 1, 0.1)) self.exposure.setPsf(psf) for x, y in [ (20, 20), #(30, 35), (50, 50), (60, 20), (60, 210), (20, 210) ]: flux = 10000 - 0 * x - 10 * y sigma = 3 + 0.01 * (y - self.mi.getHeight() / 2) psf = roundTripPsf( 3, algorithms.DoubleGaussianPsf(self.ksize, self.ksize, sigma, 1, 0.1)) im = psf.computeImage().convertF() im *= flux x0y0 = afwGeom.PointI(x - self.ksize // 2, y - self.ksize // 2) smi = self.mi.getImage().Factory( self.mi.getImage(), afwGeom.BoxI(x0y0, afwGeom.ExtentI(self.ksize)), afwImage.LOCAL) if False: # Test subtraction with non-centered psfs im = afwMath.offsetImage(im, 0.5, 0.5) smi += im del psf del im del smi roundTripPsf( 4, algorithms.DoubleGaussianPsf( self.ksize, self.ksize, self.FWHM / (2 * math.sqrt(2 * math.log(2))), 1, 0.1)) self.cellSet = afwMath.SpatialCellSet( afwGeom.BoxI(afwGeom.PointI(0, 0), afwGeom.ExtentI(width, height)), 100) ds = afwDetection.FootprintSet(self.mi, afwDetection.Threshold(10), "DETECTED") # # Prepare to measure # schema = afwTable.SourceTable.makeMinimalSchema() sfm_config = measBase.SingleFrameMeasurementConfig() sfm_config.plugins = [ "base_SdssCentroid", "base_CircularApertureFlux", "base_PsfFlux", "base_SdssShape", "base_GaussianFlux", "base_PixelFlags" ] sfm_config.slots.centroid = "base_SdssCentroid" sfm_config.slots.shape = "base_SdssShape" sfm_config.slots.psfFlux = "base_PsfFlux" sfm_config.slots.instFlux = None sfm_config.slots.apFlux = "base_CircularApertureFlux_3_0" sfm_config.slots.modelFlux = "base_GaussianFlux" sfm_config.slots.calibFlux = None sfm_config.plugins["base_SdssShape"].maxShift = 10.0 sfm_config.plugins["base_CircularApertureFlux"].radii = [3.0] task = measBase.SingleFrameMeasurementTask(schema, config=sfm_config) measCat = afwTable.SourceCatalog(schema) # detect the sources and run with the measurement task ds.makeSources(measCat) task.run(measCat, self.exposure) for source in measCat: self.cellSet.insertCandidate( algorithms.makePsfCandidate(source, self.exposure))
def _fillVisitCatalog(self, butler, visitCat, srcVisits, srcCcds): """ Fill the visit catalog with visit metadata Parameters ---------- butler: `lsst.daf.persistence.Butler` visitCat: `afw.table.BaseCatalog` Catalog with schema from _createFgcmVisitSchema() srcVisits: `list' List of source visits srcCcds: `list` List of source CCDs """ bbox = afwGeom.BoxI(afwGeom.PointI(0, 0), afwGeom.PointI(1, 1)) # now loop over visits and get the information for i, srcVisit in enumerate(srcVisits): # We don't use the bypasses since we need the psf info which does # not have a bypass # TODO: When DM-15500 is implemented in the Gen3 Butler, this # can be fixed dataId = { self.config.visitDataRefName: srcVisit, self.config.ccdDataRefName: srcCcds[i] } exp = butler.get('calexp_sub', dataId=dataId, bbox=bbox, flags=afwTable.SOURCE_IO_NO_FOOTPRINTS) visitInfo = exp.getInfo().getVisitInfo() f = exp.getFilter() psf = exp.getPsf() rec = visitCat.addNew() rec['visit'] = srcVisit rec['filtername'] = f.getName() radec = visitInfo.getBoresightRaDec() rec['telra'] = radec.getRa().asDegrees() rec['teldec'] = radec.getDec().asDegrees() rec['telha'] = visitInfo.getBoresightHourAngle().asDegrees() rec['mjd'] = visitInfo.getDate().get(system=DateTime.MJD) rec['exptime'] = visitInfo.getExposureTime() # convert from Pa to millibar # Note that I don't know if this unit will need to be per-camera config rec['pmb'] = visitInfo.getWeather().getAirPressure() / 100 # Flag to signify if this is a "deep" field. Not currently used rec['deepFlag'] = 0 # Relative flat scaling (1.0 means no relative scaling) rec['scaling'][:] = 1.0 # Median delta aperture, to be measured from stars rec['deltaAper'] = 0.0 rec['psfSigma'] = psf.computeShape().getDeterminantRadius() if butler.datasetExists('calexpBackground', dataId=dataId): # Get background for reference CCD # This approximation is good enough for now bgStats = ( bg[0].getStatsImage().getImage().array for bg in butler.get('calexpBackground', dataId=dataId)) rec['skyBackground'] = sum( np.median(bg[np.isfinite(bg)]) for bg in bgStats) else: rec['skyBackground'] = -1.0
def setUp(self): config = SingleFrameMeasurementTask.ConfigClass() config.slots.apFlux = 'base_CircularApertureFlux_12_0' self.schema = afwTable.SourceTable.makeMinimalSchema() self.measureSources = SingleFrameMeasurementTask(self.schema, config=config) width, height = 110, 301 self.mi = afwImage.MaskedImageF(afwGeom.ExtentI(width, height)) self.mi.set(0) sd = 3 # standard deviation of image self.mi.getVariance().set(sd * sd) self.mi.getMask().addMaskPlane("DETECTED") self.ksize = 31 # size of desired kernel sigma1 = 1.75 sigma2 = 2 * sigma1 self.exposure = afwImage.makeExposure(self.mi) self.exposure.setPsf( measAlg.DoubleGaussianPsf(self.ksize, self.ksize, 1.5 * sigma1, 1, 0.1)) cdMatrix = np.array([1.0, 0.0, 0.0, 1.0]) cdMatrix.shape = (2, 2) wcs = afwGeom.makeSkyWcs(crpix=afwGeom.PointD(0, 0), crval=afwGeom.SpherePoint( 0.0, 0.0, afwGeom.degrees), cdMatrix=cdMatrix) self.exposure.setWcs(wcs) # # Make a kernel with the exactly correct basis functions. Useful for debugging # basisKernelList = [] for sigma in (sigma1, sigma2): basisKernel = afwMath.AnalyticKernel( self.ksize, self.ksize, afwMath.GaussianFunction2D(sigma, sigma)) basisImage = afwImage.ImageD(basisKernel.getDimensions()) basisKernel.computeImage(basisImage, True) basisImage /= np.sum(basisImage.getArray()) if sigma == sigma1: basisImage0 = basisImage else: basisImage -= basisImage0 basisKernelList.append(afwMath.FixedKernel(basisImage)) order = 1 # 1 => up to linear spFunc = afwMath.PolynomialFunction2D(order) exactKernel = afwMath.LinearCombinationKernel(basisKernelList, spFunc) exactKernel.setSpatialParameters([[1.0, 0, 0], [0.0, 0.5 * 1e-2, 0.2e-2]]) rand = afwMath.Random() # make these tests repeatable by setting seed addNoise = True if addNoise: im = self.mi.getImage() afwMath.randomGaussianImage(im, rand) # N(0, 1) im *= sd # N(0, sd^2) del im xarr, yarr = [], [] for x, y in [ (20, 20), (60, 20), (30, 35), (50, 50), (20, 90), (70, 160), (25, 265), (75, 275), (85, 30), (50, 120), (70, 80), (60, 210), (20, 210), ]: xarr.append(x) yarr.append(y) for x, y in zip(xarr, yarr): dx = rand.uniform() - 0.5 # random (centered) offsets dy = rand.uniform() - 0.5 k = exactKernel.getSpatialFunction(1)( x, y) # functional variation of Kernel ... b = (k * sigma1**2 / ((1 - k) * sigma2**2) ) # ... converted double Gaussian's "b" #flux = 80000 - 20*x - 10*(y/float(height))**2 flux = 80000 * (1 + 0.1 * (rand.uniform() - 0.5)) I0 = flux * (1 + b) / (2 * np.pi * (sigma1**2 + b * sigma2**2)) for iy in range(y - self.ksize // 2, y + self.ksize // 2 + 1): if iy < 0 or iy >= self.mi.getHeight(): continue for ix in range(x - self.ksize // 2, x + self.ksize // 2 + 1): if ix < 0 or ix >= self.mi.getWidth(): continue I = I0 * psfVal(ix, iy, x + dx, y + dy, sigma1, sigma2, b) Isample = rand.poisson(I) if addNoise else I self.mi.getImage().set( ix, iy, self.mi.getImage().get(ix, iy) + Isample) self.mi.getVariance().set( ix, iy, self.mi.getVariance().get(ix, iy) + I) bbox = afwGeom.BoxI(afwGeom.PointI(0, 0), afwGeom.ExtentI(width, height)) self.cellSet = afwMath.SpatialCellSet(bbox, 100) self.footprintSet = afwDetection.FootprintSet( self.mi, afwDetection.Threshold(100), "DETECTED") self.catalog = self.measure(self.footprintSet, self.exposure) for source in self.catalog: try: cand = measAlg.makePsfCandidate(source, self.exposure) self.cellSet.insertCandidate(cand) except Exception as e: print(e) continue
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)