def testSubImage(self): """Test getImage on a subregion of the full background image Using real image data is a cheap way to get a variable background """ mi = self.getCfhtImage() bctrl = afwMath.BackgroundControl(mi.getWidth() // 128, mi.getHeight() // 128) backobj = afwMath.makeBackground(mi.getImage(), bctrl) subBBox = afwGeom.Box2I(afwGeom.Point2I(1000, 3000), afwGeom.Extent2I(100, 100)) bgFullImage = backobj.getImageF() self.assertEqual(bgFullImage.getBBox(), mi.getBBox()) subFullArr = afwImage.ImageF(bgFullImage, subBBox).getArray() bgSubImage = backobj.getImageF(subBBox, bctrl.getInterpStyle()) subArr = bgSubImage.getArray() # the pixels happen to be identical but it is safer not to rely on # that; close is good enough self.assertFloatsEqual(subArr, subFullArr)
def makeTestImage(xsize=200, ysize=100, nCR=15): randArr = numpy.random.poisson(1000., xsize*ysize) randArr = numpy.array(randArr.reshape(ysize, xsize), dtype=numpy.float32) # force to ImageF factory = measAlg.GaussianPsfFactory() factory.addWing = False psf = factory.apply(4) # FWHM in pixels img = afwImage.makeImageFromArray(randArr) var = afwImage.ImageF(img, True) # copy constructor mask = afwImage.Mask(xsize, ysize) xind = numpy.random.randint(0, xsize, nCR) yind = numpy.random.randint(0, ysize, nCR) # set some CRs for xi, yi in zip(xind, yind): xi, yi = int(xi), int(yi) img.set(xi, yi, 1e6) mi = afwImage.makeMaskedImage(img, mask, var) exp = afwImage.makeExposure(mi) exp.setPsf(psf) return exp
def testObjectPlacement(self): """ Test that GalSim places objects on the correct pixel by drawing images containing single objects and no background, reading those images back in, and comparing the flux-averaged centroids of the images with the expected pixel positions of the input objects. """ scratchDir = tempfile.mkdtemp(dir=ROOT, prefix='testLSSTObjectPlacement-') if os.path.exists(scratchDir): shutil.rmtree(scratchDir) os.mkdir(scratchDir) detector = lsst_camera()['R:0,3 S:2,2'] det_name = 'R03_S22' magNorm = 19.0 pixel_transformer = DMtoCameraPixelTransformer() for band in 'ugrizy': obs = self.obs_dict[band] catName = os.path.join(scratchDir, 'placementCatalog.dat') imageRoot = os.path.join(scratchDir, 'placementImage') dbFileName = os.path.join(scratchDir, 'placementInputCatalog.dat') imageName = '%s_%s_%s.fits' % (imageRoot, det_name, obs.bandpass) ra_c, dec_c = raDecFromPixelCoordsLSST(2000.0, 2000.0, detector.getName(), band=obs.bandpass, obs_metadata=obs) nSamples = 30 rng = np.random.RandomState(42) fwhm = 0.12 for iteration in range(nSamples): if os.path.exists(dbFileName): os.unlink(dbFileName) ra_obj = ra_c + rng.random_sample()*0.2 - 0.1 dec_obj = dec_c + rng.random_sample()*0.2 - 0.1 dmx_wrong, dmy_wrong = pixelCoordsFromRaDec(ra_obj, dec_obj, chipName=detector.getName(), obs_metadata=obs, camera=lsst_camera()) dmx_pix, dmy_pix = pixelCoordsFromRaDecLSST(ra_obj, dec_obj, chipName=detector.getName(), obs_metadata=obs, band=obs.bandpass) x_pix, y_pix = pixel_transformer.cameraPixFromDMPix(dmx_pix, dmy_pix, detector.getName()) x_pix_wrong, y_pix_wrong = pixel_transformer.cameraPixFromDMPix(dmx_wrong, dmy_wrong, detector.getName()) d_ra = 360.0*(ra_obj - obs.pointingRA) # in arcseconds d_dec = 360.0*(dec_obj - obs.pointingDec) create_text_catalog(obs, dbFileName, np.array([d_ra]), np.array([d_dec]), mag_norm=[magNorm]) db = LSSTPlacementFileDBObj(dbFileName, runtable='test') cat = LSSTPlacementCatalog(db, obs_metadata=obs) cat.camera_wrapper = LSSTCameraWrapper() psf = SNRdocumentPSF(fwhm=fwhm) cat.setPSF(psf) cat.write_catalog(catName) cat.write_images(nameRoot=imageRoot) im = afwImage.ImageF(imageName).getArray() tot_flux = im.sum() self.assertGreater(tot_flux, 10.0) y_centroid = sum([ii*im[ii,:].sum() for ii in range(im.shape[0])])/tot_flux x_centroid = sum([ii*im[:,ii].sum() for ii in range(im.shape[1])])/tot_flux dd = np.sqrt((x_pix-x_centroid)**2 + (y_pix-y_centroid)**2) self.assertLess(dd, 0.5*fwhm) dd_wrong = np.sqrt((x_pix_wrong-x_centroid)**2 + (y_pix_wrong-y_centroid)**2) self.assertLess(dd, dd_wrong) if os.path.exists(dbFileName): os.unlink(dbFileName) if os.path.exists(catName): os.unlink(catName) if os.path.exists(imageName): os.unlink(imageName) if os.path.exists(scratchDir): shutil.rmtree(scratchDir)
>>> import ds9 >>> ds9.run() """ from __future__ import absolute_import, division, print_function import unittest import lsst.utils.tests import lsst.afw.image as afwImage try: import lsst.afw.display.ds9 as ds9 except Exception: ds9 = None if ds9: try: ds9.mtv(afwImage.ImageF(1, 1)) except Exception as e: print("Unable to use ds9: %s" % e) ds9 = None class DisplayTestCase(unittest.TestCase): """A test case for Display""" def setUp(self): pass def tearDown(self): pass @unittest.skipUnless(ds9, "You must setup display.ds9 to run this test") def testMtv(self):
def testRamp(self): """tests Laher's afwdata/Statistics/*.fits images (doubles)""" # make a ramping image (spline should be exact for linear increasing # image nx = 512 ny = 512 x0, y0 = 9876, 54321 box = lsst.geom.Box2I(lsst.geom.Point2I(x0, y0), lsst.geom.Extent2I(nx, ny)) rampimg = afwImage.ImageF(box) dzdx, dzdy, z0 = 0.1, 0.2, 10000.0 for x in range(nx): for y in range(ny): rampimg[x, y, afwImage.LOCAL] = dzdx * x + dzdy * y + z0 # check corner, edge, and center pixels bctrl = afwMath.BackgroundControl(10, 10) bctrl.setNxSample(6) bctrl.setNySample(6) # large enough to entirely avoid clipping bctrl.getStatisticsControl().setNumSigmaClip(20.0) bctrl.getStatisticsControl().setNumIter(1) backobj = afwMath.makeBackground(rampimg, bctrl) if debugMode: print(rampimg.getArray()) frame = 1 for interp in ("CONSTANT", "LINEAR", "NATURAL_SPLINE", "AKIMA_SPLINE"): diff = backobj.getImageF(interp) if debugMode: afwDisplay.Display(frame=frame).mtv( diff, title=self._testMethodName + " diff") frame += 1 diff -= rampimg if debugMode: print(interp, diff.getArray().mean(), diff.getArray().std()) if debugMode: afwDisplay.Display(frame=frame).mtv( diff, title=self._testMethodName + " diff-ramping") frame += 1 if debugMode: afwDisplay.Display(frame=frame).mtv(rampimg, title=self._testMethodName + " ramping") frame += 1 afwDisplay.Display(frame=frame).mtv(backobj.getStatsImage(), title=self._testMethodName + " bkgd StatsImage") frame += 1 xpixels = [0, nx // 2, nx - 1] ypixels = [0, ny // 2, ny - 1] for xpix in xpixels: for ypix in ypixels: testval = backobj.getImageF( afwMath.Interpolate.CUBIC_SPLINE)[xpix, ypix, afwImage.LOCAL] self.assertAlmostEqual( testval / rampimg[xpix, ypix, afwImage.LOCAL], 1, 6) # Test pickle new = pickle.loads(pickle.dumps(backobj)) self.assertBackgroundEqual(backobj, new) # Check creation of sub-image box = lsst.geom.Box2I(lsst.geom.Point2I(123, 45), lsst.geom.Extent2I(45, 123)) box.shift(lsst.geom.Extent2I(x0, y0)) bgImage = backobj.getImageF("AKIMA_SPLINE") bgSubImage = afwImage.ImageF(bgImage, box) testImage = backobj.getImageF(box, "AKIMA_SPLINE") self.assertEqual(testImage.getXY0(), bgSubImage.getXY0()) self.assertEqual(testImage.getDimensions(), bgSubImage.getDimensions()) self.assertImagesEqual(testImage, bgSubImage)
def subtractCrosstalkYagi(mi, coeffs1List, coeffs2List, gainsPreampSig, minPixelToMask=45000, crosstalkStr="CROSSTALK"): """Subtract the crosstalk from MaskedImage mi given a set of coefficients based on procedure presented in Yagi et al. 2012, PASP in publication; arXiv:1210.8212 The pixels affected by signal over minPixelToMask have the crosstalkStr bit set """ # # 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) # if True: tempStr = "TEMP" # mask plane used to record the bright pixels that we need to mask mi.getMask().addMaskPlane(tempStr) fs = afwDetect.FootprintSet(mi, afwDetect.Threshold(minPixelToMask), tempStr) mi.getMask().addMaskPlane(crosstalkStr) afwDisplay.setMaskPlaneColor(crosstalkStr, afwDisplay.MAGENTA) fs.setMask(mi.getMask(), crosstalkStr) # the crosstalkStr bit will now be set # whenever we subtract crosstalk crosstalk = mi.getMask().getPlaneBitMask(crosstalkStr) if True: # This python implementation is fairly fast image = mi.getImage() xtalk = afwImage.ImageF(image.getDimensions()) xtalk.set(0) for i in range(4): xAmp, xOff = getAmplifier(xtalk, i, i) for j in range(4): if i == j: continue gainRatio = gainsPreampSig[j] / gainsPreampSig[i] jAmp, jOff = getAmplifier(image, j, i) xAmp.scaledPlus(gainRatio * coeffs1List[i][j], jAmp) xOff.scaledPlus(gainRatio * coeffs2List[i][j], jOff) image -= xtalk else: nAmp = 4 subtractCrosstalk(mi, nAmp, coeffs1List, coeffs2List, gainsPreampSig) # # 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() np_msk[np.where( np.bitwise_and(np_msk, xtalk_temp) == xtalk_temp)] &= ~crosstalk try: msk.removeAndClearMaskPlane(tempStr, True) # added in afw #1853 except AttributeError: afwDisplay.setMaskPlaneColor(tempStr, color="ignore")
def testEllipticalGaussian(self): """Test measuring elliptical aperture mags for an elliptical Gaussian""" width, height = 200, 200 xcen, ycen = 0.5 * width, 0.5 * height # # Make the object # gal = afwImage.ImageF(afwGeom.ExtentI(width, height)) a, b, theta = float(10), float(5), 20 flux = 1e4 I0 = flux / (2 * math.pi * a * b) c, s = math.cos(math.radians(theta)), math.sin(math.radians(theta)) for y in range(height): for x in range(width): dx, dy = x - xcen, y - ycen u = c * dx + s * dy v = -s * dx + c * dy val = I0 * math.exp(-0.5 * ((u / a)**2 + (v / b)**2)) if val < 0: val = 0 gal.set(x, y, val) objImg = afwImage.makeExposure(afwImage.makeMaskedImage(gal)) del gal if display: frame = 0 ds9.mtv(objImg, frame=frame, title="Elliptical") self.assertAlmostEqual( 1.0, afwMath.makeStatistics(objImg.getMaskedImage().getImage(), afwMath.SUM).getValue() / flux) # # Now measure some annuli # sincConfig = measAlgorithms.SincFluxConfig(radius1=0.0, radius2=0.0, angle=math.radians(theta), ellipticity=(1 - b / a)) for r1, r2 in [ (0., 0.45 * a), (0.45 * a, 1.0 * a), (1.0 * a, 2.0 * a), (2.0 * a, 3.0 * a), (3.0 * a, 5.0 * a), (3.0 * a, 10.0 * a), ]: sincConfig.radius1 = r1 sincConfig.radius2 = r2 schema = afwTable.SourceTable.makeMinimalSchema() mp = measAlgorithms.MeasureSourcesBuilder().addAlgorithm( sincConfig.makeControl()).build(schema) if display: # draw the inner and outer boundaries of the aperture Mxx = 1 Myy = (b / a)**2 mxx, mxy, myy = c**2 * Mxx + s**2 * Myy, c * s * ( Mxx - Myy), s**2 * Mxx + c**2 * Myy for r in (r1, r2): ds9.dot("@:%g,%g,%g" % (r**2 * mxx, r**2 * mxy, r**2 * myy), xcen, ycen, frame=frame) table = afwTable.SourceTable.make(schema) source = table.makeRecord() center = afwGeom.Point2D(xcen, ycen) mp.apply(source, objImg, center) self.assertAlmostEqual( math.exp(-0.5 * (r1 / a)**2) - math.exp(-0.5 * (r2 / a)**2), source["flux.sinc"] / flux, 5)
def processImagesForInsertion(self, fakeCat, wcs, psf, photoCalib, band, pixelScale): """Process images from files into the format needed for insertion. Parameters ---------- fakeCat : `pandas.core.frame.DataFrame` The catalog of fake sources to be input wcs : `lsst.afw.geom.skyWcs.skyWcs.SkyWc` WCS to use to add fake sources psf : `lsst.meas.algorithms.coaddPsf.coaddPsf.CoaddPsf` or `lsst.meas.extensions.psfex.psfexPsf.PsfexPsf` The PSF information to use to make the PSF images photoCalib : `lsst.afw.image.photoCalib.PhotoCalib` Photometric calibration to be used to calibrate the fake sources band : `str` The filter band that the observation was taken in. pixelScale : `float` The pixel scale of the image the sources are to be added to. Returns ------- galImages : `list` A list of tuples of `lsst.afw.image.exposure.exposure.ExposureF` and `lsst.geom.Point2D` of their locations. For sources labelled as galaxy. starImages : `list` A list of tuples of `lsst.afw.image.exposure.exposure.ExposureF` and `lsst.geom.Point2D` of their locations. For sources labelled as star. Notes ----- The input fakes catalog needs to contain the absolute path to the image in the band that is being used to add images to. It also needs to have the R.A. and declination of the fake source in radians and the sourceType of the object. """ galImages = [] starImages = [] self.log.info("Processing %d fake images" % len(fakeCat)) for (imFile, sourceType, mag, x, y) in zip(fakeCat[band + "imFilename"].array, fakeCat["sourceType"].array, fakeCat[self.config.magVar % band].array, fakeCat["x"].array, fakeCat["y"].array): im = afwImage.ImageF.readFits(imFile) xy = geom.Point2D(x, y) # We put these two PSF calculations within this same try block so that we catch cases # where the object's position is outside of the image. try: correctedFlux = psf.computeApertureFlux( self.config.calibFluxRadius, xy) psfKernel = psf.computeKernelImage(xy).getArray() psfKernel /= correctedFlux except InvalidParameterError: self.log.info("%s at %0.4f, %0.4f outside of image" % (sourceType, x, y)) continue psfIm = galsim.InterpolatedImage(galsim.Image(psfKernel), scale=pixelScale) galsimIm = galsim.InterpolatedImage(galsim.Image(im.array), scale=pixelScale) convIm = galsim.Convolve([galsimIm, psfIm]) try: outIm = convIm.drawImage(scale=pixelScale, method="real_space").array except (galsim.errors.GalSimFFTSizeError, MemoryError): continue imSum = np.sum(outIm) divIm = outIm / imSum try: flux = photoCalib.magnitudeToInstFlux(mag, xy) except LogicError: flux = 0 imWithFlux = flux * divIm if sourceType == b"galaxy": galImages.append((afwImage.ImageF(imWithFlux), xy)) if sourceType == b"star": starImages.append((afwImage.ImageF(imWithFlux), xy)) return galImages, starImages
def bypass_raw(self, datasetType, pythonType, location, dataId): """Magic method that is called automatically if it exists. """ from lsst.ip.isr import AssembleCcdTask config = AssembleCcdTask.ConfigClass() config.doTrim = False assembleTask = AssembleCcdTask(config=config) logger = lsst.log.Log.getLogger("ZtfMapper") # -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= assert len(location.getLocations() ) == 1 # KTL says that this is always true, but check relativeFileName = location.getLocations()[0] if location.storage.exists(relativeFileName): fileName = location.storage.locationWithRoot(relativeFileName) data = afwImage.ImageF(fileName, hdu=1) data = pythonType(afwImage.MaskedImageF(data)) else: raise IOError(f"Unable to find {relativeFileName}") # Get the Detector detector = self.camera[self._extractDetectorName(dataId)] # # Read all the data # ampDict = {} for i, amp in enumerate(detector, 1): ampExp = pythonType(amp.getRawBBox()) ampExp.setDetector(detector) ampDict[amp.getName()] = ampExp hdu = amp.get('hdu') if i == 1: assert i == hdu # don't read twice data = data.image else: data = afwImage.ImageF(fileName, hdu=hdu) ampExp[amp.getRawDataBBox()].image = data bias = afwImage.ImageF(fileName, hdu=hdu + 4) ampExp[amp.getRawHorizontalOverscanBBox()].image = bias exposure = assembleTask.assembleCcd(ampDict) md = afwImage.readMetadata(fileName, hdu=0) fix_header(md, translator_class=self.translatorClass) exposure.setMetadata(md) visitInfo = ZtfMakeRawVisitInfo(logger)(md) exposure.getInfo().setVisitInfo(visitInfo) boresight = visitInfo.getBoresightRaDec() if boresight.isFinite(): exposure.setWcs( getWcsFromDetector(exposure.getDetector(), boresight, 180 * geom.degrees, flipX=True)) else: logger.warn( f"Unable to set WCS for {dataId} from header as RA/Dec/Angle are unavailable" ) return exposure
def __init__(self, image, nSamples=1000, contrast=0.25): if not hasattr(image, "getArray"): image = afwImage.ImageF(image) z1, z2 = getZScale(image, nSamples, contrast) LinearMapping.__init__(self, z1, z2, image)
def check_placement(self, imageName, raList, decList, fwhmList, countList, gain, detector, camera, obs, epoch=2000.0): """ Read in a FITS image and a list of objects meant to be on that image. Verify that the objects were placed at the correct pixel by counting up all of the flux within 2 fwhm of each object's expected location and verifying it with the counts expected for that object. @param [in] imageName is the name of the FITS file to be read in @param [in] raList is a numpy array of the RA coordinates of the objects in the image (in radians) @param [in] decList is a numpy array of the Dec coordinates of the objects in the image (in radians) @param [in] fwhmList is a list of the Full Width at Half Maximum of each object in arcseconds @param [in] countList is a list of the counts expected for each object @param [in] gain is the gain of the detector (electrons per ADU) @param [in] detector is an instantiation of the afw.cameraGeom Detector class characterizing the detector corresponding to this image @param [in] camera is an instantiation of the afw.cameraGeom Camera class characterizing the camera to which detector belongs @param [in] obs is an instantiation of ObservationMetaData characterizing the telescope pointing @param [in] epoch is the epoch in Julian years of the equinox against which RA and Dec are measured. Raises an exception of the counts detected for each object differs from the expected amount by more than 3 sigma. """ im = afwImage.ImageF(imageName).getArray() activePixels = np.where(im > 1.0e-10) # I know this seems backwards, but the way numpy handles arrays, # the first index is the row (i.e. the y coordinate) imXList = activePixels[1] imYList = activePixels[0] nameList = [detector.getName()]*len(raList) xPixList, yPixList = _pixelCoordsFromRaDec(raList, decList, chipName=nameList, camera=camera, obs_metadata=obs, epoch=epoch) for rr, dd, xx, yy, fwhm, cc in \ zip(raList, decList, xPixList, yPixList, fwhmList, countList): countSigma = np.sqrt(cc/gain) imNameList = [detector.getName()]*len(imXList) raImList, decImList = _raDecFromPixelCoords(imXList, imYList, imNameList, camera=camera, obs_metadata=obs, epoch=epoch) distanceList = arcsecFromRadians(haversine(raImList, decImList, rr, dd)) fluxArray = np.array([im[imYList[ix]][imXList[ix]] for ix in range(len(distanceList)) if distanceList[ix] < 2.0*fwhm]) totalFlux = fluxArray.sum() msg = 'totalFlux %e should be %e diff/sigma %e' \ % (totalFlux, cc, np.abs(totalFlux-cc)/countSigma) self.assertLess(np.abs(totalFlux-cc), 3.0*countSigma, msg=msg)
def get_flux_in_half_light_radius(self, fileName, hlr, detector, camera, obs, epoch=2000.0): """ Read in a FITS image. Return the total flux in that image as well as the flux contained within a specified radius of the maximum pixel of the image. @param [in] fileName is the name of the FITS file to be read in @param [in] hlr is the half light radius to be tested (in arc seconds) @param [in] detector is an instantiation of the afw.cameraGeom Detector class characterizing the detector corresponding to this image @param [in] camera is an instantiation of the afw.cameraGeom Camera class characterizing the camera to which detector belongs @param [in] obs is an instantiation of ObservationMetaData characterizing the telescope pointing @param [in] epoch is the epoch in Julian years of the equinox against which RA and Dec are measured. @param [out] totalFlux is the total number of counts in the images @param [out] measuredHalfFlux is the measured flux within hlr of the maximum pixel """ im = afwImage.ImageF(fileName).getArray() totalFlux = im.sum() _maxPixel = np.array( [im.argmax() / im.shape[1], im.argmax() % im.shape[1]]) maxPixel = np.array([_maxPixel[1], _maxPixel[0]]) raMax, decMax = _raDecFromPixelCoords(maxPixel[0:1], maxPixel[1:2], [detector.getName()], camera=camera, obs_metadata=obs, epoch=epoch) activePoints = np.where(im > 1.0e-10) self.assertGreater(len(activePoints), 0) xPixList = activePoints[ 1] # this looks backwards, but remember: the way numpy handles yPixList = activePoints[ 0] # arrays, the first index indicates what row it is in (the y coordinate) chipNameList = [detector.getName()] * len(xPixList) raList, decList = _raDecFromPixelCoords(xPixList, yPixList, chipNameList, camera=camera, obs_metadata=obs, epoch=epoch) distanceList = arcsecFromRadians( haversine(raList, decList, raMax[0], decMax[0])) dexContained = [ix for ix, dd in enumerate(distanceList) if dd <= hlr] measuredHalfFlux = np.array( [im[yPixList[dex]][xPixList[dex]] for dex in dexContained]).sum() return totalFlux, measuredHalfFlux
def setUp(self): np.random.seed(1) self.val = 10 self.image = afwImage.ImageF(lsst.geom.Box2I( lsst.geom.Point2I(1000, 500), lsst.geom.Extent2I(100, 200))) self.image.set(self.val)
def main(): ''' Runs the deblender and creates plots for the "design document", doc/design.tex. See the file NOTES for how to get set up to the point where you can actually run this on data. ''' from optparse import OptionParser parser = OptionParser() parser.add_option('--root', dest='root', help='Root directory for Subaru data') parser.add_option('--outroot', '-o', dest='outroot', help='Output root directory for Subaru data') parser.add_option('--sources', help='Read a FITS table of sources') parser.add_option('--calexp', help='Read a FITS calexp') parser.add_option('--psf', help='Read a FITS PSF') parser.add_option('--drill', '-D', dest='drill', action='append', type=str, default=[], help='Drill down on individual source IDs') parser.add_option( '--drillxy', dest='drillxy', action='append', type=str, default=[], help='Drill down on individual source positions, eg 132,46;54,67') parser.add_option('--visit', dest='visit', type=int, default=108792, help='Suprimecam visit id') parser.add_option('--ccd', dest='ccd', type=int, default=5, help='Suprimecam CCD number') parser.add_option('--prefix', dest='prefix', default='design-', help='plot filename prefix') parser.add_option('--suffix', dest='suffix', default=None, help='plot filename suffix (default: ".png")') parser.add_option( '--pat', dest='pat', help= 'Plot filename pattern: eg, "design-%(pid)04i-%(name).png"; overrides --prefix and --suffix' ) parser.add_option('--pdf', dest='pdf', action='store_true', default=False, help='save in PDF format?') parser.add_option('-v', dest='verbose', action='store_true') parser.add_option('--figw', dest='figw', type=float, help='Figure window width (inches)', default=4.) parser.add_option('--figh', dest='figh', type=float, help='Figure window height (inches)', default=4.) parser.add_option('--order', dest='order', type=str, help='Child order: eg 3,0,1,2') parser.add_option('--sdss', dest='sec', action='store_const', const='sdss', help='Produce plots for the SDSS section.') parser.add_option('--mono', dest='sec', action='store_const', const='mono', help='Produce plots for the "monotonic" section.') parser.add_option('--median', dest='sec', action='store_const', const='median', help='Produce plots for the "median filter" section.') parser.add_option('--ramp', dest='sec', action='store_const', const='ramp', help='Produce plots for the "ramp edges" section.') parser.add_option( '--ramp2', dest='sec', action='store_const', const='ramp2', help='Produce plots for the "ramp edges + stray flux" section.') parser.add_option('--patch', dest='sec', action='store_const', const='patch', help='Produce plots for the "patch edges" section.') opt, args = parser.parse_args() # Logging if opt.verbose: lsst.log.setLevel('', lsst.log.DEBUG) else: lsst.log.setLevel('', lsst.log.INFO) if opt.sec is None: opt.sec = 'sdss' if opt.pdf: if opt.suffix is None: opt.suffix = '' opt.suffix += '.pdf' if not opt.suffix: opt.suffix = '.png' if opt.pat: plotpattern = opt.pat else: plotpattern = opt.prefix + '%(pid)04i-%(name)s' + opt.suffix if opt.order is not None: opt.order = [int(x) for x in opt.order.split(',')] invorder = np.zeros(len(opt.order)) invorder[opt.order] = np.arange(len(opt.order)) def mapchild(i): if opt.order is None: return i return invorder[i] def savefig(pid, figname): fn = plotpattern % dict(pid=pid, name=figname) plt.savefig(fn) # Load data using the butler, if desired dr = None if opt.sources is None or opt.calexp is None: print('Creating DataRef...') dr = getSuprimeDataref(opt.visit, opt.ccd, rootdir=opt.root, outrootdir=opt.outroot) print('Got', dr) # Which parent ids / deblend families are we going to plot? keepids = None if len(opt.drill): keepids = [] for d in opt.drill: for dd in d.split(','): keepids.append(int(dd)) print('Keeping parent ids', keepids) keepxys = None if len(opt.drillxy): keepxys = [] for d in opt.drillxy: for dd in d.split(';'): xy = dd.split(',') assert (len(xy) == 2) keepxys.append((int(xy[0]), int(xy[1]))) print('Keeping parents at xy', keepxys) # Read from butler or local file cat = readCatalog(opt.sources, None, dataref=dr, keepids=keepids, keepxys=keepxys, patargs=dict(visit=opt.visit, ccd=opt.ccd)) print('Got', len(cat), 'sources') # Load data from butler or local files if opt.calexp is not None: print('Reading exposure from', opt.calexp) exposure = afwImage.ExposureF(opt.calexp) else: exposure = dr.get('calexp') print('Exposure', exposure) mi = exposure.getMaskedImage() if opt.psf is not None: print('Reading PSF from', opt.psf) psf = afwDet.Psf.readFits(opt.psf) print('Got', psf) elif dr: psf = dr.get('psf') else: psf = exposure.getPsf() sigma1 = get_sigma1(mi) fams = getFamilies(cat) print(len(fams), 'deblend families') if False: for j, (parent, children) in enumerate(fams): print('parent', parent) print('children', children) plotDeblendFamily(mi, parent, children, cat, sigma1, ellipses=False) fn = '%04i.png' % parent.getId() plt.savefig(fn) print('wrote', fn) def nlmap(X): return np.arcsinh(X / (3. * sigma1)) def myimshow(im, **kwargs): kwargs = kwargs.copy() mn = kwargs.get('vmin', -5 * sigma1) kwargs['vmin'] = nlmap(mn) mx = kwargs.get('vmax', 100 * sigma1) kwargs['vmax'] = nlmap(mx) plt.imshow(nlmap(im), **kwargs) plt.figure(figsize=(opt.figw, opt.figh)) plt.subplot(1, 1, 1) plt.subplots_adjust(left=0.01, right=0.99, bottom=0.01, top=0.99, wspace=0.05, hspace=0.1) # Make plots for each deblend family. for j, (parent, children) in enumerate(fams): print('parent', parent.getId()) print('children', [ch.getId() for ch in children]) print('parent x,y', parent.getX(), parent.getY()) pid = parent.getId() fp = parent.getFootprint() bb = fp.getBBox() pim = footprintToImage(parent.getFootprint(), mi).getArray() pext = getExtent(bb) imargs = dict(interpolation='nearest', origin='lower', vmax=pim.max() * 0.95, vmin=-3. * sigma1) pksty = dict(linestyle='None', marker='+', color='r', mew=3, ms=20, alpha=0.6) plt.clf() myimshow(afwImage.ImageF(mi.getImage(), bb).getArray(), **imargs) plt.gray() plt.xticks([]) plt.yticks([]) savefig(pid, 'image') # Parent footprint plt.clf() myimshow(pim, extent=pext, **imargs) plt.gray() pks = fp.getPeaks() plt.plot([pk.getIx() for pk in pks], [pk.getIy() for pk in pks], **pksty) plt.xticks([]) plt.yticks([]) plt.axis(pext) savefig(pid, 'parent') from lsst.meas.deblender.baseline import deblend xc = int((bb.getMinX() + bb.getMaxX()) / 2.) yc = int((bb.getMinY() + bb.getMaxY()) / 2.) if hasattr(psf, 'getFwhm'): psf_fwhm = psf.getFwhm(xc, yc) else: psf_fwhm = psf.computeShape().getDeterminantRadius() * 2.35 # Each section of the design doc runs the deblender with different args. kwargs = dict(sigma1=sigma1, verbose=opt.verbose, getTemplateSum=True) basic = kwargs.copy() basic.update(fit_psfs=False, median_smooth_template=False, monotonic_template=False, lstsq_weight_templates=False, assignStrayFlux=False, rampFluxAtEdge=False, patchEdges=False) if opt.sec == 'sdss': # SDSS intro kwargs = basic kwargs.update(lstsq_weight_templates=True) elif opt.sec == 'mono': kwargs = basic kwargs.update(lstsq_weight_templates=True, monotonic_template=True) elif opt.sec == 'median': kwargs = basic kwargs.update(lstsq_weight_templates=True, median_smooth_template=True, monotonic_template=True) elif opt.sec == 'ramp': kwargs = basic kwargs.update(median_smooth_template=True, monotonic_template=True, rampFluxAtEdge=True) elif opt.sec == 'ramp2': kwargs = basic kwargs.update(median_smooth_template=True, monotonic_template=True, rampFluxAtEdge=True, assignStrayFlux=True) elif opt.sec == 'patch': kwargs = basic kwargs.update(median_smooth_template=True, monotonic_template=True, patchEdges=True) else: raise 'Unknown section: "%s"' % opt.sec print('Running deblender with kwargs:', kwargs) res = deblend(fp, mi, psf, psf_fwhm, **kwargs) # print('got result with', [x for x in dir(res) if not x.startswith('__')]) # for pk in res.peaks: # print('got peak with', [x for x in dir(pk) if not x.startswith('__')]) # print(' deblend as psf?', pk.deblend_as_psf) # Find bounding-box of all templates. tbb = fp.getBBox() for pkres, pk in zip(res.peaks, pks): tbb.include(pkres.template_foot.getBBox()) print('Bounding-box of all templates:', tbb) # Sum-of-templates plot tsum = np.zeros((tbb.getHeight(), tbb.getWidth())) tx0, ty0 = tbb.getMinX(), tbb.getMinY() # Sum-of-deblended children plot(s) # "heavy" bbox == template bbox. hsum = np.zeros((tbb.getHeight(), tbb.getWidth())) hsum2 = np.zeros((tbb.getHeight(), tbb.getWidth())) # Sum of templates from the deblender itself plt.clf() t = res.templateSum myimshow(t.getArray(), extent=getExtent(t.getBBox()), **imargs) plt.gray() plt.xticks([]) plt.yticks([]) savefig(pid, 'tsum1') # Make plots for each deblended child (peak) k = 0 for pkres, pk in zip(res.peaks, pks): heavy = pkres.get_flux_portion() if heavy is None: print('Child has no HeavyFootprint -- skipping') continue kk = mapchild(k) w = pkres.template_weight cfp = pkres.template_foot cbb = cfp.getBBox() cext = getExtent(cbb) # Template image tim = pkres.template_mimg.getImage() timext = cext tim = tim.getArray() (x0, x1, y0, y1) = timext print('tim ext', timext) tsum[y0 - ty0:y1 - ty0, x0 - tx0:x1 - tx0] += tim # "Heavy" image -- flux assigned to child him = footprintToImage(heavy).getArray() hext = getExtent(heavy.getBBox()) (x0, x1, y0, y1) = hext hsum[y0 - ty0:y1 - ty0, x0 - tx0:x1 - tx0] += him # "Heavy" without stray flux h2 = pkres.get_flux_portion(strayFlux=False) him2 = footprintToImage(h2).getArray() hext2 = getExtent(h2.getBBox()) (x0, x1, y0, y1) = hext2 hsum2[y0 - ty0:y1 - ty0, x0 - tx0:x1 - tx0] += him2 if opt.sec == 'median': try: med = pkres.median_filtered_template except Exception: med = pkres.orig_template for im, nm in [(pkres.orig_template, 'symm'), (med, 'med')]: # print('im:', im) plt.clf() myimshow(im.getArray(), extent=cext, **imargs) plt.gray() plt.xticks([]) plt.yticks([]) plt.plot([pk.getIx()], [pk.getIy()], **pksty) plt.axis(pext) savefig(pid, nm + '%i' % (kk)) # Template plt.clf() myimshow(pkres.template_mimg.getImage().getArray() / w, extent=cext, **imargs) plt.gray() plt.xticks([]) plt.yticks([]) plt.plot([pk.getIx()], [pk.getIy()], **pksty) plt.axis(pext) savefig(pid, 't%i' % (kk)) # Weighted template plt.clf() myimshow(tim, extent=cext, **imargs) plt.gray() plt.xticks([]) plt.yticks([]) plt.plot([pk.getIx()], [pk.getIy()], **pksty) plt.axis(pext) savefig(pid, 'tw%i' % (kk)) # "Heavy" plt.clf() myimshow(him, extent=hext, **imargs) plt.gray() plt.xticks([]) plt.yticks([]) plt.plot([pk.getIx()], [pk.getIy()], **pksty) plt.axis(pext) savefig(pid, 'h%i' % (kk)) # Original symmetric template plt.clf() t = pkres.orig_template foot = pkres.orig_foot myimshow(t.getArray(), extent=getExtent(foot.getBBox()), **imargs) plt.gray() plt.xticks([]) plt.yticks([]) plt.plot([pk.getIx()], [pk.getIy()], **pksty) plt.axis(pext) savefig(pid, 'o%i' % (kk)) if opt.sec == 'patch' and pkres.patched: pass if opt.sec in ['ramp', 'ramp2'] and pkres.has_ramped_template: # Ramped template plt.clf() t = pkres.ramped_template myimshow(t.getArray(), extent=getExtent(t.getBBox()), **imargs) plt.gray() plt.xticks([]) plt.yticks([]) plt.plot([pk.getIx()], [pk.getIy()], **pksty) plt.axis(pext) savefig(pid, 'r%i' % (kk)) # Median-filtered template plt.clf() t = pkres.median_filtered_template myimshow(t.getArray(), extent=getExtent(t.getBBox()), **imargs) plt.gray() plt.xticks([]) plt.yticks([]) plt.plot([pk.getIx()], [pk.getIy()], **pksty) plt.axis(pext) savefig(pid, 'med%i' % (kk)) # Assigned flux plt.clf() t = pkres.portion_mimg.getImage() myimshow(t.getArray(), extent=getExtent(t.getBBox()), **imargs) plt.gray() plt.xticks([]) plt.yticks([]) plt.plot([pk.getIx()], [pk.getIy()], **pksty) plt.axis(pext) savefig(pid, 'p%i' % (kk)) if opt.sec == 'ramp2': # stray flux if pkres.stray_flux is not None: s = pkres.stray_flux strayim = footprintToImage(s).getArray() strayext = getExtent(s.getBBox()) plt.clf() myimshow(strayim, extent=strayext, **imargs) plt.gray() plt.xticks([]) plt.yticks([]) plt.plot([pk.getIx()], [pk.getIy()], **pksty) plt.axis(pext) savefig(pid, 's%i' % (kk)) # Assigned flux, omitting stray flux. plt.clf() myimshow(him2, extent=hext2, **imargs) plt.gray() plt.xticks([]) plt.yticks([]) plt.plot([pk.getIx()], [pk.getIy()], **pksty) plt.axis(pext) savefig(pid, 'hb%i' % (kk)) k += 1 # sum of templates plt.clf() myimshow(tsum, extent=getExtent(tbb), **imargs) plt.gray() plt.xticks([]) plt.yticks([]) plt.plot([pk.getIx() for pk in pks], [pk.getIy() for pk in pks], **pksty) plt.axis(pext) savefig(pid, 'tsum') # sum of assigned flux plt.clf() myimshow(hsum, extent=getExtent(tbb), **imargs) plt.gray() plt.xticks([]) plt.yticks([]) plt.plot([pk.getIx() for pk in pks], [pk.getIy() for pk in pks], **pksty) plt.axis(pext) savefig(pid, 'hsum') plt.clf() myimshow(hsum2, extent=getExtent(tbb), **imargs) plt.gray() plt.xticks([]) plt.yticks([]) plt.plot([pk.getIx() for pk in pks], [pk.getIy() for pk in pks], **pksty) plt.axis(pext) savefig(pid, 'hsum2') k = 0 for pkres, pk in zip(res.peaks, pks): heavy = pkres.get_flux_portion() if heavy is None: continue print('Template footprint:', pkres.template_foot.getBBox()) print('Template img:', pkres.template_mimg.getBBox()) print('Heavy footprint:', heavy.getBBox()) cfp = pkres.template_foot cbb = cfp.getBBox() cext = getExtent(cbb) tim = pkres.template_mimg.getImage().getArray() (x0, x1, y0, y1) = cext frac = tim / tsum[y0 - ty0:y1 - ty0, x0 - tx0:x1 - tx0] msk = afwImage.ImageF(cbb.getWidth(), cbb.getHeight()) msk.setXY0(cbb.getMinX(), cbb.getMinY()) afwDet.setImageFromFootprint(msk, cfp, 1.) msk = msk.getArray() frac[msk == 0.] = np.nan # Fraction of flux assigned to this child. plt.clf() plt.imshow(frac, extent=cext, interpolation='nearest', origin='lower', vmin=0, vmax=1) # plt.plot([x0,x0,x1,x1,x0], [y0,y1,y1,y0,y0], 'k-') plt.gray() plt.xticks([]) plt.yticks([]) plt.plot([pk.getIx()], [pk.getIy()], **pksty) plt.gca().set_axis_bgcolor((0.9, 0.9, 0.5)) plt.axis(pext) savefig(pid, 'f%i' % (mapchild(k))) k += 1
def getRandomImage(self, bb): # Create an Image and fill it with Gaussian noise. rim = afwImage.ImageF(bb.getWidth(), bb.getHeight()) rim.setXY0(bb.getMinX(), bb.getMinY()) afwMath.randomGaussianImage(rim, self.rand) return rim
def get_position_angle(self, imageName, afwCamera, afwDetector, obs_metadata, epoch): """ Read in a FITS image containing one extended object. Determine its north and east axes by examining how RA and Dec change with pixel position. Determine the semi-major axis of the object by treating the distribution of flux as a covariance matrix and finding its eigen vectors. Return the angle between the semi-major axis and the north axis of the image @param [in] imageName is the name of the FITS image to be read @param [in] afwCamera is an afw.cameraGeom.Camera @param [in] afwDetector is an afw.cameraGeom.Detector @param [in] obs_metadata is an ObservationMetaData describing the pointing of the telescope @param [in] epoch is the epoch in Julian years of the equinox against which RA and Dec are measured @param [out] the position angle of the object in the image in degrees """ im = afwImage.ImageF(imageName).getArray() activePixels = np.where(im > 1.0e-10) self.assertGreater(len(activePixels), 0) xPixList = activePixels[1] yPixList = activePixels[0] xCenterPix = np.array([im.shape[1] / 2]) yCenterPix = np.array([im.shape[0] / 2]) raCenter, decCenter = _raDecFromPixelCoords(xCenterPix, yCenterPix, [afwDetector.getName()], camera=afwCamera, obs_metadata=obs_metadata, epoch=epoch) xCenterP1 = xCenterPix + 1 yCenterP1 = yCenterPix + 1 raCenterP1, decCenterP1 = _raDecFromPixelCoords( xCenterP1, yCenterP1, [afwDetector.getName()], camera=afwCamera, obs_metadata=obs_metadata, epoch=epoch) # find the angle between the (1,1) vector in pixel space and the # north axis of the image theta = np.arctan2((raCenterP1[0] - raCenter[0]), decCenterP1[0] - decCenter[0]) # rotate the (1,1) vector in pixel space so that it is pointing # along the north axis north = np.array( [np.cos(theta) - np.sin(theta), np.cos(theta) + np.sin(theta)]) north = north / np.sqrt(north[0] * north[0] + north[1] * north[1]) # find the east axis of the image east = np.array([north[1], -1.0 * north[0]]) # now find the covariance matrix of the x, y pixel space distribution # of flux on the image maxPixel = np.array( [im.argmax() % im.shape[1], im.argmax() / im.shape[1]]) xx = np.array([ im[iy][ix] * np.power(ix - maxPixel[0], 2) for ix, iy in zip(xPixList, yPixList) ]).sum() xy = np.array([ im[iy][ix] * (ix - maxPixel[0]) * (iy - maxPixel[1]) for ix, iy in zip(xPixList, yPixList) ]).sum() yy = np.array([ im[iy][ix] * (iy - maxPixel[1]) * (iy - maxPixel[1]) for ix, iy in zip(xPixList, yPixList) ]).sum() covar = np.array([[xx, xy], [xy, yy]]) # find the eigen vectors of this covarinace matrix; # treat the one with the largest eigen value as the # semi-major axis of the object eigenVals, eigenVecs = np.linalg.eig(covar) iMax = eigenVals.argmax() majorAxis = eigenVecs[:, iMax] majorAxis = majorAxis / np.sqrt(majorAxis[0] * majorAxis[0] + majorAxis[1] * majorAxis[1]) # return the angle between the north axis of the image # and the semi-major axis of the object cosTheta = np.dot(majorAxis, north) sinTheta = np.dot(majorAxis, east) theta = np.arctan2(sinTheta, cosTheta) return np.degrees(theta)
def testMergeHeavyFootprints(self): mi = afwImage.MaskedImageF(20, 10) objectPixelVal = (42, 0x9, 400) foot = afwDetect.Footprint() for y, x0, x1 in [(1, 9, 12), (2, 12, 13), (3, 11, 15)]: foot.addSpan(y, x0, x1) for x in range(x0, x1 + 1): mi.set(x, y, objectPixelVal) hfoot1 = afwDetect.makeHeavyFootprint(self.foot, self.mi) hfoot2 = afwDetect.makeHeavyFootprint(foot, mi) hfoot1.normalize() hfoot2.normalize() hsum = afwDetect.mergeHeavyFootprintsF(hfoot1, hfoot2) bb = hsum.getBBox() self.assertEquals(bb.getMinX(), 9) self.assertEquals(bb.getMaxX(), 15) self.assertEquals(bb.getMinY(), 1) self.assertEquals(bb.getMaxY(), 3) msum = afwImage.MaskedImageF(20,10) hsum.insert(msum) sa = msum.getImage().getArray() self.assertTrue(np.all(sa[1, 9:13] == objectPixelVal[0])) self.assertTrue(np.all(sa[2, 12:14] == objectPixelVal[0] + self.objectPixelVal[0])) self.assertTrue(np.all(sa[2, 10:12] == self.objectPixelVal[0])) sv = msum.getVariance().getArray() self.assertTrue(np.all(sv[1, 9:13] == objectPixelVal[2])) self.assertTrue(np.all(sv[2, 12:14] == objectPixelVal[2] + self.objectPixelVal[2])) self.assertTrue(np.all(sv[2, 10:12] == self.objectPixelVal[2])) sm = msum.getMask().getArray() self.assertTrue(np.all(sm[1, 9:13] == objectPixelVal[1])) self.assertTrue(np.all(sm[2, 12:14] == objectPixelVal[1] | self.objectPixelVal[1])) self.assertTrue(np.all(sm[2, 10:12] == self.objectPixelVal[1])) if False: import matplotlib matplotlib.use('Agg') import pylab as plt im1 = afwImage.ImageF(bb) hfoot1.insert(im1) im2 = afwImage.ImageF(bb) hfoot2.insert(im2) im3 = afwImage.ImageF(bb) hsum.insert(im3) plt.clf() plt.subplot(1,3,1) plt.imshow(im1.getArray(), interpolation='nearest', origin='lower') plt.subplot(1,3,2) plt.imshow(im2.getArray(), interpolation='nearest', origin='lower') plt.subplot(1,3,3) plt.imshow(im3.getArray(), interpolation='nearest', origin='lower') plt.savefig('merge.png')
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 mkFakeGalsimGalaxies(self, fakeCat, band, photoCalib, pixelScale, psf, image): """Make images of fake galaxies using GalSim. Parameters ---------- band : `str` pixelScale : `float` psf : `lsst.meas.extensions.psfex.psfexPsf.PsfexPsf` The PSF information to use to make the PSF images fakeCat : `pandas.core.frame.DataFrame` The catalog of fake sources to be input photoCalib : `lsst.afw.image.photoCalib.PhotoCalib` Photometric calibration to be used to calibrate the fake sources Yields ------- galImages : `generator` A generator of tuples of `lsst.afw.image.exposure.exposure.ExposureF` and `lsst.geom.Point2D` of their locations. Notes ----- Fake galaxies are made by combining two sersic profiles, one for the bulge and one for the disk. Each component has an individual sersic index (n), a, b and position angle (PA). The combined profile is then convolved with the PSF at the specified x, y position on the image. The names of the columns in the ``fakeCat`` are configurable and are the column names from the University of Washington simulations database as default. For more information see the doc strings attached to the config options. See mkFakeStars doc string for an explanation of calibration to instrumental flux. """ self.log.info("Making %d fake galaxy images" % len(fakeCat)) for (index, row) in fakeCat.iterrows(): xy = geom.Point2D(row["x"], row["y"]) # We put these two PSF calculations within this same try block so that we catch cases # where the object's position is outside of the image. try: correctedFlux = psf.computeApertureFlux( self.config.calibFluxRadius, xy) psfKernel = psf.computeKernelImage(xy).getArray() psfKernel /= correctedFlux except InvalidParameterError: self.log.info("Galaxy at %0.4f, %0.4f outside of image" % (row["x"], row["y"])) continue try: flux = photoCalib.magnitudeToInstFlux( row[self.config.magVar % band], xy) except LogicError: flux = 0 bulge = galsim.Sersic(row[self.config.nBulge], half_light_radius=row[self.config.bulgeHLR]) axisRatioBulge = row[self.config.bBulge] / row[self.config.aBulge] bulge = bulge.shear(q=axisRatioBulge, beta=((90 - row[self.config.paBulge]) * galsim.degrees)) disk = galsim.Sersic(row[self.config.nDisk], half_light_radius=row[self.config.diskHLR]) axisRatioDisk = row[self.config.bDisk] / row[self.config.aDisk] disk = disk.shear(q=axisRatioDisk, beta=((90 - row[self.config.paDisk]) * galsim.degrees)) gal = disk + bulge gal = gal.withFlux(flux) psfIm = galsim.InterpolatedImage(galsim.Image(psfKernel), scale=pixelScale) gal = galsim.Convolve([gal, psfIm]) try: galIm = gal.drawImage(scale=pixelScale, method="real_space").array except (galsim.errors.GalSimFFTSizeError, MemoryError): continue yield (afwImage.ImageF(galIm), xy)
def testClipToNonzero(self): # create a circular footprint ellipse = afwGeomEllipses.Ellipse(afwGeomEllipses.Axes(6, 6, 0), afwGeom.Point2D(9, 15)) bb = afwGeom.Box2I(afwGeom.Point2I(0, 0), afwGeom.Extent2I(20, 30)) foot = afwDetect.Footprint(ellipse, bb) a0 = foot.getArea() plots = False if plots: import matplotlib matplotlib.use('Agg') import pylab as plt plt.clf() img = afwImage.ImageU(bb) foot.insertIntoImage(img, 1) ima = dict(interpolation='nearest', origin='lower', cmap='gray') plt.imshow(img.getArray(), **ima) plt.savefig('clipnz1.png') source = afwImage.ImageF(bb) source.getArray()[:, :] = 1. source.getArray()[:, 0:10] = 0. foot.clipToNonzero(source) foot.normalize() a1 = foot.getArea() self.assertLess(a1, a0) img = afwImage.ImageU(bb) foot.insertIntoImage(img, 1) self.assertTrue(np.all(img.getArray()[source.getArray() == 0] == 0)) if plots: plt.clf() plt.subplot(1, 2, 1) plt.imshow(source.getArray(), **ima) plt.subplot(1, 2, 2) plt.imshow(img.getArray(), **ima) plt.savefig('clipnz2.png') source.getArray()[:12, :] = 0. foot.clipToNonzero(source) foot.normalize() a2 = foot.getArea() self.assertLess(a2, a1) img = afwImage.ImageU(bb) foot.insertIntoImage(img, 1) self.assertTrue(np.all(img.getArray()[source.getArray() == 0] == 0)) if plots: plt.clf() plt.subplot(1, 2, 1) plt.imshow(source.getArray(), **ima) plt.subplot(1, 2, 2) img = afwImage.ImageU(bb) foot.insertIntoImage(img, 1) plt.imshow(img.getArray(), **ima) plt.savefig('clipnz3.png')
def getExposure(self): """Construct a test exposure. The test exposure has a simple WCS set, as well as a list of unlikely header keywords that can be removed during ISR processing to exercise that code. Returns ------- exposure : `lsst.afw.exposure.Exposure` Construct exposure containing masked image of the appropriate size. """ camera = self.getCamera() detector = camera[self.config.detectorIndex] image = afwUtils.makeImageFromCcd(detector, isTrimmed=self.config.isTrimmed, showAmpGain=False, rcMarkSize=0, binSize=1, imageFactory=afwImage.ImageF) var = afwImage.ImageF(image.getDimensions()) mask = afwImage.Mask(image.getDimensions()) image.assign(0.0) maskedImage = afwImage.makeMaskedImage(image, mask, var) exposure = afwImage.makeExposure(maskedImage) exposure.setDetector(detector) exposure.setWcs(self.getWcs()) visitInfo = afwImage.VisitInfo(exposureTime=self.config.expTime, darkTime=self.config.darkTime) exposure.getInfo().setVisitInfo(visitInfo) metadata = exposure.getMetadata() metadata.add("SHEEP", 7.3, "number of sheep on farm") metadata.add("MONKEYS", 155, "monkeys per tree") metadata.add("VAMPIRES", 4, "How scary are vampires.") ccd = exposure.getDetector() newCcd = ccd.rebuild() newCcd.clear() for amp in ccd: newAmp = amp.rebuild() newAmp.setLinearityCoeffs((0., 1., 0., 0.)) newAmp.setLinearityType("Polynomial") newAmp.setGain(self.config.gain) newAmp.setSuspectLevel(25000.0) newAmp.setSaturation(32000.0) newCcd.append(newAmp) exposure.setDetector(newCcd.finish()) exposure.image.array[:] = np.zeros( exposure.getImage().getDimensions()).transpose() exposure.mask.array[:] = np.zeros( exposure.getMask().getDimensions()).transpose() exposure.variance.array[:] = np.zeros( exposure.getVariance().getDimensions()).transpose() return exposure
def testGrow(self): """Test growing a footprint""" x0, y0 = 20, 20 width, height = 20, 30 foot1 = afwDetect.Footprint( afwGeom.Box2I(afwGeom.Point2I(x0, y0), afwGeom.Extent2I(width, height)), afwGeom.Box2I(afwGeom.Point2I(0, 0), afwGeom.Extent2I(100, 100))) # Add some peaks and check that they get copied into the new grown footprint foot1.addPeak(20, 20, 1) foot1.addPeak(30, 35, 2) foot1.addPeak(25, 45, 3) self.assertEqual(len(foot1.getPeaks()), 3) bbox1 = foot1.getBBox() self.assertEqual(bbox1.getMinX(), x0) self.assertEqual(bbox1.getMaxX(), x0 + width - 1) self.assertEqual(bbox1.getWidth(), width) self.assertEqual(bbox1.getMinY(), y0) self.assertEqual(bbox1.getMaxY(), y0 + height - 1) self.assertEqual(bbox1.getHeight(), height) ngrow = 5 for isotropic in (True, False): foot2 = afwDetect.growFootprint(foot1, ngrow, isotropic) # Check that the grown footprint is normalized self.assertTrue(foot2.isNormalized()) # Check that the grown footprint is bigger than the original self.assertGreater(foot2.getArea(), foot1.getArea()) # Check that peaks got copied into grown footprint self.assertEqual(len(foot2.getPeaks()), 3) for peak in foot2.getPeaks(): self.assertIn((peak.getIx(), peak.getIy()), [(20, 20), (30, 35), (25, 45)]) bbox2 = foot2.getBBox() if False and display: idImage = afwImage.ImageU(width, height) idImage.set(0) i = 1 for foot in [foot1, foot2]: foot.insertIntoImage(idImage, i) i += 1 metricImage = afwImage.ImageF("foo.fits") ds9.mtv(metricImage, frame=1) ds9.mtv(idImage) # check bbox2 self.assertEqual(bbox2.getMinX(), x0 - ngrow) self.assertEqual(bbox2.getWidth(), width + 2 * ngrow) self.assertEqual(bbox2.getMinY(), y0 - ngrow) self.assertEqual(bbox2.getHeight(), height + 2 * ngrow) # Check that region was preserved self.assertEqual(foot1.getRegion(), foot2.getRegion())
def plantSources(x0, y0, nx, ny, sky, nObj, wid, detector, useRandom=False): tanSys = detector.makeCameraSys(cameraGeom.TAN_PIXELS) pixToTanXYTransform = detector.getTransformMap()[tanSys] img0 = afwImage.ImageF(afwGeom.ExtentI(nx, ny)) img = afwImage.ImageF(afwGeom.ExtentI(nx, ny)) ixx0, iyy0, ixy0 = wid*wid, wid*wid, 0.0 edgeBuffer = 40.0*wid flux = 1.0e4 nkx, nky = int(10*wid) + 1, int(10*wid) + 1 xhwid,yhwid = nkx/2, nky/2 nRow = int(math.sqrt(nObj)) xstep = (nx - 1 - 0.0*edgeBuffer)/(nRow+1) ystep = (ny - 1 - 0.0*edgeBuffer)/(nRow+1) if useRandom: nObj = nRow*nRow goodAdded0 = [] goodAdded = [] for i in range(nObj): # get our position if useRandom: xcen0, ycen0 = numpy.random.uniform(nx), numpy.random.uniform(ny) else: xcen0, ycen0 = xstep*((i%nRow) + 1), ystep*(int(i/nRow) + 1) ixcen0, iycen0 = int(xcen0), int(ycen0) # distort position and shape pTan = afwGeom.Point2D(xcen0, ycen0) linTransform = pixToTanXYTransform.linearizeReverseTransform(pTan).getLinear() m = geomEllip.Quadrupole(ixx0, iyy0, ixy0) m.transform(linTransform) p = pixToTanXYTransform.reverseTransform(pTan) xcen, ycen = xcen0, ycen0 #p.getX(), p.getY() if (xcen < 1.0*edgeBuffer or (nx - xcen) < 1.0*edgeBuffer or ycen < 1.0*edgeBuffer or (ny - ycen) < 1.0*edgeBuffer): continue ixcen, iycen = int(xcen), int(ycen) ixx, iyy, ixy = m.getIxx(), m.getIyy(), m.getIxy() # plant the object tmp = 0.25*(ixx-iyy)**2 + ixy**2 a2 = 0.5*(ixx+iyy) + numpy.sqrt(tmp) b2 = 0.5*(ixx+iyy) - numpy.sqrt(tmp) #ellip = 1.0 - numpy.sqrt(b2/a2) theta = 0.5*numpy.arctan2(2.0*ixy, ixx-iyy) a = numpy.sqrt(a2) b = numpy.sqrt(b2) c, s = math.cos(theta), math.sin(theta) good0, good = True, True for y in range(nky): iy = iycen + y - yhwid iy0 = iycen0 + y - yhwid for x in range(nkx): ix = ixcen + x - xhwid ix0 = ixcen0 + x - xhwid if ix >= 0 and ix < nx and iy >= 0 and iy < ny: dx, dy = ix - xcen, iy - ycen u = c*dx + s*dy v = -s*dx + c*dy I0 = flux/(2*math.pi*a*b) val = I0*math.exp(-0.5*((u/a)**2 + (v/b)**2)) if val < 0: val = 0 prevVal = img.get(ix, iy) img.set(ix, iy, val+prevVal) else: good = False if ix0 >=0 and ix0 < nx and iy0 >= 0 and iy0 < ny: dx, dy = ix - xcen, iy - ycen I0 = flux/(2*math.pi*wid*wid) val = I0*math.exp(-0.5*((dx/wid)**2 + (dy/wid)**2)) if val < 0: val = 0 prevVal = img0.get(ix0, iy0) img0.set(ix0, iy0, val+prevVal) else: good0 = False if good0: goodAdded0.append([xcen,ycen]) if good: goodAdded.append([xcen,ycen]) # add sky and noise img += sky img0 += sky noise = afwImage.ImageF(afwGeom.ExtentI(nx, ny)) noise0 = afwImage.ImageF(afwGeom.ExtentI(nx, ny)) for i in range(nx): for j in range(ny): noise.set(i, j, numpy.random.poisson(img.get(i,j) )) noise0.set(i, j, numpy.random.poisson(img0.get(i,j) )) edgeWidth = int(0.5*edgeBuffer) mask = afwImage.MaskU(afwGeom.ExtentI(nx, ny)) left = afwGeom.Box2I(afwGeom.Point2I(0,0), afwGeom.ExtentI(edgeWidth, ny)) right = afwGeom.Box2I(afwGeom.Point2I(nx - edgeWidth,0), afwGeom.ExtentI(edgeWidth, ny)) top = afwGeom.Box2I(afwGeom.Point2I(0,ny - edgeWidth), afwGeom.ExtentI(nx, edgeWidth)) bottom = afwGeom.Box2I(afwGeom.Point2I(0,0), afwGeom.ExtentI(nx, edgeWidth)) for pos in [left, right, top, bottom]: msk = afwImage.MaskU(mask, pos, False) msk.set(msk.getPlaneBitMask('EDGE')) expos = afwImage.makeExposure(afwImage.makeMaskedImage(noise, mask, afwImage.ImageF(noise, True))) expos0 = afwImage.makeExposure(afwImage.makeMaskedImage(noise0, mask, afwImage.ImageF(noise0, True))) im = expos.getMaskedImage().getImage() im0 = expos0.getMaskedImage().getImage() im -= sky im0 -= sky return expos, goodAdded, expos0, goodAdded0
def makeLsstFile(imfile, expfile, lsstfile, interpolateNans=False, debug=True): import lsst.afw.image as afwImage from astropy.io import fits import numpy as np from lsst.ip.isr.isrFunctions import saturationCorrection if debug: print("Generating:\n%s\n from\n%s\n%s" % (lsstfile, imfile, expfile)) gain = 3.4 # WIYN WHIRC Data Reduction Guide imdir, imbasename = os.path.dirname(imfile), os.path.basename(imfile) tmp_imfile = os.path.join(imdir, "tmp_" + imbasename) os.system("cp {} {}".format(imfile, tmp_imfile)) # os.system("sethead EXPID=0 {}".format(tmp_imfile)) with fits.open(tmp_imfile, mode='update') as hdu: # Some DTTITLE keywords were written with trailing quotes: # 'Type Ia Supernovae in the Near-Infrared: A Three-Year Survey toward'' # AST chokes on these presumably invalid entries. dttitle = hdu[0].header['DTTITLE'] hdu[0].header['DTTITLE'] = dttitle.rstrip("'") hdu[0].header['EXPID'] = 0 hdu.flush() #Exposures should keep your header keys exp = afwImage.ExposureF(tmp_imfile) im = exp.getMaskedImage().getImage() imArr = im.getArray() var = exp.getMaskedImage().getVariance() varArr = var.getArray() mask = exp.getMaskedImage().getMask() maskArr = mask.getArray() os.system("rm {}".format(tmp_imfile)) # Calculate the variance based on the background level / second # stored in the coadd, which is the background level of # the first individual images included in the coadd # The coadd was created as an average # so is noramlized to the count level of one input image. expTimeArr = afwImage.ImageF(expfile).getArray() exphead = fits.getheader(expfile) exptime_per_image = exphead[ "EXPTIME"] # EXPTIME is not in the afwImage info (why not?) so we fall back on astropy.io.fits background_per_image = exphead["BGLEVEL"] # print "BGLEVEL: ", background_per_image # print "EXPTIME: ", exptime background_per_second = background_per_image / exptime_per_image fullexptime = np.percentile(expTimeArr, 99) if debug: print("(Background counts/sec, fullexptime, exptime_per_image)") print(background_per_second, fullexptime, exptime_per_image) idx = range(int(0.2 * len(maskArr[0, :])), int(0.8 * len(maskArr[0, :]))) photonArr = gain * \ (imArr+background_per_image) * (expTimeArr/exptime_per_image) ## Variance in ADU for the accumulated counts ## (i.e., that has not been re-scaled to have the same normalization across the image) varArr[:, :] = photonArr / gain**2 ## Variance in ADU for the normalized image varArr /= (expTimeArr / exptime_per_image)**2 varArr[np.logical_not(np.isfinite(imArr))] = np.inf # Reject all of the locations with less than 20% of the effective exposure time idxs = np.where(expTimeArr < 0.2 * fullexptime) badmask = mask.getPlaneBitMask('BAD') maskArr[idxs] |= badmask intrpmask = mask.getPlaneBitMask('INTRP') maskArr[idxs] |= intrpmask if interpolateNans: SAT_LEVEL = 100000 imArr[np.logical_not(np.isfinite(imArr))] = 2 * SAT_LEVEL saturationCorrection(exp.getMaskedImage(), saturation=SAT_LEVEL, fwhm=2, growFootprints=False) # import lsst.afw.display.ds9 as ds9 # ds9.mtv(exp, title="Foo") # If set, then boost the variance of low-exposure regions to 10x higher. # This is a bit of a hack that I'm unhappy about, but there's no # way to tell the current (2015-03-19) DM stack software to ignore a region. boostBadPixelVariance = False if boostBadPixelVariance: varArr[idxs] *= 10 exp.writeFits(lsstfile)
fratio = np.mean(imarr1 / imarr2) imarr2 *= fratio # Calculate the mean value of the flat field images. fmean = (np.mean(imarr1) + np.mean(imarr2)) / 2. # Calculate the variance of the flat difference image. fvar = np.var(imarr1 - imarr2) / 2. gains.append(fvar / fmean) gain = 1. / np.median(gains) # gain in Ne/DN return gain, im1, im2 if __name__ == '__main__': import os from .sim_tools import simulateFlat file1 = 'test_flat1.fits' file2 = 'test_flat2.fits' hdus = 2 simulateFlat(file1, 4000, 5, hdus=hdus) simulateFlat(file2, 4000, 5, hdus=hdus) for amp in range(1, hdus + 1): image1 = afwImage.ImageF(file1, imutils.dm_hdu(amp)) image2 = afwImage.ImageF(file2, imutils.dm_hdu(amp)) gain, im1, im2 = flat_gain(image1, image2, count=1000) print(amp, gain) os.remove(file1) os.remove(file2)
for x in (0, width // 2, width): for y in (0, height // 2, height): im = afwImage.ImageD(spatialKernel.getDimensions()) ksum = spatialKernel.computeImage(im, False, afwImage.indexToPosition(x), afwImage.indexToPosition(y)) mos.append(im, "x=%d y=%d kSum=%.2f" % (x, y, ksum)) mosaic = mos.makeMosaic() frame += 1 ds9.mtv(mosaic, frame=frame, title="Spatial Kernels") mos.drawLabels(frame=frame) # Background backgroundIm = afwImage.ImageF( afwGeom.Extent2I(templateExposure.getWidth(), templateExposure.getHeight()), 0) backgroundIm += spatialBg frame += 1 ds9.mtv(backgroundIm, frame=frame, title="Background model") # Diffim! diffIm = ipDiffim.convolveAndSubtract(templateExposure.getMaskedImage(), scienceExposure.getMaskedImage(), spatialKernel, spatialBg) frame += 1 ds9.mtv(diffIm, frame=frame, title="Diffim") # examples/runSpatialModel.py $AFWDATA_DIR/DC3a-Sim/sci/v5-e0/v5-e0-c011-a00.sci # ... $AFWDATA_DIR/DC3a-Sim/sci/v26-e0/v26-e0-c011-a00.sci
def testMtv(self): """Test basic image display""" exp = afwImage.ImageF(10, 20) ds9.mtv(exp, title="parent")
def compareToSwarp(self, kernelName, useWarpExposure=True, useSubregion=False, useDeepCopy=False, interpLength=10, cacheSize=100000, rtol=4e-05, atol=1e-2): """Compare warpExposure to swarp for given warping kernel. Note that swarp only warps the image plane, so only test that plane. Inputs: - kernelName: name of kernel in the form used by afwImage.makeKernel - useWarpExposure: if True, call warpExposure to warp an ExposureF, else call warpImage to warp an ImageF and also call the Transform version - useSubregion: if True then the original source exposure (from which the usual test exposure was extracted) is read and the correct subregion extracted - useDeepCopy: if True then the copy of the subimage is a deep copy, else it is a shallow copy; ignored if useSubregion is False - interpLength: interpLength argument for lsst.afw.math.WarpingControl - cacheSize: cacheSize argument for lsst.afw.math.WarpingControl; 0 disables the cache 10000 gives some speed improvement but less accurate results (atol must be increased) 100000 gives better accuracy but no speed improvement in this test - rtol: relative tolerance as used by np.allclose - atol: absolute tolerance as used by np.allclose """ warpingControl = afwMath.WarpingControl( kernelName, "", # there is no point to a separate mask kernel since we aren't testing the mask plane cacheSize, interpLength, ) if useSubregion: originalFullExposure = afwImage.ExposureF(originalExposurePath) # "medsub" is a subregion of med starting at 0-indexed pixel (40, 150) of size 145 x 200 bbox = lsst.geom.Box2I(lsst.geom.Point2I(40, 150), lsst.geom.Extent2I(145, 200)) originalExposure = afwImage.ExposureF(originalFullExposure, bbox, afwImage.LOCAL, useDeepCopy) swarpedImageName = f"medsubswarp1{kernelName}.fits" else: originalExposure = afwImage.ExposureF(originalExposurePath) swarpedImageName = f"medswarp1{kernelName}.fits" swarpedImagePath = os.path.join(dataDir, swarpedImageName) swarpedDecoratedImage = afwImage.DecoratedImageF(swarpedImagePath) swarpedImage = swarpedDecoratedImage.getImage() swarpedMetadata = swarpedDecoratedImage.getMetadata() warpedWcs = afwGeom.makeSkyWcs(swarpedMetadata) if useWarpExposure: # path for saved afw-warped image afwWarpedImagePath = f"afwWarpedExposure1{kernelName}.fits" afwWarpedMaskedImage = afwImage.MaskedImageF( swarpedImage.getDimensions()) afwWarpedExposure = afwImage.ExposureF(afwWarpedMaskedImage, warpedWcs) afwMath.warpExposure(afwWarpedExposure, originalExposure, warpingControl) afwWarpedMask = afwWarpedMaskedImage.getMask() if SAVE_FITS_FILES: afwWarpedExposure.writeFits(afwWarpedImagePath) if display: afwDisplay.Display(frame=1).mtv(afwWarpedExposure, title="Warped") swarpedMaskedImage = afwImage.MaskedImageF(swarpedImage) if display: afwDisplay.Display(frame=2).mtv(swarpedMaskedImage, title="SWarped") msg = f"afw and swarp {kernelName}-warped differ (ignoring bad pixels)" try: self.assertMaskedImagesAlmostEqual(afwWarpedMaskedImage, swarpedMaskedImage, doImage=True, doMask=False, doVariance=False, skipMask=afwWarpedMask, rtol=rtol, atol=atol, msg=msg) except Exception: if SAVE_FAILED_FITS_FILES: afwWarpedExposure.writeFits(afwWarpedImagePath) print( f"Saved failed afw-warped exposure as: {afwWarpedImagePath}" ) raise else: # path for saved afw-warped image afwWarpedImagePath = f"afwWarpedImage1{kernelName}.fits" afwWarpedImage2Path = f"afwWarpedImage1{kernelName}_xyTransform.fits" afwWarpedImage = afwImage.ImageF(swarpedImage.getDimensions()) originalImage = originalExposure.getMaskedImage().getImage() originalWcs = originalExposure.getWcs() afwMath.warpImage(afwWarpedImage, warpedWcs, originalImage, originalWcs, warpingControl) if display: afwDisplay.Display(frame=1).mtv(afwWarpedImage, title="Warped") afwDisplay.Display(frame=2).mtv(swarpedImage, title="SWarped") diff = swarpedImage.Factory(swarpedImage, True) diff -= afwWarpedImage afwDisplay.Display(frame=3).mtv(diff, title="swarp - afw") if SAVE_FITS_FILES: afwWarpedImage.writeFits(afwWarpedImagePath) afwWarpedImageArr = afwWarpedImage.getArray() noDataMaskArr = np.isnan(afwWarpedImageArr) msg = f"afw and swarp {kernelName}-warped images do not match (ignoring NaN pixels)" try: self.assertImagesAlmostEqual(afwWarpedImage, swarpedImage, skipMask=noDataMaskArr, rtol=rtol, atol=atol, msg=msg) except Exception: if SAVE_FAILED_FITS_FILES: # save the image anyway afwWarpedImage.writeFits(afwWarpedImagePath) print( f"Saved failed afw-warped image as: {afwWarpedImagePath}" ) raise afwWarpedImage2 = afwImage.ImageF(swarpedImage.getDimensions()) srcToDest = afwGeom.makeWcsPairTransform(originalWcs, warpedWcs) afwMath.warpImage(afwWarpedImage2, originalImage, srcToDest, warpingControl) msg = f"afw transform-based and WCS-based {kernelName}-warped images do not match" try: self.assertImagesAlmostEqual(afwWarpedImage2, afwWarpedImage, rtol=rtol, atol=atol, msg=msg) except Exception: if SAVE_FAILED_FITS_FILES: # save the image anyway afwWarpedImage.writeFits(afwWarpedImage2) print( f"Saved failed afw-warped image as: {afwWarpedImage2Path}" ) raise
# We need to fit for a TAN-SIP x = np.arange(0, 1489 + stepSize, stepSize) y = np.arange(0, 2048 + stepSize, stepSize) coords = np.meshgrid(x, y) xs = np.ravel(coords[0]).astype(np.float) ys = np.ravel(coords[1]).astype(np.float) mapper = CoordinateMapper(node_rad, incl_rad, dRow0, dRow1, dRow2, dRow3, dCol0, dCol1, dCol2, dCol3, a, b, c, d, e, f) wcs = createWcs(xs, ys, mapper) if doValidate: validate(xs, ys, mapper, wcs) return wcs if __name__ == '__main__': infile = sys.argv[1] filt = sys.argv[2] camcol = int(sys.argv[3]) field = int(sys.argv[4]) wcs = convertasTrans(infile, filt, camcol, field, doValidate=True) if len(sys.argv) > 5: fpC = sys.argv[5] image = afwImage.ImageF(fpC) mi = afwImage.MaskedImageF(image) exp = afwImage.ExposureF(mi, wcs) exp.writeFits("/tmp/exp.fits")
def setUp(self): self.binsizes = range(1, 10) imsize = lcm(*self.binsizes) self.input_image = afwImage.ImageF(imsize, imsize) self.input_image += 1