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 = geom.BoxI(geom.PointI(0, 0), geom.ExtentI(xtot, height)) biasSec = geom.BoxI(geom.PointI(nExtended if i == 0 else width, 0), geom.ExtentI(nOverclock, height)) dataSec = geom.BoxI( geom.PointI(nExtended + nOverclock if i == 0 else 0, 0), geom.ExtentI(width, height)) emptyBox = geom.BoxI() bbox = geom.BoxI(geom.PointI(0, 0), geom.ExtentI(width, height)) bbox.shift(geom.Extent2I(width * i, 0)) shiftp = geom.Extent2I(xtot * i, 0) allPixels.shift(shiftp) biasSec.shift(shiftp) dataSec.shift(shiftp) record.setBBox(bbox) record.setRawXYOffset(geom.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 testValid(self): test_data = { "[1:1084,1:1024]": geom.BoxI(geom.PointI(0, 0), geom.PointI(1083, 1023)), "[0:0,0:0]": geom.BoxI(geom.PointI(-1, -1), geom.PointI(-1, -1)) } for val, result in test_data.items(): self.assertEqual(obsBase.bboxFromIraf(val), result)
def bboxFromIraf(irafBBoxStr): """Return a Box2I corresponding to an IRAF-style BBOX [x0:x1,y0:y1] where x0 and x1 are the one-indexed start and end columns, and correspondingly y0 and y1 are the start and end rows. """ mat = re.search(r"^\[([-\d]+):([-\d]+),([-\d]+):([-\d]+)\]$", irafBBoxStr) if not mat: raise RuntimeError("Unable to parse IRAF-style bbox \"%s\"" % irafBBoxStr) x0, x1, y0, y1 = [int(_) for _ in mat.groups()] return geom.BoxI(geom.PointI(x0 - 1, y0 - 1), geom.PointI(x1 - 1, y1 - 1))
def testParentTrailingSlash2527(self): """Just like testParentNormal, but put a trailing slash on the root paths. Test that an object can be found at root location and put into an output location. Then test that when the output locaiton is used as an input location, and with a new output location, that the object is found in the first output location.""" # todo these shouldn't be commented out, I think the test wants the # trailing slash. testOutput = self.mkdtemp("testOutput") + '/' butler = dafPersist.Butler(inputs={ 'root': ROOT + '/', 'mapper': MinMapper1 }, outputs=testOutput) mapper1 = butler._repos.inputs()[0].repo._mapper loc = mapper1.map("x", dict(sensor="1,1"), write=True) self.assertEqual(loc.getPythonType(), "lsst.afw.geom.BoxI") self.assertEqual(loc.getCppType(), "BoxI") self.assertEqual(loc.getStorageName(), "PickleStorage") self.assertEqual(loc.getLocations(), ["foo-1,1.pickle"]) self.assertEqual(loc.getStorage().root, ROOT) self.assertEqual(loc.getAdditionalData().toString(), "sensor = \"1,1\"\n") box = geom.BoxI(geom.PointI(0, 1), geom.PointI(2, 3)) butler.put(box, "x", sensor="1,1") self.assertTrue( os.path.exists(os.path.join(testOutput, loc.getLocations()[0]))) del butler del mapper1 testOutput2 = self.mkdtemp("testOutput2") + '/' butler = dafPersist.Butler(inputs={ 'root': testOutput, 'mapper': MinMapper1 }, outputs=testOutput2) mapper2 = butler._repos.inputs()[0].repo._mapper loc = mapper2.map("x", dict(sensor="1,1")) self.assertEqual(loc.getPythonType(), "lsst.afw.geom.BoxI") self.assertEqual(loc.getCppType(), "BoxI") self.assertEqual(loc.getStorageName(), "PickleStorage") self.assertEqual(loc.getLocations(), ["foo-1,1.pickle"]) self.assertEqual(os.path.normpath(loc.getStorage().root), os.path.normpath(testOutput)) self.assertEqual(loc.getAdditionalData().toString(), "sensor = \"1,1\"\n")
def subtractStars(self, exposure, catalog, chi_lim=-1): """Subtract the exposure's PSF from all the sources in catalog""" mi, psf = exposure.getMaskedImage(), exposure.getPsf() subtracted = mi.Factory(mi, True) for s in catalog: xc, yc = s.getX(), s.getY() bbox = subtracted.getBBox(afwImage.PARENT) if bbox.contains(geom.PointI(int(xc), int(yc))): try: measAlg.subtractPsf(psf, subtracted, xc, yc) except Exception: pass chi = subtracted.Factory(subtracted, True) var = subtracted.getVariance() np.sqrt(var.getArray(), var.getArray()) # inplace sqrt chi /= var if display: afwDisplay.Display(frame=1).mtv(subtracted, title="Subtracted") afwDisplay.Display(frame=2).mtv(chi, title="Chi") xc, yc = exposure.getWidth() // 2, exposure.getHeight() // 2 afwDisplay.Display(frame=3).mtv(psf.computeImage( geom.Point2D(xc, yc)), title="Psf %.1f,%.1f" % (xc, yc)) chi_min, chi_max = np.min(chi.getImage().getArray()), np.max( chi.getImage().getArray()) if False: print(chi_min, chi_max) if chi_lim > 0: self.assertGreater(chi_min, -chi_lim) self.assertLess(chi_max, chi_lim)
def display_cutout(exposure, x, y, title=None, frame=None, cutout_size=100): cutout_exp = make_cutout(exposure, x, y, cutout_size=cutout_size) xy = geom.PointI(x, y) afwDisplay.setDefaultBackend('matplotlib') display = afwDisplay.Display(frame=frame) display.mtv(cutout_exp) plt.title(title)
def testSwap(self): x00, y00, x01, y01 = (0, 1, 2, 3) x10, y10, x11, y11 = (4, 5, 6, 7) box0 = geom.Box2I(geom.PointI(x00, y00), geom.PointI(x01, y01)) box1 = geom.Box2I(geom.PointI(x10, y10), geom.PointI(x11, y11)) box0.swap(box1) self.assertEqual(box0.getMinX(), x10) self.assertEqual(box0.getMinY(), y10) self.assertEqual(box0.getMaxX(), x11) self.assertEqual(box0.getMaxY(), y11) self.assertEqual(box1.getMinX(), x00) self.assertEqual(box1.getMinY(), y00) self.assertEqual(box1.getMaxX(), x01) self.assertEqual(box1.getMaxY(), y01)
def run(self, durationInSeconds=-1): """Run the monitor, displaying new images as they are taken. Parameters ---------- durationInSeconds : `int`, optional How long to run for. Use -1 for infinite. """ if durationInSeconds == -1: nLoops = int(1e9) else: nLoops = int(durationInSeconds // self.cadence) lastDisplayed = -1 for i in range(nLoops): try: dataId, expId = self._getLatestImageDataIdAndExpId() if lastDisplayed == expId: sleep(self.cadence) continue if self.runIsr: exp = self.bestEffort.getExposure(dataId) else: exp = self.butler.get('raw', dataId=dataId) # TODO: add logic to deal with amp overlay and chip center # being mutually exclusive if self.measureFromChipCenter: # after writing only! exp.setXY0(geom.PointI(-2036, -2000)) print(f"Displaying {dataId}...") imageInfoText = self._makeImageInfoText(dataId, exp, asList=True) # too long of a title breaks Java FITS i/o fireflyTitle = " ".join([s for s in imageInfoText])[:67] try: self.display.scale('asinh', 'zscale') self.display.mtv(exp, title=fireflyTitle) except Exception as e: # includes JSONDecodeError, HTTPError, anything else print(f'Caught error {e}, skipping this image' ) # TODO: try again maybe? if self.overlayAmps: cgUtils.overlayCcdBoxes(exp.getDetector(), display=self.display, isTrimmed=True) self._printImageInfo(imageInfoText) lastDisplayed = expId except NotFoundError as e: # NotFoundError when filters aren't defined print(f'Skipped displaying {dataId} due to {e}') return
def addDefects(exp, nBadCols=10): img = exp.getMaskedImage().getImage() (xsize, ysize) = img.getDimensions() defectList = measAlg.Defects() # set some bad cols and add them to a defect list for xi in numpy.random.randint(0, xsize, nBadCols): yi = numpy.random.randint(0, ysize) xi, yi = int(xi), int(yi) bbox = geom.Box2I(geom.PointI(xi, 0), geom.ExtentI(1, yi+1)) subIm = afwImage.ImageF(img, bbox) subIm.set(1e7) defectList.append(bbox) # set a 15 pixel box of defects at the upper left corner to demonstrate fallbackValue bbox = geom.Box2I(geom.PointI(0, ysize-15), geom.ExtentI(15, 15)) subIm = afwImage.ImageF(img, bbox) subIm.set(1e7) defectList.append(bbox) return defectList
def testParentNormal(self): """Test that an object can be found at root location and put into an output location. Then test that when the output locaiton is used as an input location, and with a new output location, that the object is found in the first output location. """ testOutput = self.mkdtemp("testOutput") butler = dafPersist.Butler(inputs={ 'root': ROOT, 'mapper': MinMapper1 }, outputs=testOutput) mapper1 = butler._repos.inputs()[0].repo._mapper loc = mapper1.map("x", dict(sensor="1,1"), write=True) self.assertEqual(loc.getPythonType(), "lsst.afw.geom.BoxI") self.assertEqual(loc.getCppType(), "BoxI") self.assertEqual(loc.getStorageName(), "PickleStorage") self.assertEqual(loc.getLocations(), ["foo-1,1.pickle"]) self.assertEqual(loc.getAdditionalData().toString(), "sensor = \"1,1\"\n") box = geom.BoxI(geom.PointI(0, 1), geom.PointI(2, 3)) butler.put(box, "x", sensor="1,1") self.assertTrue( os.path.exists(os.path.join(testOutput, loc.getLocations()[0]))) del butler testOutput2 = self.mkdtemp("testOutput2") butler = dafPersist.Butler(inputs={ 'root': testOutput, 'mapper': MinMapper1 }, outputs=testOutput2) mapper2 = butler._repos.inputs()[0].repo._mapper loc = mapper2.map("x", dict(sensor="1,1")) self.assertEqual(loc.getPythonType(), "lsst.afw.geom.BoxI") self.assertEqual(loc.getCppType(), "BoxI") self.assertEqual(loc.getStorageName(), "PickleStorage") self.assertEqual(loc.getLocations(), ["foo-1,1.pickle"]) self.assertEqual(loc.getStorage().root, testOutput) self.assertEqual(loc.getAdditionalData().toString(), "sensor = \"1,1\"\n")
def show_injection(exposure, x, y, x_ref=False, y_ref=False, title=None, frame=None, cutout_size=100): # x, y: center of the postage stamps, shown in red # x_ref, y_ref: a reference circle shown in green, it can be used to compared xy = geom.PointI(x, y) afwDisplay.setDefaultBackend('matplotlib') display = afwDisplay.Display(frame=frame) display.mtv(exposure) display.dot('o', xy.getX(), xy.getY(), ctype='red') if x_ref or y_ref: xy_ref = geom.PointI(x_ref, y_ref) display.dot('o', xy_ref.getX(), xy_ref.getY(), ctype='green') plt.title(title)
def findBrightestStarRHL(exp, minArea=100, maxRadialOffset=1500): import lsst.afw.detection as afwDetect import lsst.afw.image as afwImage import lsst.afw.math as afwMath cexp = exp.clone() sigma = 4 ksize = 1 + 4*int(sigma + 1) gauss1d = afwMath.GaussianFunction1D(sigma) kernel = afwMath.SeparableKernel(ksize, ksize, gauss1d, gauss1d) convolvedImage = cexp.maskedImage.Factory(cexp.maskedImage.getBBox()) afwMath.convolve(convolvedImage, cexp.maskedImage, kernel) bbox = geom.BoxI(geom.PointI(ksize//2, ksize//2), geom.PointI(cexp.getWidth() - ksize//2 - 1, cexp.getHeight() - ksize//2 - 1)) tmp = cexp.maskedImage[bbox] cexp.image *= 0 tmp[bbox, afwImage.PARENT] = convolvedImage[bbox] del convolvedImage; del tmp if False: defectBBoxes = [ geom.BoxI(geom.PointI(548, 3562), geom.PointI(642, 3999)), geom.BoxI(geom.PointI(474, 3959), geom.PointI(1056, 3999)), geom.BoxI(geom.PointI(500, 0), geom.PointI(680, 40)), ] for bbox in defectBBoxes: cexp.maskedImage[bbox] = 0 threshold = 1000 feet = afwDetect.FootprintSet(cexp.maskedImage, afwDetect.Threshold(threshold), "DETECTED").getFootprints() maxVal = None centroid = None for foot in feet: peak = foot.peaks[0] if minArea > 0 and foot.getArea() < minArea: continue x, y = peak.getCentroid() if np.hypot(x - 2036, y - 2000) > maxRadialOffset: continue if maxVal is None or peak.getPeakValue() > maxVal: maxVal = peak.getPeakValue() centroid = peak.getCentroid() return centroid
def radec2xy(ra, dec, wcs): """This function converts radec to xy. ra: ra in degree dec: dec in degree wcs: wcs of an exposure Returns: xy coordinates """ radec = geom.SpherePoint(ra * geom.degrees, dec * geom.degrees) xy = geom.PointI(wcs.skyToPixel(radec)) return xy.getX(), xy.getY()
def _gridImage(self, maskedImage, binsize, statsFlag): """Private method to grid an image for debugging""" width, height = maskedImage.getDimensions() x0, y0 = maskedImage.getXY0() xedges = numpy.arange(0, width, binsize) yedges = numpy.arange(0, height, binsize) xedges = numpy.hstack((xedges, width)) # add final edge yedges = numpy.hstack((yedges, height)) # add final edge # Use lists/append to protect against the case where # a bin has no valid pixels and should not be included in the fit bgX = [] bgY = [] bgZ = [] bgdZ = [] for ymin, ymax in zip(yedges[0:-1], yedges[1:]): for xmin, xmax in zip(xedges[0:-1], xedges[1:]): subBBox = geom.Box2I( geom.PointI(int(x0 + xmin), int(y0 + ymin)), geom.PointI(int(x0 + xmax - 1), int(y0 + ymax - 1))) subIm = afwImage.MaskedImageF(maskedImage, subBBox, afwImage.PARENT, False) stats = afwMath.makeStatistics( subIm, afwMath.MEAN | afwMath.MEANCLIP | afwMath.MEDIAN | afwMath.NPOINT | afwMath.STDEV, self.sctrl) npoints, _ = stats.getResult(afwMath.NPOINT) if npoints >= 2: stdev, _ = stats.getResult(afwMath.STDEV) if stdev < self.config.gridStdevEpsilon: stdev = self.config.gridStdevEpsilon bgX.append(0.5 * (x0 + xmin + x0 + xmax)) bgY.append(0.5 * (y0 + ymin + y0 + ymax)) bgdZ.append(stdev / numpy.sqrt(npoints)) est, _ = stats.getResult(statsFlag) bgZ.append(est) return numpy.array(bgX), numpy.array(bgY), numpy.array( bgZ), numpy.array(bgdZ)
def ccdPixelToAmpPixel(xy, detector): r"""Given an position within a detector return position within an amplifier Parameters ---------- xy : `lsst.geom.PointD` pixel position within detector detector : `lsst.afw.cameraGeom.Detector` The requested detector N.b. all pixel coordinates have the centre of the bottom-left pixel at (0.0, 0.0) Returns ------- amp : `lsst.afw.table.AmpInfoRecord` The amplifier that the pixel lies in ampXY : `lsst.geom.PointI` The pixel coordinate relative to the corner of the single-amp image Raises ------ RuntimeError If the requested pixel doesn't lie on the detector """ found = False for amp in detector: if geom.BoxD(amp.getBBox()).contains(xy): found = True xy = geom.PointI(xy) # pixel coordinates as ints break if not found: raise RuntimeError("Point (%g, %g) does not lie on detector %s" % (xy[0], xy[1], detector.getName())) x, y = xy - amp.getBBox().getBegin() # offset from origin of amp's data segment # Allow for flips (due e.g. to physical location of the amplifiers) w, h = amp.getRawDataBBox().getDimensions() if amp.getRawFlipX(): x = w - x - 1 if amp.getRawFlipY(): y = h - y - 1 dxy = amp.getRawBBox().getBegin() - amp.getRawDataBBox().getBegin() # correction for overscan etc. xy = geom.ExtentI(x, y) - dxy return amp, xy
def display_exposure(exposure, x, y, cutout_size=60, coord_list=None, scale=None, frame=None, show_colorbar=False, title=None, save_name=None): """This function displays the postage stamp of an exposure. The center of the postage stamp is marked by a red circle. We can also overlay blue circles corresponding to the coordinaes given in the coord_list on the postage stamp. exposure: lsst.afw.image.exposure.exposure.ExposureF x: x pixel coordinate y: y pixel coordinate cutout_size: Width(in pixel unit) of the postage stamp , default value is 60 coord_list: A list of coordinates where we can overlay blue circles on the postage stamp scale: [min_val, max_val], set the min value and the max value for display, default is None frame: The frame of the afwDisplay.Display object show_colorbar: Show colorbar of the postage stamp, default is False title: Title of the postage stamp save_name: If provided, the postage stamp will be saved as 'save_name.png' in the current working directory, default if None """ cutout_extent = geom.ExtentI(cutout_size, cutout_size) radec = geom.SpherePoint(exposure.getWcs().pixelToSky(x, y)) cutout_image = exposure.getCutout(radec, cutout_extent) xy = geom.PointI(x, y) display = afwDisplay.Display(frame=frame, backend='matplotlib') if scale: display.scale("linear", scale[0], scale[1]) else: display.scale("linear", "zscale") display.mtv(cutout_image) if show_colorbar: display.show_colorbar() display.dot('o', xy.getX(), xy.getY(), ctype='red') if coord_list: for coord in coord_list: coord_x, coord_y = coord display.dot('o', coord_x, coord_y, ctype='blue') plt.title(title) if save_name: plt.savefig(save_name, dpi=500)
def format_coord(x, y, wcs=self._wcs, x0=self._xy0[0], y0=self._xy0[1], origin=afwImage.PARENT, bbox=self._image.getBBox(afwImage.PARENT), _useSexagesimal=self._useSexagesimal): fmt = '(%1.2f, %1.2f)' if self._mtvOrigin == afwImage.PARENT: msg = fmt % (x, y) else: msg = (fmt + "L") % (x - x0, y - y0) col = int(x + 0.5) row = int(y + 0.5) if bbox.contains(geom.PointI(col, row)): if wcs is not None: raDec = wcs.pixelToSky(x, y) ra = raDec[0].asDegrees() dec = raDec[1].asDegrees() if _useSexagesimal[0]: from astropy import units as u from astropy.coordinates import Angle as apAngle kwargs = dict(sep=':', pad=True, precision=2) ra = apAngle(ra*u.deg).to_string(unit=u.hour, **kwargs) dec = apAngle(dec*u.deg).to_string(unit=u.deg, **kwargs) else: ra = "%9.4f" % ra dec = "%9.4f" % dec msg += r" (%s, %s): (%s, %s)" % (self.__alpha, self.__delta, ra, dec) msg += ' %1.3f' % (self._image[col, row]) if self._mask: val = self._mask[col, row] if self._interpretMaskBits: msg += " [%s]" % self._mask.interpret(val) else: msg += " 0x%x" % val return msg
def subtractStars(self, exposure, catalog, chi_lim=-1): """Subtract the exposure's PSF from all the sources in catalog""" mi, psf = exposure.getMaskedImage(), exposure.getPsf() subtracted = mi.Factory(mi, True) for s in catalog: xc, yc = s.getX(), s.getY() bbox = subtracted.getBBox(afwImage.PARENT) if bbox.contains(geom.PointI(int(xc), int(yc))): measAlg.subtractPsf(psf, subtracted, xc, yc) chi = subtracted.Factory(subtracted, True) var = subtracted.getVariance() np.sqrt(var.getArray(), var.getArray()) # inplace sqrt chi /= var chi_min = np.min(chi.getImage().getArray()) chi_max = np.max(chi.getImage().getArray()) print(chi_min, chi_max) if chi_lim > 0: self.assertGreater(chi_min, -chi_lim) self.assertLess(chi_max, chi_lim)
def _serial_box(self, start, end): llc = lsstGeom.PointI(start, self.imaging.getMinY()) urc = lsstGeom.PointI(end, self.imaging.getMaxY()) return lsstGeom.BoxI(llc, urc)
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 = geom.BoxI(geom.PointI(i*(width//nAmp), 0), geom.ExtentI(width//nAmp, height)) ampI = mi.Factory(mi, bbox) for j in range(nAmp): if i == j: continue bbox = geom.BoxI(geom.PointI(j*(width//nAmp), 0), geom.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 makeBBoxFromList(ylist): """Given a list [(x0, y0), (xsize, ysize)], probably from a yaml file, return a BoxI """ (x0, y0), (xsize, ysize) = ylist return geom.BoxI(geom.PointI(x0, y0), geom.ExtentI(xsize, ysize))
def _fillVisitCatalog(self, visitCat, groupedDataRefs, visitCatDataRef=None): """ Fill the visit catalog with visit metadata Parameters ---------- visitCat: `afw.table.BaseCatalog` Catalog with schema from _makeFgcmVisitSchema() groupedDataRefs: `dict` Dictionary with visit keys, and `list`s of `lsst.daf.persistence.ButlerDataRef visitCatDataRef: `lsst.daf.persistence.ButlerDataRef`, optional Dataref to write visitCat for checkpoints """ bbox = geom.BoxI(geom.PointI(0, 0), geom.PointI(1, 1)) for i, visit in enumerate(sorted(groupedDataRefs)): # 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 # Do not read those that have already been read if visitCat['used'][i]: continue if (i % self.config.nVisitsPerCheckpoint) == 0: self.log.info("Retrieving metadata for %s %d (%d/%d)" % (self.config.visitDataRefName, visit, i, len(groupedDataRefs))) # Save checkpoint if desired if visitCatDataRef is not None: visitCatDataRef.put(visitCat) # Note that the reference ccd is first in the list (if available). # The first dataRef in the group will be the reference ccd (if available) dataRef = groupedDataRefs[visit][0] exp = dataRef.get(datasetType='calexp_sub', bbox=bbox, flags=afwTable.SOURCE_IO_NO_FOOTPRINTS) visitInfo = exp.getInfo().getVisitInfo() f = exp.getFilter() psf = exp.getPsf() rec = visitCat[i] rec['visit'] = visit rec['filtername'] = f.getName() # TODO DM-26991: when gen2 is removed, gen3 workflow will make it # much easier to get the wcs's necessary to recompute the pointing # ra/dec at the center of the camera. radec = visitInfo.getBoresightRaDec() rec['telra'] = radec.getRa().asDegrees() rec['teldec'] = radec.getDec().asDegrees() rec['telha'] = visitInfo.getBoresightHourAngle().asDegrees() rec['telrot'] = visitInfo.getBoresightRotAngle().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 dataRef.datasetExists(datasetType='calexpBackground'): # Get background for reference CCD # This approximation is good enough for now bgStats = (bg[0].getStatsImage().getImage().array for bg in dataRef.get( datasetType='calexpBackground')) rec['skyBackground'] = sum( np.median(bg[np.isfinite(bg)]) for bg in bgStats) else: self.log.warn( 'Sky background not found for visit %d / ccd %d' % (visit, dataRef.dataId[self.config.ccdDataRefName])) rec['skyBackground'] = -1.0 rec['used'] = 1
def bbox(self): """Return the detector bounding box from the separate box endpoint values. """ return geom.BoxI(geom.PointI(self.bbox_x0, self.bbox_y0), geom.PointI(self.bbox_x1, self.bbox_y1))
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(geom.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=geom.PointD(0, 0), crval=geom.SpherePoint( 0.0, 0.0, geom.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 II = I0 * psfVal(ix, iy, x + dx, y + dy, sigma1, sigma2, b) Isample = rand.poisson(II) if addNoise else II self.mi.image[ix, iy, afwImage.LOCAL] += Isample self.mi.variance[ix, iy, afwImage.LOCAL] += II bbox = geom.BoxI(geom.PointI(0, 0), geom.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 _parallel_box(self, start, end): llc = lsstGeom.PointI(self.imaging.getMinX(), start) urc = lsstGeom.PointI(self.imaging.getMaxX(), end) return lsstGeom.BoxI(llc, urc)
def makeAmplifierList(ccd): """Construct a list of AmplifierBuilder objects """ # Much of this will need to be filled in when we know it. assert len(ccd) > 0 amp = list(ccd['amplifiers'].values())[0] rawBBox = makeBBoxFromList(amp['rawBBox']) # total in file xRawExtent, yRawExtent = rawBBox.getDimensions() readCorners = {"LL": ReadoutCorner.LL, "LR": ReadoutCorner.LR, "UL": ReadoutCorner.UL, "UR": ReadoutCorner.UR} amplifierList = [] for name, amp in sorted(ccd['amplifiers'].items(), key=lambda x: x[1]['hdu']): amplifier = Amplifier.Builder() amplifier.setName(name) 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 = makeBBoxFromList(amp['rawDataBBox']) # Photosensitive area xDataExtent, yDataExtent = rawDataBBox.getDimensions() amplifier.setBBox(geom.BoxI( geom.PointI(ix*xDataExtent, iy*yDataExtent), rawDataBBox.getDimensions())) rawBBox = makeBBoxFromList(amp['rawBBox']) rawBBox.shift(geom.ExtentI(x0, y0)) amplifier.setRawBBox(rawBBox) rawDataBBox = makeBBoxFromList(amp['rawDataBBox']) rawDataBBox.shift(geom.ExtentI(x0, y0)) amplifier.setRawDataBBox(rawDataBBox) rawSerialOverscanBBox = makeBBoxFromList(amp['rawSerialOverscanBBox']) rawSerialOverscanBBox.shift(geom.ExtentI(x0, y0)) amplifier.setRawHorizontalOverscanBBox(rawSerialOverscanBBox) rawParallelOverscanBBox = makeBBoxFromList(amp['rawParallelOverscanBBox']) rawParallelOverscanBBox.shift(geom.ExtentI(x0, y0)) amplifier.setRawVerticalOverscanBBox(rawParallelOverscanBBox) rawSerialPrescanBBox = makeBBoxFromList(amp['rawSerialPrescanBBox']) rawSerialPrescanBBox.shift(geom.ExtentI(x0, y0)) amplifier.setRawPrescanBBox(rawSerialPrescanBBox) if perAmpData: amplifier.setRawXYOffset(geom.Extent2I(ix*xRawExtent, iy*yRawExtent)) else: amplifier.setRawXYOffset(geom.Extent2I(0, 0)) amplifier.setReadoutCorner(readCorners[amp['readCorner']]) amplifier.setGain(amp['gain']) amplifier.setReadNoise(amp['readNoise']) amplifier.setSaturation(amp['saturation']) amplifier.setSuspectLevel(amp.get('suspect', np.nan)) # flip data when assembling if needs be (e.g. data from the serial at the top of a CCD) flipX, flipY = amp.get("flipXY") amplifier.setRawFlipX(flipX) amplifier.setRawFlipY(flipY) # linearity placeholder stuff amplifier.setLinearityCoeffs([float(val) for val in amp['linearityCoeffs']]) amplifier.setLinearityType(amp['linearityType']) amplifier.setLinearityThreshold(float(amp['linearityThreshold'])) amplifier.setLinearityMaximum(float(amp['linearityMax'])) amplifier.setLinearityUnits("DN") amplifierList.append(amplifier) return amplifierList