379 photoRefObjLoader=None, icSourceSchema=None,
380 initInputs=None, **kwargs):
383 if initInputs
is not None:
384 icSourceSchema = initInputs[
'icSourceSchema'].schema
386 if icSourceSchema
is not None:
389 minimumSchema = afwTable.SourceTable.makeMinimalSchema()
390 self.
schemaMapper.addMinimalSchema(minimumSchema,
False)
398 afwTable.Field[
"Flag"](
"calib_detected",
399 "Source was detected as an icSource"))
400 missingFieldNames = []
401 for fieldName
in self.config.icSourceFieldsToCopy:
403 schemaItem = icSourceSchema.find(fieldName)
405 missingFieldNames.append(fieldName)
410 if missingFieldNames:
411 raise RuntimeError(
"isSourceCat is missing fields {} "
412 "specified in icSourceFieldsToCopy"
413 .format(missingFieldNames))
420 self.
schema = afwTable.SourceTable.makeMinimalSchema()
421 afwTable.CoordKey.addErrorFields(self.
schema)
422 self.makeSubtask(
'detection', schema=self.
schema)
426 if self.config.doDeblend:
427 self.makeSubtask(
"deblend", schema=self.
schema)
428 if self.config.doSkySources:
429 self.makeSubtask(
"skySources")
431 self.makeSubtask(
'measurement', schema=self.
schema,
433 self.makeSubtask(
'postCalibrationMeasurement', schema=self.
schema,
435 self.makeSubtask(
"setPrimaryFlags", schema=self.
schema, isSingleFrame=
True)
436 if self.config.doApCorr:
437 self.makeSubtask(
'applyApCorr', schema=self.
schema)
438 self.makeSubtask(
'catalogCalculation', schema=self.
schema)
440 if self.config.doAstrometry:
441 self.makeSubtask(
"astrometry", refObjLoader=astromRefObjLoader,
443 if self.config.doPhotoCal:
444 self.makeSubtask(
"photoCal", refObjLoader=photoRefObjLoader,
446 if self.config.doComputeSummaryStats:
447 self.makeSubtask(
'computeSummaryStats')
449 if initInputs
is not None and (astromRefObjLoader
is not None or photoRefObjLoader
is not None):
450 raise RuntimeError(
"PipelineTask form of this task should not be initialized with "
451 "reference object loaders.")
456 self.
schema.checkUnits(parse_strict=self.config.checkUnitsParseStrict)
458 sourceCatSchema = afwTable.SourceCatalog(self.
schema)
463 inputs = butlerQC.get(inputRefs)
464 inputs[
'idGenerator'] = self.config.idGenerator.apply(butlerQC.quantum.dataId)
466 if self.config.doAstrometry:
467 refObjLoader = ReferenceObjectLoader(dataIds=[ref.datasetRef.dataId
468 for ref
in inputRefs.astromRefCat],
469 refCats=inputs.pop(
'astromRefCat'),
470 name=self.config.connections.astromRefCat,
471 config=self.config.astromRefObjLoader, log=self.log)
472 self.astrometry.setRefObjLoader(refObjLoader)
474 if self.config.doPhotoCal:
475 photoRefObjLoader = ReferenceObjectLoader(dataIds=[ref.datasetRef.dataId
476 for ref
in inputRefs.photoRefCat],
477 refCats=inputs.pop(
'photoRefCat'),
478 name=self.config.connections.photoRefCat,
479 config=self.config.photoRefObjLoader,
481 self.photoCal.match.setRefObjLoader(photoRefObjLoader)
483 outputs = self.
run(**inputs)
485 if self.config.doWriteMatches
and self.config.doAstrometry:
486 if outputs.astromMatches
is not None:
487 normalizedMatches = afwTable.packMatches(outputs.astromMatches)
488 normalizedMatches.table.setMetadata(outputs.matchMeta)
489 if self.config.doWriteMatchesDenormalized:
490 denormMatches = denormalizeMatches(outputs.astromMatches, outputs.matchMeta)
491 outputs.matchesDenormalized = denormMatches
492 outputs.matches = normalizedMatches
494 del outputRefs.matches
495 if self.config.doWriteMatchesDenormalized:
496 del outputRefs.matchesDenormalized
497 butlerQC.put(outputs, outputRefs)
500 def run(self, exposure, background=None,
501 icSourceCat=None, idGenerator=None):
502 """Calibrate an exposure.
506 exposure : `lsst.afw.image.ExposureF`
507 Exposure to calibrate.
508 background : `lsst.afw.math.BackgroundList`, optional
509 Initial model of background already subtracted from exposure.
510 icSourceCat : `lsst.afw.image.SourceCatalog`, optional
511 SourceCatalog from CharacterizeImageTask from which we can copy
513 idGenerator : `lsst.meas.base.IdGenerator`, optional
514 Object that generates source IDs and provides RNG seeds.
518 result : `lsst.pipe.base.Struct`
519 Results as a struct with attributes:
522 Characterized exposure (`lsst.afw.image.ExposureF`).
524 Detected sources (`lsst.afw.table.SourceCatalog`).
526 Model of subtracted background (`lsst.afw.math.BackgroundList`).
528 List of source/ref matches from astrometry solver.
530 Metadata from astrometry matches.
532 Another reference to ``exposure`` for compatibility.
534 Another reference to ``sourceCat`` for compatibility.
537 if idGenerator
is None:
538 idGenerator = IdGenerator()
540 if background
is None:
541 background = BackgroundList()
542 table = SourceTable.make(self.
schema, idGenerator.make_table_id_factory())
545 detRes = self.detection.run(table=table, exposure=exposure,
547 sourceCat = detRes.sources
548 if detRes.background:
549 for bg
in detRes.background:
550 background.append(bg)
551 if self.config.doSkySources:
552 skySourceFootprints = self.skySources.run(mask=exposure.mask, seed=idGenerator.catalog_id)
553 if skySourceFootprints:
554 for foot
in skySourceFootprints:
555 s = sourceCat.addNew()
558 if self.config.doDeblend:
559 self.deblend.run(exposure=exposure, sources=sourceCat)
560 self.measurement.run(
563 exposureId=idGenerator.catalog_id,
565 if self.config.doApCorr:
566 apCorrMap = exposure.getInfo().getApCorrMap()
567 if apCorrMap
is None:
568 self.log.warning(
"Image does not have valid aperture correction map for %r; "
569 "skipping aperture correction", idGenerator)
571 self.applyApCorr.run(
575 self.catalogCalculation.run(sourceCat)
577 self.setPrimaryFlags.run(sourceCat)
579 if icSourceCat
is not None and \
580 len(self.config.icSourceFieldsToCopy) > 0:
588 if not sourceCat.isContiguous():
589 sourceCat = sourceCat.copy(deep=
True)
595 if self.config.doAstrometry:
596 astromRes = self.astrometry.run(
600 astromMatches = astromRes.matches
601 matchMeta = astromRes.matchMeta
602 if exposure.getWcs()
is None:
603 if self.config.requireAstrometry:
604 raise RuntimeError(f
"WCS fit failed for {idGenerator} and requireAstrometry "
607 self.log.warning(
"Unable to perform astrometric calibration for %r but "
608 "requireAstrometry is False: attempting to proceed...",
612 if self.config.doPhotoCal:
613 if np.all(np.isnan(sourceCat[
"coord_ra"]))
or np.all(np.isnan(sourceCat[
"coord_dec"])):
614 if self.config.requirePhotoCal:
615 raise RuntimeError(f
"Astrometry failed for {idGenerator}, so cannot do "
616 "photoCal, but requirePhotoCal is True.")
617 self.log.warning(
"Astrometry failed for %r, so cannot do photoCal. requirePhotoCal "
618 "is False, so skipping photometric calibration and setting photoCalib "
619 "to None. Attempting to proceed...", idGenerator)
620 exposure.setPhotoCalib(
None)
624 photoRes = self.photoCal.run(
625 exposure, sourceCat=sourceCat, expId=idGenerator.catalog_id
627 exposure.setPhotoCalib(photoRes.photoCalib)
630 self.log.info(
"Photometric zero-point: %f",
631 photoRes.photoCalib.instFluxToMagnitude(1.0))
632 self.
setMetadata(exposure=exposure, photoRes=photoRes)
633 except Exception
as e:
634 if self.config.requirePhotoCal:
636 self.log.warning(
"Unable to perform photometric calibration "
637 "(%s): attempting to proceed", e)
640 self.postCalibrationMeasurement.run(
643 exposureId=idGenerator.catalog_id,
646 if self.config.doComputeSummaryStats:
647 summary = self.computeSummaryStats.run(exposure=exposure,
649 background=background)
650 exposure.getInfo().setSummaryStats(summary)
652 frame = getDebugFrame(self._display,
"calibrate")
657 matches=astromMatches,
662 return pipeBase.Struct(
664 astromMatches=astromMatches,
666 outputExposure=exposure,
668 outputBackground=background,
708 """Match sources in an icSourceCat and a sourceCat and copy fields.
710 The fields copied are those specified by
711 ``config.icSourceFieldsToCopy``.
715 icSourceCat : `lsst.afw.table.SourceCatalog`
716 Catalog from which to copy fields.
717 sourceCat : `lsst.afw.table.SourceCatalog`
718 Catalog to which to copy fields.
723 Raised if any of the following occur:
724 - icSourceSchema and icSourceKeys are not specified.
725 - icSourceCat and sourceCat are not specified.
726 - icSourceFieldsToCopy is empty.
729 raise RuntimeError(
"To copy icSource fields you must specify "
730 "icSourceSchema and icSourceKeys when "
731 "constructing this task")
732 if icSourceCat
is None or sourceCat
is None:
733 raise RuntimeError(
"icSourceCat and sourceCat must both be "
735 if len(self.config.icSourceFieldsToCopy) == 0:
736 self.log.warning(
"copyIcSourceFields doing nothing because "
737 "icSourceFieldsToCopy is empty")
740 mc = afwTable.MatchControl()
741 mc.findOnlyClosest =
False
742 matches = afwTable.matchXy(icSourceCat, sourceCat,
743 self.config.matchRadiusPix, mc)
744 if self.config.doDeblend:
745 deblendKey = sourceCat.schema[
"deblend_nChild"].asKey()
747 matches = [m
for m
in matches
if m[1].get(deblendKey) == 0]
754 for m0, m1, d
in matches:
756 match = bestMatches.get(id0)
757 if match
is None or d <= match[2]:
758 bestMatches[id0] = (m0, m1, d)
759 matches = list(bestMatches.values())
764 numMatches = len(matches)
765 numUniqueSources = len(set(m[1].getId()
for m
in matches))
766 if numUniqueSources != numMatches:
767 self.log.warning(
"%d icSourceCat sources matched only %d sourceCat "
768 "sources", numMatches, numUniqueSources)
770 self.log.info(
"Copying flags from icSourceCat to sourceCat for "
771 "%d sources", numMatches)
775 for icSrc, src, d
in matches:
781 icSrcFootprint = icSrc.getFootprint()
783 icSrc.setFootprint(src.getFootprint())
786 icSrc.setFootprint(icSrcFootprint)