23 Measure intra-CCD crosstalk coefficients.
25 from __future__
import absolute_import, division, print_function
27 __all__ = [
"extractCrosstalkRatios",
"measureCrosstalkCoefficients",
28 "MeasureCrosstalkConfig",
"MeasureCrosstalkTask"]
30 from builtins
import range
35 from lsst.afw.detection
import FootprintSet, Threshold
36 from lsst.pex.config
import Config, Field, ListField, ConfigurableField
37 from lsst.pipe.base
import CmdLineTask
39 from .crosstalk
import calculateBackground, extractAmp
40 from .isrTask
import IsrTask
44 """Extract crosstalk ratios between different amplifiers
46 For pixels above ``threshold``, we calculate the ratio between each
47 target amp and source amp. We return a list of ratios for each pixel
48 for each target/source combination, as a matrix of lists.
52 exposure : `lsst.afw.image.Exposure`
53 Exposure for which to measure crosstalk.
55 Lower limit on pixels for which we measure crosstalk.
56 badPixels : `list` of `str`
57 Mask planes indicating a pixel is bad.
61 ratios : `list` of `list` of `numpy.ndarray`
62 A matrix of pixel arrays. ``ratios[i][j]`` is an array of
63 the fraction of the ``j``-th amp present on the ``i``-th amp.
64 The value is `None` for the diagonal elements.
66 mi = exposure.getMaskedImage()
67 FootprintSet(mi, Threshold(threshold),
"DETECTED")
68 detected = mi.getMask().getPlaneBitMask(
"DETECTED")
69 bad = mi.getMask().getPlaneBitMask(badPixels)
72 ccd = exposure.getDetector()
74 ratios = [[
None for iAmp
in ccd]
for jAmp
in ccd]
76 for ii, iAmp
in enumerate(ccd):
77 iImage = mi.Factory(mi, iAmp.getBBox())
78 iMask = iImage.getMask().getArray()
79 select = (iMask & detected > 0) & (iMask & bad == 0) & np.isfinite(iImage.getImage().getArray())
80 for jj, jAmp
in enumerate(ccd):
83 jImage =
extractAmp(mi.getImage(), jAmp, iAmp.getReadoutCorner())
84 ratios[jj][ii] = (jImage.getArray()[select] - bg)/iImage.getImage().getArray()[select]
90 """Measure crosstalk coefficients from the ratios
92 Given a list of ratios for each target/source amp combination,
93 we measure a robust mean and error.
95 The coefficient errors returned are the (robust) standard deviation of
100 ratios : `list` of `list` of `numpy.ndarray`
101 Matrix of arrays of ratios.
103 Number of rejection iterations.
105 Rejection threshold (sigma).
109 coeff : `numpy.ndarray`
110 Crosstalk coefficients.
111 coeffErr : `numpy.ndarray`
112 Crosstalk coefficient errors.
113 coeffNum : `numpy.ndarray`
114 Number of pixels for each measurement.
116 numAmps = len(ratios)
117 assert all(len(rr) == numAmps
for rr
in ratios)
119 coeff = np.zeros((numAmps, numAmps))
120 coeffErr = np.zeros((numAmps, numAmps))
121 coeffNum = np.zeros((numAmps, numAmps), dtype=int)
123 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]
128 for rej
in range(rejIter):
129 lo, med, hi = np.percentile(values, [25.0, 50.0, 75.0])
130 sigma = 0.741*(hi - lo)
131 good = np.abs(values - med) < rejSigma*sigma
132 if good.sum() == len(good):
134 values = values[good]
136 coeff[ii][jj] = np.mean(values)
137 coeffErr[ii][jj] = np.std(values)
138 coeffNum[ii][jj] = len(values)
140 return coeff, coeffErr, coeffNum
144 """Configuration for MeasureCrosstalkTask"""
145 isr = ConfigurableField(target=IsrTask, doc=
"Instrument signature removal")
146 threshold = Field(dtype=float, default=30000, doc=
"Minimum level for which to measure crosstalk")
147 badMask = ListField(dtype=str, default=[
"SAT",
"BAD",
"INTRP"], doc=
"Mask planes to ignore")
148 rejIter = Field(dtype=int, default=3, doc=
"Number of rejection iterations")
149 rejSigma = Field(dtype=float, default=2.0, doc=
"Rejection threshold (sigma)")
152 Config.setDefaults(self)
153 self.isr.doWrite =
False
154 self.isr.growSaturationFootprintSize = 0
158 """Measure intra-CCD crosstalk
160 This Task behaves in a scatter-gather fashion:
161 * Scatter: get ratios for each CCD.
162 * Gather: combine ratios to produce crosstalk coefficients.
164 ConfigClass = MeasureCrosstalkConfig
165 _DefaultName =
"measureCrosstalk"
168 CmdLineTask.__init__(self, *args, **kwargs)
169 self.makeSubtask(
"isr")
172 def _makeArgumentParser(cls):
173 parser = super(MeasureCrosstalkTask, cls)._makeArgumentParser()
174 parser.add_argument(
"--dump-ratios", dest=
"dumpRatios",
175 help=
"Name of pickle file to which to write crosstalk ratios")
180 """Implement scatter/gather
184 coeff : `numpy.ndarray`
185 Crosstalk coefficients.
186 coeffErr : `numpy.ndarray`
187 Crosstalk coefficient errors.
188 coeffNum : `numpy.ndarray`
189 Number of pixels used for crosstalk measurement.
191 kwargs[
"doReturnResults"] =
True
192 results = super(MeasureCrosstalkTask, cls).
parseAndRun(*args, **kwargs)
193 task = cls(config=results.parsedCmd.config, log=results.parsedCmd.log)
194 resultList = [rr.result
for rr
in results.resultList]
195 if results.parsedCmd.dumpRatios:
197 pickle.dump(resultList, open(results.parsedCmd.dumpRatios,
"w"))
198 return task.reduce(resultList)
201 """Get crosstalk ratios for CCD
205 dataRef : `lsst.daf.peristence.ButlerDataRef`
206 Data reference for CCD.
210 ratios : `list` of `list` of `numpy.ndarray`
211 A matrix of pixel arrays.
213 exposure = self.isr.runDataRef(dataRef).exposure
215 self.log.info(
"Extracted %d pixels from %s",
216 sum(len(jj)
for ii
in ratios
for jj
in ii
if jj
is not None), dataRef.dataId)
220 """Combine ratios to produce crosstalk coefficients
224 ratioList : `list` of `list` of `list` of `numpy.ndarray`
225 A list of matrices of arrays; a list of results from
226 `extractCrosstalkRatios`.
230 coeff : `numpy.ndarray`
231 Crosstalk coefficients.
232 coeffErr : `numpy.ndarray`
233 Crosstalk coefficient errors.
234 coeffNum : `numpy.ndarray`
235 Number of pixels used for crosstalk measurement.
237 numAmps = len(ratioList[0])
238 assert all(len(rr) == numAmps
for rr
in ratioList)
239 assert all(all(len(xx) == numAmps
for xx
in rr)
for rr
in ratioList)
240 ratios = [[
None for jj
in range(numAmps)]
for ii
in range(numAmps)]
241 for ii, jj
in itertools.product(range(numAmps), range(numAmps)):
245 values = [rr[ii][jj]
for rr
in ratioList]
246 num = sum(len(vv)
for vv
in values)
248 raise RuntimeError(
"No values for matrix element %d,%d" % (ii, jj))
249 result = np.concatenate([vv
for vv
in values
if len(vv) > 0])
250 ratios[ii][jj] = result
252 self.config.rejSigma)
253 self.log.info(
"Coefficients:\n%s\n", coeff)
254 self.log.info(
"Errors:\n%s\n", coeffErr)
255 self.log.info(
"Numbers:\n%s\n", coeffNum)
256 return coeff, coeffErr, coeffNum
258 def _getConfigName(self):
259 """Disable config output"""
262 def _getMetadataName(self):
263 """Disable metdata output"""
def measureCrosstalkCoefficients
def extractCrosstalkRatios