33 from .sfm
import SingleFrameMeasurementTask
34 from .forcedMeasurement
import ForcedMeasurementTask
35 from .
import CentroidResultKey
37 __all__ = (
"BlendContext",
"TestDataset",
"AlgorithmTestCase",
"TransformTestCase",
38 "SingleFramePluginTransformSetupHelper",
"ForcedPluginTransformSetupHelper",
39 "FluxTransformTestCase",
"CentroidTransformTestCase")
44 A Python context manager used to add multiple overlapping sources along with a parent source 45 that represents all of them together. 47 This is used as the return value for TestDataset.addBlend(), and this is the only way it should 48 be used. The only public method is addChild(). 61 def addChild(self, flux, centroid, shape=None):
63 Add a child source to the blend, and return the truth catalog record that corresponds to it. 65 @param[in] flux Total flux of the source to be added. 66 @param[in] centroid Position of the source to be added (lsst.geom.Point2D). 67 @param[in] shape 2nd moments of the source before PSF convolution 68 (lsst.afw.geom.Quadrupole). Note that the truth catalog 69 records post-convolution moments) 71 record, image = self.
owner.addSource(flux, centroid, shape)
74 self.
children.append((record, image))
87 flux += record.get(self.
owner.keys[
"flux"])
93 w = record.get(self.
owner.keys[
"flux"])/flux
94 x += record.get(self.
owner.keys[
"centroid"].getX())*w
95 y += record.get(self.
owner.keys[
"centroid"].getY())*w
102 w = record.get(self.
owner.keys[
"flux"])/flux
103 dx = record.get(self.
owner.keys[
"centroid"].getX()) - x
104 dy = record.get(self.
owner.keys[
"centroid"].getY()) - y
105 xx += (record.get(self.
owner.keys[
"shape"].getIxx()) + dx**2)*w
106 yy += (record.get(self.
owner.keys[
"shape"].getIyy()) + dy**2)*w
107 xy += (record.get(self.
owner.keys[
"shape"].getIxy()) + dx*dy)*w
108 self.
parentRecord.set(self.
owner.keys[
"shape"], lsst.afw.geom.Quadrupole(xx, yy, xy))
113 deblend = lsst.afw.image.MaskedImageF(self.
owner.exposure.getMaskedImage(),
True)
115 deblend.getImage().getArray()[:, :] = image.getArray()
116 heavyFootprint = lsst.afw.detection.HeavyFootprintF(self.
parentRecord.getFootprint(), deblend)
117 record.setFootprint(heavyFootprint)
122 A simulated dataset consisting of a test image and an associated truth catalog. 124 TestDataset creates an idealized image made of pure Gaussians (including a Gaussian PSF), 125 with simple noise and idealized Footprints/HeavyFootprints that simulated the outputs 126 of detection and deblending. Multiple noise realizations can be created from the same 127 underlying sources, allowing uncertainty estimates to be verified via Monte Carlo. 131 bbox = lsst.geom.Box2I(lsst.geom.Point2I(0,0), lsst.geom.Point2I(100, 100)) 132 dataset = TestDataset(bbox) 133 dataset.addSource(flux=1E5, centroid=lsst.geom.Point2D(25, 26)) 134 dataset.addSource(flux=2E5, centroid=lsst.geom.Point2D(75, 24), 135 shape=lsst.afw.geom.Quadrupole(8, 7, 2)) 136 with dataset.addBlend() as family: 137 family.addChild(flux=2E5, centroid=lsst.geom.Point2D(50, 72)) 138 family.addChild(flux=1.5E5, centroid=lsst.geom.Point2D(51, 74)) 139 exposure, catalog = dataset.realize(noise=100.0, schema=TestDataset.makeMinimalSchema()) 145 """Return the minimal schema needed to hold truth catalog fields. 147 When TestDataset.realize() is called, the schema must include at least these fields. 148 Usually it will include additional fields for measurement algorithm outputs, allowing 149 the same catalog to be used for both truth values (the fields from the minimal schema) 150 and the measurements. 152 if not hasattr(cls,
"_schema"):
155 cls.
keys[
"parent"] = schema.find(
"parent").key
156 cls.
keys[
"nChild"] = schema.addField(
"deblend_nChild", type=np.int32)
157 cls.
keys[
"flux"] = schema.addField(
"truth_flux", type=np.float64, doc=
"true flux", units=
"count")
158 cls.
keys[
"centroid"] = lsst.afw.table.Point2DKey.addFields(
159 schema,
"truth",
"true simulated centroid",
"pixel" 161 cls.
keys[
"centroid_sigma"] = lsst.afw.table.CovarianceMatrix2fKey.addFields(
162 schema,
"truth", [
'x',
'y'],
"pixel" 164 cls.
keys[
"centroid_flag"] = schema.addField(
"truth_flag", type=
"Flag",
165 doc=
"set if the object is a star")
167 schema,
"truth",
"true shape after PSF convolution", lsst.afw.table.CoordinateType.PIXEL
169 cls.
keys[
"isStar"] = schema.addField(
"truth_isStar", type=
"Flag",
170 doc=
"set if the object is a star")
171 schema.getAliasMap().set(
"slot_Shape",
"truth")
172 schema.getAliasMap().set(
"slot_Centroid",
"truth")
173 schema.getAliasMap().set(
"slot_ModelFlux",
"truth")
174 schema.getCitizen().markPersistent()
177 schema.disconnectAliases()
182 minRotation=None, maxRotation=None,
183 minRefShift=None, maxRefShift=None,
184 minPixShift=2.0, maxPixShift=4.0, randomSeed=1):
186 Create a new undistorted TAN WCS that is similar but not identical to another, with random 187 scaling, rotation, and offset (in both pixel position and reference position). 189 The maximum and minimum arguments are interpreted as absolute values for a split 190 range that covers both positive and negative values (as this method is used 191 in testing, it is typically most important to avoid perturbations near zero). 192 Scale factors are treated somewhat differently: the actual scale factor is chosen between 193 minScaleFactor and maxScaleFactor OR (1/maxScaleFactor) and (1/minScaleFactor). 195 The default range for rotation is 30-60 degrees, and the default range for reference shift 196 is 0.5-1.0 arcseconds (these cannot be safely included directly as default values because Angle 197 objects are mutable). 199 The random number generator is primed with the seed given. If ``None``, a seed is 200 automatically chosen. 202 random_state = np.random.RandomState(randomSeed)
203 if minRotation
is None:
204 minRotation = 30.0*lsst.geom.degrees
205 if maxRotation
is None:
206 maxRotation = 60.0*lsst.geom.degrees
207 if minRefShift
is None:
208 minRefShift = 0.5*lsst.geom.arcseconds
209 if maxRefShift
is None:
210 maxRefShift = 1.0*lsst.geom.arcseconds
212 def splitRandom(min1, max1, min2=None, max2=None):
217 if random_state.uniform() > 0.5:
218 return float(random_state.uniform(min1, max1))
220 return float(random_state.uniform(min2, max2))
222 scaleFactor = splitRandom(minScaleFactor, maxScaleFactor, 1.0/maxScaleFactor, 1.0/minScaleFactor)
223 rotation = splitRandom(minRotation.asRadians(), maxRotation.asRadians())*lsst.geom.radians
224 refShiftRa = splitRandom(minRefShift.asRadians(), maxRefShift.asRadians())*lsst.geom.radians
225 refShiftDec = splitRandom(minRefShift.asRadians(), maxRefShift.asRadians())*lsst.geom.radians
226 pixShiftX = splitRandom(minPixShift, maxPixShift)
227 pixShiftY = splitRandom(minPixShift, maxPixShift)
232 newTransform = oldTransform*rTransform*sTransform
233 matrix = newTransform.getMatrix()
235 oldSkyOrigin = oldWcs.getSkyOrigin()
237 oldSkyOrigin.getDec() + refShiftDec)
239 oldPixOrigin = oldWcs.getPixelOrigin()
241 oldPixOrigin.getY() + pixShiftY)
245 def makeEmptyExposure(bbox, wcs=None, crval=None, cdelt=None, psfSigma=2.0, psfDim=17, fluxMag0=1E12):
247 Create an Exposure, with a Calib, Wcs, and Psf, but no pixel values set. 249 @param[in] bbox Bounding box of the image (image coordinates) as returned by makeCatalog. 250 @param[in] wcs New Wcs for the exposure (created from crval and cdelt if None). 251 @param[in] crval afw.geom.SpherePoint: ICRS center of the TAN WCS attached to the image. 252 @param[in] cdelt afw.geom.Angle: pixel scale of the image 253 @param[in] psfSigma Radius (sigma) of the Gaussian PSF attached to the image 254 @param[in] psfDim Width and height of the image's Gaussian PSF attached to the image 255 @param[in] fluxMag0 Flux at magnitude zero (in e-) used to set the Calib of the exposure. 261 cdelt = 0.2*lsst.geom.arcseconds
265 exposure = lsst.afw.image.ExposureF(bbox)
268 calib.setFluxMag0(fluxMag0)
271 exposure.setCalib(calib)
277 Create an image of an elliptical Gaussian. 279 @param[in,out] bbox Bounding box of image to create. 280 @param[in] flux Total flux of the Gaussian (normalized analytically, not using pixel 282 @param[in] ellipse lsst.afw.geom.Ellipse holding the centroid and shape. 284 x, y = np.meshgrid(np.arange(bbox.getBeginX(), bbox.getEndX()),
285 np.arange(bbox.getBeginY(), bbox.getEndY()))
286 t = ellipse.getGridTransform()
287 xt = t[t.XX] * x + t[t.XY] * y + t[t.X]
288 yt = t[t.YX] * x + t[t.YY] * y + t[t.Y]
289 image = lsst.afw.image.ImageF(bbox)
290 image.getArray()[:, :] = np.exp(-0.5*(xt**2 + yt**2))*flux/(2.0*ellipse.getCore().getArea())
293 def __init__(self, bbox, threshold=10.0, exposure=None, **kwds):
295 Initialize the dataset. 297 @param[in] bbox Bounding box of the test image. 298 @param[in] threshold Threshold absolute value used to determine footprints for 299 simulated sources. This thresholding will be applied before noise is 300 actually added to images (or before the noise level is even known), so 301 this will necessarily produce somewhat artificial footprints. 302 @param[in] exposure lsst.afw.image.ExposureF test sources should be added to. Ownership should 303 be considered transferred from the caller to the TestDataset. 304 Must have a Gaussian Psf for truth catalog shapes to be exact. 305 @param[in] **kwds Keyword arguments forwarded to makeEmptyExposure if exposure is None. 315 def _installFootprint(self, record, image):
316 """Create a Footprint for a simulated source and add it to its truth catalog record. 323 fpSet.setMask(self.
exposure.getMaskedImage().getMask(),
"DETECTED")
325 if len(fpSet.getFootprints()) > 1:
326 raise RuntimeError(
"Threshold value results in multiple Footprints for a single object")
327 if len(fpSet.getFootprints()) == 0:
328 raise RuntimeError(
"Threshold value results in zero Footprints for object")
329 record.setFootprint(fpSet.getFootprints()[0])
333 Add a source to the simulation 335 @param[in] flux Total flux of the source to be added. 336 @param[in] centroid Position of the source to be added (lsst.geom.Point2D). 337 @param[in] shape 2nd moments of the source before PSF convolution 338 (lsst.afw.geom.Quadrupole). Note that the truth catalog 339 records post-convolution moments). If None, a point source 342 @return a truth catalog record and single-source image corresponding to the new source. 346 record.set(self.
keys[
"flux"], flux)
347 record.set(self.
keys[
"centroid"], centroid)
348 covariance = np.random.normal(0, 0.1, 4).reshape(2, 2)
349 covariance[0, 1] = covariance[1, 0]
350 record.set(self.
keys[
"centroid_sigma"], covariance.astype(np.float32))
352 record.set(self.
keys[
"isStar"],
True)
355 record.set(self.
keys[
"isStar"],
False)
356 fullShape = shape.convolve(self.
psfShape)
357 record.set(self.
keys[
"shape"], fullShape)
360 lsst.afw.geom.Ellipse(fullShape, centroid))
364 self.
exposure.getMaskedImage().getImage().getArray()[:, :] += image.getArray()
369 Return a context manager that allows a blend of multiple sources to be added. 374 with d.addBlend() as b: 375 b.addChild(flux1, centroid1) 376 b.addChild(flux2, centroid2, shape2) 379 Note that nothing stops you from creating overlapping sources just using the addSource() method, 380 but addBlend() is necesssary to create a parent object and deblended HeavyFootprints of the type 381 produced by the detection and deblending pipelines. 387 Create a copy of the dataset transformed to a new WCS, with new Psf and Calib. 389 @param[in] wcs Wcs for the new dataset. 390 @param[in] **kwds Additional keyword arguments passed on to makeEmptyExposure. If not 391 specified, these revert to the defaults for makeEmptyExposure, not the 392 values in the current dataset. 401 newCalib = result.exposure.getCalib()
402 oldPsfShape = self.
exposure.getPsf().computeShape()
404 if record.get(self.
keys[
"nChild"]):
405 raise NotImplementedError(
"Transforming blended sources in TestDatasets is not supported")
406 magnitude = oldCalib.getMagnitude(record.get(self.
keys[
"flux"]))
407 newFlux = newCalib.getFlux(magnitude)
408 oldCentroid = record.get(self.
keys[
"centroid"])
409 newCentroid = xyt.applyForward(oldCentroid)
410 if record.get(self.
keys[
"isStar"]):
411 newDeconvolvedShape =
None 414 oldFullShape = record.get(self.
keys[
"shape"])
415 oldDeconvolvedShape = lsst.afw.geom.Quadrupole(
416 oldFullShape.getIxx() - oldPsfShape.getIxx(),
417 oldFullShape.getIyy() - oldPsfShape.getIyy(),
418 oldFullShape.getIxy() - oldPsfShape.getIxy(),
421 newDeconvolvedShape = oldDeconvolvedShape.transform(affine.getLinear())
422 result.addSource(newFlux, newCentroid, newDeconvolvedShape)
425 def realize(self, noise, schema, randomSeed=1):
427 Create a simulated with noise and a simulated post-detection catalog with (Heavy)Footprints. 429 @param[in] noise Standard deviation of noise to be added to the exposure. The noise will be 430 Gaussian and constant, appropriate for the sky-limited regime. 431 @param[in] schema Schema of the new catalog to be created. Must start with self.schema (i.e. 432 schema.contains(self.schema) must be True), but typically contains fields for 433 already-configured measurement algorithms as well. 434 @param[in] randomSeed Seed for the random number generator. If None, a seed is chosen automatically. 436 @return a tuple of (exposure, catalog) 438 random_state = np.random.RandomState(randomSeed)
439 assert schema.contains(self.
schema)
441 mapper.addMinimalSchema(self.
schema,
True)
443 exposure.getMaskedImage().getVariance().getArray()[:, :] = noise**2
444 exposure.getMaskedImage().getImage().getArray()[:, :] \
445 += random_state.randn(exposure.getHeight(), exposure.getWidth())*noise
447 catalog.extend(self.
catalog, mapper=mapper)
450 for record
in catalog:
452 if record.getParent() == 0:
455 parent = catalog.find(record.getParent())
456 footprint = parent.getFootprint()
457 parentFluxArrayNoNoise = np.zeros(footprint.getArea(), dtype=np.float32)
458 footprint.spans.flatten(parentFluxArrayNoNoise,
459 self.
exposure.getMaskedImage().getImage().getArray(),
461 parentFluxArrayNoisy = np.zeros(footprint.getArea(), dtype=np.float32)
462 footprint.spans.flatten(parentFluxArrayNoisy,
463 exposure.getMaskedImage().getImage().getArray(),
465 oldHeavy = record.getFootprint()
466 fraction = (oldHeavy.getImageArray() / parentFluxArrayNoNoise)
469 newHeavy = lsst.afw.detection.HeavyFootprintF(oldHeavy)
470 newHeavy.getImageArray()[:] = parentFluxArrayNoisy*fraction
471 newHeavy.getMaskArray()[:] = oldHeavy.getMaskArray()
472 newHeavy.getVarianceArray()[:] = oldHeavy.getVarianceArray()
473 record.setFootprint(newHeavy)
474 return exposure, catalog
480 """Convenience function to create a Config instance for SingleFrameMeasurementTask 482 The plugin and its dependencies will be the only plugins run, while the Centroid, Shape, 483 and ModelFlux slots will be set to the truth fields generated by the TestDataset class. 485 config = SingleFrameMeasurementTask.ConfigClass()
486 config.slots.centroid =
"truth" 487 config.slots.shape =
"truth" 488 config.slots.modelFlux =
None 489 config.slots.apFlux =
None 490 config.slots.psfFlux =
None 491 config.slots.instFlux =
None 492 config.slots.calibFlux =
None 493 config.plugins.names = (plugin,) + tuple(dependencies)
498 """Convenience function to create a SingleFrameMeasurementTask with a simple configuration. 502 raise ValueError(
"Either plugin or config argument must not be None")
505 schema = TestDataset.makeMinimalSchema()
507 schema.setAliasMap(
None)
508 if algMetadata
is None:
513 """Convenience function to create a Config instance for ForcedMeasurementTask 515 In addition to the plugins specified in the plugin and dependencies arguments, 516 the TransformedCentroid and TransformedShape plugins will be run and used as the 517 Centroid and Shape slots; these simply transform the reference catalog centroid 518 and shape to the measurement coordinate system. 520 config = ForcedMeasurementTask.ConfigClass()
521 config.slots.centroid =
"base_TransformedCentroid" 522 config.slots.shape =
"base_TransformedShape" 523 config.slots.modelFlux =
None 524 config.slots.apFlux =
None 525 config.slots.psfFlux =
None 526 config.slots.instFlux =
None 527 config.plugins.names = (plugin,) + tuple(dependencies) + (
"base_TransformedCentroid",
528 "base_TransformedShape")
533 """Convenience function to create a ForcedMeasurementTask with a simple configuration. 537 raise ValueError(
"Either plugin or config argument must not be None")
539 if refSchema
is None:
540 refSchema = TestDataset.makeMinimalSchema()
541 if algMetadata
is None:
548 Base class for testing measurement transformations. 550 We test both that the transform itself operates successfully (fluxes are 551 converted to magnitudes, flags are propagated properly) and that the 552 transform is registered as the default for the appropriate measurement 555 In the simple case of one-measurement-per-transformation, the developer 556 need not directly write any tests themselves: simply customizing the class 557 variables is all that is required. More complex measurements (e.g. 558 multiple aperture fluxes) require extra effort. 563 name =
"MeasurementTransformTest" 567 algorithmClass =
None 568 transformClass =
None 572 flagNames = (
"flag",)
577 singleFramePlugins = ()
582 self.
calexp = TestDataset.makeEmptyExposure(bbox)
583 self._setupTransform()
592 def _populateCatalog(self, baseNames):
594 for flagValue
in (
True,
False):
595 records.append(self.inputCat.addNew())
596 for baseName
in baseNames:
598 if records[-1].schema.join(baseName, flagName)
in records[-1].schema:
599 records[-1].set(records[-1].schema.join(baseName, flagName), flagValue)
600 self._setFieldsInRecords(records, baseName)
602 def _checkOutput(self, baseNames):
603 for inSrc, outSrc
in zip(self.inputCat, self.outputCat):
604 for baseName
in baseNames:
605 self._compareFieldsInRecords(inSrc, outSrc, baseName)
607 keyName = outSrc.schema.join(baseName, flagName)
608 if keyName
in inSrc.schema:
609 self.assertEqual(outSrc.get(keyName), inSrc.get(keyName))
611 self.assertFalse(keyName
in outSrc.schema)
613 def _runTransform(self, doExtend=True):
615 self.outputCat.extend(self.inputCat, mapper=self.mapper)
616 self.transform(self.inputCat, self.outputCat, self.
calexp.getWcs(), self.
calexp.getCalib())
620 Test the operation of the transformation on a catalog containing random data. 624 * An appropriate exception is raised on an attempt to transform between catalogs with different 626 * Otherwise, all appropriate conversions are properly appled and that flags have been propagated. 628 The `baseNames` argument requires some explanation. This should be an iterable of the leading parts of 629 the field names for each measurement; that is, everything that appears before `_flux`, `_flag`, etc. 630 In the simple case of a single measurement per plugin, this is simply equal to `self.name` (thus 631 measurements are stored as `self.name + "_flux"`, etc). More generally, the developer may specify 632 whatever iterable they require. For example, to handle multiple apertures, we could have 633 `(self.name + "_0", self.name + "_1", ...)`. 635 @param[in] baseNames Iterable of the initial parts of measurement field names. 637 baseNames = baseNames
or [self.
name]
643 def _checkRegisteredTransform(self, registry, name):
646 self.assertEqual(registry[name].PluginClass.getTransformClass(), self.
transformClass)
650 Test that the transformation is appropriately registered with the relevant measurement algorithms. 660 def _setupTransform(self):
666 inputSchema.getAliasMap().set(
"slot_Centroid",
"dummy")
667 inputSchema.getAliasMap().set(
"slot_Shape",
"dummy")
668 self.algorithmClass(self.
control, self.name, inputSchema)
669 inputSchema.getAliasMap().erase(
"slot_Centroid")
670 inputSchema.getAliasMap().erase(
"slot_Shape")
679 def _setupTransform(self):
686 inputMapper.editOutputSchema().getAliasMap().set(
"slot_Centroid",
"dummy")
687 inputMapper.editOutputSchema().getAliasMap().set(
"slot_Shape",
"dummy")
689 inputMapper.editOutputSchema().getAliasMap().erase(
"slot_Centroid")
690 inputMapper.editOutputSchema().getAliasMap().erase(
"slot_Shape")
699 def _setFieldsInRecords(self, records, name):
700 for record
in records:
701 record[record.schema.join(name,
'flux')] = np.random.random()
702 record[record.schema.join(name,
'fluxSigma')] = np.random.random()
705 assert len(records) > 1
706 records[0][record.schema.join(name,
'flux')] = -1
708 def _compareFieldsInRecords(self, inSrc, outSrc, name):
709 fluxName, fluxSigmaName = inSrc.schema.join(name,
'flux'), inSrc.schema.join(name,
'fluxSigma')
710 if inSrc[fluxName] > 0:
711 mag, magErr = self.
calexp.getCalib().getMagnitude(inSrc[fluxName], inSrc[fluxSigmaName])
712 self.assertEqual(outSrc[outSrc.schema.join(name,
'mag')], mag)
713 self.assertEqual(outSrc[outSrc.schema.join(name,
'magErr')], magErr)
715 self.assertTrue(np.isnan(outSrc[outSrc.schema.join(name,
'mag')]))
716 self.assertTrue(np.isnan(outSrc[outSrc.schema.join(name,
'magErr')]))
721 def _setFieldsInRecords(self, records, name):
722 for record
in records:
723 record[record.schema.join(name,
'x')] = np.random.random()
724 record[record.schema.join(name,
'y')] = np.random.random()
727 for fieldSuffix
in (
'xSigma',
'ySigma',
'x_y_Cov'):
728 fieldName = record.schema.join(name, fieldSuffix)
729 if fieldName
in record.schema:
730 record[fieldName] = np.random.random()
732 def _compareFieldsInRecords(self, inSrc, outSrc, name):
733 centroidResultKey = CentroidResultKey(inSrc.schema[self.
name])
734 centroidResult = centroidResultKey.get(inSrc)
737 coordTruth = self.
calexp.getWcs().pixelToSky(centroidResult.getCentroid())
738 self.assertEqual(coordTruth, coord)
743 coordErr = lsst.afw.table.CovarianceMatrix2fKey(outSrc.schema[self.
name],
744 [
"ra",
"dec"]).get(outSrc)
746 self.assertFalse(centroidResultKey.getCentroidErr().isValid())
748 transform = self.
calexp.getWcs().linearizePixelToSky(coordTruth, lsst.geom.radians)
749 coordErrTruth = np.dot(np.dot(transform.getLinear().getMatrix(),
750 centroidResult.getCentroidErr()),
751 transform.getLinear().getMatrix().transpose())
752 np.testing.assert_array_almost_equal(np.array(coordErrTruth), coordErr)
def makeSingleFrameMeasurementConfig(self, plugin=None, dependencies=())
lsst::geom::AffineTransform linearizeTransform(TransformPoint2ToPoint2 const &original, lsst::geom::Point2D const &inPoint)
def transform(self, wcs, kwds)
Create a copy of the dataset transformed to a new WCS, with new Psf and Calib.
def __init__(self, bbox, threshold=10.0, exposure=None, kwds)
Initialize the dataset.
A subtask for measuring the properties of sources on a single exposure, using an existing "reference"...
A subtask for measuring the properties of sources on a single exposure.
def addBlend(self)
Return a context manager that allows a blend of multiple sources to be added.
def makeEmptyExposure(bbox, wcs=None, crval=None, cdelt=None, psfSigma=2.0, psfDim=17, fluxMag0=1E12)
Create an Exposure, with a Calib, Wcs, and Psf, but no pixel values set.
def makeSingleFrameMeasurementTask(self, plugin=None, dependencies=(), config=None, schema=None, algMetadata=None)
def realize(self, noise, schema, randomSeed=1)
Create a simulated with noise and a simulated post-detection catalog with (Heavy)Footprints.
def makePerturbedWcs(oldWcs, minScaleFactor=1.2, maxScaleFactor=1.5, minRotation=None, maxRotation=None, minRefShift=None, maxRefShift=None, minPixShift=2.0, maxPixShift=4.0, randomSeed=1)
Create a new undistorted TAN WCS that is similar but not identical to another, with random scaling...
def __init__(self, owner)
def drawGaussian(bbox, flux, ellipse)
Create an image of an elliptical Gaussian.
def _installFootprint(self, record, image)
static QuadrupoleKey addFields(Schema &schema, std::string const &name, std::string const &doc, CoordinateType coordType=CoordinateType::PIXEL)
def addSource(self, flux, centroid, shape=None)
Add a source to the simulation.
static Schema makeMinimalSchema()
A simulated dataset consisting of a test image and an associated truth catalog.
def makeForcedMeasurementTask(self, plugin=None, dependencies=(), config=None, refSchema=None, algMetadata=None)
std::shared_ptr< TransformPoint2ToPoint2 > makeWcsPairTransform(SkyWcs const &src, SkyWcs const &dst)
Eigen::Matrix2d makeCdMatrix(lsst::geom::Angle const &scale, lsst::geom::Angle const &orientation=0 *lsst::geom::degrees, bool flipX=false)
def addChild(self, flux, centroid, shape=None)
Add a child source to the blend, and return the truth catalog record that corresponds to it...
std::shared_ptr< SkyWcs > makeSkyWcs(lsst::geom::Point2D const &crpix, lsst::geom::SpherePoint const &crval, Eigen::Matrix2d const &cdMatrix, std::string const &projection="TAN")
def __exit__(self, type_, value, tb)
def makeForcedMeasurementConfig(self, plugin=None, dependencies=())
def makeMinimalSchema(cls)
A Python context manager used to add multiple overlapping sources along with a parent source that rep...