416 photoRefObjLoader=None, icSourceSchema=None,
417 initInputs=None, **kwargs):
418 super().__init__(**kwargs)
420 if initInputs
is not None:
421 icSourceSchema = initInputs[
'icSourceSchema'].schema
423 if icSourceSchema
is not None:
426 minimumSchema = afwTable.SourceTable.makeMinimalSchema()
427 self.
schemaMapper.addMinimalSchema(minimumSchema,
False)
436 "Source was detected as an icSource"))
437 missingFieldNames = []
438 for fieldName
in self.config.icSourceFieldsToCopy:
440 schemaItem = icSourceSchema.find(fieldName)
442 missingFieldNames.append(fieldName)
447 if missingFieldNames:
448 raise RuntimeError(
"isSourceCat is missing fields {} "
449 "specified in icSourceFieldsToCopy"
450 .format(missingFieldNames))
457 self.
schema = afwTable.SourceTable.makeMinimalSchema()
458 afwTable.CoordKey.addErrorFields(self.
schema)
459 self.makeSubtask(
'detection', schema=self.
schema)
463 if self.config.doDeblend:
464 self.makeSubtask(
"deblend", schema=self.
schema)
465 if self.config.doSkySources:
466 self.makeSubtask(
"skySources")
468 self.makeSubtask(
'measurement', schema=self.
schema,
470 self.makeSubtask(
'postCalibrationMeasurement', schema=self.
schema,
472 self.makeSubtask(
"setPrimaryFlags", schema=self.
schema, isSingleFrame=
True)
473 if self.config.doApCorr:
474 self.makeSubtask(
'applyApCorr', schema=self.
schema)
475 self.makeSubtask(
'catalogCalculation', schema=self.
schema)
477 if self.config.doAstrometry:
478 self.makeSubtask(
"astrometry", refObjLoader=astromRefObjLoader,
480 if self.config.doPhotoCal:
481 self.makeSubtask(
"photoCal", refObjLoader=photoRefObjLoader,
483 if self.config.doComputeSummaryStats:
484 self.makeSubtask(
'computeSummaryStats')
485 if self.config.doCreateSummaryMetrics:
486 self.makeSubtask(
'createSummaryMetrics')
488 if initInputs
is not None and (astromRefObjLoader
is not None or photoRefObjLoader
is not None):
489 raise RuntimeError(
"PipelineTask form of this task should not be initialized with "
490 "reference object loaders.")
495 self.
schema.checkUnits(parse_strict=self.config.checkUnitsParseStrict)
502 inputs = butlerQC.get(inputRefs)
503 inputs[
'idGenerator'] = self.config.idGenerator.apply(butlerQC.quantum.dataId)
505 if self.config.doAstrometry:
507 for ref
in inputRefs.astromRefCat],
508 refCats=inputs.pop(
'astromRefCat'),
509 name=self.config.connections.astromRefCat,
510 config=self.config.astromRefObjLoader, log=self.log)
511 self.astrometry.setRefObjLoader(refObjLoader)
513 if self.config.doPhotoCal:
515 for ref
in inputRefs.photoRefCat],
516 refCats=inputs.pop(
'photoRefCat'),
517 name=self.config.connections.photoRefCat,
518 config=self.config.photoRefObjLoader,
520 self.photoCal.match.setRefObjLoader(photoRefObjLoader)
522 outputs = self.
run(**inputs)
524 if self.config.doWriteMatches
and self.config.doAstrometry:
525 if outputs.astromMatches
is not None:
527 normalizedMatches.table.setMetadata(outputs.matchMeta)
528 if self.config.doWriteMatchesDenormalized:
529 denormMatches = denormalizeMatches(outputs.astromMatches, outputs.matchMeta)
530 outputs.matchesDenormalized = denormMatches
531 outputs.matches = normalizedMatches
533 del outputRefs.matches
534 if self.config.doWriteMatchesDenormalized:
535 del outputRefs.matchesDenormalized
536 butlerQC.put(outputs, outputRefs)
539 def run(self, exposure, background=None,
540 icSourceCat=None, idGenerator=None):
541 """Calibrate an exposure.
545 exposure : `lsst.afw.image.ExposureF`
546 Exposure to calibrate.
547 background : `lsst.afw.math.BackgroundList`, optional
548 Initial model of background already subtracted from exposure.
549 icSourceCat : `lsst.afw.image.SourceCatalog`, optional
550 SourceCatalog from CharacterizeImageTask from which we can copy
552 idGenerator : `lsst.meas.base.IdGenerator`, optional
553 Object that generates source IDs and provides RNG seeds.
557 result : `lsst.pipe.base.Struct`
558 Results as a struct with attributes:
561 Characterized exposure (`lsst.afw.image.ExposureF`).
563 Detected sources (`lsst.afw.table.SourceCatalog`).
565 Model of subtracted background (`lsst.afw.math.BackgroundList`).
567 List of source/ref matches from astrometry solver.
569 Metadata from astrometry matches.
571 Another reference to ``exposure`` for compatibility.
573 Another reference to ``sourceCat`` for compatibility.
576 if idGenerator
is None:
579 if background
is None:
581 table = SourceTable.make(self.
schema, idGenerator.make_table_id_factory())
584 detRes = self.detection.run(table=table, exposure=exposure,
586 sourceCat = detRes.sources
587 if detRes.background:
588 for bg
in detRes.background:
589 background.append(bg)
590 if self.config.doSkySources:
591 skySourceFootprints = self.skySources.run(mask=exposure.mask, seed=idGenerator.catalog_id)
592 if skySourceFootprints:
593 for foot
in skySourceFootprints:
594 s = sourceCat.addNew()
597 if self.config.doDeblend:
598 self.deblend.run(exposure=exposure, sources=sourceCat)
599 if not sourceCat.isContiguous():
600 sourceCat = sourceCat.copy(deep=
True)
601 self.measurement.run(
604 exposureId=idGenerator.catalog_id,
606 if self.config.doApCorr:
607 apCorrMap = exposure.getInfo().getApCorrMap()
608 if apCorrMap
is None:
609 self.log.warning(
"Image does not have valid aperture correction map for %r; "
610 "skipping aperture correction", idGenerator)
612 self.applyApCorr.run(
616 self.catalogCalculation.run(sourceCat)
618 self.setPrimaryFlags.run(sourceCat)
620 if icSourceCat
is not None and \
621 len(self.config.icSourceFieldsToCopy) > 0:
629 if not sourceCat.isContiguous():
630 sourceCat = sourceCat.copy(deep=
True)
636 if self.config.doAstrometry:
637 astromRes = self.astrometry.run(
641 astromMatches = astromRes.matches
642 matchMeta = astromRes.matchMeta
643 if exposure.getWcs()
is None:
644 if self.config.requireAstrometry:
645 raise RuntimeError(f
"WCS fit failed for {idGenerator} and requireAstrometry "
648 self.log.warning(
"Unable to perform astrometric calibration for %r but "
649 "requireAstrometry is False: attempting to proceed...",
653 if self.config.doPhotoCal:
654 if np.all(np.isnan(sourceCat[
"coord_ra"]))
or np.all(np.isnan(sourceCat[
"coord_dec"])):
655 if self.config.requirePhotoCal:
656 raise RuntimeError(f
"Astrometry failed for {idGenerator}, so cannot do "
657 "photoCal, but requirePhotoCal is True.")
658 self.log.warning(
"Astrometry failed for %r, so cannot do photoCal. requirePhotoCal "
659 "is False, so skipping photometric calibration and setting photoCalib "
660 "to None. Attempting to proceed...", idGenerator)
661 exposure.setPhotoCalib(
None)
665 photoRes = self.photoCal.run(
666 exposure, sourceCat=sourceCat, expId=idGenerator.catalog_id
668 exposure.setPhotoCalib(photoRes.photoCalib)
671 self.log.info(
"Photometric zero-point: %f",
672 photoRes.photoCalib.instFluxToMagnitude(1.0))
673 self.
setMetadata(exposure=exposure, photoRes=photoRes)
674 except Exception
as e:
675 if self.config.requirePhotoCal:
677 self.log.warning(
"Unable to perform photometric calibration "
678 "(%s): attempting to proceed", e)
681 self.postCalibrationMeasurement.run(
684 exposureId=idGenerator.catalog_id,
687 summaryMetrics =
None
688 if self.config.doComputeSummaryStats:
689 summary = self.computeSummaryStats.run(exposure=exposure,
691 background=background)
692 exposure.getInfo().setSummaryStats(summary)
693 if self.config.doCreateSummaryMetrics:
694 summaryMetrics = self.createSummaryMetrics.run(data=summary.__dict__).metrics
696 frame = getDebugFrame(self._display,
"calibrate")
701 matches=astromMatches,
706 return pipeBase.Struct(
708 astromMatches=astromMatches,
710 outputExposure=exposure,
712 outputBackground=background,
713 outputSummaryMetrics=summaryMetrics
753 """Match sources in an icSourceCat and a sourceCat and copy fields.
755 The fields copied are those specified by
756 ``config.icSourceFieldsToCopy``.
760 icSourceCat : `lsst.afw.table.SourceCatalog`
761 Catalog from which to copy fields.
762 sourceCat : `lsst.afw.table.SourceCatalog`
763 Catalog to which to copy fields.
768 Raised if any of the following occur:
769 - icSourceSchema and icSourceKeys are not specified.
770 - icSourceCat and sourceCat are not specified.
771 - icSourceFieldsToCopy is empty.
774 raise RuntimeError(
"To copy icSource fields you must specify "
775 "icSourceSchema and icSourceKeys when "
776 "constructing this task")
777 if icSourceCat
is None or sourceCat
is None:
778 raise RuntimeError(
"icSourceCat and sourceCat must both be "
780 if len(self.config.icSourceFieldsToCopy) == 0:
781 self.log.warning(
"copyIcSourceFields doing nothing because "
782 "icSourceFieldsToCopy is empty")
786 mc.findOnlyClosest =
False
788 self.config.matchRadiusPix, mc)
789 if self.config.doDeblend:
790 deblendKey = sourceCat.schema[
"deblend_nChild"].asKey()
792 matches = [m
for m
in matches
if m[1].get(deblendKey) == 0]
799 for m0, m1, d
in matches:
801 match = bestMatches.get(id0)
802 if match
is None or d <= match[2]:
803 bestMatches[id0] = (m0, m1, d)
804 matches = list(bestMatches.values())
809 numMatches = len(matches)
810 numUniqueSources = len(
set(m[1].getId()
for m
in matches))
811 if numUniqueSources != numMatches:
812 self.log.warning(
"%d icSourceCat sources matched only %d sourceCat "
813 "sources", numMatches, numUniqueSources)
815 self.log.info(
"Copying flags from icSourceCat to sourceCat for "
816 "%d sources", numMatches)
820 for icSrc, src, d
in matches:
826 icSrcFootprint = icSrc.getFootprint()
828 icSrc.setFootprint(src.getFootprint())
831 icSrc.setFootprint(icSrcFootprint)