1 from __future__
import absolute_import, division, print_function
2 from builtins
import zip
3 from builtins
import range
39 from .coaddBase
import CoaddBaseTask, SelectDataIdContainer, scaleVariance
40 from .interpImage
import InterpImageTask
41 from .scaleZeroPoint
import ScaleZeroPointTask
42 from .coaddHelpers
import groupPatchExposures, getGroupDataRef
45 __all__ = [
"AssembleCoaddTask",
"SafeClipAssembleCoaddTask",
"CompareWarpAssembleCoaddTask"]
50 \anchor AssembleCoaddConfig_ 52 \brief Configuration parameters for the \ref AssembleCoaddTask_ "AssembleCoaddTask" 54 warpType = pexConfig.Field(
55 doc=
"Warp name: one of 'direct' or 'psfMatched'",
59 subregionSize = pexConfig.ListField(
61 doc=
"Width, height of stack subregion size; " 62 "make small enough that a full stack of images will fit into memory at once.",
66 statistic = pexConfig.Field(
68 doc=
"Main stacking statistic for aggregating over the epochs.",
71 doSigmaClip = pexConfig.Field(
73 doc=
"Perform sigma clipped outlier rejection with MEANCLIP statistic? (DEPRECATED)",
76 sigmaClip = pexConfig.Field(
78 doc=
"Sigma for outlier rejection; ignored if non-clipping statistic selected.",
81 clipIter = pexConfig.Field(
83 doc=
"Number of iterations of outlier rejection; ignored if non-clipping statistic selected.",
86 calcErrorFromInputVariance = pexConfig.Field(
88 doc=
"Calculate coadd variance from input variance by stacking statistic." 89 "Passed to StatisticsControl.setCalcErrorFromInputVariance()",
92 scaleZeroPoint = pexConfig.ConfigurableField(
93 target=ScaleZeroPointTask,
94 doc=
"Task to adjust the photometric zero point of the coadd temp exposures",
96 doInterp = pexConfig.Field(
97 doc=
"Interpolate over NaN pixels? Also extrapolate, if necessary, but the results are ugly.",
101 interpImage = pexConfig.ConfigurableField(
102 target=InterpImageTask,
103 doc=
"Task to interpolate (and extrapolate) over NaN pixels",
105 doWrite = pexConfig.Field(
106 doc=
"Persist coadd?",
110 doNImage = pexConfig.Field(
111 doc=
"Create image of number of contributing exposures for each pixel",
115 maskPropagationThresholds = pexConfig.DictField(
118 doc=(
"Threshold (in fractional weight) of rejection at which we propagate a mask plane to " 119 "the coadd; that is, we set the mask bit on the coadd if the fraction the rejected frames " 120 "would have contributed exceeds this value."),
121 default={
"SAT": 0.1},
123 removeMaskPlanes = pexConfig.ListField(dtype=str, default=[
"NOT_DEBLENDED"],
124 doc=
"Mask planes to remove before coadding")
133 doMaskBrightObjects = pexConfig.Field(dtype=bool, default=
False,
134 doc=
"Set mask and flag bits for bright objects?")
135 brightObjectMaskName = pexConfig.Field(dtype=str, default=
"BRIGHT_OBJECT",
136 doc=
"Name of mask bit used for bright objects")
138 coaddPsf = pexConfig.ConfigField(
139 doc=
"Configuration for CoaddPsf",
140 dtype=measAlg.CoaddPsfConfig,
142 doAttachTransmissionCurve = pexConfig.Field(
143 dtype=bool, default=
False, optional=
False,
144 doc=(
"Attach a piecewise TransmissionCurve for the coadd? " 145 "(requires all input Exposures to have TransmissionCurves).")
149 CoaddBaseTask.ConfigClass.setDefaults(self)
153 CoaddBaseTask.ConfigClass.validate(self)
157 log.warn(
"Config doPsfMatch deprecated. Setting warpType='psfMatched'")
160 log.warn(
'doSigmaClip deprecated. To replicate behavior, setting statistic to "MEANCLIP"')
162 if self.
doInterp and self.
statistic not in [
'MEAN',
'MEDIAN',
'MEANCLIP',
'VARIANCE',
'VARIANCECLIP']:
163 raise ValueError(
"Must set doInterp=False for statistic=%s, which does not " 164 "compute and set a non-zero coadd variance estimate." % (self.
statistic))
166 unstackableStats = [
'NOTHING',
'ERROR',
'ORMASK']
167 if not hasattr(afwMath.Property, self.
statistic)
or self.
statistic in unstackableStats:
168 stackableStats = [str(k)
for k
in afwMath.Property.__members__.keys()
169 if str(k)
not in unstackableStats]
170 raise ValueError(
"statistic %s is not allowed. Please choose one of %s." 182 \anchor AssembleCoaddTask_ 184 \brief Assemble a coadded image from a set of warps (coadded temporary exposures). 186 \section pipe_tasks_assembleCoadd_Contents Contents 187 - \ref pipe_tasks_assembleCoadd_AssembleCoaddTask_Purpose 188 - \ref pipe_tasks_assembleCoadd_AssembleCoaddTask_Initialize 189 - \ref pipe_tasks_assembleCoadd_AssembleCoaddTask_Run 190 - \ref pipe_tasks_assembleCoadd_AssembleCoaddTask_Config 191 - \ref pipe_tasks_assembleCoadd_AssembleCoaddTask_Debug 192 - \ref pipe_tasks_assembleCoadd_AssembleCoaddTask_Example 194 \section pipe_tasks_assembleCoadd_AssembleCoaddTask_Purpose Description 196 \copybrief AssembleCoaddTask_ 198 We want to assemble a coadded image from a set of Warps (also called 199 coadded temporary exposures or coaddTempExps. 200 Each input Warp covers a patch on the sky and corresponds to a single run/visit/exposure of the 201 covered patch. We provide the task with a list of Warps (selectDataList) from which it selects 202 Warps that cover the specified patch (pointed at by dataRef). 203 Each Warp that goes into a coadd will typically have an independent photometric zero-point. 204 Therefore, we must scale each Warp to set it to a common photometric zeropoint. 205 WarpType may be one of 'direct' or 'psfMatched', and the boolean configs config.makeDirect and 206 config.makePsfMatched set which of the warp types will be coadded. 207 The coadd is computed as a mean with optional outlier rejection. 208 Criteria for outlier rejection are set in \ref AssembleCoaddConfig. Finally, Warps can have bad 'NaN' 209 pixels which received no input from the source calExps. We interpolate over these bad (NaN) pixels. 211 AssembleCoaddTask uses several sub-tasks. These are 213 <DT>\ref ScaleZeroPointTask_ "ScaleZeroPointTask"</DT> 214 <DD> create and use an imageScaler object to scale the photometric zeropoint for each Warp</DD> 215 <DT>\ref InterpImageTask_ "InterpImageTask"</DT> 216 <DD>interpolate across bad pixels (NaN) in the final coadd</DD> 218 You can retarget these subtasks if you wish. 220 \section pipe_tasks_assembleCoadd_AssembleCoaddTask_Initialize Task initialization 221 \copydoc \_\_init\_\_ 223 \section pipe_tasks_assembleCoadd_AssembleCoaddTask_Run Invoking the Task 226 \section pipe_tasks_assembleCoadd_AssembleCoaddTask_Config Configuration parameters 227 See \ref AssembleCoaddConfig_ 229 \section pipe_tasks_assembleCoadd_AssembleCoaddTask_Debug Debug variables 230 The \link lsst.pipe.base.cmdLineTask.CmdLineTask command line task\endlink interface supports a 231 flag \c -d to import \b debug.py from your \c PYTHONPATH; see \ref baseDebug for more about \b debug.py files. 232 AssembleCoaddTask has no debug variables of its own. Some of the subtasks may support debug variables. See 233 the documetation for the subtasks for further information. 235 \section pipe_tasks_assembleCoadd_AssembleCoaddTask_Example A complete example of using AssembleCoaddTask 237 AssembleCoaddTask assembles a set of warped images into a coadded image. The AssembleCoaddTask 238 can be invoked by running assembleCoadd.py with the flag '--legacyCoadd'. Usage of assembleCoadd.py expects 239 a data reference to the tract patch and filter to be coadded (specified using 240 '--id = [KEY=VALUE1[^VALUE2[^VALUE3...] [KEY=VALUE1[^VALUE2[^VALUE3...] ...]]') along with a list of 241 Warps to attempt to coadd (specified using 242 '--selectId [KEY=VALUE1[^VALUE2[^VALUE3...] [KEY=VALUE1[^VALUE2[^VALUE3...] ...]]'). Only the Warps 243 that cover the specified tract and patch will be coadded. A list of the available optional 244 arguments can be obtained by calling assembleCoadd.py with the --help command line argument: 246 assembleCoadd.py --help 248 To demonstrate usage of the AssembleCoaddTask in the larger context of multi-band processing, we will generate 249 the HSC-I & -R band coadds from HSC engineering test data provided in the ci_hsc package. To begin, assuming 250 that the lsst stack has been already set up, we must set up the obs_subaru and ci_hsc packages. 251 This defines the environment variable $CI_HSC_DIR and points at the location of the package. The raw HSC 252 data live in the $CI_HSC_DIR/raw directory. To begin assembling the coadds, we must first 255 <DD> process the individual ccds in $CI_HSC_RAW to produce calibrated exposures</DD> 257 <DD> create a skymap that covers the area of the sky present in the raw exposures</DD> 258 <DT>makeCoaddTempExp</DT> 259 <DD> warp the individual calibrated exposures to the tangent plane of the coadd</DD> 261 We can perform all of these steps by running 263 $CI_HSC_DIR scons warp-903986 warp-904014 warp-903990 warp-904010 warp-903988 265 This will produce warped exposures for each visit. To coadd the warped data, we call assembleCoadd.py as 268 assembleCoadd.py --legacyCoadd $CI_HSC_DIR/DATA --id patch=5,4 tract=0 filter=HSC-I \ 269 --selectId visit=903986 ccd=16 --selectId visit=903986 ccd=22 --selectId visit=903986 ccd=23 \ 270 --selectId visit=903986 ccd=100 --selectId visit=904014 ccd=1 --selectId visit=904014 ccd=6 \ 271 --selectId visit=904014 ccd=12 --selectId visit=903990 ccd=18 --selectId visit=903990 ccd=25 \ 272 --selectId visit=904010 ccd=4 --selectId visit=904010 ccd=10 --selectId visit=904010 ccd=100 \ 273 --selectId visit=903988 ccd=16 --selectId visit=903988 ccd=17 --selectId visit=903988 ccd=23 \ 274 --selectId visit=903988 ccd=24 276 that will process the HSC-I band data. The results are written in 277 `$CI_HSC_DIR/DATA/deepCoadd-results/HSC-I`. 279 You may also choose to run: 281 scons warp-903334 warp-903336 warp-903338 warp-903342 warp-903344 warp-903346 282 assembleCoadd.py --legacyCoadd $CI_HSC_DIR/DATA --id patch=5,4 tract=0 filter=HSC-R \ 283 --selectId visit=903334 ccd=16 --selectId visit=903334 ccd=22 --selectId visit=903334 ccd=23 \ 284 --selectId visit=903334 ccd=100 --selectId visit=903336 ccd=17 --selectId visit=903336 ccd=24 \ 285 --selectId visit=903338 ccd=18 --selectId visit=903338 ccd=25 --selectId visit=903342 ccd=4 \ 286 --selectId visit=903342 ccd=10 --selectId visit=903342 ccd=100 --selectId visit=903344 ccd=0 \ 287 --selectId visit=903344 ccd=5 --selectId visit=903344 ccd=11 --selectId visit=903346 ccd=1 \ 288 --selectId visit=903346 ccd=6 --selectId visit=903346 ccd=12 290 to generate the coadd for the HSC-R band if you are interested in following multiBand Coadd processing as 291 discussed in \ref pipeTasks_multiBand (but note that normally, one would use the 292 \ref SafeClipAssembleCoaddTask_ "SafeClipAssembleCoaddTask" rather than AssembleCoaddTask to make the coadd. 294 ConfigClass = AssembleCoaddConfig
295 _DefaultName =
"assembleCoadd" 299 \brief Initialize the task. Create the \ref InterpImageTask "interpImage", 300 & \ref ScaleZeroPointTask "scaleZeroPoint" subtasks. 302 CoaddBaseTask.__init__(self, *args, **kwargs)
303 self.makeSubtask(
"interpImage")
304 self.makeSubtask(
"scaleZeroPoint")
306 if self.config.doMaskBrightObjects:
307 mask = afwImage.Mask()
310 except pexExceptions.LsstCppException:
311 raise RuntimeError(
"Unable to define mask plane for bright objects; planes used are %s" %
312 mask.getMaskPlaneDict().keys())
318 def run(self, dataRef, selectDataList=[]):
320 \brief Assemble a coadd from a set of Warps 322 Coadd a set of Warps. Compute weights to be applied to each Warp and find scalings to 323 match the photometric zeropoint to a reference Warp. Assemble the Warps using 324 \ref assemble. Interpolate over NaNs and optionally write the coadd to disk. Return the coadded 328 \param[in] dataRef: Data reference defining the patch for coaddition and the reference Warp 329 (if config.autoReference=False). Used to access the following data products: 330 - [in] self.config.coaddName + "Coadd_skyMap" 331 - [in] self.config.coaddName + "Coadd_ + <warpType> + "Warp" (optionally) 332 - [out] self.config.coaddName + "Coadd" 333 \param[in] selectDataList[in]: List of data references to Warps. Data to be coadded will be 334 selected from this list based on overlap with the patch defined by dataRef. 336 \return a pipeBase.Struct with fields: 337 - coaddExposure: coadded exposure 338 - nImage: exposure count image 341 calExpRefList = self.
selectExposures(dataRef, skyInfo, selectDataList=selectDataList)
342 if len(calExpRefList) == 0:
343 self.log.warn(
"No exposures to coadd")
345 self.log.info(
"Coadding %d exposures", len(calExpRefList))
349 self.log.info(
"Found %d %s", len(inputData.tempExpRefList),
351 if len(inputData.tempExpRefList) == 0:
352 self.log.warn(
"No coadd temporary exposures found")
357 retStruct = self.
assemble(skyInfo, inputData.tempExpRefList, inputData.imageScalerList,
358 inputData.weightList, supplementaryData=supplementaryData)
360 if self.config.doInterp:
361 self.interpImage.
run(retStruct.coaddExposure.getMaskedImage(), planeName=
"NO_DATA")
363 varArray = retStruct.coaddExposure.getMaskedImage().getVariance().getArray()
364 with numpy.errstate(invalid=
"ignore"):
365 varArray[:] = numpy.where(varArray > 0, varArray, numpy.inf)
367 if self.config.doMaskBrightObjects:
371 if self.config.doWrite:
374 if self.config.doNImage
and retStruct.nImage
is not None:
381 \brief Make additional inputs to assemble() specific to subclasses. 383 Available to be implemented by subclasses only if they need the 384 coadd dataRef for performing preliminary processing before 385 assembling the coadd. 391 \brief Generate list data references corresponding to warped exposures that lie within the 394 \param[in] patchRef: Data reference for patch 395 \param[in] calExpRefList: List of data references for input calexps 396 \return List of Warp/CoaddTempExp data references 398 butler = patchRef.getButler()
399 groupData =
groupPatchExposures(patchRef, calExpRefList, self.getCoaddDatasetName(self.warpType),
400 self.getTempExpDatasetName(self.warpType))
401 tempExpRefList = [
getGroupDataRef(butler, self.getTempExpDatasetName(self.warpType),
402 g, groupData.keys)
for 403 g
in groupData.groups.keys()]
404 return tempExpRefList
408 \brief Prepare the input warps for coaddition by measuring the weight for each warp and the scaling 409 for the photometric zero point. 411 Each Warp has its own photometric zeropoint and background variance. Before coadding these 412 Warps together, compute a scale factor to normalize the photometric zeropoint and compute the 413 weight for each Warp. 415 \param[in] refList: List of data references to tempExp 417 - tempExprefList: List of data references to tempExp 418 - weightList: List of weightings 419 - imageScalerList: List of image scalers 421 statsCtrl = afwMath.StatisticsControl()
422 statsCtrl.setNumSigmaClip(self.config.sigmaClip)
423 statsCtrl.setNumIter(self.config.clipIter)
425 statsCtrl.setNanSafe(
True)
433 for tempExpRef
in refList:
434 if not tempExpRef.datasetExists(tempExpName):
435 self.log.warn(
"Could not find %s %s; skipping it", tempExpName, tempExpRef.dataId)
438 tempExp = tempExpRef.get(tempExpName, immediate=
True)
439 maskedImage = tempExp.getMaskedImage()
440 imageScaler = self.scaleZeroPoint.computeImageScaler(
445 imageScaler.scaleMaskedImage(maskedImage)
446 except Exception
as e:
447 self.log.warn(
"Scaling failed for %s (skipping it): %s", tempExpRef.dataId, e)
449 statObj = afwMath.makeStatistics(maskedImage.getVariance(), maskedImage.getMask(),
450 afwMath.MEANCLIP, statsCtrl)
451 meanVar, meanVarErr = statObj.getResult(afwMath.MEANCLIP)
452 weight = 1.0 / float(meanVar)
453 if not numpy.isfinite(weight):
454 self.log.warn(
"Non-finite weight for %s: skipping", tempExpRef.dataId)
456 self.log.info(
"Weight of %s %s = %0.3f", tempExpName, tempExpRef.dataId, weight)
461 tempExpRefList.append(tempExpRef)
462 weightList.append(weight)
463 imageScalerList.append(imageScaler)
465 return pipeBase.Struct(tempExpRefList=tempExpRefList, weightList=weightList,
466 imageScalerList=imageScalerList)
468 def assemble(self, skyInfo, tempExpRefList, imageScalerList, weightList,
469 altMaskList=None, mask=None, supplementaryData=None):
471 \anchor AssembleCoaddTask.assemble_ 473 \brief Assemble a coadd from input warps 475 Assemble the coadd using the provided list of coaddTempExps. Since the full coadd covers a patch (a 476 large area), the assembly is performed over small areas on the image at a time in order to 477 conserve memory usage. Iterate over subregions within the outer bbox of the patch using 478 \ref assembleSubregion to stack the corresponding subregions from the coaddTempExps with the 479 statistic specified. Set the edge bits the coadd mask based on the weight map. 481 \param[in] skyInfo: Patch geometry information, from getSkyInfo 482 \param[in] tempExpRefList: List of data references to Warps (previously called CoaddTempExps) 483 \param[in] imageScalerList: List of image scalers 484 \param[in] weightList: List of weights 485 \param[in] altMaskList: List of alternate masks to use rather than those stored with tempExp, or None 486 \param[in] mask: Mask to ignore when coadding 487 \param[in] supplementaryData: pipeBase.Struct with additional data products needed to assemble coadd. 488 Only used by subclasses that implement makeSupplementaryData and override assemble. 489 \return pipeBase.Struct with coaddExposure, nImage if requested 492 self.log.info(
"Assembling %s %s", len(tempExpRefList), tempExpName)
496 statsCtrl = afwMath.StatisticsControl()
497 statsCtrl.setNumSigmaClip(self.config.sigmaClip)
498 statsCtrl.setNumIter(self.config.clipIter)
499 statsCtrl.setAndMask(mask)
500 statsCtrl.setNanSafe(
True)
501 statsCtrl.setWeighted(
True)
502 statsCtrl.setCalcErrorFromInputVariance(self.config.calcErrorFromInputVariance)
503 for plane, threshold
in self.config.maskPropagationThresholds.items():
504 bit = afwImage.Mask.getMaskPlane(plane)
505 statsCtrl.setMaskPropagationThreshold(bit, threshold)
507 statsFlags = afwMath.stringToStatisticsProperty(self.config.statistic)
509 if altMaskList
is None:
510 altMaskList = [
None]*len(tempExpRefList)
512 coaddExposure = afwImage.ExposureF(skyInfo.bbox, skyInfo.wcs)
513 coaddExposure.setCalib(self.scaleZeroPoint.getCalib())
514 coaddExposure.getInfo().setCoaddInputs(self.inputRecorder.makeCoaddInputs())
516 coaddMaskedImage = coaddExposure.getMaskedImage()
517 subregionSizeArr = self.config.subregionSize
518 subregionSize = afwGeom.Extent2I(subregionSizeArr[0], subregionSizeArr[1])
520 if self.config.doNImage:
521 nImage = afwImage.ImageU(skyInfo.bbox)
524 for subBBox
in _subBBoxIter(skyInfo.bbox, subregionSize):
527 weightList, altMaskList, statsFlags, statsCtrl,
529 except Exception
as e:
530 self.log.fatal(
"Cannot compute coadd %s: %s", subBBox, e)
535 coaddUtils.setCoaddEdgeBits(coaddMaskedImage.getMask(), coaddMaskedImage.getVariance())
536 return pipeBase.Struct(coaddExposure=coaddExposure, nImage=nImage)
540 \brief Set the metadata for the coadd 542 This basic implementation simply sets the filter from the 545 \param[in] coaddExposure: The target image for the coadd 546 \param[in] tempExpRefList: List of data references to tempExp 547 \param[in] weightList: List of weights 549 assert len(tempExpRefList) == len(weightList),
"Length mismatch" 554 tempExpList = [tempExpRef.get(tempExpName +
"_sub",
555 bbox=afwGeom.Box2I(afwGeom.Point2I(0, 0), afwGeom.Extent2I(1, 1)),
556 imageOrigin=
"LOCAL", immediate=
True)
for tempExpRef
in tempExpRefList]
557 numCcds = sum(len(tempExp.getInfo().getCoaddInputs().ccds)
for tempExp
in tempExpList)
559 coaddExposure.setFilter(tempExpList[0].getFilter())
560 coaddInputs = coaddExposure.getInfo().getCoaddInputs()
561 coaddInputs.ccds.reserve(numCcds)
562 coaddInputs.visits.reserve(len(tempExpList))
564 for tempExp, weight
in zip(tempExpList, weightList):
565 self.inputRecorder.addVisitToCoadd(coaddInputs, tempExp, weight)
566 coaddInputs.visits.sort()
572 modelPsfList = [tempExp.getPsf()
for tempExp
in tempExpList]
573 modelPsfWidthList = [modelPsf.computeBBox().getWidth()
for modelPsf
in modelPsfList]
574 psf = modelPsfList[modelPsfWidthList.index(max(modelPsfWidthList))]
576 psf = measAlg.CoaddPsf(coaddInputs.ccds, coaddExposure.getWcs(),
577 self.config.coaddPsf.makeControl())
578 coaddExposure.setPsf(psf)
579 apCorrMap = measAlg.makeCoaddApCorrMap(coaddInputs.ccds, coaddExposure.getBBox(afwImage.PARENT),
580 coaddExposure.getWcs())
581 coaddExposure.getInfo().setApCorrMap(apCorrMap)
582 if self.config.doAttachTransmissionCurve:
583 transmissionCurve = measAlg.makeCoaddTransmissionCurve(coaddExposure.getWcs(), coaddInputs.ccds)
584 coaddExposure.getInfo().setTransmissionCurve(transmissionCurve)
586 def assembleSubregion(self, coaddExposure, bbox, tempExpRefList, imageScalerList, weightList,
587 altMaskList, statsFlags, statsCtrl, nImage=None):
589 \brief Assemble the coadd for a sub-region. 591 For each coaddTempExp, check for (and swap in) an alternative mask if one is passed. Remove mask 592 planes listed in config.removeMaskPlanes, Finally, stack the actual exposures using 593 \ref afwMath.statisticsStack "statisticsStack" with the statistic specified 594 by statsFlags. Typically, the statsFlag will be one of afwMath.MEAN for a mean-stack or 595 afwMath.MEANCLIP for outlier rejection using an N-sigma clipped mean where N and iterations 596 are specified by statsCtrl. Assign the stacked subregion back to the coadd. 598 \param[in] coaddExposure: The target image for the coadd 599 \param[in] bbox: Sub-region to coadd 600 \param[in] tempExpRefList: List of data reference to tempExp 601 \param[in] imageScalerList: List of image scalers 602 \param[in] weightList: List of weights 603 \param[in] altMaskList: List of alternate masks to use rather than those stored with tempExp, or None 604 Each element is dict with keys = mask plane name to which to add the spans 605 \param[in] statsFlags: afwMath.Property object for statistic for coadd 606 \param[in] statsCtrl: Statistics control object for coadd 607 \param[in] nImage: optional ImageU keeps track of exposure count for each pixel 609 self.log.debug(
"Computing coadd over %s", bbox)
611 coaddExposure.mask.addMaskPlane(
"REJECTED")
612 coaddExposure.mask.addMaskPlane(
"CLIPPED")
613 coaddExposure.mask.addMaskPlane(
"SENSOR_EDGE")
618 edge = afwImage.Mask.getPlaneBitMask(
"EDGE")
619 noData = afwImage.Mask.getPlaneBitMask(
"NO_DATA")
620 clipped = afwImage.Mask.getPlaneBitMask(
"CLIPPED")
621 toReject = statsCtrl.getAndMask() & (~noData) & (~edge) & (~clipped)
622 maskMap = [(toReject, coaddExposure.mask.getPlaneBitMask(
"REJECTED")),
623 (edge, coaddExposure.mask.getPlaneBitMask(
"SENSOR_EDGE")),
626 if nImage
is not None:
627 subNImage = afwImage.ImageU(bbox.getWidth(), bbox.getHeight())
628 for tempExpRef, imageScaler, altMask
in zip(tempExpRefList, imageScalerList, altMaskList):
629 exposure = tempExpRef.get(tempExpName +
"_sub", bbox=bbox)
630 maskedImage = exposure.getMaskedImage()
631 mask = maskedImage.getMask()
632 if altMask
is not None:
634 imageScaler.scaleMaskedImage(maskedImage)
638 if nImage
is not None:
639 subNImage.getArray()[maskedImage.getMask().getArray() & statsCtrl.getAndMask() == 0] += 1
640 if self.config.removeMaskPlanes:
641 mask = maskedImage.getMask()
642 for maskPlane
in self.config.removeMaskPlanes:
644 mask &= ~mask.getPlaneBitMask(maskPlane)
645 except Exception
as e:
646 self.log.warn(
"Unable to remove mask plane %s: %s", maskPlane, e.args[0])
648 maskedImageList.append(maskedImage)
650 with self.timer(
"stack"):
651 coaddSubregion = afwMath.statisticsStack(maskedImageList, statsFlags, statsCtrl, weightList,
654 coaddExposure.maskedImage.assign(coaddSubregion, bbox)
655 if nImage
is not None:
656 nImage.assign(subNImage, bbox)
660 \brief Apply in place alt mask formatted as SpanSets to a mask 662 @param mask: original mask 663 @param altMaskSpans: Dictionary containing spanSet lists to apply. 664 Each element contains the new mask plane name 665 (e.g. "CLIPPED and/or "NO_DATA") as the key, 666 and list of SpanSets to apply to the mask 668 for plane, spanSetList
in altMaskSpans.items():
669 maskClipValue = mask.addMaskPlane(plane)
670 for spanSet
in spanSetList:
671 spanSet.clippedTo(mask.getBBox()).setMask(mask, 2**maskClipValue)
675 """Returns None on failure""" 677 return dataRef.get(
"brightObjectMask", immediate=
True)
678 except Exception
as e:
679 self.log.warn(
"Unable to read brightObjectMask for %s: %s", dataRef.dataId, e)
683 """Set the bright object masks 685 exposure: Exposure under consideration 686 dataId: Data identifier dict for patch 687 brightObjectMasks: afwTable of bright objects to mask 692 if brightObjectMasks
is None:
693 self.log.warn(
"Unable to apply bright object mask: none supplied")
695 self.log.info(
"Applying %d bright object masks to %s", len(brightObjectMasks), dataId)
696 md = brightObjectMasks.table.getMetadata()
699 self.log.warn(
"Expected to see %s in metadata", k)
701 if md.get(k) != dataId[k]:
702 self.log.warn(
"Expected to see %s == %s in metadata, saw %s", k, md.get(k), dataId[k])
704 mask = exposure.getMaskedImage().getMask()
705 wcs = exposure.getWcs()
706 plateScale = wcs.pixelScale().asArcseconds()
708 for rec
in brightObjectMasks:
709 center = afwGeom.PointI(wcs.skyToPixel(rec.getCoord()))
710 if rec[
"type"] ==
"box":
711 assert rec[
"angle"] == 0.0, (
"Angle != 0 for mask object %s" % rec[
"id"])
712 width = rec[
"width"].asArcseconds()/plateScale
713 height = rec[
"height"].asArcseconds()/plateScale
715 halfSize = afwGeom.ExtentI(0.5*width, 0.5*height)
716 bbox = afwGeom.Box2I(center - halfSize, center + halfSize)
718 bbox = afwGeom.BoxI(afwGeom.PointI(int(center[0] - 0.5*width), int(center[1] - 0.5*height)),
719 afwGeom.PointI(int(center[0] + 0.5*width), int(center[1] + 0.5*height)))
720 spans = afwGeom.SpanSet(bbox)
721 elif rec[
"type"] ==
"circle":
722 radius = int(rec[
"radius"].asArcseconds()/plateScale)
723 spans = afwGeom.SpanSet.fromShape(radius, offset=center)
725 self.log.warn(
"Unexpected region type %s at %s" % rec[
"type"], center)
730 """Set INEXACT_PSF mask plane 732 If any of the input images isn't represented in the coadd (due to 733 clipped pixels or chip gaps), the `CoaddPsf` will be inexact. Flag 738 mask : `lsst.afw.image.Mask` 739 Coadded exposure's mask, modified in-place. 741 mask.addMaskPlane(
"INEXACT_PSF")
742 inexactPsf = mask.getPlaneBitMask(
"INEXACT_PSF")
743 sensorEdge = mask.getPlaneBitMask(
"SENSOR_EDGE")
744 clipped = mask.getPlaneBitMask(
"CLIPPED")
745 rejected = mask.getPlaneBitMask(
"REJECTED")
746 array = mask.getArray()
747 selected = array & (sensorEdge | clipped | rejected) > 0
748 array[selected] |= inexactPsf
751 def _makeArgumentParser(cls):
753 \brief Create an argument parser 756 parser.add_id_argument(
"--id", cls.
ConfigClass().coaddName +
"Coadd_" +
758 help=
"data ID, e.g. --id tract=12345 patch=1,2",
759 ContainerClass=AssembleCoaddDataIdContainer)
760 parser.add_id_argument(
"--selectId",
"calexp", help=
"data ID, e.g. --selectId visit=6789 ccd=0..9",
761 ContainerClass=SelectDataIdContainer)
765 def _subBBoxIter(bbox, subregionSize):
767 \brief Iterate over subregions of a bbox 769 \param[in] bbox: bounding box over which to iterate: afwGeom.Box2I 770 \param[in] subregionSize: size of sub-bboxes 772 \return subBBox: next sub-bounding box of size subregionSize or smaller; 773 each subBBox is contained within bbox, so it may be smaller than subregionSize at the edges of bbox, 774 but it will never be empty 777 raise RuntimeError(
"bbox %s is empty" % (bbox,))
778 if subregionSize[0] < 1
or subregionSize[1] < 1:
779 raise RuntimeError(
"subregionSize %s must be nonzero" % (subregionSize,))
781 for rowShift
in range(0, bbox.getHeight(), subregionSize[1]):
782 for colShift
in range(0, bbox.getWidth(), subregionSize[0]):
783 subBBox = afwGeom.Box2I(bbox.getMin() + afwGeom.Extent2I(colShift, rowShift), subregionSize)
785 if subBBox.isEmpty():
786 raise RuntimeError(
"Bug: empty bbox! bbox=%s, subregionSize=%s, colShift=%s, rowShift=%s" %
787 (bbox, subregionSize, colShift, rowShift))
793 \brief A version of lsst.pipe.base.DataIdContainer specialized for assembleCoadd. 798 \brief Make self.refList from self.idList. 800 datasetType = namespace.config.coaddName +
"Coadd" 801 keysCoadd = namespace.butler.getKeys(datasetType=datasetType, level=self.level)
803 for dataId
in self.idList:
805 for key
in keysCoadd:
806 if key
not in dataId:
807 raise RuntimeError(
"--id must include " + key)
809 dataRef = namespace.butler.dataRef(
810 datasetType=datasetType,
813 self.refList.append(dataRef)
818 \brief Function to count the number of pixels with a specific mask in a footprint. 820 Find the intersection of mask & footprint. Count all pixels in the mask that are in the intersection that 821 have bitmask set but do not have ignoreMask set. Return the count. 823 \param[in] mask: mask to define intersection region by. 824 \parma[in] footprint: footprint to define the intersection region by. 825 \param[in] bitmask: specific mask that we wish to count the number of occurances of. 826 \param[in] ignoreMask: pixels to not consider. 827 \return count of number of pixels in footprint with specified mask. 829 bbox = footprint.getBBox()
830 bbox.clip(mask.getBBox(afwImage.PARENT))
831 fp = afwImage.Mask(bbox)
832 subMask = mask.Factory(mask, bbox, afwImage.PARENT)
833 footprint.spans.setMask(fp, bitmask)
834 return numpy.logical_and((subMask.getArray() & fp.getArray()) > 0,
835 (subMask.getArray() & ignoreMask) == 0).sum()
840 \anchor SafeClipAssembleCoaddConfig 842 \brief Configuration parameters for the SafeClipAssembleCoaddTask 844 clipDetection = pexConfig.ConfigurableField(
845 target=SourceDetectionTask,
846 doc=
"Detect sources on difference between unclipped and clipped coadd")
847 minClipFootOverlap = pexConfig.Field(
848 doc=
"Minimum fractional overlap of clipped footprint with visit DETECTED to be clipped",
852 minClipFootOverlapSingle = pexConfig.Field(
853 doc=
"Minimum fractional overlap of clipped footprint with visit DETECTED to be " 854 "clipped when only one visit overlaps",
858 minClipFootOverlapDouble = pexConfig.Field(
859 doc=
"Minimum fractional overlap of clipped footprints with visit DETECTED to be " 860 "clipped when two visits overlap",
864 maxClipFootOverlapDouble = pexConfig.Field(
865 doc=
"Maximum fractional overlap of clipped footprints with visit DETECTED when " 866 "considering two visits",
870 minBigOverlap = pexConfig.Field(
871 doc=
"Minimum number of pixels in footprint to use DETECTED mask from the single visits " 872 "when labeling clipped footprints",
880 AssembleCoaddConfig.setDefaults(self)
896 log.warn(
"Additional Sigma-clipping not allowed in Safe-clipped Coadds. " 897 "Ignoring doSigmaClip.")
900 raise ValueError(
"Only MEAN statistic allowed for final stacking in SafeClipAssembleCoadd " 901 "(%s chosen). Please set statistic to MEAN." 903 AssembleCoaddTask.ConfigClass.validate(self)
916 \anchor SafeClipAssembleCoaddTask_ 918 \brief Assemble a coadded image from a set of coadded temporary exposures, 919 being careful to clip & flag areas with potential artifacts. 921 \section pipe_tasks_assembleCoadd_Contents Contents 922 - \ref pipe_tasks_assembleCoadd_SafeClipAssembleCoaddTask_Purpose 923 - \ref pipe_tasks_assembleCoadd_SafeClipAssembleCoaddTask_Initialize 924 - \ref pipe_tasks_assembleCoadd_SafeClipAssembleCoaddTask_Run 925 - \ref pipe_tasks_assembleCoadd_SafeClipAssembleCoaddTask_Config 926 - \ref pipe_tasks_assembleCoadd_SafeClipAssembleCoaddTask_Debug 927 - \ref pipe_tasks_assembleCoadd_SafeClipAssembleCoaddTask_Example 929 \section pipe_tasks_assembleCoadd_SafeClipAssembleCoaddTask_Purpose Description 931 \copybrief SafeClipAssembleCoaddTask 933 Read the documentation for \ref AssembleCoaddTask_ "AssembleCoaddTask" first since 934 SafeClipAssembleCoaddTask subtasks that task. 935 In \ref AssembleCoaddTask_ "AssembleCoaddTask", we compute the coadd as an clipped mean (i.e. we clip 937 The problem with doing this is that when computing the coadd PSF at a given location, individual visit 938 PSFs from visits with outlier pixels contribute to the coadd PSF and cannot be treated correctly. 939 In this task, we correct for this behavior by creating a new badMaskPlane 'CLIPPED'. 940 We populate this plane on the input coaddTempExps and the final coadd where i. difference imaging suggests 941 that there is an outlier and ii. this outlier appears on only one or two images. 942 Such regions will not contribute to the final coadd. 943 Furthermore, any routine to determine the coadd PSF can now be cognizant of clipped regions. 944 Note that the algorithm implemented by this task is preliminary and works correctly for HSC data. 945 Parameter modifications and or considerable redesigning of the algorithm is likley required for other 948 SafeClipAssembleCoaddTask uses a \ref SourceDetectionTask_ "clipDetection" subtask and also sub-classes 949 \ref AssembleCoaddTask_ "AssembleCoaddTask". You can retarget the 950 \ref SourceDetectionTask_ "clipDetection" subtask if you wish. 952 \section pipe_tasks_assembleCoadd_SafeClipAssembleCoaddTask_Initialize Task initialization 953 \copydoc \_\_init\_\_ 955 \section pipe_tasks_assembleCoadd_SafeClipAssembleCoaddTask_Run Invoking the Task 958 \section pipe_tasks_assembleCoadd_SafeClipAssembleCoaddTask_Config Configuration parameters 959 See \ref SafeClipAssembleCoaddConfig 961 \section pipe_tasks_assembleCoadd_SafeClipAssembleCoaddTask_Debug Debug variables 962 The \link lsst.pipe.base.cmdLineTask.CmdLineTask command line task\endlink interface supports a 963 flag \c -d to import \b debug.py from your \c PYTHONPATH; see \ref baseDebug for more about \b debug.py 965 SafeClipAssembleCoaddTask has no debug variables of its own. The \ref SourceDetectionTask_ "clipDetection" 966 subtasks may support debug variables. See the documetation for \ref SourceDetectionTask_ "clipDetection" 967 for further information. 969 \section pipe_tasks_assembleCoadd_SafeClipAssembleCoaddTask_Example A complete example of using 970 SafeClipAssembleCoaddTask 972 SafeClipAssembleCoaddTask assembles a set of warped coaddTempExp images into a coadded image. 973 The SafeClipAssembleCoaddTask is invoked by running assembleCoadd.py <em>without</em> the flag 975 Usage of assembleCoadd.py expects a data reference to the tract patch and filter to be coadded 976 (specified using '--id = [KEY=VALUE1[^VALUE2[^VALUE3...] [KEY=VALUE1[^VALUE2[^VALUE3...] ...]]') along 977 with a list of coaddTempExps to attempt to coadd (specified using 978 '--selectId [KEY=VALUE1[^VALUE2[^VALUE3...] [KEY=VALUE1[^VALUE2[^VALUE3...] ...]]'). 979 Only the coaddTempExps that cover the specified tract and patch will be coadded. 980 A list of the available optional arguments can be obtained by calling assembleCoadd.py with the --help 981 command line argument: 983 assembleCoadd.py --help 985 To demonstrate usage of the SafeClipAssembleCoaddTask in the larger context of multi-band processing, we 986 will generate the HSC-I & -R band coadds from HSC engineering test data provided in the ci_hsc package. To 987 begin, assuming that the lsst stack has been already set up, we must set up the obs_subaru and ci_hsc 989 This defines the environment variable $CI_HSC_DIR and points at the location of the package. The raw HSC 990 data live in the $CI_HSC_DIR/raw directory. To begin assembling the coadds, we must first 993 <DD> process the individual ccds in $CI_HSC_RAW to produce calibrated exposures</DD> 995 <DD> create a skymap that covers the area of the sky present in the raw exposures</DD> 996 <DT>makeCoaddTempExp</DT> 997 <DD> warp the individual calibrated exposures to the tangent plane of the coadd</DD> 999 We can perform all of these steps by running 1001 $CI_HSC_DIR scons warp-903986 warp-904014 warp-903990 warp-904010 warp-903988 1003 This will produce warped coaddTempExps for each visit. To coadd the warped data, we call assembleCoadd.py 1006 assembleCoadd.py $CI_HSC_DIR/DATA --id patch=5,4 tract=0 filter=HSC-I \ 1007 --selectId visit=903986 ccd=16 --selectId visit=903986 ccd=22 --selectId visit=903986 ccd=23 \ 1008 --selectId visit=903986 ccd=100--selectId visit=904014 ccd=1 --selectId visit=904014 ccd=6 \ 1009 --selectId visit=904014 ccd=12 --selectId visit=903990 ccd=18 --selectId visit=903990 ccd=25 \ 1010 --selectId visit=904010 ccd=4 --selectId visit=904010 ccd=10 --selectId visit=904010 ccd=100 \ 1011 --selectId visit=903988 ccd=16 --selectId visit=903988 ccd=17 --selectId visit=903988 ccd=23 \ 1012 --selectId visit=903988 ccd=24 1014 This will process the HSC-I band data. The results are written in 1015 `$CI_HSC_DIR/DATA/deepCoadd-results/HSC-I`. 1017 You may also choose to run: 1019 scons warp-903334 warp-903336 warp-903338 warp-903342 warp-903344 warp-903346 1020 assembleCoadd.py $CI_HSC_DIR/DATA --id patch=5,4 tract=0 filter=HSC-R --selectId visit=903334 ccd=16 \ 1021 --selectId visit=903334 ccd=22 --selectId visit=903334 ccd=23 --selectId visit=903334 ccd=100 \ 1022 --selectId visit=903336 ccd=17 --selectId visit=903336 ccd=24 --selectId visit=903338 ccd=18 \ 1023 --selectId visit=903338 ccd=25 --selectId visit=903342 ccd=4 --selectId visit=903342 ccd=10 \ 1024 --selectId visit=903342 ccd=100 --selectId visit=903344 ccd=0 --selectId visit=903344 ccd=5 \ 1025 --selectId visit=903344 ccd=11 --selectId visit=903346 ccd=1 --selectId visit=903346 ccd=6 \ 1026 --selectId visit=903346 ccd=12 1028 to generate the coadd for the HSC-R band if you are interested in following multiBand Coadd processing as 1029 discussed in \ref pipeTasks_multiBand. 1031 ConfigClass = SafeClipAssembleCoaddConfig
1032 _DefaultName =
"safeClipAssembleCoadd" 1036 \brief Initialize the task and make the \ref SourceDetectionTask_ "clipDetection" subtask. 1038 AssembleCoaddTask.__init__(self, *args, **kwargs)
1039 schema = afwTable.SourceTable.makeMinimalSchema()
1040 self.makeSubtask(
"clipDetection", schema=schema)
1042 def assemble(self, skyInfo, tempExpRefList, imageScalerList, weightList, *args, **kwargs):
1044 \brief Assemble the coadd for a region 1046 Compute the difference of coadds created with and without outlier rejection to identify coadd pixels 1047 that have outlier values in some individual visits. Detect clipped regions on the difference image and 1048 mark these regions on the one or two individual coaddTempExps where they occur if there is significant 1049 overlap between the clipped region and a source. 1050 This leaves us with a set of footprints from the difference image that have been identified as having 1051 occured on just one or two individual visits. However, these footprints were generated from a 1052 difference image. It is conceivable for a large diffuse source to have become broken up into multiple 1053 footprints acrosss the coadd difference in this process. 1054 Determine the clipped region from all overlapping footprints from the detected sources in each visit - 1055 these are big footprints. 1056 Combine the small and big clipped footprints and mark them on a new bad mask plane 1057 Generate the coadd using \ref AssembleCoaddTask.assemble_ "AssembleCoaddTask.assemble" without outlier 1058 removal. Clipped footprints will no longer make it into the coadd because they are marked in the new 1061 N.b. *args and **kwargs are passed but ignored in order to match the call signature expected by the 1064 @param skyInfo: Patch geometry information, from getSkyInfo 1065 @param tempExpRefList: List of data reference to tempExp 1066 @param imageScalerList: List of image scalers 1067 @param weightList: List of weights 1068 return pipeBase.Struct with coaddExposure, nImage 1071 mask = exp.getMaskedImage().getMask()
1072 mask.addMaskPlane(
"CLIPPED")
1074 result = self.
detectClip(exp, tempExpRefList)
1076 self.log.info(
'Found %d clipped objects', len(result.clipFootprints))
1078 maskClipValue = mask.getPlaneBitMask(
"CLIPPED")
1079 maskDetValue = mask.getPlaneBitMask(
"DETECTED") | mask.getPlaneBitMask(
"DETECTED_NEGATIVE")
1081 bigFootprints = self.
detectClipBig(result.clipSpans, result.clipFootprints, result.clipIndices,
1082 result.detectionFootprints, maskClipValue, maskDetValue,
1085 maskClip = mask.Factory(mask.getBBox(afwImage.PARENT))
1086 afwDet.setMaskFromFootprintList(maskClip, result.clipFootprints, maskClipValue)
1088 maskClipBig = maskClip.Factory(mask.getBBox(afwImage.PARENT))
1089 afwDet.setMaskFromFootprintList(maskClipBig, bigFootprints, maskClipValue)
1090 maskClip |= maskClipBig
1093 badMaskPlanes = self.config.badMaskPlanes[:]
1094 badMaskPlanes.append(
"CLIPPED")
1095 badPixelMask = afwImage.Mask.getPlaneBitMask(badMaskPlanes)
1096 return AssembleCoaddTask.assemble(self, skyInfo, tempExpRefList, imageScalerList, weightList,
1097 result.clipSpans, mask=badPixelMask)
1101 \brief Return an exposure that contains the difference between and unclipped and clipped coadds. 1103 Generate a difference image between clipped and unclipped coadds. 1104 Compute the difference image by subtracting an outlier-clipped coadd from an outlier-unclipped coadd. 1105 Return the difference image. 1107 @param skyInfo: Patch geometry information, from getSkyInfo 1108 @param tempExpRefList: List of data reference to tempExp 1109 @param imageScalerList: List of image scalers 1110 @param weightList: List of weights 1111 @return Difference image of unclipped and clipped coadd wrapped in an Exposure 1116 configIntersection = {k: getattr(self.config, k)
1117 for k, v
in self.config.toDict().items()
if (k
in config.keys())}
1118 config.update(**configIntersection)
1121 config.statistic =
'MEAN' 1123 coaddMean = task.assemble(skyInfo, tempExpRefList, imageScalerList, weightList).coaddExposure
1125 config.statistic =
'MEANCLIP' 1127 coaddClip = task.assemble(skyInfo, tempExpRefList, imageScalerList, weightList).coaddExposure
1129 coaddDiff = coaddMean.getMaskedImage().Factory(coaddMean.getMaskedImage())
1130 coaddDiff -= coaddClip.getMaskedImage()
1131 exp = afwImage.ExposureF(coaddDiff)
1132 exp.setPsf(coaddMean.getPsf())
1137 \brief Detect clipped regions on an exposure and set the mask on the individual tempExp masks 1139 Detect footprints in the difference image after smoothing the difference image with a Gaussian kernal. 1140 Identify footprints that overlap with one or two input coaddTempExps by comparing the computed overlap 1141 fraction to thresholds set in the config. 1142 A different threshold is applied depending on the number of overlapping visits (restricted to one or 1144 If the overlap exceeds the thresholds, the footprint is considered "CLIPPED" and is marked as such on 1146 Return a struct with the clipped footprints, the indices of the coaddTempExps that end up overlapping 1147 with the clipped footprints and a list of new masks for the coaddTempExps. 1149 \param[in] exp: Exposure to run detection on 1150 \param[in] tempExpRefList: List of data reference to tempExp 1151 \return struct containing: 1152 - clipFootprints: list of clipped footprints 1153 - clipIndices: indices for each clippedFootprint in tempExpRefList 1154 - clipSpans: List of dictionaries containing spanSet lists to clip. Each element contains the new 1155 maskplane name ("CLIPPED")" as the key and list of SpanSets as value 1156 - detectionFootprints: List of DETECTED/DETECTED_NEGATIVE plane compressed into footprints 1158 mask = exp.getMaskedImage().getMask()
1159 maskDetValue = mask.getPlaneBitMask(
"DETECTED") | mask.getPlaneBitMask(
"DETECTED_NEGATIVE")
1160 fpSet = self.clipDetection.detectFootprints(exp, doSmooth=
True, clearMask=
True)
1162 fpSet.positive.merge(fpSet.negative)
1163 footprints = fpSet.positive
1164 self.log.info(
'Found %d potential clipped objects', len(footprints.getFootprints()))
1169 artifactSpanSets = [{
'CLIPPED': list()}
for _
in tempExpRefList]
1172 visitDetectionFootprints = []
1174 dims = [len(tempExpRefList), len(footprints.getFootprints())]
1175 overlapDetArr = numpy.zeros(dims, dtype=numpy.uint16)
1176 ignoreArr = numpy.zeros(dims, dtype=numpy.uint16)
1179 for i, warpRef
in enumerate(tempExpRefList):
1181 immediate=
True).getMaskedImage().getMask()
1182 maskVisitDet = tmpExpMask.Factory(tmpExpMask, tmpExpMask.getBBox(afwImage.PARENT),
1183 afwImage.PARENT,
True)
1184 maskVisitDet &= maskDetValue
1185 visitFootprints = afwDet.FootprintSet(maskVisitDet, afwDet.Threshold(1))
1186 visitDetectionFootprints.append(visitFootprints)
1188 for j, footprint
in enumerate(footprints.getFootprints()):
1193 for j, footprint
in enumerate(footprints.getFootprints()):
1194 nPixel = footprint.getArea()
1197 for i
in range(len(tempExpRefList)):
1198 ignore = ignoreArr[i, j]
1199 overlapDet = overlapDetArr[i, j]
1200 totPixel = nPixel - ignore
1203 if ignore > overlapDet
or totPixel <= 0.5*nPixel
or overlapDet == 0:
1205 overlap.append(overlapDet/float(totPixel))
1208 overlap = numpy.array(overlap)
1209 if not len(overlap):
1216 if len(overlap) == 1:
1217 if overlap[0] > self.config.minClipFootOverlapSingle:
1222 clipIndex = numpy.where(overlap > self.config.minClipFootOverlap)[0]
1223 if len(clipIndex) == 1:
1225 keepIndex = [clipIndex[0]]
1228 clipIndex = numpy.where(overlap > self.config.minClipFootOverlapDouble)[0]
1229 if len(clipIndex) == 2
and len(overlap) > 3:
1230 clipIndexComp = numpy.where(overlap <= self.config.minClipFootOverlapDouble)[0]
1231 if numpy.max(overlap[clipIndexComp]) <= self.config.maxClipFootOverlapDouble:
1233 keepIndex = clipIndex
1238 for index
in keepIndex:
1239 globalIndex = indexList[index]
1240 artifactSpanSets[globalIndex][
'CLIPPED'].append(footprint.spans)
1242 clipIndices.append(numpy.array(indexList)[keepIndex])
1243 clipFootprints.append(footprint)
1245 return pipeBase.Struct(clipFootprints=clipFootprints, clipIndices=clipIndices,
1246 clipSpans=artifactSpanSets, detectionFootprints=visitDetectionFootprints)
1248 def detectClipBig(self, clipList, clipFootprints, clipIndices, detectionFootprints,
1249 maskClipValue, maskDetValue, coaddBBox):
1251 \brief Return individual warp footprints for large artifacts and append them to clipList in place 1253 Identify big footprints composed of many sources in the coadd difference that may have originated in a 1254 large diffuse source in the coadd. We do this by indentifying all clipped footprints that overlap 1255 significantly with each source in all the coaddTempExps. 1256 \param[in] clipList: List of alt mask SpanSets with clipping information. Modified. 1257 \param[in] clipFootprints: List of clipped footprints 1258 \param[in] clipIndices: List of which entries in tempExpClipList each footprint belongs to 1259 \param[in] maskClipValue: Mask value of clipped pixels 1260 \param[in] maskDetValue: Mask value of detected pixels 1261 \param[in] coaddBBox: BBox of the coadd and warps 1262 \return list of big footprints 1264 bigFootprintsCoadd = []
1266 for index, (clippedSpans, visitFootprints)
in enumerate(zip(clipList, detectionFootprints)):
1267 maskVisitDet = afwImage.MaskX(coaddBBox, 0x0)
1268 for footprint
in visitFootprints.getFootprints():
1269 footprint.spans.setMask(maskVisitDet, maskDetValue)
1272 clippedFootprintsVisit = []
1273 for foot, clipIndex
in zip(clipFootprints, clipIndices):
1274 if index
not in clipIndex:
1276 clippedFootprintsVisit.append(foot)
1277 maskVisitClip = maskVisitDet.Factory(maskVisitDet.getBBox(afwImage.PARENT))
1278 afwDet.setMaskFromFootprintList(maskVisitClip, clippedFootprintsVisit, maskClipValue)
1280 bigFootprintsVisit = []
1281 for foot
in visitFootprints.getFootprints():
1282 if foot.getArea() < self.config.minBigOverlap:
1285 if nCount > self.config.minBigOverlap:
1286 bigFootprintsVisit.append(foot)
1287 bigFootprintsCoadd.append(foot)
1289 for footprint
in bigFootprintsVisit:
1290 clippedSpans[
"CLIPPED"].append(footprint.spans)
1292 return bigFootprintsCoadd
1296 assembleStaticSkyModel = pexConfig.ConfigurableField(
1297 target=AssembleCoaddTask,
1298 doc=
"Task to assemble an artifact-free, PSF-matched Coadd to serve as a" 1299 " naive/first-iteration model of the static sky.",
1301 detect = pexConfig.ConfigurableField(
1302 target=SourceDetectionTask,
1303 doc=
"Detect outlier sources on difference between each psfMatched warp and static sky model" 1305 detectTemplate = pexConfig.ConfigurableField(
1306 target=SourceDetectionTask,
1307 doc=
"Detect sources on static sky model. Only used if doPreserveContainedBySource is True" 1309 maxNumEpochs = pexConfig.Field(
1310 doc=
"Charactistic maximum local number of epochs/visits in which an artifact candidate can appear " 1311 "and still be masked. The effective maxNumEpochs is a broken linear function of local " 1312 "number of epochs (N): min(maxFractionEpochsLow*N, maxNumEpochs + maxFractionEpochsHigh*N). " 1313 "For each footprint detected on the image difference between the psfMatched warp and static sky " 1314 "model, if a significant fraction of pixels (defined by spatialThreshold) are residuals in more " 1315 "than the computed effective maxNumEpochs, the artifact candidate is deemed persistant rather " 1316 "than transient and not masked.",
1320 maxFractionEpochsLow = pexConfig.RangeField(
1321 doc=
"Fraction of local number of epochs (N) to use as effective maxNumEpochs for low N. " 1322 "Effective maxNumEpochs = " 1323 "min(maxFractionEpochsLow * N, maxNumEpochs + maxFractionEpochsHigh * N)",
1328 maxFractionEpochsHigh = pexConfig.RangeField(
1329 doc=
"Fraction of local number of epochs (N) to use as effective maxNumEpochs for high N. " 1330 "Effective maxNumEpochs = " 1331 "min(maxFractionEpochsLow * N, maxNumEpochs + maxFractionEpochsHigh * N)",
1336 spatialThreshold = pexConfig.RangeField(
1337 doc=
"Unitless fraction of pixels defining how much of the outlier region has to meet the " 1338 "temporal criteria. If 0, clip all. If 1, clip none.",
1342 inclusiveMin=
True, inclusiveMax=
True 1344 doScaleWarpVariance = pexConfig.Field(
1345 doc=
"Rescale Warp variance plane using empirical noise?",
1349 maskScaleWarpVariance = pexConfig.ListField(
1351 default=[
"DETECTED",
"BAD",
"SAT",
"NO_DATA",
"INTRP"],
1352 doc=
"Mask planes for pixels to ignore when rescaling warp variance",
1354 doPreserveContainedBySource = pexConfig.Field(
1355 doc=
"Rescue artifacts from clipping that completely lie within a footprint detected" 1356 "on the PsfMatched Template Coadd. Replicates a behavior of SafeClip.",
1362 AssembleCoaddConfig.setDefaults(self)
1371 self.
detect.doTempLocalBackground =
False 1372 self.
detect.reEstimateBackground =
False 1373 self.
detect.returnOriginalFootprints =
False 1374 self.
detect.thresholdPolarity =
"both" 1375 self.
detect.thresholdValue = 5
1376 self.
detect.nSigmaToGrow = 2
1377 self.
detect.minPixels = 4
1378 self.
detect.isotropicGrow =
True 1379 self.
detect.thresholdType =
"pixel_stdev" 1395 \anchor CompareWarpAssembleCoaddTask_ 1397 \brief Assemble a compareWarp coadded image from a set of warps 1398 by masking artifacts detected by comparing PSF-matched warps 1400 \section pipe_tasks_assembleCoadd_Contents Contents 1401 - \ref pipe_tasks_assembleCoadd_CompareWarpAssembleCoaddTask_Purpose 1402 - \ref pipe_tasks_assembleCoadd_CompareWarpAssembleCoaddTask_Initialize 1403 - \ref pipe_tasks_assembleCoadd_CompareWarpAssembleCoaddTask_Run 1404 - \ref pipe_tasks_assembleCoadd_CompareWarpAssembleCoaddTask_Config 1405 - \ref pipe_tasks_assembleCoadd_CompareWarpAssembleCoaddTask_Debug 1406 - \ref pipe_tasks_assembleCoadd_CompareWarpAssembleCoaddTask_Example 1408 \section pipe_tasks_assembleCoadd_CompareWarpAssembleCoaddTask_Purpose Description 1410 \copybrief CompareWarpAssembleCoaddTask 1412 In \ref AssembleCoaddTask_ "AssembleCoaddTask", we compute the coadd as an clipped mean (i.e. we clip 1414 The problem with doing this is that when computing the coadd PSF at a given location, individual visit 1415 PSFs from visits with outlier pixels contribute to the coadd PSF and cannot be treated correctly. 1416 In this task, we correct for this behavior by creating a new badMaskPlane 'CLIPPED' which marks 1417 pixels in the individual warps suspected to contain an artifact. 1418 We populate this plane on the input warps by comparing PSF-matched warps with a PSF-matched median coadd 1419 which serves as a model of the static sky. Any group of pixels that deviates from the PSF-matched 1420 template coadd by more than config.detect.threshold sigma, is an artifact candidate. 1421 The candidates are then filtered to remove variable sources and sources that are difficult to subtract 1422 such as bright stars. 1423 This filter is configured using the config parameters temporalThreshold and spatialThreshold. 1424 The temporalThreshold is the maximum fraction of epochs that the deviation can 1425 appear in and still be considered an artifact. The spatialThreshold is the maximum fraction of pixels in 1426 the footprint of the deviation that appear in other epochs (where other epochs is defined by the 1427 temporalThreshold). If the deviant region meets this criteria of having a significant percentage of pixels 1428 that deviate in only a few epochs, these pixels have the 'CLIPPED' bit set in the mask. 1429 These regions will not contribute to the final coadd. 1430 Furthermore, any routine to determine the coadd PSF can now be cognizant of clipped regions. 1431 Note that the algorithm implemented by this task is preliminary and works correctly for HSC data. 1432 Parameter modifications and or considerable redesigning of the algorithm is likley required for other 1435 CompareWarpAssembleCoaddTask sub-classes 1436 \ref AssembleCoaddTask_ "AssembleCoaddTask" and instantiates \ref AssembleCoaddTask_ "AssembleCoaddTask" 1437 as a subtask to generate the TemplateCoadd (the model of the static sky) 1439 \section pipe_tasks_assembleCoadd_CompareWarpAssembleCoaddTask_Initialize Task initialization 1440 \copydoc \_\_init\_\_ 1442 \section pipe_tasks_assembleCoadd_CompareWarpAssembleCoaddTask_Run Invoking the Task 1445 \section pipe_tasks_assembleCoadd_CompareWarpAssembleCoaddTask_Config Configuration parameters 1446 See \ref CompareWarpAssembleCoaddConfig 1448 \section pipe_tasks_assembleCoadd_CompareWarpAssembleCoaddTask_Debug Debug variables 1449 The \link lsst.pipe.base.cmdLineTask.CmdLineTask command line task\endlink interface supports a 1450 flag \c -d to import \b debug.py from your \c PYTHONPATH; see \ref baseDebug for more about \b debug.py 1453 This task supports the following debug variables: 1456 <dd> If True then save the Epoch Count Image as a fits file in the `figPath` 1458 <dd> Path to save the debug fits images and figures 1461 For example, put something like: 1464 def DebugInfo(name): 1465 di = lsstDebug.getInfo(name) 1466 if name == "lsst.pipe.tasks.assembleCoadd": 1467 di.saveCountIm = True 1468 di.figPath = "/desired/path/to/debugging/output/images" 1470 lsstDebug.Info = DebugInfo 1472 into your `debug.py` file and run `assemebleCoadd.py` with the `--debug` 1474 Some subtasks may have their own debug variables; see individual Task 1477 \section pipe_tasks_assembleCoadd_CompareWarpAssembleCoaddTask_Example A complete example of using 1478 CompareWarpAssembleCoaddTask 1480 CompareWarpAssembleCoaddTask assembles a set of warped images into a coadded image. 1481 The CompareWarpAssembleCoaddTask is invoked by running assembleCoadd.py with the flag 1482 '--compareWarpCoadd'. 1483 Usage of assembleCoadd.py expects a data reference to the tract patch and filter to be coadded 1484 (specified using '--id = [KEY=VALUE1[^VALUE2[^VALUE3...] [KEY=VALUE1[^VALUE2[^VALUE3...] ...]]') along 1485 with a list of coaddTempExps to attempt to coadd (specified using 1486 '--selectId [KEY=VALUE1[^VALUE2[^VALUE3...] [KEY=VALUE1[^VALUE2[^VALUE3...] ...]]'). 1487 Only the warps that cover the specified tract and patch will be coadded. 1488 A list of the available optional arguments can be obtained by calling assembleCoadd.py with the --help 1489 command line argument: 1491 assembleCoadd.py --help 1493 To demonstrate usage of the CompareWarpAssembleCoaddTask in the larger context of multi-band processing, 1494 we will generate the HSC-I & -R band coadds from HSC engineering test data provided in the ci_hsc package. 1495 To begin, assuming that the lsst stack has been already set up, we must set up the obs_subaru and ci_hsc 1497 This defines the environment variable $CI_HSC_DIR and points at the location of the package. The raw HSC 1498 data live in the $CI_HSC_DIR/raw directory. To begin assembling the coadds, we must first 1501 <DD> process the individual ccds in $CI_HSC_RAW to produce calibrated exposures</DD> 1503 <DD> create a skymap that covers the area of the sky present in the raw exposures</DD> 1504 <DT>makeCoaddTempExp</DT> 1505 <DD> warp the individual calibrated exposures to the tangent plane of the coadd</DD> 1507 We can perform all of these steps by running 1509 $CI_HSC_DIR scons warp-903986 warp-904014 warp-903990 warp-904010 warp-903988 1511 This will produce warped coaddTempExps for each visit. To coadd the warped data, we call assembleCoadd.py 1514 assembleCoadd.py --compareWarpCoadd $CI_HSC_DIR/DATA --id patch=5,4 tract=0 filter=HSC-I \ 1515 --selectId visit=903986 ccd=16 --selectId visit=903986 ccd=22 --selectId visit=903986 ccd=23 \ 1516 --selectId visit=903986 ccd=100 --selectId visit=904014 ccd=1 --selectId visit=904014 ccd=6 \ 1517 --selectId visit=904014 ccd=12 --selectId visit=903990 ccd=18 --selectId visit=903990 ccd=25 \ 1518 --selectId visit=904010 ccd=4 --selectId visit=904010 ccd=10 --selectId visit=904010 ccd=100 \ 1519 --selectId visit=903988 ccd=16 --selectId visit=903988 ccd=17 --selectId visit=903988 ccd=23 \ 1520 --selectId visit=903988 ccd=24 1522 This will process the HSC-I band data. The results are written in 1523 `$CI_HSC_DIR/DATA/deepCoadd-results/HSC-I`. 1525 ConfigClass = CompareWarpAssembleCoaddConfig
1526 _DefaultName =
"compareWarpAssembleCoadd" 1530 \brief Initialize the task and make the \ref AssembleCoadd_ "assembleStaticSkyModel" subtask. 1532 AssembleCoaddTask.__init__(self, *args, **kwargs)
1533 self.makeSubtask(
"assembleStaticSkyModel")
1534 detectionSchema = afwTable.SourceTable.makeMinimalSchema()
1535 self.makeSubtask(
"detect", schema=detectionSchema)
1536 if self.config.doPreserveContainedBySource:
1537 self.makeSubtask(
"detectTemplate", schema=afwTable.SourceTable.makeMinimalSchema())
1541 \brief Make inputs specific to Subclass 1543 Generate a templateCoadd to use as a native model of static sky to subtract from warps. 1545 templateCoadd = self.assembleStaticSkyModel.
run(dataRef, selectDataList)
1547 if templateCoadd
is None:
1548 warpName = (self.assembleStaticSkyModel.warpType[0].upper() +
1549 self.assembleStaticSkyModel.warpType[1:])
1550 message =
"""No %(warpName)s warps were found to build the template coadd which is 1551 required to run CompareWarpAssembleCoaddTask. To continue assembling this type of coadd, 1552 first either rerun makeCoaddTempExp with config.make%(warpName)s=True or 1553 coaddDriver with config.makeCoadTempExp.make%(warpName)s=True, before assembleCoadd. 1555 Alternatively, to use another algorithm with existing warps, retarget the CoaddDriverConfig to 1556 another algorithm like: 1558 from lsst.pipe.tasks.assembleCoadd import SafeClipAssembleCoaddTask 1559 config.assemble.retarget(SafeClipAssembleCoaddTask) 1560 """ % {
"warpName": warpName}
1561 raise RuntimeError(message)
1563 return pipeBase.Struct(templateCoadd=templateCoadd.coaddExposure)
1565 def assemble(self, skyInfo, tempExpRefList, imageScalerList, weightList,
1566 supplementaryData, *args, **kwargs):
1568 \brief Assemble the coadd 1570 Requires additional inputs Struct `supplementaryData` to contain a `templateCoadd` that serves 1571 as the model of the static sky. 1573 Find artifacts and apply them to the warps' masks creating a list of alternative masks with a 1574 new "CLIPPED" plane and updated "NO_DATA" plane. 1575 Then pass these alternative masks to the base class's assemble method. 1577 @param skyInfo: Patch geometry information 1578 @param tempExpRefList: List of data references to warps 1579 @param imageScalerList: List of image scalers 1580 @param weightList: List of weights 1581 @param supplementaryData: PipeBase.Struct containing a templateCoadd 1583 return pipeBase.Struct with coaddExposure, nImage if requested 1585 templateCoadd = supplementaryData.templateCoadd
1586 spanSetMaskList = self.
findArtifacts(templateCoadd, tempExpRefList, imageScalerList)
1587 badMaskPlanes = self.config.badMaskPlanes[:]
1588 badMaskPlanes.append(
"CLIPPED")
1589 badPixelMask = afwImage.Mask.getPlaneBitMask(badMaskPlanes)
1591 return AssembleCoaddTask.assemble(self, skyInfo, tempExpRefList, imageScalerList, weightList,
1592 spanSetMaskList, mask=badPixelMask)
1596 \brief Find artifacts 1598 Loop through warps twice. The first loop builds a map with the count of how many 1599 epochs each pixel deviates from the templateCoadd by more than config.chiThreshold sigma. 1600 The second loop takes each difference image and filters the artifacts detected 1601 in each using count map to filter out variable sources and sources that are difficult to 1604 @param templateCoadd: Exposure to serve as model of static sky 1605 @param tempExpRefList: List of data references to warps 1606 @param imageScalerList: List of image scalers 1609 self.log.debug(
"Generating Count Image, and mask lists.")
1610 coaddBBox = templateCoadd.getBBox()
1611 slateIm = afwImage.ImageU(coaddBBox)
1612 epochCountImage = afwImage.ImageU(coaddBBox)
1613 nImage = afwImage.ImageU(coaddBBox)
1614 spanSetArtifactList = []
1615 spanSetNoDataMaskList = []
1617 if self.config.doPreserveContainedBySource:
1618 templateFootprints = self.detectTemplate.detectFootprints(templateCoadd)
1620 templateFootprints =
None 1622 for warpRef, imageScaler
in zip(tempExpRefList, imageScalerList):
1624 if warpDiffExp
is not None:
1625 nImage.array += numpy.where(numpy.isnan(warpDiffExp.image.array),
1626 0, 1).astype(numpy.uint16)
1627 fpSet = self.detect.detectFootprints(warpDiffExp, doSmooth=
False, clearMask=
True)
1628 fpSet.positive.merge(fpSet.negative)
1629 footprints = fpSet.positive
1631 spanSetList = [footprint.spans
for footprint
in footprints.getFootprints()]
1632 for spans
in spanSetList:
1633 spans.setImage(slateIm, 1, doClip=
True)
1634 epochCountImage += slateIm
1640 nans = numpy.where(numpy.isnan(warpDiffExp.maskedImage.image.array), 1, 0)
1641 nansMask = afwImage.makeMaskFromArray(nans.astype(afwImage.MaskPixel))
1642 nansMask.setXY0(warpDiffExp.getXY0())
1646 nansMask = afwImage.MaskX(coaddBBox, 1)
1649 spanSetNoDataMask = afwGeom.SpanSet.fromMask(nansMask).split()
1651 spanSetNoDataMaskList.append(spanSetNoDataMask)
1652 spanSetArtifactList.append(spanSetList)
1656 epochCountImage.writeFits(path)
1658 for i, spanSetList
in enumerate(spanSetArtifactList):
1660 filteredSpanSetList = self.
_filterArtifacts(spanSetList, epochCountImage, nImage,
1662 spanSetArtifactList[i] = filteredSpanSetList
1665 for artifacts, noData
in zip(spanSetArtifactList, spanSetNoDataMaskList):
1666 altMasks.append({
'CLIPPED': artifacts,
1670 def _filterArtifacts(self, spanSetList, epochCountImage, nImage, footprintsToExclude=None):
1672 \brief Filter artifact candidates 1674 @param spanSetList: List of SpanSets representing artifact candidates 1675 @param epochCountImage: Image of accumulated number of warpDiff detections 1676 @param nImage: Image of the accumulated number of total epochs contributing 1678 return List of SpanSets with artifacts 1681 maskSpanSetList = []
1682 x0, y0 = epochCountImage.getXY0()
1683 for i, span
in enumerate(spanSetList):
1684 y, x = span.indices()
1685 yIdxLocal = [y1 - y0
for y1
in y]
1686 xIdxLocal = [x1 - x0
for x1
in x]
1687 outlierN = epochCountImage.array[yIdxLocal, xIdxLocal]
1688 totalN = nImage.array[yIdxLocal, xIdxLocal]
1691 effMaxNumEpochsHighN = (self.config.maxNumEpochs +
1692 self.config.maxFractionEpochsHigh*numpy.mean(totalN))
1693 effMaxNumEpochsLowN = self.config.maxFractionEpochsLow * numpy.mean(totalN)
1694 effectiveMaxNumEpochs = int(min(effMaxNumEpochsLowN, effMaxNumEpochsHighN))
1695 nPixelsBelowThreshold = numpy.count_nonzero((outlierN > 0) &
1696 (outlierN <= effectiveMaxNumEpochs))
1697 percentBelowThreshold = nPixelsBelowThreshold / len(outlierN)
1698 if percentBelowThreshold > self.config.spatialThreshold:
1699 maskSpanSetList.append(span)
1701 if self.config.doPreserveContainedBySource
and footprintsToExclude
is not None:
1703 filteredMaskSpanSetList = []
1704 for span
in maskSpanSetList:
1706 for footprint
in footprintsToExclude.positive.getFootprints():
1707 if footprint.spans.contains(span):
1711 filteredMaskSpanSetList.append(span)
1712 maskSpanSetList = filteredMaskSpanSetList
1714 return maskSpanSetList
1716 def _readAndComputeWarpDiff(self, warpRef, imageScaler, templateCoadd):
1718 \brief Fetch a warp from the butler and return a warpDiff 1720 @param warpRef: `Butler dataRef` for the warp 1721 @param imageScaler: `scaleZeroPoint.ImageScaler` object 1722 @param templateCoadd: Exposure to be substracted from the scaled warp 1724 return Exposure of the image difference between the warp and template 1729 if not warpRef.datasetExists(warpName):
1730 self.log.warn(
"Could not find %s %s; skipping it", warpName, warpRef.dataId)
1732 warp = warpRef.get(warpName, immediate=
True)
1734 imageScaler.scaleMaskedImage(warp.getMaskedImage())
1735 mi = warp.getMaskedImage()
1736 if self.config.doScaleWarpVariance:
1739 mi -= templateCoadd.getMaskedImage()
1742 def _dataRef2DebugPath(self, prefix, warpRef, coaddLevel=False):
1744 \brief Return a path to which to write debugging output 1746 @param prefix: string, prefix for filename 1747 @param warpRef: Butler dataRef 1748 @param coaddLevel: bool, optional. If True, include only coadd-level keys 1749 (e.g. 'tract', 'patch', 'filter', but no 'visit') 1751 Creates a hyphen-delimited string of dataId values for simple filenames. 1756 keys = warpRef.dataId.keys()
1757 keyList = sorted(keys, reverse=
True)
1759 filename =
"%s-%s.fits" % (prefix,
'-'.join([str(warpRef.dataId[k])
for k
in keyList]))
1760 return os.path.join(directory, filename)
def setBrightObjectMasks(self, exposure, dataId, brightObjectMasks)
def _filterArtifacts(self, spanSetList, epochCountImage, nImage, footprintsToExclude=None)
Filter artifact candidates.
def getCoaddDatasetName(self, warpType="direct")
def _dataRef2DebugPath(self, prefix, warpRef, coaddLevel=False)
Return a path to which to write debugging output.
def getGroupDataRef(butler, datasetType, groupTuple, keys)
Base class for coaddition.
def findArtifacts(self, templateCoadd, tempExpRefList, imageScalerList)
Find artifacts.
def assembleMetadata(self, coaddExposure, tempExpRefList, weightList)
Set the metadata for the coadd.
def makeSupplementaryData(self, dataRef, selectDataList)
Make additional inputs to assemble() specific to subclasses.
def makeSupplementaryData(self, dataRef, selectDataList)
Make inputs specific to Subclass.
def getTempExpRefList(self, patchRef, calExpRefList)
Generate list data references corresponding to warped exposures that lie within the patch to be coadd...
def assemble(self, skyInfo, tempExpRefList, imageScalerList, weightList, args, kwargs)
Assemble the coadd for a region.
def run(self, dataRef, selectDataList=[])
Assemble a coadd from a set of Warps.
def _readAndComputeWarpDiff(self, warpRef, imageScaler, templateCoadd)
Fetch a warp from the butler and return a warpDiff.
def prepareInputs(self, refList)
Prepare the input warps for coaddition by measuring the weight for each warp and the scaling for the ...
def applyAltMaskPlanes(self, mask, altMaskSpans)
Apply in place alt mask formatted as SpanSets to a mask.
def getSkyInfo(self, patchRef)
Use getSkyinfo to return the skyMap, tract and patch information, wcs and the outer bbox of the patch...
def getTempExpDatasetName(self, warpType="direct")
def __init__(self, args, kwargs)
Initialize the task and make the assembleStaticSkyModel subtask.
def makeDataRefList(self, namespace)
Make self.refList from self.idList.
def getBadPixelMask(self)
Convenience method to provide the bitmask from the mask plane names.
def assembleSubregion(self, coaddExposure, bbox, tempExpRefList, imageScalerList, weightList, altMaskList, statsFlags, statsCtrl, nImage=None)
Assemble the coadd for a sub-region.
def detectClip(self, exp, tempExpRefList)
Detect clipped regions on an exposure and set the mask on the individual tempExp masks.
def setInexactPsf(self, mask)
Configuration parameters for the SafeClipAssembleCoaddTask.
def __init__(self, args, kwargs)
Initialize the task.
Assemble a coadded image from a set of warps (coadded temporary exposures).
Assemble a coadded image from a set of coadded temporary exposures, being careful to clip & flag area...
def buildDifferenceImage(self, skyInfo, tempExpRefList, imageScalerList, weightList)
Return an exposure that contains the difference between and unclipped and clipped coadds...
def selectExposures(self, patchRef, skyInfo=None, selectDataList=[])
Select exposures to coadd.
def readBrightObjectMasks(self, dataRef)
Configuration parameters for the AssembleCoaddTask.
Assemble a compareWarp coadded image from a set of warps by masking artifacts detected by comparing P...
def scaleVariance(maskedImage, maskPlanes, log=None)
Scale the variance in a maskedImage.
def __init__(self, args, kwargs)
Initialize the task and make the clipDetection subtask.
def assemble(self, skyInfo, tempExpRefList, imageScalerList, weightList, supplementaryData, args, kwargs)
Assemble the coadd.
A version of lsst.pipe.base.DataIdContainer specialized for assembleCoadd.
def countMaskFromFootprint(mask, footprint, bitmask, ignoreMask)
Function to count the number of pixels with a specific mask in a footprint.
def groupPatchExposures(patchDataRef, calexpDataRefList, coaddDatasetType="deepCoadd", tempExpDatasetType="deepCoadd_directWarp")
def assemble(self, skyInfo, tempExpRefList, imageScalerList, weightList, altMaskList=None, mask=None, supplementaryData=None)
Assemble a coadd from input warps.
def detectClipBig(self, clipList, clipFootprints, clipIndices, detectionFootprints, maskClipValue, maskDetValue, coaddBBox)
Return individual warp footprints for large artifacts and append them to clipList in place...