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