DM-54625: Update preconvolution with recent diffim improvements#453
DM-54625: Update preconvolution with recent diffim improvements#453
Conversation
1516e2a to
a51832a
Compare
3a48aa0 to
8b113d3
Compare
8a2e2ab to
cd5766b
Compare
kfindeisen
left a comment
There was a problem hiding this comment.
As forewarned, lots of comments. Some of the factoring I'm asking for is pre-existing and should probably be deferred to a later work item, but I suggest at least streamlining the "when to do CR removal vs. background removal" logic. And I really don't understand how the Diff_scoreExp vs. Diff_scoreTempExp swap doesn't break things.
| x0 = 123 | ||
| y0 = 456 | ||
| kernelSize = 31 | ||
| templateBorderSize = kernelSize//2 |
There was a problem hiding this comment.
Again, if you're going to have a comment for one copy of the code, you probably need it everywhere. (Can the copies be factored out?)
|
This is ready for another round of review. The commits that start "fixup" are intended to be squashed into the referenced commit. I left them pre-rebasing so that the changes are visible. |
Also make sure to use a consistent PSF size for preconvolution and decorrelation.
069011b to
930510f
Compare
kfindeisen
left a comment
There was a problem hiding this comment.
Mostly minor comments, but I'm still concerned about the change in role for *Diff_scoreExp -- something you said was a bug earlier? If it is, it would affect lsst/ap_pipe#269 as well.
| # the size was auto-computed. An explicit ``scienceFwhmPix`` is | ||
| # assumed already to reflect the preconvolved effective width. | ||
| if preconvolved: | ||
| scienceFwhmPix *= np.sqrt(2) |
There was a problem hiding this comment.
This code runs if scienceFwhmPix was passed in but templateFwhmPix was None. Was that the intent? Same with the indented block on lines 213-214 and the version moved into _checkPsfWidths.
There was a problem hiding this comment.
Yes, that was intentional. Both scienceFwhmPix and templateFwhmPix have to be specified or else both are recalculated, and in that case the preconvolved check needs to be made.
| # Background is measured on the difference image, and then | ||
| # subtracted from both the difference and score images. | ||
| # The preconvolution kernel is normalized to 1, so the | ||
| # same background level applies to both. | ||
| background = self.subtractInitialBackground.run(difference.clone()).background | ||
| detectionScoreExposure = scoreExposure.clone() | ||
| detectionScoreExposure.image -= background.getImage() |
There was a problem hiding this comment.
I still think it's confusing to talk about subtracting the background from the difference image when you're going out of your way to not do that. Maybe just have the last two lines (with "both difference and score images")?
| _detection_wrapper(positive=True) | ||
| _detection_wrapper(positive=False) | ||
|
|
||
| def test_mask_cosmic_rays(self): |
There was a problem hiding this comment.
Redefinition of method from line 417.
There was a problem hiding this comment.
It is not obvious, but this is a different version than the first one. I'll update the docstring to mention the difference.
| backgroundModel.setParameters([-p for p in modelParams]) | ||
| invertedBackground = backgroundModel.clone() | ||
| invertedBackground.setParameters([-p for p in backgroundModel.getParameters()]) | ||
| backgroundModel = invertedBackground |
There was a problem hiding this comment.
Could you expand the commit message to mention that this is for legibility? Right now it just says "Clone the background model before inverting", which implies that there's some obvious(?) reason why you have to do that (e.g., a bug fix).
| maglim = np.nan | ||
| return np.nan | ||
| self.log.info("Unable to evaluate PSF, using fallback FWHM %f", fallbackPsfSize) | ||
| psf_area = np.pi*(fallbackPsfSize/2)**2 |
There was a problem hiding this comment.
Could you mention the return-from-finally in the commit message, so that it's clearer what's going on?
| self._prepareInputs(template, science, visitSummary=visitSummary) | ||
|
|
||
| convolveTemplate = self.chooseConvolutionMethod(template, science) | ||
| self.matchedPsfSize = self.sciencePsfSize if convolveTemplate else self.templatePsfSize |
There was a problem hiding this comment.
Should the commit message read "exactly once" instead of "only once"?
| Sources from the input catalog that were used to construct the | ||
| PSF-matching kernel. | ||
| """ | ||
| self.usePreconvolution = False |
There was a problem hiding this comment.
Something seems fishy here. The only references that set usePreconvolution are:
AlardLuptonSubtractTask.runsetsself.usePreconvolution = FalseAlardLuptonPreconvolveSubtractTask.runsetsself.usePreconvolution = TrueSimplifiedSubtractTask.runsetsself.usePreconvolution = False
If the goal is to define usePreconvolution as a function of the implementation class instead of the arguments, then it would be better to set as a class constant, where at least it's clear that there won't be any side effects or weird interactions (e.g., what if AlardLuptonPreconvolveSubtractTask.run called super().run(), unlikely as it is?). It also avoids any potential problems from usePreconvolution being referenced before run is called.
(TBH, making this a class variable/constant still feels wrong, but I can't come up with a better idea at the moment.)
There was a problem hiding this comment.
I've moved usePreconvolution to a class variable, and checked that it is used instead of other flags where possible.
| """ | ||
|
|
||
| if self.usePreconvolution: | ||
| raise RuntimeError("Incorrect matching kernel calculation configured.") |
There was a problem hiding this comment.
Be more specific about how it's incorrect?
| self.preconvolvedSciencePsfSize = self.sciencePsfSize*np.sqrt(2) | ||
| self.log.info("Preconvolved science PSF FWHM: %f pixels", self.preconvolvedSciencePsfSize) | ||
| self.metadata["preconvolvedSciencePsfSize"] = self.preconvolvedSciencePsfSize | ||
| self.matchedPsfSize = self.sciencePsfSize*np.sqrt(2) |
There was a problem hiding this comment.
Duplicate of line 1228 (though I think this spot is better, since it's immediately before first use).
| if self.config.doSubtractBackground: | ||
| # Run background subtraction before clearing the mask planes | ||
| detectionExposure = difference.clone() | ||
| background = self.subtractInitialBackground.run(detectionExposure).background | ||
| else: | ||
| detectionExposure = difference | ||
| background = afwMath.BackgroundList() | ||
| # Run background subtraction (if configured) before clearing the mask | ||
| # planes. | ||
| detectionExposure, background = self._subtractInitialBackground(differenceExposure=difference) | ||
|
|
||
| self._prepareInputs(detectionExposure) | ||
|
|
||
| if self.config.doFindCosmicRays and not self.config.doSubtractBackground: | ||
| # Detect and interpolate over any remaining cosmic rays | ||
| # This only needs to run once, right before the final detection. | ||
| self.findAndMaskCosmicRays(detectionExposure) | ||
| # Detect and interpolate over any remaining cosmic rays. When | ||
| # background subtraction is enabled, this is deferred to the second | ||
| # pass so it runs only once on a properly background-subtracted image. | ||
| self._findInitialCosmicRays(differenceExposure=difference) |
There was a problem hiding this comment.
This isn't what I had in mind; I thought it would be simpler to have something like:
# Preprocess or skip
if doSubtractBackground:
detectionExposure = copy
background = rough background
self._prepareInputs(detectionExposure) # Only run on first pass, but not second?
results = detection.run(detectionExposure)
background = final background
else:
background = empty
self._prepareInputs(difference) # Run on only pass
# Final processing
self.findAndMaskCosmicRays(difference) # current code aliases as detectionExposure if not doSubtractBackground
results = detection.run(difference)
What you're proposing here obfuscates the multiple doSubtractBackground checks instead of eliminating them.
There was a problem hiding this comment.
I've pushed a new version that refactors background subtraction and cosmic ray masking.
kfindeisen
left a comment
There was a problem hiding this comment.
With the connections question resolved, I have no major objections.
Otherwise the results are unstable when the science image has a psfSize of 2.0 in the unit tests and the template has a psfSize of 2.3 to 2.4. This change makes the basis a smooth transition instead of a sudden jump.
Mostly for readability, but this does prevent modifying the original background model if the function is called in a notebook environment.
The return statement inside the finally block is bad practice and suppressed error messages.
0faf38c to
229d253
Compare
This does not suffer from the same bug as setting the image plane to 0 for masked pixels, since the peak is removed after detection.
Consolidate setting common values, but there should be no behavior changes.
Also remove the redundant calculation of the kernel source list in subtractImages - makeCandidateList was effectively called twice.
Use common code for both standard and Score versions.
229d253 to
610e70c
Compare
No description provided.