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
35 import lsst.pipe.base.connectionTyes
as cT
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 nameTemplate=
"{CoaddName}Coadd",
50 storageClass=
"ExposureF",
51 dimensions=(
"tract",
"patch",
"abstract_filter",
"skymap")
55 doc=
"Catalog of fake sources to draw inputs from.",
56 nameTemplate=
"{CoaddName}Coadd_fakeSourceCat",
57 storageClass=
"Parquet",
58 dimensions=(
"tract",
"skymap")
61 imageWithFakes = cT.Output(
62 doc=
"Image with fake sources added.",
63 nameTemplate=
"fakes_{CoaddName}Coadd",
64 storageClass=
"ExposureF",
65 dimensions=(
"tract",
"patch",
"abstract_filter",
"skymap")
70 """Config for inserting fake sources 74 The default column names are those from the University of Washington sims database. 77 raColName = pexConfig.Field(
78 doc=
"RA column name used in the fake source catalog.",
83 decColName = pexConfig.Field(
84 doc=
"Dec. column name used in the fake source catalog.",
89 doCleanCat = pexConfig.Field(
90 doc=
"If true removes bad sources from the catalog.",
95 diskHLR = pexConfig.Field(
96 doc=
"Column name for the disk half light radius used in the fake source catalog.",
98 default=
"DiskHalfLightRadius",
101 bulgeHLR = pexConfig.Field(
102 doc=
"Column name for the bulge half light radius used in the fake source catalog.",
104 default=
"BulgeHalfLightRadius",
107 magVar = pexConfig.Field(
108 doc=
"The column name for the magnitude calculated taking variability into account. In the format " 109 "``filter name``magVar, e.g. imagVar for the magnitude in the i band.",
114 nDisk = pexConfig.Field(
115 doc=
"The column name for the sersic index of the disk component used in the fake source catalog.",
120 nBulge = pexConfig.Field(
121 doc=
"The column name for the sersic index of the bulge component used in the fake source catalog.",
126 aDisk = pexConfig.Field(
127 doc=
"The column name for the semi major axis length of the disk component used in the fake source" 133 aBulge = pexConfig.Field(
134 doc=
"The column name for the semi major axis length of the bulge component.",
139 bDisk = pexConfig.Field(
140 doc=
"The column name for the semi minor axis length of the disk component.",
145 bBulge = pexConfig.Field(
146 doc=
"The column name for the semi minor axis length of the bulge component used in the fake source " 152 paDisk = pexConfig.Field(
153 doc=
"The column name for the PA of the disk component used in the fake source catalog.",
158 paBulge = pexConfig.Field(
159 doc=
"The column name for the PA of the bulge component used in the fake source catalog.",
164 fakeType = pexConfig.Field(
165 doc=
"What type of fake catalog to use, snapshot (includes variability in the magnitudes calculated " 166 "from the MJD of the image), static (no variability) or filename for a user defined fits" 172 calibFluxRadius = pexConfig.Field(
173 doc=
"Radius for the calib flux (in pixels).",
178 coaddName = pexConfig.Field(
179 doc=
"The name of the type of coadd used",
186 """Insert fake objects into images. 188 Add fake stars and galaxies to the given image, read in through the dataRef. Galaxy parameters are read in 189 from the specified file and then modelled using galsim. 191 `InsertFakesTask` has five functions that make images of the fake sources and then add them to the 195 Use the WCS information to add the pixel coordinates of each source. 196 `mkFakeGalsimGalaxies` 197 Use Galsim to make fake double sersic galaxies for each set of galaxy parameters in the input file. 199 Use the PSF information from the image to make a fake star using the magnitude information from the 202 Remove rows of the input fake catalog which have half light radius, of either the bulge or the disk, 205 Add the fake sources to the image. 209 _DefaultName =
"insertFakes" 210 ConfigClass = InsertFakesConfig
212 def runDataRef(self, dataRef):
213 """Read in/write out the required data products and add fake sources to the deepCoadd. 217 dataRef : `lsst.daf.persistence.butlerSubset.ButlerDataRef` 218 Data reference defining the image to have fakes added to it 219 Used to access the following data products: 225 if self.
config.fakeType ==
"static":
226 fakeCat = dataRef.get(
"deepCoadd_fakeSourceCat").toDataFrame()
229 self.fakeSourceCatType =
"deepCoadd_fakeSourceCat" 231 fakeCat = Table.read(self.
config.fakeType).to_pandas()
233 coadd = dataRef.get(
"deepCoadd")
235 photoCalib = coadd.getPhotoCalib()
237 imageWithFakes = self.run(fakeCat, coadd, wcs, photoCalib)
239 dataRef.put(imageWithFakes.imageWithFakes,
"fakes_deepCoadd")
241 def runQuantum(self, butlerQC, inputRefs, outputRefs):
242 inputs = butlerQC.get(inputRefs)
243 inputs[
"wcs"] = inputs[
"image"].getWcs()
244 inputs[
"photoCalib"] = inputs[
"image"].getPhotoCalib()
246 outputs = self.run(**inputs)
247 butlerQC.put(outputs, outputRefs)
250 def _makeArgumentParser(cls):
251 parser = pipeBase.ArgumentParser(name=cls._DefaultName)
252 parser.add_id_argument(name=
"--id", datasetType=
"deepCoadd",
253 help=
"data IDs for the deepCoadd, e.g. --id tract=12345 patch=1,2 filter=r",
254 ContainerClass=ExistingCoaddDataIdContainer)
257 def run(self, fakeCat, image, wcs, photoCalib):
258 """Add fake sources to an image. 262 fakeCat : `pandas.core.frame.DataFrame` 263 The catalog of fake sources to be input 264 image : `lsst.afw.image.exposure.exposure.ExposureF` 265 The image into which the fake sources should be added 266 wcs : `lsst.afw.geom.SkyWcs` 267 WCS to use to add fake sources 268 photoCalib : `lsst.afw.image.photoCalib.PhotoCalib` 269 Photometric calibration to be used to calibrate the fake sources 273 resultStruct : `lsst.pipe.base.struct.Struct` 274 contains : image : `lsst.afw.image.exposure.exposure.ExposureF` 278 Adds pixel coordinates for each source to the fakeCat and removes objects with bulge or disk half 279 light radius = 0 (if ``config.doCleanCat = True``). 281 Adds the ``Fake`` mask plane to the image which is then set by `addFakeSources` to mark where fake 282 sources have been added. Uses the information in the ``fakeCat`` to make fake galaxies (using galsim) 283 and fake stars, using the PSF models from the PSF information for the image. These are then added to 284 the image and the image with fakes included returned. 286 The galsim galaxies are made using a double sersic profile, one for the bulge and one for the disk, 287 this is then convolved with the PSF at that point. 290 image.mask.addMaskPlane(
"FAKE")
291 self.bitmask = image.mask.getPlaneBitMask(
"FAKE")
292 self.log.
info(
"Adding mask plane with bitmask %d" % self.bitmask)
294 fakeCat = self.addPixCoords(fakeCat, wcs)
295 if self.config.doCleanCat:
296 fakeCat = self.cleanCat(fakeCat)
297 fakeCat = self.trimFakeCat(fakeCat, image, wcs)
299 band = image.getFilter().getName()
300 pixelScale = wcs.getPixelScale().asArcseconds()
303 galaxies = (fakeCat[
"sourceType"] ==
"galaxy")
304 galImages = self.mkFakeGalsimGalaxies(fakeCat[galaxies], band, photoCalib, pixelScale, psf, image)
305 image = self.addFakeSources(image, galImages,
"galaxy")
307 stars = (fakeCat[
"sourceType"] ==
"star")
308 starImages = self.mkFakeStars(fakeCat[stars], band, photoCalib, psf, image)
309 image = self.addFakeSources(image, starImages,
"star")
310 resultStruct = pipeBase.Struct(imageWithFakes=image)
314 def addPixCoords(self, fakeCat, wcs):
316 """Add pixel coordinates to the catalog of fakes. 320 fakeCat : `pandas.core.frame.DataFrame` 321 The catalog of fake sources to be input 322 wcs : `lsst.afw.geom.SkyWcs` 323 WCS to use to add fake sources 327 fakeCat : `pandas.core.frame.DataFrame` 331 The default option is to use the WCS information from the image. If the ``useUpdatedCalibs`` config 332 option is set then it will use the updated WCS from jointCal. 335 ras = fakeCat[self.config.raColName].values
336 decs = fakeCat[self.config.decColName].values
337 skyCoords = [
SpherePoint(ra, dec, radians)
for (ra, dec)
in zip(ras, decs)]
338 pixCoords = wcs.skyToPixel(skyCoords)
339 xs = [coord.getX()
for coord
in pixCoords]
340 ys = [coord.getY()
for coord
in pixCoords]
346 def trimFakeCat(self, fakeCat, image, wcs):
347 """Trim the fake cat to about the size of the input image. 351 fakeCat : `pandas.core.frame.DataFrame` 352 The catalog of fake sources to be input 353 image : `lsst.afw.image.exposure.exposure.ExposureF` 354 The image into which the fake sources should be added 355 wcs : `lsst.afw.geom.SkyWcs` 356 WCS to use to add fake sources 360 fakeCat : `pandas.core.frame.DataFrame` 361 The original fakeCat trimmed to the area of the image 364 bbox =
Box2D(image.getBBox())
365 corners = bbox.getCorners()
367 skyCorners = wcs.pixelToSky(corners)
371 coord =
SpherePoint(row[self.config.raColName], row[self.config.decColName], radians)
372 return region.contains(coord.getVector())
374 return fakeCat[fakeCat.apply(trim, axis=1)]
376 def mkFakeGalsimGalaxies(self, fakeCat, band, photoCalib, pixelScale, psf, image):
377 """Make images of fake galaxies using GalSim. 383 psf : `lsst.meas.extensions.psfex.psfexPsf.PsfexPsf` 384 The PSF information to use to make the PSF images 385 fakeCat : `pandas.core.frame.DataFrame` 386 The catalog of fake sources to be input 387 photoCalib : `lsst.afw.image.photoCalib.PhotoCalib` 388 Photometric calibration to be used to calibrate the fake sources 393 A list of tuples of `lsst.afw.image.exposure.exposure.ExposureF` and 394 `lsst.geom.Point2D` of their locations. 399 Fake galaxies are made by combining two sersic profiles, one for the bulge and one for the disk. Each 400 component has an individual sersic index (n), a, b and position angle (PA). The combined profile is 401 then convolved with the PSF at the specified x, y position on the image. 403 The names of the columns in the ``fakeCat`` are configurable and are the column names from the 404 University of Washington simulations database as default. For more information see the doc strings 405 attached to the config options. 410 self.log.
info(
"Making %d fake galaxy images" % len(fakeCat))
412 for (index, row)
in fakeCat.iterrows():
418 correctedFlux = psf.computeApertureFlux(self.config.calibFluxRadius, xy)
419 psfKernel = psf.computeKernelImage(xy).getArray()
420 psfKernel /= correctedFlux
422 except InvalidParameterError:
423 self.log.
info(
"Galaxy at %0.4f, %0.4f outside of image" % (row[
"x"], row[
"y"]))
427 flux = photoCalib.magnitudeToInstFlux(row[self.config.magVar % band], xy)
431 bulge = galsim.Sersic(row[self.config.nBulge], half_light_radius=row[self.config.bulgeHLR])
432 axisRatioBulge = row[self.config.bBulge]/row[self.config.aBulge]
433 bulge = bulge.shear(q=axisRatioBulge, beta=((90 - row[self.config.paBulge])*galsim.degrees))
435 disk = galsim.Sersic(row[self.config.nDisk], half_light_radius=row[self.config.diskHLR])
436 axisRatioDisk = row[self.config.bDisk]/row[self.config.aDisk]
437 disk = disk.shear(q=axisRatioDisk, beta=((90 - row[self.config.paDisk])*galsim.degrees))
440 gal = gal.withFlux(flux)
442 psfIm = galsim.InterpolatedImage(galsim.Image(psfKernel), scale=pixelScale)
443 gal = galsim.Convolve([gal, psfIm])
445 galIm = gal.drawImage(scale=pixelScale, method=
"real_space").array
446 except (galsim.errors.GalSimFFTSizeError, MemoryError):
449 galImages.append((afwImage.ImageF(galIm), xy))
453 def mkFakeStars(self, fakeCat, band, photoCalib, psf, image):
455 """Make fake stars based off the properties in the fakeCat. 460 psf : `lsst.meas.extensions.psfex.psfexPsf.PsfexPsf` 461 The PSF information to use to make the PSF images 462 fakeCat : `pandas.core.frame.DataFrame` 463 The catalog of fake sources to be input 464 image : `lsst.afw.image.exposure.exposure.ExposureF` 465 The image into which the fake sources should be added 466 photoCalib : `lsst.afw.image.photoCalib.PhotoCalib` 467 Photometric calibration to be used to calibrate the fake sources 472 A list of tuples of `lsst.afw.image.ImageF` of fake stars and 473 `lsst.geom.Point2D` of their locations. 478 self.log.
info(
"Making %d fake star images" % len(fakeCat))
480 for (index, row)
in fakeCat.iterrows():
486 correctedFlux = psf.computeApertureFlux(self.config.calibFluxRadius, xy)
487 starIm = psf.computeImage(xy)
488 starIm /= correctedFlux
490 except InvalidParameterError:
491 self.log.
info(
"Star at %0.4f, %0.4f outside of image" % (row[
"x"], row[
"y"]))
495 flux = photoCalib.magnitudeToInstFlux(row[band +
"magVar"], xy)
500 starImages.append((starIm.convertF(), xy))
504 def cleanCat(self, fakeCat):
505 """Remove rows from the fakes catalog which have HLR = 0 for either the buldge or disk component 509 fakeCat : `pandas.core.frame.DataFrame` 510 The catalog of fake sources to be input 514 fakeCat : `pandas.core.frame.DataFrame` 515 The input catalog of fake sources but with the bad objects removed 518 goodRows = ((fakeCat[self.config.bulgeHLR] != 0.0) & (fakeCat[self.config.diskHLR] != 0.0))
520 badRows = len(fakeCat) - len(goodRows)
521 self.log.
info(
"Removing %d rows with HLR = 0 for either the bulge or disk" % badRows)
523 return fakeCat[goodRows]
525 def addFakeSources(self, image, fakeImages, sourceType):
526 """Add the fake sources to the given image 530 image : `lsst.afw.image.exposure.exposure.ExposureF` 531 The image into which the fake sources should be added 533 A list of tuples of `lsst.afw.image.ImageF` and `lsst.geom.Point2D, 534 the images and the locations they are to be inserted at. 536 The type (star/galaxy) of fake sources input 540 image : `lsst.afw.image.exposure.exposure.ExposureF` 544 Uses the x, y information in the ``fakeCat`` to position an image of the fake interpolated onto the 545 pixel grid of the image. Sets the ``FAKE`` mask plane for the pixels added with the fake source. 548 imageBBox = image.getBBox()
549 imageMI = image.maskedImage
551 for (fakeImage, xy)
in fakeImages:
552 X0 = xy.getX() - fakeImage.getWidth()/2 + 0.5
553 Y0 = xy.getY() - fakeImage.getHeight()/2 + 0.5
554 self.log.
debug(
"Adding fake source at %d, %d" % (xy.getX(), xy.getY()))
555 if sourceType ==
"galaxy":
557 interpFakeImBBox = interpFakeImage.getBBox()
559 interpFakeImage = fakeImage
560 interpFakeImBBox = fakeImage.getBBox()
562 interpFakeImBBox.clip(imageBBox)
563 imageMIView = imageMI.Factory(imageMI, interpFakeImBBox)
565 if interpFakeImBBox.getArea() > 0:
566 clippedFakeImage = interpFakeImage.Factory(interpFakeImage, interpFakeImBBox)
567 clippedFakeImageMI = afwImage.MaskedImageF(clippedFakeImage)
568 clippedFakeImageMI.mask.set(self.bitmask)
569 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...