24 from builtins
import zip
25 from builtins
import object
36 from .sfm
import SingleFrameMeasurementTask
37 from .forcedMeasurement
import ForcedMeasurementTask
38 from .
import CentroidResultKey
40 __all__ = (
"BlendContext",
"TestDataset",
"AlgorithmTestCase",
"TransformTestCase",
41 "SingleFramePluginTransformSetupHelper",
"ForcedPluginTransformSetupHelper",
42 "FluxTransformTestCase",
"CentroidTransformTestCase")
47 A Python context manager used to add multiple overlapping sources along with a parent source 48 that represents all of them together. 50 This is used as the return value for TestDataset.addBlend(), and this is the only way it should 51 be used. The only public method is addChild(). 64 def addChild(self, flux, centroid, shape=None):
66 Add a child source to the blend, and return the truth catalog record that corresponds to it. 68 @param[in] flux Total flux of the source to be added. 69 @param[in] centroid Position of the source to be added (lsst.afw.geom.Point2D). 70 @param[in] shape 2nd moments of the source before PSF convolution 71 (lsst.afw.geom.ellipses.Quadrupole). Note that the truth catalog 72 records post-convolution moments) 74 record, image = self.
owner.addSource(flux, centroid, shape)
77 self.
children.append((record, image))
90 flux += record.get(self.
owner.keys[
"flux"])
96 w = record.get(self.
owner.keys[
"flux"])/flux
97 x += record.get(self.
owner.keys[
"centroid"].getX())*w
98 y += record.get(self.
owner.keys[
"centroid"].getY())*w
105 w = record.get(self.
owner.keys[
"flux"])/flux
106 dx = record.get(self.
owner.keys[
"centroid"].getX()) - x
107 dy = record.get(self.
owner.keys[
"centroid"].getY()) - y
108 xx += (record.get(self.
owner.keys[
"shape"].getIxx()) + dx**2)*w
109 yy += (record.get(self.
owner.keys[
"shape"].getIyy()) + dy**2)*w
110 xy += (record.get(self.
owner.keys[
"shape"].getIxy()) + dx*dy)*w
116 deblend = lsst.afw.image.MaskedImageF(self.
owner.exposure.getMaskedImage(),
True)
118 deblend.getImage().getArray()[:, :] = image.getArray()
119 heavyFootprint = lsst.afw.detection.HeavyFootprintF(self.
parentRecord.getFootprint(), deblend)
120 record.setFootprint(heavyFootprint)
125 A simulated dataset consisting of a test image and an associated truth catalog. 127 TestDataset creates an idealized image made of pure Gaussians (including a Gaussian PSF), 128 with simple noise and idealized Footprints/HeavyFootprints that simulated the outputs 129 of detection and deblending. Multiple noise realizations can be created from the same 130 underlying sources, allowing uncertainty estimates to be verified via Monte Carlo. 134 bbox = lsst.afw.geom.Box2I(lsst.afw.geom.Point2I(0,0), lsst.afw.geom.Point2I(100, 100)) 135 dataset = TestDataset(bbox) 136 dataset.addSource(flux=1E5, centroid=lsst.afw.geom.Point2D(25, 26)) 137 dataset.addSource(flux=2E5, centroid=lsst.afw.geom.Point2D(75, 24), 138 shape=lsst.afw.geom.ellipses.Quadrupole(8, 7, 2)) 139 with dataset.addBlend() as family: 140 family.addChild(flux=2E5, centroid=lsst.afw.geom.Point2D(50, 72)) 141 family.addChild(flux=1.5E5, centroid=lsst.afw.geom.Point2D(51, 74)) 142 exposure, catalog = dataset.realize(noise=100.0, schema=TestDataset.makeMinimalSchema()) 148 """Return the minimal schema needed to hold truth catalog fields. 150 When TestDataset.realize() is called, the schema must include at least these fields. 151 Usually it will include additional fields for measurement algorithm outputs, allowing 152 the same catalog to be used for both truth values (the fields from the minimal schema) 153 and the measurements. 155 if not hasattr(cls,
"_schema"):
158 cls.
keys[
"parent"] = schema.find(
"parent").key
159 cls.
keys[
"nChild"] = schema.addField(
"deblend_nChild", type=np.int32)
160 cls.
keys[
"flux"] = schema.addField(
"truth_flux", type=np.float64, doc=
"true flux", units=
"count")
161 cls.
keys[
"centroid"] = lsst.afw.table.Point2DKey.addFields(
162 schema,
"truth",
"true simulated centroid",
"pixel" 164 cls.
keys[
"centroid_sigma"] = lsst.afw.table.CovarianceMatrix2fKey.addFields(
165 schema,
"truth", [
'x',
'y'],
"pixel" 167 cls.
keys[
"centroid_flag"] = schema.addField(
"truth_flag", type=
"Flag",
168 doc=
"set if the object is a star")
170 schema,
"truth",
"true shape after PSF convolution", lsst.afw.table.CoordinateType.PIXEL
172 cls.
keys[
"isStar"] = schema.addField(
"truth_isStar", type=
"Flag",
173 doc=
"set if the object is a star")
174 schema.getAliasMap().set(
"slot_Shape",
"truth")
175 schema.getAliasMap().set(
"slot_Centroid",
"truth")
176 schema.getAliasMap().set(
"slot_ModelFlux",
"truth")
177 schema.getCitizen().markPersistent()
180 schema.disconnectAliases()
185 minRotation=None, maxRotation=None,
186 minRefShift=None, maxRefShift=None,
187 minPixShift=2.0, maxPixShift=4.0, randomSeed=None):
189 Create a new undistorted TanWcs that is similar but not identical to another, with random 190 scaling, rotation, and offset (in both pixel position and reference position). 192 The maximum and minimum arguments are interpreted as absolute values for a split 193 range that covers both positive and negative values (as this method is used 194 in testing, it is typically most important to avoid perturbations near zero). 195 Scale factors are treated somewhat differently: the actual scale factor is chosen between 196 minScaleFactor and maxScaleFactor OR (1/maxScaleFactor) and (1/minScaleFactor). 198 The default range for rotation is 30-60 degrees, and the default range for reference shift 199 is 0.5-1.0 arcseconds (these cannot be safely included directly as default values because Angle 200 objects are mutable). 202 The random number generator is primed with the seed given. If ``None``, a seed is 203 automatically chosen. 205 random_state = np.random.RandomState(randomSeed)
206 if minRotation
is None:
207 minRotation = 30.0*lsst.afw.geom.degrees
208 if maxRotation
is None:
209 maxRotation = 60.0*lsst.afw.geom.degrees
210 if minRefShift
is None:
211 minRefShift = 0.5*lsst.afw.geom.arcseconds
212 if maxRefShift
is None:
213 maxRefShift = 1.0*lsst.afw.geom.arcseconds
215 def splitRandom(min1, max1, min2=None, max2=None):
220 if random_state.uniform() > 0.5:
221 return float(random_state.uniform(min1, max1))
223 return float(random_state.uniform(min2, max2))
225 scaleFactor = splitRandom(minScaleFactor, maxScaleFactor, 1.0/maxScaleFactor, 1.0/minScaleFactor)
226 rotation = splitRandom(minRotation.asRadians(), maxRotation.asRadians())*lsst.afw.geom.radians
227 refShiftRa = splitRandom(minRefShift.asRadians(), maxRefShift.asRadians())*lsst.afw.geom.radians
228 refShiftDec = splitRandom(minRefShift.asRadians(), maxRefShift.asRadians())*lsst.afw.geom.radians
229 pixShiftX = splitRandom(minPixShift, maxPixShift)
230 pixShiftY = splitRandom(minPixShift, maxPixShift)
235 newTransform = oldTransform*rTransform*sTransform
236 matrix = newTransform.getMatrix()
238 oldSkyOrigin = oldWcs.getSkyOrigin().toIcrs()
240 oldSkyOrigin.getDec() + refShiftDec)
242 oldPixOrigin = oldWcs.getPixelOrigin()
244 oldPixOrigin.getY() + pixShiftY)
246 matrix[0, 0], matrix[0, 1], matrix[1, 0], matrix[1, 1])
249 def makeEmptyExposure(bbox, wcs=None, crval=None, cdelt=None, psfSigma=2.0, psfDim=17, fluxMag0=1E12):
251 Create an Exposure, with a Calib, Wcs, and Psf, but no pixel values set. 253 @param[in] bbox Bounding box of the image (image coordinates) as returned by makeCatalog. 254 @param[in] wcs New Wcs for the exposure (created from crval and cdelt if None). 255 @param[in] crval afw.coord.Coord: center of the TAN WCS attached to the image. 256 @param[in] cdelt afw.geom.Angle: pixel scale of the image 257 @param[in] psfSigma Radius (sigma) of the Gaussian PSF attached to the image 258 @param[in] psfDim Width and height of the image's Gaussian PSF attached to the image 259 @param[in] fluxMag0 Flux at magnitude zero (in e-) used to set the Calib of the exposure. 265 cdelt = 0.2*lsst.afw.geom.arcseconds
268 exposure = lsst.afw.image.ExposureF(bbox)
271 calib.setFluxMag0(fluxMag0)
274 exposure.setCalib(calib)
280 Create an image of an elliptical Gaussian. 282 @param[in,out] bbox Bounding box of image to create. 283 @param[in] flux Total flux of the Gaussian (normalized analytically, not using pixel 285 @param[in] ellipse lsst.afw.geom.ellipses.Ellipse holding the centroid and shape. 287 x, y = np.meshgrid(np.arange(bbox.getBeginX(), bbox.getEndX()),
288 np.arange(bbox.getBeginY(), bbox.getEndY()))
289 t = ellipse.getGridTransform()
290 xt = t[t.XX] * x + t[t.XY] * y + t[t.X]
291 yt = t[t.YX] * x + t[t.YY] * y + t[t.Y]
292 image = lsst.afw.image.ImageF(bbox)
293 image.getArray()[:, :] = np.exp(-0.5*(xt**2 + yt**2))*flux/(2.0*ellipse.getCore().getArea())
296 def __init__(self, bbox, threshold=10.0, exposure=None, **kwds):
298 Initialize the dataset. 300 @param[in] bbox Bounding box of the test image. 301 @param[in] threshold Threshold absolute value used to determine footprints for 302 simulated sources. This thresholding will be applied before noise is 303 actually added to images (or before the noise level is even known), so 304 this will necessarily produce somewhat artificial footprints. 305 @param[in] exposure lsst.afw.image.ExposureF test sources should be added to. Ownership should 306 be considered transferred from the caller to the TestDataset. 307 Must have a Gaussian Psf for truth catalog shapes to be exact. 308 @param[in] **kwds Keyword arguments forwarded to makeEmptyExposure if exposure is None. 318 def _installFootprint(self, record, image):
319 """Create a Footprint for a simulated source and add it to its truth catalog record. 326 fpSet.setMask(self.
exposure.getMaskedImage().getMask(),
"DETECTED")
328 if len(fpSet.getFootprints()) > 1:
329 raise RuntimeError(
"Threshold value results in multiple Footprints for a single object")
330 if len(fpSet.getFootprints()) == 0:
331 raise RuntimeError(
"Threshold value results in zero Footprints for object")
332 record.setFootprint(fpSet.getFootprints()[0])
336 Add a source to the simulation 338 @param[in] flux Total flux of the source to be added. 339 @param[in] centroid Position of the source to be added (lsst.afw.geom.Point2D). 340 @param[in] shape 2nd moments of the source before PSF convolution 341 (lsst.afw.geom.ellipses.Quadrupole). Note that the truth catalog 342 records post-convolution moments). If None, a point source 345 @return a truth catalog record and single-source image corresponding to the new source. 349 record.set(self.
keys[
"flux"], flux)
350 record.set(self.
keys[
"centroid"], centroid)
351 covariance = np.random.normal(0, 0.1, 4).reshape(2, 2)
352 covariance[0, 1] = covariance[1, 0]
353 record.set(self.
keys[
"centroid_sigma"], covariance.astype(np.float32))
355 record.set(self.
keys[
"isStar"],
True)
358 record.set(self.
keys[
"isStar"],
False)
359 fullShape = shape.convolve(self.
psfShape)
360 record.set(self.
keys[
"shape"], fullShape)
367 self.
exposure.getMaskedImage().getImage().getArray()[:, :] += image.getArray()
372 Return a context manager that allows a blend of multiple sources to be added. 377 with d.addBlend() as b: 378 b.addChild(flux1, centroid1) 379 b.addChild(flux2, centroid2, shape2) 382 Note that nothing stops you from creating overlapping sources just using the addSource() method, 383 but addBlend() is necesssary to create a parent object and deblended HeavyFootprints of the type 384 produced by the detection and deblending pipelines. 390 Create a copy of the dataset transformed to a new WCS, with new Psf and Calib. 392 @param[in] wcs Wcs for the new dataset. 393 @param[in] **kwds Additional keyword arguments passed on to makeEmptyExposure. If not 394 specified, these revert to the defaults for makeEmptyExposure, not the 395 values in the current dataset. 404 newCalib = result.exposure.getCalib()
405 oldPsfShape = self.
exposure.getPsf().computeShape()
407 if record.get(self.
keys[
"nChild"]):
408 raise NotImplementedError(
"Transforming blended sources in TestDatasets is not supported")
409 magnitude = oldCalib.getMagnitude(record.get(self.
keys[
"flux"]))
410 newFlux = newCalib.getFlux(magnitude)
411 oldCentroid = record.get(self.
keys[
"centroid"])
412 newCentroid = xyt.forwardTransform(oldCentroid)
413 if record.get(self.
keys[
"isStar"]):
414 newDeconvolvedShape =
None 416 affine = xyt.linearizeForwardTransform(oldCentroid)
417 oldFullShape = record.get(self.
keys[
"shape"])
419 oldFullShape.getIxx() - oldPsfShape.getIxx(),
420 oldFullShape.getIyy() - oldPsfShape.getIyy(),
421 oldFullShape.getIxy() - oldPsfShape.getIxy(),
424 newDeconvolvedShape = oldDeconvolvedShape.transform(affine.getLinear())
425 result.addSource(newFlux, newCentroid, newDeconvolvedShape)
428 def realize(self, noise, schema, randomSeed=None):
430 Create a simulated with noise and a simulated post-detection catalog with (Heavy)Footprints. 432 @param[in] noise Standard deviation of noise to be added to the exposure. The noise will be 433 Gaussian and constant, appropriate for the sky-limited regime. 434 @param[in] schema Schema of the new catalog to be created. Must start with self.schema (i.e. 435 schema.contains(self.schema) must be True), but typically contains fields for 436 already-configured measurement algorithms as well. 437 @param[in] randomSeed Seed for the random number generator. If None, a seed is chosen automatically. 439 @return a tuple of (exposure, catalog) 441 random_state = np.random.RandomState(randomSeed)
442 assert schema.contains(self.
schema)
444 mapper.addMinimalSchema(self.
schema,
True)
446 exposure.getMaskedImage().getVariance().getArray()[:, :] = noise**2
447 exposure.getMaskedImage().getImage().getArray()[:, :] \
448 += random_state.randn(exposure.getHeight(), exposure.getWidth())*noise
450 catalog.extend(self.
catalog, mapper=mapper)
453 for record
in catalog:
455 if record.getParent() == 0:
458 parent = catalog.find(record.getParent())
459 footprint = parent.getFootprint()
460 parentFluxArrayNoNoise = np.zeros(footprint.getArea(), dtype=np.float32)
461 footprint.spans.flatten(parentFluxArrayNoNoise,
462 self.
exposure.getMaskedImage().getImage().getArray(),
464 parentFluxArrayNoisy = np.zeros(footprint.getArea(), dtype=np.float32)
465 footprint.spans.flatten(parentFluxArrayNoisy,
466 exposure.getMaskedImage().getImage().getArray(),
468 oldHeavy = record.getFootprint()
469 fraction = (oldHeavy.getImageArray() / parentFluxArrayNoNoise)
472 newHeavy = lsst.afw.detection.HeavyFootprintF(oldHeavy)
473 newHeavy.getImageArray()[:] = parentFluxArrayNoisy*fraction
474 newHeavy.getMaskArray()[:] = oldHeavy.getMaskArray()
475 newHeavy.getVarianceArray()[:] = oldHeavy.getVarianceArray()
476 record.setFootprint(newHeavy)
477 return exposure, catalog
483 """Convenience function to create a Config instance for SingleFrameMeasurementTask 485 The plugin and its dependencies will be the only plugins run, while the Centroid, Shape, 486 and ModelFlux slots will be set to the truth fields generated by the TestDataset class. 488 config = SingleFrameMeasurementTask.ConfigClass()
489 config.slots.centroid =
"truth" 490 config.slots.shape =
"truth" 491 config.slots.modelFlux =
None 492 config.slots.apFlux =
None 493 config.slots.psfFlux =
None 494 config.slots.instFlux =
None 495 config.slots.calibFlux =
None 496 config.plugins.names = (plugin,) + tuple(dependencies)
501 """Convenience function to create a SingleFrameMeasurementTask with a simple configuration. 505 raise ValueError(
"Either plugin or config argument must not be None")
508 schema = TestDataset.makeMinimalSchema()
510 schema.setAliasMap(
None)
511 if algMetadata
is None:
516 """Convenience function to create a Config instance for ForcedMeasurementTask 518 In addition to the plugins specified in the plugin and dependencies arguments, 519 the TransformedCentroid and TransformedShape plugins will be run and used as the 520 Centroid and Shape slots; these simply transform the reference catalog centroid 521 and shape to the measurement coordinate system. 523 config = ForcedMeasurementTask.ConfigClass()
524 config.slots.centroid =
"base_TransformedCentroid" 525 config.slots.shape =
"base_TransformedShape" 526 config.slots.modelFlux =
None 527 config.slots.apFlux =
None 528 config.slots.psfFlux =
None 529 config.slots.instFlux =
None 530 config.plugins.names = (plugin,) + tuple(dependencies) + (
"base_TransformedCentroid",
531 "base_TransformedShape")
536 """Convenience function to create a ForcedMeasurementTask with a simple configuration. 540 raise ValueError(
"Either plugin or config argument must not be None")
542 if refSchema
is None:
543 refSchema = TestDataset.makeMinimalSchema()
544 if algMetadata
is None:
551 Base class for testing measurement transformations. 553 We test both that the transform itself operates successfully (fluxes are 554 converted to magnitudes, flags are propagated properly) and that the 555 transform is registered as the default for the appropriate measurement 558 In the simple case of one-measurement-per-transformation, the developer 559 need not directly write any tests themselves: simply customizing the class 560 variables is all that is required. More complex measurements (e.g. 561 multiple aperture fluxes) require extra effort. 566 name =
"MeasurementTransformTest" 570 algorithmClass =
None 571 transformClass =
None 575 flagNames = (
"flag",)
580 singleFramePlugins = ()
585 self.
calexp = TestDataset.makeEmptyExposure(bbox)
586 self._setupTransform()
595 def _populateCatalog(self, baseNames):
597 for flagValue
in (
True,
False):
598 records.append(self.inputCat.addNew())
599 for baseName
in baseNames:
601 if records[-1].schema.join(baseName, flagName)
in records[-1].schema:
602 records[-1].set(records[-1].schema.join(baseName, flagName), flagValue)
603 self._setFieldsInRecords(records, baseName)
605 def _checkOutput(self, baseNames):
606 for inSrc, outSrc
in zip(self.inputCat, self.outputCat):
607 for baseName
in baseNames:
608 self._compareFieldsInRecords(inSrc, outSrc, baseName)
610 keyName = outSrc.schema.join(baseName, flagName)
611 if keyName
in inSrc.schema:
612 self.assertEqual(outSrc.get(keyName), inSrc.get(keyName))
614 self.assertFalse(keyName
in outSrc.schema)
616 def _runTransform(self, doExtend=True):
618 self.outputCat.extend(self.inputCat, mapper=self.mapper)
619 self.transform(self.inputCat, self.outputCat, self.
calexp.getWcs(), self.
calexp.getCalib())
623 Test the operation of the transformation on a catalog containing random data. 627 * An appropriate exception is raised on an attempt to transform between catalogs with different 629 * Otherwise, all appropriate conversions are properly appled and that flags have been propagated. 631 The `baseNames` argument requires some explanation. This should be an iterable of the leading parts of 632 the field names for each measurement; that is, everything that appears before `_flux`, `_flag`, etc. 633 In the simple case of a single measurement per plugin, this is simply equal to `self.name` (thus 634 measurements are stored as `self.name + "_flux"`, etc). More generally, the developer may specify 635 whatever iterable they require. For example, to handle multiple apertures, we could have 636 `(self.name + "_0", self.name + "_1", ...)`. 638 @param[in] baseNames Iterable of the initial parts of measurement field names. 640 baseNames = baseNames
or [self.
name]
646 def _checkRegisteredTransform(self, registry, name):
649 self.assertEqual(registry[name].PluginClass.getTransformClass(), self.
transformClass)
653 Test that the transformation is appropriately registered with the relevant measurement algorithms. 663 def _setupTransform(self):
669 inputSchema.getAliasMap().set(
"slot_Centroid",
"dummy")
670 inputSchema.getAliasMap().set(
"slot_Shape",
"dummy")
671 self.algorithmClass(self.
control, self.name, inputSchema)
672 inputSchema.getAliasMap().erase(
"slot_Centroid")
673 inputSchema.getAliasMap().erase(
"slot_Shape")
682 def _setupTransform(self):
689 inputMapper.editOutputSchema().getAliasMap().set(
"slot_Centroid",
"dummy")
690 inputMapper.editOutputSchema().getAliasMap().set(
"slot_Shape",
"dummy")
692 inputMapper.editOutputSchema().getAliasMap().erase(
"slot_Centroid")
693 inputMapper.editOutputSchema().getAliasMap().erase(
"slot_Shape")
702 def _setFieldsInRecords(self, records, name):
703 for record
in records:
704 record[record.schema.join(name,
'flux')] = np.random.random()
705 record[record.schema.join(name,
'fluxSigma')] = np.random.random()
708 assert len(records) > 1
709 records[0][record.schema.join(name,
'flux')] = -1
711 def _compareFieldsInRecords(self, inSrc, outSrc, name):
712 fluxName, fluxSigmaName = inSrc.schema.join(name,
'flux'), inSrc.schema.join(name,
'fluxSigma')
713 if inSrc[fluxName] > 0:
714 mag, magErr = self.
calexp.getCalib().getMagnitude(inSrc[fluxName], inSrc[fluxSigmaName])
715 self.assertEqual(outSrc[outSrc.schema.join(name,
'mag')], mag)
716 self.assertEqual(outSrc[outSrc.schema.join(name,
'magErr')], magErr)
718 self.assertTrue(np.isnan(outSrc[outSrc.schema.join(name,
'mag')]))
719 self.assertTrue(np.isnan(outSrc[outSrc.schema.join(name,
'magErr')]))
724 def _setFieldsInRecords(self, records, name):
725 for record
in records:
726 record[record.schema.join(name,
'x')] = np.random.random()
727 record[record.schema.join(name,
'y')] = np.random.random()
730 for fieldSuffix
in (
'xSigma',
'ySigma',
'x_y_Cov'):
731 fieldName = record.schema.join(name, fieldSuffix)
732 if fieldName
in record.schema:
733 record[fieldName] = np.random.random()
735 def _compareFieldsInRecords(self, inSrc, outSrc, name):
736 centroidResultKey = CentroidResultKey(inSrc.schema[self.
name])
737 centroidResult = centroidResultKey.get(inSrc)
740 coordTruth = self.
calexp.getWcs().pixelToSky(centroidResult.getCentroid())
741 self.assertEqual(coordTruth, coord)
746 coordErr = lsst.afw.table.CovarianceMatrix2fKey(outSrc.schema[self.
name],
747 [
"ra",
"dec"]).get(outSrc)
749 self.assertFalse(centroidResultKey.getCentroidErr().isValid())
751 transform = self.
calexp.getWcs().linearizePixelToSky(coordTruth, lsst.afw.geom.radians)
752 coordErrTruth = np.dot(np.dot(transform.getLinear().getMatrix(),
753 centroidResult.getCentroidErr()),
754 transform.getLinear().getMatrix().transpose())
755 np.testing.assert_array_almost_equal(np.array(coordErrTruth), coordErr)
def realize(self, noise, schema, randomSeed=None)
Create a simulated with noise and a simulated post-detection catalog with (Heavy)Footprints.
def makeSingleFrameMeasurementConfig(self, plugin=None, dependencies=())
def transform(self, wcs, kwds)
Create a copy of the dataset transformed to a new WCS, with new Psf and Calib.
def makePerturbedWcs(oldWcs, minScaleFactor=1.2, maxScaleFactor=1.5, minRotation=None, maxRotation=None, minRefShift=None, maxRefShift=None, minPixShift=2.0, maxPixShift=4.0, randomSeed=None)
Create a new undistorted TanWcs that is similar but not identical to another, with random scaling...
std::shared_ptr< Wcs > makeWcs(coord::Coord const &crval, geom::Point2D const &crpix, double CD11, double CD12, double CD21, double CD22)
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 __init__(self, owner)
static QuadrupoleKey addFields(Schema &schema, std::string const &name, std::string const &doc, CoordinateType coordType=CoordinateType::PIXEL)
def drawGaussian(bbox, flux, ellipse)
Create an image of an elliptical Gaussian.
def _installFootprint(self, record, image)
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)
def addChild(self, flux, centroid, shape=None)
Add a child source to the blend, and return the truth catalog record that corresponds to it...
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...