23 Insert fakes into deepCoadds 26 from astropy.table
import Table
31 import lsst.pex.config
as pexConfig
34 from lsst.pipe.base import CmdLineTask, PipelineTask, PipelineTaskConfig, PipelineTaskConnections
38 from lsst.geom import SpherePoint, radians, Box2D
41 __all__ = [
"InsertFakesConfig",
"InsertFakesTask"]
45 dimensions=(
"tract",
"patch",
"abstract_filter",
"skymap")):
48 doc=
"Image into which fakes are to be added.",
49 name=
"{CoaddName}Coadd",
50 storageClass=
"ExposureF",
51 dimensions=(
"tract",
"patch",
"abstract_filter",
"skymap")
55 doc=
"Catalog of fake sources to draw inputs from.",
56 name=
"{CoaddName}Coadd_fakeSourceCat",
57 storageClass=
"Parquet",
58 dimensions=(
"tract",
"skymap")
61 imageWithFakes = cT.Output(
62 doc=
"Image with fake sources added.",
63 name=
"fakes_{CoaddName}Coadd",
64 storageClass=
"ExposureF",
65 dimensions=(
"tract",
"patch",
"abstract_filter",
"skymap")
70 pipelineConnections=InsertFakesConnections):
71 """Config for inserting fake sources 75 The default column names are those from the University of Washington sims database. 78 raColName = pexConfig.Field(
79 doc=
"RA column name used in the fake source catalog.",
84 decColName = pexConfig.Field(
85 doc=
"Dec. column name used in the fake source catalog.",
90 doCleanCat = pexConfig.Field(
91 doc=
"If true removes bad sources from the catalog.",
96 diskHLR = pexConfig.Field(
97 doc=
"Column name for the disk half light radius used in the fake source catalog.",
99 default=
"DiskHalfLightRadius",
102 bulgeHLR = pexConfig.Field(
103 doc=
"Column name for the bulge half light radius used in the fake source catalog.",
105 default=
"BulgeHalfLightRadius",
108 magVar = pexConfig.Field(
109 doc=
"The column name for the magnitude calculated taking variability into account. In the format " 110 "``filter name``magVar, e.g. imagVar for the magnitude in the i band.",
115 nDisk = pexConfig.Field(
116 doc=
"The column name for the sersic index of the disk component used in the fake source catalog.",
121 nBulge = pexConfig.Field(
122 doc=
"The column name for the sersic index of the bulge component used in the fake source catalog.",
127 aDisk = pexConfig.Field(
128 doc=
"The column name for the semi major axis length of the disk component used in the fake source" 134 aBulge = pexConfig.Field(
135 doc=
"The column name for the semi major axis length of the bulge component.",
140 bDisk = pexConfig.Field(
141 doc=
"The column name for the semi minor axis length of the disk component.",
146 bBulge = pexConfig.Field(
147 doc=
"The column name for the semi minor axis length of the bulge component used in the fake source " 153 paDisk = pexConfig.Field(
154 doc=
"The column name for the PA of the disk component used in the fake source catalog.",
159 paBulge = pexConfig.Field(
160 doc=
"The column name for the PA of the bulge component used in the fake source catalog.",
165 fakeType = pexConfig.Field(
166 doc=
"What type of fake catalog to use, snapshot (includes variability in the magnitudes calculated " 167 "from the MJD of the image), static (no variability) or filename for a user defined fits" 173 calibFluxRadius = pexConfig.Field(
174 doc=
"Radius for the calib flux (in pixels).",
179 coaddName = pexConfig.Field(
180 doc=
"The name of the type of coadd used",
187 """Insert fake objects into images. 189 Add fake stars and galaxies to the given image, read in through the dataRef. Galaxy parameters are read in 190 from the specified file and then modelled using galsim. 192 `InsertFakesTask` has five functions that make images of the fake sources and then add them to the 196 Use the WCS information to add the pixel coordinates of each source. 197 `mkFakeGalsimGalaxies` 198 Use Galsim to make fake double sersic galaxies for each set of galaxy parameters in the input file. 200 Use the PSF information from the image to make a fake star using the magnitude information from the 203 Remove rows of the input fake catalog which have half light radius, of either the bulge or the disk, 206 Add the fake sources to the image. 210 _DefaultName =
"insertFakes" 211 ConfigClass = InsertFakesConfig
213 def runDataRef(self, dataRef):
214 """Read in/write out the required data products and add fake sources to the deepCoadd. 218 dataRef : `lsst.daf.persistence.butlerSubset.ButlerDataRef` 219 Data reference defining the image to have fakes added to it 220 Used to access the following data products: 226 if self.
config.fakeType ==
"static":
227 fakeCat = dataRef.get(
"deepCoadd_fakeSourceCat").toDataFrame()
230 self.fakeSourceCatType =
"deepCoadd_fakeSourceCat" 232 fakeCat = Table.read(self.
config.fakeType).to_pandas()
234 coadd = dataRef.get(
"deepCoadd")
236 photoCalib = coadd.getPhotoCalib()
238 imageWithFakes = self.run(fakeCat, coadd, wcs, photoCalib)
240 dataRef.put(imageWithFakes.imageWithFakes,
"fakes_deepCoadd")
242 def runQuantum(self, butlerQC, inputRefs, outputRefs):
243 inputs = butlerQC.get(inputRefs)
244 inputs[
"wcs"] = inputs[
"image"].getWcs()
245 inputs[
"photoCalib"] = inputs[
"image"].getPhotoCalib()
247 outputs = self.run(**inputs)
248 butlerQC.put(outputs, outputRefs)
251 def _makeArgumentParser(cls):
252 parser = pipeBase.ArgumentParser(name=cls._DefaultName)
253 parser.add_id_argument(name=
"--id", datasetType=
"deepCoadd",
254 help=
"data IDs for the deepCoadd, e.g. --id tract=12345 patch=1,2 filter=r",
255 ContainerClass=ExistingCoaddDataIdContainer)
258 def run(self, fakeCat, image, wcs, photoCalib):
259 """Add fake sources to an image. 263 fakeCat : `pandas.core.frame.DataFrame` 264 The catalog of fake sources to be input 265 image : `lsst.afw.image.exposure.exposure.ExposureF` 266 The image into which the fake sources should be added 267 wcs : `lsst.afw.geom.SkyWcs` 268 WCS to use to add fake sources 269 photoCalib : `lsst.afw.image.photoCalib.PhotoCalib` 270 Photometric calibration to be used to calibrate the fake sources 274 resultStruct : `lsst.pipe.base.struct.Struct` 275 contains : image : `lsst.afw.image.exposure.exposure.ExposureF` 279 Adds pixel coordinates for each source to the fakeCat and removes objects with bulge or disk half 280 light radius = 0 (if ``config.doCleanCat = True``). 282 Adds the ``Fake`` mask plane to the image which is then set by `addFakeSources` to mark where fake 283 sources have been added. Uses the information in the ``fakeCat`` to make fake galaxies (using galsim) 284 and fake stars, using the PSF models from the PSF information for the image. These are then added to 285 the image and the image with fakes included returned. 287 The galsim galaxies are made using a double sersic profile, one for the bulge and one for the disk, 288 this is then convolved with the PSF at that point. 291 image.mask.addMaskPlane(
"FAKE")
292 self.bitmask = image.mask.getPlaneBitMask(
"FAKE")
293 self.log.
info(
"Adding mask plane with bitmask %d" % self.bitmask)
295 fakeCat = self.addPixCoords(fakeCat, wcs)
296 if self.config.doCleanCat:
297 fakeCat = self.cleanCat(fakeCat)
298 fakeCat = self.trimFakeCat(fakeCat, image, wcs)
300 band = image.getFilter().getName()
301 pixelScale = wcs.getPixelScale().asArcseconds()
304 galaxies = (fakeCat[
"sourceType"] ==
"galaxy")
305 galImages = self.mkFakeGalsimGalaxies(fakeCat[galaxies], band, photoCalib, pixelScale, psf, image)
306 image = self.addFakeSources(image, galImages,
"galaxy")
308 stars = (fakeCat[
"sourceType"] ==
"star")
309 starImages = self.mkFakeStars(fakeCat[stars], band, photoCalib, psf, image)
310 image = self.addFakeSources(image, starImages,
"star")
311 resultStruct = pipeBase.Struct(imageWithFakes=image)
315 def addPixCoords(self, fakeCat, wcs):
317 """Add pixel coordinates to the catalog of fakes. 321 fakeCat : `pandas.core.frame.DataFrame` 322 The catalog of fake sources to be input 323 wcs : `lsst.afw.geom.SkyWcs` 324 WCS to use to add fake sources 328 fakeCat : `pandas.core.frame.DataFrame` 332 The default option is to use the WCS information from the image. If the ``useUpdatedCalibs`` config 333 option is set then it will use the updated WCS from jointCal. 336 ras = fakeCat[self.config.raColName].values
337 decs = fakeCat[self.config.decColName].values
338 skyCoords = [
SpherePoint(ra, dec, radians)
for (ra, dec)
in zip(ras, decs)]
339 pixCoords = wcs.skyToPixel(skyCoords)
340 xs = [coord.getX()
for coord
in pixCoords]
341 ys = [coord.getY()
for coord
in pixCoords]
347 def trimFakeCat(self, fakeCat, image, wcs):
348 """Trim the fake cat to about the size of the input image. 352 fakeCat : `pandas.core.frame.DataFrame` 353 The catalog of fake sources to be input 354 image : `lsst.afw.image.exposure.exposure.ExposureF` 355 The image into which the fake sources should be added 356 wcs : `lsst.afw.geom.SkyWcs` 357 WCS to use to add fake sources 361 fakeCat : `pandas.core.frame.DataFrame` 362 The original fakeCat trimmed to the area of the image 365 bbox =
Box2D(image.getBBox())
366 corners = bbox.getCorners()
368 skyCorners = wcs.pixelToSky(corners)
372 coord =
SpherePoint(row[self.config.raColName], row[self.config.decColName], radians)
373 return region.contains(coord.getVector())
375 return fakeCat[fakeCat.apply(trim, axis=1)]
377 def mkFakeGalsimGalaxies(self, fakeCat, band, photoCalib, pixelScale, psf, image):
378 """Make images of fake galaxies using GalSim. 384 psf : `lsst.meas.extensions.psfex.psfexPsf.PsfexPsf` 385 The PSF information to use to make the PSF images 386 fakeCat : `pandas.core.frame.DataFrame` 387 The catalog of fake sources to be input 388 photoCalib : `lsst.afw.image.photoCalib.PhotoCalib` 389 Photometric calibration to be used to calibrate the fake sources 393 galImages : `generator` 394 A generator of tuples of `lsst.afw.image.exposure.exposure.ExposureF` and 395 `lsst.geom.Point2D` of their locations. 400 Fake galaxies are made by combining two sersic profiles, one for the bulge and one for the disk. Each 401 component has an individual sersic index (n), a, b and position angle (PA). The combined profile is 402 then convolved with the PSF at the specified x, y position on the image. 404 The names of the columns in the ``fakeCat`` are configurable and are the column names from the 405 University of Washington simulations database as default. For more information see the doc strings 406 attached to the config options. 409 self.log.
info(
"Making %d fake galaxy images" % len(fakeCat))
411 for (index, row)
in fakeCat.iterrows():
417 correctedFlux = psf.computeApertureFlux(self.config.calibFluxRadius, xy)
418 psfKernel = psf.computeKernelImage(xy).getArray()
419 psfKernel /= correctedFlux
421 except InvalidParameterError:
422 self.log.
info(
"Galaxy at %0.4f, %0.4f outside of image" % (row[
"x"], row[
"y"]))
426 flux = photoCalib.magnitudeToInstFlux(row[self.config.magVar % band], xy)
430 bulge = galsim.Sersic(row[self.config.nBulge], half_light_radius=row[self.config.bulgeHLR])
431 axisRatioBulge = row[self.config.bBulge]/row[self.config.aBulge]
432 bulge = bulge.shear(q=axisRatioBulge, beta=((90 - row[self.config.paBulge])*galsim.degrees))
434 disk = galsim.Sersic(row[self.config.nDisk], half_light_radius=row[self.config.diskHLR])
435 axisRatioDisk = row[self.config.bDisk]/row[self.config.aDisk]
436 disk = disk.shear(q=axisRatioDisk, beta=((90 - row[self.config.paDisk])*galsim.degrees))
439 gal = gal.withFlux(flux)
441 psfIm = galsim.InterpolatedImage(galsim.Image(psfKernel), scale=pixelScale)
442 gal = galsim.Convolve([gal, psfIm])
444 galIm = gal.drawImage(scale=pixelScale, method=
"real_space").array
445 except (galsim.errors.GalSimFFTSizeError, MemoryError):
448 yield (afwImage.ImageF(galIm), xy)
450 def mkFakeStars(self, fakeCat, band, photoCalib, psf, image):
452 """Make fake stars based off the properties in the fakeCat. 457 psf : `lsst.meas.extensions.psfex.psfexPsf.PsfexPsf` 458 The PSF information to use to make the PSF images 459 fakeCat : `pandas.core.frame.DataFrame` 460 The catalog of fake sources to be input 461 image : `lsst.afw.image.exposure.exposure.ExposureF` 462 The image into which the fake sources should be added 463 photoCalib : `lsst.afw.image.photoCalib.PhotoCalib` 464 Photometric calibration to be used to calibrate the fake sources 468 starImages : `generator` 469 A generator of tuples of `lsst.afw.image.ImageF` of fake stars and 470 `lsst.geom.Point2D` of their locations. 473 self.log.
info(
"Making %d fake star images" % len(fakeCat))
475 for (index, row)
in fakeCat.iterrows():
481 correctedFlux = psf.computeApertureFlux(self.config.calibFluxRadius, xy)
482 starIm = psf.computeImage(xy)
483 starIm /= correctedFlux
485 except InvalidParameterError:
486 self.log.
info(
"Star at %0.4f, %0.4f outside of image" % (row[
"x"], row[
"y"]))
490 flux = photoCalib.magnitudeToInstFlux(row[band +
"magVar"], xy)
495 yield ((starIm.convertF(), xy))
497 def cleanCat(self, fakeCat):
498 """Remove rows from the fakes catalog which have HLR = 0 for either the buldge or disk component 502 fakeCat : `pandas.core.frame.DataFrame` 503 The catalog of fake sources to be input 507 fakeCat : `pandas.core.frame.DataFrame` 508 The input catalog of fake sources but with the bad objects removed 511 goodRows = ((fakeCat[self.config.bulgeHLR] != 0.0) & (fakeCat[self.config.diskHLR] != 0.0))
513 badRows = len(fakeCat) - len(goodRows)
514 self.log.
info(
"Removing %d rows with HLR = 0 for either the bulge or disk" % badRows)
516 return fakeCat[goodRows]
518 def addFakeSources(self, image, fakeImages, sourceType):
519 """Add the fake sources to the given image 523 image : `lsst.afw.image.exposure.exposure.ExposureF` 524 The image into which the fake sources should be added 525 fakeImages : `typing.Iterator` [`tuple` ['lsst.afw.image.ImageF`, `lsst.geom.Point2d`]] 526 An iterator of tuples that contains (or generates) images of fake sources, 527 and the locations they are to be inserted at. 529 The type (star/galaxy) of fake sources input 533 image : `lsst.afw.image.exposure.exposure.ExposureF` 537 Uses the x, y information in the ``fakeCat`` to position an image of the fake interpolated onto the 538 pixel grid of the image. Sets the ``FAKE`` mask plane for the pixels added with the fake source. 541 imageBBox = image.getBBox()
542 imageMI = image.maskedImage
544 for (fakeImage, xy)
in fakeImages:
545 X0 = xy.getX() - fakeImage.getWidth()/2 + 0.5
546 Y0 = xy.getY() - fakeImage.getHeight()/2 + 0.5
547 self.log.
debug(
"Adding fake source at %d, %d" % (xy.getX(), xy.getY()))
548 if sourceType ==
"galaxy":
550 interpFakeImBBox = interpFakeImage.getBBox()
552 interpFakeImage = fakeImage
553 interpFakeImBBox = fakeImage.getBBox()
555 interpFakeImBBox.clip(imageBBox)
556 imageMIView = imageMI.Factory(imageMI, interpFakeImBBox)
558 if interpFakeImBBox.getArea() > 0:
559 clippedFakeImage = interpFakeImage.Factory(interpFakeImage, interpFakeImBBox)
560 clippedFakeImageMI = afwImage.MaskedImageF(clippedFakeImage)
561 clippedFakeImageMI.mask.set(self.bitmask)
562 imageMIView += clippedFakeImageMI
A floating-point coordinate rectangle geometry.
ConvexPolygon is a closed convex polygon on the unit sphere.
def run(self, skyInfo, tempExpRefList, imageScalerList, weightList, altMaskList=None, mask=None, supplementaryData=None)
Point in an unspecified spherical coordinate system.
std::shared_ptr< ImageT > offsetImage(ImageT const &image, float dx, float dy, std::string const &algorithmName="lanczos5", unsigned int buffer=0)
Return an image offset by (dx, dy) using the specified algorithm.
Backwards-compatibility support for depersisting the old Calib (FluxMag0/FluxMag0Err) objects...