-
Notifications
You must be signed in to change notification settings - Fork 18
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
DM-12445: Set appropriate default configs for CompareWarp Coadds #162
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -1446,28 +1446,31 @@ class CompareWarpAssembleCoaddConfig(AssembleCoaddConfig): | |
target=SourceDetectionTask, | ||
doc="Detect outlier sources on difference between each psfMatched warp and static sky model" | ||
) | ||
temporalThreshold = pexConfig.Field( | ||
doc="Unitless fraction of number of epochs to classify as an artifact/outlier source versus" | ||
" a source that is intrinsically variable or difficult to subtract cleanly. " | ||
"If outlier region in warp-diff Chi-image is mostly (defined by spatialThreshold) " | ||
"an outlier in less than temporalThreshold * number of epochs, then mask. " | ||
"Otherwise, do not mask.", | ||
dtype=float, | ||
default=0.075 | ||
maxNumEpochs = pexConfig.Field( | ||
doc="Maximum number of epochs/visits in which an artifact candidate can appear and still be masked. " | ||
"For each footprint detected on the image difference between the psfMatched warp and static sky " | ||
"model, if a significant fraction of pixels (defined by spatialThreshold) are residuals in more " | ||
"than maxNumEpochs, the artifact candidate is persistant rather than transient and not masked.", | ||
dtype=int, | ||
default=2 | ||
) | ||
spatialThreshold = pexConfig.Field( | ||
spatialThreshold = pexConfig.RangeField( | ||
doc="Unitless fraction of pixels defining how much of the outlier region has to meet the " | ||
"temporal criteria", | ||
"temporal criteria. If 0, clip all. If 1, clip none.", | ||
dtype=float, | ||
default=0.5 | ||
default=0.5, | ||
min=0., max=1., | ||
inclusiveMin=True, inclusiveMax=True | ||
) | ||
|
||
def setDefaults(self): | ||
AssembleCoaddConfig.setDefaults(self) | ||
self.statistic = 'MEAN' | ||
self.assembleStaticSkyModel.warpType = 'psfMatched' | ||
self.assembleStaticSkyModel.statistic = 'MEDIAN' | ||
self.assembleStaticSkyModel.statistic = 'MEANCLIP' | ||
self.assembleStaticSkyModel.sigmaClip = 1.5 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why such harsh clipping? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Borrowed from SafeClip. Running a grid of configs is still a to do: DM-12698 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. But harsh clipping like this is part of the SafeClip algorithm (we deliberately toss out anything that looks even remotely bad in order to make a very clean model of the sky), whereas I wouldn't have thought it would be for this. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is the same idea. Make a very very clean and naive robust PSF-match coadd to serve as the naive model of the static sky. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ah, I see --- this is for the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. For 2< N<=5 especially, we have to be strict to get the junk out of the naive template:
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Perhaps that's due to a shortcoming in our statistics suite (no robust stdev), or because you're not using
( There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That's cool. My point was that if I increase
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. OK. How common is it that 40% of the inputs have outliers? |
||
self.assembleStaticSkyModel.clipIter = 3 | ||
self.assembleStaticSkyModel.doWrite = False | ||
self.statistic = 'MEAN' | ||
self.detect.doTempLocalBackground = False | ||
self.detect.reEstimateBackground = False | ||
self.detect.returnOriginalFootprints = False | ||
|
@@ -1721,7 +1724,6 @@ def findArtifacts(self, templateCoadd, tempExpRefList, imageScalerList): | |
spanSetArtifactList = [] | ||
spanSetNoDataMaskList = [] | ||
|
||
maxNumEpochs = int(max(1, self.config.temporalThreshold*len(tempExpRefList))) | ||
for warpRef, imageScaler in zip(tempExpRefList, imageScalerList): | ||
warpDiffExp = self._readAndComputeWarpDiff(warpRef, imageScaler, templateCoadd) | ||
if warpDiffExp is not None: | ||
|
@@ -1758,8 +1760,7 @@ def findArtifacts(self, templateCoadd, tempExpRefList, imageScalerList): | |
|
||
for i, spanSetList in enumerate(spanSetArtifactList): | ||
if spanSetList: | ||
filteredSpanSetList = self._filterArtifacts(spanSetList, epochCountImage, | ||
maxNumEpochs=maxNumEpochs) | ||
filteredSpanSetList = self._filterArtifacts(spanSetList, epochCountImage) | ||
spanSetArtifactList[i] = filteredSpanSetList | ||
|
||
return pipeBase.Struct(artifacts=spanSetArtifactList, | ||
|
@@ -1794,25 +1795,44 @@ def computeAltMaskList(self, tempExpRefList, maskSpanSets): | |
|
||
return altMaskList | ||
|
||
def _filterArtifacts(self, spanSetList, epochCountImage, maxNumEpochs=None): | ||
def _filterArtifacts(self, spanSetList, epochCountImage): | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This method needs a docstring. |
||
"""! | ||
\brief Filter artifact candidates | ||
|
||
@param spanSetList: List of SpanSets representing artifact candidates | ||
@param epochCountImage: Image of accumulated number of warpDiff detections | ||
|
||
return List of SpanSets with artifacts | ||
""" | ||
|
||
maskSpanSetList = [] | ||
x0, y0 = epochCountImage.getXY0() | ||
for i, span in enumerate(spanSetList): | ||
y, x = span.indices() | ||
counts = epochCountImage.array[[y1 - y0 for y1 in y], [x1 - x0 for x1 in x]] | ||
idx = numpy.where((counts > 0) & (counts <= maxNumEpochs)) | ||
percentBelowThreshold = len(idx[0]) / len(counts) | ||
nCountsBelowThreshold = numpy.count_nonzero((counts > 0) & (counts <= self.config.maxNumEpochs)) | ||
percentBelowThreshold = nCountsBelowThreshold / len(counts) | ||
if percentBelowThreshold > self.config.spatialThreshold: | ||
maskSpanSetList.append(span) | ||
return maskSpanSetList | ||
|
||
def _readAndComputeWarpDiff(self, warpRef, imageScaler, templateCoadd): | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This method needs a docstring. |
||
"""! | ||
\brief Fetch a warp from the butler and return a warpDiff | ||
|
||
@param warpRef: `Butler dataRef` for the warp | ||
@param imageScaler: `scaleZeroPoint.ImageScaler` object | ||
@param templateCoadd: Exposure to be substracted from the scaled warp | ||
|
||
return Exposure of the image difference between the warp and template | ||
""" | ||
|
||
# Warp comparison must use PSF-Matched Warps regardless of requested coadd warp type | ||
warpName = self.getTempExpDatasetName('psfMatched') | ||
if not warpRef.datasetExists(warpName): | ||
self.log.warn("Could not find %s %s; skipping it", warpName, warpRef.dataId) | ||
return None | ||
warp = warpRef.get(self.getTempExpDatasetName('psfMatched'), immediate=True) | ||
warp = warpRef.get(warpName, immediate=True) | ||
# direct image scaler OK for PSF-matched Warp | ||
imageScaler.scaleMaskedImage(warp.getMaskedImage()) | ||
mi = warp.getMaskedImage() | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would have thought that a fraction would be so much more generally useful than a hard number. I'm worried that a hard number isn't going to scale as you go from n=4 to n=400 inputs.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just made a new ticket for this: DM-12692. I had talked briefly with @RobertLuptonTheGood about it. You're right maxNumEpochs won't scale, but neither will a fraction of the maximum number of epochs. I've seen some steep Nepoch gradients in a patch. Its also depends on how many repeat pointings line up the artifacts.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, it probably needs some combination of a number and a fraction, maybe
max(maxNumEpochs, fracMaxEpochs*num)
?