LSST Applications g02d81e74bb+86cf3d8bc9,g180d380827+7a4e862ed4,g2079a07aa2+86d27d4dc4,g2305ad1205+e1ca1c66fa,g29320951ab+012e1474a1,g295015adf3+341ea1ce94,g2bbee38e9b+0e5473021a,g337abbeb29+0e5473021a,g33d1c0ed96+0e5473021a,g3a166c0a6a+0e5473021a,g3ddfee87b4+c429d67c83,g48712c4677+f88676dd22,g487adcacf7+27e1e21933,g50ff169b8f+96c6868917,g52b1c1532d+585e252eca,g591dd9f2cf+b41db86c35,g5a732f18d5+53520f316c,g64a986408d+86cf3d8bc9,g858d7b2824+86cf3d8bc9,g8a8a8dda67+585e252eca,g99cad8db69+84912a7fdc,g9ddcbc5298+9a081db1e4,ga1e77700b3+15fc3df1f7,ga8c6da7877+a2b54eae19,gb0e22166c9+60f28cb32d,gba4ed39666+c2a2e4ac27,gbb8dafda3b+6681f309db,gc120e1dc64+f0fcc2f6d8,gc28159a63d+0e5473021a,gcf0d15dbbd+c429d67c83,gdaeeff99f8+f9a426f77a,ge6526c86ff+0433e6603d,ge79ae78c31+0e5473021a,gee10cc3b42+585e252eca,gff1a9f87cc+86cf3d8bc9,w.2024.17
LSST Data Management Base Package
Loading...
Searching...
No Matches
Public Member Functions | Static Public Member Functions | Public Attributes | Protected Member Functions | Protected Attributes | List of all members
lsst.meas.base.tests.TestDataset Class Reference

Public Member Functions

 __init__ (self, bbox, threshold=10.0, exposure=None, **kwds)
 
 makeMinimalSchema (cls)
 
 addSource (self, instFlux, centroid, shape=None, setPeakSignificance=True)
 
 addBlend (self)
 
 transform (self, wcs, **kwds)
 
 realize (self, noise, schema, randomSeed=1)
 

Static Public Member Functions

 makePerturbedWcs (oldWcs, minScaleFactor=1.2, maxScaleFactor=1.5, minRotation=None, maxRotation=None, minRefShift=None, maxRefShift=None, minPixShift=2.0, maxPixShift=4.0, randomSeed=1)
 
 makeEmptyExposure (bbox, wcs=None, crval=None, cdelt=None, psfSigma=2.0, psfDim=17, calibration=4, visitId=1234, mjd=60000.0)
 
 drawGaussian (bbox, instFlux, ellipse)
 

Public Attributes

 threshold
 
 exposure
 
 psfShape
 
 schema
 
 catalog
 
 keys
 

Protected Member Functions

 _installFootprint (self, record, image, setPeakSignificance=True)
 

Protected Attributes

 _schema
 

Detailed Description

A simulated dataset consisuting of test image and truth catalog.

TestDataset creates an idealized image made of pure Gaussians (including a
Gaussian PSF), with simple noise and idealized Footprints/HeavyFootprints
that simulated the outputs of detection and deblending.  Multiple noise
realizations can be created from the same underlying sources, allowing
uncertainty estimates to be verified via Monte Carlo.

Parameters
----------
bbox : `lsst.geom.Box2I` or `lsst.geom.Box2D`
    Bounding box of the test image.
threshold : `float`
    Threshold absolute value used to determine footprints for
    simulated sources.  This thresholding will be applied before noise is
    actually added to images (or before the noise level is even known), so
    this will necessarily produce somewhat artificial footprints.
exposure : `lsst.afw.image.ExposureF`
    The image to which test sources should be added. Ownership should
    be considered transferred from the caller to the TestDataset.
    Must have a Gaussian PSF for truth catalog shapes to be exact.
**kwds
    Keyword arguments forwarded to makeEmptyExposure if exposure is `None`.

Notes
-----
Typical usage:

.. code-block: py

    bbox = lsst.geom.Box2I(lsst.geom.Point2I(0,0), lsst.geom.Point2I(100,
                                                                     100))
    dataset = TestDataset(bbox)
    dataset.addSource(instFlux=1E5, centroid=lsst.geom.Point2D(25, 26))
    dataset.addSource(instFlux=2E5, centroid=lsst.geom.Point2D(75, 24),
                    shape=lsst.afw.geom.Quadrupole(8, 7, 2))
    with dataset.addBlend() as family:
        family.addChild(instFlux=2E5, centroid=lsst.geom.Point2D(50, 72))
        family.addChild(instFlux=1.5E5, centroid=lsst.geom.Point2D(51, 74))
    exposure, catalog = dataset.realize(noise=100.0,
                                        schema=TestDataset.makeMinimalSchema())

Definition at line 126 of file tests.py.

Constructor & Destructor Documentation

◆ __init__()

lsst.meas.base.tests.TestDataset.__init__ ( self,
bbox,
threshold = 10.0,
exposure = None,
** kwds )

Definition at line 170 of file tests.py.

170 def __init__(self, bbox, threshold=10.0, exposure=None, **kwds):
171 if exposure is None:
172 exposure = self.makeEmptyExposure(bbox, **kwds)
173 self.threshold = lsst.afw.detection.Threshold(threshold, lsst.afw.detection.Threshold.VALUE)
174 self.exposure = exposure
175 self.psfShape = self.exposure.getPsf().computeShape(bbox.getCenter())
176 self.schema = self.makeMinimalSchema()
177 self.catalog = lsst.afw.table.SourceCatalog(self.schema)
178
A Threshold is used to pass a threshold value to detection algorithms.
Definition Threshold.h:43

Member Function Documentation

◆ _installFootprint()

lsst.meas.base.tests.TestDataset._installFootprint ( self,
record,
image,
setPeakSignificance = True )
protected
Create simulated Footprint and add it to a truth catalog record.

Definition at line 410 of file tests.py.

410 def _installFootprint(self, record, image, setPeakSignificance=True):
411 """Create simulated Footprint and add it to a truth catalog record.
412 """
414 if setPeakSignificance:
415 schema.addField("significance", type=float,
416 doc="Ratio of peak value to configured standard deviation.")
417 # Run detection on the single-source image
418 fpSet = lsst.afw.detection.FootprintSet(image, self.threshold, peakSchema=schema)
419 # the call below to the FootprintSet ctor is actually a grow operation
420 fpSet = lsst.afw.detection.FootprintSet(fpSet, int(self.psfShape.getDeterminantRadius() + 1.0), True)
421 if setPeakSignificance:
422 # This isn't a traditional significance, since we're using the VALUE
423 # threshold type, but it's the best we can do in that case.
424 for footprint in fpSet.getFootprints():
425 footprint.updatePeakSignificance(self.threshold.getValue())
426 # Update the full exposure's mask plane to indicate the detection
427 fpSet.setMask(self.exposure.mask, "DETECTED")
428 # Attach the new footprint to the exposure
429 if len(fpSet.getFootprints()) > 1:
430 raise RuntimeError("Threshold value results in multiple Footprints for a single object")
431 if len(fpSet.getFootprints()) == 0:
432 raise RuntimeError("Threshold value results in zero Footprints for object")
433 record.setFootprint(fpSet.getFootprints()[0])
434
A set of Footprints, associated with a MaskedImage.
static afw::table::Schema makeMinimalSchema()
Return a minimal schema for Peak tables and records.
Definition Peak.h:137

◆ addBlend()

lsst.meas.base.tests.TestDataset.addBlend ( self)
Return a context manager which can add a blend of multiple sources.

Notes
-----
Note that nothing stops you from creating overlapping sources just using the addSource() method,
but addBlend() is necesssary to create a parent object and deblended HeavyFootprints of the type
produced by the detection and deblending pipelines.

Examples
--------
.. code-block: py
    d = TestDataset(...)
    with d.addBlend() as b:
        b.addChild(flux1, centroid1)
        b.addChild(flux2, centroid2, shape2)

Definition at line 489 of file tests.py.

489 def addBlend(self):
490 """Return a context manager which can add a blend of multiple sources.
491
492 Notes
493 -----
494 Note that nothing stops you from creating overlapping sources just using the addSource() method,
495 but addBlend() is necesssary to create a parent object and deblended HeavyFootprints of the type
496 produced by the detection and deblending pipelines.
497
498 Examples
499 --------
500 .. code-block: py
501 d = TestDataset(...)
502 with d.addBlend() as b:
503 b.addChild(flux1, centroid1)
504 b.addChild(flux2, centroid2, shape2)
505 """
506 return BlendContext(self)
507

◆ addSource()

lsst.meas.base.tests.TestDataset.addSource ( self,
instFlux,
centroid,
shape = None,
setPeakSignificance = True )
Add a source to the simulation.

To insert a point source with a given signal-to-noise (sn), the total
``instFlux`` should be: ``sn*noise*psf_scale``, where ``noise`` is the
noise you will pass to ``realize()``, and
``psf_scale=sqrt(4*pi*r^2)``, where ``r`` is the width of the PSF.

Parameters
----------
instFlux : `float`
    Total instFlux of the source to be added.
centroid : `lsst.geom.Point2D`
    Position of the source to be added.
shape : `lsst.afw.geom.Quadrupole`
    Second moments of the source before PSF convolution. Note that the
    truth catalog records post-convolution moments. If `None`, a point
    source will be added.
setPeakSignificance : `bool`
    Set the ``significance`` field for peaks in the footprints?
    See ``lsst.meas.algorithms.SourceDetectionTask.setPeakSignificance``
    for how this field is computed for real datasets.

Returns
-------
record : `lsst.afw.table.SourceRecord`
    A truth catalog record.
image : `lsst.afw.image.ImageF`
    Single-source image corresponding to the new source.

Definition at line 435 of file tests.py.

435 def addSource(self, instFlux, centroid, shape=None, setPeakSignificance=True):
436 """Add a source to the simulation.
437
438 To insert a point source with a given signal-to-noise (sn), the total
439 ``instFlux`` should be: ``sn*noise*psf_scale``, where ``noise`` is the
440 noise you will pass to ``realize()``, and
441 ``psf_scale=sqrt(4*pi*r^2)``, where ``r`` is the width of the PSF.
442
443 Parameters
444 ----------
445 instFlux : `float`
446 Total instFlux of the source to be added.
447 centroid : `lsst.geom.Point2D`
448 Position of the source to be added.
449 shape : `lsst.afw.geom.Quadrupole`
450 Second moments of the source before PSF convolution. Note that the
451 truth catalog records post-convolution moments. If `None`, a point
452 source will be added.
453 setPeakSignificance : `bool`
454 Set the ``significance`` field for peaks in the footprints?
455 See ``lsst.meas.algorithms.SourceDetectionTask.setPeakSignificance``
456 for how this field is computed for real datasets.
457
458 Returns
459 -------
460 record : `lsst.afw.table.SourceRecord`
461 A truth catalog record.
462 image : `lsst.afw.image.ImageF`
463 Single-source image corresponding to the new source.
464 """
465 # Create and set the truth catalog fields
466 record = self.catalog.addNew()
467 record.set(self.keys["instFlux"], instFlux)
468 record.set(self.keys["instFluxErr"], 0)
469 record.set(self.keys["centroid"], centroid)
470 covariance = np.random.normal(0, 0.1, 4).reshape(2, 2)
471 covariance[0, 1] = covariance[1, 0] # CovarianceMatrixKey assumes symmetric x_y_Cov
472 record.set(self.keys["centroid_sigma"], covariance.astype(np.float32))
473 if shape is None:
474 record.set(self.keys["isStar"], True)
475 fullShape = self.psfShape
476 else:
477 record.set(self.keys["isStar"], False)
478 fullShape = shape.convolve(self.psfShape)
479 record.set(self.keys["shape"], fullShape)
480 # Create an image containing just this source
481 image = self.drawGaussian(self.exposure.getBBox(), instFlux,
482 lsst.afw.geom.Ellipse(fullShape, centroid))
483 # Generate a footprint for this source
484 self._installFootprint(record, image, setPeakSignificance)
485 # Actually add the source to the full exposure
486 self.exposure.image.array[:, :] += image.array
487 return record, image
488
An ellipse defined by an arbitrary BaseCore and a center point.
Definition Ellipse.h:51

◆ drawGaussian()

lsst.meas.base.tests.TestDataset.drawGaussian ( bbox,
instFlux,
ellipse )
static
Create an image of an elliptical Gaussian.

Parameters
----------
bbox : `lsst.geom.Box2I` or `lsst.geom.Box2D`
    Bounding box of image to create.
instFlux : `float`
    Total instrumental flux of the Gaussian (normalized analytically,
    not using pixel values).
ellipse : `lsst.afw.geom.Ellipse`
    Defines the centroid and shape.

Returns
-------
image : `lsst.afw.image.ImageF`
    An image of the Gaussian.

Definition at line 383 of file tests.py.

383 def drawGaussian(bbox, instFlux, ellipse):
384 """Create an image of an elliptical Gaussian.
385
386 Parameters
387 ----------
388 bbox : `lsst.geom.Box2I` or `lsst.geom.Box2D`
389 Bounding box of image to create.
390 instFlux : `float`
391 Total instrumental flux of the Gaussian (normalized analytically,
392 not using pixel values).
393 ellipse : `lsst.afw.geom.Ellipse`
394 Defines the centroid and shape.
395
396 Returns
397 -------
398 image : `lsst.afw.image.ImageF`
399 An image of the Gaussian.
400 """
401 x, y = np.meshgrid(np.arange(bbox.getBeginX(), bbox.getEndX()),
402 np.arange(bbox.getBeginY(), bbox.getEndY()))
403 t = ellipse.getGridTransform()
404 xt = t[t.XX] * x + t[t.XY] * y + t[t.X]
405 yt = t[t.YX] * x + t[t.YY] * y + t[t.Y]
406 image = lsst.afw.image.ImageF(bbox)
407 image.array[:, :] = np.exp(-0.5*(xt**2 + yt**2))*instFlux/(2.0*ellipse.getCore().getArea())
408 return image
409

◆ makeEmptyExposure()

lsst.meas.base.tests.TestDataset.makeEmptyExposure ( bbox,
wcs = None,
crval = None,
cdelt = None,
psfSigma = 2.0,
psfDim = 17,
calibration = 4,
visitId = 1234,
mjd = 60000.0 )
static
Create an Exposure, with a PhotoCalib, Wcs, and Psf, but no pixel values.

Parameters
----------
bbox : `lsst.geom.Box2I` or `lsst.geom.Box2D`
    Bounding box of the image in image coordinates.
wcs : `lsst.afw.geom.SkyWcs`, optional
    New WCS for the exposure (created from CRVAL and CDELT if `None`).
crval : `lsst.afw.geom.SpherePoint`, optional
    ICRS center of the TAN WCS attached to the image. If `None`, (45
    degrees, 45 degrees) is assumed.
cdelt : `lsst.geom.Angle`, optional
    Pixel scale of the image. If `None`, 0.2 arcsec is assumed.
psfSigma : `float`, optional
    Radius (sigma) of the Gaussian PSF attached to the image
psfDim : `int`, optional
    Width and height of the image's Gaussian PSF attached to the image
calibration : `float`, optional
    The spatially-constant calibration (in nJy/count) to set the
    PhotoCalib of the exposure.
visitId : `int`, optional
    Visit id to store in VisitInfo.
mjd : `float`, optional
    Modified Julian Date of this exposure to store in VisitInfo.

Returns
-------
exposure : `lsst.age.image.ExposureF`
    An empty image.

Definition at line 326 of file tests.py.

327 visitId=1234, mjd=60000.0):
328 """Create an Exposure, with a PhotoCalib, Wcs, and Psf, but no pixel values.
329
330 Parameters
331 ----------
332 bbox : `lsst.geom.Box2I` or `lsst.geom.Box2D`
333 Bounding box of the image in image coordinates.
334 wcs : `lsst.afw.geom.SkyWcs`, optional
335 New WCS for the exposure (created from CRVAL and CDELT if `None`).
336 crval : `lsst.afw.geom.SpherePoint`, optional
337 ICRS center of the TAN WCS attached to the image. If `None`, (45
338 degrees, 45 degrees) is assumed.
339 cdelt : `lsst.geom.Angle`, optional
340 Pixel scale of the image. If `None`, 0.2 arcsec is assumed.
341 psfSigma : `float`, optional
342 Radius (sigma) of the Gaussian PSF attached to the image
343 psfDim : `int`, optional
344 Width and height of the image's Gaussian PSF attached to the image
345 calibration : `float`, optional
346 The spatially-constant calibration (in nJy/count) to set the
347 PhotoCalib of the exposure.
348 visitId : `int`, optional
349 Visit id to store in VisitInfo.
350 mjd : `float`, optional
351 Modified Julian Date of this exposure to store in VisitInfo.
352
353 Returns
354 -------
355 exposure : `lsst.age.image.ExposureF`
356 An empty image.
357 """
358 if wcs is None:
359 if crval is None:
360 crval = lsst.geom.SpherePoint(45.0, 45.0, lsst.geom.degrees)
361 if cdelt is None:
362 cdelt = 0.2*lsst.geom.arcseconds
363 crpix = lsst.geom.Box2D(bbox).getCenter()
364 wcs = lsst.afw.geom.makeSkyWcs(crpix=crpix, crval=crval,
365 cdMatrix=lsst.afw.geom.makeCdMatrix(scale=cdelt))
366 exposure = lsst.afw.image.ExposureF(bbox)
367 psf = lsst.afw.detection.GaussianPsf(psfDim, psfDim, psfSigma)
368 photoCalib = lsst.afw.image.PhotoCalib(calibration)
369 visitInfo = lsst.afw.image.VisitInfo(id=visitId,
370 exposureTime=30.0,
371 date=lsst.daf.base.DateTime(mjd),
372 observatory=lsst.afw.coord.Observatory(11.1*lsst.geom.degrees,
373 22.2*lsst.geom.degrees,
374 0.333),
375 hasSimulatedContent=True)
376 exposure.setWcs(wcs)
377 exposure.setPsf(psf)
378 exposure.setPhotoCalib(photoCalib)
379 exposure.info.setVisitInfo(visitInfo)
380 return exposure
381
Hold the location of an observatory.
Definition Observatory.h:43
A circularly symmetric Gaussian Psf class with no spatial variation, intended mostly for testing purp...
Definition GaussianPsf.h:42
The photometric calibration of an exposure.
Definition PhotoCalib.h:114
Information about a single exposure of an imaging camera.
Definition VisitInfo.h:68
Class for handling dates/times, including MJD, UTC, and TAI.
Definition DateTime.h:64
A floating-point coordinate rectangle geometry.
Definition Box.h:413
Point in an unspecified spherical coordinate system.
Definition SpherePoint.h:57
std::shared_ptr< SkyWcs > makeSkyWcs(daf::base::PropertySet &metadata, bool strip=false)
Construct a SkyWcs from FITS keywords.
Definition SkyWcs.cc:521
Eigen::Matrix2d makeCdMatrix(lsst::geom::Angle const &scale, lsst::geom::Angle const &orientation=0 *lsst::geom::degrees, bool flipX=false)
Make a WCS CD matrix.
Definition SkyWcs.cc:133

◆ makeMinimalSchema()

lsst.meas.base.tests.TestDataset.makeMinimalSchema ( cls)
Return the minimal schema needed to hold truth catalog fields.

Notes
-----
When `TestDataset.realize` is called, the schema must include at least
these fields.  Usually it will include additional fields for
measurement algorithm outputs, allowing the same catalog to be used
for both truth values (the fields from the minimal schema) and the
measurements.

Definition at line 180 of file tests.py.

180 def makeMinimalSchema(cls):
181 """Return the minimal schema needed to hold truth catalog fields.
182
183 Notes
184 -----
185 When `TestDataset.realize` is called, the schema must include at least
186 these fields. Usually it will include additional fields for
187 measurement algorithm outputs, allowing the same catalog to be used
188 for both truth values (the fields from the minimal schema) and the
189 measurements.
190 """
191 if not hasattr(cls, "_schema"):
193 cls.keys = {}
194 cls.keys["coordErr"] = lsst.afw.table.CoordKey.addErrorFields(schema)
195 cls.keys["parent"] = schema.find("parent").key
196 cls.keys["nChild"] = schema.addField("deblend_nChild", type=np.int32)
197 cls.keys["instFlux"] = schema.addField("truth_instFlux", type=np.float64,
198 doc="true instFlux", units="count")
199 cls.keys["instFluxErr"] = schema.addField("truth_instFluxErr", type=np.float64,
200 doc="true instFluxErr", units="count")
201 cls.keys["centroid"] = lsst.afw.table.Point2DKey.addFields(
202 schema, "truth", "true simulated centroid", "pixel"
203 )
204 cls.keys["centroid_sigma"] = lsst.afw.table.CovarianceMatrix2fKey.addFields(
205 schema, "truth", ['x', 'y'], "pixel"
206 )
207 cls.keys["centroid_flag"] = schema.addField("truth_flag", type="Flag",
208 doc="set if the object is a star")
209 cls.keys["shape"] = lsst.afw.table.QuadrupoleKey.addFields(
210 schema, "truth", "true shape after PSF convolution", lsst.afw.table.CoordinateType.PIXEL
211 )
212 cls.keys["isStar"] = schema.addField("truth_isStar", type="Flag",
213 doc="set if the object is a star")
214 schema.getAliasMap().set("slot_Shape", "truth")
215 schema.getAliasMap().set("slot_Centroid", "truth")
216 schema.getAliasMap().set("slot_ModelFlux", "truth")
217 cls._schema = schema
218 schema = lsst.afw.table.Schema(cls._schema)
219 schema.disconnectAliases()
220 return schema
221
static ErrorKey addErrorFields(Schema &schema)
static QuadrupoleKey addFields(Schema &schema, std::string const &name, std::string const &doc, CoordinateType coordType=CoordinateType::PIXEL)
Add a set of quadrupole subfields to a schema and return a QuadrupoleKey that points to them.
Defines the fields and offsets for a table.
Definition Schema.h:51
static Schema makeMinimalSchema()
Return a minimal schema for Source tables and records.
Definition Source.h:258
daf::base::PropertySet * set
Definition fits.cc:931

◆ makePerturbedWcs()

lsst.meas.base.tests.TestDataset.makePerturbedWcs ( oldWcs,
minScaleFactor = 1.2,
maxScaleFactor = 1.5,
minRotation = None,
maxRotation = None,
minRefShift = None,
maxRefShift = None,
minPixShift = 2.0,
maxPixShift = 4.0,
randomSeed = 1 )
static
Return a perturbed version of the input WCS.

Create a new undistorted TAN WCS that is similar but not identical to
another, with random scaling, rotation, and offset (in both pixel
position and reference position).

Parameters
----------
oldWcs : `lsst.afw.geom.SkyWcs`
    The input WCS.
minScaleFactor : `float`
    Minimum scale factor to apply to the input WCS.
maxScaleFactor : `float`
    Maximum scale factor to apply to the input WCS.
minRotation : `lsst.geom.Angle` or `None`
    Minimum rotation to apply to the input WCS. If `None`, defaults to
    30 degrees.
maxRotation : `lsst.geom.Angle` or `None`
    Minimum rotation to apply to the input WCS. If `None`, defaults to
    60 degrees.
minRefShift : `lsst.geom.Angle` or `None`
    Miniumum shift to apply to the input WCS reference value. If
    `None`, defaults to 0.5 arcsec.
maxRefShift : `lsst.geom.Angle` or `None`
    Miniumum shift to apply to the input WCS reference value. If
    `None`, defaults to 1.0 arcsec.
minPixShift : `float`
    Minimum shift to apply to the input WCS reference pixel.
maxPixShift : `float`
    Maximum shift to apply to the input WCS reference pixel.
randomSeed : `int`
    Random seed.

Returns
-------
newWcs : `lsst.afw.geom.SkyWcs`
    A perturbed version of the input WCS.

Notes
-----
The maximum and minimum arguments are interpreted as absolute values
for a split range that covers both positive and negative values (as
this method is used in testing, it is typically most important to
avoid perturbations near zero). Scale factors are treated somewhat
differently: the actual scale factor is chosen between
``minScaleFactor`` and ``maxScaleFactor`` OR (``1/maxScaleFactor``)
and (``1/minScaleFactor``).

The default range for rotation is 30-60 degrees, and the default range
for reference shift is 0.5-1.0 arcseconds (these cannot be safely
included directly as default values because Angle objects are
mutable).

The random number generator is primed with the seed given. If
`None`, a seed is automatically chosen.

Definition at line 223 of file tests.py.

226 minPixShift=2.0, maxPixShift=4.0, randomSeed=1):
227 """Return a perturbed version of the input WCS.
228
229 Create a new undistorted TAN WCS that is similar but not identical to
230 another, with random scaling, rotation, and offset (in both pixel
231 position and reference position).
232
233 Parameters
234 ----------
235 oldWcs : `lsst.afw.geom.SkyWcs`
236 The input WCS.
237 minScaleFactor : `float`
238 Minimum scale factor to apply to the input WCS.
239 maxScaleFactor : `float`
240 Maximum scale factor to apply to the input WCS.
241 minRotation : `lsst.geom.Angle` or `None`
242 Minimum rotation to apply to the input WCS. If `None`, defaults to
243 30 degrees.
244 maxRotation : `lsst.geom.Angle` or `None`
245 Minimum rotation to apply to the input WCS. If `None`, defaults to
246 60 degrees.
247 minRefShift : `lsst.geom.Angle` or `None`
248 Miniumum shift to apply to the input WCS reference value. If
249 `None`, defaults to 0.5 arcsec.
250 maxRefShift : `lsst.geom.Angle` or `None`
251 Miniumum shift to apply to the input WCS reference value. If
252 `None`, defaults to 1.0 arcsec.
253 minPixShift : `float`
254 Minimum shift to apply to the input WCS reference pixel.
255 maxPixShift : `float`
256 Maximum shift to apply to the input WCS reference pixel.
257 randomSeed : `int`
258 Random seed.
259
260 Returns
261 -------
262 newWcs : `lsst.afw.geom.SkyWcs`
263 A perturbed version of the input WCS.
264
265 Notes
266 -----
267 The maximum and minimum arguments are interpreted as absolute values
268 for a split range that covers both positive and negative values (as
269 this method is used in testing, it is typically most important to
270 avoid perturbations near zero). Scale factors are treated somewhat
271 differently: the actual scale factor is chosen between
272 ``minScaleFactor`` and ``maxScaleFactor`` OR (``1/maxScaleFactor``)
273 and (``1/minScaleFactor``).
274
275 The default range for rotation is 30-60 degrees, and the default range
276 for reference shift is 0.5-1.0 arcseconds (these cannot be safely
277 included directly as default values because Angle objects are
278 mutable).
279
280 The random number generator is primed with the seed given. If
281 `None`, a seed is automatically chosen.
282 """
283 random_state = np.random.RandomState(randomSeed)
284 if minRotation is None:
285 minRotation = 30.0*lsst.geom.degrees
286 if maxRotation is None:
287 maxRotation = 60.0*lsst.geom.degrees
288 if minRefShift is None:
289 minRefShift = 0.5*lsst.geom.arcseconds
290 if maxRefShift is None:
291 maxRefShift = 1.0*lsst.geom.arcseconds
292
293 def splitRandom(min1, max1, min2=None, max2=None):
294 if min2 is None:
295 min2 = -max1
296 if max2 is None:
297 max2 = -min1
298 if random_state.uniform() > 0.5:
299 return float(random_state.uniform(min1, max1))
300 else:
301 return float(random_state.uniform(min2, max2))
302 # Generate random perturbations
303 scaleFactor = splitRandom(minScaleFactor, maxScaleFactor, 1.0/maxScaleFactor, 1.0/minScaleFactor)
304 rotation = splitRandom(minRotation.asRadians(), maxRotation.asRadians())*lsst.geom.radians
305 refShiftRa = splitRandom(minRefShift.asRadians(), maxRefShift.asRadians())*lsst.geom.radians
306 refShiftDec = splitRandom(minRefShift.asRadians(), maxRefShift.asRadians())*lsst.geom.radians
307 pixShiftX = splitRandom(minPixShift, maxPixShift)
308 pixShiftY = splitRandom(minPixShift, maxPixShift)
309 # Compute new CD matrix
310 oldTransform = lsst.geom.LinearTransform(oldWcs.getCdMatrix())
311 rTransform = lsst.geom.LinearTransform.makeRotation(rotation)
312 sTransform = lsst.geom.LinearTransform.makeScaling(scaleFactor)
313 newTransform = oldTransform*rTransform*sTransform
314 matrix = newTransform.getMatrix()
315 # Compute new coordinate reference pixel (CRVAL)
316 oldSkyOrigin = oldWcs.getSkyOrigin()
317 newSkyOrigin = lsst.geom.SpherePoint(oldSkyOrigin.getRa() + refShiftRa,
318 oldSkyOrigin.getDec() + refShiftDec)
319 # Compute new pixel reference pixel (CRPIX)
320 oldPixOrigin = oldWcs.getPixelOrigin()
321 newPixOrigin = lsst.geom.Point2D(oldPixOrigin.getX() + pixShiftX,
322 oldPixOrigin.getY() + pixShiftY)
323 return lsst.afw.geom.makeSkyWcs(crpix=newPixOrigin, crval=newSkyOrigin, cdMatrix=matrix)
324
A 2D linear coordinate transformation.
static LinearTransform makeRotation(Angle t) noexcept
static LinearTransform makeScaling(double s) noexcept

◆ realize()

lsst.meas.base.tests.TestDataset.realize ( self,
noise,
schema,
randomSeed = 1 )
Simulate an exposure and detection catalog for this dataset.

The simulation includes noise, and the detection catalog includes
`~lsst.afw.detection.heavyFootprint.HeavyFootprint`\ s.

Parameters
----------
noise : `float`
Standard deviation of noise to be added to the exposure.  The
noise will be Gaussian and constant, appropriate for the
sky-limited regime.
schema : `lsst.afw.table.Schema`
Schema of the new catalog to be created.  Must start with
``self.schema`` (i.e. ``schema.contains(self.schema)`` must be
`True`), but typically contains fields for already-configured
measurement algorithms as well.
randomSeed : `int`, optional
Seed for the random number generator.
If `None`, a seed is chosen automatically.

Returns
-------
`exposure` : `lsst.afw.image.ExposureF`
Simulated image.
`catalog` : `lsst.afw.table.SourceCatalog`
Simulated detection catalog.

Definition at line 557 of file tests.py.

557 def realize(self, noise, schema, randomSeed=1):
558 r"""Simulate an exposure and detection catalog for this dataset.
559
560 The simulation includes noise, and the detection catalog includes
561 `~lsst.afw.detection.heavyFootprint.HeavyFootprint`\ s.
562
563 Parameters
564 ----------
565 noise : `float`
566 Standard deviation of noise to be added to the exposure. The
567 noise will be Gaussian and constant, appropriate for the
568 sky-limited regime.
569 schema : `lsst.afw.table.Schema`
570 Schema of the new catalog to be created. Must start with
571 ``self.schema`` (i.e. ``schema.contains(self.schema)`` must be
572 `True`), but typically contains fields for already-configured
573 measurement algorithms as well.
574 randomSeed : `int`, optional
575 Seed for the random number generator.
576 If `None`, a seed is chosen automatically.
577
578 Returns
579 -------
580 `exposure` : `lsst.afw.image.ExposureF`
581 Simulated image.
582 `catalog` : `lsst.afw.table.SourceCatalog`
583 Simulated detection catalog.
584 """
585 random_state = np.random.RandomState(randomSeed)
586 assert schema.contains(self.schema)
587 mapper = lsst.afw.table.SchemaMapper(self.schema)
588 mapper.addMinimalSchema(self.schema, True)
589 exposure = self.exposure.clone()
590 exposure.variance.array[:, :] = noise**2
591 exposure.image.array[:, :] += random_state.randn(exposure.height, exposure.width)*noise
592 catalog = lsst.afw.table.SourceCatalog(schema)
593 catalog.extend(self.catalog, mapper=mapper)
594 # Loop over sources and generate new HeavyFootprints that divide up
595 # the noisy pixels, not the ideal no-noise pixels.
596 for record in catalog:
597 # parent objects have non-Heavy Footprints, which don't need to be
598 # updated after adding noise.
599 if record.getParent() == 0:
600 continue
601 # get flattened arrays that correspond to the no-noise and noisy
602 # parent images
603 parent = catalog.find(record.getParent())
604 footprint = parent.getFootprint()
605 parentFluxArrayNoNoise = np.zeros(footprint.getArea(), dtype=np.float32)
606 footprint.spans.flatten(parentFluxArrayNoNoise, self.exposure.image.array, self.exposure.getXY0())
607 parentFluxArrayNoisy = np.zeros(footprint.getArea(), dtype=np.float32)
608 footprint.spans.flatten(parentFluxArrayNoisy, exposure.image.array, exposure.getXY0())
609 oldHeavy = record.getFootprint()
610 fraction = (oldHeavy.getImageArray() / parentFluxArrayNoNoise)
611 # N.B. this isn't a copy ctor - it's a copy from a vanilla
612 # Footprint, so it doesn't copy the arrays we don't want to
613 # change, and hence we have to do that ourselves below.
614 newHeavy = lsst.afw.detection.HeavyFootprintF(oldHeavy)
615 newHeavy.getImageArray()[:] = parentFluxArrayNoisy*fraction
616 newHeavy.getMaskArray()[:] = oldHeavy.getMaskArray()
617 newHeavy.getVarianceArray()[:] = oldHeavy.getVarianceArray()
618 record.setFootprint(newHeavy)
619 lsst.afw.table.updateSourceCoords(exposure.wcs, catalog)
620 return exposure, catalog
621
622
A mapping between the keys of two Schemas, used to copy data between them.
void updateSourceCoords(geom::SkyWcs const &wcs, SourceCollection &sourceList, bool include_covariance=true)
Update sky coordinates in a collection of source objects.
Definition wcsUtils.cc:125

◆ transform()

lsst.meas.base.tests.TestDataset.transform ( self,
wcs,
** kwds )
Copy this dataset transformed to a new WCS, with new Psf and PhotoCalib.

Parameters
----------
wcs : `lsst.afw.geom.SkyWcs`
    WCS for the new dataset.
**kwds
    Additional keyword arguments passed on to
    `TestDataset.makeEmptyExposure`.  If not specified, these revert
    to the defaults for `~TestDataset.makeEmptyExposure`, not the
    values in the current dataset.

Returns
-------
newDataset : `TestDataset`
    Transformed copy of this dataset.

Definition at line 508 of file tests.py.

508 def transform(self, wcs, **kwds):
509 """Copy this dataset transformed to a new WCS, with new Psf and PhotoCalib.
510
511 Parameters
512 ----------
513 wcs : `lsst.afw.geom.SkyWcs`
514 WCS for the new dataset.
515 **kwds
516 Additional keyword arguments passed on to
517 `TestDataset.makeEmptyExposure`. If not specified, these revert
518 to the defaults for `~TestDataset.makeEmptyExposure`, not the
519 values in the current dataset.
520
521 Returns
522 -------
523 newDataset : `TestDataset`
524 Transformed copy of this dataset.
525 """
526 bboxD = lsst.geom.Box2D()
527 xyt = lsst.afw.geom.makeWcsPairTransform(self.exposure.getWcs(), wcs)
528 for corner in lsst.geom.Box2D(self.exposure.getBBox()).getCorners():
529 bboxD.include(xyt.applyForward(lsst.geom.Point2D(corner)))
530 bboxI = lsst.geom.Box2I(bboxD)
531 result = TestDataset(bbox=bboxI, wcs=wcs, **kwds)
532 oldPhotoCalib = self.exposure.getPhotoCalib()
533 newPhotoCalib = result.exposure.getPhotoCalib()
534 oldPsfShape = self.exposure.getPsf().computeShape(bboxD.getCenter())
535 for record in self.catalog:
536 if record.get(self.keys["nChild"]):
537 raise NotImplementedError("Transforming blended sources in TestDatasets is not supported")
538 magnitude = oldPhotoCalib.instFluxToMagnitude(record.get(self.keys["instFlux"]))
539 newFlux = newPhotoCalib.magnitudeToInstFlux(magnitude)
540 oldCentroid = record.get(self.keys["centroid"])
541 newCentroid = xyt.applyForward(oldCentroid)
542 if record.get(self.keys["isStar"]):
543 newDeconvolvedShape = None
544 else:
545 affine = lsst.afw.geom.linearizeTransform(xyt, oldCentroid)
546 oldFullShape = record.get(self.keys["shape"])
547 oldDeconvolvedShape = lsst.afw.geom.Quadrupole(
548 oldFullShape.getIxx() - oldPsfShape.getIxx(),
549 oldFullShape.getIyy() - oldPsfShape.getIyy(),
550 oldFullShape.getIxy() - oldPsfShape.getIxy(),
551 False
552 )
553 newDeconvolvedShape = oldDeconvolvedShape.transform(affine.getLinear())
554 result.addSource(newFlux, newCentroid, newDeconvolvedShape)
555 return result
556
table::Key< int > transform
An ellipse core with quadrupole moments as parameters.
Definition Quadrupole.h:47
An integer coordinate rectangle.
Definition Box.h:55
std::shared_ptr< TransformPoint2ToPoint2 > makeWcsPairTransform(SkyWcs const &src, SkyWcs const &dst)
A Transform obtained by putting two SkyWcs objects "back to back".
Definition SkyWcs.cc:146
lsst::geom::AffineTransform linearizeTransform(TransformPoint2ToPoint2 const &original, lsst::geom::Point2D const &inPoint)
Approximate a Transform by its local linearization.

Member Data Documentation

◆ _schema

lsst.meas.base.tests.TestDataset._schema
protected

Definition at line 217 of file tests.py.

◆ catalog

lsst.meas.base.tests.TestDataset.catalog

Definition at line 177 of file tests.py.

◆ exposure

lsst.meas.base.tests.TestDataset.exposure

Definition at line 174 of file tests.py.

◆ keys

lsst.meas.base.tests.TestDataset.keys

Definition at line 193 of file tests.py.

◆ psfShape

lsst.meas.base.tests.TestDataset.psfShape

Definition at line 175 of file tests.py.

◆ schema

lsst.meas.base.tests.TestDataset.schema

Definition at line 176 of file tests.py.

◆ threshold

lsst.meas.base.tests.TestDataset.threshold

Definition at line 173 of file tests.py.


The documentation for this class was generated from the following file: