23 Measure intra-CCD crosstalk coefficients. 26 __all__ = [
"extractCrosstalkRatios",
"measureCrosstalkCoefficients",
27 "MeasureCrosstalkConfig",
"MeasureCrosstalkTask"]
38 from .crosstalk
import calculateBackground, extractAmp, writeCrosstalkCoeffs
39 from .isrTask
import IsrTask
43 """Extract crosstalk ratios between different amplifiers 45 For pixels above ``threshold``, we calculate the ratio between each 46 target amp and source amp. We return a list of ratios for each pixel 47 for each target/source combination, as a matrix of lists. 51 exposure : `lsst.afw.image.Exposure` 52 Exposure for which to measure crosstalk. 54 Lower limit on pixels for which we measure crosstalk. 55 badPixels : `list` of `str` 56 Mask planes indicating a pixel is bad. 60 ratios : `list` of `list` of `numpy.ndarray` 61 A matrix of pixel arrays. ``ratios[i][j]`` is an array of 62 the fraction of the ``j``-th amp present on the ``i``-th amp. 63 The value is `None` for the diagonal elements. 65 mi = exposure.getMaskedImage()
67 detected = mi.getMask().getPlaneBitMask(
"DETECTED")
68 bad = mi.getMask().getPlaneBitMask(badPixels)
71 ccd = exposure.getDetector()
73 ratios = [[
None for iAmp
in ccd]
for jAmp
in ccd]
75 for ii, iAmp
in enumerate(ccd):
76 iImage = mi[iAmp.getBBox()]
77 iMask = iImage.mask.array
78 select = (iMask & detected > 0) & (iMask & bad == 0) & np.isfinite(iImage.image.array)
79 for jj, jAmp
in enumerate(ccd):
82 jImage =
extractAmp(mi.image, jAmp, iAmp.getReadoutCorner(), isTrimmed=
True)
83 ratios[jj][ii] = (jImage.array[select] - bg)/iImage.image.array[select]
89 """Measure crosstalk coefficients from the ratios 91 Given a list of ratios for each target/source amp combination, 92 we measure a robust mean and error. 94 The coefficient errors returned are the (robust) standard deviation of 99 ratios : `list` of `list` of `numpy.ndarray` 100 Matrix of arrays of ratios. 102 Number of rejection iterations. 104 Rejection threshold (sigma). 108 coeff : `numpy.ndarray` 109 Crosstalk coefficients. 110 coeffErr : `numpy.ndarray` 111 Crosstalk coefficient errors. 112 coeffNum : `numpy.ndarray` 113 Number of pixels for each measurement. 115 numAmps = len(ratios)
116 assert all(len(rr) == numAmps
for rr
in ratios)
118 coeff = np.zeros((numAmps, numAmps))
119 coeffErr = np.zeros((numAmps, numAmps))
120 coeffNum = np.zeros((numAmps, numAmps), dtype=int)
122 for ii, jj
in itertools.product(range(numAmps), range(numAmps)):
126 values = np.array(ratios[ii][jj])
127 values = values[np.abs(values) < 1.0]
129 coeffNum[ii][jj] = len(values)
132 coeff[ii][jj] = np.nan
133 coeffErr[ii][jj] = np.nan
136 for rej
in range(rejIter):
137 lo, med, hi = np.percentile(values, [25.0, 50.0, 75.0])
138 sigma = 0.741*(hi - lo)
139 good = np.abs(values - med) < rejSigma*sigma
140 if good.sum() == len(good):
142 values = values[good]
144 coeff[ii][jj] = np.mean(values)
145 coeffErr[ii][jj] = np.nan
if coeffNum[ii][jj] == 1
else np.std(values)
147 return coeff, coeffErr, coeffNum
151 """Configuration for MeasureCrosstalkTask""" 153 threshold =
Field(dtype=float, default=30000, doc=
"Minimum level for which to measure crosstalk")
154 doRerunIsr =
Field(dtype=bool, default=
True, doc=
"Rerun the ISR, even if postISRCCD files are available")
155 badMask =
ListField(dtype=str, default=[
"SAT",
"BAD",
"INTRP"], doc=
"Mask planes to ignore")
156 rejIter =
Field(dtype=int, default=3, doc=
"Number of rejection iterations")
157 rejSigma =
Field(dtype=float, default=2.0, doc=
"Rejection threshold (sigma)")
160 Config.setDefaults(self)
161 self.
isr.doWrite =
False 162 self.
isr.growSaturationFootprintSize = 0
166 """Measure intra-CCD crosstalk 168 This Task behaves in a scatter-gather fashion: 169 * Scatter: get ratios for each CCD. 170 * Gather: combine ratios to produce crosstalk coefficients. 172 ConfigClass = MeasureCrosstalkConfig
173 _DefaultName =
"measureCrosstalk" 176 CmdLineTask.__init__(self, *args, **kwargs)
180 def _makeArgumentParser(cls):
181 parser = super(MeasureCrosstalkTask, cls)._makeArgumentParser()
182 parser.add_argument(
"--crosstalkName",
183 help=
"Name for this set of crosstalk coefficients", default=
"Unknown")
184 parser.add_argument(
"--outputFileName",
185 help=
"Name of yaml file to which to write crosstalk coefficients")
186 parser.add_argument(
"--dump-ratios", dest=
"dumpRatios",
187 help=
"Name of pickle file to which to write crosstalk ratios")
192 """Implement scatter/gather 196 coeff : `numpy.ndarray` 197 Crosstalk coefficients. 198 coeffErr : `numpy.ndarray` 199 Crosstalk coefficient errors. 200 coeffNum : `numpy.ndarray` 201 Number of pixels used for crosstalk measurement. 203 kwargs[
"doReturnResults"] =
True 204 results = super(MeasureCrosstalkTask, cls).
parseAndRun(*args, **kwargs)
205 task =
cls(config=results.parsedCmd.config, log=results.parsedCmd.log)
206 resultList = [rr.result
for rr
in results.resultList]
207 if results.parsedCmd.dumpRatios:
209 pickle.dump(resultList, open(results.parsedCmd.dumpRatios,
"wb"))
210 coeff, coeffErr, coeffNum = task.reduce(resultList)
212 outputFileName = results.parsedCmd.outputFileName
213 if outputFileName
is not None:
214 butler = results.parsedCmd.butler
215 dataId = results.parsedCmd.id.idList[0]
216 dataId[
"detector"] = butler.queryMetadata(
"raw", [
"detector"], dataId)[0]
218 det = butler.get(
'raw', dataId).getDetector()
220 crosstalkName=results.parsedCmd.crosstalkName, indent=2)
229 """Get crosstalk ratios for CCD 233 dataRef : `lsst.daf.peristence.ButlerDataRef` 234 Data reference for CCD. 238 ratios : `list` of `list` of `numpy.ndarray` 239 A matrix of pixel arrays. 242 if not self.
config.doRerunIsr:
244 exposure = dataRef.get(
"postISRCCD")
249 exposure = self.isr.
runDataRef(dataRef).exposure
251 dataId = dataRef.dataId
252 return self.
run(exposure, dataId=dataId)
254 def run(self, exposure, dataId=None):
255 """Extract and return cross talk ratios for an exposure 259 exposure : `lsst.afw.image.Exposure` 260 Image data to measure crosstalk ratios from. 262 Optional data ID for the exposure to process; used for logging. 266 ratios : `list` of `list` of `numpy.ndarray` 267 A matrix of pixel arrays. 270 self.
log.
info(
"Extracted %d pixels from %s",
271 sum(len(jj)
for ii
in ratios
for jj
in ii
if jj
is not None), dataId)
275 """Combine ratios to produce crosstalk coefficients 279 ratioList : `list` of `list` of `list` of `numpy.ndarray` 280 A list of matrices of arrays; a list of results from 281 `extractCrosstalkRatios`. 285 coeff : `numpy.ndarray` 286 Crosstalk coefficients. 287 coeffErr : `numpy.ndarray` 288 Crosstalk coefficient errors. 289 coeffNum : `numpy.ndarray` 290 Number of pixels used for crosstalk measurement. 300 assert len(rr) == numAmps
301 assert all(len(xx) == numAmps
for xx
in rr)
304 raise RuntimeError(
"Unable to measure crosstalk signal for any amplifier")
306 ratios = [[
None for jj
in range(numAmps)]
for ii
in range(numAmps)]
307 for ii, jj
in itertools.product(range(numAmps), range(numAmps)):
311 values = [rr[ii][jj]
for rr
in ratioList]
312 num = sum(len(vv)
for vv
in values)
314 self.
log.
warn(
"No values for matrix element %d,%d" % (ii, jj))
317 result = np.concatenate([vv
for vv
in values
if len(vv) > 0])
318 ratios[ii][jj] = result
321 self.
log.
info(
"Coefficients:\n%s\n", coeff)
322 self.
log.
info(
"Errors:\n%s\n", coeffErr)
323 self.
log.
info(
"Numbers:\n%s\n", coeffNum)
324 return coeff, coeffErr, coeffNum
326 def _getConfigName(self):
327 """Disable config output""" 330 def _getMetadataName(self):
331 """Disable metdata output"""
def __init__(self, args, kwargs)
def parseAndRun(cls, args, kwargs)
def makeSubtask(self, name, keyArgs)
def reduce(self, ratioList)
A Threshold is used to pass a threshold value to detection algorithms.
bool all(CoordinateExpr< N > const &expr) noexcept
Return true if all elements are true.
def measureCrosstalkCoefficients(ratios, rejIter=3, rejSigma=2.0)
def calculateBackground(mi, badPixels=["BAD"])
def extractCrosstalkRatios(exposure, threshold=30000, badPixels=["SAT", BAD, INTRP)
def extractAmp(image, amp, corner, isTrimmed=False)
def runDataRef(self, dataRef)
def run(self, exposure, dataId=None)
daf::base::PropertyList * list
def writeCrosstalkCoeffs(outputFileName, coeff, det=None, crosstalkName="Unknown", indent=2)