1 from builtins
import object
24 from builtins
import zip
25 from future.utils
import with_metaclass
30 from .isrLib
import applyLookupTable
32 __all__ = [
"LinearizeBase",
"LinearizeLookupTable",
"LinearizeSquared"]
36 """Abstract base class functor for correcting non-linearity
38 Subclasses must define __call__ and set class variable LinearityType to a string
39 that will be used for linearity type in AmpInfoCatalog
45 """Correct non-linearity
47 @param[in] image image to be corrected (an lsst.afw.image.Image)
48 @param[in] detector detector information (an instance of lsst::afw::cameraGeom::Detector)
49 @param[in] log logger (an lsst.log.Log), or None to disable logging;
50 a warning is logged if amplifiers are skipped or other worrisome events occur
52 @return an lsst.pipe.base.Struct containing at least the following fields:
53 - numAmps number of amplifiers found
54 - numLinearized number of amplifiers linearized
56 @throw RuntimeError if the linearity type is wrong
57 @throw a subclass of Exception if linearization fails for any other reason
62 """Verify that the linearity type is correct for this detector
64 @warning only checks the first record of the amp info catalog
66 @param[in] detector detector information (an instance of lsst::afw::cameraGeom::Detector)
68 @throw RuntimeError if anything doesn't match
70 ampInfoType = detector.getAmpInfoCatalog()[0].getLinearityType()
71 if self.LinearityType != ampInfoType:
72 raise RuntimeError(
"Linearity types don't match: %s != %s" % (self.LinearityType, ampInfoType))
76 """Correct non-linearity with a persisted lookup table
78 for each i,j of image:
80 colInd = int(c1 + uncorrImage[i,j])
81 corrImage[i,j] = uncorrImage[i,j] + table[rowInd, colInd]
83 where c0, c1 are collimation coefficients from the AmpInfoTable of the detector:
84 - c0: row index; used to identify which row of the table to use (typically one per amplifier,
85 though one can have multiple amplifiers use the same table)
86 - c1: column index offset; added to the uncorrected image value before truncation;
87 this supports tables that can handle negative image values; also, if the c1 ends with .5
88 then the nearest index is used instead of truncating to the next smaller index
90 In order to keep related data together, the coefficients are persisted along with the table.
92 LinearityType =
"LookupTable"
95 """Construct a LinearizeLookupTable
97 @param[in] table lookup table; a 2-dimensional array of floats:
98 - one row for each row index (value of coef[0] in the amp info catalog)
99 - one column for each image value
100 To avoid copying the table the last index should vary fastest (numpy default "C" order)
101 @param[in] detector detector information (an instance of lsst::afw::cameraGeom::Detector);
102 the name, serial, and amplifier linearization type and coefficients are saved
104 @throw RuntimeError if table is not 2-dimensional,
105 table has fewer columns than rows (indicating that the indices are swapped),
106 or if any row index (linearity coefficient 0) is out of range
108 LinearizeBase.__init__(self)
111 if len(table.shape) != 2:
112 raise RuntimeError(
"table shape = %s; must have two dimensions" % (table.shape,))
113 if table.shape[1] < table.shape[0]:
114 raise RuntimeError(
"table shape = %s; indices are switched" % (table.shape,))
119 ampInfoCat = detector.getAmpInfoCatalog()
121 colIndOffsetList = []
122 numTableRows = table.shape[0]
123 for ampInfo
in ampInfoCat:
124 rowInd, colIndOffset = ampInfo.getLinearityCoeffs()[0:2]
126 if rowInd < 0
or rowInd >= numTableRows:
127 raise RuntimeError(
"Amplifier %s has rowInd=%s not in range[0, %s)" %
128 (ampInfo.getName(), rowInd, numTableRows))
129 rowIndList.append(int(rowInd))
130 colIndOffsetList.append(colIndOffset)
135 """Correct for non-linearity
137 @param[in] image image to be corrected (an lsst.afw.image.Image)
138 @param[in] detector detector info about image (an lsst.afw.cameraGeom.Detector);
139 the name, serial and number of amplifiers must match persisted data;
140 the bbox from each amplifier is read;
141 the linearization coefficients are ignored in favor of the persisted values
142 @param[in] log logger (an lsst.log.Log), or None to disable logging;
143 a warning is logged if any pixels are out of range of their lookup table
145 @return an lsst.pipe.base.Struct containing:
146 - numAmps number of amplifiers found
147 - numLinearized number of amplifiers linearized (always equal to numAmps for this linearizer)
148 - numOutOfRange number of pixels out of range of their lookup table (summed across all amps)
150 @throw RuntimeError if the linearity type is wrong or if the detector name, serial
151 or number of amplifiers does not match the saved data
154 ampInfoCat = detector.getAmpInfoCatalog()
157 bbox = ampInfo.getBBox()
158 ampView = image.Factory(image, bbox)
159 tableRow = self.
_table[rowInd, :]
162 if numOutOfRange > 0
and log
is not None:
163 log.warn(
"%s pixels of detector \"%s\" were out of range of the linearization table",
164 numOutOfRange, detector.getName())
165 numAmps = len(ampInfoCat)
168 numLinearized=numAmps,
169 numOutOfRange=numOutOfRange,
173 """Check detector name and serial number, ampInfo table length and linearity type
175 @param[in] detector detector info about image (an lsst.afw.cameraGeom.Detector);
177 @throw RuntimeError if anything doesn't match
180 raise RuntimeError(
"Detector names don't match: %s != %s" %
183 raise RuntimeError(
"Detector serial numbers don't match: %s != %s" %
186 numAmps = len(detector.getAmpInfoCatalog())
188 raise RuntimeError(
"Detector number of amps = %s does not match saved value %s" %
194 """Correct non-linearity with a squared model
196 corrImage = uncorrImage + c0*uncorrImage^2
198 where c0 is linearity coefficient 0 in the AmpInfoCatalog of the detector
200 LinearityType =
"Squared"
203 """Correct for non-linearity
205 @param[in] image image to be corrected (an lsst.afw.image.Image)
206 @param[in] detector detector info about image (an lsst.afw.cameraGeom.Detector)
207 @param[in] log logger (an lsst.log.Log), or None to disable logging;
208 a warning is logged if any amplifiers are skipped because the square coefficient is 0
210 @return an lsst.pipe.base.Struct containing at least the following fields:
211 - nAmps number of amplifiers found
212 - nLinearized number of amplifiers linearized
214 @throw RuntimeError if the linearity type is wrong
217 ampInfoCat = detector.getAmpInfoCatalog()
219 for ampInfo
in ampInfoCat:
220 sqCoeff = ampInfo.getLinearityCoeffs()[0]
222 bbox = ampInfo.getBBox()
223 ampArr = image.Factory(image, bbox).getArray()
224 ampArr *= (1 + sqCoeff*ampArr)
227 numAmps = len(ampInfoCat)
228 if numAmps > numLinearized
and log
is not None:
229 log.warn(
"%s of %s amps in detector \"%s\" were not linearized (coefficient = 0)",
230 numAmps - numLinearized, numAmps, detector.getName())
233 numLinearized=numLinearized,
int applyLookupTable(afw::image::Image< PixelT > &image, ndarray::Array< PixelT, 1, 1 > const &table, PixelT indOffset)