1 from __future__
import division, absolute_import
35 from .coaddBase
import CoaddBaseTask, SelectDataIdContainer
36 from .interpImage
import InterpImageTask
37 from .matchBackgrounds
import MatchBackgroundsTask
38 from .scaleZeroPoint
import ScaleZeroPointTask
39 from .coaddHelpers
import groupPatchExposures, getGroupDataRef
42 __all__ = [
"AssembleCoaddTask",
"SafeClipAssembleCoaddTask"]
46 \anchor AssembleCoaddConfig_
48 \brief Configuration parameters for the \ref AssembleCoaddTask_ "AssembleCoaddTask"
50 subregionSize = pexConfig.ListField(
52 doc =
"Width, height of stack subregion size; " \
53 "make small enough that a full stack of images will fit into memory at once.",
55 default = (2000, 2000),
57 doSigmaClip = pexConfig.Field(
59 doc =
"Perform sigma clipped outlier rejection? If False then compute a simple mean.",
62 sigmaClip = pexConfig.Field(
64 doc =
"Sigma for outlier rejection; ignored if doSigmaClip false.",
67 clipIter = pexConfig.Field(
69 doc =
"Number of iterations of outlier rejection; ignored if doSigmaClip false.",
72 scaleZeroPoint = pexConfig.ConfigurableField(
73 target = ScaleZeroPointTask,
74 doc =
"Task to adjust the photometric zero point of the coadd temp exposures",
76 doInterp = pexConfig.Field(
77 doc =
"Interpolate over NaN pixels? Also extrapolate, if necessary, but the results are ugly.",
81 interpImage = pexConfig.ConfigurableField(
82 target = InterpImageTask,
83 doc =
"Task to interpolate (and extrapolate) over NaN pixels",
85 matchBackgrounds = pexConfig.ConfigurableField(
86 target = MatchBackgroundsTask,
87 doc =
"Task to match backgrounds",
89 maxMatchResidualRatio = pexConfig.Field(
90 doc =
"Maximum ratio of the mean squared error of the background matching model to the variance " \
91 "of the difference in backgrounds",
95 maxMatchResidualRMS = pexConfig.Field(
96 doc =
"Maximum RMS of residuals of the background offset fit in matchBackgrounds.",
100 doWrite = pexConfig.Field(
101 doc =
"Persist coadd?",
105 doMatchBackgrounds = pexConfig.Field(
106 doc =
"Match backgrounds of coadd temp exposures before coadding them? " \
107 "If False, the coadd temp expsosures must already have been background subtracted or matched",
111 autoReference = pexConfig.Field(
112 doc =
"Automatically select the coadd temp exposure to use as a reference for background matching? " \
113 "Ignored if doMatchBackgrounds false. " \
114 "If False you must specify the reference temp exposure as the data Id",
118 maskPropagationThresholds = pexConfig.DictField(
121 doc = (
"Threshold (in fractional weight) of rejection at which we propagate a mask plane to "
122 "the coadd; that is, we set the mask bit on the coadd if the fraction the rejected frames "
123 "would have contributed exceeds this value."),
124 default = {
"SAT": 0.1},
126 removeMaskPlanes = pexConfig.ListField(dtype=str, default=[
"CROSSTALK",
"NOT_DEBLENDED"],\
127 doc=
"Mask planes to remove before coadding")
136 doMaskBrightObjects = pexConfig.Field(dtype=bool, default=
False,
137 doc=
"Set mask and flag bits for bright objects?")
138 brightObjectMaskName = pexConfig.Field(dtype=str, default=
"BRIGHT_OBJECT",
139 doc=
"Name of mask bit used for bright objects")
142 CoaddBaseTask.ConfigClass.setDefaults(self)
155 \anchor AssembleCoaddTask_
157 \brief Assemble a coadded image from a set of coadded temporary exposures.
159 \section pipe_tasks_assembleCoadd_Contents Contents
160 - \ref pipe_tasks_assembleCoadd_AssembleCoaddTask_Purpose
161 - \ref pipe_tasks_assembleCoadd_AssembleCoaddTask_Initialize
162 - \ref pipe_tasks_assembleCoadd_AssembleCoaddTask_Run
163 - \ref pipe_tasks_assembleCoadd_AssembleCoaddTask_Config
164 - \ref pipe_tasks_assembleCoadd_AssembleCoaddTask_Debug
165 - \ref pipe_tasks_assembleCoadd_AssembleCoaddTask_Example
167 \section pipe_tasks_assembleCoadd_AssembleCoaddTask_Purpose Description
169 \copybrief AssembleCoaddTask_
171 We want to assemble a coadded image from a set of coadded temporary exposures (coaddTempExps).
172 Each input coaddTempExp covers a patch on the sky and corresponds to a single run/visit/exposure of the
173 covered patch. We provide the task with a list of coaddTempExps (selectDataList) from which it selects
174 coaddTempExps that cover the specified patch (pointed at by dataRef).
175 Each coaddTempExp that goes into a coadd will typically have an independent photometric zero-point.
176 Therefore, we must scale each coaddTempExp to set it to a common photometric zeropoint. By default, each
177 coaddTempExp has backgrounds and hence will require config.doMatchBackgrounds=True.
178 When background matching is enabled, the task may be configured to automatically select a reference exposure
179 (config.autoReference=True). If this is not done, we require that the input dataRef provides access to a
180 coaddTempExp (dataset type coaddName + 'Coadd_tempExp') which is used as the reference exposure.
181 The coadd is computed as a mean with optional outlier rejection.
182 Criteria for outlier rejection are set in \ref AssembleCoaddConfig. Finally, coaddTempExps can have bad 'NaN'
183 pixels which received no input from the source calExps. We interpolate over these bad (NaN) pixels.
185 AssembleCoaddTask uses several sub-tasks. These are
187 <DT>\ref ScaleZeroPointTask_ "ScaleZeroPointTask"</DT>
188 <DD> create and use an imageScaler object to scale the photometric zeropoint for each coaddTempExp</DD>
189 <DT>\ref MatchBackgroundsTask_ "MatchBackgroundsTask"</DT>
190 <DD> match background in a coaddTempExp to a reference exposure (and select the reference exposure if one is
192 <DT>\ref InterpImageTask_ "InterpImageTask"</DT>
193 <DD>interpolate across bad pixels (NaN) in the final coadd</DD>
195 You can retarget these subtasks if you wish.
197 \section pipe_tasks_assembleCoadd_AssembleCoaddTask_Initialize Task initialization
198 \copydoc \_\_init\_\_
200 \section pipe_tasks_assembleCoadd_AssembleCoaddTask_Run Invoking the Task
203 \section pipe_tasks_assembleCoadd_AssembleCoaddTask_Config Configuration parameters
204 See \ref AssembleCoaddConfig_
206 \section pipe_tasks_assembleCoadd_AssembleCoaddTask_Debug Debug variables
207 The \link lsst.pipe.base.cmdLineTask.CmdLineTask command line task\endlink interface supports a
208 flag \c -d to import \b debug.py from your \c PYTHONPATH; see \ref baseDebug for more about \b debug.py files.
209 AssembleCoaddTask has no debug variables of its own. Some of the subtasks may support debug variables. See
210 the documetation for the subtasks for further information.
212 \section pipe_tasks_assembleCoadd_AssembleCoaddTask_Example A complete example of using AssembleCoaddTask
214 AssembleCoaddTask assembles a set of warped coaddTempExp images into a coadded image. The AssembleCoaddTask
215 can be invoked by running assembleCoadd.py with the flag '--legacyCoadd'. Usage of assembleCoadd.py expects
216 a data reference to the tract patch and filter to be coadded (specified using
217 '--id = [KEY=VALUE1[^VALUE2[^VALUE3...] [KEY=VALUE1[^VALUE2[^VALUE3...] ...]]') along with a list of
218 coaddTempExps to attempt to coadd (specified using
219 '--selectId [KEY=VALUE1[^VALUE2[^VALUE3...] [KEY=VALUE1[^VALUE2[^VALUE3...] ...]]'). Only the coaddTempExps
220 that cover the specified tract and patch will be coadded. A list of the available optional
221 arguments can be obtained by calling assembleCoadd.py with the --help command line argument:
223 assembleCoadd.py --help
225 To demonstrate usage of the AssembleCoaddTask in the larger context of multi-band processing, we will generate
226 the HSC-I & -R band coadds from HSC engineering test data provided in the ci_hsc package. To begin, assuming
227 that the lsst stack has been already set up, we must set up the obs_subaru and ci_hsc packages.
228 This defines the environment variable $CI_HSC_DIR and points at the location of the package. The raw HSC
229 data live in the $CI_HSC_DIR/raw directory. To begin assembling the coadds, we must first
232 <DD> process the individual ccds in $CI_HSC_RAW to produce calibrated exposures</DD>
234 <DD> create a skymap that covers the area of the sky present in the raw exposures</DD>
235 <DT>makeCoaddTempExp</DT>
236 <DD> warp the individual calibrated exposures to the tangent plane of the coadd</DD>
238 We can perform all of these steps by running
240 $CI_HSC_DIR scons warp-903986 warp-904014 warp-903990 warp-904010 warp-903988
242 This will produce warped coaddTempExps for each visit. To coadd the warped data, we call assembleCoadd.py as
245 assembleCoadd.py --legacyCoadd $CI_HSC_DIR/DATA --id patch=5,4 tract=0 filter=HSC-I --selectId visit=903986 ccd=16 --selectId visit=903986 ccd=22 --selectId visit=903986 ccd=23 --selectId visit=903986 ccd=100 --selectId visit=904014 ccd=1 --selectId visit=904014 ccd=6 --selectId visit=904014 ccd=12 --selectId visit=903990 ccd=18 --selectId visit=903990 ccd=25 --selectId visit=904010 ccd=4 --selectId visit=904010 ccd=10 --selectId visit=904010 ccd=100 --selectId visit=903988 ccd=16 --selectId visit=903988 ccd=17 --selectId visit=903988 ccd=23 --selectId visit=903988 ccd=24\endcode
246 that will process the HSC-I band data. The results are written in $CI_HSC_DIR/DATA/deepCoadd-results/HSC-I
247 You may also choose to run:
249 scons warp-903334 warp-903336 warp-903338 warp-903342 warp-903344 warp-903346
250 assembleCoadd.py --legacyCoadd $CI_HSC_DIR/DATA --id patch=5,4 tract=0 filter=HSC-R --selectId visit=903334 ccd=16 --selectId visit=903334 ccd=22 --selectId visit=903334 ccd=23 --selectId visit=903334 ccd=100 --selectId visit=903336 ccd=17 --selectId visit=903336 ccd=24 --selectId visit=903338 ccd=18 --selectId visit=903338 ccd=25 --selectId visit=903342 ccd=4 --selectId visit=903342 ccd=10 --selectId visit=903342 ccd=100 --selectId visit=903344 ccd=0 --selectId visit=903344 ccd=5 --selectId visit=903344 ccd=11 --selectId visit=903346 ccd=1 --selectId visit=903346 ccd=6 --selectId visit=903346 ccd=12
252 to generate the coadd for the HSC-R band if you are interested in following multiBand Coadd processing as
253 discussed in \ref pipeTasks_multiBand (but note that normally, one would use the
254 \ref SafeClipAssembleCoaddTask_ "SafeClipAssembleCoaddTask" rather than AssembleCoaddTask to make the coadd.
256 ConfigClass = AssembleCoaddConfig
257 _DefaultName =
"assembleCoadd"
261 \brief Initialize the task. Create the \ref InterpImageTask "interpImage",
262 \ref MatchBackgroundsTask "matchBackgrounds", & \ref ScaleZeroPointTask "scaleZeroPoint" subtasks.
264 CoaddBaseTask.__init__(self, *args, **kwargs)
265 self.makeSubtask(
"interpImage")
266 self.makeSubtask(
"matchBackgrounds")
267 self.makeSubtask(
"scaleZeroPoint")
269 if self.config.doMaskBrightObjects:
270 mask = afwImage.MaskU()
273 except pexExceptions.LsstCppException:
274 raise RuntimeError(
"Unable to define mask plane for bright objects; planes used are %s" %
275 mask.getMaskPlaneDict().keys())
279 def run(self, dataRef, selectDataList=[]):
281 \brief Assemble a coadd from a set of coaddTempExp
283 Coadd a set of coaddTempExps. Compute weights to be applied to each coaddTempExp and find scalings to
284 match the photometric zeropoint to a reference coaddTempExp. Optionally, match backgrounds across
285 coaddTempExps if the background has not already been removed. Assemble the coaddTempExps using
286 \ref assemble. Interpolate over NaNs and optionally write the coadd to disk. Return the coadded
290 \param[in] dataRef: Data reference defining the patch for coaddition and the reference coaddTempExp
291 (if config.autoReference=False). Used to access the following data products:
292 - [in] self.config.coaddName + "Coadd_skyMap"
293 - [in] self.config.coaddName + "Coadd_tempExp" (optionally)
294 - [out] self.config.coaddName + "Coadd"
295 \param[in] selectDataList[in]: List of data references to coaddTempExps. Data to be coadded will be
296 selected from this list based on overlap with the patch defined by dataRef.
298 \return a pipeBase.Struct with fields:
299 - coaddExposure: coadded exposure
301 skyInfo = self.getSkyInfo(dataRef)
302 calExpRefList = self.selectExposures(dataRef, skyInfo, selectDataList=selectDataList)
303 if len(calExpRefList) == 0:
304 self.log.warn(
"No exposures to coadd")
306 self.log.info(
"Coadding %d exposures", len(calExpRefList))
310 self.log.info(
"Found %d %s", len(inputData.tempExpRefList), self.getTempExpDatasetName())
311 if len(inputData.tempExpRefList) == 0:
312 self.log.warn(
"No coadd temporary exposures found")
314 if self.config.doMatchBackgrounds:
317 if len(inputData.tempExpRefList) == 0:
318 self.log.warn(
"No valid background models")
321 coaddExp = self.
assemble(skyInfo, inputData.tempExpRefList, inputData.imageScalerList,
322 inputData.weightList,
323 inputData.backgroundInfoList
if self.config.doMatchBackgrounds
else None,
324 doClip=self.config.doSigmaClip)
325 if self.config.doMatchBackgrounds:
327 inputData.backgroundInfoList)
329 if self.config.doInterp:
330 self.interpImage.run(coaddExp.getMaskedImage(), planeName=
"NO_DATA")
332 varArray = coaddExp.getMaskedImage().getVariance().getArray()
333 varArray[:] = numpy.where(varArray > 0, varArray, numpy.inf)
335 if self.config.doMaskBrightObjects:
339 if self.config.doWrite:
340 self.writeCoaddOutput(dataRef, coaddExp)
342 return pipeBase.Struct(coaddExposure=coaddExp)
346 \brief Generate list of coaddTempExp data references corresponding to exposures that lie within the
349 \param[in] patchRef: Data reference for patch
350 \param[in] calExpRefList: List of data references for input calexps
351 \return List of coaddTempExp data references
353 butler = patchRef.getButler()
355 self.getTempExpDatasetName())
356 tempExpRefList = [
getGroupDataRef(butler, self.getTempExpDatasetName(), g, groupData.keys)
for
357 g
in groupData.groups.keys()]
358 return tempExpRefList
362 \brief Construct an image scaler for the background reference frame
364 Each coaddTempExp has a different background level. A reference background level must be chosen before
365 coaddition. If config.autoReference=True, \ref backgroundMatching will pick the reference level and
366 this routine is a no-op and None is returned. Otherwise, use the
367 \ref ScaleZeroPointTask_ "scaleZeroPoint" subtask to compute an imageScaler object for the provided
368 reference image and return it.
370 \param[in] dataRef: Data reference for the background reference frame, or None
371 \return image scaler, or None
373 if self.config.autoReference:
377 dataset = self.getTempExpDatasetName()
378 if not dataRef.datasetExists(dataset):
379 raise RuntimeError(
"Could not find reference exposure %s %s." % (dataset, dataRef.dataId))
381 refExposure = dataRef.get(self.getTempExpDatasetName(), immediate=
True)
382 refImageScaler = self.scaleZeroPoint.computeImageScaler(
383 exposure = refExposure,
386 return refImageScaler
390 \brief Prepare the input warps for coaddition by measuring the weight for each warp and the scaling
391 for the photometric zero point.
393 Each coaddTempExp has its own photometric zeropoint and background variance. Before coadding these
394 coaddTempExps together, compute a scale factor to normalize the photometric zeropoint and compute the
395 weight for each coaddTempExp.
397 \param[in] refList: List of data references to tempExp
399 - tempExprefList: List of data references to tempExp
400 - weightList: List of weightings
401 - imageScalerList: List of image scalers
404 statsCtrl.setNumSigmaClip(self.config.sigmaClip)
405 statsCtrl.setNumIter(self.config.clipIter)
406 statsCtrl.setAndMask(self.getBadPixelMask())
407 statsCtrl.setNanSafe(
True)
415 tempExpName = self.getTempExpDatasetName()
416 for tempExpRef
in refList:
417 if not tempExpRef.datasetExists(tempExpName):
418 self.log.warn(
"Could not find %s %s; skipping it", tempExpName, tempExpRef.dataId)
421 tempExp = tempExpRef.get(tempExpName, immediate=
True)
422 maskedImage = tempExp.getMaskedImage()
423 imageScaler = self.scaleZeroPoint.computeImageScaler(
425 dataRef = tempExpRef,
428 imageScaler.scaleMaskedImage(maskedImage)
429 except Exception
as e:
430 self.log.warn(
"Scaling failed for %s (skipping it): %s", tempExpRef.dataId, e)
433 afwMath.MEANCLIP, statsCtrl)
434 meanVar, meanVarErr = statObj.getResult(afwMath.MEANCLIP);
435 weight = 1.0 / float(meanVar)
436 if not numpy.isfinite(weight):
437 self.log.warn(
"Non-finite weight for %s: skipping", tempExpRef.dataId)
439 self.log.info(
"Weight of %s %s = %0.3f", tempExpName, tempExpRef.dataId, weight)
444 tempExpRefList.append(tempExpRef)
445 weightList.append(weight)
446 imageScalerList.append(imageScaler)
448 return pipeBase.Struct(tempExpRefList=tempExpRefList, weightList=weightList,
449 imageScalerList=imageScalerList)
454 \brief Perform background matching on the prepared inputs
456 Each coaddTempExp has a different background level that must be normalized to a reference level
457 before coaddition. If no reference is provided, the background matcher selects one. If the background
458 matching is performed sucessfully, recompute the weight to be applied to the coaddTempExp to be
459 consistent with the scaled background.
461 \param[in] inputData: Struct from prepareInputs() with tempExpRefList, weightList, imageScalerList
462 \param[in] refExpDataRef: Data reference for background reference tempExp, or None
463 \param[in] refImageScaler: Image scaler for background reference tempExp, or None
465 - tempExprefList: List of data references to tempExp
466 - weightList: List of weightings
467 - imageScalerList: List of image scalers
468 - backgroundInfoList: result from background matching
471 backgroundInfoList = self.matchBackgrounds.run(
472 expRefList = inputData.tempExpRefList,
473 imageScalerList = inputData.imageScalerList,
474 refExpDataRef = refExpDataRef
if not self.config.autoReference
else None,
475 refImageScaler = refImageScaler,
476 expDatasetType = self.getTempExpDatasetName(),
478 except Exception
as e:
479 self.log.fatal(
"Cannot match backgrounds: %s", e)
480 raise pipeBase.TaskError(
"Background matching failed.")
483 newTempExpRefList = []
484 newBackgroundStructList = []
488 for tempExpRef, bgInfo, scaler, weight
in zip(inputData.tempExpRefList, backgroundInfoList,
489 inputData.imageScalerList, inputData.weightList):
490 if not bgInfo.isReference:
493 if (bgInfo.backgroundModel
is None):
494 self.log.info(
"No background offset model available for %s: skipping", tempExpRef.dataId)
497 varianceRatio = bgInfo.matchedMSE / bgInfo.diffImVar
498 except Exception
as e:
499 self.log.info(
"MSE/Var ratio not calculable (%s) for %s: skipping",
500 e, tempExpRef.dataId)
502 if not numpy.isfinite(varianceRatio):
503 self.log.info(
"MSE/Var ratio not finite (%.2f / %.2f) for %s: skipping",
504 bgInfo.matchedMSE, bgInfo.diffImVar, tempExpRef.dataId)
506 elif (varianceRatio > self.config.maxMatchResidualRatio):
507 self.log.info(
"Bad fit. MSE/Var ratio %.2f > %.2f for %s: skipping",
508 varianceRatio, self.config.maxMatchResidualRatio, tempExpRef.dataId)
510 elif ( bgInfo.fitRMS > self.config.maxMatchResidualRMS):
511 self.log.info(
"Bad fit. RMS %.2f > %.2f for %s: skipping",
512 bgInfo.fitRMS, self.config.maxMatchResidualRMS, tempExpRef.dataId)
514 newWeightList.append(1 / (1 / weight + bgInfo.fitRMS**2))
515 newTempExpRefList.append(tempExpRef)
516 newBackgroundStructList.append(bgInfo)
517 newScaleList.append(scaler)
519 return pipeBase.Struct(tempExpRefList=newTempExpRefList, weightList=newWeightList,
520 imageScalerList=newScaleList, backgroundInfoList=newBackgroundStructList)
522 def assemble(self, skyInfo, tempExpRefList, imageScalerList, weightList, bgInfoList=None,
523 altMaskList=
None, doClip=
False, mask=
None):
525 \anchor AssembleCoaddTask.assemble_
527 \brief Assemble a coadd from input warps
529 Assemble the coadd using the provided list of coaddTempExps. Since the full coadd covers a patch (a
530 large area), the assembly is performed over small areas on the image at a time in order to
531 conserve memory usage. Iterate over subregions within the outer bbox of the patch using
532 \ref assembleSubregion to mean-stack the corresponding subregions from the coaddTempExps (with outlier
533 rejection if config.doSigmaClip=True). Set the edge bits the the coadd mask based on the weight map.
535 \param[in] skyInfo: Patch geometry information, from getSkyInfo
536 \param[in] tempExpRefList: List of data references to tempExp
537 \param[in] imageScalerList: List of image scalers
538 \param[in] weightList: List of weights
539 \param[in] bgInfoList: List of background data from background matching, or None
540 \param[in] altMaskList: List of alternate masks to use rather than those stored with tempExp, or None
541 \param[in] doClip: Use clipping when codding?
542 \param[in] mask: Mask to ignore when coadding
543 \return coadded exposure
545 tempExpName = self.getTempExpDatasetName()
546 self.log.info(
"Assembling %s %s", len(tempExpRefList), tempExpName)
548 mask = self.getBadPixelMask()
551 statsCtrl.setNumSigmaClip(self.config.sigmaClip)
552 statsCtrl.setNumIter(self.config.clipIter)
553 statsCtrl.setAndMask(mask)
554 statsCtrl.setNanSafe(
True)
555 statsCtrl.setWeighted(
True)
556 statsCtrl.setCalcErrorFromInputVariance(
True)
557 for plane, threshold
in self.config.maskPropagationThresholds.items():
558 bit = afwImage.MaskU.getMaskPlane(plane)
559 statsCtrl.setMaskPropagationThreshold(bit, threshold)
562 statsFlags = afwMath.MEANCLIP
564 statsFlags = afwMath.MEAN
566 if bgInfoList
is None:
567 bgInfoList = [
None]*len(tempExpRefList)
569 if altMaskList
is None:
570 altMaskList = [
None]*len(tempExpRefList)
572 coaddExposure = afwImage.ExposureF(skyInfo.bbox, skyInfo.wcs)
573 coaddExposure.setCalib(self.scaleZeroPoint.getCalib())
574 coaddExposure.getInfo().setCoaddInputs(self.inputRecorder.makeCoaddInputs())
576 coaddMaskedImage = coaddExposure.getMaskedImage()
577 subregionSizeArr = self.config.subregionSize
579 for subBBox
in _subBBoxIter(skyInfo.bbox, subregionSize):
582 weightList, bgInfoList, altMaskList, statsFlags, statsCtrl)
583 except Exception
as e:
584 self.log.fatal(
"Cannot compute coadd %s: %s", subBBox, e)
592 \brief Set the metadata for the coadd
594 This basic implementation simply sets the filter from the
597 \param[in] coaddExposure: The target image for the coadd
598 \param[in] tempExpRefList: List of data references to tempExp
599 \param[in] weightList: List of weights
601 assert len(tempExpRefList) == len(weightList),
"Length mismatch"
602 tempExpName = self.getTempExpDatasetName()
606 tempExpList = [tempExpRef.get(tempExpName +
"_sub",
608 imageOrigin=
"LOCAL", immediate=
True)
for tempExpRef
in tempExpRefList]
609 numCcds = sum(len(tempExp.getInfo().getCoaddInputs().ccds)
for tempExp
in tempExpList)
611 coaddExposure.setFilter(tempExpList[0].getFilter())
612 coaddInputs = coaddExposure.getInfo().getCoaddInputs()
613 coaddInputs.ccds.reserve(numCcds)
614 coaddInputs.visits.reserve(len(tempExpList))
616 for tempExp, weight
in zip(tempExpList, weightList):
617 self.inputRecorder.addVisitToCoadd(coaddInputs, tempExp, weight)
618 coaddInputs.visits.sort()
619 if self.config.doPsfMatch:
620 psf = self.config.modelPsf.apply()
623 coaddExposure.setPsf(psf)
624 apCorrMap = measAlg.makeCoaddApCorrMap(coaddInputs.ccds, coaddExposure.getBBox(afwImage.PARENT),
625 coaddExposure.getWcs())
626 coaddExposure.getInfo().setApCorrMap(apCorrMap)
628 def assembleSubregion(self, coaddExposure, bbox, tempExpRefList, imageScalerList, weightList,
629 bgInfoList, altMaskList, statsFlags, statsCtrl):
631 \brief Assemble the coadd for a sub-region.
633 For each coaddTempExp, check for (and swap in) an alternative mask if one is passed. If background
634 matching is enabled, add the background and background variance from each coaddTempExp. Remove mask
635 planes listed in config.removeMaskPlanes, Finally, mean-stack
636 the actual exposures using \ref afwMath.statisticsStack "statisticsStack" with outlier rejection if
637 config.doSigmaClip=True. Assign the stacked subregion back to the coadd.
639 \param[in] coaddExposure: The target image for the coadd
640 \param[in] bbox: Sub-region to coadd
641 \param[in] tempExpRefList: List of data reference to tempExp
642 \param[in] imageScalerList: List of image scalers
643 \param[in] weightList: List of weights
644 \param[in] bgInfoList: List of background data from background matching
645 \param[in] altMaskList: List of alternate masks to use rather than those stored with tempExp, or None
646 \param[in] statsFlags: Statistic for coadd
647 \param[in] statsCtrl: Statistics control object for coadd
649 self.log.debug(
"Computing coadd over %s", bbox)
650 tempExpName = self.getTempExpDatasetName()
651 coaddMaskedImage = coaddExposure.getMaskedImage()
652 maskedImageList = afwImage.vectorMaskedImageF()
653 for tempExpRef, imageScaler, bgInfo, altMask
in zip(tempExpRefList, imageScalerList, bgInfoList,
655 exposure = tempExpRef.get(tempExpName +
"_sub", bbox=bbox)
656 maskedImage = exposure.getMaskedImage()
659 altMaskSub = altMask.Factory(altMask, bbox, afwImage.PARENT)
660 maskedImage.getMask().
swap(altMaskSub)
661 imageScaler.scaleMaskedImage(maskedImage)
663 if self.config.doMatchBackgrounds
and not bgInfo.isReference:
664 backgroundModel = bgInfo.backgroundModel
665 backgroundImage = backgroundModel.getImage()
if \
666 self.matchBackgrounds.config.usePolynomial
else \
667 backgroundModel.getImageF()
668 backgroundImage.setXY0(coaddMaskedImage.getXY0())
669 maskedImage += backgroundImage.Factory(backgroundImage, bbox, afwImage.PARENT,
False)
670 var = maskedImage.getVariance()
671 var += (bgInfo.fitRMS)**2
673 if self.config.removeMaskPlanes:
674 mask = maskedImage.getMask()
675 for maskPlane
in self.config.removeMaskPlanes:
677 mask &= ~mask.getPlaneBitMask(maskPlane)
678 except Exception
as e:
679 self.log.warn(
"Unable to remove mask plane %s: %s", maskPlane, e)
681 maskedImageList.append(maskedImage)
683 with self.timer(
"stack"):
685 maskedImageList, statsFlags, statsCtrl, weightList)
687 coaddMaskedImage.assign(coaddSubregion, bbox)
692 \brief Add metadata from the background matching to the coadd
694 \param[in] coaddExposure: Coadd
695 \param[in] tempExpRefList: List of data references for temp exps to go into coadd
696 \param[in] backgroundInfoList: List of background info, results from background matching
698 self.log.info(
"Adding exposure information to metadata")
699 metadata = coaddExposure.getMetadata()
700 metadata.addString(
"CTExp_SDQA1_DESCRIPTION",
701 "Background matching: Ratio of matchedMSE / diffImVar")
702 for ind, (tempExpRef, backgroundInfo)
in enumerate(zip(tempExpRefList, backgroundInfoList)):
703 tempExpStr =
'&'.join(
'%s=%s' % (k,v)
for k,v
in tempExpRef.dataId.items())
704 if backgroundInfo.isReference:
705 metadata.addString(
"ReferenceExp_ID", tempExpStr)
707 metadata.addString(
"CTExp_ID_%d" % (ind), tempExpStr)
708 metadata.addDouble(
"CTExp_SDQA1_%d" % (ind),
709 backgroundInfo.matchedMSE/backgroundInfo.diffImVar)
710 metadata.addDouble(
"CTExp_SDQA2_%d" % (ind),
711 backgroundInfo.fitRMS)
714 """Returns None on failure"""
716 return dataRef.get(
"brightObjectMask", immediate=
True)
717 except Exception
as e:
718 self.log.warn(
"Unable to read brightObjectMask for %s: %s", dataRef.dataId, e)
722 """Set the bright object masks
724 exposure: Exposure under consideration
725 dataId: Data identifier dict for patch
726 brightObjectMasks: afwTable of bright objects to mask
731 if brightObjectMasks
is None:
732 self.log.warn(
"Unable to apply bright object mask: none supplied")
734 self.log.info(
"Applying %d bright object masks to %s", len(brightObjectMasks), dataId)
735 md = brightObjectMasks.table.getMetadata()
738 self.log.warn(
"Expected to see %s in metadata", k)
740 if md.get(k) != dataId[k]:
741 self.log.warn(
"Expected to see %s == %s in metadata, saw %s", k, md.get(k), dataId[k])
743 mask = exposure.getMaskedImage().getMask()
744 wcs = exposure.getWcs()
745 plateScale = wcs.pixelScale().asArcseconds()
747 for rec
in brightObjectMasks:
749 radius = rec[
"radius"].asArcseconds()/plateScale
757 \brief Create an argument parser
759 parser = pipeBase.ArgumentParser(name=cls._DefaultName)
760 parser.add_id_argument(
"--id", cls.ConfigClass().coaddName +
"Coadd_tempExp",
761 help=
"data ID, e.g. --id tract=12345 patch=1,2",
762 ContainerClass=AssembleCoaddDataIdContainer)
763 parser.add_id_argument(
"--selectId",
"calexp", help=
"data ID, e.g. --selectId visit=6789 ccd=0..9",
764 ContainerClass=SelectDataIdContainer)
771 \brief Iterate over subregions of a bbox
773 \param[in] bbox: bounding box over which to iterate: afwGeom.Box2I
774 \param[in] subregionSize: size of sub-bboxes
776 \return subBBox: next sub-bounding box of size subregionSize or smaller;
777 each subBBox is contained within bbox, so it may be smaller than subregionSize at the edges of bbox,
778 but it will never be empty
781 raise RuntimeError(
"bbox %s is empty" % (bbox,))
782 if subregionSize[0] < 1
or subregionSize[1] < 1:
783 raise RuntimeError(
"subregionSize %s must be nonzero" % (subregionSize,))
785 for rowShift
in range(0, bbox.getHeight(), subregionSize[1]):
786 for colShift
in range(0, bbox.getWidth(), subregionSize[0]):
789 if subBBox.isEmpty():
790 raise RuntimeError(
"Bug: empty bbox! bbox=%s, subregionSize=%s, colShift=%s, rowShift=%s" % \
791 (bbox, subregionSize, colShift, rowShift))
798 \brief A version of lsst.pipe.base.DataIdContainer specialized for assembleCoadd.
802 \brief Make self.refList from self.idList.
804 Interpret the config.doMatchBackgrounds, config.autoReference,
805 and whether a visit/run supplied.
806 If a visit/run is supplied, config.autoReference is automatically set to False.
807 if config.doMatchBackgrounds == false, then a visit/run will be ignored if accidentally supplied.
810 keysCoadd = namespace.butler.getKeys(datasetType=namespace.config.coaddName +
"Coadd",
812 keysCoaddTempExp = namespace.butler.getKeys(datasetType=namespace.config.coaddName +
"Coadd_tempExp",
815 if namespace.config.doMatchBackgrounds:
816 if namespace.config.autoReference:
817 datasetType = namespace.config.coaddName +
"Coadd"
818 validKeys = keysCoadd
820 datasetType = namespace.config.coaddName +
"Coadd_tempExp"
821 validKeys = keysCoaddTempExp
823 datasetType = namespace.config.coaddName +
"Coadd"
824 validKeys = keysCoadd
826 for dataId
in self.idList:
828 for key
in validKeys:
829 if key
not in dataId:
830 raise RuntimeError(
"--id must include " + key)
833 if (key
not in keysCoadd)
and (key
in keysCoaddTempExp):
834 if namespace.config.autoReference:
836 namespace.config.autoReference =
False
837 datasetType = namespace.config.coaddName +
"Coadd_tempExp"
838 print "Switching config.autoReference to False; applies only to background Matching."
841 dataRef = namespace.butler.dataRef(
842 datasetType = datasetType,
845 self.refList.append(dataRef)
849 \brief Function to count the number of pixels with a specific mask in a footprint.
851 Find the intersection of mask & footprint. Count all pixels in the mask that are in the intersection that
852 have bitmask set but do not have ignoreMask set. Return the count.
854 \param[in] mask: mask to define intersection region by.
855 \parma[in] footprint: footprint to define the intersection region by.
856 \param[in] bitmask: specific mask that we wish to count the number of occurances of.
857 \param[in] ignoreMask: pixels to not consider.
858 \return count of number of pixels in footprint with specified mask.
860 bbox = footprint.getBBox()
861 bbox.clip(mask.getBBox(afwImage.PARENT))
862 fp = afwImage.MaskU(bbox)
863 subMask = mask.Factory(mask, bbox, afwImage.PARENT)
865 return numpy.logical_and((subMask.getArray() & fp.getArray()) > 0,
866 (subMask.getArray() & ignoreMask) == 0).sum()
871 \anchor SafeClipAssembleCoaddConfig
873 \brief Configuration parameters for the SafeClipAssembleCoaddTask
875 clipDetection = pexConfig.ConfigurableField(target=SourceDetectionTask,
876 doc=
"Detect sources on difference between unclipped and clipped coadd")
877 minClipFootOverlap = pexConfig.Field(
878 doc =
"Minimum fractional overlap of clipped footprint with visit DETECTED to be clipped",
882 minClipFootOverlapSingle = pexConfig.Field(
883 doc =
"Minimum fractional overlap of clipped footprint with visit DETECTED to be " \
884 "clipped when only one visit overlaps",
888 minClipFootOverlapDouble = pexConfig.Field(
889 doc =
"Minimum fractional overlap of clipped footprints with visit DETECTED to be " \
890 "clipped when two visits overlap",
894 maxClipFootOverlapDouble = pexConfig.Field(
895 doc =
"Maximum fractional overlap of clipped footprints with visit DETECTED when " \
896 "considering two visits",
900 minBigOverlap = pexConfig.Field(
901 doc =
"Minimum number of pixels in footprint to use DETECTED mask from the single visits " \
902 "when labeling clipped footprints",
910 AssembleCoaddConfig.setDefaults(self)
911 self.clipDetection.reEstimateBackground =
False
912 self.clipDetection.returnOriginalFootprints =
False
913 self.clipDetection.thresholdPolarity =
"both"
914 self.clipDetection.thresholdValue = 2
915 self.clipDetection.nSigmaToGrow = 2
916 self.clipDetection.minPixels = 4
917 self.clipDetection.isotropicGrow =
True
918 self.clipDetection.thresholdType =
"pixel_stdev"
931 \anchor SafeClipAssembleCoaddTask_
933 \brief Assemble a coadded image from a set of coadded temporary exposures, being careful to clip & flag areas
934 with potential artifacts.
936 \section pipe_tasks_assembleCoadd_Contents Contents
937 - \ref pipe_tasks_assembleCoadd_SafeClipAssembleCoaddTask_Purpose
938 - \ref pipe_tasks_assembleCoadd_SafeClipAssembleCoaddTask_Initialize
939 - \ref pipe_tasks_assembleCoadd_SafeClipAssembleCoaddTask_Run
940 - \ref pipe_tasks_assembleCoadd_SafeClipAssembleCoaddTask_Config
941 - \ref pipe_tasks_assembleCoadd_SafeClipAssembleCoaddTask_Debug
942 - \ref pipe_tasks_assembleCoadd_SafeClipAssembleCoaddTask_Example
944 \section pipe_tasks_assembleCoadd_SafeClipAssembleCoaddTask_Purpose Description
946 \copybrief SafeClipAssembleCoaddTask
948 Read the documentation for \ref AssembleCoaddTask_ "AssembleCoaddTask" first since
949 SafeClipAssembleCoaddTask subtasks that task.
950 In \ref AssembleCoaddTask_ "AssembleCoaddTask", we compute the coadd as an clipped mean (i.e. we clip
952 The problem with doing this is that when computing the coadd PSF at a given location, individual visit
953 PSFs from visits with outlier pixels contribute to the coadd PSF and cannot be treated correctly.
954 In this task, we correct for this behavior by creating a new badMaskPlane 'CLIPPED'.
955 We populate this plane on the input coaddTempExps and the final coadd where i. difference imaging suggests
956 that there is an outlier and ii. this outlier appears on only one or two images.
957 Such regions will not contribute to the final coadd.
958 Furthermore, any routine to determine the coadd PSF can now be cognizant of clipped regions.
959 Note that the algorithm implemented by this task is preliminary and works correctly for HSC data.
960 Parameter modifications and or considerable redesigning of the algorithm is likley required for other
963 SafeClipAssembleCoaddTask uses a \ref SourceDetectionTask_ "clipDetection" subtask and also sub-classes
964 \ref AssembleCoaddTask_ "AssembleCoaddTask". You can retarget the
965 \ref SourceDetectionTask_ "clipDetection" subtask if you wish.
967 \section pipe_tasks_assembleCoadd_SafeClipAssembleCoaddTask_Initialize Task initialization
968 \copydoc \_\_init\_\_
970 \section pipe_tasks_assembleCoadd_SafeClipAssembleCoaddTask_Run Invoking the Task
973 \section pipe_tasks_assembleCoadd_SafeClipAssembleCoaddTask_Config Configuration parameters
974 See \ref SafeClipAssembleCoaddConfig
976 \section pipe_tasks_assembleCoadd_SafeClipAssembleCoaddTask_Debug Debug variables
977 The \link lsst.pipe.base.cmdLineTask.CmdLineTask command line task\endlink interface supports a
978 flag \c -d to import \b debug.py from your \c PYTHONPATH; see \ref baseDebug for more about \b debug.py
980 SafeClipAssembleCoaddTask has no debug variables of its own. The \ref SourceDetectionTask_ "clipDetection"
981 subtasks may support debug variables. See the documetation for \ref SourceDetectionTask_ "clipDetection"
982 for further information.
984 \section pipe_tasks_assembleCoadd_SafeClipAssembleCoaddTask_Example A complete example of using SafeClipAssembleCoaddTask
986 SafeClipAssembleCoaddTask assembles a set of warped coaddTempExp images into a coadded image.
987 The SafeClipAssembleCoaddTask is invoked by running assembleCoadd.py <em>without</em> the flag
989 Usage of assembleCoadd.py expects a data reference to the tract patch and filter to be coadded
990 (specified using '--id = [KEY=VALUE1[^VALUE2[^VALUE3...] [KEY=VALUE1[^VALUE2[^VALUE3...] ...]]') along
991 with a list of coaddTempExps to attempt to coadd (specified using
992 '--selectId [KEY=VALUE1[^VALUE2[^VALUE3...] [KEY=VALUE1[^VALUE2[^VALUE3...] ...]]').
993 Only the coaddTempExps that cover the specified tract and patch will be coadded.
994 A list of the available optional arguments can be obtained by calling assembleCoadd.py with the --help
995 command line argument:
997 assembleCoadd.py --help
999 To demonstrate usage of the SafeClipAssembleCoaddTask in the larger context of multi-band processing, we
1000 will generate the HSC-I & -R band coadds from HSC engineering test data provided in the ci_hsc package. To
1001 begin, assuming that the lsst stack has been already set up, we must set up the obs_subaru and ci_hsc
1003 This defines the environment variable $CI_HSC_DIR and points at the location of the package. The raw HSC
1004 data live in the $CI_HSC_DIR/raw directory. To begin assembling the coadds, we must first
1007 <DD> process the individual ccds in $CI_HSC_RAW to produce calibrated exposures</DD>
1009 <DD> create a skymap that covers the area of the sky present in the raw exposures</DD>
1010 <DT>makeCoaddTempExp</DT>
1011 <DD> warp the individual calibrated exposures to the tangent plane of the coadd</DD>
1013 We can perform all of these steps by running
1015 $CI_HSC_DIR scons warp-903986 warp-904014 warp-903990 warp-904010 warp-903988
1017 This will produce warped coaddTempExps for each visit. To coadd the wraped data, we call assembleCoadd.py
1020 assembleCoadd.py $CI_HSC_DIR/DATA --id patch=5,4 tract=0 filter=HSC-I --selectId visit=903986 ccd=16 --selectId visit=903986 ccd=22 --selectId visit=903986 ccd=23 --selectId visit=903986 ccd=100 --selectId visit=904014 ccd=1 --selectId visit=904014 ccd=6 --selectId visit=904014 ccd=12 --selectId visit=903990 ccd=18 --selectId visit=903990 ccd=25 --selectId visit=904010 ccd=4 --selectId visit=904010 ccd=10 --selectId visit=904010 ccd=100 --selectId visit=903988 ccd=16 --selectId visit=903988 ccd=17 --selectId visit=903988 ccd=23 --selectId visit=903988 ccd=24
1022 This will process the HSC-I band data. The results are written in $CI_HSC_DIR/DATA/deepCoadd-results/HSC-I
1023 You may also choose to run:
1025 scons warp-903334 warp-903336 warp-903338 warp-903342 warp-903344 warp-903346
1026 assembleCoadd.py $CI_HSC_DIR/DATA --id patch=5,4 tract=0 filter=HSC-R --selectId visit=903334 ccd=16 --selectId visit=903334 ccd=22 --selectId visit=903334 ccd=23 --selectId visit=903334 ccd=100 --selectId visit=903336 ccd=17 --selectId visit=903336 ccd=24 --selectId visit=903338 ccd=18 --selectId visit=903338 ccd=25 --selectId visit=903342 ccd=4 --selectId visit=903342 ccd=10 --selectId visit=903342 ccd=100 --selectId visit=903344 ccd=0 --selectId visit=903344 ccd=5 --selectId visit=903344 ccd=11 --selectId visit=903346 ccd=1 --selectId visit=903346 ccd=6 --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, bgModelList, *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 @param bgModelList: List of background models from background matching
1069 return coadd exposure
1071 exp = self.
buildDifferenceImage(skyInfo, tempExpRefList, imageScalerList, weightList, bgModelList)
1072 mask = exp.getMaskedImage().getMask()
1073 mask.addMaskPlane(
"CLIPPED")
1075 result = self.
detectClip(exp, tempExpRefList)
1077 self.log.info(
'Found %d clipped objects', len(result.clipFootprints))
1080 maskClipValue = mask.getPlaneBitMask(
"CLIPPED")
1081 maskDetValue = mask.getPlaneBitMask(
"DETECTED") | mask.getPlaneBitMask(
"DETECTED_NEGATIVE")
1082 bigFootprints = self.
detectClipBig(result.tempExpClipList, result.clipFootprints, result.clipIndices,
1083 maskClipValue, maskDetValue)
1086 maskClip = mask.Factory(mask.getBBox(afwImage.PARENT))
1089 maskClipBig = maskClip.Factory(mask.getBBox(afwImage.PARENT))
1091 maskClip |= maskClipBig
1094 badMaskPlanes = self.config.badMaskPlanes[:]
1095 badMaskPlanes.append(
"CLIPPED")
1096 badPixelMask = afwImage.MaskU.getPlaneBitMask(badMaskPlanes)
1097 coaddExp = AssembleCoaddTask.assemble(self, skyInfo, tempExpRefList, imageScalerList, weightList,
1098 bgModelList, result.tempExpClipList,
1104 maskExp = coaddExp.getMaskedImage().getMask()
1111 \brief Return an exposure that contains the difference between and unclipped and clipped coadds.
1113 Generate a difference image between clipped and unclipped coadds.
1114 Compute the difference image by subtracting an outlier-clipped coadd from an outlier-unclipped coadd.
1115 Return the difference image.
1117 @param skyInfo: Patch geometry information, from getSkyInfo
1118 @param tempExpRefList: List of data reference to tempExp
1119 @param imageScalerList: List of image scalers
1120 @param weightList: List of weights
1121 @param bgModelList: List of background models from background matching
1122 @return Difference image of unclipped and clipped coadd wrapped in an Exposure
1125 coaddMean = AssembleCoaddTask.assemble(self, skyInfo, tempExpRefList, imageScalerList, weightList,
1126 bgModelList, doClip=
False)
1129 coaddClip = AssembleCoaddTask.assemble(self, skyInfo, tempExpRefList, imageScalerList, weightList,
1130 bgModelList, doClip=
True)
1132 coaddDiff = coaddMean.getMaskedImage().Factory(coaddMean.getMaskedImage())
1133 coaddDiff -= coaddClip.getMaskedImage()
1134 exp = afwImage.ExposureF(coaddDiff)
1135 exp.setPsf(coaddMean.getPsf())
1141 \brief Detect clipped regions on an exposure and set the mask on the individual tempExp masks
1143 Detect footprints in the difference image after smoothing the difference image with a Gaussian kernal.
1144 Identify footprints that overlap with one or two input coaddTempExps by comparing the computed overlap
1145 fraction to thresholds set in the config.
1146 A different threshold is applied depending on the number of overlapping visits (restricted to one or
1148 If the overlap exceeds the thresholds, the footprint is considered "CLIPPED" and is marked as such on
1150 Return a struct with the clipped footprints, the indices of the coaddTempExps that end up overlapping
1151 with the clipped footprints and a list of new masks for the coaddTempExps.
1153 \param[in] exp: Exposure to run detection on
1154 \param[in] tempExpRefList: List of data reference to tempExp
1155 \return struct containing:
1156 - clippedFootprints: list of clipped footprints
1157 - clippedIndices: indices for each clippedFootprint in tempExpRefList
1158 - tempExpClipList: list of new masks for tempExp
1160 mask = exp.getMaskedImage().getMask()
1161 maskClipValue = mask.getPlaneBitMask(
"CLIPPED")
1162 maskDetValue = mask.getPlaneBitMask(
"DETECTED") | mask.getPlaneBitMask(
"DETECTED_NEGATIVE")
1163 fpSet = self.clipDetection.detectFootprints(exp, doSmooth=
True, clearMask=
True)
1165 fpSet.positive.merge(fpSet.negative)
1166 footprints = fpSet.positive
1167 self.log.info(
'Found %d potential clipped objects', len(footprints.getFootprints()))
1168 ignoreMask = self.getBadPixelMask()
1174 tempExpClipList = [tmpExpRef.get(self.getTempExpDatasetName(),
1175 immediate=
True).getMaskedImage().getMask()
for tmpExpRef
in tempExpRefList]
1177 for footprint
in footprints.getFootprints():
1178 nPixel = footprint.getArea()
1182 for i, tmpExpMask
in enumerate(tempExpClipList):
1186 totPixel = nPixel - ignore
1189 if ignore > overlapDet
or totPixel <= 0.5*nPixel
or overlapDet == 0:
1191 overlap.append(overlapDet/float(totPixel))
1192 maskList.append(tmpExpMask)
1195 overlap = numpy.array(overlap)
1196 if not len(overlap):
1203 if len(overlap) == 1:
1204 if overlap[0] > self.config.minClipFootOverlapSingle:
1209 clipIndex = numpy.where(overlap > self.config.minClipFootOverlap)[0]
1210 if len(clipIndex) == 1:
1212 keepIndex = [clipIndex[0]]
1215 clipIndex = numpy.where(overlap > self.config.minClipFootOverlapDouble)[0]
1216 if len(clipIndex) == 2
and len(overlap) > 3:
1217 clipIndexComp = numpy.where(overlap < self.config.minClipFootOverlapDouble)[0]
1218 if numpy.max(overlap[clipIndexComp]) < self.config.maxClipFootOverlapDouble:
1220 keepIndex = clipIndex
1225 for index
in keepIndex:
1228 clipIndices.append(numpy.array(indexList)[keepIndex])
1229 clipFootprints.append(footprint)
1231 return pipeBase.Struct(clipFootprints=clipFootprints, clipIndices=clipIndices,
1232 tempExpClipList=tempExpClipList)
1235 def detectClipBig(self, tempExpClipList, clipFootprints, clipIndices, maskClipValue, maskDetValue):
1237 \brief Find footprints from individual tempExp footprints for large footprints.
1239 Identify big footprints composed of many sources in the coadd difference that may have originated in a
1240 large diffuse source in the coadd. We do this by indentifying all clipped footprints that overlap
1241 significantly with each source in all the coaddTempExps.
1243 \param[in] tempExpClipList: List of tempExp masks with clipping information
1244 \param[in] clipFootprints: List of clipped footprints
1245 \param[in] clipIndices: List of which entries in tempExpClipList each footprint belongs to
1246 \param[in] maskClipValue: Mask value of clipped pixels
1247 \param[in] maskClipValue: Mask value of detected pixels
1248 \return list of big footprints
1250 bigFootprintsCoadd = []
1251 ignoreMask = self.getBadPixelMask()
1252 for index, tmpExpMask
in enumerate(tempExpClipList):
1255 maskVisitDet = tmpExpMask.Factory(tmpExpMask, tmpExpMask.getBBox(afwImage.PARENT),
1256 afwImage.PARENT,
True)
1257 maskVisitDet &= maskDetValue
1261 clippedFootprintsVisit = []
1262 for foot, clipIndex
in zip(clipFootprints, clipIndices):
1263 if index
not in clipIndex:
1265 clippedFootprintsVisit.append(foot)
1266 maskVisitClip = maskVisitDet.Factory(maskVisitDet.getBBox(afwImage.PARENT))
1269 bigFootprintsVisit = []
1270 for foot
in visitFootprints.getFootprints():
1271 if foot.getArea() < self.config.minBigOverlap:
1274 if nCount > self.config.minBigOverlap:
1275 bigFootprintsVisit.append(foot)
1276 bigFootprintsCoadd.append(foot)
1279 maskVisitClip.clearAllMaskPlanes()
1281 tmpExpMask |= maskVisitClip
1283 return bigFootprintsCoadd
def __init__
Initialize the task and make the clipDetection subtask.
def run
Assemble a coadd from a set of coaddTempExp.
def getBackgroundReferenceScaler
Construct an image scaler for the background reference frame.
void setCoaddEdgeBits(lsst::afw::image::Mask< lsst::afw::image::MaskPixel > &coaddMask, lsst::afw::image::Image< WeightPixelT > const &weightMap)
set edge bits of coadd mask based on weight map
def assemble
Assemble the coadd for a region.
def addBackgroundMatchingMetadata
Add metadata from the background matching to the coadd.
A Threshold is used to pass a threshold value to detection algorithms.
def detectClipBig
Find footprints from individual tempExp footprints for large footprints.
void swap(Mask< PixelT > &a, Mask< PixelT > &b)
def _subBBoxIter
Iterate over subregions of a bbox.
An integer coordinate rectangle.
def __init__
Initialize the task.
Pass parameters to a Statistics objectA class to pass parameters which control how the stats are calc...
def assembleSubregion
Assemble the coadd for a sub-region.
def assembleMetadata
Set the metadata for the coadd.
CoaddPsf is the Psf derived to be used for non-PSF-matched Coadd images.
def backgroundMatching
Perform background matching on the prepared inputs.
Configuration parameters for the SafeClipAssembleCoaddTask.
Assemble a coadded image from a set of coadded temporary exposures.
Assemble a coadded image from a set of coadded temporary exposures, being careful to clip & flag area...
def buildDifferenceImage
Return an exposure that contains the difference between and unclipped and clipped coadds...
def _makeArgumentParser
Create an argument parser.
def detectClip
Detect clipped regions on an exposure and set the mask on the individual tempExp masks.
def prepareInputs
Prepare the input warps for coaddition by measuring the weight for each warp and the scaling for the ...
def makeDataRefList
Make self.refList from self.idList.
MaskT setMaskFromFootprint(lsst::afw::image::Mask< MaskT > *mask, Footprint const &footprint, MaskT const bitmask)
OR bitmask into all the Mask's pixels that are in the Footprint.
Statistics makeStatistics(afwImage::Mask< afwImage::MaskPixel > const &msk, int const flags, StatisticsControl const &sctrl)
Specialization to handle Masks.
Configuration parameters for the AssembleCoaddTask.
MaskT setMaskFromFootprintList(lsst::afw::image::Mask< MaskT > *mask, boost::shared_ptr< std::vector< boost::shared_ptr< Footprint >> const > const &footprints, MaskT const bitmask)
OR bitmask into all the Mask's pixels which are in the set of Footprints.
def countMaskFromFootprint
Function to count the number of pixels with a specific mask in a footprint.
def assemble
Assemble a coadd from input warps.
def readBrightObjectMasks
A version of lsst.pipe.base.DataIdContainer specialized for assembleCoadd.
lsst::afw::image::MaskedImage< PixelT >::Ptr statisticsStack(lsst::afw::image::MaskedImage< PixelT > const &image, Property flags, char dimension, StatisticsControl const &sctrl=StatisticsControl())
A function to compute statistics on the rows or columns of an image.
def getTempExpRefList
Generate list of coaddTempExp data references corresponding to exposures that lie within the patch to...