lsst.pipe.tasks g84bf843041+72efc5b9b3
insertFakes.py
Go to the documentation of this file.
1# This file is part of pipe tasks
2#
3# Developed for the LSST Data Management System.
4# This product includes software developed by the LSST Project
5# (http://www.lsst.org).
6# See the COPYRIGHT file at the top-level directory of this distribution
7# for details of code ownership.
8#
9# This program is free software: you can redistribute it and/or modify
10# it under the terms of the GNU General Public License as published by
11# the Free Software Foundation, either version 3 of the License, or
12# (at your option) any later version.
13#
14# This program is distributed in the hope that it will be useful,
15# but WITHOUT ANY WARRANTY; without even the implied warranty of
16# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17# GNU General Public License for more details.
18#
19# You should have received a copy of the GNU General Public License
20# along with this program. If not, see <http://www.gnu.org/licenses/>.
21
22"""
23Insert fakes into deepCoadds
24"""
25import galsim
26from astropy.table import Table
27import numpy as np
28
29import lsst.geom as geom
30import lsst.afw.image as afwImage
31import lsst.afw.math as afwMath
32import lsst.pex.config as pexConfig
33import lsst.pipe.base as pipeBase
34
35from lsst.pipe.base import CmdLineTask, PipelineTask, PipelineTaskConfig, PipelineTaskConnections
36import lsst.pipe.base.connectionTypes as cT
37from lsst.pex.exceptions import LogicError, InvalidParameterError
38from lsst.coadd.utils.coaddDataIdContainer import ExistingCoaddDataIdContainer
39from lsst.geom import SpherePoint, radians, Box2D, Point2D
40
41__all__ = ["InsertFakesConfig", "InsertFakesTask"]
42
43
44def _add_fake_sources(exposure, objects, calibFluxRadius=12.0, logger=None):
45 """Add fake sources to the given exposure
46
47 Parameters
48 ----------
49 exposure : `lsst.afw.image.exposure.exposure.ExposureF`
50 The exposure into which the fake sources should be added
51 objects : `typing.Iterator` [`tuple` ['lsst.geom.SpherePoint`, `galsim.GSObject`]]
52 An iterator of tuples that contains (or generates) locations and object
53 surface brightness profiles to inject.
54 calibFluxRadius : `float`, optional
55 Aperture radius (in pixels) used to define the calibration for this
56 exposure+catalog. This is used to produce the correct instrumental fluxes
57 within the radius. The value should match that of the field defined in
58 slot_CalibFlux_instFlux.
59 logger : `lsst.log.log.log.Log` or `logging.Logger`, optional
60 Logger.
61 """
62 exposure.mask.addMaskPlane("FAKE")
63 bitmask = exposure.mask.getPlaneBitMask("FAKE")
64 if logger:
65 logger.info(f"Adding mask plane with bitmask {bitmask}")
66
67 wcs = exposure.getWcs()
68 psf = exposure.getPsf()
69
70 bbox = exposure.getBBox()
71 fullBounds = galsim.BoundsI(bbox.minX, bbox.maxX, bbox.minY, bbox.maxY)
72 gsImg = galsim.Image(exposure.image.array, bounds=fullBounds)
73
74 pixScale = wcs.getPixelScale(bbox.getCenter()).asArcseconds()
75
76 for spt, gsObj in objects:
77 pt = wcs.skyToPixel(spt)
78 posd = galsim.PositionD(pt.x, pt.y)
79 posi = galsim.PositionI(pt.x//1, pt.y//1)
80 if logger:
81 logger.debug(f"Adding fake source at {pt}")
82
83 mat = wcs.linearizePixelToSky(spt, geom.arcseconds).getMatrix()
84 gsWCS = galsim.JacobianWCS(mat[0, 0], mat[0, 1], mat[1, 0], mat[1, 1])
85
86 # This check is here because sometimes the WCS
87 # is multivalued and objects that should not be
88 # were being included.
89 gsPixScale = np.sqrt(gsWCS.pixelArea())
90 if gsPixScale < pixScale/2 or gsPixScale > pixScale*2:
91 continue
92
93 try:
94 psfArr = psf.computeKernelImage(pt).array
95 except InvalidParameterError:
96 # Try mapping to nearest point contained in bbox.
97 contained_pt = Point2D(
98 np.clip(pt.x, bbox.minX, bbox.maxX),
99 np.clip(pt.y, bbox.minY, bbox.maxY)
100 )
101 if pt == contained_pt: # no difference, so skip immediately
102 if logger:
103 logger.infof(
104 "Cannot compute Psf for object at {}; skipping",
105 pt
106 )
107 continue
108 # otherwise, try again with new point
109 try:
110 psfArr = psf.computeKernelImage(contained_pt).array
111 except InvalidParameterError:
112 if logger:
113 logger.infof(
114 "Cannot compute Psf for object at {}; skipping",
115 pt
116 )
117 continue
118
119 apCorr = psf.computeApertureFlux(calibFluxRadius)
120 psfArr /= apCorr
121 gsPSF = galsim.InterpolatedImage(galsim.Image(psfArr), wcs=gsWCS)
122
123 conv = galsim.Convolve(gsObj, gsPSF)
124 stampSize = conv.getGoodImageSize(gsWCS.minLinearScale())
125 subBounds = galsim.BoundsI(posi).withBorder(stampSize//2)
126 subBounds &= fullBounds
127
128 if subBounds.area() > 0:
129 subImg = gsImg[subBounds]
130 offset = posd - subBounds.true_center
131 # Note, for calexp injection, pixel is already part of the PSF and
132 # for coadd injection, it's incorrect to include the output pixel.
133 # So for both cases, we draw using method='no_pixel'.
134
135 conv.drawImage(
136 subImg,
137 add_to_image=True,
138 offset=offset,
139 wcs=gsWCS,
140 method='no_pixel'
141 )
142
143 subBox = geom.Box2I(
144 geom.Point2I(subBounds.xmin, subBounds.ymin),
145 geom.Point2I(subBounds.xmax, subBounds.ymax)
146 )
147 exposure[subBox].mask.array |= bitmask
148
149
150def _isWCSGalsimDefault(wcs, hdr):
151 """Decide if wcs = galsim.PixelScale(1.0) is explicitly present in header,
152 or if it's just the galsim default.
153
154 Parameters
155 ----------
156 wcs : galsim.BaseWCS
157 Potentially default WCS.
158 hdr : galsim.fits.FitsHeader
159 Header as read in by galsim.
160
161 Returns
162 -------
163 isDefault : bool
164 True if default, False if explicitly set in header.
165 """
166 if wcs != galsim.PixelScale(1.0):
167 return False
168 if hdr.get('GS_WCS') is not None:
169 return False
170 if hdr.get('CTYPE1', 'LINEAR') == 'LINEAR':
171 return not any(k in hdr for k in ['CD1_1', 'CDELT1'])
172 for wcs_type in galsim.fitswcs.fits_wcs_types:
173 # If one of these succeeds, then assume result is explicit
174 try:
175 wcs_type._readHeader(hdr)
176 return False
177 except Exception:
178 pass
179 else:
180 return not any(k in hdr for k in ['CD1_1', 'CDELT1'])
181
182
183class InsertFakesConnections(PipelineTaskConnections,
184 defaultTemplates={"coaddName": "deep",
185 "fakesType": "fakes_"},
186 dimensions=("tract", "patch", "band", "skymap")):
187
188 image = cT.Input(
189 doc="Image into which fakes are to be added.",
190 name="{coaddName}Coadd",
191 storageClass="ExposureF",
192 dimensions=("tract", "patch", "band", "skymap")
193 )
194
195 fakeCat = cT.Input(
196 doc="Catalog of fake sources to draw inputs from.",
197 name="{fakesType}fakeSourceCat",
198 storageClass="DataFrame",
199 dimensions=("tract", "skymap")
200 )
201
202 imageWithFakes = cT.Output(
203 doc="Image with fake sources added.",
204 name="{fakesType}{coaddName}Coadd",
205 storageClass="ExposureF",
206 dimensions=("tract", "patch", "band", "skymap")
207 )
208
209
210class InsertFakesConfig(PipelineTaskConfig,
211 pipelineConnections=InsertFakesConnections):
212 """Config for inserting fake sources
213 """
214
215 # Unchanged
216
217 doCleanCat = pexConfig.Field(
218 doc="If true removes bad sources from the catalog.",
219 dtype=bool,
220 default=True,
221 )
222
223 fakeType = pexConfig.Field(
224 doc="What type of fake catalog to use, snapshot (includes variability in the magnitudes calculated "
225 "from the MJD of the image), static (no variability) or filename for a user defined fits"
226 "catalog.",
227 dtype=str,
228 default="static",
229 )
230
231 calibFluxRadius = pexConfig.Field(
232 doc="Aperture radius (in pixels) that was used to define the calibration for this image+catalog. "
233 "This will be used to produce the correct instrumental fluxes within the radius. "
234 "This value should match that of the field defined in slot_CalibFlux_instFlux.",
235 dtype=float,
236 default=12.0,
237 )
238
239 coaddName = pexConfig.Field(
240 doc="The name of the type of coadd used",
241 dtype=str,
242 default="deep",
243 )
244
245 doSubSelectSources = pexConfig.Field(
246 doc="Set to True if you wish to sub select sources to be input based on the value in the column"
247 "set in the sourceSelectionColName config option.",
248 dtype=bool,
249 default=False
250 )
251
252 insertImages = pexConfig.Field(
253 doc="Insert images directly? True or False.",
254 dtype=bool,
255 default=False,
256 )
257
258 insertOnlyStars = pexConfig.Field(
259 doc="Insert only stars? True or False.",
260 dtype=bool,
261 default=False,
262 )
263
264 doProcessAllDataIds = pexConfig.Field(
265 doc="If True, all input data IDs will be processed, even those containing no fake sources.",
266 dtype=bool,
267 default=False,
268 )
269
270 trimBuffer = pexConfig.Field(
271 doc="Size of the pixel buffer surrounding the image. Only those fake sources with a centroid"
272 "falling within the image+buffer region will be considered for fake source injection.",
273 dtype=int,
274 default=100,
275 )
276
277 sourceType = pexConfig.Field(
278 doc="The column name for the source type used in the fake source catalog.",
279 dtype=str,
280 default="sourceType",
281 )
282
283 fits_alignment = pexConfig.ChoiceField(
284 doc="How should injections from FITS files be aligned?",
285 dtype=str,
286 allowed={
287 "wcs": (
288 "Input image will be transformed such that the local WCS in "
289 "the FITS header matches the local WCS in the target image. "
290 "I.e., North, East, and angular distances in the input image "
291 "will match North, East, and angular distances in the target "
292 "image."
293 ),
294 "pixel": (
295 "Input image will _not_ be transformed. Up, right, and pixel "
296 "distances in the input image will match up, right and pixel "
297 "distances in the target image."
298 )
299 },
300 default="pixel"
301 )
302
303 # New source catalog config variables
304
305 ra_col = pexConfig.Field(
306 doc="Source catalog column name for RA (in radians).",
307 dtype=str,
308 default="ra",
309 )
310
311 dec_col = pexConfig.Field(
312 doc="Source catalog column name for dec (in radians).",
313 dtype=str,
314 default="dec",
315 )
316
317 bulge_semimajor_col = pexConfig.Field(
318 doc="Source catalog column name for the semimajor axis (in arcseconds) "
319 "of the bulge half-light ellipse.",
320 dtype=str,
321 default="bulge_semimajor",
322 )
323
324 bulge_axis_ratio_col = pexConfig.Field(
325 doc="Source catalog column name for the axis ratio of the bulge "
326 "half-light ellipse.",
327 dtype=str,
328 default="bulge_axis_ratio",
329 )
330
331 bulge_pa_col = pexConfig.Field(
332 doc="Source catalog column name for the position angle (measured from "
333 "North through East in degrees) of the semimajor axis of the bulge "
334 "half-light ellipse.",
335 dtype=str,
336 default="bulge_pa",
337 )
338
339 bulge_n_col = pexConfig.Field(
340 doc="Source catalog column name for the Sersic index of the bulge.",
341 dtype=str,
342 default="bulge_n",
343 )
344
345 disk_semimajor_col = pexConfig.Field(
346 doc="Source catalog column name for the semimajor axis (in arcseconds) "
347 "of the disk half-light ellipse.",
348 dtype=str,
349 default="disk_semimajor",
350 )
351
352 disk_axis_ratio_col = pexConfig.Field(
353 doc="Source catalog column name for the axis ratio of the disk "
354 "half-light ellipse.",
355 dtype=str,
356 default="disk_axis_ratio",
357 )
358
359 disk_pa_col = pexConfig.Field(
360 doc="Source catalog column name for the position angle (measured from "
361 "North through East in degrees) of the semimajor axis of the disk "
362 "half-light ellipse.",
363 dtype=str,
364 default="disk_pa",
365 )
366
367 disk_n_col = pexConfig.Field(
368 doc="Source catalog column name for the Sersic index of the disk.",
369 dtype=str,
370 default="disk_n",
371 )
372
373 bulge_disk_flux_ratio_col = pexConfig.Field(
374 doc="Source catalog column name for the bulge/disk flux ratio.",
375 dtype=str,
376 default="bulge_disk_flux_ratio",
377 )
378
379 mag_col = pexConfig.Field(
380 doc="Source catalog column name template for magnitudes, in the format "
381 "``filter name``_mag_col. E.g., if this config variable is set to "
382 "``%s_mag``, then the i-band magnitude will be searched for in the "
383 "``i_mag`` column of the source catalog.",
384 dtype=str,
385 default="%s_mag"
386 )
387
388 select_col = pexConfig.Field(
389 doc="Source catalog column name to be used to select which sources to "
390 "add.",
391 dtype=str,
392 default="select",
393 )
394
395 # Deprecated config variables
396
397 raColName = pexConfig.Field(
398 doc="RA column name used in the fake source catalog.",
399 dtype=str,
400 default="raJ2000",
401 deprecated="Use `ra_col` instead."
402 )
403
404 decColName = pexConfig.Field(
405 doc="Dec. column name used in the fake source catalog.",
406 dtype=str,
407 default="decJ2000",
408 deprecated="Use `dec_col` instead."
409 )
410
411 diskHLR = pexConfig.Field(
412 doc="Column name for the disk half light radius used in the fake source catalog.",
413 dtype=str,
414 default="DiskHalfLightRadius",
415 deprecated=(
416 "Use `disk_semimajor_col`, `disk_axis_ratio_col`, and `disk_pa_col`"
417 " to specify disk half-light ellipse."
418 )
419 )
420
421 aDisk = pexConfig.Field(
422 doc="The column name for the semi major axis length of the disk component used in the fake source"
423 "catalog.",
424 dtype=str,
425 default="a_d",
426 deprecated=(
427 "Use `disk_semimajor_col`, `disk_axis_ratio_col`, and `disk_pa_col`"
428 " to specify disk half-light ellipse."
429 )
430 )
431
432 bDisk = pexConfig.Field(
433 doc="The column name for the semi minor axis length of the disk component.",
434 dtype=str,
435 default="b_d",
436 deprecated=(
437 "Use `disk_semimajor_col`, `disk_axis_ratio_col`, and `disk_pa_col`"
438 " to specify disk half-light ellipse."
439 )
440 )
441
442 paDisk = pexConfig.Field(
443 doc="The column name for the PA of the disk component used in the fake source catalog.",
444 dtype=str,
445 default="pa_disk",
446 deprecated=(
447 "Use `disk_semimajor_col`, `disk_axis_ratio_col`, and `disk_pa_col`"
448 " to specify disk half-light ellipse."
449 )
450 )
451
452 nDisk = pexConfig.Field(
453 doc="The column name for the sersic index of the disk component used in the fake source catalog.",
454 dtype=str,
455 default="disk_n",
456 deprecated="Use `disk_n_col` instead."
457 )
458
459 bulgeHLR = pexConfig.Field(
460 doc="Column name for the bulge half light radius used in the fake source catalog.",
461 dtype=str,
462 default="BulgeHalfLightRadius",
463 deprecated=(
464 "Use `bulge_semimajor_col`, `bulge_axis_ratio_col`, and "
465 "`bulge_pa_col` to specify disk half-light ellipse."
466 )
467 )
468
469 aBulge = pexConfig.Field(
470 doc="The column name for the semi major axis length of the bulge component.",
471 dtype=str,
472 default="a_b",
473 deprecated=(
474 "Use `bulge_semimajor_col`, `bulge_axis_ratio_col`, and "
475 "`bulge_pa_col` to specify disk half-light ellipse."
476 )
477 )
478
479 bBulge = pexConfig.Field(
480 doc="The column name for the semi minor axis length of the bulge component used in the fake source "
481 "catalog.",
482 dtype=str,
483 default="b_b",
484 deprecated=(
485 "Use `bulge_semimajor_col`, `bulge_axis_ratio_col`, and "
486 "`bulge_pa_col` to specify disk half-light ellipse."
487 )
488 )
489
490 paBulge = pexConfig.Field(
491 doc="The column name for the PA of the bulge component used in the fake source catalog.",
492 dtype=str,
493 default="pa_bulge",
494 deprecated=(
495 "Use `bulge_semimajor_col`, `bulge_axis_ratio_col`, and "
496 "`bulge_pa_col` to specify disk half-light ellipse."
497 )
498 )
499
500 nBulge = pexConfig.Field(
501 doc="The column name for the sersic index of the bulge component used in the fake source catalog.",
502 dtype=str,
503 default="bulge_n",
504 deprecated="Use `bulge_n_col` instead."
505 )
506
507 magVar = pexConfig.Field(
508 doc="The column name for the magnitude calculated taking variability into account. In the format "
509 "``filter name``magVar, e.g. imagVar for the magnitude in the i band.",
510 dtype=str,
511 default="%smagVar",
512 deprecated="Use `mag_col` instead."
513 )
514
515 sourceSelectionColName = pexConfig.Field(
516 doc="The name of the column in the input fakes catalogue to be used to determine which sources to"
517 "add, default is none and when this is used all sources are added.",
518 dtype=str,
519 default="templateSource",
520 deprecated="Use `select_col` instead."
521 )
522
523
524class InsertFakesTask(PipelineTask, CmdLineTask):
525 """Insert fake objects into images.
526
527 Add fake stars and galaxies to the given image, read in through the dataRef. Galaxy parameters are read in
528 from the specified file and then modelled using galsim.
529
530 `InsertFakesTask` has five functions that make images of the fake sources and then add them to the
531 image.
532
533 `addPixCoords`
534 Use the WCS information to add the pixel coordinates of each source.
535 `mkFakeGalsimGalaxies`
536 Use Galsim to make fake double sersic galaxies for each set of galaxy parameters in the input file.
537 `mkFakeStars`
538 Use the PSF information from the image to make a fake star using the magnitude information from the
539 input file.
540 `cleanCat`
541 Remove rows of the input fake catalog which have half light radius, of either the bulge or the disk,
542 that are 0. Also removes rows that have Sersic index outside of galsim's allowed paramters. If
543 the config option sourceSelectionColName is set then this function limits the catalog of input fakes
544 to only those which are True in this column.
545 `addFakeSources`
546 Add the fake sources to the image.
547
548 """
549
550 _DefaultName = "insertFakes"
551 ConfigClass = InsertFakesConfig
552
553 def runDataRef(self, dataRef):
554 """Read in/write out the required data products and add fake sources to the deepCoadd.
555
556 Parameters
557 ----------
559 Data reference defining the image to have fakes added to it
560 Used to access the following data products:
561 deepCoadd
562 """
563
564 self.log.info("Adding fakes to: tract: %d, patch: %s, filter: %s",
565 dataRef.dataId["tract"], dataRef.dataId["patch"], dataRef.dataId["filter"])
566
567 # To do: should it warn when asked to insert variable sources into the coadd
568
569 if self.config.fakeType == "static":
570 fakeCat = dataRef.get("deepCoadd_fakeSourceCat").toDataFrame()
571 # To do: DM-16254, the read and write of the fake catalogs will be changed once the new pipeline
572 # task structure for ref cats is in place.
573 self.fakeSourceCatType = "deepCoadd_fakeSourceCat"
574 else:
575 fakeCat = Table.read(self.config.fakeType).to_pandas()
576
577 coadd = dataRef.get("deepCoadd")
578 wcs = coadd.getWcs()
579 photoCalib = coadd.getPhotoCalib()
580
581 imageWithFakes = self.run(fakeCat, coadd, wcs, photoCalib)
582
583 dataRef.put(imageWithFakes.imageWithFakes, "fakes_deepCoadd")
584
585 def runQuantum(self, butlerQC, inputRefs, outputRefs):
586 inputs = butlerQC.get(inputRefs)
587 inputs["wcs"] = inputs["image"].getWcs()
588 inputs["photoCalib"] = inputs["image"].getPhotoCalib()
589
590 outputs = self.run(**inputs)
591 butlerQC.put(outputs, outputRefs)
592
593 @classmethod
594 def _makeArgumentParser(cls):
595 parser = pipeBase.ArgumentParser(name=cls._DefaultName)
596 parser.add_id_argument(name="--id", datasetType="deepCoadd",
597 help="data IDs for the deepCoadd, e.g. --id tract=12345 patch=1,2 filter=r",
598 ContainerClass=ExistingCoaddDataIdContainer)
599 return parser
600
601 def run(self, fakeCat, image, wcs, photoCalib):
602 """Add fake sources to an image.
603
604 Parameters
605 ----------
606 fakeCat : `pandas.core.frame.DataFrame`
607 The catalog of fake sources to be input
608 image : `lsst.afw.image.exposure.exposure.ExposureF`
609 The image into which the fake sources should be added
611 WCS to use to add fake sources
612 photoCalib : `lsst.afw.image.photoCalib.PhotoCalib`
613 Photometric calibration to be used to calibrate the fake sources
614
615 Returns
616 -------
617 resultStruct : `lsst.pipe.base.struct.Struct`
618 contains : image : `lsst.afw.image.exposure.exposure.ExposureF`
619
620 Notes
621 -----
622 Adds pixel coordinates for each source to the fakeCat and removes objects with bulge or disk half
623 light radius = 0 (if ``config.doCleanCat = True``).
624
625 Adds the ``Fake`` mask plane to the image which is then set by `addFakeSources` to mark where fake
626 sources have been added. Uses the information in the ``fakeCat`` to make fake galaxies (using galsim)
627 and fake stars, using the PSF models from the PSF information for the image. These are then added to
628 the image and the image with fakes included returned.
629
630 The galsim galaxies are made using a double sersic profile, one for the bulge and one for the disk,
631 this is then convolved with the PSF at that point.
632 """
633 # Attach overriding wcs and photoCalib to image, but retain originals
634 # so we can reset at the end.
635 origWcs = image.getWcs()
636 origPhotoCalib = image.getPhotoCalib()
637 image.setWcs(wcs)
638 image.setPhotoCalib(photoCalib)
639
640 band = image.getFilterLabel().bandLabel
641 fakeCat = self._standardizeColumns(fakeCat, band)
642
643 fakeCat = self.addPixCoords(fakeCat, image)
644 fakeCat = self.trimFakeCat(fakeCat, image)
645
646 if len(fakeCat) > 0:
647 if not self.config.insertImages:
648 if isinstance(fakeCat[self.config.sourceType].iloc[0], str):
649 galCheckVal = "galaxy"
650 starCheckVal = "star"
651 elif isinstance(fakeCat[self.config.sourceType].iloc[0], bytes):
652 galCheckVal = b"galaxy"
653 starCheckVal = b"star"
654 elif isinstance(fakeCat[self.config.sourceType].iloc[0], (int, float)):
655 galCheckVal = 1
656 starCheckVal = 0
657 else:
658 raise TypeError(
659 "sourceType column does not have required type, should be str, bytes or int"
660 )
661 if self.config.doCleanCat:
662 fakeCat = self.cleanCat(fakeCat, starCheckVal)
663
664 generator = self._generateGSObjectsFromCatalog(image, fakeCat, galCheckVal, starCheckVal)
665 else:
666 generator = self._generateGSObjectsFromImages(image, fakeCat)
667 _add_fake_sources(image, generator, calibFluxRadius=self.config.calibFluxRadius, logger=self.log)
668 elif len(fakeCat) == 0 and self.config.doProcessAllDataIds:
669 self.log.warning("No fakes found for this dataRef; processing anyway.")
670 image.mask.addMaskPlane("FAKE")
671 else:
672 raise RuntimeError("No fakes found for this dataRef.")
673
674 # restore original exposure WCS and photoCalib
675 image.setWcs(origWcs)
676 image.setPhotoCalib(origPhotoCalib)
677
678 resultStruct = pipeBase.Struct(imageWithFakes=image)
679
680 return resultStruct
681
682 def _standardizeColumns(self, fakeCat, band):
683 """Use config variables to 'standardize' the expected columns and column
684 names in the input catalog.
685
686 Parameters
687 ----------
688 fakeCat : `pandas.core.frame.DataFrame`
689 The catalog of fake sources to be input
690 band : `str`
691 Label for the current band being processed.
692
693 Returns
694 -------
695 outCat : `pandas.core.frame.DataFrame`
696 The standardized catalog of fake sources
697 """
698 cfg = self.config
699 replace_dict = {}
700
701 def add_to_replace_dict(new_name, depr_name, std_name):
702 if new_name in fakeCat.columns:
703 replace_dict[new_name] = std_name
704 elif depr_name in fakeCat.columns:
705 replace_dict[depr_name] = std_name
706 else:
707 raise ValueError(f"Could not determine column for {std_name}.")
708
709 # Prefer new config variables over deprecated config variables.
710 # RA, dec, and mag are always required. Do these first
711 for new_name, depr_name, std_name in [
712 (cfg.ra_col, cfg.raColName, 'ra'),
713 (cfg.dec_col, cfg.decColName, 'dec'),
714 (cfg.mag_col%band, cfg.magVar%band, 'mag')
715 ]:
716 add_to_replace_dict(new_name, depr_name, std_name)
717 # Only handle bulge/disk params if not injecting images
718 if not cfg.insertImages and not cfg.insertOnlyStars:
719 for new_name, depr_name, std_name in [
720 (cfg.bulge_n_col, cfg.nBulge, 'bulge_n'),
721 (cfg.bulge_pa_col, cfg.paBulge, 'bulge_pa'),
722 (cfg.disk_n_col, cfg.nDisk, 'disk_n'),
723 (cfg.disk_pa_col, cfg.paDisk, 'disk_pa'),
724 ]:
725 add_to_replace_dict(new_name, depr_name, std_name)
726
727 if cfg.doSubSelectSources:
728 add_to_replace_dict(
729 cfg.select_col,
730 cfg.sourceSelectionColName,
731 'select'
732 )
733 fakeCat = fakeCat.rename(columns=replace_dict, copy=False)
734
735 # Handling the half-light radius and axis-ratio are trickier, since we
736 # moved from expecting (HLR, a, b) to expecting (semimajor, axis_ratio).
737 # Just handle these manually.
738 if not cfg.insertImages and not cfg.insertOnlyStars:
739 if (
740 cfg.bulge_semimajor_col in fakeCat.columns
741 and cfg.bulge_axis_ratio_col in fakeCat.columns
742 ):
743 fakeCat = fakeCat.rename(
744 columns={
745 cfg.bulge_semimajor_col: 'bulge_semimajor',
746 cfg.bulge_axis_ratio_col: 'bulge_axis_ratio',
747 cfg.disk_semimajor_col: 'disk_semimajor',
748 cfg.disk_axis_ratio_col: 'disk_axis_ratio',
749 },
750 copy=False
751 )
752 elif (
753 cfg.bulgeHLR in fakeCat.columns
754 and cfg.aBulge in fakeCat.columns
755 and cfg.bBulge in fakeCat.columns
756 ):
757 fakeCat['bulge_axis_ratio'] = (
758 fakeCat[cfg.bBulge]/fakeCat[cfg.aBulge]
759 )
760 fakeCat['bulge_semimajor'] = (
761 fakeCat[cfg.bulgeHLR]/np.sqrt(fakeCat['bulge_axis_ratio'])
762 )
763 fakeCat['disk_axis_ratio'] = (
764 fakeCat[cfg.bDisk]/fakeCat[cfg.aDisk]
765 )
766 fakeCat['disk_semimajor'] = (
767 fakeCat[cfg.diskHLR]/np.sqrt(fakeCat['disk_axis_ratio'])
768 )
769 else:
770 raise ValueError(
771 "Could not determine columns for half-light radius and "
772 "axis ratio."
773 )
774
775 # Process the bulge/disk flux ratio if possible.
776 if cfg.bulge_disk_flux_ratio_col in fakeCat.columns:
777 fakeCat = fakeCat.rename(
778 columns={
779 cfg.bulge_disk_flux_ratio_col: 'bulge_disk_flux_ratio'
780 },
781 copy=False
782 )
783 else:
784 fakeCat['bulge_disk_flux_ratio'] = 1.0
785
786 return fakeCat
787
788 def _generateGSObjectsFromCatalog(self, exposure, fakeCat, galCheckVal, starCheckVal):
789 """Process catalog to generate `galsim.GSObject` s.
790
791 Parameters
792 ----------
793 exposure : `lsst.afw.image.exposure.exposure.ExposureF`
794 The exposure into which the fake sources should be added
795 fakeCat : `pandas.core.frame.DataFrame`
796 The catalog of fake sources to be input
797 galCheckVal : `str`, `bytes` or `int`
798 The value that is set in the sourceType column to specifiy an object is a galaxy.
799 starCheckVal : `str`, `bytes` or `int`
800 The value that is set in the sourceType column to specifiy an object is a star.
801
802 Yields
803 ------
804 gsObjects : `generator`
805 A generator of tuples of `lsst.geom.SpherePoint` and `galsim.GSObject`.
806 """
807 wcs = exposure.getWcs()
808 photoCalib = exposure.getPhotoCalib()
809
810 self.log.info("Making %d objects for insertion", len(fakeCat))
811
812 for (index, row) in fakeCat.iterrows():
813 ra = row['ra']
814 dec = row['dec']
815 skyCoord = SpherePoint(ra, dec, radians)
816 xy = wcs.skyToPixel(skyCoord)
817
818 try:
819 flux = photoCalib.magnitudeToInstFlux(row['mag'], xy)
820 except LogicError:
821 continue
822
823 sourceType = row[self.config.sourceType]
824 if sourceType == galCheckVal:
825 # GalSim convention: HLR = sqrt(a * b) = a * sqrt(b / a)
826 bulge_gs_HLR = row['bulge_semimajor']*np.sqrt(row['bulge_axis_ratio'])
827 bulge = galsim.Sersic(n=row['bulge_n'], half_light_radius=bulge_gs_HLR)
828 bulge = bulge.shear(q=row['bulge_axis_ratio'], beta=((90 - row['bulge_pa'])*galsim.degrees))
829
830 disk_gs_HLR = row['disk_semimajor']*np.sqrt(row['disk_axis_ratio'])
831 disk = galsim.Sersic(n=row['disk_n'], half_light_radius=disk_gs_HLR)
832 disk = disk.shear(q=row['disk_axis_ratio'], beta=((90 - row['disk_pa'])*galsim.degrees))
833
834 gal = bulge*row['bulge_disk_flux_ratio'] + disk
835 gal = gal.withFlux(flux)
836
837 yield skyCoord, gal
838 elif sourceType == starCheckVal:
839 star = galsim.DeltaFunction()
840 star = star.withFlux(flux)
841 yield skyCoord, star
842 else:
843 raise TypeError(f"Unknown sourceType {sourceType}")
844
845 def _generateGSObjectsFromImages(self, exposure, fakeCat):
846 """Process catalog to generate `galsim.GSObject` s.
847
848 Parameters
849 ----------
850 exposure : `lsst.afw.image.exposure.exposure.ExposureF`
851 The exposure into which the fake sources should be added
852 fakeCat : `pandas.core.frame.DataFrame`
853 The catalog of fake sources to be input
854
855 Yields
856 ------
857 gsObjects : `generator`
858 A generator of tuples of `lsst.geom.SpherePoint` and `galsim.GSObject`.
859 """
860 band = exposure.getFilterLabel().bandLabel
861 wcs = exposure.getWcs()
862 photoCalib = exposure.getPhotoCalib()
863
864 self.log.info("Processing %d fake images", len(fakeCat))
865
866 for (index, row) in fakeCat.iterrows():
867 ra = row['ra']
868 dec = row['dec']
869 skyCoord = SpherePoint(ra, dec, radians)
870 xy = wcs.skyToPixel(skyCoord)
871
872 try:
873 flux = photoCalib.magnitudeToInstFlux(row['mag'], xy)
874 except LogicError:
875 continue
876
877 imFile = row[band+"imFilename"]
878 try:
879 imFile = imFile.decode("utf-8")
880 except AttributeError:
881 pass
882 imFile = imFile.strip()
883 im = galsim.fits.read(imFile, read_header=True)
884
885 if self.config.fits_alignment == "wcs":
886 # galsim.fits.read will always attach a WCS to its output. If it
887 # can't find a WCS in the FITS header, then it defaults to
888 # scale = 1.0 arcsec / pix. So if that's the scale, then we
889 # need to check if it was explicitly set or if it's just the
890 # default. If it's just the default then we should raise an
891 # exception.
892 if _isWCSGalsimDefault(im.wcs, im.header):
893 raise RuntimeError(
894 f"Cannot find WCS in input FITS file {imFile}"
895 )
896 elif self.config.fits_alignment == "pixel":
897 # Here we need to set im.wcs to the local WCS at the target
898 # position.
899 linWcs = wcs.linearizePixelToSky(skyCoord, geom.arcseconds)
900 mat = linWcs.getMatrix()
901 im.wcs = galsim.JacobianWCS(
902 mat[0, 0], mat[0, 1], mat[1, 0], mat[1, 1]
903 )
904 else:
905 raise ValueError(
906 f"Unknown fits_alignment type {self.config.fits_alignment}"
907 )
908
909 obj = galsim.InterpolatedImage(im, calculate_stepk=False)
910 obj = obj.withFlux(flux)
911 yield skyCoord, obj
912
913 def processImagesForInsertion(self, fakeCat, wcs, psf, photoCalib, band, pixelScale):
914 """Process images from files into the format needed for insertion.
915
916 Parameters
917 ----------
918 fakeCat : `pandas.core.frame.DataFrame`
919 The catalog of fake sources to be input
920 wcs : `lsst.afw.geom.skyWcs.skyWcs.SkyWc`
921 WCS to use to add fake sources
922 psf : `lsst.meas.algorithms.coaddPsf.coaddPsf.CoaddPsf` or
923 `lsst.meas.extensions.psfex.psfexPsf.PsfexPsf`
924 The PSF information to use to make the PSF images
925 photoCalib : `lsst.afw.image.photoCalib.PhotoCalib`
926 Photometric calibration to be used to calibrate the fake sources
927 band : `str`
928 The filter band that the observation was taken in.
929 pixelScale : `float`
930 The pixel scale of the image the sources are to be added to.
931
932 Returns
933 -------
934 galImages : `list`
935 A list of tuples of `lsst.afw.image.exposure.exposure.ExposureF` and
936 `lsst.geom.Point2D` of their locations.
937 For sources labelled as galaxy.
938 starImages : `list`
939 A list of tuples of `lsst.afw.image.exposure.exposure.ExposureF` and
940 `lsst.geom.Point2D` of their locations.
941 For sources labelled as star.
942
943 Notes
944 -----
945 The input fakes catalog needs to contain the absolute path to the image in the
946 band that is being used to add images to. It also needs to have the R.A. and
947 declination of the fake source in radians and the sourceType of the object.
948 """
949 galImages = []
950 starImages = []
951
952 self.log.info("Processing %d fake images", len(fakeCat))
953
954 for (imFile, sourceType, mag, x, y) in zip(fakeCat[band + "imFilename"].array,
955 fakeCat["sourceType"].array,
956 fakeCat['mag'].array,
957 fakeCat["x"].array, fakeCat["y"].array):
958
959 im = afwImage.ImageF.readFits(imFile)
960
961 xy = geom.Point2D(x, y)
962
963 # We put these two PSF calculations within this same try block so that we catch cases
964 # where the object's position is outside of the image.
965 try:
966 correctedFlux = psf.computeApertureFlux(self.config.calibFluxRadius, xy)
967 psfKernel = psf.computeKernelImage(xy).getArray()
968 psfKernel /= correctedFlux
969
970 except InvalidParameterError:
971 self.log.info("%s at %0.4f, %0.4f outside of image", sourceType, x, y)
972 continue
973
974 psfIm = galsim.InterpolatedImage(galsim.Image(psfKernel), scale=pixelScale)
975 galsimIm = galsim.InterpolatedImage(galsim.Image(im.array), scale=pixelScale)
976 convIm = galsim.Convolve([galsimIm, psfIm])
977
978 try:
979 outIm = convIm.drawImage(scale=pixelScale, method="real_space").array
980 except (galsim.errors.GalSimFFTSizeError, MemoryError):
981 continue
982
983 imSum = np.sum(outIm)
984 divIm = outIm/imSum
985
986 try:
987 flux = photoCalib.magnitudeToInstFlux(mag, xy)
988 except LogicError:
989 flux = 0
990
991 imWithFlux = flux*divIm
992
993 if sourceType == b"galaxy":
994 galImages.append((afwImage.ImageF(imWithFlux), xy))
995 if sourceType == b"star":
996 starImages.append((afwImage.ImageF(imWithFlux), xy))
997
998 return galImages, starImages
999
1000 def addPixCoords(self, fakeCat, image):
1001
1002 """Add pixel coordinates to the catalog of fakes.
1003
1004 Parameters
1005 ----------
1006 fakeCat : `pandas.core.frame.DataFrame`
1007 The catalog of fake sources to be input
1008 image : `lsst.afw.image.exposure.exposure.ExposureF`
1009 The image into which the fake sources should be added
1010
1011 Returns
1012 -------
1013 fakeCat : `pandas.core.frame.DataFrame`
1014 """
1015 wcs = image.getWcs()
1016 ras = fakeCat['ra'].values
1017 decs = fakeCat['dec'].values
1018 xs, ys = wcs.skyToPixelArray(ras, decs)
1019 fakeCat["x"] = xs
1020 fakeCat["y"] = ys
1021
1022 return fakeCat
1023
1024 def trimFakeCat(self, fakeCat, image):
1025 """Trim the fake cat to about the size of the input image.
1026
1027 `fakeCat` must be processed with addPixCoords before using this method.
1028
1029 Parameters
1030 ----------
1031 fakeCat : `pandas.core.frame.DataFrame`
1032 The catalog of fake sources to be input
1033 image : `lsst.afw.image.exposure.exposure.ExposureF`
1034 The image into which the fake sources should be added
1035
1036 Returns
1037 -------
1038 fakeCat : `pandas.core.frame.DataFrame`
1039 The original fakeCat trimmed to the area of the image
1040 """
1041
1042 bbox = Box2D(image.getBBox()).dilatedBy(self.config.trimBuffer)
1043 xs = fakeCat["x"].values
1044 ys = fakeCat["y"].values
1045
1046 isContained = xs >= bbox.minX
1047 isContained &= xs <= bbox.maxX
1048 isContained &= ys >= bbox.minY
1049 isContained &= ys <= bbox.maxY
1050
1051 return fakeCat[isContained]
1052
1053 def mkFakeGalsimGalaxies(self, fakeCat, band, photoCalib, pixelScale, psf, image):
1054 """Make images of fake galaxies using GalSim.
1055
1056 Parameters
1057 ----------
1058 band : `str`
1059 pixelScale : `float`
1060 psf : `lsst.meas.extensions.psfex.psfexPsf.PsfexPsf`
1061 The PSF information to use to make the PSF images
1062 fakeCat : `pandas.core.frame.DataFrame`
1063 The catalog of fake sources to be input
1064 photoCalib : `lsst.afw.image.photoCalib.PhotoCalib`
1065 Photometric calibration to be used to calibrate the fake sources
1066
1067 Yields
1068 -------
1069 galImages : `generator`
1070 A generator of tuples of `lsst.afw.image.exposure.exposure.ExposureF` and
1071 `lsst.geom.Point2D` of their locations.
1072
1073 Notes
1074 -----
1075
1076 Fake galaxies are made by combining two sersic profiles, one for the bulge and one for the disk. Each
1077 component has an individual sersic index (n), a, b and position angle (PA). The combined profile is
1078 then convolved with the PSF at the specified x, y position on the image.
1079
1080 The names of the columns in the ``fakeCat`` are configurable and are the column names from the
1081 University of Washington simulations database as default. For more information see the doc strings
1082 attached to the config options.
1083
1084 See mkFakeStars doc string for an explanation of calibration to instrumental flux.
1085 """
1086
1087 self.log.info("Making %d fake galaxy images", len(fakeCat))
1088
1089 for (index, row) in fakeCat.iterrows():
1090 xy = geom.Point2D(row["x"], row["y"])
1091
1092 # We put these two PSF calculations within this same try block so that we catch cases
1093 # where the object's position is outside of the image.
1094 try:
1095 correctedFlux = psf.computeApertureFlux(self.config.calibFluxRadius, xy)
1096 psfKernel = psf.computeKernelImage(xy).getArray()
1097 psfKernel /= correctedFlux
1098
1099 except InvalidParameterError:
1100 self.log.info("Galaxy at %0.4f, %0.4f outside of image", row["x"], row["y"])
1101 continue
1102
1103 try:
1104 flux = photoCalib.magnitudeToInstFlux(row['mag'], xy)
1105 except LogicError:
1106 flux = 0
1107
1108 # GalSim convention: HLR = sqrt(a * b) = a * sqrt(b / a)
1109 bulge_gs_HLR = row['bulge_semimajor']*np.sqrt(row['bulge_axis_ratio'])
1110 bulge = galsim.Sersic(n=row['bulge_n'], half_light_radius=bulge_gs_HLR)
1111 bulge = bulge.shear(q=row['bulge_axis_ratio'], beta=((90 - row['bulge_pa'])*galsim.degrees))
1112
1113 disk_gs_HLR = row['disk_semimajor']*np.sqrt(row['disk_axis_ratio'])
1114 disk = galsim.Sersic(n=row['disk_n'], half_light_radius=disk_gs_HLR)
1115 disk = disk.shear(q=row['disk_axis_ratio'], beta=((90 - row['disk_pa'])*galsim.degrees))
1116
1117 gal = bulge*row['bulge_disk_flux_ratio'] + disk
1118 gal = gal.withFlux(flux)
1119
1120 psfIm = galsim.InterpolatedImage(galsim.Image(psfKernel), scale=pixelScale)
1121 gal = galsim.Convolve([gal, psfIm])
1122 try:
1123 galIm = gal.drawImage(scale=pixelScale, method="real_space").array
1124 except (galsim.errors.GalSimFFTSizeError, MemoryError):
1125 continue
1126
1127 yield (afwImage.ImageF(galIm), xy)
1128
1129 def mkFakeStars(self, fakeCat, band, photoCalib, psf, image):
1130
1131 """Make fake stars based off the properties in the fakeCat.
1132
1133 Parameters
1134 ----------
1135 band : `str`
1136 psf : `lsst.meas.extensions.psfex.psfexPsf.PsfexPsf`
1137 The PSF information to use to make the PSF images
1138 fakeCat : `pandas.core.frame.DataFrame`
1139 The catalog of fake sources to be input
1140 image : `lsst.afw.image.exposure.exposure.ExposureF`
1141 The image into which the fake sources should be added
1142 photoCalib : `lsst.afw.image.photoCalib.PhotoCalib`
1143 Photometric calibration to be used to calibrate the fake sources
1144
1145 Yields
1146 -------
1147 starImages : `generator`
1148 A generator of tuples of `lsst.afw.image.ImageF` of fake stars and
1149 `lsst.geom.Point2D` of their locations.
1150
1151 Notes
1152 -----
1153 To take a given magnitude and translate to the number of counts in the image
1154 we use photoCalib.magnitudeToInstFlux, which returns the instrumental flux for the
1155 given calibration radius used in the photometric calibration step.
1156 Thus `calibFluxRadius` should be set to this same radius so that we can normalize
1157 the PSF model to the correct instrumental flux within calibFluxRadius.
1158 """
1159
1160 self.log.info("Making %d fake star images", len(fakeCat))
1161
1162 for (index, row) in fakeCat.iterrows():
1163 xy = geom.Point2D(row["x"], row["y"])
1164
1165 # We put these two PSF calculations within this same try block so that we catch cases
1166 # where the object's position is outside of the image.
1167 try:
1168 correctedFlux = psf.computeApertureFlux(self.config.calibFluxRadius, xy)
1169 starIm = psf.computeImage(xy)
1170 starIm /= correctedFlux
1171
1172 except InvalidParameterError:
1173 self.log.info("Star at %0.4f, %0.4f outside of image", row["x"], row["y"])
1174 continue
1175
1176 try:
1177 flux = photoCalib.magnitudeToInstFlux(row['mag'], xy)
1178 except LogicError:
1179 flux = 0
1180
1181 starIm *= flux
1182 yield ((starIm.convertF(), xy))
1183
1184 def cleanCat(self, fakeCat, starCheckVal):
1185 """Remove rows from the fakes catalog which have HLR = 0 for either the buldge or disk component,
1186 also remove galaxies that have Sersic index outside the galsim min and max
1187 allowed (0.3 <= n <= 6.2).
1188
1189 Parameters
1190 ----------
1191 fakeCat : `pandas.core.frame.DataFrame`
1192 The catalog of fake sources to be input
1193 starCheckVal : `str`, `bytes` or `int`
1194 The value that is set in the sourceType column to specifiy an object is a star.
1195
1196 Returns
1197 -------
1198 fakeCat : `pandas.core.frame.DataFrame`
1199 The input catalog of fake sources but with the bad objects removed
1200 """
1201
1202 rowsToKeep = (((fakeCat['bulge_semimajor'] != 0.0) & (fakeCat['disk_semimajor'] != 0.0))
1203 | (fakeCat[self.config.sourceType] == starCheckVal))
1204 numRowsNotUsed = len(fakeCat) - len(np.where(rowsToKeep)[0])
1205 self.log.info("Removing %d rows with HLR = 0 for either the bulge or disk", numRowsNotUsed)
1206 fakeCat = fakeCat[rowsToKeep]
1207
1208 minN = galsim.Sersic._minimum_n
1209 maxN = galsim.Sersic._maximum_n
1210 rowsWithGoodSersic = (((fakeCat['bulge_n'] >= minN) & (fakeCat['bulge_n'] <= maxN)
1211 & (fakeCat['disk_n'] >= minN) & (fakeCat['disk_n'] <= maxN))
1212 | (fakeCat[self.config.sourceType] == starCheckVal))
1213 numRowsNotUsed = len(fakeCat) - len(np.where(rowsWithGoodSersic)[0])
1214 self.log.info("Removing %d rows of galaxies with nBulge or nDisk outside of %0.2f <= n <= %0.2f",
1215 numRowsNotUsed, minN, maxN)
1216 fakeCat = fakeCat[rowsWithGoodSersic]
1217
1218 if self.config.doSubSelectSources:
1219 numRowsNotUsed = len(fakeCat) - len(fakeCat['select'])
1220 self.log.info("Removing %d rows which were not designated as template sources", numRowsNotUsed)
1221 fakeCat = fakeCat[fakeCat['select']]
1222
1223 return fakeCat
1224
1225 def addFakeSources(self, image, fakeImages, sourceType):
1226 """Add the fake sources to the given image
1227
1228 Parameters
1229 ----------
1230 image : `lsst.afw.image.exposure.exposure.ExposureF`
1231 The image into which the fake sources should be added
1232 fakeImages : `typing.Iterator` [`tuple` ['lsst.afw.image.ImageF`, `lsst.geom.Point2d`]]
1233 An iterator of tuples that contains (or generates) images of fake sources,
1234 and the locations they are to be inserted at.
1235 sourceType : `str`
1236 The type (star/galaxy) of fake sources input
1237
1238 Returns
1239 -------
1240 image : `lsst.afw.image.exposure.exposure.ExposureF`
1241
1242 Notes
1243 -----
1244 Uses the x, y information in the ``fakeCat`` to position an image of the fake interpolated onto the
1245 pixel grid of the image. Sets the ``FAKE`` mask plane for the pixels added with the fake source.
1246 """
1247
1248 imageBBox = image.getBBox()
1249 imageMI = image.maskedImage
1250
1251 for (fakeImage, xy) in fakeImages:
1252 X0 = xy.getX() - fakeImage.getWidth()/2 + 0.5
1253 Y0 = xy.getY() - fakeImage.getHeight()/2 + 0.5
1254 self.log.debug("Adding fake source at %d, %d", xy.getX(), xy.getY())
1255 if sourceType == "galaxy":
1256 interpFakeImage = afwMath.offsetImage(fakeImage, X0, Y0, "lanczos3")
1257 else:
1258 interpFakeImage = fakeImage
1259
1260 interpFakeImBBox = interpFakeImage.getBBox()
1261 interpFakeImBBox.clip(imageBBox)
1262
1263 if interpFakeImBBox.getArea() > 0:
1264 imageMIView = imageMI[interpFakeImBBox]
1265 clippedFakeImage = interpFakeImage[interpFakeImBBox]
1266 clippedFakeImageMI = afwImage.MaskedImageF(clippedFakeImage)
1267 clippedFakeImageMI.mask.set(self.bitmask)
1268 imageMIView += clippedFakeImageMI
1269
1270 return image
1271
1272 def _getMetadataName(self):
1273 """Disable metadata writing"""
1274 return None
def addPixCoords(self, fakeCat, image)
def mkFakeStars(self, fakeCat, band, photoCalib, psf, image)
def mkFakeGalsimGalaxies(self, fakeCat, band, photoCalib, pixelScale, psf, image)
def cleanCat(self, fakeCat, starCheckVal)
def processImagesForInsertion(self, fakeCat, wcs, psf, photoCalib, band, pixelScale)
Definition: insertFakes.py:913
def trimFakeCat(self, fakeCat, image)
def addFakeSources(self, image, fakeImages, sourceType)