-
Notifications
You must be signed in to change notification settings - Fork 0
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-38563: Compare LATISS Defects generated from combined images #31
Merged
Merged
Changes from all commits
Commits
Show all changes
15 commits
Select commit
Hold shift + click to select a range
dce9ddc
Add new verify defect pipeline to compare on vs off.
e34fe44
Add latiss-specific verify defects pipeline.
a2c4836
Add connections to verify defects
02607bb
Use Defects connections in config
d4ccdd8
Change datasets names
c9a063b
Rename ingredients file for combined defects.
f99d529
Adjust Latiss combined defect pipeline
28038f9
Rename LAtiss combined defect pipeline
0749c69
Add verify combined tests
f45653b
Add connections to verifyStats
d3dc2c8
Update verifystats
f948d6c
Update pipeline
63886a4
Define detector statistics function
622c5cd
Add comment and fix formatting
ff957ec
Adjust unit tests
File filter
Filter by extension
Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
description: Another cp_verify DEFECT calibration verification | ||
instrument: lsst.obs.lsst.Latiss | ||
imports: | ||
- location: $CP_VERIFY_DIR/pipelines/_ingredients/VerifyCombinedDefect.yaml |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
description: Another cp_verify defect calibration verification pipeline | ||
tasks: | ||
verifyDefectIsr: | ||
class: lsst.ip.isr.isrTask.IsrTask | ||
config: | ||
connections.ccdExposure: 'raw' | ||
connections.outputExposure: 'verifyDefectIsr' | ||
doDefect: true | ||
doCrosstalk: true | ||
verifyDefectChar: | ||
class: lsst.pipe.tasks.characterizeImage.CharacterizeImageTask | ||
config: | ||
connections.exposure: 'verifyDefectIsr' | ||
doApCorr: false | ||
doDeblend: false | ||
verifyUncorrectedDefectIsr: | ||
class: lsst.ip.isr.isrTask.IsrTask | ||
config: | ||
connections.ccdExposure: 'raw' | ||
connections.outputExposure: 'verifyUncorrDefectIsr' | ||
doDefect: false | ||
doCrosstalk: true | ||
verifyUncorrectedDefectChar: | ||
class: lsst.pipe.tasks.characterizeImage.CharacterizeImageTask | ||
config: | ||
connections.exposure: 'verifyUncorrDefectIsr' | ||
connections.characterized: 'verifyUncorrDefectExp' | ||
connections.sourceCat: 'verifyUncorrDefectSrc' | ||
connections.backgroundModel: 'verifyUncorrDefectBackground' | ||
connections.outputSchema: 'verifyUncorrDefect_schema' | ||
doApCorr: false | ||
doDeblend: false | ||
verifyDefectChip: | ||
class: lsst.cp.verify.verifyDefects.CpVerifyDefectsTask | ||
config: | ||
connections.inputExp: 'icExp' | ||
connections.uncorrectedExp: 'verifyUncorrDefectExp' | ||
connections.inputCatalog: 'icSrc' | ||
connections.uncorrectedCatalog: 'verifyUncorrDefectSrc' | ||
connections.outputStats: 'verifyCombinedDefectDetStats' | ||
verifyDefectExp: | ||
class: lsst.cp.verify.CpVerifyVisitExpMergeTask | ||
config: | ||
connections.inputStats: 'verifyCombinedDefectDetStats' | ||
connections.outputStats: 'verifyCombinedDefectExpStats' | ||
verifyDefect: | ||
class: lsst.cp.verify.CpVerifyVisitRunMergeTask | ||
config: | ||
connections.inputStats: 'verifyCombinedDefectExpStats' | ||
connections.outputStats: 'verifyCombinedDefectStats' |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -20,43 +20,190 @@ | |
# along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
import numpy as np | ||
import scipy.stats | ||
import lsst.pipe.base.connectionTypes as cT | ||
from lsst.ip.isr.isrFunctions import countMaskedPixels | ||
|
||
from .verifyStats import CpVerifyStatsConfig, CpVerifyStatsTask, CpVerifyStatsConnections | ||
from .verifyStats import ( | ||
CpVerifyStatsConfig, | ||
CpVerifyStatsTask, | ||
CpVerifyStatsConnections, | ||
) | ||
|
||
|
||
__all__ = ['CpVerifyDefectsConfig', 'CpVerifyDefectsTask'] | ||
__all__ = ["CpVerifyDefectsConfig", "CpVerifyDefectsTask"] | ||
|
||
|
||
class CpVerifyDefectsConfig(CpVerifyStatsConfig, | ||
pipelineConnections=CpVerifyStatsConnections): | ||
"""Inherits from base CpVerifyStatsConfig. | ||
""" | ||
class CpVerifyDefectsConnections( | ||
CpVerifyStatsConnections, dimensions={"instrument", "visit", "detector"} | ||
): | ||
inputExp = cT.Input( | ||
name="icExp", | ||
doc="Input exposure to calculate statistics for.", | ||
storageClass="ExposureF", | ||
dimensions=["instrument", "visit", "detector"], | ||
) | ||
uncorrectedExp = cT.Input( | ||
name="uncorrectedExp", | ||
doc="Uncorrected input exposure to calculate statistics for.", | ||
storageClass="ExposureF", | ||
dimensions=["instrument", "visit", "detector"], | ||
) | ||
inputCatalog = cT.Input( | ||
name="icSrc", | ||
doc="Input catalog to calculate statistics from.", | ||
storageClass="SourceCatalog", | ||
dimensions=["instrument", "visit", "detector"], | ||
) | ||
uncorrectedCatalog = cT.Input( | ||
name="uncorrectedSrc", | ||
doc="Input catalog without correction applied.", | ||
storageClass="SourceCatalog", | ||
dimensions=["instrument", "visit", "detector"], | ||
) | ||
camera = cT.PrerequisiteInput( | ||
name="camera", | ||
storageClass="Camera", | ||
doc="Input camera.", | ||
dimensions=["instrument", ], | ||
isCalibration=True, | ||
) | ||
outputStats = cT.Output( | ||
name="detectorStats", | ||
doc="Output statistics from cp_verify.", | ||
storageClass="StructuredDataDict", | ||
dimensions=["instrument", "visit", "detector"], | ||
) | ||
|
||
|
||
class CpVerifyDefectsConfig( | ||
CpVerifyStatsConfig, pipelineConnections=CpVerifyDefectsConnections | ||
): | ||
"""Inherits from base CpVerifyStatsConfig.""" | ||
|
||
def setDefaults(self): | ||
super().setDefaults() | ||
self.maskNameList = ['BAD'] # noqa F821 | ||
self.maskNameList = ["BAD"] # noqa F821 | ||
|
||
self.imageStatKeywords = {'DEFECT_PIXELS': 'NMASKED', # noqa F821 | ||
'OUTLIERS': 'NCLIPPED', | ||
'MEDIAN': 'MEDIAN', | ||
'STDEV': 'STDEVCLIP', | ||
'MIN': 'MIN', | ||
'MAX': 'MAX', } | ||
self.unmaskedImageStatKeywords = {'UNMASKED_MIN': 'MIN', # noqa F821 | ||
'UNMASKED_MAX': 'MAX', | ||
'UNMASKED_STDEV': 'STDEVCLIP', | ||
'UNMASKED_OUTLIERS': 'NCLIPPED', } | ||
self.imageStatKeywords = { | ||
"DEFECT_PIXELS": "NMASKED", # noqa F821 | ||
"OUTLIERS": "NCLIPPED", | ||
"MEDIAN": "MEDIAN", | ||
"STDEV": "STDEVCLIP", | ||
"MIN": "MIN", | ||
"MAX": "MAX", | ||
} | ||
self.unmaskedImageStatKeywords = { | ||
"UNMASKED_MIN": "MIN", # noqa F821 | ||
"UNMASKED_MAX": "MAX", | ||
"UNMASKED_STDEV": "STDEVCLIP", | ||
"UNMASKED_OUTLIERS": "NCLIPPED", | ||
} | ||
self.uncorrectedImageStatKeywords = { | ||
"UNC_DEFECT_PIXELS": "NMASKED", # noqa F821 | ||
"UNC_OUTLIERS": "NCLIPPED", | ||
"UNC_MEDIAN": "MEDIAN", | ||
"UNC_STDEV": "STDEVCLIP", | ||
"UNC_MIN": "MIN", | ||
"UNC_MAX": "MAX", | ||
} | ||
# These config options need to have a key/value pair | ||
# to run verification analysis, but the contents of | ||
# that pair are not used. | ||
self.catalogStatKeywords = {"empty": "dictionary"} | ||
self.detectorStatKeywords = {"empty": "dictionary"} | ||
|
||
|
||
class CpVerifyDefectsTask(CpVerifyStatsTask): | ||
"""Defects verification sub-class, implementing the verify method. | ||
|
||
This also applies additional image processing statistics. | ||
""" | ||
|
||
ConfigClass = CpVerifyDefectsConfig | ||
_DefaultName = 'cpVerifyDefects' | ||
_DefaultName = "cpVerifyDefects" | ||
|
||
def catalogStatistics(self, exposure, catalog, uncorrectedCatalog, statControl): | ||
"""Measure the catalog statistics. | ||
|
||
Parameters | ||
---------- | ||
exposure : `lsst.afw.image.Exposure` | ||
The exposure to measure. | ||
catalog : `lsst.afw.table.Table` | ||
The catalog to measure. | ||
uncorrectedCatalog : `lsst.afw.table.Table` | ||
The uncorrected catalog to measure. | ||
statControl : `lsst.afw.math.StatisticsControl` | ||
Statistics control object with parameters defined by | ||
the config. | ||
|
||
Returns | ||
------- | ||
outputStatistics : `dict` [`str`, `dict` [`str`, scalar]] | ||
A dictionary indexed by the amplifier name, containing | ||
dictionaries of the statistics measured and their values. | ||
|
||
Notes | ||
----- | ||
Number of detections test: running with defects would have fewer | ||
detections | ||
""" | ||
outputStatistics = {} | ||
|
||
# Number of detections test | ||
outputStatistics["NUM_OBJECTS_BEFORE"] = len(uncorrectedCatalog) | ||
outputStatistics["NUM_OBJECTS_AFTER"] = len(catalog) | ||
|
||
return outputStatistics | ||
|
||
def detectorStatistics(self, statsDict, statControl, exposure, uncorrectedExposure): | ||
"""Measure the detector statistics. | ||
|
||
def imageStatistics(self, exposure, statControl): | ||
Parameters | ||
---------- | ||
statsDict : `dict` [`str`, scalar] | ||
Dictionary with detector tests. | ||
exposure : `lsst.afw.image.Exposure` | ||
Exposure containing the ISR processed data to measure. | ||
statControl : `lsst.afw.math.StatControl` | ||
Statistics control object with parameters defined by | ||
the config. | ||
exposure : `lsst.afw.image.Exposure` | ||
Exposure containing the ISR-processed data to measure. | ||
uncorrectedExposure : `lsst.afw.image.Exposure` | ||
uncorrected esposure (no defects) containing the | ||
ISR-processed data to measure. | ||
|
||
Returns | ||
------- | ||
outputStatistics : `dict` [`str`, scalar] | ||
A dictionary containing statistics measured and their values. | ||
|
||
Notes | ||
----- | ||
Number of cosmic rays test: If there are defects in our data that | ||
we didn't properly identify and cover, they might appear similar | ||
to cosmic rays because they have sharp edges compared to the point | ||
spread function (PSF). When we process the data, if these defects | ||
aren't marked in our defect mask, the software might mistakenly think | ||
they are cosmic rays and try to remove them. However, if we've already | ||
included these defects in the defect mask, the software won't treat | ||
them as cosmic rays, so we'll have fewer pixels that are falsely | ||
identified and removed as cosmic rays when we compare two sets of | ||
data reductions. | ||
""" | ||
outputStatistics = {} | ||
# Cosmic Rays test: Count number of cosmic rays before | ||
# and after masking with defects | ||
nCosmicsBefore = countMaskedPixels(uncorrectedExposure, ["CR"]) | ||
nCosmicsAfter = countMaskedPixels(exposure, ["CR"]) | ||
|
||
outputStatistics["NUM_COSMICS_BEFORE"] = nCosmicsBefore | ||
outputStatistics["NUM_COSMICS_AFTER"] = nCosmicsAfter | ||
|
||
return outputStatistics | ||
|
||
def imageStatistics(self, exposure, uncorrectedExposure, statControl): | ||
"""Measure additional defect statistics. | ||
|
||
This calls the parent class method first, then adds additional | ||
|
@@ -75,8 +222,23 @@ def imageStatistics(self, exposure, statControl): | |
outputStatistics : `dict` [`str`, `dict` [`str`, scalar]] | ||
A dictionary indexed by the amplifier name, containing | ||
dictionaries of the statistics measured and their values. | ||
|
||
Notes | ||
----- | ||
Number of cosmic rays test: If there are defects in our data that | ||
we didn't properly identify and cover, they might appear similar | ||
to cosmic rays because they have sharp edges compared to the point | ||
spread function (PSF). When we process the data, if these defects | ||
aren't marked in our defect mask, the software might mistakenly think | ||
they are cosmic rays and try to remove them. However, if we've already | ||
included these defects in the defect mask, the software won't treat | ||
them as cosmic rays, so we'll have fewer pixels that are falsely | ||
identified and removed as cosmic rays when we compare two sets of | ||
data reductions. | ||
""" | ||
outputStatistics = super().imageStatistics(exposure, statControl) | ||
outputStatistics = super().imageStatistics( | ||
exposure, uncorrectedExposure, statControl | ||
) | ||
|
||
# Is this a useful test? It saves having to do chi^2 fits, | ||
# which are going to be biased by the bulk of points. | ||
|
@@ -87,12 +249,12 @@ def imageStatistics(self, exposure, statControl): | |
normImage = ampExp.getImage() | ||
normArray = normImage.getArray() | ||
|
||
normArray -= outputStatistics[ampName]['MEDIAN'] | ||
normArray /= outputStatistics[ampName]['STDEV'] | ||
normArray -= outputStatistics[ampName]["MEDIAN"] | ||
normArray /= outputStatistics[ampName]["STDEV"] | ||
|
||
probability = scipy.stats.norm.pdf(normArray) | ||
outliers = np.where(probability < 1.0 / probability.size, 1.0, 0.0) | ||
outputStatistics[ampName]['STAT_OUTLIERS'] = int(np.sum(outliers)) | ||
outputStatistics[ampName]["STAT_OUTLIERS"] = int(np.sum(outliers)) | ||
|
||
return outputStatistics | ||
|
||
|
@@ -117,25 +279,57 @@ def verify(self, exposure, statisticsDict): | |
success : `bool` | ||
A boolean indicating if all tests have passed. | ||
""" | ||
ampStats = statisticsDict['AMP'] | ||
# Amplifier statistics | ||
ampStats = statisticsDict["AMP"] | ||
verifyStats = {} | ||
success = True | ||
successAmp = True | ||
for ampName, stats in ampStats.items(): | ||
verify = {} | ||
|
||
# These are not defined in DMTN-101 yet. | ||
verify['OUTLIERS'] = bool(stats['UNMASKED_OUTLIERS'] >= stats['OUTLIERS']) | ||
verify['STDEV'] = bool(stats['UNMASKED_STDEV'] >= stats['STDEV']) | ||
verify['MIN'] = bool(stats['UNMASKED_MIN'] <= stats['MIN']) | ||
verify['MAX'] = bool(stats['UNMASKED_MAX'] >= stats['MAX']) | ||
verify["OUTLIERS"] = bool(stats["UNMASKED_OUTLIERS"] >= stats["OUTLIERS"]) | ||
verify["STDEV"] = bool(stats["UNMASKED_STDEV"] >= stats["STDEV"]) | ||
verify["MIN"] = bool(stats["UNMASKED_MIN"] <= stats["MIN"]) | ||
verify["MAX"] = bool(stats["UNMASKED_MAX"] >= stats["MAX"]) | ||
|
||
# This test is bad, and should be made not bad. | ||
verify['PROB_TEST'] = bool(stats['STAT_OUTLIERS'] == stats['DEFECT_PIXELS']) | ||
verify["PROB_TEST"] = bool(stats["STAT_OUTLIERS"] == stats["DEFECT_PIXELS"]) | ||
|
||
verify['SUCCESS'] = bool(np.all(list(verify.values()))) | ||
if verify['SUCCESS'] is False: | ||
success = False | ||
verify["SUCCESS"] = bool(np.all(list(verify.values()))) | ||
if verify["SUCCESS"] is False: | ||
successAmp = False | ||
|
||
verifyStats[ampName] = verify | ||
|
||
return {'AMP': verifyStats}, bool(success) | ||
# Detector statistics | ||
detStats = statisticsDict["DET"] | ||
verifyStatsDet = {} | ||
successDet = True | ||
# Cosmic rays test from DM-38563, before and after defects. | ||
verifyStatsDet["NUMBER_COSMIC_RAYS"] = bool( | ||
detStats["NUM_COSMICS_BEFORE"] > detStats["NUM_COSMICS_AFTER"] | ||
) | ||
|
||
verifyStatsDet["SUCCESS"] = bool(np.all(list(verifyStatsDet.values()))) | ||
if verifyStatsDet["SUCCESS"] is False: | ||
successDet = False | ||
|
||
# Catalog statistics | ||
catStats = statisticsDict["CATALOG"] | ||
verifyStatsCat = {} | ||
successCat = True | ||
# Detection tests from DM-38563, before and after defects. | ||
verifyStatsCat["NUMBER_DETECTIONS"] = bool( | ||
catStats["NUM_OBJECTS_BEFORE"] > catStats["NUM_OBJECTS_AFTER"] | ||
) | ||
|
||
verifyStatsCat["SUCCESS"] = bool(np.all(list(verifyStatsCat.values()))) | ||
if verifyStatsCat["SUCCESS"] is False: | ||
successCat = False | ||
|
||
success = successDet & successAmp & successCat | ||
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. I'm not convinced |
||
return { | ||
"AMP": verifyStats, | ||
"DET": verifyStatsDet, | ||
"CATALOG": verifyStatsCat, | ||
}, bool(success) |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
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.
Thank you for clearing up my inconsistent use of ' and ". It's the same as the formatting issue, and I know I'm consistently inconsistent.