1 from __future__
import absolute_import, division, print_function
2 from builtins
import object
25 from builtins
import zip
26 from future.utils
import with_metaclass
30 from lsst.pipe.base
import Struct
31 from .applyLookupTable
import applyLookupTable
33 __all__ = [
"LinearizeBase",
"LinearizeLookupTable",
"LinearizeSquared"]
37 """Abstract base class functor for correcting non-linearity
39 Subclasses must define __call__ and set class variable LinearityType to a string
40 that will be used for linearity type in AmpInfoCatalog
46 """Correct non-linearity
48 @param[in] image image to be corrected (an lsst.afw.image.Image)
49 @param[in] detector detector information (an instance of lsst::afw::cameraGeom::Detector)
50 @param[in] log logger (an lsst.log.Log), or None to disable logging;
51 a warning is logged if amplifiers are skipped or other worrisome events occur
53 @return an lsst.pipe.base.Struct containing at least the following fields:
54 - numAmps number of amplifiers found
55 - numLinearized number of amplifiers linearized
57 @throw RuntimeError if the linearity type is wrong
58 @throw a subclass of Exception if linearization fails for any other reason
63 """Verify that the linearity type is correct for this detector
65 @warning only checks the first record of the amp info catalog
67 @param[in] detector detector information (an instance of lsst::afw::cameraGeom::Detector)
69 @throw RuntimeError if anything doesn't match
71 ampInfoType = detector.getAmpInfoCatalog()[0].getLinearityType()
72 if self.LinearityType != ampInfoType:
73 raise RuntimeError(
"Linearity types don't match: %s != %s" % (self.LinearityType, ampInfoType))
77 """Correct non-linearity with a persisted lookup table
79 for each i,j of image:
81 colInd = int(c1 + uncorrImage[i,j])
82 corrImage[i,j] = uncorrImage[i,j] + table[rowInd, colInd]
84 where c0, c1 are collimation coefficients from the AmpInfoTable of the detector:
85 - c0: row index; used to identify which row of the table to use (typically one per amplifier,
86 though one can have multiple amplifiers use the same table)
87 - c1: column index offset; added to the uncorrected image value before truncation;
88 this supports tables that can handle negative image values; also, if the c1 ends with .5
89 then the nearest index is used instead of truncating to the next smaller index
91 In order to keep related data together, the coefficients are persisted along with the table.
93 LinearityType =
"LookupTable"
96 """Construct a LinearizeLookupTable
98 @param[in] table lookup table; a 2-dimensional array of floats:
99 - one row for each row index (value of coef[0] in the amp info catalog)
100 - one column for each image value
101 To avoid copying the table the last index should vary fastest (numpy default "C" order)
102 @param[in] detector detector information (an instance of lsst::afw::cameraGeom::Detector);
103 the name, serial, and amplifier linearization type and coefficients are saved
105 @throw RuntimeError if table is not 2-dimensional,
106 table has fewer columns than rows (indicating that the indices are swapped),
107 or if any row index (linearity coefficient 0) is out of range
109 LinearizeBase.__init__(self)
111 self.
_table = np.array(table, order=
"C")
112 if len(table.shape) != 2:
113 raise RuntimeError(
"table shape = %s; must have two dimensions" % (table.shape,))
114 if table.shape[1] < table.shape[0]:
115 raise RuntimeError(
"table shape = %s; indices are switched" % (table.shape,))
120 ampInfoCat = detector.getAmpInfoCatalog()
122 colIndOffsetList = []
123 numTableRows = table.shape[0]
124 for ampInfo
in ampInfoCat:
125 rowInd, colIndOffset = ampInfo.getLinearityCoeffs()[0:2]
127 if rowInd < 0
or rowInd >= numTableRows:
128 raise RuntimeError(
"Amplifier %s has rowInd=%s not in range[0, %s)" %
129 (ampInfo.getName(), rowInd, numTableRows))
130 rowIndList.append(int(rowInd))
131 colIndOffsetList.append(colIndOffset)
132 self.
_rowIndArr = np.array(rowIndList, dtype=int)
136 """Correct for non-linearity
138 @param[in] image image to be corrected (an lsst.afw.image.Image)
139 @param[in] detector detector info about image (an lsst.afw.cameraGeom.Detector);
140 the name, serial and number of amplifiers must match persisted data;
141 the bbox from each amplifier is read;
142 the linearization coefficients are ignored in favor of the persisted values
143 @param[in] log logger (an lsst.log.Log), or None to disable logging;
144 a warning is logged if any pixels are out of range of their lookup table
146 @return an lsst.pipe.base.Struct containing:
147 - numAmps number of amplifiers found
148 - numLinearized number of amplifiers linearized (always equal to numAmps for this linearizer)
149 - numOutOfRange number of pixels out of range of their lookup table (summed across all amps)
151 @throw RuntimeError if the linearity type is wrong or if the detector name, serial
152 or number of amplifiers does not match the saved data
155 ampInfoCat = detector.getAmpInfoCatalog()
158 bbox = ampInfo.getBBox()
159 ampView = image.Factory(image, bbox)
160 tableRow = self.
_table[rowInd, :]
163 if numOutOfRange > 0
and log
is not None:
164 log.warn(
"%s pixels of detector \"%s\" were out of range of the linearization table",
165 numOutOfRange, detector.getName())
166 numAmps = len(ampInfoCat)
169 numLinearized=numAmps,
170 numOutOfRange=numOutOfRange,
174 """Check detector name and serial number, ampInfo table length and linearity type
176 @param[in] detector detector info about image (an lsst.afw.cameraGeom.Detector);
178 @throw RuntimeError if anything doesn't match
181 raise RuntimeError(
"Detector names don't match: %s != %s" %
184 raise RuntimeError(
"Detector serial numbers don't match: %s != %s" %
187 numAmps = len(detector.getAmpInfoCatalog())
189 raise RuntimeError(
"Detector number of amps = %s does not match saved value %s" %
195 """Correct non-linearity with a squared model
197 corrImage = uncorrImage + c0*uncorrImage^2
199 where c0 is linearity coefficient 0 in the AmpInfoCatalog of the detector
201 LinearityType =
"Squared"
204 """Correct for non-linearity
206 @param[in] image image to be corrected (an lsst.afw.image.Image)
207 @param[in] detector detector info about image (an lsst.afw.cameraGeom.Detector)
208 @param[in] log logger (an lsst.log.Log), or None to disable logging;
209 a warning is logged if any amplifiers are skipped because the square coefficient is 0
211 @return an lsst.pipe.base.Struct containing at least the following fields:
212 - nAmps number of amplifiers found
213 - nLinearized number of amplifiers linearized
215 @throw RuntimeError if the linearity type is wrong
218 ampInfoCat = detector.getAmpInfoCatalog()
220 for ampInfo
in ampInfoCat:
221 sqCoeff = ampInfo.getLinearityCoeffs()[0]
223 bbox = ampInfo.getBBox()
224 ampArr = image.Factory(image, bbox).getArray()
225 ampArr *= (1 + sqCoeff*ampArr)
228 numAmps = len(ampInfoCat)
229 if numAmps > numLinearized
and log
is not None:
230 log.warn(
"%s of %s amps in detector \"%s\" were not linearized (coefficient = 0)",
231 numAmps - numLinearized, numAmps, detector.getName())
234 numLinearized=numLinearized,
int applyLookupTable(afw::image::Image< PixelT > &image, ndarray::Array< PixelT, 1, 1 > const &table, PixelT indOffset)
Add the values in a lookup table to an image, e.g.