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
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,
def __call__(self, image, detector, log=None)
def __call__(self, image, detector, log=None)
def checkLinearityType(self, detector)
def checkDetector(self, detector)
def __call__(self, image, detector, log=None)
def __init__(self, table, detector)